pytest-neon 3.0.0__py3-none-any.whl → 3.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pytest_neon/__init__.py +1 -1
- pytest_neon/plugin.py +73 -0
- {pytest_neon-3.0.0.dist-info → pytest_neon-3.0.1.dist-info}/METADATA +1 -1
- pytest_neon-3.0.1.dist-info/RECORD +8 -0
- pytest_neon-3.0.0.dist-info/RECORD +0 -8
- {pytest_neon-3.0.0.dist-info → pytest_neon-3.0.1.dist-info}/WHEEL +0 -0
- {pytest_neon-3.0.0.dist-info → pytest_neon-3.0.1.dist-info}/entry_points.txt +0 -0
- {pytest_neon-3.0.0.dist-info → pytest_neon-3.0.1.dist-info}/licenses/LICENSE +0 -0
pytest_neon/__init__.py
CHANGED
pytest_neon/plugin.py
CHANGED
|
@@ -577,6 +577,7 @@ class XdistCoordinator:
|
|
|
577
577
|
def __init__(self, tmp_path_factory: pytest.TempPathFactory):
|
|
578
578
|
self.worker_id = _get_xdist_worker_id()
|
|
579
579
|
self.is_xdist = self.worker_id != "main"
|
|
580
|
+
self._worker_count: int | None = None
|
|
580
581
|
|
|
581
582
|
if self.is_xdist:
|
|
582
583
|
root_tmp_dir = tmp_path_factory.getbasetemp().parent
|
|
@@ -584,6 +585,21 @@ class XdistCoordinator:
|
|
|
584
585
|
else:
|
|
585
586
|
self._lock_dir = None
|
|
586
587
|
|
|
588
|
+
def _get_worker_count(self) -> int:
|
|
589
|
+
"""Get the total number of xdist workers."""
|
|
590
|
+
if self._worker_count is not None:
|
|
591
|
+
return self._worker_count
|
|
592
|
+
|
|
593
|
+
# PYTEST_XDIST_WORKER_COUNT is set by xdist
|
|
594
|
+
count_str = os.environ.get("PYTEST_XDIST_WORKER_COUNT")
|
|
595
|
+
if count_str:
|
|
596
|
+
self._worker_count = int(count_str)
|
|
597
|
+
else:
|
|
598
|
+
# Fallback: count from worker ID pattern (gw0, gw1, etc.)
|
|
599
|
+
# This shouldn't happen in normal xdist runs
|
|
600
|
+
self._worker_count = 1
|
|
601
|
+
return self._worker_count
|
|
602
|
+
|
|
587
603
|
def coordinate_resource(
|
|
588
604
|
self,
|
|
589
605
|
resource_name: str,
|
|
@@ -642,6 +658,53 @@ class XdistCoordinator:
|
|
|
642
658
|
signal_file = self._lock_dir / f"neon_{signal_name}"
|
|
643
659
|
signal_file.write_text("done")
|
|
644
660
|
|
|
661
|
+
def signal_worker_done(self) -> None:
|
|
662
|
+
"""Signal that this worker has completed all tests."""
|
|
663
|
+
if not self.is_xdist or self._lock_dir is None:
|
|
664
|
+
return
|
|
665
|
+
|
|
666
|
+
done_file = self._lock_dir / f"neon_worker_done_{self.worker_id}"
|
|
667
|
+
done_file.write_text("done")
|
|
668
|
+
|
|
669
|
+
def wait_for_all_workers_done(self, timeout: float = 300) -> None:
|
|
670
|
+
"""
|
|
671
|
+
Wait for all xdist workers to signal completion.
|
|
672
|
+
|
|
673
|
+
This ensures the branch isn't deleted while other workers are still
|
|
674
|
+
running tests.
|
|
675
|
+
|
|
676
|
+
Args:
|
|
677
|
+
timeout: Maximum time to wait in seconds (default: 5 minutes)
|
|
678
|
+
"""
|
|
679
|
+
if not self.is_xdist or self._lock_dir is None:
|
|
680
|
+
return
|
|
681
|
+
|
|
682
|
+
worker_count = self._get_worker_count()
|
|
683
|
+
waited = 0.0
|
|
684
|
+
poll_interval = 0.5
|
|
685
|
+
|
|
686
|
+
while waited < timeout:
|
|
687
|
+
done_count = 0
|
|
688
|
+
for i in range(worker_count):
|
|
689
|
+
done_file = self._lock_dir / f"neon_worker_done_gw{i}"
|
|
690
|
+
if done_file.exists():
|
|
691
|
+
done_count += 1
|
|
692
|
+
|
|
693
|
+
if done_count >= worker_count:
|
|
694
|
+
return
|
|
695
|
+
|
|
696
|
+
time.sleep(poll_interval)
|
|
697
|
+
waited += poll_interval
|
|
698
|
+
|
|
699
|
+
# Timeout - log warning but proceed with cleanup anyway
|
|
700
|
+
# This prevents infinite hangs if a worker crashes
|
|
701
|
+
warnings.warn(
|
|
702
|
+
f"Timeout waiting for all workers to complete after {timeout}s. "
|
|
703
|
+
f"Only {done_count}/{worker_count} workers signaled completion. "
|
|
704
|
+
f"Proceeding with branch cleanup.",
|
|
705
|
+
stacklevel=2,
|
|
706
|
+
)
|
|
707
|
+
|
|
645
708
|
|
|
646
709
|
class EnvironmentManager:
|
|
647
710
|
"""Manages DATABASE_URL environment variable lifecycle."""
|
|
@@ -870,6 +933,10 @@ def _neon_test_branch(
|
|
|
870
933
|
This creates a single branch with expiry that all tests share.
|
|
871
934
|
The first worker creates the branch, others reuse it.
|
|
872
935
|
|
|
936
|
+
Branch cleanup is coordinated so the creator waits for ALL workers
|
|
937
|
+
to complete before deleting the branch, preventing connection errors
|
|
938
|
+
for workers that finish later.
|
|
939
|
+
|
|
873
940
|
Yields:
|
|
874
941
|
Tuple of (branch, is_creator) where is_creator indicates if this
|
|
875
942
|
worker created the branch (and should run migrations/cleanup).
|
|
@@ -893,7 +960,13 @@ def _neon_test_branch(
|
|
|
893
960
|
yield branch, is_creator
|
|
894
961
|
finally:
|
|
895
962
|
env_manager.restore()
|
|
963
|
+
# Signal that this worker is done with all tests
|
|
964
|
+
_neon_xdist_coordinator.signal_worker_done()
|
|
965
|
+
|
|
896
966
|
if is_creator:
|
|
967
|
+
# Wait for all other workers to finish before deleting the branch
|
|
968
|
+
# This prevents "endpoint not found" errors for slower workers
|
|
969
|
+
_neon_xdist_coordinator.wait_for_all_workers_done()
|
|
897
970
|
_neon_branch_manager.delete_branch(branch.branch_id)
|
|
898
971
|
|
|
899
972
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytest-neon
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.1
|
|
4
4
|
Summary: Pytest plugin for Neon database branch isolation in tests
|
|
5
5
|
Project-URL: Homepage, https://github.com/ZainRizvi/pytest-neon
|
|
6
6
|
Project-URL: Repository, https://github.com/ZainRizvi/pytest-neon
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
pytest_neon/__init__.py,sha256=VNFVMj2RGnWj_CYynDMxIoPZ8XSKcN6v_lT94pAlYgg,404
|
|
2
|
+
pytest_neon/plugin.py,sha256=9wL17R95Wcu2fTti_MGNe4y66BZmcGs86qwgZ4GHD1c,42825
|
|
3
|
+
pytest_neon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
pytest_neon-3.0.1.dist-info/METADATA,sha256=U1j8ZiEFOTSl_h4VNmnf7YKywXZ_mb7l8DEjzeUgE-s,10706
|
|
5
|
+
pytest_neon-3.0.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
6
|
+
pytest_neon-3.0.1.dist-info/entry_points.txt,sha256=5U88Idj_G8-PSDb9VF3OwYFbGLHnGOo_GxgYvi0dtXw,37
|
|
7
|
+
pytest_neon-3.0.1.dist-info/licenses/LICENSE,sha256=aKKp_Ex4WBHTByY4BhXJ181dzB_qYhi2pCUmZ7Spn_0,1067
|
|
8
|
+
pytest_neon-3.0.1.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
pytest_neon/__init__.py,sha256=W6OIAvx3sB2jfKCZaUUK1XcQPBxtWCnaYQCRn5NQhuA,404
|
|
2
|
-
pytest_neon/plugin.py,sha256=ADH8on2MMI7jIKdk9mQDKbk59ypMwGNuh4_NsYNTphQ,40114
|
|
3
|
-
pytest_neon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
pytest_neon-3.0.0.dist-info/METADATA,sha256=F68tFRyHHx6C5eKTXjq_bUMRtmR_Q0LcVrlKm8CwLHs,10706
|
|
5
|
-
pytest_neon-3.0.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
6
|
-
pytest_neon-3.0.0.dist-info/entry_points.txt,sha256=5U88Idj_G8-PSDb9VF3OwYFbGLHnGOo_GxgYvi0dtXw,37
|
|
7
|
-
pytest_neon-3.0.0.dist-info/licenses/LICENSE,sha256=aKKp_Ex4WBHTByY4BhXJ181dzB_qYhi2pCUmZ7Spn_0,1067
|
|
8
|
-
pytest_neon-3.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|