Skip to content

Commit 2a5bf0a

Browse files
committed
Merge branch 'feat/pytest-container-management' of https://github.com/d-v-b/datajoint-python into feat/djzarr
2 parents 09c4f53 + de13442 commit 2a5bf0a

File tree

6 files changed

+2801
-0
lines changed

6 files changed

+2801
-0
lines changed

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SCM syntax highlighting & preventing 3-way merges
2+
pixi.lock merge=binary linguist-language=YAML linguist-generated=true

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,5 @@ cython_debug/
185185
dj_local_conf.json
186186
*.env
187187
!.vscode/launch.json
188+
# pixi environments
189+
.pixi

pixi.lock

Lines changed: 2712 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,10 @@ datajoint = "datajoint.cli:cli"
8484
test = [
8585
"pytest",
8686
"pytest-cov",
87+
"pytest-env",
8788
"docker",
8889
"requests",
90+
"graphviz"
8991
]
9092

9193
[project.optional-dependencies]
@@ -109,3 +111,33 @@ package-dir = {"" = "src"}
109111

110112
[tool.setuptools.dynamic]
111113
version = { attr = "datajoint.version.__version__"}
114+
115+
[tool.pytest_env]
116+
# Default values - pytest fixtures will override with actual container details
117+
DJ_USER="root"
118+
DJ_PASS="password"
119+
DJ_TEST_USER="datajoint"
120+
DJ_TEST_PASSWORD="datajoint"
121+
S3_ACCESS_KEY="datajoint"
122+
S3_SECRET_KEY="datajoint"
123+
S3_BUCKET="datajoint.test"
124+
PYTHON_USER="dja"
125+
JUPYTER_PASSWORD="datajoint"
126+
127+
128+
[tool.pixi.workspace]
129+
channels = ["conda-forge"]
130+
platforms = ["linux-64"]
131+
132+
[tool.pixi.pypi-dependencies]
133+
datajoint = { path = ".", editable = true }
134+
135+
[tool.pixi.environments]
136+
default = { solve-group = "default" }
137+
dev = { features = ["dev"], solve-group = "default" }
138+
test = { features = ["test"], solve-group = "default" }
139+
140+
[tool.pixi.tasks]
141+
142+
[tool.pixi.dependencies]
143+
graphviz = ">=13.1.2,<14"

src/datajoint/settings.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ def __setitem__(self, key, value):
318318
"database.host",
319319
"database.user",
320320
"database.password",
321+
"database.port",
321322
"external.aws_access_key_id",
322323
"external.aws_secret_access_key",
323324
"loglevel",
@@ -328,6 +329,7 @@ def __setitem__(self, key, value):
328329
"DJ_HOST",
329330
"DJ_USER",
330331
"DJ_PASS",
332+
"DJ_PORT",
331333
"DJ_AWS_ACCESS_KEY_ID",
332334
"DJ_AWS_SECRET_ACCESS_KEY",
333335
"DJ_LOG_LEVEL",
@@ -336,6 +338,14 @@ def __setitem__(self, key, value):
336338
)
337339
if v is not None
338340
}
341+
342+
# Convert DJ_PORT from string to int if present
343+
if "database.port" in mapping and mapping["database.port"] is not None:
344+
try:
345+
mapping["database.port"] = int(mapping["database.port"])
346+
except ValueError:
347+
logger.warning(f"Invalid DJ_PORT value: {mapping['database.port']}, using default port 3306")
348+
del mapping["database.port"]
339349
if mapping:
340350
logger.info(f"Overloaded settings {tuple(mapping)} from environment variables.")
341351
config.update(mapping)

tests/conftest.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@
3434
logger = logging.getLogger(__name__)
3535

3636

37+
def pytest_sessionstart(session):
38+
"""Called after the Session object has been created and configured."""
39+
# This runs very early, before most fixtures, but we don't have container info yet
40+
pass
41+
42+
43+
def pytest_configure(config):
44+
"""Called after command line options have been parsed."""
45+
# This runs before pytest_sessionstart but still too early for containers
46+
pass
47+
48+
3749

3850

3951
# Global container registry for cleanup
@@ -298,13 +310,42 @@ def enable_filepath_feature(monkeypatch):
298310
@pytest.fixture(scope="session")
299311
def db_creds_test(mysql_container) -> Dict:
300312
_, host, port = mysql_container
313+
# Set environment variables for DataJoint at module level
314+
os.environ["DJ_TEST_HOST"] = host
315+
os.environ["DJ_TEST_PORT"] = str(port)
316+
317+
# Also update DataJoint's test configuration directly
318+
dj.config["database.test.host"] = host
319+
dj.config["database.test.port"] = port
320+
301321
return dict(
302322
host=f"{host}:{port}",
303323
user=os.getenv("DJ_TEST_USER", "datajoint"),
304324
password=os.getenv("DJ_TEST_PASSWORD", "datajoint"),
305325
)
306326

307327

328+
@pytest.fixture(scope="session", autouse=True)
329+
def configure_datajoint_for_containers(mysql_container):
330+
"""Configure DataJoint to use pytest-managed containers. Runs automatically for all tests."""
331+
_, host, port = mysql_container
332+
333+
# Set environment variables FIRST - these will be inherited by subprocesses
334+
logger.info(f"🔧 Setting environment: DJ_HOST={host}, DJ_PORT={port}")
335+
os.environ["DJ_HOST"] = host
336+
os.environ["DJ_PORT"] = str(port)
337+
338+
# Verify the environment variables were set
339+
logger.info(f"🔧 Environment after setting: DJ_HOST={os.environ.get('DJ_HOST')}, DJ_PORT={os.environ.get('DJ_PORT')}")
340+
341+
# Also update DataJoint's configuration directly for in-process connections
342+
dj.config["database.host"] = host
343+
dj.config["database.port"] = port
344+
345+
logger.info(f"🔧 Configured DataJoint to use MySQL container at {host}:{port}")
346+
return host, port # Return values so other fixtures can use them
347+
348+
308349
@pytest.fixture(scope="session")
309350
def db_creds_root(mysql_container) -> Dict:
310351
_, host, port = mysql_container
@@ -434,6 +475,8 @@ def connection_test(connection_root, prefix, db_creds_test):
434475
@pytest.fixture(scope="session")
435476
def s3_creds(minio_container) -> Dict:
436477
_, host, port = minio_container
478+
# Set environment variable for S3 endpoint at module level
479+
os.environ["S3_ENDPOINT"] = f"{host}:{port}"
437480
return dict(
438481
endpoint=f"{host}:{port}",
439482
access_key=os.environ.get("S3_ACCESS_KEY", "datajoint"),

0 commit comments

Comments
 (0)