Compare commits

...
Author SHA1 Message Date
praisonai-triage-agent[bot]andMervin Praison ce8fe5f88d fix: carry Windows lock length through load() and correct conftest agents path
- load() now passes the acquired lock_length to _release_exclusive_lock so
  Windows unlocks the exact byte range it locked (max(file_size,1)), fixing a
  release/lock mismatch on nonempty session files.
- conftest _agents_path now resolves to src/praisonai-agents (was pointing to a
  nonexistent repo-root praisonai-agents), fixing test collection on source
  checkouts. Corrected path comments.

Co-authored-by: Mervin Praison <[email protected]>
2026-07-29 10:06:56 +00:00
Hore01 2e4d0c571c fix: preserve Windows file lock length during save 2026-07-29 10:58:37 +01:00
Hore01 37cea31ea9 fix: resolve pytest_plugins error with pytest 9 2026-07-28 18:32:08 +01:00
Hore01 02c1c87d58 fix: Remove pytest_plugins from conftest to resolve pytest error 2026-07-27 14:30:07 +01:00
2 changed files with 47 additions and 31 deletions

No files matched your search

@@ -272,35 +272,35 @@ class UnifiedSessionStore:
return merged
def _acquire_exclusive_lock(self, file_obj) -> None:
def _acquire_exclusive_lock(self, file_obj):
if sys.platform == "win32":
import msvcrt
# Lock entire file by using file size (or large value for empty files)
file_obj.seek(0, os.SEEK_END)
file_size = file_obj.tell()
lock_length = max(file_size, 1)
file_obj.seek(0)
msvcrt.locking(file_obj.fileno(), msvcrt.LK_LOCK, lock_length)
return lock_length
elif _HAS_FCNTL:
fcntl.flock(file_obj.fileno(), fcntl.LOCK_EX)
else:
global _WARNED_NO_FCNTL
if not _WARNED_NO_FCNTL:
logger.warning(
"File locking unavailable on this platform (fcntl not available); "
"concurrent writers may corrupt session files."
)
_WARNED_NO_FCNTL = True
return None
def _release_exclusive_lock(self, file_obj) -> None:
return None
def _release_exclusive_lock(self, file_obj, lock_length=None) -> None:
if sys.platform == "win32":
import msvcrt
# Use the same lock length as acquisition
file_obj.seek(0, os.SEEK_END)
file_size = file_obj.tell()
lock_length = max(file_size, 1)
file_obj.seek(0)
if lock_length is None:
lock_length = 1
msvcrt.locking(file_obj.fileno(), msvcrt.LK_UNLCK, lock_length)
elif _HAS_FCNTL:
fcntl.flock(file_obj.fileno(), fcntl.LOCK_UN)
@@ -340,7 +340,7 @@ class UnifiedSessionStore:
to_save = session
with open(path, "r+b") as f:
self._acquire_exclusive_lock(f)
lock_length = self._acquire_exclusive_lock(f)
try:
existing_data = self._read_json_locked(f)
if existing_data:
@@ -349,8 +349,7 @@ class UnifiedSessionStore:
to_save.updated_at = datetime.now().isoformat()
self._write_json_locked(f, to_save.to_dict())
finally:
self._release_exclusive_lock(f)
self._release_exclusive_lock(f, lock_length)
# Safely update mtime cache with error handling
try:
mtime = path.stat().st_mtime
@@ -405,11 +404,11 @@ class UnifiedSessionStore:
try:
with open(path, "r+b") as f:
self._acquire_exclusive_lock(f)
lock_length = self._acquire_exclusive_lock(f)
try:
data = self._read_json_locked(f)
finally:
self._release_exclusive_lock(f)
self._release_exclusive_lock(f, lock_length)
if data is None:
return None
+28 -11
View File
@@ -6,13 +6,29 @@ import warnings
import gc
from unittest.mock import Mock, patch
# Register pytest plugins
pytest_plugins = (
'pytest_asyncio',
'tests._pytest_plugins.test_gating',
'tests._pytest_plugins.network_guard',
)
def pytest_configure(config):
"""Register custom markers to avoid warnings."""
config.pluginmanager.import_plugin("tests._pytest_plugins.test_gating")
config.pluginmanager.import_plugin("tests._pytest_plugins.network_guard")
config.addinivalue_line("markers", "real: Test requires real API keys")
config.addinivalue_line("markers", "integration: Integration test")
config.addinivalue_line("markers", "network: Test requires network access")
config.addinivalue_line("markers", "e2e: End-to-end test")
config.addinivalue_line("markers", "provider_anthropic: Anthropic provider test")
config.addinivalue_line("markers", "provider_openai: OpenAI provider test")
config.addinivalue_line("markers", "provider_google: Google provider test")
config.addinivalue_line("markers", "local_service: Test requires local service")
config.addinivalue_line("markers", "allow_sleep: Allow real sleep in test")
config.addinivalue_line("markers", "slow: Slow running test")
config.addinivalue_line("markers", "xdist_group: Group for xdist parallelization")
config.addinivalue_line("markers", "no_session_isolation: Disable session isolation")
config.addinivalue_line("markers", "unit: Unit test")
config.addinivalue_line("markers", "flaky: Flaky test")
# Suppress aiohttp unclosed session warnings during tests
warnings.filterwarnings("ignore", message="Unclosed client session")
warnings.filterwarnings("ignore", message="Unclosed connector")
@@ -44,14 +60,15 @@ def cleanup_async_resources():
# Force garbage collection to clean up any lingering async resources
gc.collect()
# Add the source paths to sys.path for imports
# praisonai-agents package (core SDK)
# Correct paths for src/praisonai/tests/conftest.py
# Go up 2 levels (tests -> praisonai -> src), then to praisonai-agents
_agents_path = os.path.join(os.path.dirname(__file__), '..', '..', 'praisonai-agents')
if _agents_path not in sys.path:
if os.path.exists(_agents_path) and _agents_path not in sys.path:
sys.path.insert(0, _agents_path)
# praisonai wrapper package
# Go up 1 level from tests to the wrapper root (src/praisonai)
_wrapper_path = os.path.join(os.path.dirname(__file__), '..')
if _wrapper_path not in sys.path:
if os.path.exists(_wrapper_path) and _wrapper_path not in sys.path:
sys.path.insert(0, _wrapper_path)
@pytest.fixture