[BugFIx] Fix PackageBuilder to Handle Platform Agnostic File Lock (#7242)

* fix package builder to handle platform agnostic file lock

* black

* lint

* unnecessary-pass

* update reference.json

* cleanup import

---------

Co-authored-by: deeleeramone <>
This commit is contained in:
Danglewood
2025-10-18 10:03:53 -07:00
committed by GitHub
parent 039c183c60
commit e7e672f7d8
6 changed files with 64 additions and 15 deletions

View File

@@ -1,9 +1,9 @@
{
"openbb": "1.5.1core",
"openbb": "1.5.2core",
"info": {
"title": "OpenBB Platform (Python)",
"description": "Investment research for everyone, anywhere.",
"core": "1.5.1",
"core": "1.5.2",
"extensions": {
"openbb_core_extension": [],
"openbb_provider_extension": [],

View File

@@ -2,7 +2,7 @@
# pylint: disable=too-many-lines,too-many-locals,too-many-nested-blocks,too-many-statements,too-many-branches,too-many-positional-arguments
import builtins
import fcntl
import contextlib
import inspect
import os
import re
@@ -59,6 +59,14 @@ try:
except ImportError:
CHARTING_INSTALLED = False
try:
import fcntl # type: ignore
_HAS_FCNTL = True
except Exception: # pylint: disable=broad-except # noqa
_HAS_FCNTL = False
import msvcrt # pylint: disable=unused-import # noqa
DataProcessingSupportedTypes = TypeVar(
"DataProcessingSupportedTypes",
list,
@@ -79,6 +87,45 @@ def create_indent(n: int) -> str:
return TAB * n
class FileLock:
"""Simple cross-platform file lock wrapper used only for this module."""
def __init__(self, file_obj):
"""Initialize the file lock."""
self._file = file_obj
def acquire(self, blocking: bool = True) -> None:
"""Acquire the file lock."""
if _HAS_FCNTL:
flags = fcntl.LOCK_EX | (0 if blocking else fcntl.LOCK_NB)
fcntl.flock(self._file.fileno(), flags)
else: # Windows via msvcrt
mode = msvcrt.LK_LOCK if blocking else msvcrt.LK_NBLCK # type: ignore # pylint: disable=E0601
try:
# lock 1 byte at file start; file.seek(0) to ensure position
self._file.seek(0)
msvcrt.locking(self._file.fileno(), mode, 1) # type: ignore
except OSError as exc: # pragma: no cover - platform specific
# Normalize to BlockingIOError for parity with fcntl non-blocking
raise BlockingIOError from exc
def release(self) -> None:
"""Release the file lock."""
try:
if _HAS_FCNTL:
fcntl.flock(self._file.fileno(), fcntl.LOCK_UN)
else:
try:
self._file.seek(0)
msvcrt.locking(self._file.fileno(), msvcrt.LK_UNLCK, 1) # type: ignore
except OSError:
# If unlocking fails on Windows, ignore - file will be closed soon
pass
except Exception: # pylint: disable=broad-except # noqa
pass
class PackageBuilder:
"""Build the extension package for the Platform."""
@@ -123,12 +170,14 @@ class PackageBuilder:
# Open lock file and acquire exclusive lock
with open(self._lock_path, "w", encoding="utf-8") as lock_file:
file_lock = FileLock(lock_file)
try:
# Get exclusive lock on file
fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
file_lock.acquire(blocking=False)
# Write PID to lock file for debugging
lock_file.seek(0)
lock_file.truncate()
lock_file.write(str(os.getpid()))
lock_file.flush()
@@ -141,14 +190,14 @@ class PackageBuilder:
self._save_reference_file(ext_map)
if self.lint:
self._run_linters()
except BlockingIOError:
raise RuntimeError( # noqa # pylint: disable=W0707
f"Another build process is running and has locked {self._lock_path}"
)
finally:
# Release lock
fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
# Release the file lock, suppressing any exceptions during cleanup
with contextlib.suppress(Exception):
file_lock.release()
def _clean(self, modules: str | list[str] | None = None) -> None:
"""Delete the assets and package folder or modules before building."""

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "openbb-core"
version = "1.5.1"
version = "1.5.2"
description = "OpenBB package with core functionality."
authors = ["OpenBB Team <hello@openbb.co>"]
license = "AGPL-3.0-only"

View File

@@ -26,7 +26,7 @@ def mock_reference_data(tmp_path):
mock_data = {"key": "value"}
with open(reference_file, "w", encoding="utf-8") as f:
json.dump(mock_data, f)
return directory
return tmp_path
def test_load_reference_data(mock_reference_data, reference_loader):

10
openbb_platform/poetry.lock generated vendored
View File

@@ -3070,14 +3070,14 @@ openbb-core = ">=1.5.1,<2.0.0"
[[package]]
name = "openbb-core"
version = "1.5.1"
version = "1.5.2"
description = "OpenBB package with core functionality."
optional = false
python-versions = "<3.14,>=3.9.21"
python-versions = "<3.14,>=3.10"
groups = ["main"]
files = [
{file = "openbb_core-1.5.1-py3-none-any.whl", hash = "sha256:5ebd221f82c57dd15a84bc3787965df335604b26bfe71338b5d099ebf5cdfcf2"},
{file = "openbb_core-1.5.1.tar.gz", hash = "sha256:57a424fdc0ae9eb10b855c77dc63e0bafdad4f981120b452e15cd973fedf25a2"},
{file = "openbb_core-1.5.2-py3-none-any.whl", hash = "sha256:a3f1be561a5f6c87b8160f44293fc05f9308e365b0d76a744b6c18154d7c7473"},
{file = "openbb_core-1.5.2.tar.gz", hash = "sha256:6017407025dd4f1ee326c0e99421267f6254efe83fc10099ba63241203d56153"},
]
[package.dependencies]
@@ -6178,4 +6178,4 @@ wsj = ["openbb-wsj"]
[metadata]
lock-version = "2.1"
python-versions = ">=3.10,<3.14"
content-hash = "ef984b27041496f4caabe2c31ed3ef94a42afa9bb05531872377aedd0fc8333e"
content-hash = "74aa34922fd5b5dde0b10df4e402878ea12dcb56bb549ea105768d47afff8309"

View File

@@ -10,7 +10,7 @@ packages = [{ include = "openbb", from = "core" }]
[tool.poetry.dependencies]
python = ">=3.10,<3.14"
openbb-core = "^1.5.1"
openbb-core = "^1.5.2"
openbb-platform-api = "^1.2.1"
openbb-benzinga = "^1.5.0"