pytest-neon 2.2.0__tar.gz → 2.2.1__tar.gz

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.
Files changed (28) hide show
  1. pytest_neon-2.2.1/.config/wt.toml +4 -0
  2. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/.github/workflows/release.yml +4 -1
  3. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/PKG-INFO +2 -1
  4. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/pyproject.toml +2 -1
  5. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/src/pytest_neon/__init__.py +1 -1
  6. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/src/pytest_neon/plugin.py +180 -16
  7. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/tests/test_xdist_worker_support.py +49 -0
  8. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/uv.lock +28 -13
  9. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/.env.example +0 -0
  10. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/.github/workflows/tests.yml +0 -0
  11. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/.gitignore +0 -0
  12. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/.neon +0 -0
  13. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/CLAUDE.md +0 -0
  14. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/LICENSE +0 -0
  15. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/README.md +0 -0
  16. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/src/pytest_neon/py.typed +0 -0
  17. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/tests/conftest.py +0 -0
  18. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/tests/test_branch_lifecycle.py +0 -0
  19. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/tests/test_branch_name_prefix.py +0 -0
  20. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/tests/test_cli_options.py +0 -0
  21. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/tests/test_default_branch_safety.py +0 -0
  22. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/tests/test_env_var.py +0 -0
  23. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/tests/test_fixture_errors.py +0 -0
  24. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/tests/test_integration.py +0 -0
  25. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/tests/test_migrations.py +0 -0
  26. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/tests/test_readwrite_readonly_fixtures.py +0 -0
  27. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/tests/test_reset_behavior.py +0 -0
  28. {pytest_neon-2.2.0 → pytest_neon-2.2.1}/tests/test_skip_behavior.py +0 -0
@@ -0,0 +1,4 @@
1
+ # Worktrunk project configuration for pytest-neon
2
+
3
+ # Create fresh uv venv and install all dependencies when creating worktrees
4
+ post-create = "uv venv && uv sync --all-extras"
@@ -92,13 +92,16 @@ jobs:
92
92
  # Update __init__.py
93
93
  sed -i "s/__version__ = \".*\"/__version__ = \"$new_version\"/" src/pytest_neon/__init__.py
94
94
 
95
+ # Update uv.lock to reflect new version
96
+ uv lock
97
+
95
98
  - name: Commit version bump
96
99
  env:
97
100
  VERSION: ${{ steps.bump.outputs.version }}
98
101
  run: |
99
102
  git config user.name "github-actions[bot]"
100
103
  git config user.email "github-actions[bot]@users.noreply.github.com"
101
- git add pyproject.toml src/pytest_neon/__init__.py
104
+ git add pyproject.toml src/pytest_neon/__init__.py uv.lock
102
105
  git commit -m "Release v$VERSION"
103
106
  git tag "v$VERSION"
104
107
  git push origin main --tags
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytest-neon
3
- Version: 2.2.0
3
+ Version: 2.2.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
@@ -24,6 +24,7 @@ Classifier: Programming Language :: Python :: 3.14
24
24
  Classifier: Topic :: Database
25
25
  Classifier: Topic :: Software Development :: Testing
26
26
  Requires-Python: >=3.9
27
+ Requires-Dist: filelock>=3.0
27
28
  Requires-Dist: neon-api>=0.1.0
28
29
  Requires-Dist: pytest>=7.0
29
30
  Requires-Dist: requests>=2.20
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pytest-neon"
7
- version = "2.2.0"
7
+ version = "2.2.1"
8
8
  description = "Pytest plugin for Neon database branch isolation in tests"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -31,6 +31,7 @@ dependencies = [
31
31
  "pytest>=7.0",
32
32
  "neon-api>=0.1.0",
33
33
  "requests>=2.20",
34
+ "filelock>=3.0",
34
35
  ]
35
36
 
36
37
  [project.optional-dependencies]
@@ -9,7 +9,7 @@ from pytest_neon.plugin import (
9
9
  neon_engine,
10
10
  )
11
11
 
12
- __version__ = "2.2.0"
12
+ __version__ = "2.2.1"
13
13
  __all__ = [
14
14
  "NeonBranch",
15
15
  "neon_branch",
@@ -36,17 +36,19 @@ For full documentation, see: https://github.com/ZainRizvi/pytest-neon
36
36
  from __future__ import annotations
37
37
 
38
38
  import contextlib
39
+ import json
39
40
  import os
40
41
  import random
41
42
  import time
42
43
  import warnings
43
44
  from collections.abc import Callable, Generator
44
- from dataclasses import dataclass
45
+ from dataclasses import asdict, dataclass
45
46
  from datetime import datetime, timedelta, timezone
46
47
  from typing import Any, TypeVar
47
48
 
48
49
  import pytest
49
50
  import requests
51
+ from filelock import FileLock
50
52
  from neon_api import NeonAPI
51
53
  from neon_api.exceptions import NeonAPIError
52
54
  from neon_api.schema import EndpointState
@@ -806,9 +808,24 @@ def _wait_for_operations(
806
808
  )
807
809
 
808
810
 
811
+ def _branch_to_dict(branch: NeonBranch) -> dict[str, Any]:
812
+ """Convert NeonBranch to a JSON-serializable dict."""
813
+ return asdict(branch)
814
+
815
+
816
+ def _dict_to_branch(data: dict[str, Any]) -> NeonBranch:
817
+ """Convert a dict back to NeonBranch."""
818
+ return NeonBranch(**data)
819
+
820
+
821
+ # Timeout for waiting for migrations to complete (seconds)
822
+ _MIGRATION_WAIT_TIMEOUT = 300 # 5 minutes
823
+
824
+
809
825
  @pytest.fixture(scope="session")
810
826
  def _neon_migration_branch(
811
827
  request: pytest.FixtureRequest,
828
+ tmp_path_factory: pytest.TempPathFactory,
812
829
  ) -> Generator[NeonBranch, None, None]:
813
830
  """
814
831
  Session-scoped branch where migrations are applied.
@@ -817,6 +834,13 @@ def _neon_migration_branch(
817
834
  the parent for all test branches. Migrations run once per session
818
835
  on this branch.
819
836
 
837
+ pytest-xdist Support:
838
+ When running with pytest-xdist, the first worker to acquire the lock
839
+ creates the migration branch. Other workers wait for migrations to
840
+ complete, then reuse the same branch. This avoids redundant API calls
841
+ and ensures migrations only run once. Only the creator cleans up the
842
+ branch at session end.
843
+
820
844
  Note: The migration branch cannot have an expiry because Neon doesn't
821
845
  allow creating child branches from branches with expiration dates.
822
846
  Cleanup relies on the fixture teardown at session end.
@@ -826,25 +850,102 @@ def _neon_migration_branch(
826
850
  it on request.config. After migrations run, _neon_branch_for_reset
827
851
  compares the fingerprint to detect if the schema actually changed.
828
852
  """
829
- # No expiry - Neon doesn't allow children from branches with expiry
830
- branch_gen = _create_neon_branch(
831
- request,
832
- branch_expiry_override=0,
833
- branch_name_suffix="-migrated",
853
+ config = request.config
854
+ worker_id = _get_xdist_worker_id()
855
+ is_xdist = worker_id != "main"
856
+
857
+ # Get env var name for DATABASE_URL
858
+ env_var_name = _get_config_value(
859
+ config, "neon_env_var", "", "neon_env_var", "DATABASE_URL"
834
860
  )
835
- branch = next(branch_gen)
836
861
 
837
- # Capture schema fingerprint BEFORE migrations run
838
- # This is stored on config so _neon_branch_for_reset can compare after
839
- pre_migration_fingerprint = _get_schema_fingerprint(branch.connection_string)
840
- request.config._neon_pre_migration_fingerprint = pre_migration_fingerprint # type: ignore[attr-defined]
862
+ # For xdist, use shared temp directory and filelock
863
+ # tmp_path_factory.getbasetemp().parent is shared across all workers
864
+ if is_xdist:
865
+ root_tmp_dir = tmp_path_factory.getbasetemp().parent
866
+ cache_file = root_tmp_dir / "neon_migration_branch.json"
867
+ lock_file = root_tmp_dir / "neon_migration_branch.lock"
868
+ else:
869
+ cache_file = None
870
+ lock_file = None
871
+
872
+ is_creator = False
873
+ branch: NeonBranch
874
+ branch_gen: Generator[NeonBranch, None, None] | None = None
875
+ original_env_value: str | None = None
876
+
877
+ if is_xdist:
878
+ assert cache_file is not None and lock_file is not None
879
+ with FileLock(str(lock_file)):
880
+ if cache_file.exists():
881
+ # Another worker already created the branch - reuse it
882
+ data = json.loads(cache_file.read_text())
883
+ branch = _dict_to_branch(data["branch"])
884
+ pre_migration_fingerprint = tuple(
885
+ tuple(row) for row in data["pre_migration_fingerprint"]
886
+ )
887
+ config._neon_pre_migration_fingerprint = pre_migration_fingerprint # type: ignore[attr-defined]
888
+
889
+ # Set DATABASE_URL for this worker (not done by _create_neon_branch)
890
+ original_env_value = os.environ.get(env_var_name)
891
+ os.environ[env_var_name] = branch.connection_string
892
+ else:
893
+ # First worker - create branch and cache it
894
+ is_creator = True
895
+ branch_gen = _create_neon_branch(
896
+ request,
897
+ branch_expiry_override=0,
898
+ branch_name_suffix="-migrated",
899
+ )
900
+ branch = next(branch_gen)
901
+
902
+ # Capture schema fingerprint BEFORE migrations run
903
+ pre_migration_fingerprint = _get_schema_fingerprint(
904
+ branch.connection_string
905
+ )
906
+ config._neon_pre_migration_fingerprint = pre_migration_fingerprint # type: ignore[attr-defined]
907
+
908
+ # Cache for other workers (they'll read this after lock released)
909
+ # Note: We cache now with pre-migration fingerprint. The branch
910
+ # content will have migrations applied by neon_apply_migrations.
911
+ cache_file.write_text(
912
+ json.dumps(
913
+ {
914
+ "branch": _branch_to_dict(branch),
915
+ "pre_migration_fingerprint": pre_migration_fingerprint,
916
+ }
917
+ )
918
+ )
919
+ else:
920
+ # Not using xdist - create branch normally
921
+ is_creator = True
922
+ branch_gen = _create_neon_branch(
923
+ request,
924
+ branch_expiry_override=0,
925
+ branch_name_suffix="-migrated",
926
+ )
927
+ branch = next(branch_gen)
928
+
929
+ # Capture schema fingerprint BEFORE migrations run
930
+ pre_migration_fingerprint = _get_schema_fingerprint(branch.connection_string)
931
+ config._neon_pre_migration_fingerprint = pre_migration_fingerprint # type: ignore[attr-defined]
932
+
933
+ # Mark whether this worker is the creator (used by neon_apply_migrations)
934
+ config._neon_is_migration_creator = is_creator # type: ignore[attr-defined]
841
935
 
842
936
  try:
843
937
  yield branch
844
938
  finally:
845
- # Clean up by exhausting the generator (triggers branch deletion)
846
- with contextlib.suppress(StopIteration):
847
- next(branch_gen)
939
+ # Restore env var if we set it (non-creator workers)
940
+ if original_env_value is not None:
941
+ os.environ[env_var_name] = original_env_value
942
+ elif not is_creator and env_var_name in os.environ:
943
+ os.environ.pop(env_var_name, None)
944
+
945
+ # Only the creator cleans up the branch
946
+ if is_creator and branch_gen is not None:
947
+ with contextlib.suppress(StopIteration):
948
+ next(branch_gen)
848
949
 
849
950
 
850
951
  @pytest.fixture(scope="session")
@@ -855,6 +956,12 @@ def neon_apply_migrations(_neon_migration_branch: NeonBranch) -> Any:
855
956
  The migration branch is already created and DATABASE_URL is set.
856
957
  Migrations run once per test session, before any tests execute.
857
958
 
959
+ pytest-xdist Support:
960
+ When running with pytest-xdist, migrations only run on the first
961
+ worker (the one that created the migration branch). Other workers
962
+ wait for migrations to complete before proceeding. This ensures
963
+ migrations run exactly once, even with parallel workers.
964
+
858
965
  Smart Migration Detection:
859
966
  The plugin automatically detects whether migrations actually modified
860
967
  the database schema. If no schema changes occurred (or this fixture
@@ -897,11 +1004,67 @@ def neon_apply_migrations(_neon_migration_branch: NeonBranch) -> Any:
897
1004
  return _MIGRATIONS_NOT_DEFINED
898
1005
 
899
1006
 
1007
+ @pytest.fixture(scope="session")
1008
+ def _neon_migrations_synchronized(
1009
+ request: pytest.FixtureRequest,
1010
+ tmp_path_factory: pytest.TempPathFactory,
1011
+ _neon_migration_branch: NeonBranch,
1012
+ neon_apply_migrations: Any,
1013
+ ) -> Any:
1014
+ """
1015
+ Internal fixture that synchronizes migrations across xdist workers.
1016
+
1017
+ This fixture ensures that:
1018
+ 1. Only the creator worker runs migrations
1019
+ 2. Other workers wait for migrations to complete before proceeding
1020
+ 3. The return value from neon_apply_migrations is preserved for detection
1021
+
1022
+ Without xdist, this is a simple passthrough.
1023
+ """
1024
+ config = request.config
1025
+ worker_id = _get_xdist_worker_id()
1026
+ is_xdist = worker_id != "main"
1027
+ is_creator = getattr(config, "_neon_is_migration_creator", True)
1028
+
1029
+ if not is_xdist:
1030
+ # Not using xdist - migrations already ran, just return the value
1031
+ return neon_apply_migrations
1032
+
1033
+ # For xdist, use a signal file to coordinate
1034
+ root_tmp_dir = tmp_path_factory.getbasetemp().parent
1035
+ migrations_done_file = root_tmp_dir / "neon_migrations_done"
1036
+ migrations_lock_file = root_tmp_dir / "neon_migrations.lock"
1037
+
1038
+ if is_creator:
1039
+ # Creator: migrations just ran via neon_apply_migrations dependency
1040
+ # Signal completion to other workers
1041
+ with FileLock(str(migrations_lock_file)):
1042
+ migrations_done_file.write_text("done")
1043
+ return neon_apply_migrations
1044
+ else:
1045
+ # Non-creator: wait for migrations to complete
1046
+ # The neon_apply_migrations fixture still runs but on already-migrated DB
1047
+ # (most migration tools handle this gracefully as a no-op)
1048
+ waited = 0.0
1049
+ poll_interval = 0.5
1050
+ while not migrations_done_file.exists():
1051
+ if waited >= _MIGRATION_WAIT_TIMEOUT:
1052
+ raise RuntimeError(
1053
+ f"Timeout waiting for migrations to complete after "
1054
+ f"{_MIGRATION_WAIT_TIMEOUT}s. The creator worker may have "
1055
+ f"failed or is still running migrations."
1056
+ )
1057
+ time.sleep(poll_interval)
1058
+ waited += poll_interval
1059
+
1060
+ return neon_apply_migrations
1061
+
1062
+
900
1063
  @pytest.fixture(scope="session")
901
1064
  def _neon_branch_for_reset(
902
1065
  request: pytest.FixtureRequest,
903
1066
  _neon_migration_branch: NeonBranch,
904
- neon_apply_migrations: Any, # Ensures migrations run first; value for detection
1067
+ _neon_migrations_synchronized: Any, # Ensures migrations complete; for detection
905
1068
  ) -> Generator[NeonBranch, None, None]:
906
1069
  """
907
1070
  Internal fixture that creates a test branch from the migration branch.
@@ -931,7 +1094,8 @@ def _neon_branch_for_reset(
931
1094
  - Migrations exist but are already applied (no schema changes)
932
1095
  """
933
1096
  # Check if migrations fixture was overridden
934
- migrations_defined = neon_apply_migrations is not _MIGRATIONS_NOT_DEFINED
1097
+ # _neon_migrations_synchronized passes through the neon_apply_migrations value
1098
+ migrations_defined = _neon_migrations_synchronized is not _MIGRATIONS_NOT_DEFINED
935
1099
 
936
1100
  # Check if schema actually changed (if we have a pre-migration fingerprint)
937
1101
  pre_fingerprint = getattr(request.config, "_neon_pre_migration_fingerprint", ())
@@ -1,5 +1,54 @@
1
1
  """Tests for pytest-xdist parallel worker support."""
2
2
 
3
+ import json
4
+
5
+ from pytest_neon.plugin import (
6
+ NeonBranch,
7
+ _branch_to_dict,
8
+ _dict_to_branch,
9
+ )
10
+
11
+
12
+ class TestBranchSerialization:
13
+ """Test NeonBranch serialization for cache file."""
14
+
15
+ def test_branch_round_trip(self):
16
+ """Test that branch can be serialized and deserialized."""
17
+ branch = NeonBranch(
18
+ branch_id="br-test-123",
19
+ project_id="proj-abc",
20
+ connection_string="postgresql://user:pass@host/db",
21
+ host="host.neon.tech",
22
+ parent_id="br-parent-456",
23
+ )
24
+
25
+ data = _branch_to_dict(branch)
26
+ restored = _dict_to_branch(data)
27
+
28
+ assert restored.branch_id == branch.branch_id
29
+ assert restored.project_id == branch.project_id
30
+ assert restored.connection_string == branch.connection_string
31
+ assert restored.host == branch.host
32
+ assert restored.parent_id == branch.parent_id
33
+
34
+ def test_branch_to_dict_is_json_serializable(self):
35
+ """Test that branch dict can be JSON serialized."""
36
+ branch = NeonBranch(
37
+ branch_id="br-test-123",
38
+ project_id="proj-abc",
39
+ connection_string="postgresql://user:pass@host/db",
40
+ host="host.neon.tech",
41
+ parent_id=None,
42
+ )
43
+
44
+ data = _branch_to_dict(branch)
45
+ json_str = json.dumps(data)
46
+ restored_data = json.loads(json_str)
47
+ restored = _dict_to_branch(restored_data)
48
+
49
+ assert restored.branch_id == branch.branch_id
50
+ assert restored.parent_id is None
51
+
3
52
 
4
53
  class TestXdistBranchIsolation:
5
54
  """Test that parallel workers get separate branches."""
@@ -376,6 +376,30 @@ wheels = [
376
376
  { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
377
377
  ]
378
378
 
379
+ [[package]]
380
+ name = "filelock"
381
+ version = "3.19.1"
382
+ source = { registry = "https://pypi.org/simple" }
383
+ resolution-markers = [
384
+ "python_full_version < '3.10'",
385
+ ]
386
+ sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" }
387
+ wheels = [
388
+ { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" },
389
+ ]
390
+
391
+ [[package]]
392
+ name = "filelock"
393
+ version = "3.20.3"
394
+ source = { registry = "https://pypi.org/simple" }
395
+ resolution-markers = [
396
+ "python_full_version >= '3.10'",
397
+ ]
398
+ sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" }
399
+ wheels = [
400
+ { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" },
401
+ ]
402
+
379
403
  [[package]]
380
404
  name = "greenlet"
381
405
  version = "3.2.4"
@@ -388,7 +412,6 @@ wheels = [
388
412
  { url = "https://files.pythonhosted.org/packages/7d/ed/6bfa4109fcb23a58819600392564fea69cdc6551ffd5e69ccf1d52a40cbc/greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c", size = 271061, upload-time = "2025-08-07T13:17:15.373Z" },
389
413
  { url = "https://files.pythonhosted.org/packages/2a/fc/102ec1a2fc015b3a7652abab7acf3541d58c04d3d17a8d3d6a44adae1eb1/greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590", size = 629475, upload-time = "2025-08-07T13:42:54.009Z" },
390
414
  { url = "https://files.pythonhosted.org/packages/c5/26/80383131d55a4ac0fb08d71660fd77e7660b9db6bdb4e8884f46d9f2cc04/greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c", size = 640802, upload-time = "2025-08-07T13:45:25.52Z" },
391
- { url = "https://files.pythonhosted.org/packages/9f/7c/e7833dbcd8f376f3326bd728c845d31dcde4c84268d3921afcae77d90d08/greenlet-3.2.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c8c9e331e58180d0d83c5b7999255721b725913ff6bc6cf39fa2a45841a4fd4b", size = 636703, upload-time = "2025-08-07T13:53:12.622Z" },
392
415
  { url = "https://files.pythonhosted.org/packages/e9/49/547b93b7c0428ede7b3f309bc965986874759f7d89e4e04aeddbc9699acb/greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31", size = 635417, upload-time = "2025-08-07T13:18:25.189Z" },
393
416
  { url = "https://files.pythonhosted.org/packages/7f/91/ae2eb6b7979e2f9b035a9f612cf70f1bf54aad4e1d125129bef1eae96f19/greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d", size = 584358, upload-time = "2025-08-07T13:18:23.708Z" },
394
417
  { url = "https://files.pythonhosted.org/packages/f7/85/433de0c9c0252b22b16d413c9407e6cb3b41df7389afc366ca204dbc1393/greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5", size = 1113550, upload-time = "2025-08-07T13:42:37.467Z" },
@@ -399,7 +422,6 @@ wheels = [
399
422
  { url = "https://files.pythonhosted.org/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2", size = 272305, upload-time = "2025-08-07T13:15:41.288Z" },
400
423
  { url = "https://files.pythonhosted.org/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246", size = 632472, upload-time = "2025-08-07T13:42:55.044Z" },
401
424
  { url = "https://files.pythonhosted.org/packages/ae/8f/95d48d7e3d433e6dae5b1682e4292242a53f22df82e6d3dda81b1701a960/greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3", size = 644646, upload-time = "2025-08-07T13:45:26.523Z" },
402
- { url = "https://files.pythonhosted.org/packages/d5/5e/405965351aef8c76b8ef7ad370e5da58d57ef6068df197548b015464001a/greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633", size = 640519, upload-time = "2025-08-07T13:53:13.928Z" },
403
425
  { url = "https://files.pythonhosted.org/packages/25/5d/382753b52006ce0218297ec1b628e048c4e64b155379331f25a7316eb749/greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079", size = 639707, upload-time = "2025-08-07T13:18:27.146Z" },
404
426
  { url = "https://files.pythonhosted.org/packages/1f/8e/abdd3f14d735b2929290a018ecf133c901be4874b858dd1c604b9319f064/greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8", size = 587684, upload-time = "2025-08-07T13:18:25.164Z" },
405
427
  { url = "https://files.pythonhosted.org/packages/5d/65/deb2a69c3e5996439b0176f6651e0052542bb6c8f8ec2e3fba97c9768805/greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52", size = 1116647, upload-time = "2025-08-07T13:42:38.655Z" },
@@ -410,7 +432,6 @@ wheels = [
410
432
  { url = "https://files.pythonhosted.org/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079, upload-time = "2025-08-07T13:15:45.033Z" },
411
433
  { url = "https://files.pythonhosted.org/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997, upload-time = "2025-08-07T13:42:56.234Z" },
412
434
  { url = "https://files.pythonhosted.org/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968", size = 655185, upload-time = "2025-08-07T13:45:27.624Z" },
413
- { url = "https://files.pythonhosted.org/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9", size = 649926, upload-time = "2025-08-07T13:53:15.251Z" },
414
435
  { url = "https://files.pythonhosted.org/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6", size = 651839, upload-time = "2025-08-07T13:18:30.281Z" },
415
436
  { url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586, upload-time = "2025-08-07T13:18:28.544Z" },
416
437
  { url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281, upload-time = "2025-08-07T13:42:39.858Z" },
@@ -421,7 +442,6 @@ wheels = [
421
442
  { url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814, upload-time = "2025-08-07T13:15:50.011Z" },
422
443
  { url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073, upload-time = "2025-08-07T13:42:57.23Z" },
423
444
  { url = "https://files.pythonhosted.org/packages/f7/0b/bc13f787394920b23073ca3b6c4a7a21396301ed75a655bcb47196b50e6e/greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc", size = 655191, upload-time = "2025-08-07T13:45:29.752Z" },
424
- { url = "https://files.pythonhosted.org/packages/f2/d6/6adde57d1345a8d0f14d31e4ab9c23cfe8e2cd39c3baf7674b4b0338d266/greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a", size = 649516, upload-time = "2025-08-07T13:53:16.314Z" },
425
445
  { url = "https://files.pythonhosted.org/packages/7f/3b/3a3328a788d4a473889a2d403199932be55b1b0060f4ddd96ee7cdfcad10/greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504", size = 652169, upload-time = "2025-08-07T13:18:32.861Z" },
426
446
  { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497, upload-time = "2025-08-07T13:18:31.636Z" },
427
447
  { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662, upload-time = "2025-08-07T13:42:41.117Z" },
@@ -432,7 +452,6 @@ wheels = [
432
452
  { url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586, upload-time = "2025-08-07T13:16:08.004Z" },
433
453
  { url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346, upload-time = "2025-08-07T13:42:59.944Z" },
434
454
  { url = "https://files.pythonhosted.org/packages/c0/aa/687d6b12ffb505a4447567d1f3abea23bd20e73a5bed63871178e0831b7a/greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5", size = 699218, upload-time = "2025-08-07T13:45:30.969Z" },
435
- { url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659, upload-time = "2025-08-07T13:53:17.759Z" },
436
455
  { url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355, upload-time = "2025-08-07T13:18:34.517Z" },
437
456
  { url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512, upload-time = "2025-08-07T13:18:33.969Z" },
438
457
  { url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508, upload-time = "2025-11-04T12:42:23.427Z" },
@@ -441,7 +460,6 @@ wheels = [
441
460
  { url = "https://files.pythonhosted.org/packages/f7/c0/93885c4106d2626bf51fdec377d6aef740dfa5c4877461889a7cf8e565cc/greenlet-3.2.4-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:b6a7c19cf0d2742d0809a4c05975db036fdff50cd294a93632d6a310bf9ac02c", size = 269859, upload-time = "2025-08-07T13:16:16.003Z" },
442
461
  { url = "https://files.pythonhosted.org/packages/4d/f5/33f05dc3ba10a02dedb1485870cf81c109227d3d3aa280f0e48486cac248/greenlet-3.2.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:27890167f55d2387576d1f41d9487ef171849ea0359ce1510ca6e06c8bece11d", size = 627610, upload-time = "2025-08-07T13:43:01.345Z" },
443
462
  { url = "https://files.pythonhosted.org/packages/b2/a7/9476decef51a0844195f99ed5dc611d212e9b3515512ecdf7321543a7225/greenlet-3.2.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:18d9260df2b5fbf41ae5139e1be4e796d99655f023a636cd0e11e6406cca7d58", size = 639417, upload-time = "2025-08-07T13:45:32.094Z" },
444
- { url = "https://files.pythonhosted.org/packages/bd/e0/849b9159cbb176f8c0af5caaff1faffdece7a8417fcc6fe1869770e33e21/greenlet-3.2.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:671df96c1f23c4a0d4077a325483c1503c96a1b7d9db26592ae770daa41233d4", size = 634751, upload-time = "2025-08-07T13:53:18.848Z" },
445
463
  { url = "https://files.pythonhosted.org/packages/5f/d3/844e714a9bbd39034144dca8b658dcd01839b72bb0ec7d8014e33e3705f0/greenlet-3.2.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:16458c245a38991aa19676900d48bd1a6f2ce3e16595051a4db9d012154e8433", size = 634020, upload-time = "2025-08-07T13:18:36.841Z" },
446
464
  { url = "https://files.pythonhosted.org/packages/6b/4c/f3de2a8de0e840ecb0253ad0dc7e2bb3747348e798ec7e397d783a3cb380/greenlet-3.2.4-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9913f1a30e4526f432991f89ae263459b1c64d1608c0d22a5c79c287b3c70df", size = 582817, upload-time = "2025-08-07T13:18:35.48Z" },
447
465
  { url = "https://files.pythonhosted.org/packages/89/80/7332915adc766035c8980b161c2e5d50b2f941f453af232c164cff5e0aeb/greenlet-3.2.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b90654e092f928f110e0007f572007c9727b5265f7632c2fa7415b4689351594", size = 1111985, upload-time = "2025-08-07T13:42:42.425Z" },
@@ -464,7 +482,6 @@ wheels = [
464
482
  { url = "https://files.pythonhosted.org/packages/32/6a/33d1702184d94106d3cdd7bfb788e19723206fce152e303473ca3b946c7b/greenlet-3.3.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f8496d434d5cb2dce025773ba5597f71f5410ae499d5dd9533e0653258cdb3d", size = 273658, upload-time = "2025-12-04T14:23:37.494Z" },
465
483
  { url = "https://files.pythonhosted.org/packages/d6/b7/2b5805bbf1907c26e434f4e448cd8b696a0b71725204fa21a211ff0c04a7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b96dc7eef78fd404e022e165ec55327f935b9b52ff355b067eb4a0267fc1cffb", size = 574810, upload-time = "2025-12-04T14:50:04.154Z" },
466
484
  { url = "https://files.pythonhosted.org/packages/94/38/343242ec12eddf3d8458c73f555c084359883d4ddc674240d9e61ec51fd6/greenlet-3.3.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73631cd5cccbcfe63e3f9492aaa664d278fda0ce5c3d43aeda8e77317e38efbd", size = 586248, upload-time = "2025-12-04T14:57:39.35Z" },
467
- { url = "https://files.pythonhosted.org/packages/f0/d0/0ae86792fb212e4384041e0ef8e7bc66f59a54912ce407d26a966ed2914d/greenlet-3.3.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b299a0cb979f5d7197442dccc3aee67fce53500cd88951b7e6c35575701c980b", size = 597403, upload-time = "2025-12-04T15:07:10.831Z" },
468
485
  { url = "https://files.pythonhosted.org/packages/b6/a8/15d0aa26c0036a15d2659175af00954aaaa5d0d66ba538345bd88013b4d7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dee147740789a4632cace364816046e43310b59ff8fb79833ab043aefa72fd5", size = 586910, upload-time = "2025-12-04T14:25:59.705Z" },
469
486
  { url = "https://files.pythonhosted.org/packages/e1/9b/68d5e3b7ccaba3907e5532cf8b9bf16f9ef5056a008f195a367db0ff32db/greenlet-3.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:39b28e339fc3c348427560494e28d8a6f3561c8d2bcf7d706e1c624ed8d822b9", size = 1547206, upload-time = "2025-12-04T15:04:21.027Z" },
470
487
  { url = "https://files.pythonhosted.org/packages/66/bd/e3086ccedc61e49f91e2cfb5ffad9d8d62e5dc85e512a6200f096875b60c/greenlet-3.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3c374782c2935cc63b2a27ba8708471de4ad1abaa862ffdb1ef45a643ddbb7d", size = 1613359, upload-time = "2025-12-04T14:27:26.548Z" },
@@ -472,7 +489,6 @@ wheels = [
472
489
  { url = "https://files.pythonhosted.org/packages/1f/cb/48e964c452ca2b92175a9b2dca037a553036cb053ba69e284650ce755f13/greenlet-3.3.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e29f3018580e8412d6aaf5641bb7745d38c85228dacf51a73bd4e26ddf2a6a8e", size = 274908, upload-time = "2025-12-04T14:23:26.435Z" },
473
490
  { url = "https://files.pythonhosted.org/packages/28/da/38d7bff4d0277b594ec557f479d65272a893f1f2a716cad91efeb8680953/greenlet-3.3.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a687205fb22794e838f947e2194c0566d3812966b41c78709554aa883183fb62", size = 577113, upload-time = "2025-12-04T14:50:05.493Z" },
474
491
  { url = "https://files.pythonhosted.org/packages/3c/f2/89c5eb0faddc3ff014f1c04467d67dee0d1d334ab81fadbf3744847f8a8a/greenlet-3.3.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4243050a88ba61842186cb9e63c7dfa677ec146160b0efd73b855a3d9c7fcf32", size = 590338, upload-time = "2025-12-04T14:57:41.136Z" },
475
- { url = "https://files.pythonhosted.org/packages/80/d7/db0a5085035d05134f8c089643da2b44cc9b80647c39e93129c5ef170d8f/greenlet-3.3.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:670d0f94cd302d81796e37299bcd04b95d62403883b24225c6b5271466612f45", size = 601098, upload-time = "2025-12-04T15:07:11.898Z" },
476
492
  { url = "https://files.pythonhosted.org/packages/dc/a6/e959a127b630a58e23529972dbc868c107f9d583b5a9f878fb858c46bc1a/greenlet-3.3.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb3a8ec3db4a3b0eb8a3c25436c2d49e3505821802074969db017b87bc6a948", size = 590206, upload-time = "2025-12-04T14:26:01.254Z" },
477
493
  { url = "https://files.pythonhosted.org/packages/48/60/29035719feb91798693023608447283b266b12efc576ed013dd9442364bb/greenlet-3.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2de5a0b09eab81fc6a382791b995b1ccf2b172a9fec934747a7a23d2ff291794", size = 1550668, upload-time = "2025-12-04T15:04:22.439Z" },
478
494
  { url = "https://files.pythonhosted.org/packages/0a/5f/783a23754b691bfa86bd72c3033aa107490deac9b2ef190837b860996c9f/greenlet-3.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4449a736606bd30f27f8e1ff4678ee193bc47f6ca810d705981cfffd6ce0d8c5", size = 1615483, upload-time = "2025-12-04T14:27:28.083Z" },
@@ -480,7 +496,6 @@ wheels = [
480
496
  { url = "https://files.pythonhosted.org/packages/f8/0a/a3871375c7b9727edaeeea994bfff7c63ff7804c9829c19309ba2e058807/greenlet-3.3.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:b01548f6e0b9e9784a2c99c5651e5dc89ffcbe870bc5fb2e5ef864e9cc6b5dcb", size = 276379, upload-time = "2025-12-04T14:23:30.498Z" },
481
497
  { url = "https://files.pythonhosted.org/packages/43/ab/7ebfe34dce8b87be0d11dae91acbf76f7b8246bf9d6b319c741f99fa59c6/greenlet-3.3.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:349345b770dc88f81506c6861d22a6ccd422207829d2c854ae2af8025af303e3", size = 597294, upload-time = "2025-12-04T14:50:06.847Z" },
482
498
  { url = "https://files.pythonhosted.org/packages/a4/39/f1c8da50024feecd0793dbd5e08f526809b8ab5609224a2da40aad3a7641/greenlet-3.3.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e8e18ed6995e9e2c0b4ed264d2cf89260ab3ac7e13555b8032b25a74c6d18655", size = 607742, upload-time = "2025-12-04T14:57:42.349Z" },
483
- { url = "https://files.pythonhosted.org/packages/77/cb/43692bcd5f7a0da6ec0ec6d58ee7cddb606d055ce94a62ac9b1aa481e969/greenlet-3.3.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c024b1e5696626890038e34f76140ed1daf858e37496d33f2af57f06189e70d7", size = 622297, upload-time = "2025-12-04T15:07:13.552Z" },
484
499
  { url = "https://files.pythonhosted.org/packages/75/b0/6bde0b1011a60782108c01de5913c588cf51a839174538d266de15e4bf4d/greenlet-3.3.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:047ab3df20ede6a57c35c14bf5200fcf04039d50f908270d3f9a7a82064f543b", size = 609885, upload-time = "2025-12-04T14:26:02.368Z" },
485
500
  { url = "https://files.pythonhosted.org/packages/49/0e/49b46ac39f931f59f987b7cd9f34bfec8ef81d2a1e6e00682f55be5de9f4/greenlet-3.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2d9ad37fc657b1102ec880e637cccf20191581f75c64087a549e66c57e1ceb53", size = 1567424, upload-time = "2025-12-04T15:04:23.757Z" },
486
501
  { url = "https://files.pythonhosted.org/packages/05/f5/49a9ac2dff7f10091935def9165c90236d8f175afb27cbed38fb1d61ab6b/greenlet-3.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83cd0e36932e0e7f36a64b732a6f60c2fc2df28c351bae79fbaf4f8092fe7614", size = 1636017, upload-time = "2025-12-04T14:27:29.688Z" },
@@ -488,7 +503,6 @@ wheels = [
488
503
  { url = "https://files.pythonhosted.org/packages/02/2f/28592176381b9ab2cafa12829ba7b472d177f3acc35d8fbcf3673d966fff/greenlet-3.3.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:a1e41a81c7e2825822f4e068c48cb2196002362619e2d70b148f20a831c00739", size = 275140, upload-time = "2025-12-04T14:23:01.282Z" },
489
504
  { url = "https://files.pythonhosted.org/packages/2c/80/fbe937bf81e9fca98c981fe499e59a3f45df2a04da0baa5c2be0dca0d329/greenlet-3.3.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f515a47d02da4d30caaa85b69474cec77b7929b2e936ff7fb853d42f4bf8808", size = 599219, upload-time = "2025-12-04T14:50:08.309Z" },
490
505
  { url = "https://files.pythonhosted.org/packages/c2/ff/7c985128f0514271b8268476af89aee6866df5eec04ac17dcfbc676213df/greenlet-3.3.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d2d9fd66bfadf230b385fdc90426fcd6eb64db54b40c495b72ac0feb5766c54", size = 610211, upload-time = "2025-12-04T14:57:43.968Z" },
491
- { url = "https://files.pythonhosted.org/packages/79/07/c47a82d881319ec18a4510bb30463ed6891f2ad2c1901ed5ec23d3de351f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30a6e28487a790417d036088b3bcb3f3ac7d8babaa7d0139edbaddebf3af9492", size = 624311, upload-time = "2025-12-04T15:07:14.697Z" },
492
506
  { url = "https://files.pythonhosted.org/packages/fd/8e/424b8c6e78bd9837d14ff7df01a9829fc883ba2ab4ea787d4f848435f23f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:087ea5e004437321508a8d6f20efc4cfec5e3c30118e1417ea96ed1d93950527", size = 612833, upload-time = "2025-12-04T14:26:03.669Z" },
493
507
  { url = "https://files.pythonhosted.org/packages/b5/ba/56699ff9b7c76ca12f1cdc27a886d0f81f2189c3455ff9f65246780f713d/greenlet-3.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab97cf74045343f6c60a39913fa59710e4bd26a536ce7ab2397adf8b27e67c39", size = 1567256, upload-time = "2025-12-04T15:04:25.276Z" },
494
508
  { url = "https://files.pythonhosted.org/packages/1e/37/f31136132967982d698c71a281a8901daf1a8fbab935dce7c0cf15f942cc/greenlet-3.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5375d2e23184629112ca1ea89a53389dddbffcf417dad40125713d88eb5f96e8", size = 1636483, upload-time = "2025-12-04T14:27:30.804Z" },
@@ -496,7 +510,6 @@ wheels = [
496
510
  { url = "https://files.pythonhosted.org/packages/d7/7c/f0a6d0ede2c7bf092d00bc83ad5bafb7e6ec9b4aab2fbdfa6f134dc73327/greenlet-3.3.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:60c2ef0f578afb3c8d92ea07ad327f9a062547137afe91f38408f08aacab667f", size = 275671, upload-time = "2025-12-04T14:23:05.267Z" },
497
511
  { url = "https://files.pythonhosted.org/packages/44/06/dac639ae1a50f5969d82d2e3dd9767d30d6dbdbab0e1a54010c8fe90263c/greenlet-3.3.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a5d554d0712ba1de0a6c94c640f7aeba3f85b3a6e1f2899c11c2c0428da9365", size = 646360, upload-time = "2025-12-04T14:50:10.026Z" },
498
512
  { url = "https://files.pythonhosted.org/packages/e0/94/0fb76fe6c5369fba9bf98529ada6f4c3a1adf19e406a47332245ef0eb357/greenlet-3.3.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3a898b1e9c5f7307ebbde4102908e6cbfcb9ea16284a3abe15cab996bee8b9b3", size = 658160, upload-time = "2025-12-04T14:57:45.41Z" },
499
- { url = "https://files.pythonhosted.org/packages/93/79/d2c70cae6e823fac36c3bbc9077962105052b7ef81db2f01ec3b9bf17e2b/greenlet-3.3.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dcd2bdbd444ff340e8d6bdf54d2f206ccddbb3ccfdcd3c25bf4afaa7b8f0cf45", size = 671388, upload-time = "2025-12-04T15:07:15.789Z" },
500
513
  { url = "https://files.pythonhosted.org/packages/b8/14/bab308fc2c1b5228c3224ec2bf928ce2e4d21d8046c161e44a2012b5203e/greenlet-3.3.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5773edda4dc00e173820722711d043799d3adb4f01731f40619e07ea2750b955", size = 660166, upload-time = "2025-12-04T14:26:05.099Z" },
501
514
  { url = "https://files.pythonhosted.org/packages/4b/d2/91465d39164eaa0085177f61983d80ffe746c5a1860f009811d498e7259c/greenlet-3.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ac0549373982b36d5fd5d30beb8a7a33ee541ff98d2b502714a09f1169f31b55", size = 1615193, upload-time = "2025-12-04T15:04:27.041Z" },
502
515
  { url = "https://files.pythonhosted.org/packages/42/1b/83d110a37044b92423084d52d5d5a3b3a73cafb51b547e6d7366ff62eff1/greenlet-3.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d198d2d977460358c3b3a4dc844f875d1adb33817f0613f663a656f463764ccc", size = 1683653, upload-time = "2025-12-04T14:27:32.366Z" },
@@ -504,7 +517,6 @@ wheels = [
504
517
  { url = "https://files.pythonhosted.org/packages/a0/66/bd6317bc5932accf351fc19f177ffba53712a202f9df10587da8df257c7e/greenlet-3.3.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d6ed6f85fae6cdfdb9ce04c9bf7a08d666cfcfb914e7d006f44f840b46741931", size = 282638, upload-time = "2025-12-04T14:25:20.941Z" },
505
518
  { url = "https://files.pythonhosted.org/packages/30/cf/cc81cb030b40e738d6e69502ccbd0dd1bced0588e958f9e757945de24404/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9125050fcf24554e69c4cacb086b87b3b55dc395a8b3ebe6487b045b2614388", size = 651145, upload-time = "2025-12-04T14:50:11.039Z" },
506
519
  { url = "https://files.pythonhosted.org/packages/9c/ea/1020037b5ecfe95ca7df8d8549959baceb8186031da83d5ecceff8b08cd2/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:87e63ccfa13c0a0f6234ed0add552af24cc67dd886731f2261e46e241608bee3", size = 654236, upload-time = "2025-12-04T14:57:47.007Z" },
507
- { url = "https://files.pythonhosted.org/packages/69/cc/1e4bae2e45ca2fa55299f4e85854606a78ecc37fead20d69322f96000504/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2662433acbca297c9153a4023fe2161c8dcfdcc91f10433171cf7e7d94ba2221", size = 662506, upload-time = "2025-12-04T15:07:16.906Z" },
508
520
  { url = "https://files.pythonhosted.org/packages/57/b9/f8025d71a6085c441a7eaff0fd928bbb275a6633773667023d19179fe815/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c6e9b9c1527a78520357de498b0e709fb9e2f49c3a513afd5a249007261911b", size = 653783, upload-time = "2025-12-04T14:26:06.225Z" },
509
521
  { url = "https://files.pythonhosted.org/packages/f6/c7/876a8c7a7485d5d6b5c6821201d542ef28be645aa024cfe1145b35c120c1/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:286d093f95ec98fdd92fcb955003b8a3d054b4e2cab3e2707a5039e7b50520fd", size = 1614857, upload-time = "2025-12-04T15:04:28.484Z" },
510
522
  { url = "https://files.pythonhosted.org/packages/4f/dc/041be1dff9f23dac5f48a43323cd0789cb798342011c19a248d9c9335536/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c10513330af5b8ae16f023e8ddbfb486ab355d04467c4679c5cfe4659975dd9", size = 1676034, upload-time = "2025-12-04T14:27:33.531Z" },
@@ -1200,9 +1212,11 @@ wheels = [
1200
1212
 
1201
1213
  [[package]]
1202
1214
  name = "pytest-neon"
1203
- version = "2.1.2"
1215
+ version = "2.2.1"
1204
1216
  source = { editable = "." }
1205
1217
  dependencies = [
1218
+ { name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
1219
+ { name = "filelock", version = "3.20.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
1206
1220
  { name = "neon-api" },
1207
1221
  { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
1208
1222
  { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
@@ -1233,6 +1247,7 @@ sqlalchemy = [
1233
1247
 
1234
1248
  [package.metadata]
1235
1249
  requires-dist = [
1250
+ { name = "filelock", specifier = ">=3.0" },
1236
1251
  { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.0" },
1237
1252
  { name = "neon-api", specifier = ">=0.1.0" },
1238
1253
  { name = "psycopg", extras = ["binary"], marker = "extra == 'dev'", specifier = ">=3.1" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes