Skip to content

Commit 584ff7a

Browse files
authored
refactor: remove __file__ (#571)
* refactor: remove __file__ Signed-off-by: Sricharan Reddy Varra <sricharan.varra@biohub.org> * refactor: fix cwd to use benchmark_root directly Signed-off-by: Sricharan Reddy Varra <sricharan.varra@biohub.org> --------- Signed-off-by: Sricharan Reddy Varra <sricharan.varra@biohub.org>
1 parent 1930049 commit 584ff7a

6 files changed

Lines changed: 63 additions & 68 deletions

File tree

benchmarks/utils.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import time
77
from contextlib import contextmanager
88
from dataclasses import dataclass, field
9+
from importlib.resources import as_file, files
910
from pathlib import Path
1011

1112
import numpy as np
@@ -82,19 +83,17 @@ def save(self, path: Path):
8283
# --- Metadata collection ---
8384

8485

85-
# Path to the waveorder repo root (two levels up from this file)
86-
_REPO_ROOT = str(Path(__file__).resolve().parent.parent)
87-
88-
8986
def _run_git(*args: str) -> str:
9087
"""Run a git command in the waveorder repo and return stripped stdout, or '' on failure."""
9188
try:
92-
result = subprocess.run(
93-
["git", "-C", _REPO_ROOT, *args],
94-
capture_output=True,
95-
text=True,
96-
timeout=5,
97-
)
89+
with as_file(files("benchmarks")) as benchmark_root:
90+
result = subprocess.run(
91+
["git", *args],
92+
cwd=benchmark_root,
93+
capture_output=True,
94+
text=True,
95+
timeout=5,
96+
)
9897
return result.stdout.strip()
9998
except (subprocess.TimeoutExpired, FileNotFoundError):
10099
return ""

tests/cli_tests/test_settings.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
from pathlib import Path
2-
31
import pytest
42
import yaml
53
from pydantic import ValidationError
@@ -79,9 +77,8 @@ def test_fluor_tf_settings():
7977
fluorescence.TransferFunctionSettings(wavelength_emission=0.500, yx_pixel_size=2000)
8078

8179

82-
def test_generate_example_settings():
83-
project_root = Path(__file__).parent.parent.parent
84-
example_path = project_root / "docs" / "examples" / "cli" / "configs"
80+
def test_generate_example_settings(pytestconfig):
81+
example_path = pytestconfig.rootpath / "docs" / "examples" / "cli" / "configs"
8582

8683
# 2D configs override regularization_strength for better 2D defaults
8784
phase_2d_apply_inverse = phase.ApplyInverseSettings(regularization_strength=1e-2)

tests/test_benchmark_runner.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,9 @@ def test_from_dict(self):
132132

133133

134134
class TestLoadRegressionExperiment:
135-
def test_regression_yml_valid(self):
135+
def test_regression_yml_valid(self, pytestconfig):
136136
"""Validate the committed regression.yml experiment."""
137-
from pathlib import Path
138-
139-
regression_path = Path(__file__).parent.parent / "benchmarks" / "experiments" / "regression.yml"
137+
regression_path = pytestconfig.rootpath / "benchmarks" / "experiments" / "regression.yml"
140138
if not regression_path.exists():
141139
pytest.skip("regression.yml not found")
142140
exp = load_experiment(regression_path)

tests/test_examples.py

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,43 @@
22
import runpy
33
import subprocess
44
import sys
5-
from pathlib import Path
65
from unittest.mock import patch
76

87
import matplotlib.pyplot as plt
98
import pytest
109

11-
DOCS = Path(__file__).parent.parent / "docs"
12-
EXAMPLES = DOCS / "examples"
10+
# Examples per test, relative to docs/examples: either explicit files or a glob pattern.
11+
_EXAMPLES = {
12+
"test_maintenance_examples": [
13+
"maintenance/QLIPP_simulation/2D_QLIPP_forward.py",
14+
"maintenance/QLIPP_simulation/2D_QLIPP_recon.py",
15+
"maintenance/PTI_simulation/PTI_Simulation_Forward_2D3D.py",
16+
"maintenance/PTI_simulation/PTI_Simulation_Recon2D.py",
17+
"maintenance/PTI_simulation/PTI_Simulation_Recon3D.py",
18+
],
19+
"test_demo_examples": [
20+
"demos/QPI_defocus/QPI_defocus_simulation.py",
21+
"demos/QLIPP/QLIPP_simulation.py",
22+
],
23+
"test_api_examples": "api/*.py",
24+
"test_cli_examples": "cli/*.sh",
25+
}
26+
27+
28+
def pytest_generate_tests(metafunc):
29+
"""Resolve repository examples from pytest's configured project root."""
30+
entry = _EXAMPLES.get(metafunc.function.__name__)
31+
if entry is None:
32+
return
33+
34+
examples_dir = metafunc.config.rootpath / "docs" / "examples"
35+
if isinstance(entry, str):
36+
examples = sorted(examples_dir.glob(entry))
37+
else:
38+
examples = [examples_dir / name for name in entry]
39+
metafunc.parametrize("example", examples, ids=lambda path: path.name)
1340

1441

15-
@pytest.mark.parametrize(
16-
"example",
17-
[
18-
EXAMPLES / "maintenance" / "QLIPP_simulation/2D_QLIPP_forward.py",
19-
EXAMPLES / "maintenance" / "QLIPP_simulation/2D_QLIPP_recon.py",
20-
EXAMPLES / "maintenance" / "PTI_simulation/PTI_Simulation_Forward_2D3D.py",
21-
EXAMPLES / "maintenance" / "PTI_simulation/PTI_Simulation_Recon2D.py",
22-
EXAMPLES / "maintenance" / "PTI_simulation/PTI_Simulation_Recon3D.py",
23-
],
24-
ids=lambda p: p.name,
25-
)
2642
def test_maintenance_examples(example):
2743
"""Test maintenance examples (QLIPP, PTI) with mocked plotting"""
2844
with (
@@ -39,14 +55,6 @@ def test_maintenance_examples(example):
3955
plt.close("all")
4056

4157

42-
@pytest.mark.parametrize(
43-
"example",
44-
[
45-
EXAMPLES / "demos/QPI_defocus/QPI_defocus_simulation.py",
46-
EXAMPLES / "demos/QLIPP/QLIPP_simulation.py",
47-
],
48-
ids=lambda p: p.name,
49-
)
5058
def test_demo_examples(example):
5159
"""Run Colab demo scripts so renamed APIs in waveorder are caught early.
5260
@@ -80,9 +88,9 @@ def test_demo_examples(example):
8088
"inplane_oriented_thick_pol3d.py",
8189
],
8290
)
83-
def test_phase_examples(script):
91+
def test_phase_examples(script, pytestconfig):
8492
"""Test phase model examples"""
85-
path = EXAMPLES / "models" / script
93+
path = pytestconfig.rootpath / "docs" / "examples" / "models" / script
8694
# examples needs two <enters>s so send input="e\ne"
8795
completed_process = subprocess.run(
8896
[sys.executable, str(path)],
@@ -93,21 +101,11 @@ def test_phase_examples(script):
93101
assert completed_process.returncode == 0
94102

95103

96-
@pytest.mark.parametrize(
97-
"example",
98-
sorted((EXAMPLES / "api").glob("*.py")),
99-
ids=lambda p: p.name,
100-
)
101104
def test_api_examples(example):
102105
"""Test API-level examples (no napari, no matplotlib)"""
103106
runpy.run_path(str(example), run_name="__main__")
104107

105108

106-
@pytest.mark.parametrize(
107-
"example",
108-
sorted((EXAMPLES / "cli").glob("*.sh")),
109-
ids=lambda p: p.name,
110-
)
111109
def test_cli_examples(example, tmp_path, monkeypatch):
112110
"""Test CLI-level shell script examples (skip 'wo view' lines)."""
113111
import shlex

waveorder/cli/bench.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,13 @@
88
import shutil
99
import traceback
1010
from datetime import datetime
11+
from importlib.resources import as_file, files
1112
from pathlib import Path
1213

1314
import click
1415
import numpy as np
1516
import yaml
1617

17-
_DEFAULT_EXPERIMENT = Path(__file__).parent.parent.parent / "benchmarks" / "experiments" / "regression.yml"
18-
1918

2019
def _resolve_output_dir(cli_value: str | None) -> Path:
2120
"""Resolve the output directory.
@@ -102,9 +101,10 @@ def run(experiment, scope, output_dir, save_all):
102101
from benchmarks.utils import collect_metadata
103102

104103
if experiment is None:
105-
experiment = str(_DEFAULT_EXPERIMENT)
106-
107-
experiment_path = Path(experiment)
104+
benchmark_root = click.get_current_context().with_resource(as_file(files("benchmarks")))
105+
experiment_path = benchmark_root / "experiments" / "regression.yml"
106+
else:
107+
experiment_path = Path(experiment)
108108
output_dir = _resolve_output_dir(output_dir)
109109
exp = load_experiment(experiment_path)
110110

waveorder/plugin/main_widget.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import logging
55
import os
66
import textwrap
7-
from os.path import dirname
7+
from importlib.resources import files
88
from pathlib import Path, PurePath
99

1010
# type hint/check
@@ -52,6 +52,15 @@
5252
pass
5353

5454

55+
def _load_asset_pixmap(name: str) -> QPixmap:
56+
"""Load a bundled image from ``waveorder/assets`` as a ``QPixmap``."""
57+
data = (files("waveorder") / "assets" / name).read_bytes()
58+
pixmap = QPixmap()
59+
if not pixmap.loadFromData(data):
60+
raise ValueError(f"Unable to load bundled image: {name}")
61+
return pixmap
62+
63+
5564
class MainWidget(QWidget):
5665
"""
5766
This is the main waveorder widget that houses all of the GUI components of waveorder.
@@ -173,7 +182,6 @@ def __init__(self, napari_viewer: Viewer):
173182
self.reconstruction_data = None
174183
self.calib_assessment_level = None
175184
self.ret_max = 25
176-
waveorder_dir = dirname(dirname(dirname(os.path.abspath(__file__))))
177185
self.worker = None
178186

179187
## Initialize calibration plot
@@ -185,15 +193,10 @@ def __init__(self, napari_viewer: Viewer):
185193

186194
## Initialize visuals
187195
# Initialize GUI Images (plotting legends, waveorder logo)
188-
assets_dir = Path(__file__).parent.parent / "assets"
189-
jch_legend_path = assets_dir / "JCh_legend.png"
190-
hsv_legend_path = assets_dir / "HSV_legend.png"
191-
logo_path = assets_dir / "waveorder_plugin_logo.png"
192-
193-
self.jch_pixmap = QPixmap(str(jch_legend_path))
194-
self.hsv_pixmap = QPixmap(str(hsv_legend_path))
196+
self.jch_pixmap = _load_asset_pixmap("JCh_legend.png")
197+
self.hsv_pixmap = _load_asset_pixmap("HSV_legend.png")
195198
self.ui.label_orientation_image.setPixmap(self.hsv_pixmap)
196-
logo_pixmap = QPixmap(str(logo_path))
199+
logo_pixmap = _load_asset_pixmap("waveorder_plugin_logo.png")
197200
self.ui.label_logo.setPixmap(logo_pixmap)
198201

199202
# Hide UI elements for popups

0 commit comments

Comments
 (0)