quickpub 3.0.1__tar.gz → 3.0.5__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 (63) hide show
  1. {quickpub-3.0.1/quickpub.egg-info → quickpub-3.0.5}/PKG-INFO +1 -1
  2. {quickpub-3.0.1 → quickpub-3.0.5}/pyproject.toml +1 -1
  3. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/__init__.py +1 -0
  4. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/__main__.py +3 -3
  5. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/classifiers.py +6 -2
  6. quickpub-3.0.5/quickpub/enforcers.py +39 -0
  7. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/files.py +33 -0
  8. quickpub-3.0.5/quickpub/functions.py +125 -0
  9. quickpub-3.0.5/quickpub/logging_.py +105 -0
  10. quickpub-3.0.5/quickpub/proxy.py +56 -0
  11. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/qa.py +114 -33
  12. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/__init__.py +1 -1
  13. quickpub-3.0.5/quickpub/strategies/build_schema.py +28 -0
  14. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/constraint_enforcer.py +8 -1
  15. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/build_schemas/setuptools_build_schema.py +19 -2
  16. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/constraint_enforcers/license_enforcer.py +9 -0
  17. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/constraint_enforcers/local_version_enforcer.py +12 -0
  18. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/constraint_enforcers/pypi_remote_version_enforcer.py +12 -0
  19. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/constraint_enforcers/pypirc_enforcer.py +11 -0
  20. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/constraint_enforcers/readme_enforcer.py +9 -0
  21. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/python_providers/conda_python_provider.py +20 -2
  22. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/python_providers/default_python_provider.py +12 -0
  23. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/quality_assurance_runners/mypy_qa_runner.py +17 -1
  24. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/quality_assurance_runners/pylint_qa_runner.py +19 -1
  25. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/quality_assurance_runners/pytest_qa_runner.py +21 -2
  26. quickpub-3.0.5/quickpub/strategies/implementations/quality_assurance_runners/unittest_qa_runner.py +108 -0
  27. quickpub-3.0.5/quickpub/strategies/implementations/upload_targets/github_upload_target.py +45 -0
  28. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/upload_targets/pypirc_upload_target.py +27 -6
  29. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/python_provider.py +17 -0
  30. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/quality_assurance_runner.py +65 -9
  31. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/quickpub_strategy.py +1 -0
  32. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/upload_target.py +8 -1
  33. quickpub-3.0.5/quickpub/structures/bound.py +60 -0
  34. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/structures/dependency.py +27 -3
  35. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/structures/version.py +15 -1
  36. quickpub-3.0.5/quickpub/validators.py +113 -0
  37. quickpub-3.0.5/quickpub/worker_pool.py +15 -0
  38. {quickpub-3.0.1 → quickpub-3.0.5/quickpub.egg-info}/PKG-INFO +1 -1
  39. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub.egg-info/SOURCES.txt +2 -0
  40. quickpub-3.0.1/quickpub/enforcers.py +0 -27
  41. quickpub-3.0.1/quickpub/functions.py +0 -72
  42. quickpub-3.0.1/quickpub/proxy.py +0 -24
  43. quickpub-3.0.1/quickpub/strategies/build_schema.py +0 -17
  44. quickpub-3.0.1/quickpub/strategies/implementations/quality_assurance_runners/unittest_qa_runner.py +0 -69
  45. quickpub-3.0.1/quickpub/strategies/implementations/upload_targets/github_upload_target.py +0 -26
  46. quickpub-3.0.1/quickpub/structures/bound.py +0 -37
  47. quickpub-3.0.1/quickpub/validators.py +0 -51
  48. {quickpub-3.0.1 → quickpub-3.0.5}/LICENSE +0 -0
  49. {quickpub-3.0.1 → quickpub-3.0.5}/MANIFEST.in +0 -0
  50. {quickpub-3.0.1 → quickpub-3.0.5}/README.md +0 -0
  51. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/py.typed +0 -0
  52. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/__init__.py +0 -0
  53. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/build_schemas/__init__.py +0 -0
  54. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/constraint_enforcers/__init__.py +0 -0
  55. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/python_providers/__init__.py +0 -0
  56. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/quality_assurance_runners/__init__.py +0 -0
  57. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/strategies/implementations/upload_targets/__init__.py +0 -0
  58. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub/structures/__init__.py +0 -0
  59. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub.egg-info/dependency_links.txt +0 -0
  60. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub.egg-info/requires.txt +0 -0
  61. {quickpub-3.0.1 → quickpub-3.0.5}/quickpub.egg-info/top_level.txt +0 -0
  62. {quickpub-3.0.1 → quickpub-3.0.5}/setup.cfg +0 -0
  63. {quickpub-3.0.1 → quickpub-3.0.5}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quickpub
3
- Version: 3.0.1
3
+ Version: 3.0.5
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "quickpub"
7
- version = "3.0.1"
7
+ version = "3.0.5"
8
8
  authors = [
9
9
  { name = "danielnachumdev", email = "danielnachumdev@gmail.com" },
10
10
  ]
@@ -2,4 +2,5 @@ from .structures import *
2
2
  from .strategies import *
3
3
  from .enforcers import ExitEarlyError
4
4
  from .qa import SupportsProgress
5
+ from .logging_ import set_log_level
5
6
  from .__main__ import publish
@@ -1,5 +1,5 @@
1
1
  import asyncio
2
- from typing import Optional, Union, List, Any, Callable
2
+ from typing import Optional, Union, List, Any
3
3
 
4
4
  import fire
5
5
  from danielutils import warning, error
@@ -13,7 +13,9 @@ from .structures import Version, Dependency
13
13
  from .files import create_toml, create_setup, create_manifest
14
14
  from .classifiers import *
15
15
  from .qa import qa, SupportsProgress
16
+ from .logging_ import setup_logging
16
17
 
18
+ setup_logging()
17
19
 
18
20
  def publish(
19
21
  *,
@@ -37,7 +39,6 @@ def publish(
37
39
  keywords: Optional[List[str]] = None,
38
40
  explicit_src_folder_path: Optional[str] = None,
39
41
  # ========== QA Parameters ==========
40
- log: Optional[Callable[[Any], None]] = None,
41
42
  pbar: Optional[SupportsProgress] = None, # tqdm
42
43
 
43
44
  demo: bool = False,
@@ -87,7 +88,6 @@ def publish(
87
88
  name,
88
89
  explicit_src_folder_path,
89
90
  validated_dependencies,
90
- log,
91
91
  pbar
92
92
  ))
93
93
  if not res:
@@ -1,6 +1,8 @@
1
- from abc import ABC, abstractmethod
1
+ import logging
2
2
  from enum import Enum
3
3
 
4
+ logger = logging.getLogger(__name__)
5
+
4
6
 
5
7
  class Classifier(Enum):
6
8
  """
@@ -36,7 +38,9 @@ class Classifier(Enum):
36
38
  def __str__(self) -> str:
37
39
  name = Classifier._split_name(self.__class__.__qualname__)
38
40
  value = self._str()
39
- return f"{name} :: {value}"
41
+ result = f"{name} :: {value}"
42
+ logger.debug("Classifier string representation: %s", result)
43
+ return result
40
44
 
41
45
 
42
46
  class DevelopmentStatusClassifier(Classifier):
@@ -0,0 +1,39 @@
1
+ import logging
2
+ from typing import Union, Callable
3
+
4
+ from danielutils import error
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ class ExitEarlyError(Exception):
10
+ """Custom exception raised when early exit conditions are met."""
11
+
12
+
13
+ def exit_if(
14
+ predicate: Union[bool, Callable[[], bool]],
15
+ msg: str,
16
+ *,
17
+ verbose: bool = True,
18
+ err_func: Callable[[str], None] = error
19
+ ) -> None:
20
+ """
21
+ Exit the program if the given predicate is true.
22
+
23
+ :param predicate: Boolean value or callable that returns a boolean
24
+ :param msg: Error message to display
25
+ :param verbose: Whether to display the error message
26
+ :param err_func: Function to call for error display
27
+ :raises ExitEarlyError: When the predicate condition is met
28
+ """
29
+ if (isinstance(predicate, bool) and predicate) or (callable(predicate) and predicate()):
30
+ logger.error("Exit condition met: %s", msg)
31
+ if verbose:
32
+ err_func(msg)
33
+ raise ExitEarlyError(msg)
34
+
35
+
36
+ __all__ = [
37
+ "exit_if",
38
+ "ExitEarlyError",
39
+ ]
@@ -1,9 +1,12 @@
1
+ import logging
1
2
  from typing import List
2
3
  from danielutils import get_files
3
4
 
4
5
  from .classifiers import Classifier
5
6
  from .structures import Version, Dependency
6
7
 
8
+ logger = logging.getLogger(__name__)
9
+
7
10
 
8
11
  def create_toml(
9
12
  *,
@@ -21,6 +24,24 @@ def create_toml(
21
24
  dependencies: List[Dependency],
22
25
  classifiers: List[Classifier]
23
26
  ) -> None:
27
+ """
28
+ Create a pyproject.toml file for the package.
29
+
30
+ :param name: Package name
31
+ :param src_folder_path: Path to source folder
32
+ :param readme_file_path: Path to README file
33
+ :param license_file_path: Path to LICENSE file
34
+ :param version: Package version
35
+ :param author: Author name
36
+ :param author_email: Author email
37
+ :param description: Package description
38
+ :param homepage: Package homepage URL
39
+ :param keywords: List of package keywords
40
+ :param min_python: Minimum Python version required
41
+ :param dependencies: List of package dependencies
42
+ :param classifiers: List of package classifiers
43
+ """
44
+ logger.info("Creating pyproject.toml for package '%s' version '%s'", name, version)
24
45
  classifiers_string = ",\n\t".join([f"\"{str(c)}\"" for c in classifiers])
25
46
  if len(classifiers_string) > 0:
26
47
  classifiers_string = f"\n\t{classifiers_string}\n"
@@ -29,6 +50,7 @@ def create_toml(
29
50
  if file == "py.typed":
30
51
  py_typed = f"""[tool.setuptools.package-data]
31
52
  "{name}" = ["py.typed"]"""
53
+ logger.debug("Found py.typed file, adding package-data configuration")
32
54
  break
33
55
 
34
56
  s = f"""[build-system]
@@ -59,16 +81,27 @@ packages = ["{name}"]
59
81
  """
60
82
  with open("pyproject.toml", "w", encoding="utf8") as f:
61
83
  f.write(s)
84
+ logger.info("Successfully created pyproject.toml")
62
85
 
63
86
 
64
87
  def create_setup() -> None:
88
+ """Create a basic setup.py file for the package."""
89
+ logger.info("Creating setup.py file")
65
90
  with open("./setup.py", "w", encoding="utf8") as f:
66
91
  f.write("from setuptools import setup\n\nsetup()\n")
92
+ logger.info("Successfully created setup.py")
67
93
 
68
94
 
69
95
  def create_manifest(*, name: str) -> None:
96
+ """
97
+ Create a MANIFEST.in file for the package.
98
+
99
+ :param name: Package name
100
+ """
101
+ logger.info("Creating MANIFEST.in for package '%s'", name)
70
102
  with open("./MANIFEST.in", "w", encoding="utf8") as f:
71
103
  f.write(f"recursive-include {name} *.py")
104
+ logger.info("Successfully created MANIFEST.in")
72
105
 
73
106
 
74
107
  __all__ = [
@@ -0,0 +1,125 @@
1
+ import logging
2
+ from danielutils import info
3
+
4
+ from .enforcers import exit_if
5
+ from .structures import Version
6
+ import quickpub.proxy
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ def build(
12
+ *,
13
+ verbose: bool = True
14
+ ) -> None:
15
+ """
16
+ Build the package distribution.
17
+
18
+ :param verbose: Whether to display verbose output
19
+ """
20
+ logger.info("Starting build process")
21
+ if verbose:
22
+ info("Creating new distribution...")
23
+ logger.info("Creating new distribution...")
24
+
25
+ ret, stdout, stderr = quickpub.proxy.cm("python", "setup.py", "sdist")
26
+
27
+ if ret != 0:
28
+ logger.error("Build failed with return code %d: %s", ret, stderr.decode(encoding='utf8'))
29
+ exit_if(
30
+ ret != 0,
31
+ stderr.decode(encoding="utf8")
32
+ )
33
+
34
+ logger.info("Build completed successfully")
35
+
36
+
37
+ def upload(
38
+ *,
39
+ name: str,
40
+ version: Version,
41
+ verbose: bool = True
42
+ ) -> None:
43
+ """
44
+ Upload the package to PyPI.
45
+
46
+ :param name: Package name
47
+ :param version: Package version
48
+ :param verbose: Whether to display verbose output
49
+ """
50
+ logger.info("Starting upload process for package '%s' version '%s'", name, version)
51
+ if verbose:
52
+ info("Uploading")
53
+ logger.info("Uploading package to PyPI")
54
+
55
+ ret, stdout, stderr = quickpub.proxy.cm("twine", "upload", "--config-file", ".pypirc",
56
+ f"dist/{name}-{version}.tar.gz")
57
+
58
+ if ret != 0:
59
+ logger.error("Upload failed with return code %d: %s", ret, stderr.decode(encoding='utf8'))
60
+ exit_if(
61
+ 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"
63
+ )
64
+
65
+ logger.info("Successfully uploaded package '%s' version '%s'", name, version)
66
+
67
+
68
+ def commit(
69
+ *,
70
+ version: Version,
71
+ verbose: bool = True
72
+ ) -> None:
73
+ """
74
+ Commit and push changes to Git repository.
75
+
76
+ :param version: Package version
77
+ :param verbose: Whether to display verbose output
78
+ """
79
+ logger.info("Starting Git commit process for version '%s'", version)
80
+
81
+ if verbose:
82
+ info("Git")
83
+ info("\tStaging")
84
+ logger.info("Staging files for Git commit")
85
+
86
+ ret, stdout, stderr = quickpub.proxy.cm("git add .")
87
+ if ret != 0:
88
+ logger.error("Git add failed with return code %d: %s", ret, stderr.decode(encoding='utf8'))
89
+ exit_if(
90
+ ret != 0,
91
+ stderr.decode(encoding="utf8")
92
+ )
93
+
94
+ if verbose:
95
+ info("\tCommitting")
96
+ logger.info("Committing changes with message 'updated to version %s'", version)
97
+
98
+ ret, stdout, stderr = quickpub.proxy.cm(f"git commit -m \"updated to version {version}\"")
99
+ if ret != 0:
100
+ logger.error("Git commit failed with return code %d: %s", ret, stderr.decode(encoding='utf8'))
101
+ exit_if(
102
+ ret != 0,
103
+ stderr.decode(encoding="utf8")
104
+ )
105
+
106
+ if verbose:
107
+ info("\tPushing")
108
+ logger.info("Pushing changes to remote repository")
109
+
110
+ ret, stdout, stderr = quickpub.proxy.cm("git push")
111
+ if ret != 0:
112
+ logger.error("Git push failed with return code %d: %s", ret, stderr.decode(encoding='utf8'))
113
+ exit_if(
114
+ ret != 0,
115
+ stderr.decode(encoding="utf8")
116
+ )
117
+
118
+ logger.info("Successfully committed and pushed version '%s'", version)
119
+
120
+
121
+ __all__ = [
122
+ "build",
123
+ "upload",
124
+ "commit",
125
+ ]
@@ -0,0 +1,105 @@
1
+ import logging
2
+ import sys
3
+
4
+ # Global log level that can be modified by users
5
+ _LOG_LEVEL = logging.INFO
6
+
7
+ class QuickpubLogFilter(logging.Filter):
8
+ """
9
+ Filter that only allows logs from the quickpub package.
10
+ """
11
+
12
+ def filter(self, record):
13
+ return record.name.startswith('quickpub')
14
+
15
+ class TqdmLoggingHandler(logging.Handler):
16
+ """
17
+ Custom logging handler that uses tqdm.write to avoid conflicts with progress bars.
18
+ """
19
+
20
+ def __init__(self, level: int = logging.NOTSET):
21
+ super().__init__(level)
22
+ self._tqdm = None
23
+
24
+ def emit(self, record):
25
+ try:
26
+ import tqdm
27
+ msg = self.format(record)
28
+ tqdm.tqdm.write(msg, file=sys.stdout)
29
+ except ImportError:
30
+ # Fallback if tqdm becomes unavailable
31
+ print(self.format(record), file=sys.stdout)
32
+ except Exception:
33
+ self.handleError(record)
34
+
35
+ def setup_logging(level: int = None):
36
+ """
37
+ Set up logging with appropriate handler based on tqdm availability.
38
+
39
+ If tqdm is installed, uses tqdm.write for output to avoid conflicts with progress bars.
40
+ If tqdm is not installed, uses a standard StreamHandler to stdout.
41
+
42
+ Args:
43
+ level: Logging level (default: uses the global _LOG_LEVEL constant)
44
+
45
+ Returns:
46
+ Configured logger instance
47
+ """
48
+ global _LOG_LEVEL
49
+
50
+ # Use provided level or fall back to global constant
51
+ if level is not None:
52
+ _LOG_LEVEL = level
53
+
54
+ logger = logging.getLogger()
55
+ logger.setLevel(_LOG_LEVEL)
56
+
57
+ # Clear any existing handlers
58
+ logger.handlers.clear()
59
+
60
+ # Common formatter for both handlers
61
+ formatter = logging.Formatter(
62
+ '%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
63
+ )
64
+
65
+ try:
66
+ import tqdm
67
+
68
+ # Use tqdm.write if tqdm is available
69
+ handler = TqdmLoggingHandler()
70
+ except ImportError:
71
+ handler = logging.StreamHandler(sys.stdout)
72
+ handler.setLevel(_LOG_LEVEL)
73
+ handler.setFormatter(formatter)
74
+
75
+ # Add filter to only allow quickpub logs
76
+ handler.addFilter(QuickpubLogFilter())
77
+
78
+ logger.addHandler(handler)
79
+
80
+ def set_log_level(level: int):
81
+ """
82
+ Set the logging level for the root logger and all its handlers.
83
+
84
+ This function allows end users to dynamically change the log level
85
+ after logging has been set up. It also updates the global _LOG_LEVEL
86
+ constant so that future calls to setup_logging() will use this level.
87
+
88
+ Args:
89
+ level: Logging level (e.g., logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR)
90
+ """
91
+ global _LOG_LEVEL
92
+ _LOG_LEVEL = level
93
+
94
+ logger = logging.getLogger()
95
+ logger.setLevel(level)
96
+
97
+ # Update all handlers to use the new level
98
+ for handler in logger.handlers:
99
+ handler.setLevel(level)
100
+
101
+ __all__ = [
102
+ "setup_logging",
103
+ "set_log_level",
104
+ "QuickpubLogFilter"
105
+ ]
@@ -0,0 +1,56 @@
1
+ import logging
2
+ import os
3
+ from typing import Tuple
4
+ import requests
5
+ import danielutils
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ # need it like this for the testing
11
+ def cm(*args, **kwargs) -> Tuple[int, bytes, bytes]:
12
+ """
13
+ Execute a command and return the result.
14
+
15
+ :param args: Command arguments
16
+ :param kwargs: Additional keyword arguments
17
+ :return: Tuple of (return_code, stdout, stderr)
18
+ """
19
+ logger.debug("Executing command: %s", ' '.join(args))
20
+ result = danielutils.cm(*args, **kwargs)
21
+ logger.debug("Command completed with return code: %d", result[0])
22
+ return result
23
+
24
+
25
+ def os_system(command) -> int:
26
+ """
27
+ Execute a system command.
28
+
29
+ :param command: Command to execute
30
+ :return: Return code of the command
31
+ """
32
+ logger.debug("Executing system command: %s", command)
33
+ result = os.system(command)
34
+ logger.debug("System command completed with return code: %d", result)
35
+ return result
36
+
37
+
38
+ def get(*args, **kwargs) -> requests.models.Response:
39
+ """
40
+ Make an HTTP GET request.
41
+
42
+ :param args: Request arguments
43
+ :param kwargs: Additional keyword arguments
44
+ :return: Response object
45
+ """
46
+ logger.debug("Making HTTP GET request to: %s", args[0] if args else 'URL not provided')
47
+ response = requests.get(*args, **kwargs)
48
+ logger.debug("HTTP GET request completed with status code: %d", response.status_code)
49
+ return response
50
+
51
+
52
+ __all__ = [
53
+ "cm",
54
+ 'os_system',
55
+ "get"
56
+ ]