@@ -134,8 +134,6 @@ def load_hatch_config(pyproject_path: str | Path = "pyproject.toml") -> dict:
134134 return config .get ("tool" , {}).get ("hatch" , {}).get ("build" , {})
135135
136136
137- # ── Copier template defaults ─────────────────────────────────────────
138-
139137# Per-extension type defaults for sdist/wheel present/absent patterns.
140138# Keys follow the ``add_extension`` value in ``.copier-answers.yaml``.
141139_EXTENSION_DEFAULTS : dict [str , dict ] = {
@@ -217,6 +215,39 @@ def _module_name_from_project(project_name: str) -> str:
217215 return re .sub (r"[\s-]+" , "_" , project_name ).strip ("_" )
218216
219217
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+
220251def copier_defaults (copier_config : dict , hatch_config : dict | None = None ) -> dict | None :
221252 """Derive default check-dist config from copier answers.
222253
@@ -240,6 +271,8 @@ def copier_defaults(copier_config: dict, hatch_config: dict | None = None) -> di
240271 return None
241272
242273 module = _module_name_from_project (project_name )
274+ if hatch_config :
275+ module = _resolve_module_from_hatch (module , hatch_config )
243276
244277 sdist_present_extra = list (ext_defaults .get ("sdist_present_extra" , []))
245278
@@ -301,9 +334,6 @@ def _filter_extras_by_hatch(extras: list[str], hatch_config: dict) -> list[str]:
301334 return extras
302335
303336
304- # ── Building ──────────────────────────────────────────────────────────
305-
306-
307337def build_dists (source_dir : str , output_dir : str , * , no_isolation : bool = False ) -> list [str ]:
308338 """Build sdist and wheel into *output_dir*.
309339
@@ -378,9 +408,6 @@ def _find_pre_built(source_dir: str) -> str | None:
378408 return None
379409
380410
381- # ── Listing files ─────────────────────────────────────────────────────
382-
383-
384411def list_sdist_files (sdist_path : str ) -> list [str ]:
385412 """List files inside an sdist, stripping the top-level directory."""
386413 files : list [str ] = []
@@ -407,9 +434,6 @@ def list_wheel_files(wheel_path: str) -> list[str]:
407434 return sorted (name for name in zf .namelist () if not name .endswith ("/" ))
408435
409436
410- # ── VCS integration ───────────────────────────────────────────────────
411-
412-
413437def get_vcs_files (source_dir : str ) -> list [str ]:
414438 """Return files tracked by git in *source_dir*."""
415439 try :
@@ -426,9 +450,6 @@ def get_vcs_files(source_dir: str) -> list[str]:
426450 return sorted (f for f in result .stdout .split ("\0 " ) if f )
427451
428452
429- # ── Pattern matching ──────────────────────────────────────────────────
430-
431-
432453def matches_pattern (filepath : str , pattern : str ) -> bool :
433454 """Check whether *filepath* matches *pattern*.
434455
@@ -479,9 +500,6 @@ def _matches_hatch_pattern(filepath: str, pattern: str) -> bool:
479500 return False
480501
481502
482- # ── Checking helpers ──────────────────────────────────────────────────
483-
484-
485503def check_present (files : list [str ], patterns : list [str ], dist_type : str ) -> list [str ]:
486504 """Return error strings for any *patterns* not found in *files*."""
487505 errors : list [str ] = []
@@ -664,9 +682,6 @@ def check_sdist_vs_vcs(
664682 return errors
665683
666684
667- # ── Main entry point ──────────────────────────────────────────────────
668-
669-
670685def check_dist (
671686 source_dir : str = "." ,
672687 * ,
@@ -742,7 +757,6 @@ def check_dist(
742757 if not wheel_path :
743758 errors .append ("No wheel found in pre-built directory" )
744759
745- # ── sdist checks ─────────────────────────────────────────
746760 if sdist_path :
747761 sdist_files = list_sdist_files (sdist_path )
748762 messages .append (f"\n sdist ({ os .path .basename (sdist_path )} ) – { len (sdist_files )} file(s):" )
@@ -760,7 +774,6 @@ def check_dist(
760774 errors .extend (check_absent (sdist_files , config ["sdist" ]["absent" ], "sdist" , present_patterns = config ["sdist" ]["present" ]))
761775 errors .extend (check_wrong_platform_extensions (sdist_files , "sdist" ))
762776
763- # ── wheel checks ─────────────────────────────────────────
764777 if wheel_path :
765778 wheel_files = list_wheel_files (wheel_path )
766779 messages .append (f"\n wheel ({ os .path .basename (wheel_path )} ) – { len (wheel_files )} file(s):" )
0 commit comments