python-package-folder 5.1.6__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.1.6 → python_package_folder-5.2.1}/PKG-INFO +1 -1
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/pyproject.toml +1 -1
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/publisher.py +50 -2
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/version_calculator.py +160 -17
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/test_version_calculator.py +54 -2
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/.copier-answers.yml +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/.cursor/plans/optional_version_+_semantic-release_efed88a6.plan.md +0 -0
- {python_package_folder-5.1.6 → 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.1.6 → python_package_folder-5.2.1}/.cursor/rules/general.mdc +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/.cursor/rules/python.mdc +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/.github/workflows/ci.yml +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/.github/workflows/publish.yml +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/.gitignore +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/.vscode/settings.json +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/LICENSE +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/MANIFEST.in +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/Makefile +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/README.md +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/coverage.svg +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/development.md +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/docs/DEVELOPMENT.md +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/docs/INSTALLATION.md +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/docs/PUBLISHING.md +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/docs/REFERENCE.md +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/docs/USAGE.md +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/docs/VERSION_RESOLUTION.md +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/installation.md +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/publishing.md +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/__init__.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/__main__.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/analyzer.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/finder.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/manager.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/py.typed +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/python_package_folder.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/subfolder_build.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/types.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/utils.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/version.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/conftest.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/folder_structure/some_globals.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/folder_structure/subfolder_to_build/README.md +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/folder_structure/subfolder_to_build/__init__.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/folder_structure/subfolder_to_build/some_function.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/folder_structure/subfolder_to_build/some_globals.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/folder_structure/utility_folder/_SS/some_superseded_file.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/folder_structure/utility_folder/some_utility.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/test_build_with_external_deps.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/test_exclude_patterns.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/test_linting.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/test_preserve_directory_structure.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/test_publisher.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/test_shared_subdirectory_imports.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/test_spreadsheet_creation_imports.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/test_subfolder_build.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/test_third_party_dependencies.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/test_utils.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/test_version_manager.py +0 -0
- {python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/tests.py +0 -0
- {python_package_folder-5.1.6 → 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.1
|
|
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.1.6 → 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:
|
|
@@ -13,6 +13,7 @@ from __future__ import annotations
|
|
|
13
13
|
import logging
|
|
14
14
|
import re
|
|
15
15
|
import subprocess
|
|
16
|
+
from html.parser import HTMLParser
|
|
16
17
|
from pathlib import Path
|
|
17
18
|
|
|
18
19
|
import requests
|
|
@@ -110,6 +111,76 @@ def _query_pypi_version(package_name: str, registry: str) -> str | None:
|
|
|
110
111
|
return None
|
|
111
112
|
|
|
112
113
|
|
|
114
|
+
class SimpleIndexParser(HTMLParser):
|
|
115
|
+
"""Parser for PEP 503 simple index HTML to extract package versions."""
|
|
116
|
+
|
|
117
|
+
def __init__(self, package_name: str):
|
|
118
|
+
super().__init__()
|
|
119
|
+
self.package_name = package_name
|
|
120
|
+
self.versions: set[str] = set()
|
|
121
|
+
self.in_anchor = False
|
|
122
|
+
self.current_href = ""
|
|
123
|
+
self.links_processed = 0
|
|
124
|
+
|
|
125
|
+
def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
|
|
126
|
+
if tag == "a":
|
|
127
|
+
self.in_anchor = True
|
|
128
|
+
# Extract href attribute
|
|
129
|
+
for attr_name, attr_value in attrs:
|
|
130
|
+
if attr_name == "href" and attr_value:
|
|
131
|
+
self.current_href = attr_value
|
|
132
|
+
break
|
|
133
|
+
|
|
134
|
+
def handle_data(self, data: str) -> None:
|
|
135
|
+
if self.in_anchor:
|
|
136
|
+
# Extract version from link text or href
|
|
137
|
+
# Format: package-name-version-... or package-name-version.tar.gz
|
|
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)
|
|
145
|
+
# Also check href if it contains version info
|
|
146
|
+
if self.current_href:
|
|
147
|
+
logger.debug(f"Processing href: '{self.current_href}'")
|
|
148
|
+
version = self._extract_version_from_filename(self.current_href)
|
|
149
|
+
if version:
|
|
150
|
+
logger.debug(f"Extracted version '{version}' from href: '{self.current_href}'")
|
|
151
|
+
self.versions.add(version)
|
|
152
|
+
|
|
153
|
+
def handle_endtag(self, tag: str) -> None:
|
|
154
|
+
if tag == "a":
|
|
155
|
+
self.links_processed += 1
|
|
156
|
+
self.in_anchor = False
|
|
157
|
+
self.current_href = ""
|
|
158
|
+
|
|
159
|
+
def _extract_version_from_filename(self, filename: str) -> str | None:
|
|
160
|
+
"""Extract version number from package filename."""
|
|
161
|
+
# Pattern: package-name-version-... or package-name-version.tar.gz
|
|
162
|
+
# Examples: data-0.1.0-py3-none-any.whl, data-0.1.0.tar.gz
|
|
163
|
+
# The version is between the package name and the next separator
|
|
164
|
+
|
|
165
|
+
# Normalize package name (replace - with _ for matching)
|
|
166
|
+
normalized_package = self.package_name.replace("-", "_").replace(".", "_")
|
|
167
|
+
|
|
168
|
+
# Try to match: package-name-version- or package-name-version.
|
|
169
|
+
# Version format: X.Y.Z (semantic versioning)
|
|
170
|
+
pattern = rf"{re.escape(self.package_name)}-(\d+\.\d+\.\d+(?:\.\d+)?(?:[a-zA-Z0-9]+)?)"
|
|
171
|
+
match = re.search(pattern, filename, re.IGNORECASE)
|
|
172
|
+
if match:
|
|
173
|
+
return match.group(1)
|
|
174
|
+
|
|
175
|
+
# Fallback: try with normalized package name
|
|
176
|
+
pattern = rf"{re.escape(normalized_package)}-(\d+\.\d+\.\d+(?:\.\d+)?(?:[a-zA-Z0-9]+)?)"
|
|
177
|
+
match = re.search(pattern, filename, re.IGNORECASE)
|
|
178
|
+
if match:
|
|
179
|
+
return match.group(1)
|
|
180
|
+
|
|
181
|
+
return None
|
|
182
|
+
|
|
183
|
+
|
|
113
184
|
def _query_azure_artifacts_version(
|
|
114
185
|
package_name: str,
|
|
115
186
|
repository_url: str,
|
|
@@ -117,8 +188,8 @@ def _query_azure_artifacts_version(
|
|
|
117
188
|
"""
|
|
118
189
|
Query Azure Artifacts for the latest version.
|
|
119
190
|
|
|
120
|
-
Azure Artifacts uses a simple index format (HTML)
|
|
121
|
-
|
|
191
|
+
Azure Artifacts uses a simple index format (HTML) following PEP 503.
|
|
192
|
+
Parses the HTML to extract version numbers from package filenames.
|
|
122
193
|
|
|
123
194
|
Args:
|
|
124
195
|
package_name: Package name to query
|
|
@@ -135,35 +206,107 @@ def _query_azure_artifacts_version(
|
|
|
135
206
|
simple_index_url = repository_url.replace("/upload", f"/simple/{package_name}/")
|
|
136
207
|
else:
|
|
137
208
|
simple_index_url = repository_url.rstrip("/") + f"/simple/{package_name}/"
|
|
138
|
-
logger.
|
|
209
|
+
logger.info(f"Constructed Azure Artifacts simple index URL: {simple_index_url}")
|
|
139
210
|
except Exception as e:
|
|
140
211
|
logger.warning(f"Error constructing Azure Artifacts URL for '{package_name}': {e}")
|
|
141
212
|
return None
|
|
142
213
|
|
|
143
214
|
try:
|
|
144
|
-
|
|
145
|
-
|
|
215
|
+
logger.info(f"Fetching Azure Artifacts simple index for '{package_name}'...")
|
|
216
|
+
response = requests.get(simple_index_url, timeout=10)
|
|
217
|
+
logger.info(f"Azure Artifacts response: status={response.status_code}, content_length={len(response.text)} bytes")
|
|
146
218
|
|
|
147
219
|
if response.status_code == 401:
|
|
148
|
-
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
|
+
)
|
|
225
|
+
return None
|
|
149
226
|
elif response.status_code == 403:
|
|
150
|
-
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
|
+
)
|
|
232
|
+
return None
|
|
151
233
|
elif response.status_code == 404:
|
|
152
|
-
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
|
+
)
|
|
238
|
+
return None
|
|
153
239
|
elif response.status_code != 200:
|
|
154
|
-
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
|
+
)
|
|
244
|
+
return None
|
|
155
245
|
|
|
156
|
-
#
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
246
|
+
# Parse HTML to extract versions
|
|
247
|
+
logger.info(f"Parsing HTML response to extract versions for '{package_name}'...")
|
|
248
|
+
parser = SimpleIndexParser(package_name)
|
|
249
|
+
try:
|
|
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
|
+
)
|
|
255
|
+
except Exception as e:
|
|
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
|
+
)
|
|
261
|
+
return None
|
|
262
|
+
|
|
263
|
+
if not parser.versions:
|
|
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
|
+
)
|
|
278
|
+
return None
|
|
279
|
+
|
|
280
|
+
# Find the latest version
|
|
281
|
+
versions = list(parser.versions)
|
|
282
|
+
logger.info(f"Found {len(versions)} version(s) in Azure Artifacts HTML: {versions}")
|
|
283
|
+
|
|
284
|
+
# Sort versions to find the latest
|
|
285
|
+
try:
|
|
286
|
+
sorted_versions = sorted(versions, key=_parse_version_for_sort, reverse=True)
|
|
287
|
+
latest_version = sorted_versions[0]
|
|
288
|
+
logger.info(f"Latest version on Azure Artifacts for '{package_name}': {latest_version}")
|
|
289
|
+
return latest_version
|
|
290
|
+
except Exception as e:
|
|
291
|
+
logger.warning(
|
|
292
|
+
f"Error sorting versions for '{package_name}': {e}. "
|
|
293
|
+
f"Versions found: {versions}. Using first version as fallback."
|
|
294
|
+
)
|
|
295
|
+
# Fallback: return the first version found
|
|
296
|
+
return versions[0]
|
|
297
|
+
|
|
162
298
|
except requests.RequestException as e:
|
|
163
|
-
logger.warning(
|
|
299
|
+
logger.warning(
|
|
300
|
+
f"Network error querying Azure Artifacts for '{package_name}': {e}. "
|
|
301
|
+
f"URL: {simple_index_url}"
|
|
302
|
+
)
|
|
164
303
|
return None
|
|
165
304
|
except Exception as e:
|
|
166
|
-
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
|
+
)
|
|
167
310
|
return None
|
|
168
311
|
|
|
169
312
|
|
{python_package_folder-5.1.6 → python_package_folder-5.2.1}/tests/test_version_calculator.py
RENAMED
|
@@ -86,9 +86,22 @@ class TestQueryRegistryVersion:
|
|
|
86
86
|
|
|
87
87
|
@patch("python_package_folder.version_calculator.requests.get")
|
|
88
88
|
def test_query_azure_artifacts_version(self, mock_get: MagicMock) -> None:
|
|
89
|
-
"""Test querying Azure Artifacts
|
|
89
|
+
"""Test querying Azure Artifacts with HTML parsing."""
|
|
90
90
|
mock_response = Mock()
|
|
91
91
|
mock_response.status_code = 200
|
|
92
|
+
# Simulate PEP 503 simple index HTML response
|
|
93
|
+
mock_response.text = """<!DOCTYPE html>
|
|
94
|
+
<html>
|
|
95
|
+
<head>
|
|
96
|
+
<title>Links for test-package</title>
|
|
97
|
+
</head>
|
|
98
|
+
<body>
|
|
99
|
+
<h1>Links for test-package</h1>
|
|
100
|
+
<a href="test-package-0.1.0-py3-none-any.whl">test-package-0.1.0-py3-none-any.whl</a>
|
|
101
|
+
<a href="test-package-0.2.0-py3-none-any.whl">test-package-0.2.0-py3-none-any.whl</a>
|
|
102
|
+
<a href="test-package-0.1.5.tar.gz">test-package-0.1.5.tar.gz</a>
|
|
103
|
+
</body>
|
|
104
|
+
</html>"""
|
|
92
105
|
mock_get.return_value = mock_response
|
|
93
106
|
|
|
94
107
|
version = query_registry_version(
|
|
@@ -96,7 +109,46 @@ class TestQueryRegistryVersion:
|
|
|
96
109
|
"azure",
|
|
97
110
|
repository_url="https://pkgs.dev.azure.com/ORG/PROJECT/_packaging/FEED/pypi/upload",
|
|
98
111
|
)
|
|
99
|
-
#
|
|
112
|
+
# Should parse HTML and return the latest version
|
|
113
|
+
assert version == "0.2.0"
|
|
114
|
+
|
|
115
|
+
@patch("python_package_folder.version_calculator.requests.get")
|
|
116
|
+
def test_query_azure_artifacts_version_not_found(self, mock_get: MagicMock) -> None:
|
|
117
|
+
"""Test querying Azure Artifacts when package doesn't exist (404)."""
|
|
118
|
+
mock_response = Mock()
|
|
119
|
+
mock_response.status_code = 404
|
|
120
|
+
mock_get.return_value = mock_response
|
|
121
|
+
|
|
122
|
+
version = query_registry_version(
|
|
123
|
+
"test-package",
|
|
124
|
+
"azure",
|
|
125
|
+
repository_url="https://pkgs.dev.azure.com/ORG/PROJECT/_packaging/FEED/pypi/upload",
|
|
126
|
+
)
|
|
127
|
+
# Should return None for 404 (first release)
|
|
128
|
+
assert version is None
|
|
129
|
+
|
|
130
|
+
@patch("python_package_folder.version_calculator.requests.get")
|
|
131
|
+
def test_query_azure_artifacts_version_empty_html(self, mock_get: MagicMock) -> None:
|
|
132
|
+
"""Test querying Azure Artifacts with empty HTML (no versions)."""
|
|
133
|
+
mock_response = Mock()
|
|
134
|
+
mock_response.status_code = 200
|
|
135
|
+
mock_response.text = """<!DOCTYPE html>
|
|
136
|
+
<html>
|
|
137
|
+
<head>
|
|
138
|
+
<title>Links for test-package</title>
|
|
139
|
+
</head>
|
|
140
|
+
<body>
|
|
141
|
+
<h1>Links for test-package</h1>
|
|
142
|
+
</body>
|
|
143
|
+
</html>"""
|
|
144
|
+
mock_get.return_value = mock_response
|
|
145
|
+
|
|
146
|
+
version = query_registry_version(
|
|
147
|
+
"test-package",
|
|
148
|
+
"azure",
|
|
149
|
+
repository_url="https://pkgs.dev.azure.com/ORG/PROJECT/_packaging/FEED/pypi/upload",
|
|
150
|
+
)
|
|
151
|
+
# Should return None when no versions found in HTML
|
|
100
152
|
assert version is None
|
|
101
153
|
|
|
102
154
|
@patch("python_package_folder.version_calculator.requests.get")
|
|
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.1.6 → python_package_folder-5.2.1}/src/python_package_folder/__init__.py
RENAMED
|
File without changes
|
{python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/__main__.py
RENAMED
|
File without changes
|
{python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/analyzer.py
RENAMED
|
File without changes
|
{python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/finder.py
RENAMED
|
File without changes
|
{python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/manager.py
RENAMED
|
File without changes
|
{python_package_folder-5.1.6 → 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.1.6 → python_package_folder-5.2.1}/src/python_package_folder/types.py
RENAMED
|
File without changes
|
{python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/utils.py
RENAMED
|
File without changes
|
{python_package_folder-5.1.6 → python_package_folder-5.2.1}/src/python_package_folder/version.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_package_folder-5.1.6 → 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.1.6 → 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.1.6 → python_package_folder-5.2.1}/tests/test_third_party_dependencies.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|