@@ -81,6 +81,7 @@ class HatchlingPackageFinder(PackageFinder):
8181 Uses hatchling's BuilderConfig abstraction to enumerate packages.
8282
8383 TODO: include/exclude configuration of packages in hatch needs more thorough testing with different scenarios.
84+ TODO: hatchling supports path rewrites with the sources config, which is currently not supported
8485 """
8586
8687 builder_config : BuilderConfig
@@ -114,9 +115,9 @@ def find_packages(self) -> t.Iterable[str]:
114115 for relative_package_path in package_paths :
115116 package_name = os .path .basename (relative_package_path )
116117
117- package_path = os . path . join (
118- self .path , relative_package_path
119- ) # build package path within sources root
118+ # Package paths in hatchling are always relative to the project root, so we join
119+ # with the project root (not self.path, which is the sources root).
120+ package_path = str ( os . path . join ( self . builder_config . root , relative_package_path ))
120121 if not os .path .isdir (package_path ):
121122 continue
122123
@@ -141,16 +142,38 @@ def find_packages(self) -> t.Iterable[str]:
141142
142143 @property
143144 def path (self ) -> str :
145+ """Return the sources root — the directory under which the package names are located.
146+
147+ This is used by ``PluginFromPackageFinder._list_module_names`` to construct the
148+ file-system path for each package name, so it must point to the directory that
149+ *contains* the top-level packages (not the project root in general).
150+
151+ Hatchling's ``sources`` dict maps ``{source_dir: dest_dir_in_wheel}``:
152+ - ``{"localstack-core/": ""}`` — packages in a subdirectory: source root is
153+ ``localstack-core/`` (the key, not the value)
154+ - ``{"": ""}`` — packages directly in the project root
155+ """
156+ root = self .builder_config .root
157+
158+ # If no sources are configured, we assume the sources root is the project root
144159 if not self .builder_config .sources :
145- where = self .builder_config .root
146- else :
147- if self .builder_config .sources ["" ]:
148- where = self .builder_config .sources ["" ]
160+ return root
161+
162+ # The keys themselves are source directories (e.g. "localstack-core/").
163+ # Filter out any empty-string keys, strip trailing separators.
164+ source_dirs = {k .rstrip ("/" ): v for k , v in self .builder_config .sources .items () if k }
165+ if len (source_dirs ) == 1 :
166+ source_dir , dest_dir = next (iter (source_dirs .items ()))
167+ if dest_dir :
168+ LOG .warning (
169+ "plux doesn't know how to resolve sources with non-empty destination directories, using root dir."
170+ )
149171 else :
150- LOG .warning ("plux doesn't know how to resolve multiple sources directories" )
151- where = self .builder_config .root
172+ return os .path .join (root , str (source_dir ))
152173
153- return where
174+ if source_dirs :
175+ LOG .warning ("plux doesn't know how to resolve multiple sources directories, using root dir." )
176+ return root
154177
155178 def filter_packages (self , packages : t .Iterable [str ]) -> t .Iterable [str ]:
156179 return [item for item in packages if not self .exclude (item ) and self .include (item )]
0 commit comments