Skip to content

Commit ed80803

Browse files
committed
Make sure we handle variations in name and import name
1 parent 539c467 commit ed80803

2 files changed

Lines changed: 112 additions & 0 deletions

File tree

check_dist/_core.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,39 @@ def _module_name_from_project(project_name: str) -> str:
215215
return re.sub(r"[\s-]+", "_", project_name).strip("_")
216216

217217

218+
def _normalize_name(name: str) -> str:
219+
"""Normalize a name by stripping underscores, hyphens, and periods."""
220+
return re.sub(r"[-_.]+", "", name).lower()
221+
222+
223+
def _resolve_module_from_hatch(module: str, hatch_config: dict) -> str:
224+
"""Resolve the module name from hatch packages configuration.
225+
226+
If any package in the hatch sdist or wheel ``packages`` (or
227+
``only-include``) is equivalent to *module* after normalizing away
228+
underscores, hyphens, and periods, return that package name instead.
229+
This handles projects where the distribution name differs from the
230+
importable package name (e.g. ``jupyter-fs`` vs ``jupyterfs``).
231+
"""
232+
norm = _normalize_name(module)
233+
candidates: list[str] = []
234+
for target in ("sdist", "wheel"):
235+
target_cfg = hatch_config.get("targets", {}).get(target, {})
236+
for key in ("only-include", "packages"):
237+
vals = target_cfg.get(key)
238+
if vals:
239+
candidates.extend(vals)
240+
# Also check top-level packages / only-include
241+
for key in ("only-include", "packages"):
242+
vals = hatch_config.get(key)
243+
if vals:
244+
candidates.extend(vals)
245+
for candidate in candidates:
246+
if _normalize_name(candidate) == norm:
247+
return candidate
248+
return module
249+
250+
218251
def copier_defaults(copier_config: dict, hatch_config: dict | None = None) -> dict | None:
219252
"""Derive default check-dist config from copier answers.
220253
@@ -238,6 +271,8 @@ def copier_defaults(copier_config: dict, hatch_config: dict | None = None) -> di
238271
return None
239272

240273
module = _module_name_from_project(project_name)
274+
if hatch_config:
275+
module = _resolve_module_from_hatch(module, hatch_config)
241276

242277
sdist_present_extra = list(ext_defaults.get("sdist_present_extra", []))
243278

check_dist/tests/test_all.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
_find_pre_built,
2020
_matches_hatch_pattern,
2121
_module_name_from_project,
22+
_normalize_name,
23+
_resolve_module_from_hatch,
2224
_sdist_expected_files,
2325
check_absent,
2426
check_dist,
@@ -678,6 +680,81 @@ def test_no_change(self):
678680
assert _module_name_from_project("mypkg") == "mypkg"
679681

680682

683+
class TestNormalizeName:
684+
def test_underscores(self):
685+
assert _normalize_name("jupyter_fs") == "jupyterfs"
686+
687+
def test_hyphens(self):
688+
assert _normalize_name("jupyter-fs") == "jupyterfs"
689+
690+
def test_periods(self):
691+
assert _normalize_name("jupyter.fs") == "jupyterfs"
692+
693+
def test_mixed(self):
694+
assert _normalize_name("my_pkg-name.ext") == "mypkgnameext"
695+
696+
def test_no_separators(self):
697+
assert _normalize_name("mypkg") == "mypkg"
698+
699+
def test_case_insensitive(self):
700+
assert _normalize_name("MyPkg") == "mypkg"
701+
702+
703+
class TestResolveModuleFromHatch:
704+
def test_wheel_packages_match(self):
705+
hatch = {"targets": {"wheel": {"packages": ["jupyterfs"]}}}
706+
assert _resolve_module_from_hatch("jupyter_fs", hatch) == "jupyterfs"
707+
708+
def test_sdist_packages_match(self):
709+
hatch = {"targets": {"sdist": {"packages": ["jupyterfs", "js"]}}}
710+
assert _resolve_module_from_hatch("jupyter_fs", hatch) == "jupyterfs"
711+
712+
def test_no_match_returns_original(self):
713+
hatch = {"targets": {"wheel": {"packages": ["otherpkg"]}}}
714+
assert _resolve_module_from_hatch("jupyter_fs", hatch) == "jupyter_fs"
715+
716+
def test_empty_hatch_config(self):
717+
assert _resolve_module_from_hatch("jupyter_fs", {}) == "jupyter_fs"
718+
719+
def test_exact_match_unchanged(self):
720+
hatch = {"targets": {"wheel": {"packages": ["my_project"]}}}
721+
assert _resolve_module_from_hatch("my_project", hatch) == "my_project"
722+
723+
def test_only_include_match(self):
724+
hatch = {"targets": {"wheel": {"only-include": ["jupyterfs"]}}}
725+
assert _resolve_module_from_hatch("jupyter_fs", hatch) == "jupyterfs"
726+
727+
def test_top_level_packages(self):
728+
hatch = {"packages": ["jupyterfs"]}
729+
assert _resolve_module_from_hatch("jupyter_fs", hatch) == "jupyterfs"
730+
731+
def test_period_variant(self):
732+
hatch = {"targets": {"wheel": {"packages": ["my.pkg"]}}}
733+
assert _resolve_module_from_hatch("my_pkg", hatch) == "my.pkg"
734+
735+
736+
class TestCopierDefaultsWithHatchModuleResolution:
737+
def test_hatch_resolves_module_name(self):
738+
hatch = {"targets": {"wheel": {"packages": ["jupyterfs"]}}}
739+
cfg = copier_defaults(
740+
{"add_extension": "jupyter", "project_name": "jupyter-fs"},
741+
hatch_config=hatch,
742+
)
743+
assert cfg is not None
744+
assert "jupyterfs" in cfg["sdist"]["present"]
745+
assert "jupyterfs" in cfg["wheel"]["present"]
746+
assert "jupyter_fs" not in cfg["sdist"]["present"]
747+
assert "jupyter_fs" not in cfg["wheel"]["present"]
748+
749+
def test_no_hatch_uses_default_module(self):
750+
cfg = copier_defaults(
751+
{"add_extension": "jupyter", "project_name": "jupyter-fs"},
752+
)
753+
assert cfg is not None
754+
assert "jupyter_fs" in cfg["sdist"]["present"]
755+
assert "jupyter_fs" in cfg["wheel"]["present"]
756+
757+
681758
class TestLoadCopierConfig:
682759
def test_missing_file(self, tmp_path):
683760
assert load_copier_config(tmp_path) == {}

0 commit comments

Comments
 (0)