quickpub 3.0.6__tar.gz → 3.1.0__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.
- {quickpub-3.0.6/quickpub.egg-info → quickpub-3.1.0}/PKG-INFO +3 -3
- {quickpub-3.0.6 → quickpub-3.1.0}/README.md +1 -1
- {quickpub-3.0.6 → quickpub-3.1.0}/pyproject.toml +5 -2
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/__init__.py +1 -1
- quickpub-3.1.0/quickpub/__main__.py +156 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/classifiers.py +6 -2
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/enforcers.py +9 -7
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/files.py +43 -26
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/functions.py +34 -39
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/logging_.py +19 -16
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/proxy.py +11 -11
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/qa.py +145 -76
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/build_schema.py +3 -4
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/constraint_enforcer.py +3 -4
- quickpub-3.1.0/quickpub/strategies/implementations/build_schemas/__init__.py +1 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/build_schemas/setuptools_build_schema.py +14 -5
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/constraint_enforcers/license_enforcer.py +1 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/constraint_enforcers/local_version_enforcer.py +18 -8
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/constraint_enforcers/pypi_remote_version_enforcer.py +20 -12
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/constraint_enforcers/pypirc_enforcer.py +6 -2
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/python_providers/conda_python_provider.py +9 -7
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/python_providers/default_python_provider.py +8 -3
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/quality_assurance_runners/mypy_qa_runner.py +29 -11
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/quality_assurance_runners/pylint_qa_runner.py +30 -11
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/quality_assurance_runners/pytest_qa_runner.py +44 -21
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/quality_assurance_runners/unittest_qa_runner.py +142 -116
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/upload_targets/github_upload_target.py +18 -9
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/upload_targets/pypirc_upload_target.py +85 -75
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/python_provider.py +11 -13
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/quality_assurance_runner.py +76 -39
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/quickpub_strategy.py +2 -1
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/upload_target.py +3 -2
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/structures/bound.py +8 -7
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/structures/dependency.py +19 -10
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/structures/version.py +12 -6
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/validators.py +9 -7
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/worker_pool.py +1 -1
- {quickpub-3.0.6 → quickpub-3.1.0/quickpub.egg-info}/PKG-INFO +3 -3
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub.egg-info/SOURCES.txt +1 -0
- quickpub-3.1.0/quickpub.egg-info/entry_points.txt +2 -0
- quickpub-3.0.6/quickpub/__main__.py +0 -133
- quickpub-3.0.6/quickpub/strategies/implementations/build_schemas/__init__.py +0 -1
- {quickpub-3.0.6 → quickpub-3.1.0}/LICENSE +0 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/MANIFEST.in +0 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/py.typed +0 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/__init__.py +0 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/__init__.py +0 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/constraint_enforcers/__init__.py +0 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/constraint_enforcers/readme_enforcer.py +0 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/python_providers/__init__.py +0 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/quality_assurance_runners/__init__.py +0 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/strategies/implementations/upload_targets/__init__.py +0 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub/structures/__init__.py +0 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub.egg-info/dependency_links.txt +0 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub.egg-info/requires.txt +0 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/quickpub.egg-info/top_level.txt +0 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/setup.cfg +0 -0
- {quickpub-3.0.6 → quickpub-3.1.0}/setup.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quickpub
|
|
3
|
-
Version: 3.0
|
|
4
|
-
Summary: A
|
|
3
|
+
Version: 3.1.0
|
|
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
5
|
Author-email: danielnachumdev <danielnachumdev@gmail.com>
|
|
6
6
|
License: MIT License
|
|
7
7
|
|
|
@@ -38,7 +38,7 @@ Requires-Dist: requests
|
|
|
38
38
|
Requires-Dist: fire
|
|
39
39
|
Dynamic: license-file
|
|
40
40
|
|
|
41
|
-
# QuickPub v3.0.
|
|
41
|
+
# QuickPub v3.0.61
|
|
42
42
|
|
|
43
43
|
[](https://www.python.org/downloads/)
|
|
44
44
|
[](LICENSE)
|
|
@@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "quickpub"
|
|
7
|
-
version = "3.0
|
|
7
|
+
version = "3.1.0"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name = "danielnachumdev", email = "danielnachumdev@gmail.com" },
|
|
10
10
|
]
|
|
11
11
|
dependencies = ['danielutils>=1.0.0', 'requests', 'fire']
|
|
12
12
|
keywords = []
|
|
13
13
|
license = { "file" = "./LICENSE" }
|
|
14
|
-
description = "A
|
|
14
|
+
description = "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"
|
|
15
15
|
readme = {file = "./README.md", content-type = "text/markdown"}
|
|
16
16
|
requires-python = ">=3.8.0"
|
|
17
17
|
classifiers = [
|
|
@@ -20,9 +20,12 @@ classifiers = [
|
|
|
20
20
|
"Programming Language :: Python :: 3",
|
|
21
21
|
"Operating System :: Microsoft :: Windows"
|
|
22
22
|
]
|
|
23
|
+
[project.scripts]
|
|
24
|
+
quickpub = "quickpub.__main__:main"
|
|
23
25
|
|
|
24
26
|
[tool.setuptools]
|
|
25
27
|
packages = ["quickpub"]
|
|
28
|
+
|
|
26
29
|
[tool.setuptools.package-data]
|
|
27
30
|
"quickpub" = ["py.typed"]
|
|
28
31
|
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Optional, Union, List, Any, Dict, Callable
|
|
3
|
+
|
|
4
|
+
import fire
|
|
5
|
+
from danielutils import warning, error
|
|
6
|
+
|
|
7
|
+
from quickpub import ExitEarlyError
|
|
8
|
+
from .strategies import (
|
|
9
|
+
BuildSchema,
|
|
10
|
+
ConstraintEnforcer,
|
|
11
|
+
UploadTarget,
|
|
12
|
+
QualityAssuranceRunner,
|
|
13
|
+
PythonProvider,
|
|
14
|
+
DefaultPythonProvider,
|
|
15
|
+
)
|
|
16
|
+
from .validators import (
|
|
17
|
+
validate_version,
|
|
18
|
+
validate_python_version,
|
|
19
|
+
validate_keywords,
|
|
20
|
+
validate_dependencies,
|
|
21
|
+
validate_source,
|
|
22
|
+
)
|
|
23
|
+
from .structures import Version, Dependency
|
|
24
|
+
from .files import create_toml, create_setup, create_manifest
|
|
25
|
+
from .classifiers import *
|
|
26
|
+
from .qa import qa, SupportsProgress
|
|
27
|
+
from .logging_ import setup_logging
|
|
28
|
+
|
|
29
|
+
setup_logging()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def publish(
|
|
33
|
+
*,
|
|
34
|
+
name: str,
|
|
35
|
+
author: str,
|
|
36
|
+
author_email: str,
|
|
37
|
+
description: str,
|
|
38
|
+
homepage: str,
|
|
39
|
+
build_schemas: List[BuildSchema],
|
|
40
|
+
upload_targets: List[UploadTarget],
|
|
41
|
+
enforcers: Optional[List[ConstraintEnforcer]] = None,
|
|
42
|
+
global_quality_assurance_runners: Optional[List[QualityAssuranceRunner]] = None,
|
|
43
|
+
python_interpreter_provider: PythonProvider = DefaultPythonProvider(),
|
|
44
|
+
readme_file_path: str = "./README.md",
|
|
45
|
+
license_file_path: str = "./LICENSE",
|
|
46
|
+
version: Optional[Union[Version, str]] = None,
|
|
47
|
+
min_python: Optional[Union[Version, str]] = None,
|
|
48
|
+
dependencies: Optional[List[Union[str, Dependency]]] = None,
|
|
49
|
+
keywords: Optional[List[str]] = None,
|
|
50
|
+
explicit_src_folder_path: Optional[str] = None,
|
|
51
|
+
scripts: Optional[Dict[str, Callable]] = None,
|
|
52
|
+
# ========== QA Parameters ==========
|
|
53
|
+
pbar: Optional[SupportsProgress] = None, # tqdm
|
|
54
|
+
demo: bool = False,
|
|
55
|
+
config: Optional[Any] = None,
|
|
56
|
+
) -> None:
|
|
57
|
+
"""The main function for publishing a package. It performs all necessary steps to prepare and publish the package.
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
:param name: The name of the package.
|
|
61
|
+
:param author: The name of the author.
|
|
62
|
+
:param author_email: The email of the author.
|
|
63
|
+
:param description: A short description of the package.
|
|
64
|
+
:param homepage: The homepage URL for the package (e.g., GitHub repository).
|
|
65
|
+
:param global_quality_assurance_runners: Strategies for quality assurance. These will run on all Envs supplies by the supplier.
|
|
66
|
+
:param build_schemas: Strategies for building the package.
|
|
67
|
+
:param upload_targets: Strategies for uploading the package.
|
|
68
|
+
:param python_interpreter_provider: Strategy for managing Python versions. Defaults to SystemInterpreter().
|
|
69
|
+
:param explicit_src_folder_path: The path to the source code of the package. Defaults to <current working directory>/<name>.
|
|
70
|
+
:param version: The version for the new distribution. Defaults to "0.0.1".
|
|
71
|
+
:param readme_file_path: The path to the README file. Defaults to "./README.md".
|
|
72
|
+
:param license_file_path: The path to the license file. Defaults to "./LICENSE".
|
|
73
|
+
:param min_python: The minimum Python version required for the package. Defaults to the Python version running this script.
|
|
74
|
+
:param keywords: A list of keywords describing areas of interest for the package. Defaults to None.
|
|
75
|
+
:param dependencies: A list of dependencies for the package. Defaults to None.
|
|
76
|
+
:param scripts: Optional dictionary mapping script names to functions for [project.scripts] section. Each entry will create an executable entry point.
|
|
77
|
+
:param log: A function to receive log statements about the process and print them (or do something else idk)
|
|
78
|
+
:param pbar: and object that can be notified about an update of progress like a tqdm progress bar.
|
|
79
|
+
:param demo: Whether to perform checks without making any changes. Defaults to False.
|
|
80
|
+
:param config: Reserved for future use. Defaults to None.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
None
|
|
84
|
+
"""
|
|
85
|
+
version = validate_version(version)
|
|
86
|
+
explicit_src_folder_path = validate_source(name, explicit_src_folder_path)
|
|
87
|
+
if explicit_src_folder_path != f"./{name}":
|
|
88
|
+
warning(
|
|
89
|
+
"The source folder's name is different from the package's name. this may not be currently supported correctly"
|
|
90
|
+
)
|
|
91
|
+
min_python = validate_python_version(min_python) # type:ignore
|
|
92
|
+
keywords = validate_keywords(keywords)
|
|
93
|
+
validated_dependencies: List[Dependency] = validate_dependencies(dependencies)
|
|
94
|
+
for enforcer in enforcers or []:
|
|
95
|
+
enforcer.enforce(name=name, version=version, demo=demo)
|
|
96
|
+
try:
|
|
97
|
+
res = asyncio.get_event_loop().run_until_complete(
|
|
98
|
+
qa(
|
|
99
|
+
python_interpreter_provider,
|
|
100
|
+
global_quality_assurance_runners or [],
|
|
101
|
+
name,
|
|
102
|
+
explicit_src_folder_path,
|
|
103
|
+
validated_dependencies,
|
|
104
|
+
pbar,
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
if not res:
|
|
108
|
+
error(
|
|
109
|
+
f"quickpub.publish exited early as '{name}' "
|
|
110
|
+
"did not pass quality assurance step, see above "
|
|
111
|
+
"logs to pass this step."
|
|
112
|
+
)
|
|
113
|
+
raise ExitEarlyError("QA step Failed")
|
|
114
|
+
except ExitEarlyError as e:
|
|
115
|
+
raise e
|
|
116
|
+
except Exception as e:
|
|
117
|
+
raise RuntimeError("Quality assurance stage has failed", e) from e
|
|
118
|
+
|
|
119
|
+
create_setup()
|
|
120
|
+
create_toml(
|
|
121
|
+
name=name,
|
|
122
|
+
src_folder_path=explicit_src_folder_path,
|
|
123
|
+
readme_file_path=readme_file_path,
|
|
124
|
+
license_file_path=license_file_path,
|
|
125
|
+
version=version,
|
|
126
|
+
author=author,
|
|
127
|
+
author_email=author_email,
|
|
128
|
+
description=description,
|
|
129
|
+
homepage=homepage,
|
|
130
|
+
keywords=keywords,
|
|
131
|
+
dependencies=validated_dependencies,
|
|
132
|
+
classifiers=[
|
|
133
|
+
DevelopmentStatusClassifier.Alpha,
|
|
134
|
+
IntendedAudienceClassifier.Developers,
|
|
135
|
+
ProgrammingLanguageClassifier.Python3,
|
|
136
|
+
OperatingSystemClassifier.MicrosoftWindows,
|
|
137
|
+
],
|
|
138
|
+
min_python=min_python,
|
|
139
|
+
scripts=scripts,
|
|
140
|
+
)
|
|
141
|
+
create_manifest(name=name)
|
|
142
|
+
if not demo:
|
|
143
|
+
for schema in build_schemas:
|
|
144
|
+
schema.build()
|
|
145
|
+
for target in upload_targets:
|
|
146
|
+
target.upload(name=name, version=version)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def main() -> None:
|
|
150
|
+
fire.Fire(publish)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
if __name__ == "__main__":
|
|
154
|
+
main()
|
|
155
|
+
|
|
156
|
+
__all__ = ["main", "publish"]
|
|
@@ -15,7 +15,7 @@ class Classifier(Enum):
|
|
|
15
15
|
@staticmethod
|
|
16
16
|
def _split_name(name: str) -> str:
|
|
17
17
|
words = []
|
|
18
|
-
current_word =
|
|
18
|
+
current_word = ""
|
|
19
19
|
|
|
20
20
|
for char in name:
|
|
21
21
|
# Check if the character is uppercase
|
|
@@ -47,6 +47,7 @@ class DevelopmentStatusClassifier(Classifier):
|
|
|
47
47
|
"""
|
|
48
48
|
An enum class for specifying the development state of the package
|
|
49
49
|
"""
|
|
50
|
+
|
|
50
51
|
# https://pypi.org/classifiers/
|
|
51
52
|
Planning = 1
|
|
52
53
|
PreAlpha = 2
|
|
@@ -65,6 +66,7 @@ class IntendedAudienceClassifier(Classifier):
|
|
|
65
66
|
"""
|
|
66
67
|
An enum class for specifying the intended audience
|
|
67
68
|
"""
|
|
69
|
+
|
|
68
70
|
CustomerService = "CustomerService"
|
|
69
71
|
Developers = "Developers"
|
|
70
72
|
|
|
@@ -73,6 +75,7 @@ class ProgrammingLanguageClassifier(Classifier):
|
|
|
73
75
|
"""
|
|
74
76
|
An enum class for specifying the target language level
|
|
75
77
|
"""
|
|
78
|
+
|
|
76
79
|
Python3 = "Python :: 3"
|
|
77
80
|
|
|
78
81
|
|
|
@@ -80,6 +83,7 @@ class OperatingSystemClassifier(Classifier):
|
|
|
80
83
|
"""
|
|
81
84
|
An enum class for specifying the target operating system
|
|
82
85
|
"""
|
|
86
|
+
|
|
83
87
|
MicrosoftWindows = "Microsoft :: Windows"
|
|
84
88
|
|
|
85
89
|
|
|
@@ -88,5 +92,5 @@ __all__ = [
|
|
|
88
92
|
"DevelopmentStatusClassifier",
|
|
89
93
|
"IntendedAudienceClassifier",
|
|
90
94
|
"ProgrammingLanguageClassifier",
|
|
91
|
-
"OperatingSystemClassifier"
|
|
95
|
+
"OperatingSystemClassifier",
|
|
92
96
|
]
|
|
@@ -11,22 +11,24 @@ class ExitEarlyError(Exception):
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def exit_if(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
predicate: Union[bool, Callable[[], bool]],
|
|
15
|
+
msg: str,
|
|
16
|
+
*,
|
|
17
|
+
verbose: bool = True,
|
|
18
|
+
err_func: Callable[[str], None] = error,
|
|
19
19
|
) -> None:
|
|
20
20
|
"""
|
|
21
21
|
Exit the program if the given predicate is true.
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
:param predicate: Boolean value or callable that returns a boolean
|
|
24
24
|
:param msg: Error message to display
|
|
25
25
|
:param verbose: Whether to display the error message
|
|
26
26
|
:param err_func: Function to call for error display
|
|
27
27
|
:raises ExitEarlyError: When the predicate condition is met
|
|
28
28
|
"""
|
|
29
|
-
if (isinstance(predicate, bool) and predicate) or (
|
|
29
|
+
if (isinstance(predicate, bool) and predicate) or (
|
|
30
|
+
callable(predicate) and predicate()
|
|
31
|
+
):
|
|
30
32
|
logger.error("Exit condition met: %s", msg)
|
|
31
33
|
if verbose:
|
|
32
34
|
err_func(msg)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import List
|
|
2
|
+
from typing import List, Optional, Dict, Callable
|
|
3
3
|
from danielutils import get_files
|
|
4
4
|
|
|
5
5
|
from .classifiers import Classifier
|
|
@@ -9,24 +9,25 @@ logger = logging.getLogger(__name__)
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def create_toml(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
12
|
+
*,
|
|
13
|
+
name: str,
|
|
14
|
+
src_folder_path: str,
|
|
15
|
+
readme_file_path: str,
|
|
16
|
+
license_file_path: str,
|
|
17
|
+
version: Version,
|
|
18
|
+
author: str,
|
|
19
|
+
author_email: str,
|
|
20
|
+
description: str,
|
|
21
|
+
homepage: str,
|
|
22
|
+
keywords: List[str],
|
|
23
|
+
min_python: Version,
|
|
24
|
+
dependencies: List[Dependency],
|
|
25
|
+
classifiers: List[Classifier],
|
|
26
|
+
scripts: Optional[Dict[str, Callable]] = None,
|
|
26
27
|
) -> None:
|
|
27
28
|
"""
|
|
28
29
|
Create a pyproject.toml file for the package.
|
|
29
|
-
|
|
30
|
+
|
|
30
31
|
:param name: Package name
|
|
31
32
|
:param src_folder_path: Path to source folder
|
|
32
33
|
:param readme_file_path: Path to README file
|
|
@@ -40,9 +41,11 @@ def create_toml(
|
|
|
40
41
|
:param min_python: Minimum Python version required
|
|
41
42
|
:param dependencies: List of package dependencies
|
|
42
43
|
:param classifiers: List of package classifiers
|
|
44
|
+
:param scripts: Optional dictionary mapping script names to functions for [project.scripts] section
|
|
43
45
|
"""
|
|
44
|
-
logger.info(
|
|
45
|
-
|
|
46
|
+
logger.info(
|
|
47
|
+
"Creating pyproject.toml for package '%s' version '%s'", name, version)
|
|
48
|
+
classifiers_string = ",\n\t".join([f'"{str(c)}"' for c in classifiers])
|
|
46
49
|
if len(classifiers_string) > 0:
|
|
47
50
|
classifiers_string = f"\n\t{classifiers_string}\n"
|
|
48
51
|
py_typed = ""
|
|
@@ -50,9 +53,26 @@ def create_toml(
|
|
|
50
53
|
if file == "py.typed":
|
|
51
54
|
py_typed = f"""[tool.setuptools.package-data]
|
|
52
55
|
"{name}" = ["py.typed"]"""
|
|
53
|
-
logger.debug(
|
|
56
|
+
logger.debug(
|
|
57
|
+
"Found py.typed file, adding package-data configuration")
|
|
54
58
|
break
|
|
55
59
|
|
|
60
|
+
scripts_section = ""
|
|
61
|
+
if scripts:
|
|
62
|
+
logger.debug(
|
|
63
|
+
"Adding [project.scripts] section with %d entries", len(scripts))
|
|
64
|
+
scripts_entries = []
|
|
65
|
+
for script_name, func in scripts.items():
|
|
66
|
+
module = func.__module__
|
|
67
|
+
func_name = func.__name__
|
|
68
|
+
# If module is __main__, prefix it with package name
|
|
69
|
+
if module == "__main__":
|
|
70
|
+
module = f"{name}.__main__"
|
|
71
|
+
entry_point = f"{module}:{func_name}"
|
|
72
|
+
scripts_entries.append(f' {script_name} = "{entry_point}"')
|
|
73
|
+
scripts_section = "\n[project.scripts]\n" + \
|
|
74
|
+
"\n".join(scripts_entries) + "\n"
|
|
75
|
+
|
|
56
76
|
s = f"""[build-system]
|
|
57
77
|
requires = ["setuptools>=61.0"]
|
|
58
78
|
build-backend = "setuptools.build_meta"
|
|
@@ -69,10 +89,10 @@ license = {{ "file" = "{license_file_path}" }}
|
|
|
69
89
|
description = "{description}"
|
|
70
90
|
readme = {{file = "{readme_file_path}", content-type = "text/markdown"}}
|
|
71
91
|
requires-python = ">={min_python}"
|
|
72
|
-
classifiers = [{classifiers_string}]
|
|
73
|
-
|
|
92
|
+
classifiers = [{classifiers_string}]{scripts_section}
|
|
74
93
|
[tool.setuptools]
|
|
75
94
|
packages = ["{name}"]
|
|
95
|
+
|
|
76
96
|
{py_typed}
|
|
77
97
|
|
|
78
98
|
[project.urls]
|
|
@@ -95,7 +115,7 @@ def create_setup() -> None:
|
|
|
95
115
|
def create_manifest(*, name: str) -> None:
|
|
96
116
|
"""
|
|
97
117
|
Create a MANIFEST.in file for the package.
|
|
98
|
-
|
|
118
|
+
|
|
99
119
|
:param name: Package name
|
|
100
120
|
"""
|
|
101
121
|
logger.info("Creating MANIFEST.in for package '%s'", name)
|
|
@@ -104,7 +124,4 @@ def create_manifest(*, name: str) -> None:
|
|
|
104
124
|
logger.info("Successfully created MANIFEST.in")
|
|
105
125
|
|
|
106
126
|
|
|
107
|
-
__all__ = [
|
|
108
|
-
"create_setup",
|
|
109
|
-
"create_toml"
|
|
110
|
-
]
|
|
127
|
+
__all__ = ["create_setup", "create_toml"]
|
|
@@ -8,13 +8,10 @@ import quickpub.proxy
|
|
|
8
8
|
logger = logging.getLogger(__name__)
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def build(
|
|
12
|
-
*,
|
|
13
|
-
verbose: bool = True
|
|
14
|
-
) -> None:
|
|
11
|
+
def build(*, verbose: bool = True) -> None:
|
|
15
12
|
"""
|
|
16
13
|
Build the package distribution.
|
|
17
|
-
|
|
14
|
+
|
|
18
15
|
:param verbose: Whether to display verbose output
|
|
19
16
|
"""
|
|
20
17
|
logger.info("Starting build process")
|
|
@@ -25,24 +22,18 @@ def build(
|
|
|
25
22
|
ret, stdout, stderr = quickpub.proxy.cm("python", "setup.py", "sdist")
|
|
26
23
|
|
|
27
24
|
if ret != 0:
|
|
28
|
-
logger.error(
|
|
29
|
-
|
|
30
|
-
ret != 0,
|
|
31
|
-
stderr.decode(encoding="utf8")
|
|
25
|
+
logger.error(
|
|
26
|
+
"Build failed with return code %d: %s", ret, stderr.decode(encoding="utf8")
|
|
32
27
|
)
|
|
28
|
+
exit_if(ret != 0, stderr.decode(encoding="utf8"))
|
|
33
29
|
|
|
34
30
|
logger.info("Build completed successfully")
|
|
35
31
|
|
|
36
32
|
|
|
37
|
-
def upload(
|
|
38
|
-
*,
|
|
39
|
-
name: str,
|
|
40
|
-
version: Version,
|
|
41
|
-
verbose: bool = True
|
|
42
|
-
) -> None:
|
|
33
|
+
def upload(*, name: str, version: Version, verbose: bool = True) -> None:
|
|
43
34
|
"""
|
|
44
35
|
Upload the package to PyPI.
|
|
45
|
-
|
|
36
|
+
|
|
46
37
|
:param name: Package name
|
|
47
38
|
:param version: Package version
|
|
48
39
|
:param verbose: Whether to display verbose output
|
|
@@ -52,27 +43,26 @@ def upload(
|
|
|
52
43
|
info("Uploading")
|
|
53
44
|
logger.info("Uploading package to PyPI")
|
|
54
45
|
|
|
55
|
-
ret, stdout, stderr = quickpub.proxy.cm(
|
|
56
|
-
|
|
46
|
+
ret, stdout, stderr = quickpub.proxy.cm(
|
|
47
|
+
"twine", "upload", "--config-file", ".pypirc", f"dist/{name}-{version}.tar.gz"
|
|
48
|
+
)
|
|
57
49
|
|
|
58
50
|
if ret != 0:
|
|
59
|
-
logger.error(
|
|
51
|
+
logger.error(
|
|
52
|
+
"Upload failed with return code %d: %s", ret, stderr.decode(encoding="utf8")
|
|
53
|
+
)
|
|
60
54
|
exit_if(
|
|
61
55
|
ret != 0,
|
|
62
|
-
f"Failed uploading the package to pypi. Try running the following command manually:\n\ttwine upload --config-file .pypirc dist/{name}-{version}.tar.gz"
|
|
56
|
+
f"Failed uploading the package to pypi. Try running the following command manually:\n\ttwine upload --config-file .pypirc dist/{name}-{version}.tar.gz",
|
|
63
57
|
)
|
|
64
58
|
|
|
65
59
|
logger.info("Successfully uploaded package '%s' version '%s'", name, version)
|
|
66
60
|
|
|
67
61
|
|
|
68
|
-
def commit(
|
|
69
|
-
*,
|
|
70
|
-
version: Version,
|
|
71
|
-
verbose: bool = True
|
|
72
|
-
) -> None:
|
|
62
|
+
def commit(*, version: Version, verbose: bool = True) -> None:
|
|
73
63
|
"""
|
|
74
64
|
Commit and push changes to Git repository.
|
|
75
|
-
|
|
65
|
+
|
|
76
66
|
:param version: Package version
|
|
77
67
|
:param verbose: Whether to display verbose output
|
|
78
68
|
"""
|
|
@@ -85,23 +75,27 @@ def commit(
|
|
|
85
75
|
|
|
86
76
|
ret, stdout, stderr = quickpub.proxy.cm("git add .")
|
|
87
77
|
if ret != 0:
|
|
88
|
-
logger.error(
|
|
89
|
-
|
|
90
|
-
ret
|
|
91
|
-
stderr.decode(encoding="utf8")
|
|
78
|
+
logger.error(
|
|
79
|
+
"Git add failed with return code %d: %s",
|
|
80
|
+
ret,
|
|
81
|
+
stderr.decode(encoding="utf8"),
|
|
92
82
|
)
|
|
83
|
+
exit_if(ret != 0, stderr.decode(encoding="utf8"))
|
|
93
84
|
|
|
94
85
|
if verbose:
|
|
95
86
|
info("\tCommitting")
|
|
96
87
|
logger.info("Committing changes with message 'updated to version %s'", version)
|
|
97
88
|
|
|
98
|
-
ret, stdout, stderr = quickpub.proxy.cm(
|
|
89
|
+
ret, stdout, stderr = quickpub.proxy.cm(
|
|
90
|
+
f'git commit -m "updated to version {version}"'
|
|
91
|
+
)
|
|
99
92
|
if ret != 0:
|
|
100
|
-
logger.error(
|
|
101
|
-
|
|
102
|
-
ret
|
|
103
|
-
stderr.decode(encoding="utf8")
|
|
93
|
+
logger.error(
|
|
94
|
+
"Git commit failed with return code %d: %s",
|
|
95
|
+
ret,
|
|
96
|
+
stderr.decode(encoding="utf8"),
|
|
104
97
|
)
|
|
98
|
+
exit_if(ret != 0, stderr.decode(encoding="utf8"))
|
|
105
99
|
|
|
106
100
|
if verbose:
|
|
107
101
|
info("\tPushing")
|
|
@@ -109,11 +103,12 @@ def commit(
|
|
|
109
103
|
|
|
110
104
|
ret, stdout, stderr = quickpub.proxy.cm("git push")
|
|
111
105
|
if ret != 0:
|
|
112
|
-
logger.error(
|
|
113
|
-
|
|
114
|
-
ret
|
|
115
|
-
stderr.decode(encoding="utf8")
|
|
106
|
+
logger.error(
|
|
107
|
+
"Git push failed with return code %d: %s",
|
|
108
|
+
ret,
|
|
109
|
+
stderr.decode(encoding="utf8"),
|
|
116
110
|
)
|
|
111
|
+
exit_if(ret != 0, stderr.decode(encoding="utf8"))
|
|
117
112
|
|
|
118
113
|
logger.info("Successfully committed and pushed version '%s'", version)
|
|
119
114
|
|
|
@@ -4,13 +4,15 @@ 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
|
-
return record.name.startswith(
|
|
14
|
+
return record.name.startswith("quickpub")
|
|
15
|
+
|
|
14
16
|
|
|
15
17
|
class TqdmLoggingHandler(logging.Handler):
|
|
16
18
|
"""
|
|
@@ -24,6 +26,7 @@ class TqdmLoggingHandler(logging.Handler):
|
|
|
24
26
|
def emit(self, record):
|
|
25
27
|
try:
|
|
26
28
|
import tqdm
|
|
29
|
+
|
|
27
30
|
msg = self.format(record)
|
|
28
31
|
tqdm.tqdm.write(msg, file=sys.stdout)
|
|
29
32
|
except ImportError:
|
|
@@ -32,6 +35,7 @@ class TqdmLoggingHandler(logging.Handler):
|
|
|
32
35
|
except Exception:
|
|
33
36
|
self.handleError(record)
|
|
34
37
|
|
|
38
|
+
|
|
35
39
|
def setup_logging(level: int = None):
|
|
36
40
|
"""
|
|
37
41
|
Set up logging with appropriate handler based on tqdm availability.
|
|
@@ -46,11 +50,11 @@ def setup_logging(level: int = None):
|
|
|
46
50
|
Configured logger instance
|
|
47
51
|
"""
|
|
48
52
|
global _LOG_LEVEL
|
|
49
|
-
|
|
53
|
+
|
|
50
54
|
# Use provided level or fall back to global constant
|
|
51
55
|
if level is not None:
|
|
52
56
|
_LOG_LEVEL = level
|
|
53
|
-
|
|
57
|
+
|
|
54
58
|
logger = logging.getLogger()
|
|
55
59
|
logger.setLevel(_LOG_LEVEL)
|
|
56
60
|
|
|
@@ -58,8 +62,9 @@ def setup_logging(level: int = None):
|
|
|
58
62
|
logger.handlers.clear()
|
|
59
63
|
|
|
60
64
|
# Common formatter for both handlers
|
|
65
|
+
# Example output: 2024-01-15 10:30:45,123 - INFO - [quickpub.some_module - some_module.py:42] - This is a log message
|
|
61
66
|
formatter = logging.Formatter(
|
|
62
|
-
|
|
67
|
+
"%(asctime)s - %(levelname)s - [%(name)s - %(filename)s:%(lineno)d] - %(message)s"
|
|
63
68
|
)
|
|
64
69
|
|
|
65
70
|
try:
|
|
@@ -71,35 +76,33 @@ def setup_logging(level: int = None):
|
|
|
71
76
|
handler = logging.StreamHandler(sys.stdout)
|
|
72
77
|
handler.setLevel(_LOG_LEVEL)
|
|
73
78
|
handler.setFormatter(formatter)
|
|
74
|
-
|
|
79
|
+
|
|
75
80
|
# Add filter to only allow quickpub logs
|
|
76
81
|
handler.addFilter(QuickpubLogFilter())
|
|
77
|
-
|
|
82
|
+
|
|
78
83
|
logger.addHandler(handler)
|
|
79
84
|
|
|
85
|
+
|
|
80
86
|
def set_log_level(level: int):
|
|
81
87
|
"""
|
|
82
88
|
Set the logging level for the root logger and all its handlers.
|
|
83
|
-
|
|
89
|
+
|
|
84
90
|
This function allows end users to dynamically change the log level
|
|
85
91
|
after logging has been set up. It also updates the global _LOG_LEVEL
|
|
86
92
|
constant so that future calls to setup_logging() will use this level.
|
|
87
|
-
|
|
93
|
+
|
|
88
94
|
Args:
|
|
89
95
|
level: Logging level (e.g., logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR)
|
|
90
96
|
"""
|
|
91
97
|
global _LOG_LEVEL
|
|
92
98
|
_LOG_LEVEL = level
|
|
93
|
-
|
|
99
|
+
|
|
94
100
|
logger = logging.getLogger()
|
|
95
101
|
logger.setLevel(level)
|
|
96
|
-
|
|
102
|
+
|
|
97
103
|
# Update all handlers to use the new level
|
|
98
104
|
for handler in logger.handlers:
|
|
99
105
|
handler.setLevel(level)
|
|
100
106
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
"set_log_level",
|
|
104
|
-
"QuickpubLogFilter"
|
|
105
|
-
]
|
|
107
|
+
|
|
108
|
+
__all__ = ["setup_logging", "set_log_level", "QuickpubLogFilter"]
|