quickpub 4.1.0__tar.gz → 4.1.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.
Files changed (66) hide show
  1. quickpub-4.1.1/PKG-INFO +177 -0
  2. quickpub-4.1.1/README.md +136 -0
  3. {quickpub-4.1.0 → quickpub-4.1.1}/pyproject.toml +1 -1
  4. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/__init__.py +1 -1
  5. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/__main__.py +1 -1
  6. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/package_managers/installed_package_parser.py +2 -2
  7. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/python_providers/default_python_provider.py +3 -1
  8. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/python_providers/union_provider.py +3 -3
  9. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/python_providers/uv_python_provider.py +3 -1
  10. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/quality_assurance_runners/pylint_qa_runner.py +28 -16
  11. quickpub-4.1.1/quickpub.egg-info/PKG-INFO +177 -0
  12. quickpub-4.1.0/PKG-INFO +0 -455
  13. quickpub-4.1.0/README.md +0 -414
  14. quickpub-4.1.0/quickpub.egg-info/PKG-INFO +0 -455
  15. {quickpub-4.1.0 → quickpub-4.1.1}/LICENSE +0 -0
  16. {quickpub-4.1.0 → quickpub-4.1.1}/MANIFEST.in +0 -0
  17. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/classifiers.py +0 -0
  18. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/enforcers.py +0 -0
  19. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/files.py +0 -0
  20. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/logging_.py +0 -0
  21. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/package_manager_detection.py +0 -0
  22. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/proxy.py +0 -0
  23. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/py.typed +0 -0
  24. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/qa.py +0 -0
  25. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/__init__.py +0 -0
  26. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/build_schema.py +0 -0
  27. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/constraint_enforcer.py +0 -0
  28. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/__init__.py +0 -0
  29. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/build_schemas/__init__.py +0 -0
  30. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/build_schemas/setuptools_build_schema.py +0 -0
  31. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/constraint_enforcers/__init__.py +0 -0
  32. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/constraint_enforcers/license_enforcer.py +0 -0
  33. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/constraint_enforcers/local_version_enforcer.py +0 -0
  34. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/constraint_enforcers/pypi_remote_version_enforcer.py +0 -0
  35. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/constraint_enforcers/pypirc_enforcer.py +0 -0
  36. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/constraint_enforcers/readme_enforcer.py +0 -0
  37. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/package_managers/__init__.py +0 -0
  38. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/package_managers/pip_package_manager.py +0 -0
  39. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/package_managers/uv_package_manager.py +0 -0
  40. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/python_providers/__init__.py +0 -0
  41. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/python_providers/conda_python_provider.py +0 -0
  42. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/quality_assurance_runners/__init__.py +0 -0
  43. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/quality_assurance_runners/mypy_qa_runner.py +0 -0
  44. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/quality_assurance_runners/pytest_qa_runner.py +0 -0
  45. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/quality_assurance_runners/unittest_qa_runner.py +0 -0
  46. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/upload_targets/__init__.py +0 -0
  47. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/upload_targets/github_upload_target.py +0 -0
  48. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/implementations/upload_targets/pypirc_upload_target.py +0 -0
  49. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/package_manager.py +0 -0
  50. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/python_provider.py +0 -0
  51. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/quality_assurance_runner.py +0 -0
  52. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/quickpub_strategy.py +0 -0
  53. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/strategies/upload_target.py +0 -0
  54. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/structures/__init__.py +0 -0
  55. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/structures/bound.py +0 -0
  56. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/structures/dependency.py +0 -0
  57. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/structures/version.py +0 -0
  58. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/validators.py +0 -0
  59. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub/worker_pool.py +0 -0
  60. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub.egg-info/SOURCES.txt +0 -0
  61. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub.egg-info/dependency_links.txt +0 -0
  62. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub.egg-info/entry_points.txt +0 -0
  63. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub.egg-info/requires.txt +0 -0
  64. {quickpub-4.1.0 → quickpub-4.1.1}/quickpub.egg-info/top_level.txt +0 -0
  65. {quickpub-4.1.0 → quickpub-4.1.1}/setup.cfg +0 -0
  66. {quickpub-4.1.0 → quickpub-4.1.1}/setup.py +0 -0
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.4
2
+ Name: quickpub
3
+ Version: 4.1.1
4
+ Summary: A local CI/CD simulation tool that runs quality checks, tests, and validations locally before publishing Python packages, ensuring higher build pass rates and faster feedback loops
5
+ Author-email: danielnachumdev <danielnachumdev@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2022 danielnachumdev
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Homepage, https://github.com/danielnachumdev/quickpub
28
+ Project-URL: Bug Tracker, https://github.com/danielnachumdev/quickpub/issues
29
+ Classifier: Development Status :: 3 - Alpha
30
+ Classifier: Intended Audience :: Developers
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: Operating System :: Microsoft :: Windows
33
+ Requires-Python: >=3.8.0
34
+ Description-Content-Type: text/markdown
35
+ License-File: LICENSE
36
+ Requires-Dist: danielutils>=1.1.23
37
+ Requires-Dist: requests
38
+ Requires-Dist: fire
39
+ Requires-Dist: twine
40
+ Dynamic: license-file
41
+
42
+ # QuickPub
43
+
44
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
45
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
46
+
47
+ Run quality checks, tests, and packaging locally before you publish a Python package.
48
+
49
+ QuickPub validates versions and project files, runs QA tools (mypy, pylint, pytest, unittest), builds the sdist, and uploads to PyPI/GitHub — so failures show up on your machine, not in CI.
50
+
51
+ ## Requirements
52
+
53
+ - Python **3.8+**
54
+ - Tested on **3.8, 3.9, 3.10, 3.11, 3.12, 3.13** (see [CI](.github/workflows/ci.yml))
55
+
56
+ ## Install
57
+
58
+ [uv](https://docs.astral.sh/uv/) is the recommended workflow:
59
+
60
+ ```bash
61
+ git clone https://github.com/danielnachumdev/quickpub.git
62
+ cd quickpub
63
+ uv sync
64
+ ```
65
+
66
+ Runtime-only install:
67
+
68
+ ```bash
69
+ uv sync --no-group dev --no-group test
70
+ ```
71
+
72
+ Use as a CLI in another project:
73
+
74
+ ```bash
75
+ uv add quickpub
76
+ ```
77
+
78
+ ## Usage
79
+
80
+ ### Publish this repo
81
+
82
+ ```bash
83
+ uv run python publish.py
84
+ ```
85
+
86
+ Requires a valid `.pypirc` for PyPI upload. Set `demo=True` in `publish.py` to run checks without building or uploading.
87
+
88
+ ### Publish your own package
89
+
90
+ `publish()` runs enforcers, QA, file generation, build, and upload in one call:
91
+
92
+ ```python
93
+ from quickpub import (
94
+ publish,
95
+ MypyRunner,
96
+ PylintRunner,
97
+ PytestRunner,
98
+ SetuptoolsBuildSchema,
99
+ PypircUploadTarget,
100
+ GithubUploadTarget,
101
+ PypircEnforcer,
102
+ ReadmeEnforcer,
103
+ LicenseEnforcer,
104
+ LocalVersionEnforcer,
105
+ PypiRemoteVersionEnforcer,
106
+ )
107
+
108
+ publish(
109
+ name="my-package",
110
+ version="1.0.0",
111
+ author="Your Name",
112
+ author_email="you@example.com",
113
+ description="My package",
114
+ homepage="https://github.com/you/my-package",
115
+ dependencies=["requests>=2.25.0"],
116
+ min_python="3.8.0",
117
+ enforcers=[
118
+ PypircEnforcer(),
119
+ ReadmeEnforcer(),
120
+ LicenseEnforcer(),
121
+ LocalVersionEnforcer(),
122
+ PypiRemoteVersionEnforcer(),
123
+ ],
124
+ global_quality_assurance_runners=[
125
+ MypyRunner(bound="<=20"),
126
+ PylintRunner(bound=">=0.8"),
127
+ PytestRunner(bound=">=0.95"),
128
+ ],
129
+ build_schemas=[SetuptoolsBuildSchema()],
130
+ upload_targets=[PypircUploadTarget(), GithubUploadTarget()],
131
+ )
132
+ ```
133
+
134
+ Omit `python_interpreter_provider` and `package_manager` to auto-detect the environment:
135
+
136
+ | Project layout | Package manager | Python provider |
137
+ |---|---|---|
138
+ | `uv.lock` present | `UvPackageManager` | `UvPythonProvider` |
139
+ | otherwise | `PipPackageManager` | `DefaultPythonProvider` |
140
+
141
+ Override for conda or mixed setups:
142
+
143
+ ```python
144
+ from quickpub import CondaPythonProvider, UnionProvider
145
+
146
+ publish(
147
+ ...,
148
+ python_interpreter_provider=UnionProvider([
149
+ CondaPythonProvider(["base", "py39", "py38"]),
150
+ ]),
151
+ )
152
+ ```
153
+
154
+ Manual control: `resolve_publish_environment()` or explicit `package_manager` / `python_interpreter_provider` arguments.
155
+
156
+ ## What it does
157
+
158
+ **Enforcers** — README, LICENSE, `.pypirc`, local/PyPI version checks.
159
+
160
+ **QA runners** — mypy, pylint, pytest, unittest with configurable score bounds; runs in parallel across environments.
161
+
162
+ **Build & upload** — generates `setup.py` / `pyproject.toml` / `MANIFEST.in`, builds sdist, uploads via twine and/or git push.
163
+
164
+ **Environment providers** — `DefaultPythonProvider`, `UvPythonProvider`, `CondaPythonProvider`, or `UnionProvider` to combine them.
165
+
166
+ ## Development
167
+
168
+ ```bash
169
+ uv run pytest # full test suite
170
+ uv run python publish.py # publish quickpub itself
171
+ ```
172
+
173
+ Tool config lives in `pyproject.toml` (`pytest`, `mypy`, `pylint`, `coverage`).
174
+
175
+ ## License
176
+
177
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,136 @@
1
+ # QuickPub
2
+
3
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
5
+
6
+ Run quality checks, tests, and packaging locally before you publish a Python package.
7
+
8
+ QuickPub validates versions and project files, runs QA tools (mypy, pylint, pytest, unittest), builds the sdist, and uploads to PyPI/GitHub — so failures show up on your machine, not in CI.
9
+
10
+ ## Requirements
11
+
12
+ - Python **3.8+**
13
+ - Tested on **3.8, 3.9, 3.10, 3.11, 3.12, 3.13** (see [CI](.github/workflows/ci.yml))
14
+
15
+ ## Install
16
+
17
+ [uv](https://docs.astral.sh/uv/) is the recommended workflow:
18
+
19
+ ```bash
20
+ git clone https://github.com/danielnachumdev/quickpub.git
21
+ cd quickpub
22
+ uv sync
23
+ ```
24
+
25
+ Runtime-only install:
26
+
27
+ ```bash
28
+ uv sync --no-group dev --no-group test
29
+ ```
30
+
31
+ Use as a CLI in another project:
32
+
33
+ ```bash
34
+ uv add quickpub
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ ### Publish this repo
40
+
41
+ ```bash
42
+ uv run python publish.py
43
+ ```
44
+
45
+ Requires a valid `.pypirc` for PyPI upload. Set `demo=True` in `publish.py` to run checks without building or uploading.
46
+
47
+ ### Publish your own package
48
+
49
+ `publish()` runs enforcers, QA, file generation, build, and upload in one call:
50
+
51
+ ```python
52
+ from quickpub import (
53
+ publish,
54
+ MypyRunner,
55
+ PylintRunner,
56
+ PytestRunner,
57
+ SetuptoolsBuildSchema,
58
+ PypircUploadTarget,
59
+ GithubUploadTarget,
60
+ PypircEnforcer,
61
+ ReadmeEnforcer,
62
+ LicenseEnforcer,
63
+ LocalVersionEnforcer,
64
+ PypiRemoteVersionEnforcer,
65
+ )
66
+
67
+ publish(
68
+ name="my-package",
69
+ version="1.0.0",
70
+ author="Your Name",
71
+ author_email="you@example.com",
72
+ description="My package",
73
+ homepage="https://github.com/you/my-package",
74
+ dependencies=["requests>=2.25.0"],
75
+ min_python="3.8.0",
76
+ enforcers=[
77
+ PypircEnforcer(),
78
+ ReadmeEnforcer(),
79
+ LicenseEnforcer(),
80
+ LocalVersionEnforcer(),
81
+ PypiRemoteVersionEnforcer(),
82
+ ],
83
+ global_quality_assurance_runners=[
84
+ MypyRunner(bound="<=20"),
85
+ PylintRunner(bound=">=0.8"),
86
+ PytestRunner(bound=">=0.95"),
87
+ ],
88
+ build_schemas=[SetuptoolsBuildSchema()],
89
+ upload_targets=[PypircUploadTarget(), GithubUploadTarget()],
90
+ )
91
+ ```
92
+
93
+ Omit `python_interpreter_provider` and `package_manager` to auto-detect the environment:
94
+
95
+ | Project layout | Package manager | Python provider |
96
+ |---|---|---|
97
+ | `uv.lock` present | `UvPackageManager` | `UvPythonProvider` |
98
+ | otherwise | `PipPackageManager` | `DefaultPythonProvider` |
99
+
100
+ Override for conda or mixed setups:
101
+
102
+ ```python
103
+ from quickpub import CondaPythonProvider, UnionProvider
104
+
105
+ publish(
106
+ ...,
107
+ python_interpreter_provider=UnionProvider([
108
+ CondaPythonProvider(["base", "py39", "py38"]),
109
+ ]),
110
+ )
111
+ ```
112
+
113
+ Manual control: `resolve_publish_environment()` or explicit `package_manager` / `python_interpreter_provider` arguments.
114
+
115
+ ## What it does
116
+
117
+ **Enforcers** — README, LICENSE, `.pypirc`, local/PyPI version checks.
118
+
119
+ **QA runners** — mypy, pylint, pytest, unittest with configurable score bounds; runs in parallel across environments.
120
+
121
+ **Build & upload** — generates `setup.py` / `pyproject.toml` / `MANIFEST.in`, builds sdist, uploads via twine and/or git push.
122
+
123
+ **Environment providers** — `DefaultPythonProvider`, `UvPythonProvider`, `CondaPythonProvider`, or `UnionProvider` to combine them.
124
+
125
+ ## Development
126
+
127
+ ```bash
128
+ uv run pytest # full test suite
129
+ uv run python publish.py # publish quickpub itself
130
+ ```
131
+
132
+ Tool config lives in `pyproject.toml` (`pytest`, `mypy`, `pylint`, `coverage`).
133
+
134
+ ## License
135
+
136
+ MIT — see [LICENSE](LICENSE).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "quickpub"
7
- version = "4.1.0"
7
+ version = "4.1.1"
8
8
  authors = [
9
9
  { name = "danielnachumdev", email = "danielnachumdev@gmail.com" },
10
10
  ]
@@ -6,4 +6,4 @@ from .logging_ import set_log_level
6
6
  from .package_manager_detection import detect_package_manager, resolve_publish_environment
7
7
  from .__main__ import publish, main
8
8
 
9
- __version__ = "4.1.0"
9
+ __version__ = "4.1.1"
@@ -90,7 +90,7 @@ def _run_quality_assurance(
90
90
  if package_manager is None:
91
91
  package_manager = PipPackageManager()
92
92
  try:
93
- result = asyncio.get_event_loop().run_until_complete(
93
+ result = asyncio.run(
94
94
  qa(
95
95
  python_interpreter_provider,
96
96
  global_quality_assurance_runners or [],
@@ -1,5 +1,5 @@
1
1
  import re
2
- from typing import Dict, Union
2
+ from typing import Dict, List, Union
3
3
 
4
4
  from ....structures import Dependency, Version
5
5
 
@@ -7,7 +7,7 @@ VERSION_REGEX: re.Pattern = re.compile(r"^\d+\.\d+\.\d+$")
7
7
 
8
8
 
9
9
  def parse_installed_packages_output(
10
- output_lines: list[str],
10
+ output_lines: List[str],
11
11
  ) -> Dict[str, Union[str, Dependency]]:
12
12
  split_lines = (line.split(" ") for line in output_lines[2:])
13
13
  version_tuples = [(s[0], s[-1].strip()) for s in split_lines if s[0]]
@@ -32,7 +32,9 @@ class DefaultPythonProvider(PythonProvider):
32
32
  if self.aiter_index == 0:
33
33
  self.aiter_index += 1
34
34
  logger.info("Using system Python environment")
35
- return "system", AsyncLayeredCommand()
35
+ executor = AsyncLayeredCommand()
36
+ executor.prev = None
37
+ return "system", executor
36
38
  raise StopAsyncIteration
37
39
 
38
40
  @classmethod
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import List, Tuple
2
+ from typing import List, Set, Tuple
3
3
 
4
4
  from danielutils.async_.async_layered_command import AsyncLayeredCommand
5
5
 
@@ -42,8 +42,8 @@ class UnionProvider(PythonProvider):
42
42
 
43
43
  raise StopAsyncIteration
44
44
 
45
- async def _get_available_envs_impl(self) -> set[str]:
46
- available_envs: set[str] = set()
45
+ async def _get_available_envs_impl(self) -> Set[str]:
46
+ available_envs: Set[str] = set()
47
47
  for provider in self.providers:
48
48
  available_envs.update(await provider._get_available_envs())
49
49
  return available_envs
@@ -27,7 +27,9 @@ class UvPythonProvider(PythonProvider):
27
27
  if self.aiter_index == 0:
28
28
  self.aiter_index += 1
29
29
  logger.info("Using uv-managed Python environment")
30
- return "uv", AsyncLayeredCommand()
30
+ executor = AsyncLayeredCommand()
31
+ executor.prev = None
32
+ return "uv", executor
31
33
  raise StopAsyncIteration
32
34
 
33
35
  async def _get_available_envs_impl(self) -> Set[str]:
@@ -18,7 +18,7 @@ class PylintRunner(QualityAssuranceRunner):
18
18
  with base:
19
19
  base(self.package_manager.install_command("pylint"))
20
20
 
21
- RATING_PATTERN: re.Pattern = re.compile(r".*?([\d\.\/]+)")
21
+ RATING_PATTERN: re.Pattern = re.compile(r"([\d.]+)/([\d.]+)")
22
22
 
23
23
  def __init__(
24
24
  self,
@@ -52,13 +52,36 @@ class PylintRunner(QualityAssuranceRunner):
52
52
  def _calculate_score(
53
53
  self, ret: int, lines: List[str], verbose: bool = False
54
54
  ) -> float:
55
- from quickpub.enforcers import exit_if
56
-
57
55
  logger.debug("Calculating pylint score from analysis results")
58
56
 
59
57
  if len(lines) == 0:
60
58
  logger.debug("No pylint output, returning perfect score: 1.0")
61
59
  return 1
60
+
61
+ for line in reversed(lines):
62
+ match = self.RATING_PATTERN.search(line)
63
+ if match:
64
+ numerator = float(match.group(1))
65
+ denominator = float(match.group(2))
66
+ score = numerator / denominator
67
+ logger.debug(
68
+ "Pylint score calculated: %.3f (%s/%s)",
69
+ score,
70
+ match.group(1),
71
+ match.group(2),
72
+ )
73
+ return score
74
+
75
+ if ret == 0:
76
+ return 1.0
77
+
78
+ joined_output = "\n".join(lines)
79
+ if ret == 1 and "parse-error" in joined_output:
80
+ logger.debug(
81
+ "Pylint reported parse-error with an empty target, returning perfect score: 1.0"
82
+ )
83
+ return 1.0
84
+
62
85
  if len(lines) == 1:
63
86
  if lines[0].endswith("No module named pylint"):
64
87
  logger.error("Pylint module not found")
@@ -73,20 +96,9 @@ class PylintRunner(QualityAssuranceRunner):
73
96
  logger.error("Unexpected pylint error: %s", lines[0])
74
97
  raise ExitEarlyError(f"Got an unexpected error: {lines[0]}")
75
98
 
76
- index = -2
77
- if lines[-1] == "\x1b[0m":
78
- index += -1
79
- rating_line = lines[index]
80
- m = self.RATING_PATTERN.match(rating_line)
81
99
  msg = f"Failed running Pylint, got exit code {ret}. Try running manually using: {self._build_command('TARGET')}"
82
- exit_if(not m, msg)
83
- rating_string = m.group(1) # type:ignore
84
- numerator, denominator = rating_string.split("/")
85
- score = float(numerator) / float(denominator)
86
- logger.debug(
87
- "Pylint score calculated: %.3f (%s/%s)", score, numerator, denominator
88
- )
89
- return score
100
+ logger.error("Failed to parse pylint rating from output: %s", joined_output)
101
+ raise ExitEarlyError(msg)
90
102
 
91
103
 
92
104
  __all__ = [
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.4
2
+ Name: quickpub
3
+ Version: 4.1.1
4
+ Summary: A local CI/CD simulation tool that runs quality checks, tests, and validations locally before publishing Python packages, ensuring higher build pass rates and faster feedback loops
5
+ Author-email: danielnachumdev <danielnachumdev@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2022 danielnachumdev
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Homepage, https://github.com/danielnachumdev/quickpub
28
+ Project-URL: Bug Tracker, https://github.com/danielnachumdev/quickpub/issues
29
+ Classifier: Development Status :: 3 - Alpha
30
+ Classifier: Intended Audience :: Developers
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: Operating System :: Microsoft :: Windows
33
+ Requires-Python: >=3.8.0
34
+ Description-Content-Type: text/markdown
35
+ License-File: LICENSE
36
+ Requires-Dist: danielutils>=1.1.23
37
+ Requires-Dist: requests
38
+ Requires-Dist: fire
39
+ Requires-Dist: twine
40
+ Dynamic: license-file
41
+
42
+ # QuickPub
43
+
44
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
45
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
46
+
47
+ Run quality checks, tests, and packaging locally before you publish a Python package.
48
+
49
+ QuickPub validates versions and project files, runs QA tools (mypy, pylint, pytest, unittest), builds the sdist, and uploads to PyPI/GitHub — so failures show up on your machine, not in CI.
50
+
51
+ ## Requirements
52
+
53
+ - Python **3.8+**
54
+ - Tested on **3.8, 3.9, 3.10, 3.11, 3.12, 3.13** (see [CI](.github/workflows/ci.yml))
55
+
56
+ ## Install
57
+
58
+ [uv](https://docs.astral.sh/uv/) is the recommended workflow:
59
+
60
+ ```bash
61
+ git clone https://github.com/danielnachumdev/quickpub.git
62
+ cd quickpub
63
+ uv sync
64
+ ```
65
+
66
+ Runtime-only install:
67
+
68
+ ```bash
69
+ uv sync --no-group dev --no-group test
70
+ ```
71
+
72
+ Use as a CLI in another project:
73
+
74
+ ```bash
75
+ uv add quickpub
76
+ ```
77
+
78
+ ## Usage
79
+
80
+ ### Publish this repo
81
+
82
+ ```bash
83
+ uv run python publish.py
84
+ ```
85
+
86
+ Requires a valid `.pypirc` for PyPI upload. Set `demo=True` in `publish.py` to run checks without building or uploading.
87
+
88
+ ### Publish your own package
89
+
90
+ `publish()` runs enforcers, QA, file generation, build, and upload in one call:
91
+
92
+ ```python
93
+ from quickpub import (
94
+ publish,
95
+ MypyRunner,
96
+ PylintRunner,
97
+ PytestRunner,
98
+ SetuptoolsBuildSchema,
99
+ PypircUploadTarget,
100
+ GithubUploadTarget,
101
+ PypircEnforcer,
102
+ ReadmeEnforcer,
103
+ LicenseEnforcer,
104
+ LocalVersionEnforcer,
105
+ PypiRemoteVersionEnforcer,
106
+ )
107
+
108
+ publish(
109
+ name="my-package",
110
+ version="1.0.0",
111
+ author="Your Name",
112
+ author_email="you@example.com",
113
+ description="My package",
114
+ homepage="https://github.com/you/my-package",
115
+ dependencies=["requests>=2.25.0"],
116
+ min_python="3.8.0",
117
+ enforcers=[
118
+ PypircEnforcer(),
119
+ ReadmeEnforcer(),
120
+ LicenseEnforcer(),
121
+ LocalVersionEnforcer(),
122
+ PypiRemoteVersionEnforcer(),
123
+ ],
124
+ global_quality_assurance_runners=[
125
+ MypyRunner(bound="<=20"),
126
+ PylintRunner(bound=">=0.8"),
127
+ PytestRunner(bound=">=0.95"),
128
+ ],
129
+ build_schemas=[SetuptoolsBuildSchema()],
130
+ upload_targets=[PypircUploadTarget(), GithubUploadTarget()],
131
+ )
132
+ ```
133
+
134
+ Omit `python_interpreter_provider` and `package_manager` to auto-detect the environment:
135
+
136
+ | Project layout | Package manager | Python provider |
137
+ |---|---|---|
138
+ | `uv.lock` present | `UvPackageManager` | `UvPythonProvider` |
139
+ | otherwise | `PipPackageManager` | `DefaultPythonProvider` |
140
+
141
+ Override for conda or mixed setups:
142
+
143
+ ```python
144
+ from quickpub import CondaPythonProvider, UnionProvider
145
+
146
+ publish(
147
+ ...,
148
+ python_interpreter_provider=UnionProvider([
149
+ CondaPythonProvider(["base", "py39", "py38"]),
150
+ ]),
151
+ )
152
+ ```
153
+
154
+ Manual control: `resolve_publish_environment()` or explicit `package_manager` / `python_interpreter_provider` arguments.
155
+
156
+ ## What it does
157
+
158
+ **Enforcers** — README, LICENSE, `.pypirc`, local/PyPI version checks.
159
+
160
+ **QA runners** — mypy, pylint, pytest, unittest with configurable score bounds; runs in parallel across environments.
161
+
162
+ **Build & upload** — generates `setup.py` / `pyproject.toml` / `MANIFEST.in`, builds sdist, uploads via twine and/or git push.
163
+
164
+ **Environment providers** — `DefaultPythonProvider`, `UvPythonProvider`, `CondaPythonProvider`, or `UnionProvider` to combine them.
165
+
166
+ ## Development
167
+
168
+ ```bash
169
+ uv run pytest # full test suite
170
+ uv run python publish.py # publish quickpub itself
171
+ ```
172
+
173
+ Tool config lives in `pyproject.toml` (`pytest`, `mypy`, `pylint`, `coverage`).
174
+
175
+ ## License
176
+
177
+ MIT — see [LICENSE](LICENSE).