python-package-folder 5.2.1__tar.gz → 5.2.2__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.
Files changed (60) hide show
  1. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/PKG-INFO +1 -1
  2. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/coverage.svg +2 -2
  3. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/pyproject.toml +1 -1
  4. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/src/python_package_folder/python_package_folder.py +19 -0
  5. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/src/python_package_folder/version_calculator.py +23 -4
  6. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/test_version_calculator.py +33 -0
  7. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/.copier-answers.yml +0 -0
  8. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/.cursor/plans/optional_version_+_semantic-release_efed88a6.plan.md +0 -0
  9. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/.cursor/plans/replace_node.js_semantic-release_with_custom_python_implementation_64e05e1a.plan.md +0 -0
  10. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/.cursor/rules/general.mdc +0 -0
  11. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/.cursor/rules/python.mdc +0 -0
  12. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/.github/workflows/ci.yml +0 -0
  13. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/.github/workflows/publish.yml +0 -0
  14. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/.gitignore +0 -0
  15. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/.vscode/settings.json +0 -0
  16. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/LICENSE +0 -0
  17. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/MANIFEST.in +0 -0
  18. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/Makefile +0 -0
  19. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/README.md +0 -0
  20. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/development.md +0 -0
  21. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/docs/DEVELOPMENT.md +0 -0
  22. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/docs/INSTALLATION.md +0 -0
  23. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/docs/PUBLISHING.md +0 -0
  24. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/docs/REFERENCE.md +0 -0
  25. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/docs/USAGE.md +0 -0
  26. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/docs/VERSION_RESOLUTION.md +0 -0
  27. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/installation.md +0 -0
  28. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/publishing.md +0 -0
  29. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/src/python_package_folder/__init__.py +0 -0
  30. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/src/python_package_folder/__main__.py +0 -0
  31. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/src/python_package_folder/analyzer.py +0 -0
  32. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/src/python_package_folder/finder.py +0 -0
  33. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/src/python_package_folder/manager.py +0 -0
  34. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/src/python_package_folder/publisher.py +0 -0
  35. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/src/python_package_folder/py.typed +0 -0
  36. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/src/python_package_folder/subfolder_build.py +0 -0
  37. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/src/python_package_folder/types.py +0 -0
  38. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/src/python_package_folder/utils.py +0 -0
  39. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/src/python_package_folder/version.py +0 -0
  40. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/conftest.py +0 -0
  41. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/folder_structure/some_globals.py +0 -0
  42. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/folder_structure/subfolder_to_build/README.md +0 -0
  43. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/folder_structure/subfolder_to_build/__init__.py +0 -0
  44. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/folder_structure/subfolder_to_build/some_function.py +0 -0
  45. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/folder_structure/subfolder_to_build/some_globals.py +0 -0
  46. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/folder_structure/utility_folder/_SS/some_superseded_file.py +0 -0
  47. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/folder_structure/utility_folder/some_utility.py +0 -0
  48. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/test_build_with_external_deps.py +0 -0
  49. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/test_exclude_patterns.py +0 -0
  50. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/test_linting.py +0 -0
  51. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/test_preserve_directory_structure.py +0 -0
  52. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/test_publisher.py +0 -0
  53. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/test_shared_subdirectory_imports.py +0 -0
  54. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/test_spreadsheet_creation_imports.py +0 -0
  55. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/test_subfolder_build.py +0 -0
  56. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/test_third_party_dependencies.py +0 -0
  57. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/test_utils.py +0 -0
  58. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/test_version_manager.py +0 -0
  59. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/tests/tests.py +0 -0
  60. {python_package_folder-5.2.1 → python_package_folder-5.2.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-package-folder
3
- Version: 5.2.1
3
+ Version: 5.2.2
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>
@@ -14,7 +14,7 @@
14
14
  <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
15
15
  <text x="31.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
16
16
  <text x="31.5" y="14">coverage</text>
17
- <text x="81" y="15" fill="#010101" fill-opacity=".3">68%</text>
18
- <text x="81" y="14">68%</text>
17
+ <text x="81" y="15" fill="#010101" fill-opacity=".3">67%</text>
18
+ <text x="81" y="14">67%</text>
19
19
  </g>
20
20
  </svg>
@@ -43,7 +43,7 @@ dependencies = [
43
43
 
44
44
  # ---- Dev dependencies ----
45
45
 
46
- version = "5.2.1"
46
+ version = "5.2.2"
47
47
  [dependency-groups]
48
48
  dev = [
49
49
  "pytest>=8.3.5",
@@ -186,6 +186,21 @@ def main() -> int:
186
186
  repository = args.publish if args.publish else None
187
187
  repository_url = args.repository_url if args.publish else None
188
188
 
189
+ # Get credentials for authenticated registry queries (especially Azure Artifacts)
190
+ # Try to get them from args or environment variables
191
+ query_username = args.username
192
+ query_password = args.password
193
+
194
+ # If not provided via args, check environment variables (for Azure Artifacts)
195
+ if repository == "azure" and not query_username:
196
+ query_username = os.getenv("TWINE_USERNAME") or os.getenv("PYPI_USERNAME")
197
+ if repository == "azure" and not query_password:
198
+ query_password = (
199
+ os.getenv("TWINE_PASSWORD")
200
+ or os.getenv("PYPI_PASSWORD")
201
+ or os.getenv("AZURE_ARTIFACTS_TOKEN")
202
+ )
203
+
189
204
  if is_subfolder:
190
205
  # Workflow 1: subfolder build
191
206
  # src_dir is guaranteed to be relative to project_root due to is_subfolder check
@@ -199,6 +214,8 @@ def main() -> int:
199
214
  subfolder_path=subfolder_rel_path,
200
215
  repository=repository,
201
216
  repository_url=repository_url,
217
+ username=query_username,
218
+ password=query_password,
202
219
  )
203
220
  else:
204
221
  # Workflow 2: main package
@@ -221,6 +238,8 @@ def main() -> int:
221
238
  subfolder_path=None,
222
239
  repository=repository,
223
240
  repository_url=repository_url,
241
+ username=query_username,
242
+ password=query_password,
224
243
  )
225
244
 
226
245
  if resolved_version:
@@ -25,6 +25,8 @@ def query_registry_version(
25
25
  package_name: str,
26
26
  repository: str,
27
27
  repository_url: str | None = None,
28
+ username: str | None = None,
29
+ password: str | None = None,
28
30
  ) -> str | None:
29
31
  """
30
32
  Query package registry for the latest published version.
@@ -33,6 +35,8 @@ def query_registry_version(
33
35
  package_name: Package name to query
34
36
  repository: Repository type ('pypi', 'testpypi', or 'azure')
35
37
  repository_url: Repository URL (required for Azure Artifacts)
38
+ username: Optional username for authenticated queries (Azure Artifacts)
39
+ password: Optional password/token for authenticated queries (Azure Artifacts)
36
40
 
37
41
  Returns:
38
42
  Latest version string or None if not found/unsupported
@@ -54,7 +58,7 @@ def query_registry_version(
54
58
  logger.warning("Azure Artifacts repository URL not provided")
55
59
  return None
56
60
  logger.info(f"Querying Azure Artifacts for package '{package_name}' at {repository_url}")
57
- version = _query_azure_artifacts_version(package_name, repository_url)
61
+ version = _query_azure_artifacts_version(package_name, repository_url, username, password)
58
62
  if version:
59
63
  logger.info(f"Found version {version} on Azure Artifacts")
60
64
  else:
@@ -184,6 +188,8 @@ class SimpleIndexParser(HTMLParser):
184
188
  def _query_azure_artifacts_version(
185
189
  package_name: str,
186
190
  repository_url: str,
191
+ username: str | None = None,
192
+ password: str | None = None,
187
193
  ) -> str | None:
188
194
  """
189
195
  Query Azure Artifacts for the latest version.
@@ -194,6 +200,8 @@ def _query_azure_artifacts_version(
194
200
  Args:
195
201
  package_name: Package name to query
196
202
  repository_url: Azure Artifacts repository URL
203
+ username: Optional username for authentication
204
+ password: Optional password/token for authentication
197
205
 
198
206
  Returns:
199
207
  Latest version string or None if not found/unsupported
@@ -212,8 +220,15 @@ def _query_azure_artifacts_version(
212
220
  return None
213
221
 
214
222
  try:
215
- logger.info(f"Fetching Azure Artifacts simple index for '{package_name}'...")
216
- response = requests.get(simple_index_url, timeout=10)
223
+ # Prepare authentication if credentials are provided
224
+ auth = None
225
+ if username and password:
226
+ auth = (username, password)
227
+ logger.info(f"Fetching Azure Artifacts simple index for '{package_name}' with authentication...")
228
+ else:
229
+ logger.info(f"Fetching Azure Artifacts simple index for '{package_name}' (no authentication)...")
230
+
231
+ response = requests.get(simple_index_url, auth=auth, timeout=10)
217
232
  logger.info(f"Azure Artifacts response: status={response.status_code}, content_length={len(response.text)} bytes")
218
233
 
219
234
  if response.status_code == 401:
@@ -628,6 +643,8 @@ def resolve_version(
628
643
  subfolder_path: Path | None = None,
629
644
  repository: str | None = None,
630
645
  repository_url: str | None = None,
646
+ username: str | None = None,
647
+ password: str | None = None,
631
648
  ) -> tuple[str | None, str | None]:
632
649
  """
633
650
  Resolve the next version using conventional commits.
@@ -640,6 +657,8 @@ def resolve_version(
640
657
  subfolder_path: Optional path to subfolder (relative to project_root)
641
658
  repository: Optional target repository ('pypi', 'testpypi', or 'azure')
642
659
  repository_url: Optional repository URL (required for Azure Artifacts)
660
+ username: Optional username for authenticated registry queries (Azure Artifacts)
661
+ password: Optional password/token for authenticated registry queries (Azure Artifacts)
643
662
 
644
663
  Returns:
645
664
  Tuple of (version string if a release is determined, error message if any)
@@ -651,7 +670,7 @@ def resolve_version(
651
670
  baseline_version = None
652
671
  if repository and package_name:
653
672
  logger.info(f"Attempting to query {repository} for baseline version of '{package_name}'")
654
- baseline_version = query_registry_version(package_name, repository, repository_url)
673
+ baseline_version = query_registry_version(package_name, repository, repository_url, username, password)
655
674
 
656
675
  # Step 2: Fallback to git tags if registry query failed
657
676
  if not baseline_version:
@@ -151,6 +151,39 @@ class TestQueryRegistryVersion:
151
151
  # Should return None when no versions found in HTML
152
152
  assert version is None
153
153
 
154
+ @patch("python_package_folder.version_calculator.requests.get")
155
+ def test_query_azure_artifacts_version_with_auth(self, mock_get: MagicMock) -> None:
156
+ """Test querying Azure Artifacts with authentication."""
157
+ mock_response = Mock()
158
+ mock_response.status_code = 200
159
+ mock_response.text = """<!DOCTYPE html>
160
+ <html>
161
+ <head>
162
+ <title>Links for test-package</title>
163
+ </head>
164
+ <body>
165
+ <h1>Links for test-package</h1>
166
+ <a href="test-package-1.0.0-py3-none-any.whl">test-package-1.0.0-py3-none-any.whl</a>
167
+ <a href="test-package-1.1.0-py3-none-any.whl">test-package-1.1.0-py3-none-any.whl</a>
168
+ </body>
169
+ </html>"""
170
+ mock_get.return_value = mock_response
171
+
172
+ version = query_registry_version(
173
+ "test-package",
174
+ "azure",
175
+ repository_url="https://pkgs.dev.azure.com/ORG/PROJECT/_packaging/FEED/pypi/upload",
176
+ username="testuser",
177
+ password="testtoken",
178
+ )
179
+ # Should parse HTML and return the latest version
180
+ assert version == "1.1.0"
181
+
182
+ # Verify authentication was used
183
+ mock_get.assert_called_once()
184
+ call_args = mock_get.call_args
185
+ assert call_args[1]["auth"] == ("testuser", "testtoken")
186
+
154
187
  @patch("python_package_folder.version_calculator.requests.get")
155
188
  def test_query_registry_version_error_handling(self, mock_get: MagicMock) -> None:
156
189
  """Test that registry query errors are handled gracefully."""