quickpub 3.0.6__tar.gz → 3.0.61__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 (55) hide show
  1. {quickpub-3.0.6/quickpub.egg-info → quickpub-3.0.61}/PKG-INFO +2 -2
  2. {quickpub-3.0.6 → quickpub-3.0.61}/README.md +1 -1
  3. {quickpub-3.0.6 → quickpub-3.0.61}/pyproject.toml +1 -1
  4. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/logging_.py +16 -10
  5. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/quality_assurance_runners/unittest_qa_runner.py +116 -116
  6. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/upload_targets/pypirc_upload_target.py +75 -75
  7. {quickpub-3.0.6 → quickpub-3.0.61/quickpub.egg-info}/PKG-INFO +2 -2
  8. {quickpub-3.0.6 → quickpub-3.0.61}/LICENSE +0 -0
  9. {quickpub-3.0.6 → quickpub-3.0.61}/MANIFEST.in +0 -0
  10. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/__init__.py +0 -0
  11. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/__main__.py +0 -0
  12. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/classifiers.py +0 -0
  13. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/enforcers.py +0 -0
  14. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/files.py +0 -0
  15. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/functions.py +0 -0
  16. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/proxy.py +0 -0
  17. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/py.typed +0 -0
  18. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/qa.py +0 -0
  19. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/__init__.py +0 -0
  20. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/build_schema.py +0 -0
  21. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/constraint_enforcer.py +0 -0
  22. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/__init__.py +0 -0
  23. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/build_schemas/__init__.py +0 -0
  24. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/build_schemas/setuptools_build_schema.py +0 -0
  25. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/constraint_enforcers/__init__.py +0 -0
  26. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/constraint_enforcers/license_enforcer.py +0 -0
  27. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/constraint_enforcers/local_version_enforcer.py +0 -0
  28. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/constraint_enforcers/pypi_remote_version_enforcer.py +0 -0
  29. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/constraint_enforcers/pypirc_enforcer.py +0 -0
  30. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/constraint_enforcers/readme_enforcer.py +0 -0
  31. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/python_providers/__init__.py +0 -0
  32. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/python_providers/conda_python_provider.py +0 -0
  33. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/python_providers/default_python_provider.py +0 -0
  34. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/quality_assurance_runners/__init__.py +0 -0
  35. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/quality_assurance_runners/mypy_qa_runner.py +0 -0
  36. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/quality_assurance_runners/pylint_qa_runner.py +0 -0
  37. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/quality_assurance_runners/pytest_qa_runner.py +0 -0
  38. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/upload_targets/__init__.py +0 -0
  39. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/implementations/upload_targets/github_upload_target.py +0 -0
  40. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/python_provider.py +0 -0
  41. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/quality_assurance_runner.py +0 -0
  42. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/quickpub_strategy.py +0 -0
  43. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/strategies/upload_target.py +0 -0
  44. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/structures/__init__.py +0 -0
  45. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/structures/bound.py +0 -0
  46. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/structures/dependency.py +0 -0
  47. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/structures/version.py +0 -0
  48. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/validators.py +0 -0
  49. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub/worker_pool.py +0 -0
  50. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub.egg-info/SOURCES.txt +0 -0
  51. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub.egg-info/dependency_links.txt +0 -0
  52. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub.egg-info/requires.txt +0 -0
  53. {quickpub-3.0.6 → quickpub-3.0.61}/quickpub.egg-info/top_level.txt +0 -0
  54. {quickpub-3.0.6 → quickpub-3.0.61}/setup.cfg +0 -0
  55. {quickpub-3.0.6 → quickpub-3.0.61}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quickpub
3
- Version: 3.0.6
3
+ Version: 3.0.61
4
4
  Summary: A python package to quickly configure and publish a new package
5
5
  Author-email: danielnachumdev <danielnachumdev@gmail.com>
6
6
  License: MIT License
@@ -38,7 +38,7 @@ Requires-Dist: requests
38
38
  Requires-Dist: fire
39
39
  Dynamic: license-file
40
40
 
41
- # QuickPub v3.0.0
41
+ # QuickPub v3.0.61
42
42
 
43
43
  [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
44
44
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
@@ -1,4 +1,4 @@
1
- # QuickPub v3.0.0
1
+ # QuickPub v3.0.61
2
2
 
3
3
  [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
4
4
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "quickpub"
7
- version = "3.0.6"
7
+ version = "3.0.61"
8
8
  authors = [
9
9
  { name = "danielnachumdev", email = "danielnachumdev@gmail.com" },
10
10
  ]
@@ -4,14 +4,16 @@ import sys
4
4
  # Global log level that can be modified by users
5
5
  _LOG_LEVEL = logging.INFO
6
6
 
7
+
7
8
  class QuickpubLogFilter(logging.Filter):
8
9
  """
9
10
  Filter that only allows logs from the quickpub package.
10
11
  """
11
-
12
+
12
13
  def filter(self, record):
13
14
  return record.name.startswith('quickpub')
14
15
 
16
+
15
17
  class TqdmLoggingHandler(logging.Handler):
16
18
  """
17
19
  Custom logging handler that uses tqdm.write to avoid conflicts with progress bars.
@@ -32,6 +34,7 @@ class TqdmLoggingHandler(logging.Handler):
32
34
  except Exception:
33
35
  self.handleError(record)
34
36
 
37
+
35
38
  def setup_logging(level: int = None):
36
39
  """
37
40
  Set up logging with appropriate handler based on tqdm availability.
@@ -46,11 +49,11 @@ def setup_logging(level: int = None):
46
49
  Configured logger instance
47
50
  """
48
51
  global _LOG_LEVEL
49
-
52
+
50
53
  # Use provided level or fall back to global constant
51
54
  if level is not None:
52
55
  _LOG_LEVEL = level
53
-
56
+
54
57
  logger = logging.getLogger()
55
58
  logger.setLevel(_LOG_LEVEL)
56
59
 
@@ -58,8 +61,9 @@ def setup_logging(level: int = None):
58
61
  logger.handlers.clear()
59
62
 
60
63
  # Common formatter for both handlers
64
+ # Example output: 2024-01-15 10:30:45,123 - INFO - [quickpub.some_module - some_module.py:42] - This is a log message
61
65
  formatter = logging.Formatter(
62
- '%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
66
+ '%(asctime)s - %(levelname)s - [%(name)s - %(filename)s:%(lineno)d] - %(message)s'
63
67
  )
64
68
 
65
69
  try:
@@ -71,33 +75,35 @@ def setup_logging(level: int = None):
71
75
  handler = logging.StreamHandler(sys.stdout)
72
76
  handler.setLevel(_LOG_LEVEL)
73
77
  handler.setFormatter(formatter)
74
-
78
+
75
79
  # Add filter to only allow quickpub logs
76
80
  handler.addFilter(QuickpubLogFilter())
77
-
81
+
78
82
  logger.addHandler(handler)
79
83
 
84
+
80
85
  def set_log_level(level: int):
81
86
  """
82
87
  Set the logging level for the root logger and all its handlers.
83
-
88
+
84
89
  This function allows end users to dynamically change the log level
85
90
  after logging has been set up. It also updates the global _LOG_LEVEL
86
91
  constant so that future calls to setup_logging() will use this level.
87
-
92
+
88
93
  Args:
89
94
  level: Logging level (e.g., logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR)
90
95
  """
91
96
  global _LOG_LEVEL
92
97
  _LOG_LEVEL = level
93
-
98
+
94
99
  logger = logging.getLogger()
95
100
  logger.setLevel(level)
96
-
101
+
97
102
  # Update all handlers to use the new level
98
103
  for handler in logger.handlers:
99
104
  handler.setLevel(level)
100
105
 
106
+
101
107
  __all__ = [
102
108
  "setup_logging",
103
109
  "set_log_level",
@@ -1,116 +1,116 @@
1
- import logging
2
- import re
3
- import os
4
- from pathlib import Path
5
- from typing import Optional, List
6
- from danielutils import get_current_working_directory, set_current_working_directory, LayeredCommand, warning
7
-
8
- from ....enforcers import ExitEarlyError
9
- from ...quality_assurance_runner import QualityAssuranceRunner
10
-
11
- logger = logging.getLogger(__name__)
12
-
13
-
14
- def _removesuffix(string: str, suffix: str) -> str:
15
- """Remove a suffix from a string.
16
-
17
- Replace this with str.removesuffix() from stdlib when minimum Python
18
- version is 3.9.
19
- """
20
- if suffix and string.endswith(suffix):
21
- return string[: -len(suffix)]
22
- return string
23
-
24
-
25
- class UnittestRunner(QualityAssuranceRunner):
26
- """Quality assurance runner for unittest testing."""
27
- NUM_TESTS_PATTERN: re.Pattern = re.compile(
28
- r"Ran (\d+) tests? in \d+\.\d+s")
29
- NUM_FAILED_PATTERN: re.Pattern = re.compile(
30
- r"FAILED \((?:failures=(\d+))?(?:, )?(?:errors=(\d+))?(?:, )?(?:skipped=(\d+))?\)|"
31
- r"FAILED \((?:errors=(\d+))?(?:, )?(?:failures=(\d+))?(?:, )?(?:skipped=(\d+))?\)|"
32
- r"FAILED \((?:skipped=(\d+))?(?:, )?(?:failures=(\d+))?(?:, )?(?:errors=(\d+))?\)")
33
-
34
- def _install_dependencies(self, base: LayeredCommand) -> None:
35
- return None
36
-
37
- def _pre_command(self) -> None:
38
- pass
39
-
40
- def _post_command(self):
41
- pass
42
- # set_current_working_directory(self._cwd)
43
-
44
- def __init__(self, target: Optional[str] = "./tests", bound: str = ">=0.8", no_tests_score: float = 0) -> None:
45
- QualityAssuranceRunner.__init__(
46
- self, name="unittest", bound=bound, target=target)
47
- self.no_tests_score = no_tests_score
48
- logger.info("Initialized UnittestRunner with target='%s', bound='%s', no_tests_score=%s", target, bound,
49
- no_tests_score)
50
-
51
- def _build_command(self, src: str, *args, use_system_interpreter: bool = False) -> str:
52
- command: str = self.get_executable()
53
- rel = _removesuffix(os.path.relpath(
54
- src, self.target), src.lstrip("./\\"))
55
- command += f" discover -s {rel}"
56
- normalized_target_path = Path(
57
- os.path.join(os.getcwd(), self.target)).resolve()
58
- # This is for concurrency reasons
59
- return f"cd {normalized_target_path} & {command} & cd {Path(os.getcwd()).resolve()}"
60
-
61
- def _calculate_score(self, ret: int, lines: List[str], *, verbose: bool = False) -> float:
62
- logger.debug("Calculating unittest score from test results")
63
-
64
- try:
65
- num_tests_ran_line = lines[-3]
66
- num_tests_failed_line = lines[-1]
67
- num_tests = int(self.NUM_TESTS_PATTERN.match(
68
- num_tests_ran_line).group(1))
69
- if num_tests == 0:
70
- logger.debug(
71
- "No tests found, returning no_tests_score: %s", self.no_tests_score)
72
- return self.no_tests_score
73
-
74
- num_failed = 0
75
- num_errors = 0
76
- if num_tests_failed_line != "OK":
77
- if num_tests_failed_line.startswith("FAILED"):
78
- m = self.NUM_FAILED_PATTERN.match(num_tests_failed_line)
79
- if m:
80
- # Handle different group positions based on the pattern that matched
81
- groups = m.groups()
82
- if groups[0] is not None: # failures= first pattern
83
- num_failed = int(groups[0] or "0")
84
- num_errors = int(groups[1] or "0")
85
- elif groups[3] is not None: # errors= first pattern
86
- num_failed = int(groups[4] or "0")
87
- num_errors = int(groups[3] or "0")
88
- elif groups[6] is not None: # skipped= first pattern
89
- num_failed = int(groups[7] or "0")
90
- num_errors = int(groups[8] or "0")
91
- else:
92
- # Fallback to original logic if no groups match
93
- num_failed = int(m.group(1) or "0")
94
- num_errors = int(m.group(2) or "0")
95
- else:
96
- # If regex doesn't match, treat as malformed
97
- raise ValueError(
98
- f"Failed to parse FAILED line: {num_tests_failed_line}")
99
- elif num_tests_failed_line.startswith("OK"):
100
- # 'OK (skipped=3)' could it also be other stuff?
101
- pass
102
-
103
- score = 1 - ((num_failed + num_errors) / num_tests)
104
- logger.debug("Unittest score calculated: %.3f (tests: %d, failed: %d, errors: %d)", score, num_tests,
105
- num_failed, num_errors)
106
- return score
107
-
108
- except Exception as e:
109
- logger.error("Failed to calculate unittest score: %s", e)
110
- raise ExitEarlyError(f"Failed running Unittest, got exit code {ret}. "
111
- f"try running manually using: {self._build_command('TARGET')}") from e
112
-
113
-
114
- __all__ = [
115
- 'UnittestRunner',
116
- ]
1
+ import logging
2
+ import re
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Optional, List
6
+ from danielutils import get_current_working_directory, set_current_working_directory, LayeredCommand, warning
7
+
8
+ from ....enforcers import ExitEarlyError
9
+ from ...quality_assurance_runner import QualityAssuranceRunner
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ def _removesuffix(string: str, suffix: str) -> str:
15
+ """Remove a suffix from a string.
16
+
17
+ Replace this with str.removesuffix() from stdlib when minimum Python
18
+ version is 3.9.
19
+ """
20
+ if suffix and string.endswith(suffix):
21
+ return string[: -len(suffix)]
22
+ return string
23
+
24
+
25
+ class UnittestRunner(QualityAssuranceRunner):
26
+ """Quality assurance runner for unittest testing."""
27
+ NUM_TESTS_PATTERN: re.Pattern = re.compile(
28
+ r"Ran (\d+) tests? in \d+\.\d+s")
29
+ NUM_FAILED_PATTERN: re.Pattern = re.compile(
30
+ r"FAILED \((?:failures=(\d+))?(?:, )?(?:errors=(\d+))?(?:, )?(?:skipped=(\d+))?\)|"
31
+ r"FAILED \((?:errors=(\d+))?(?:, )?(?:failures=(\d+))?(?:, )?(?:skipped=(\d+))?\)|"
32
+ r"FAILED \((?:skipped=(\d+))?(?:, )?(?:failures=(\d+))?(?:, )?(?:errors=(\d+))?\)")
33
+
34
+ def _install_dependencies(self, base: LayeredCommand) -> None:
35
+ return None
36
+
37
+ def _pre_command(self) -> None:
38
+ pass
39
+
40
+ def _post_command(self):
41
+ pass
42
+ # set_current_working_directory(self._cwd)
43
+
44
+ def __init__(self, target: Optional[str] = "./tests", bound: str = ">=0.8", no_tests_score: float = 0) -> None:
45
+ QualityAssuranceRunner.__init__(
46
+ self, name="unittest", bound=bound, target=target)
47
+ self.no_tests_score = no_tests_score
48
+ logger.info("Initialized UnittestRunner with target='%s', bound='%s', no_tests_score=%s", target, bound,
49
+ no_tests_score)
50
+
51
+ def _build_command(self, src: str, *args, use_system_interpreter: bool = False) -> str:
52
+ command: str = self.get_executable()
53
+ rel = _removesuffix(os.path.relpath(
54
+ src, self.target), src.lstrip("./\\"))
55
+ command += f" discover -s {rel}"
56
+ normalized_target_path = Path(
57
+ os.path.join(os.getcwd(), self.target)).resolve()
58
+ # This is for concurrency reasons
59
+ return f"cd {normalized_target_path} & {command} & cd {Path(os.getcwd()).resolve()}"
60
+
61
+ def _calculate_score(self, ret: int, lines: List[str], *, verbose: bool = False) -> float:
62
+ logger.debug("Calculating unittest score from test results")
63
+
64
+ try:
65
+ num_tests_ran_line = lines[-3]
66
+ num_tests_failed_line = lines[-1]
67
+ num_tests = int(self.NUM_TESTS_PATTERN.match(
68
+ num_tests_ran_line).group(1))
69
+ if num_tests == 0:
70
+ logger.debug(
71
+ "No tests found, returning no_tests_score: %s", self.no_tests_score)
72
+ return self.no_tests_score
73
+
74
+ num_failed = 0
75
+ num_errors = 0
76
+ if num_tests_failed_line != "OK":
77
+ if num_tests_failed_line.startswith("FAILED"):
78
+ m = self.NUM_FAILED_PATTERN.match(num_tests_failed_line)
79
+ if m:
80
+ # Handle different group positions based on the pattern that matched
81
+ groups = m.groups()
82
+ if groups[0] is not None: # failures= first pattern
83
+ num_failed = int(groups[0] or "0")
84
+ num_errors = int(groups[1] or "0")
85
+ elif groups[3] is not None: # errors= first pattern
86
+ num_failed = int(groups[4] or "0")
87
+ num_errors = int(groups[3] or "0")
88
+ elif groups[6] is not None: # skipped= first pattern
89
+ num_failed = int(groups[7] or "0")
90
+ num_errors = int(groups[8] or "0")
91
+ else:
92
+ # Fallback to original logic if no groups match
93
+ num_failed = int(m.group(1) or "0")
94
+ num_errors = int(m.group(2) or "0")
95
+ else:
96
+ # If regex doesn't match, treat as malformed
97
+ raise ValueError(
98
+ f"Failed to parse FAILED line: {num_tests_failed_line}")
99
+ elif num_tests_failed_line.startswith("OK"):
100
+ # 'OK (skipped=3)' could it also be other stuff?
101
+ pass
102
+
103
+ score = 1 - ((num_failed + num_errors) / num_tests)
104
+ logger.debug("Unittest score calculated: %.3f (tests: %d, failed: %d, errors: %d)", score, num_tests,
105
+ num_failed, num_errors)
106
+ return score
107
+
108
+ except Exception as e:
109
+ logger.error("Failed to calculate unittest score: %s", e)
110
+ raise ExitEarlyError(f"Failed running Unittest, got exit code {ret}. "
111
+ f"try running manually using: {self._build_command('TARGET')}") from e
112
+
113
+
114
+ __all__ = [
115
+ 'UnittestRunner',
116
+ ]
@@ -1,75 +1,75 @@
1
- import logging
2
- import re
3
-
4
- from danielutils import file_exists
5
-
6
- from ..constraint_enforcers import PypircEnforcer
7
- from ...upload_target import UploadTarget
8
-
9
- logger = logging.getLogger(__name__)
10
-
11
-
12
- class PypircUploadTarget(UploadTarget):
13
- """Upload target implementation using .pypirc configuration."""
14
- REGEX_PATTERN: re.Pattern = PypircEnforcer.PYPIRC_REGEX
15
-
16
- def upload(self, name: str, version: str, **kwargs) -> None: # type: ignore
17
- from quickpub.proxy import cm
18
- from quickpub.enforcers import exit_if
19
-
20
- logger.info(
21
- "Starting PyPI upload for package '%s' version '%s'", name, version)
22
-
23
- self._validate_file_exists()
24
- self._validate_file_contents()
25
-
26
- if self.verbose:
27
- logger.info("Uploading package to PyPI")
28
-
29
- ret, stdout, stderr = cm("twine", "upload", "--config-file", ".pypirc",
30
- f"dist/{name}-{version}.tar.gz")
31
-
32
- if ret != 0:
33
- logger.error(
34
- "PyPI upload failed with return code %d: %s", ret, stderr)
35
- exit_if(
36
- ret != 0,
37
- f"Failed uploading the package to pypi. Try running the following command manually:\n\ttwine upload --config-file .pypirc dist/{name}-{version}.tar.gz"
38
- )
39
-
40
- logger.info(
41
- "Successfully uploaded package '%s' version '%s' to PyPI", name, version)
42
-
43
- def _validate_file_exists(self) -> None:
44
- logger.debug("Validating .pypirc file exists at '%s'",
45
- self.pypirc_file_path)
46
- if not file_exists(self.pypirc_file_path):
47
- logger.error(".pypirc file not found at '%s'",
48
- self.pypirc_file_path)
49
- raise RuntimeError(
50
- f"{self.__class__.__name__} can't find pypirc file at '{self.pypirc_file_path}'")
51
- logger.debug(".pypirc file found at '%s'", self.pypirc_file_path)
52
-
53
- def _validate_file_contents(self) -> None:
54
- logger.debug("Validating .pypirc file contents at '%s'",
55
- self.pypirc_file_path)
56
- with open(self.pypirc_file_path, "r", encoding="utf8") as f:
57
- text = f.read()
58
- if not self.REGEX_PATTERN.match(text):
59
- logger.error(
60
- ".pypirc file contents validation failed for '%s'", self.pypirc_file_path)
61
- raise RuntimeError(
62
- f"{self.__class__.__name__} checked the contents of '{self.pypirc_file_path}' and it failed to match the following regex: r\"{self.REGEX_PATTERN.pattern}\"")
63
- logger.debug(
64
- ".pypirc file contents validation passed for '%s'", self.pypirc_file_path)
65
-
66
- def __init__(self, pypirc_file_path: str = "./.pypirc", verbose: bool = False) -> None:
67
- super().__init__(verbose)
68
- self.pypirc_file_path = pypirc_file_path
69
- logger.info("Initialized PypircUploadTarget with pypirc_file_path='%s', verbose=%s",
70
- pypirc_file_path, verbose)
71
-
72
-
73
- __all__ = [
74
- "PypircUploadTarget"
75
- ]
1
+ import logging
2
+ import re
3
+
4
+ from danielutils import file_exists
5
+
6
+ from ..constraint_enforcers import PypircEnforcer
7
+ from ...upload_target import UploadTarget
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class PypircUploadTarget(UploadTarget):
13
+ """Upload target implementation using .pypirc configuration."""
14
+ REGEX_PATTERN: re.Pattern = PypircEnforcer.PYPIRC_REGEX
15
+
16
+ def upload(self, name: str, version: str, **kwargs) -> None: # type: ignore
17
+ from quickpub.proxy import cm
18
+ from quickpub.enforcers import exit_if
19
+
20
+ logger.info(
21
+ "Starting PyPI upload for package '%s' version '%s'", name, version)
22
+
23
+ self._validate_file_exists()
24
+ self._validate_file_contents()
25
+
26
+ if self.verbose:
27
+ logger.info("Uploading package to PyPI")
28
+
29
+ ret, stdout, stderr = cm("twine", "upload", "--config-file", ".pypirc",
30
+ f"dist/{name}-{version}.tar.gz")
31
+
32
+ if ret != 0:
33
+ logger.error(
34
+ "PyPI upload failed with return code %d: %s", ret, stderr)
35
+ exit_if(
36
+ ret != 0,
37
+ f"Failed uploading the package to pypi. Try running the following command manually:\n\ttwine upload --config-file .pypirc dist/{name}-{version}.tar.gz"
38
+ )
39
+
40
+ logger.info(
41
+ "Successfully uploaded package '%s' version '%s' to PyPI", name, version)
42
+
43
+ def _validate_file_exists(self) -> None:
44
+ logger.debug("Validating .pypirc file exists at '%s'",
45
+ self.pypirc_file_path)
46
+ if not file_exists(self.pypirc_file_path):
47
+ logger.error(".pypirc file not found at '%s'",
48
+ self.pypirc_file_path)
49
+ raise RuntimeError(
50
+ f"{self.__class__.__name__} can't find pypirc file at '{self.pypirc_file_path}'")
51
+ logger.debug(".pypirc file found at '%s'", self.pypirc_file_path)
52
+
53
+ def _validate_file_contents(self) -> None:
54
+ logger.debug("Validating .pypirc file contents at '%s'",
55
+ self.pypirc_file_path)
56
+ with open(self.pypirc_file_path, "r", encoding="utf8") as f:
57
+ text = f.read()
58
+ if not self.REGEX_PATTERN.match(text):
59
+ logger.error(
60
+ ".pypirc file contents validation failed for '%s'", self.pypirc_file_path)
61
+ raise RuntimeError(
62
+ f"{self.__class__.__name__} checked the contents of '{self.pypirc_file_path}' and it failed to match the following regex: r\"{self.REGEX_PATTERN.pattern}\"")
63
+ logger.debug(
64
+ ".pypirc file contents validation passed for '%s'", self.pypirc_file_path)
65
+
66
+ def __init__(self, pypirc_file_path: str = "./.pypirc", verbose: bool = False) -> None:
67
+ super().__init__(verbose)
68
+ self.pypirc_file_path = pypirc_file_path
69
+ logger.info("Initialized PypircUploadTarget with pypirc_file_path='%s', verbose=%s",
70
+ pypirc_file_path, verbose)
71
+
72
+
73
+ __all__ = [
74
+ "PypircUploadTarget"
75
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quickpub
3
- Version: 3.0.6
3
+ Version: 3.0.61
4
4
  Summary: A python package to quickly configure and publish a new package
5
5
  Author-email: danielnachumdev <danielnachumdev@gmail.com>
6
6
  License: MIT License
@@ -38,7 +38,7 @@ Requires-Dist: requests
38
38
  Requires-Dist: fire
39
39
  Dynamic: license-file
40
40
 
41
- # QuickPub v3.0.0
41
+ # QuickPub v3.0.61
42
42
 
43
43
  [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
44
44
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
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