python-package-folder 5.2.0__tar.gz → 5.2.1__tar.gz
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-5.2.0 → python_package_folder-5.2.1}/PKG-INFO +1 -1
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/pyproject.toml +1 -1
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/publisher.py +50 -2
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/version_calculator.py +71 -16
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/.copier-answers.yml +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/.cursor/plans/optional_version_+_semantic-release_efed88a6.plan.md +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/.cursor/plans/replace_node.js_semantic-release_with_custom_python_implementation_64e05e1a.plan.md +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/.cursor/rules/general.mdc +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/.cursor/rules/python.mdc +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/.github/workflows/ci.yml +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/.github/workflows/publish.yml +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/.gitignore +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/.vscode/settings.json +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/LICENSE +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/MANIFEST.in +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/Makefile +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/README.md +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/coverage.svg +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/development.md +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/docs/DEVELOPMENT.md +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/docs/INSTALLATION.md +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/docs/PUBLISHING.md +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/docs/REFERENCE.md +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/docs/USAGE.md +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/docs/VERSION_RESOLUTION.md +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/installation.md +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/publishing.md +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/__init__.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/__main__.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/analyzer.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/finder.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/manager.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/py.typed +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/python_package_folder.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/subfolder_build.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/types.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/utils.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/version.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/conftest.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/folder_structure/some_globals.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/folder_structure/subfolder_to_build/README.md +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/folder_structure/subfolder_to_build/__init__.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/folder_structure/subfolder_to_build/some_function.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/folder_structure/subfolder_to_build/some_globals.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/folder_structure/utility_folder/_SS/some_superseded_file.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/folder_structure/utility_folder/some_utility.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/test_build_with_external_deps.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/test_exclude_patterns.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/test_linting.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/test_preserve_directory_structure.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/test_publisher.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/test_shared_subdirectory_imports.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/test_spreadsheet_creation_imports.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/test_subfolder_build.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/test_third_party_dependencies.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/test_utils.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/test_version_calculator.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/test_version_manager.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/tests.py +0 -0
- {python_package_folder-5.2.0 → python_package_folder-5.2.1}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-package-folder
|
|
3
|
-
Version: 5.2.
|
|
3
|
+
Version: 5.2.1
|
|
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>
|
{python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/publisher.py
RENAMED
|
@@ -310,11 +310,59 @@ class Publisher:
|
|
|
310
310
|
print(f"Files to upload: {len(dist_files)}")
|
|
311
311
|
|
|
312
312
|
try:
|
|
313
|
-
subprocess.run(
|
|
313
|
+
result = subprocess.run(
|
|
314
|
+
cmd,
|
|
315
|
+
check=True,
|
|
316
|
+
text=True,
|
|
317
|
+
capture_output=True,
|
|
318
|
+
)
|
|
319
|
+
# Print twine output if available
|
|
320
|
+
if result.stdout:
|
|
321
|
+
print(result.stdout)
|
|
322
|
+
if result.stderr:
|
|
323
|
+
print(result.stderr, file=sys.stderr)
|
|
314
324
|
print(f"\n✓ Successfully published to {self.repository.value}")
|
|
315
325
|
except subprocess.CalledProcessError as e:
|
|
316
326
|
print(f"\n✗ Failed to publish to {self.repository.value}", file=sys.stderr)
|
|
317
|
-
|
|
327
|
+
|
|
328
|
+
# Extract and display twine's actual error message
|
|
329
|
+
error_details = []
|
|
330
|
+
if e.stdout:
|
|
331
|
+
error_details.append(f"stdout: {e.stdout}")
|
|
332
|
+
if e.stderr:
|
|
333
|
+
error_details.append(f"stderr: {e.stderr}")
|
|
334
|
+
if e.returncode is not None:
|
|
335
|
+
error_details.append(f"exit code: {e.returncode}")
|
|
336
|
+
|
|
337
|
+
if error_details:
|
|
338
|
+
print("Twine error details:", file=sys.stderr)
|
|
339
|
+
for detail in error_details:
|
|
340
|
+
print(f" {detail}", file=sys.stderr)
|
|
341
|
+
else:
|
|
342
|
+
# Fallback to generic error if no output captured
|
|
343
|
+
print(f"Command failed: {' '.join(cmd)}", file=sys.stderr)
|
|
344
|
+
print(f"Return code: {e.returncode}", file=sys.stderr)
|
|
345
|
+
|
|
346
|
+
# Provide helpful hints based on common errors
|
|
347
|
+
if e.returncode == 1:
|
|
348
|
+
if e.stderr and ("already exists" in e.stderr.lower() or "409" in e.stderr or "conflict" in e.stderr.lower()):
|
|
349
|
+
print(
|
|
350
|
+
"\nHint: This version may already exist on the repository. "
|
|
351
|
+
"Use --skip-existing to skip files that already exist, "
|
|
352
|
+
"or publish a new version.",
|
|
353
|
+
file=sys.stderr,
|
|
354
|
+
)
|
|
355
|
+
elif e.stderr and ("401" in e.stderr or "unauthorized" in e.stderr.lower()):
|
|
356
|
+
print(
|
|
357
|
+
"\nHint: Authentication failed. Check your credentials.",
|
|
358
|
+
file=sys.stderr,
|
|
359
|
+
)
|
|
360
|
+
elif e.stderr and ("403" in e.stderr or "forbidden" in e.stderr.lower()):
|
|
361
|
+
print(
|
|
362
|
+
"\nHint: Access forbidden. Check your permissions for this repository.",
|
|
363
|
+
file=sys.stderr,
|
|
364
|
+
)
|
|
365
|
+
|
|
318
366
|
raise
|
|
319
367
|
|
|
320
368
|
def publish_interactive(self, skip_existing: bool = False) -> None:
|
|
@@ -120,6 +120,7 @@ class SimpleIndexParser(HTMLParser):
|
|
|
120
120
|
self.versions: set[str] = set()
|
|
121
121
|
self.in_anchor = False
|
|
122
122
|
self.current_href = ""
|
|
123
|
+
self.links_processed = 0
|
|
123
124
|
|
|
124
125
|
def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
|
|
125
126
|
if tag == "a":
|
|
@@ -134,17 +135,24 @@ class SimpleIndexParser(HTMLParser):
|
|
|
134
135
|
if self.in_anchor:
|
|
135
136
|
# Extract version from link text or href
|
|
136
137
|
# Format: package-name-version-... or package-name-version.tar.gz
|
|
137
|
-
|
|
138
|
-
if
|
|
139
|
-
|
|
138
|
+
link_text = data.strip()
|
|
139
|
+
if link_text:
|
|
140
|
+
logger.debug(f"Processing link text: '{link_text}'")
|
|
141
|
+
version = self._extract_version_from_filename(link_text)
|
|
142
|
+
if version:
|
|
143
|
+
logger.debug(f"Extracted version '{version}' from link text: '{link_text}'")
|
|
144
|
+
self.versions.add(version)
|
|
140
145
|
# Also check href if it contains version info
|
|
141
146
|
if self.current_href:
|
|
147
|
+
logger.debug(f"Processing href: '{self.current_href}'")
|
|
142
148
|
version = self._extract_version_from_filename(self.current_href)
|
|
143
149
|
if version:
|
|
150
|
+
logger.debug(f"Extracted version '{version}' from href: '{self.current_href}'")
|
|
144
151
|
self.versions.add(version)
|
|
145
152
|
|
|
146
153
|
def handle_endtag(self, tag: str) -> None:
|
|
147
154
|
if tag == "a":
|
|
155
|
+
self.links_processed += 1
|
|
148
156
|
self.in_anchor = False
|
|
149
157
|
self.current_href = ""
|
|
150
158
|
|
|
@@ -198,60 +206,107 @@ def _query_azure_artifacts_version(
|
|
|
198
206
|
simple_index_url = repository_url.replace("/upload", f"/simple/{package_name}/")
|
|
199
207
|
else:
|
|
200
208
|
simple_index_url = repository_url.rstrip("/") + f"/simple/{package_name}/"
|
|
201
|
-
logger.
|
|
209
|
+
logger.info(f"Constructed Azure Artifacts simple index URL: {simple_index_url}")
|
|
202
210
|
except Exception as e:
|
|
203
211
|
logger.warning(f"Error constructing Azure Artifacts URL for '{package_name}': {e}")
|
|
204
212
|
return None
|
|
205
213
|
|
|
206
214
|
try:
|
|
215
|
+
logger.info(f"Fetching Azure Artifacts simple index for '{package_name}'...")
|
|
207
216
|
response = requests.get(simple_index_url, timeout=10)
|
|
208
|
-
logger.
|
|
217
|
+
logger.info(f"Azure Artifacts response: status={response.status_code}, content_length={len(response.text)} bytes")
|
|
209
218
|
|
|
210
219
|
if response.status_code == 401:
|
|
211
|
-
logger.warning(
|
|
220
|
+
logger.warning(
|
|
221
|
+
f"Authentication required for Azure Artifacts (401). "
|
|
222
|
+
f"Package '{package_name}' may require authentication to query. "
|
|
223
|
+
f"URL: {simple_index_url}"
|
|
224
|
+
)
|
|
212
225
|
return None
|
|
213
226
|
elif response.status_code == 403:
|
|
214
|
-
logger.warning(
|
|
227
|
+
logger.warning(
|
|
228
|
+
f"Access forbidden for Azure Artifacts (403). "
|
|
229
|
+
f"Package '{package_name}' may not be accessible or requires different permissions. "
|
|
230
|
+
f"URL: {simple_index_url}"
|
|
231
|
+
)
|
|
215
232
|
return None
|
|
216
233
|
elif response.status_code == 404:
|
|
217
|
-
logger.
|
|
234
|
+
logger.info(
|
|
235
|
+
f"Package '{package_name}' not found on Azure Artifacts (404) - this appears to be the first release. "
|
|
236
|
+
f"URL: {simple_index_url}"
|
|
237
|
+
)
|
|
218
238
|
return None
|
|
219
239
|
elif response.status_code != 200:
|
|
220
|
-
logger.warning(
|
|
240
|
+
logger.warning(
|
|
241
|
+
f"Unexpected status code {response.status_code} from Azure Artifacts for '{package_name}'. "
|
|
242
|
+
f"URL: {simple_index_url}, Response preview: {response.text[:200]}"
|
|
243
|
+
)
|
|
221
244
|
return None
|
|
222
245
|
|
|
223
246
|
# Parse HTML to extract versions
|
|
247
|
+
logger.info(f"Parsing HTML response to extract versions for '{package_name}'...")
|
|
224
248
|
parser = SimpleIndexParser(package_name)
|
|
225
249
|
try:
|
|
226
250
|
parser.feed(response.text)
|
|
251
|
+
logger.info(
|
|
252
|
+
f"HTML parsing completed: processed {parser.links_processed} link(s), "
|
|
253
|
+
f"found {len(parser.versions)} unique version(s)"
|
|
254
|
+
)
|
|
227
255
|
except Exception as e:
|
|
228
|
-
logger.warning(
|
|
256
|
+
logger.warning(
|
|
257
|
+
f"Error parsing Azure Artifacts HTML for '{package_name}': {e}. "
|
|
258
|
+
f"Response length: {len(response.text)} bytes, "
|
|
259
|
+
f"Response preview: {response.text[:500]}"
|
|
260
|
+
)
|
|
229
261
|
return None
|
|
230
262
|
|
|
231
263
|
if not parser.versions:
|
|
232
|
-
|
|
264
|
+
if parser.links_processed == 0:
|
|
265
|
+
logger.info(
|
|
266
|
+
f"No links found in Azure Artifacts HTML for '{package_name}'. "
|
|
267
|
+
f"This may indicate: (1) HTML structure differs from PEP 503 format, "
|
|
268
|
+
f"(2) package doesn't exist, or (3) authentication required. "
|
|
269
|
+
f"Response preview: {response.text[:500]}"
|
|
270
|
+
)
|
|
271
|
+
else:
|
|
272
|
+
logger.info(
|
|
273
|
+
f"Found {parser.links_processed} link(s) but no versions extracted for '{package_name}'. "
|
|
274
|
+
f"This may indicate: (1) package name mismatch (expected '{package_name}'), "
|
|
275
|
+
f"(2) filename format differs from expected pattern, or (3) first release. "
|
|
276
|
+
f"Response preview: {response.text[:500]}"
|
|
277
|
+
)
|
|
233
278
|
return None
|
|
234
279
|
|
|
235
280
|
# Find the latest version
|
|
236
281
|
versions = list(parser.versions)
|
|
237
|
-
logger.
|
|
282
|
+
logger.info(f"Found {len(versions)} version(s) in Azure Artifacts HTML: {versions}")
|
|
238
283
|
|
|
239
284
|
# Sort versions to find the latest
|
|
240
285
|
try:
|
|
241
286
|
sorted_versions = sorted(versions, key=_parse_version_for_sort, reverse=True)
|
|
242
287
|
latest_version = sorted_versions[0]
|
|
243
|
-
logger.info(f"
|
|
288
|
+
logger.info(f"Latest version on Azure Artifacts for '{package_name}': {latest_version}")
|
|
244
289
|
return latest_version
|
|
245
290
|
except Exception as e:
|
|
246
|
-
logger.warning(
|
|
291
|
+
logger.warning(
|
|
292
|
+
f"Error sorting versions for '{package_name}': {e}. "
|
|
293
|
+
f"Versions found: {versions}. Using first version as fallback."
|
|
294
|
+
)
|
|
247
295
|
# Fallback: return the first version found
|
|
248
296
|
return versions[0]
|
|
249
297
|
|
|
250
298
|
except requests.RequestException as e:
|
|
251
|
-
logger.warning(
|
|
299
|
+
logger.warning(
|
|
300
|
+
f"Network error querying Azure Artifacts for '{package_name}': {e}. "
|
|
301
|
+
f"URL: {simple_index_url}"
|
|
302
|
+
)
|
|
252
303
|
return None
|
|
253
304
|
except Exception as e:
|
|
254
|
-
logger.warning(
|
|
305
|
+
logger.warning(
|
|
306
|
+
f"Unexpected error querying Azure Artifacts for '{package_name}': {e}. "
|
|
307
|
+
f"URL: {simple_index_url}",
|
|
308
|
+
exc_info=True
|
|
309
|
+
)
|
|
255
310
|
return None
|
|
256
311
|
|
|
257
312
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/__init__.py
RENAMED
|
File without changes
|
{python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/__main__.py
RENAMED
|
File without changes
|
{python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/analyzer.py
RENAMED
|
File without changes
|
{python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/finder.py
RENAMED
|
File without changes
|
{python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/manager.py
RENAMED
|
File without changes
|
{python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/types.py
RENAMED
|
File without changes
|
{python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/utils.py
RENAMED
|
File without changes
|
{python_package_folder-5.2.0 → python_package_folder-5.2.1}/src/python_package_folder/version.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/folder_structure/some_globals.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/test_build_with_external_deps.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/test_third_party_dependencies.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_package_folder-5.2.0 → python_package_folder-5.2.1}/tests/test_version_calculator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|