Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions qualibrate_config/core/migration/migrations/v3_v4.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sys
from copy import deepcopy
from importlib.util import find_spec
from pathlib import Path

from qualibrate_config.core.migration.migrations.base import MigrateBase
Expand All @@ -22,13 +22,18 @@ def backward(data: RawConfigType, config_path: Path) -> RawConfigType:
static_files = app.get("static_site_files")
if static_files is not None:
return {"qualibrate": qualibrate, **data}
app_module = find_spec("qualibrate_app")
if app_module is None or app_module.origin is None:
return {"qualibrate": qualibrate, **data}
module_path = Path(app_module.origin)
app["static_site_files"] = str(
module_path.parents[1] / "qualibrate_static"
)
# Walk sys.path to find qualibrate_app's install location. Nuitka-safe
# alternative to find_spec("qualibrate_app").origin (which returns a
# phantom path under obfuscated builds). Semantics preserved: find the
# qualibrate_app package dir, go up one level (to site-packages), and
# append "qualibrate_static".
for entry in sys.path:
qa_dir = Path(entry) / "qualibrate_app"
if qa_dir.is_dir():
app["static_site_files"] = str(
qa_dir.parent / "qualibrate_static"
)
break
return {"qualibrate": qualibrate, **data}

@staticmethod
Expand Down
20 changes: 15 additions & 5 deletions qualibrate_config/models/q_app.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from importlib.util import find_spec
import sys
from pathlib import Path
from typing import Annotated

Expand All @@ -12,10 +12,20 @@


def get_default_static_path() -> Path | None:
app_module = find_spec("qualibrate.app")
if app_module is None or app_module.origin is None:
return None
return Path(app_module.origin).resolve().parent / "qualibrate_static"
"""Locate the qualibrate frontend static files.

Walks sys.path (CPython's list of directories where packages live) rather
than relying on __file__ / find_spec().origin / importlib.resources, all
of which Nuitka's custom loader corrupts with phantom paths in obfuscated
builds. sys.path is maintained by CPython itself and Nuitka does not
intercept it, so this lookup works identically for source and compiled
installs.
"""
for entry in sys.path:
candidate = Path(entry) / "qualibrate" / "app" / "qualibrate_static"
if candidate.is_dir():
return candidate
return None


class QualibrateAppConfig(BaseConfig):
Expand Down
15 changes: 7 additions & 8 deletions tests/unit/test_cli/test_migrations/test_v3_v4.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import sys
from copy import deepcopy
from importlib.util import find_spec
from pathlib import Path

from qualibrate_config.core.migration.migrations import v3_v4

Expand Down Expand Up @@ -79,14 +78,14 @@ def test_migrate_v4_v3_without_static_app_exists(mocker, tmp_path):
config_v4 = deepcopy(CONFIG_V3)
config_v4["qualibrate"]["version"] = 4
config_v4["qualibrate"]["app"].pop("static_site_files")
config_spec = find_spec("qualibrate_config")
mocker.patch(
"qualibrate_config.core.migration.migrations.v3_v4.find_spec",
return_value=config_spec,
)
# Simulate `qualibrate_app` being installed at tmp_path by creating the
# package dir and pointing sys.path at tmp_path. The migration's sys.path
# walk will then find it and derive the static_site_files from its parent.
(tmp_path / "qualibrate_app").mkdir()
mocker.patch.object(sys, "path", [str(tmp_path)])
config_v3_migrated = v3_v4.Migrate.backward(config_v4, tmp_path)
config_v3_expected = deepcopy(CONFIG_V3)
config_v3_expected["qualibrate"]["app"]["static_site_files"] = str(
Path(config_spec.origin).parents[1] / "qualibrate_static"
tmp_path / "qualibrate_static"
)
assert config_v3_expected == config_v3_migrated
Loading