python-package-folder 5.4.0__py3-none-any.whl → 6.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- python_package_folder/analyzer.py +22 -45
- python_package_folder/manager.py +21 -1
- python_package_folder/subfolder_build.py +74 -8
- {python_package_folder-5.4.0.dist-info → python_package_folder-6.0.0.dist-info}/METADATA +1 -1
- {python_package_folder-5.4.0.dist-info → python_package_folder-6.0.0.dist-info}/RECORD +8 -8
- {python_package_folder-5.4.0.dist-info → python_package_folder-6.0.0.dist-info}/WHEEL +0 -0
- {python_package_folder-5.4.0.dist-info → python_package_folder-6.0.0.dist-info}/entry_points.txt +0 -0
- {python_package_folder-5.4.0.dist-info → python_package_folder-6.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -330,56 +330,33 @@ class ImportAnalyzer:
|
|
|
330
330
|
|
|
331
331
|
# Check all subdirectories in parent (not just common ones)
|
|
332
332
|
# This handles cases like src/data/spreadsheet_creation/spreadsheet_formatting_dataclasses.py
|
|
333
|
-
|
|
333
|
+
# Use recursive search to find modules in nested directories
|
|
334
|
+
if parent.is_dir() and parent.is_relative_to(self.project_root):
|
|
335
|
+
# Recursively search for the module file in subdirectories
|
|
336
|
+
# Limit search to project_root and its subdirectories to avoid searching too broadly
|
|
337
|
+
module_basename = module_name.split(".")[-1]
|
|
334
338
|
try:
|
|
335
|
-
for
|
|
336
|
-
|
|
339
|
+
# Search recursively for the module file
|
|
340
|
+
for potential_file in parent.rglob(f"{module_basename}.py"):
|
|
341
|
+
# Only search within project_root to avoid going too far
|
|
342
|
+
if not potential_file.is_relative_to(self.project_root):
|
|
337
343
|
continue
|
|
338
|
-
# Skip
|
|
339
|
-
if
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
# Check if module directory exists in subdirectory
|
|
346
|
-
potential_subdir_module = subdir / module_name.replace(".", "/")
|
|
347
|
-
if (
|
|
348
|
-
potential_subdir_module.is_dir()
|
|
349
|
-
and (potential_subdir_module / "__init__.py").exists()
|
|
344
|
+
# Skip excluded patterns
|
|
345
|
+
if any(
|
|
346
|
+
part.startswith("_SS")
|
|
347
|
+
or part.startswith("__SS")
|
|
348
|
+
or part.startswith("_sandbox")
|
|
349
|
+
or part.startswith("__sandbox")
|
|
350
|
+
for part in potential_file.parts
|
|
350
351
|
):
|
|
351
|
-
return potential_subdir_module / "__init__.py"
|
|
352
|
-
if potential_subdir_module.with_suffix(".py").is_file():
|
|
353
|
-
return potential_subdir_module.with_suffix(".py")
|
|
354
|
-
# Check nested subdirectories (e.g., data/spreadsheet_creation)
|
|
355
|
-
# Recursively check subdirectories up to 2 levels deep
|
|
356
|
-
try:
|
|
357
|
-
for nested_subdir in subdir.iterdir():
|
|
358
|
-
if not nested_subdir.is_dir():
|
|
359
|
-
continue
|
|
360
|
-
# Check if module file exists in nested subdirectory
|
|
361
|
-
potential_nested_file = (
|
|
362
|
-
nested_subdir / f"{module_name.split('.')[-1]}.py"
|
|
363
|
-
)
|
|
364
|
-
if potential_nested_file.exists():
|
|
365
|
-
return potential_nested_file
|
|
366
|
-
# Check if module directory exists in nested subdirectory
|
|
367
|
-
potential_nested_module = nested_subdir / module_name.replace(
|
|
368
|
-
".", "/"
|
|
369
|
-
)
|
|
370
|
-
if (
|
|
371
|
-
potential_nested_module.is_dir()
|
|
372
|
-
and (potential_nested_module / "__init__.py").exists()
|
|
373
|
-
):
|
|
374
|
-
return potential_nested_module / "__init__.py"
|
|
375
|
-
if potential_nested_module.with_suffix(".py").is_file():
|
|
376
|
-
return potential_nested_module.with_suffix(".py")
|
|
377
|
-
except (OSError, PermissionError):
|
|
378
|
-
# Skip nested directories we can't read
|
|
379
352
|
continue
|
|
353
|
+
# Skip if it's in the src_dir (we're looking for external dependencies)
|
|
354
|
+
if potential_file.is_relative_to(src_dir):
|
|
355
|
+
continue
|
|
356
|
+
return potential_file
|
|
380
357
|
except (OSError, PermissionError):
|
|
381
|
-
# Skip
|
|
382
|
-
|
|
358
|
+
# Skip if we can't read the directory
|
|
359
|
+
pass
|
|
383
360
|
|
|
384
361
|
# Check common subdirectories in parent (e.g., _shared, shared, common)
|
|
385
362
|
# This handles cases like src/_shared/better_enum.py
|
python_package_folder/manager.py
CHANGED
|
@@ -268,10 +268,30 @@ class BuildManager:
|
|
|
268
268
|
# This is acceptable for tests or dependency-only operations
|
|
269
269
|
if temp_pyproject is None:
|
|
270
270
|
self.subfolder_config = None
|
|
271
|
+
else:
|
|
272
|
+
# If temporary package directory was created, use it for all operations
|
|
273
|
+
# This ensures dependencies are copied to the correct location and
|
|
274
|
+
# imports are fixed in the files that will actually be packaged
|
|
275
|
+
if (
|
|
276
|
+
self.subfolder_config
|
|
277
|
+
and self.subfolder_config._temp_package_dir
|
|
278
|
+
and self.subfolder_config._temp_package_dir.exists()
|
|
279
|
+
):
|
|
280
|
+
# Update src_dir to point to temp package directory
|
|
281
|
+
self.src_dir = self.subfolder_config._temp_package_dir
|
|
282
|
+
# Recreate finder with updated src_dir so it calculates target paths correctly
|
|
283
|
+
self.finder = ExternalDependencyFinder(
|
|
284
|
+
self.project_root,
|
|
285
|
+
self.src_dir,
|
|
286
|
+
exclude_patterns=self.exclude_patterns,
|
|
287
|
+
)
|
|
288
|
+
print(
|
|
289
|
+
f"Using temporary package directory for build: {self.src_dir}"
|
|
290
|
+
)
|
|
271
291
|
|
|
272
292
|
analyzer = ImportAnalyzer(self.project_root)
|
|
273
293
|
|
|
274
|
-
# Find all Python files in src/
|
|
294
|
+
# Find all Python files in src/ (which may now be the temp package directory)
|
|
275
295
|
python_files = analyzer.find_all_python_files(self.src_dir)
|
|
276
296
|
|
|
277
297
|
# Find external dependencies using the configured finder
|
|
@@ -78,6 +78,7 @@ class SubfolderBuildConfig:
|
|
|
78
78
|
self._used_subfolder_pyproject = False
|
|
79
79
|
self._excluded_files: list[tuple[Path, Path]] = [] # List of (original_path, temp_path) tuples
|
|
80
80
|
self._exclude_temp_dir: Path | None = None
|
|
81
|
+
self._temp_package_dir: Path | None = None
|
|
81
82
|
|
|
82
83
|
def _derive_package_name(self) -> str:
|
|
83
84
|
"""
|
|
@@ -121,6 +122,48 @@ class SubfolderBuildConfig:
|
|
|
121
122
|
# Fallback to just subfolder name
|
|
122
123
|
return subfolder_name
|
|
123
124
|
|
|
125
|
+
def _create_temp_package_directory(self) -> None:
|
|
126
|
+
"""
|
|
127
|
+
Create a temporary package directory with the correct import name.
|
|
128
|
+
|
|
129
|
+
This ensures the installed package has the correct directory structure.
|
|
130
|
+
The package name (with hyphens) is converted to the import name (with underscores).
|
|
131
|
+
For example: 'ml-drawing-assistant-data' -> 'ml_drawing_assistant_data'
|
|
132
|
+
|
|
133
|
+
The temporary directory is created in the project root and contains a copy
|
|
134
|
+
of the source directory contents.
|
|
135
|
+
"""
|
|
136
|
+
if not self.package_name:
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
# Convert package name (with hyphens) to import name (with underscores)
|
|
140
|
+
# PyPI package names use hyphens, but Python import names use underscores
|
|
141
|
+
import_name = self.package_name.replace("-", "_")
|
|
142
|
+
|
|
143
|
+
# Create temporary directory name
|
|
144
|
+
temp_dir_name = f".temp_package_{import_name}"
|
|
145
|
+
temp_package_dir = self.project_root / temp_dir_name
|
|
146
|
+
|
|
147
|
+
# Remove if it already exists (from a previous failed build)
|
|
148
|
+
if temp_package_dir.exists():
|
|
149
|
+
shutil.rmtree(temp_package_dir)
|
|
150
|
+
|
|
151
|
+
# Copy the entire source directory contents to the temporary directory
|
|
152
|
+
try:
|
|
153
|
+
shutil.copytree(self.src_dir, temp_package_dir)
|
|
154
|
+
self._temp_package_dir = temp_package_dir
|
|
155
|
+
print(
|
|
156
|
+
f"Created temporary package directory: {temp_package_dir} "
|
|
157
|
+
f"(import name: {import_name})"
|
|
158
|
+
)
|
|
159
|
+
except Exception as e:
|
|
160
|
+
print(
|
|
161
|
+
f"Warning: Could not create temporary package directory: {e}",
|
|
162
|
+
file=sys.stderr,
|
|
163
|
+
)
|
|
164
|
+
# Fall back to using src_dir directly
|
|
165
|
+
self._temp_package_dir = None
|
|
166
|
+
|
|
124
167
|
def _get_package_structure(self) -> tuple[str, list[str]]:
|
|
125
168
|
"""
|
|
126
169
|
Determine the package structure for hatchling.
|
|
@@ -130,21 +173,24 @@ class SubfolderBuildConfig:
|
|
|
130
173
|
- packages_path: The path to the directory containing packages
|
|
131
174
|
- package_dirs: List of package directories to include
|
|
132
175
|
"""
|
|
133
|
-
#
|
|
134
|
-
|
|
176
|
+
# Use temporary package directory if it exists, otherwise use src_dir
|
|
177
|
+
package_dir = self._temp_package_dir if self._temp_package_dir and self._temp_package_dir.exists() else self.src_dir
|
|
178
|
+
|
|
179
|
+
# Check if package_dir itself is a package (has __init__.py)
|
|
180
|
+
has_init = (package_dir / "__init__.py").exists()
|
|
135
181
|
|
|
136
|
-
# Check for Python files directly in
|
|
137
|
-
py_files = list(
|
|
182
|
+
# Check for Python files directly in package_dir
|
|
183
|
+
py_files = list(package_dir.glob("*.py"))
|
|
138
184
|
has_py_files = bool(py_files)
|
|
139
185
|
|
|
140
|
-
# Calculate relative path
|
|
186
|
+
# Calculate relative path from project root
|
|
141
187
|
try:
|
|
142
|
-
rel_path =
|
|
188
|
+
rel_path = package_dir.relative_to(self.project_root)
|
|
143
189
|
packages_path = str(rel_path).replace("\\", "/")
|
|
144
190
|
except ValueError:
|
|
145
191
|
packages_path = None
|
|
146
192
|
|
|
147
|
-
# If
|
|
193
|
+
# If package_dir has Python files but no __init__.py, we need to make it a package
|
|
148
194
|
# or include it as a module directory
|
|
149
195
|
if has_py_files and not has_init:
|
|
150
196
|
# For flat structures, we include the directory itself
|
|
@@ -298,7 +344,8 @@ class SubfolderBuildConfig:
|
|
|
298
344
|
if not self.version:
|
|
299
345
|
raise ValueError("Version is required for subfolder builds")
|
|
300
346
|
|
|
301
|
-
# Ensure src_dir is a package (has __init__.py)
|
|
347
|
+
# Ensure src_dir is a package (has __init__.py) before creating temp directory
|
|
348
|
+
# This way the __init__.py will be copied to the temp directory
|
|
302
349
|
init_file = self.src_dir / "__init__.py"
|
|
303
350
|
if not init_file.exists():
|
|
304
351
|
# Create a temporary __init__.py to make it a package
|
|
@@ -307,6 +354,13 @@ class SubfolderBuildConfig:
|
|
|
307
354
|
else:
|
|
308
355
|
self._temp_init_created = False
|
|
309
356
|
|
|
357
|
+
# Create temporary package directory with correct import name
|
|
358
|
+
# This will copy the __init__.py we just created (if any)
|
|
359
|
+
self._create_temp_package_directory()
|
|
360
|
+
|
|
361
|
+
# Determine which directory to use (temp package dir or src_dir)
|
|
362
|
+
package_dir = self._temp_package_dir if self._temp_package_dir and self._temp_package_dir.exists() else self.src_dir
|
|
363
|
+
|
|
310
364
|
# Check if pyproject.toml exists in subfolder
|
|
311
365
|
subfolder_pyproject = self.src_dir / "pyproject.toml"
|
|
312
366
|
if subfolder_pyproject.exists():
|
|
@@ -1206,6 +1260,18 @@ class SubfolderBuildConfig:
|
|
|
1206
1260
|
self.original_pyproject_path = None
|
|
1207
1261
|
self._used_subfolder_pyproject = False
|
|
1208
1262
|
|
|
1263
|
+
# Remove temporary package directory if it exists
|
|
1264
|
+
if self._temp_package_dir and self._temp_package_dir.exists():
|
|
1265
|
+
try:
|
|
1266
|
+
shutil.rmtree(self._temp_package_dir)
|
|
1267
|
+
print(f"Removed temporary package directory: {self._temp_package_dir}")
|
|
1268
|
+
except Exception as e:
|
|
1269
|
+
print(
|
|
1270
|
+
f"Warning: Could not remove temporary package directory {self._temp_package_dir}: {e}",
|
|
1271
|
+
file=sys.stderr,
|
|
1272
|
+
)
|
|
1273
|
+
self._temp_package_dir = None
|
|
1274
|
+
|
|
1209
1275
|
def __enter__(self) -> Self:
|
|
1210
1276
|
"""Context manager entry."""
|
|
1211
1277
|
return self
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-package-folder
|
|
3
|
-
Version:
|
|
3
|
+
Version: 6.0.0
|
|
4
4
|
Summary: Python package to automatically package and build a folder, fetching all relevant dependencies.
|
|
5
5
|
Project-URL: Repository, https://github.com/alelom/python-package-folder
|
|
6
6
|
Author-email: Alessio Lombardi <work@alelom.com>
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
python_package_folder/__init__.py,sha256=DQt-uldOEKfh0MUqCvKdeNKOnpuOvpb7blYvXMyO9Wc,719
|
|
2
2
|
python_package_folder/__main__.py,sha256=a-__-VLhYw-J7S7CsHdhtEvQr3RiAZxiYDvKhKTgMX4,291
|
|
3
|
-
python_package_folder/analyzer.py,sha256=
|
|
3
|
+
python_package_folder/analyzer.py,sha256=7AfJCN29aY7dSYB5H6t1xnHbT-hCZsLtYN7i27pAgMU,15191
|
|
4
4
|
python_package_folder/finder.py,sha256=RPidZ7LKCFuQ_KgCFIZdHWPXsZIDor3M4C0hKeYW7EI,11799
|
|
5
|
-
python_package_folder/manager.py,sha256=
|
|
5
|
+
python_package_folder/manager.py,sha256=erX8uPu3KA593wsSH9o15O8MNc6mwIZgGGqUJf7Z5i0,59423
|
|
6
6
|
python_package_folder/publisher.py,sha256=fmf3l0zMY9CD49gurxlXyvm9mOP0FzDjmiSt0yDqt1M,18813
|
|
7
7
|
python_package_folder/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
python_package_folder/python_package_folder.py,sha256=QZ-vdOZ40wF-eGbp39JotDhMwIhhT3Z_sC5obQj3i4s,17024
|
|
9
|
-
python_package_folder/subfolder_build.py,sha256=
|
|
9
|
+
python_package_folder/subfolder_build.py,sha256=4H6XDb8nxpjLPKlTixXy-PtX0JbW8jiMZvxn7Hgbqs0,55883
|
|
10
10
|
python_package_folder/types.py,sha256=3yeSRR5p_3PDKEAaehW_RJ7NwJHexOIeA08bGaT1iSY,2368
|
|
11
11
|
python_package_folder/utils.py,sha256=b6Ukcc0fctXdxS5zhGLS86kqn0vz1yOEK7XjCY9fjfY,5621
|
|
12
12
|
python_package_folder/version.py,sha256=kIDP6S9trEfs9gj7lBYGxrWm4RPssRla24UtlO9Jkh4,9111
|
|
13
13
|
python_package_folder/version_calculator.py,sha256=_gcc8IbMjt27UxePcc7RZlFTMCG3AGnRI_-Mp_4qRG0,39568
|
|
14
|
-
python_package_folder-
|
|
15
|
-
python_package_folder-
|
|
16
|
-
python_package_folder-
|
|
17
|
-
python_package_folder-
|
|
18
|
-
python_package_folder-
|
|
14
|
+
python_package_folder-6.0.0.dist-info/METADATA,sha256=UeEwTJf4IxlZpjf-ekl7lX4tyxf-MgqCANxi16aDENc,7838
|
|
15
|
+
python_package_folder-6.0.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
16
|
+
python_package_folder-6.0.0.dist-info/entry_points.txt,sha256=ttu4wAhoYSHGhWQNercLz9IVTTpXxhVlRA9vSTvaLe0,91
|
|
17
|
+
python_package_folder-6.0.0.dist-info/licenses/LICENSE,sha256=vNgRJh8YiecqZoZld7TtwPI5I72HIymKD9g32fiJjCE,1073
|
|
18
|
+
python_package_folder-6.0.0.dist-info/RECORD,,
|
|
File without changes
|
{python_package_folder-5.4.0.dist-info → python_package_folder-6.0.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{python_package_folder-5.4.0.dist-info → python_package_folder-6.0.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|