crackerjack 0.20.2__tar.gz → 0.20.7__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.
- {crackerjack-0.20.2 → crackerjack-0.20.7}/PKG-INFO +2 -1
- crackerjack-0.20.7/crackerjack/.ruff_cache/0.11.13/1867267426380906393 +0 -0
- crackerjack-0.20.7/crackerjack/.ruff_cache/0.11.13/4240757255861806333 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/crackerjack.py +245 -45
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/pyproject.toml +3 -1
- {crackerjack-0.20.2 → crackerjack-0.20.7}/pyproject.toml +3 -1
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/test_crackerjack.py +270 -49
- crackerjack-0.20.2/crackerjack/.ruff_cache/0.11.13/1867267426380906393 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/LICENSE +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/README.md +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.gitignore +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.libcst.codemod.yaml +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.pdm.toml +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.pre-commit-config.yaml +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.pytest_cache/.gitignore +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.pytest_cache/CACHEDIR.TAG +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.pytest_cache/README.md +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.pytest_cache/v/cache/nodeids +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.pytest_cache/v/cache/stepwise +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/.gitignore +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.11/18187162184424859798 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.12/16869036553936192448 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.12/1867267426380906393 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.12/4240757255861806333 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.12/4441409093023629623 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/CACHEDIR.TAG +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/__init__.py +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/__main__.py +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/errors.py +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/interactive.py +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/py313.py +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/TESTING.md +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/__init__.py +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/conftest.py +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/data/comments_sample.txt +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/data/docstrings_sample.txt +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/data/expected_comments_sample.txt +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/data/init.py +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/test_crackerjack_runner.py +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/test_errors.py +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/test_interactive.py +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/test_interactive_run.py +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/test_main.py +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/test_py313_advanced.py +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/test_py313_features.py +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/test_pytest_features.py +0 -0
- {crackerjack-0.20.2 → crackerjack-0.20.7}/tests/test_structured_errors.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: crackerjack
|
3
|
-
Version: 0.20.
|
3
|
+
Version: 0.20.7
|
4
4
|
Summary: Crackerjack: code quality toolkit
|
5
5
|
Keywords: bandit,black,creosote,mypy,pyright,pytest,refurb,ruff
|
6
6
|
Author-Email: lesleslie <les@wedgwoodwebworks.com>
|
@@ -23,6 +23,7 @@ Project-URL: homepage, https://github.com/lesleslie/crackerjack
|
|
23
23
|
Project-URL: repository, https://github.com/lesleslie/crackerjack
|
24
24
|
Requires-Python: >=3.13
|
25
25
|
Requires-Dist: autotyping>=24.9
|
26
|
+
Requires-Dist: keyring>=25.6
|
26
27
|
Requires-Dist: pdm>=2.24.2
|
27
28
|
Requires-Dist: pdm-bump>=0.9.12
|
28
29
|
Requires-Dist: pre-commit>=4.2
|
Binary file
|
Binary file
|
@@ -1017,40 +1017,33 @@ class Crackerjack:
|
|
1017
1017
|
self.execute_command(["pdm", "bump", option])
|
1018
1018
|
break
|
1019
1019
|
|
1020
|
-
def
|
1020
|
+
def _ensure_keyring_installed(self, options: OptionsProtocol) -> None:
|
1021
|
+
"""Ensure keyring is installed for PDM on macOS."""
|
1021
1022
|
from .errors import ErrorCode, PublishError, handle_error
|
1022
1023
|
|
1023
|
-
if
|
1024
|
-
|
1025
|
-
authorize = self.execute_command(
|
1026
|
-
["pdm", "self", "add", "keyring"], capture_output=True, text=True
|
1027
|
-
)
|
1028
|
-
if authorize.returncode > 0:
|
1029
|
-
error = PublishError(
|
1030
|
-
message="Authentication setup failed",
|
1031
|
-
error_code=ErrorCode.AUTHENTICATION_ERROR,
|
1032
|
-
details=f"Failed to add keyring support to PDM.\nCommand output:\n{authorize.stderr}",
|
1033
|
-
recovery="Please manually add your keyring credentials to PDM. Run `pdm self add keyring` and try again.",
|
1034
|
-
exit_code=1,
|
1035
|
-
)
|
1036
|
-
handle_error(
|
1037
|
-
error=error,
|
1038
|
-
console=self.console,
|
1039
|
-
verbose=options.verbose,
|
1040
|
-
ai_agent=options.ai_agent,
|
1041
|
-
)
|
1024
|
+
if platform.system() != "Darwin":
|
1025
|
+
return
|
1042
1026
|
|
1043
|
-
|
1044
|
-
|
1027
|
+
# Check if keyring is already installed in PDM
|
1028
|
+
check_keyring = self.execute_command(
|
1029
|
+
["pdm", "self", "list"], capture_output=True, text=True
|
1030
|
+
)
|
1031
|
+
keyring_installed = "keyring" in check_keyring.stdout
|
1032
|
+
|
1033
|
+
if not keyring_installed:
|
1034
|
+
# Only attempt to install keyring if it's not already installed
|
1035
|
+
self.console.print("Installing keyring for PDM...")
|
1036
|
+
authorize = self.execute_command(
|
1037
|
+
["pdm", "self", "add", "keyring"],
|
1038
|
+
capture_output=True,
|
1039
|
+
text=True,
|
1045
1040
|
)
|
1046
|
-
|
1047
|
-
|
1048
|
-
if build.returncode > 0:
|
1041
|
+
if authorize.returncode > 0:
|
1049
1042
|
error = PublishError(
|
1050
|
-
message="
|
1051
|
-
error_code=ErrorCode.
|
1052
|
-
details=f"
|
1053
|
-
recovery="
|
1043
|
+
message="Authentication setup failed",
|
1044
|
+
error_code=ErrorCode.AUTHENTICATION_ERROR,
|
1045
|
+
details=f"Failed to add keyring support to PDM.\nCommand output:\n{authorize.stderr}",
|
1046
|
+
recovery="Please manually add your keyring credentials to PDM. Run `pdm self add keyring` and try again.",
|
1054
1047
|
exit_code=1,
|
1055
1048
|
)
|
1056
1049
|
handle_error(
|
@@ -1060,26 +1053,233 @@ class Crackerjack:
|
|
1060
1053
|
ai_agent=options.ai_agent,
|
1061
1054
|
)
|
1062
1055
|
|
1063
|
-
|
1064
|
-
|
1056
|
+
def _build_package(self, options: OptionsProtocol) -> None:
|
1057
|
+
"""Build the package using PDM."""
|
1058
|
+
from .errors import ErrorCode, PublishError, handle_error
|
1059
|
+
|
1060
|
+
build = self.execute_command(["pdm", "build"], capture_output=True, text=True)
|
1061
|
+
self.console.print(build.stdout)
|
1062
|
+
|
1063
|
+
if build.returncode > 0:
|
1064
|
+
error = PublishError(
|
1065
|
+
message="Package build failed",
|
1066
|
+
error_code=ErrorCode.BUILD_ERROR,
|
1067
|
+
details=f"Command output:\n{build.stderr}",
|
1068
|
+
recovery="Review the error message above for details. Common issues include missing dependencies, invalid project structure, or incorrect metadata in pyproject.toml.",
|
1069
|
+
exit_code=1,
|
1070
|
+
)
|
1071
|
+
handle_error(
|
1072
|
+
error=error,
|
1073
|
+
console=self.console,
|
1074
|
+
verbose=options.verbose,
|
1075
|
+
ai_agent=options.ai_agent,
|
1065
1076
|
)
|
1066
1077
|
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1078
|
+
def _prepare_publish_command(self) -> list[str]:
|
1079
|
+
"""Prepare the PDM publish command with appropriate flags."""
|
1080
|
+
# Prepare the publish command
|
1081
|
+
publish_cmd = ["pdm", "publish", "--no-build"]
|
1082
|
+
|
1083
|
+
# Check if we're running in a CI environment
|
1084
|
+
is_ci = any(env in os.environ for env in ("CI", "GITHUB_ACTIONS", "GITLAB_CI"))
|
1085
|
+
|
1086
|
+
# If in CI environment, check if OIDC is likely to be the issue
|
1087
|
+
if is_ci:
|
1088
|
+
self.console.print("[yellow]Detected CI environment[/yellow]")
|
1089
|
+
|
1090
|
+
# Check for required OIDC-related environment variables
|
1091
|
+
if "GITHUB_ACTIONS" not in os.environ and "GITLAB_CI" not in os.environ:
|
1092
|
+
self.console.print(
|
1093
|
+
"[yellow]Non-GitHub/GitLab CI environment detected[/yellow]"
|
1074
1094
|
)
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1095
|
+
# Don't add --no-oidc flag as it may not be supported by all PDM versions
|
1096
|
+
elif "ACTIONS_ID_TOKEN_REQUEST_URL" not in os.environ:
|
1097
|
+
self.console.print("[yellow]OIDC token request URL not found[/yellow]")
|
1098
|
+
# Don't add --no-oidc flag as it may not be supported by all PDM versions
|
1099
|
+
|
1100
|
+
# Check for API token in environment regardless of OIDC status
|
1101
|
+
self._check_for_token_env_vars()
|
1102
|
+
|
1103
|
+
return publish_cmd
|
1104
|
+
|
1105
|
+
def _check_for_token_env_vars(self) -> None:
|
1106
|
+
"""Check for token environment variables."""
|
1107
|
+
for token_var in ("PYPI_TOKEN", "PYPI_API_TOKEN", "TWINE_PASSWORD"):
|
1108
|
+
if token_var in os.environ:
|
1109
|
+
self.console.print(
|
1110
|
+
f"[yellow]Found {token_var} environment variable, will use for authentication[/yellow]"
|
1080
1111
|
)
|
1081
|
-
|
1082
|
-
|
1112
|
+
# If we have a token, PDM will use it automatically
|
1113
|
+
break
|
1114
|
+
else:
|
1115
|
+
self.console.print(
|
1116
|
+
"[yellow]No PyPI token found in environment variables[/yellow]"
|
1117
|
+
)
|
1118
|
+
|
1119
|
+
def _check_keyring_credentials(self) -> None:
|
1120
|
+
"""Check for PyPI credentials in keyring."""
|
1121
|
+
# First try to verify if keyring has PyPI credentials, wrapped in double try/except
|
1122
|
+
# to handle both ImportError and keyring backend errors
|
1123
|
+
try:
|
1124
|
+
try:
|
1125
|
+
import keyring
|
1126
|
+
|
1127
|
+
username = keyring.get_password("pdm-publish", "username")
|
1128
|
+
token = (
|
1129
|
+
keyring.get_password("pdm-publish", username) if username else None
|
1130
|
+
)
|
1131
|
+
|
1132
|
+
if username and token:
|
1133
|
+
self.console.print(
|
1134
|
+
f"[green]Found PyPI credentials in keyring for user: {username}[/green]"
|
1135
|
+
)
|
1136
|
+
else:
|
1137
|
+
self.console.print(
|
1138
|
+
"[yellow]Warning: Could not find PyPI credentials in keyring.[/yellow]"
|
1139
|
+
)
|
1140
|
+
self.console.print(
|
1141
|
+
"[yellow]You might be prompted for username and password.[/yellow]"
|
1142
|
+
)
|
1143
|
+
except ImportError:
|
1144
|
+
self.console.print(
|
1145
|
+
"[yellow]Warning: Could not import keyring module to check credentials.[/yellow]"
|
1146
|
+
)
|
1147
|
+
except Exception as e:
|
1148
|
+
# Catch any keyring-related exceptions, including NoKeyringError
|
1149
|
+
self.console.print(f"[yellow]Warning: Keyring error: {e}[/yellow]")
|
1150
|
+
self.console.print(
|
1151
|
+
"[yellow]Will use PDM's built-in credential handling.[/yellow]"
|
1152
|
+
)
|
1153
|
+
|
1154
|
+
def _execute_publish_command(
|
1155
|
+
self, publish_cmd: list[str], options: OptionsProtocol
|
1156
|
+
) -> None:
|
1157
|
+
"""Execute the publish command and handle OIDC errors."""
|
1158
|
+
from .errors import (
|
1159
|
+
ErrorCode,
|
1160
|
+
ExecutionError,
|
1161
|
+
PublishError,
|
1162
|
+
check_command_result,
|
1163
|
+
handle_error,
|
1164
|
+
)
|
1165
|
+
|
1166
|
+
# Log the exact command we're running
|
1167
|
+
self.console.print(f"[yellow]Running command: {' '.join(publish_cmd)}[/yellow]")
|
1168
|
+
|
1169
|
+
# Execute the publish command with detailed output
|
1170
|
+
publish_result = self.execute_command(
|
1171
|
+
publish_cmd, capture_output=True, text=True
|
1172
|
+
)
|
1173
|
+
|
1174
|
+
# Print the command's stdout and stderr for debugging
|
1175
|
+
self.console.print(
|
1176
|
+
f"[yellow]Debug: publish stdout:[/yellow]\n{publish_result.stdout}"
|
1177
|
+
)
|
1178
|
+
self.console.print(
|
1179
|
+
f"[yellow]Debug: publish stderr:[/yellow]\n{publish_result.stderr}"
|
1180
|
+
)
|
1181
|
+
self.console.print(
|
1182
|
+
f"[yellow]Debug: publish return code: {publish_result.returncode}[/yellow]"
|
1183
|
+
)
|
1184
|
+
|
1185
|
+
# If first attempt failed with OIDC error, try an alternative approach
|
1186
|
+
if publish_result.returncode > 0 and "OIDC" in publish_result.stderr:
|
1187
|
+
self.console.print(
|
1188
|
+
"[yellow]Detected OIDC error, OIDC may not be supported on this platform[/yellow]"
|
1189
|
+
)
|
1190
|
+
|
1191
|
+
# Provide a more helpful error message
|
1192
|
+
error = PublishError(
|
1193
|
+
message="Package publication failed - OIDC authentication issue",
|
1194
|
+
error_code=ErrorCode.AUTHENTICATION_ERROR,
|
1195
|
+
details=f"OIDC authentication is not supported on this platform or configuration.\n\nCommand output:\n{publish_result.stderr}",
|
1196
|
+
recovery="Consider manually setting PyPI credentials using the keyring or environment variables. Run 'pdm config pypi.username YOUR_USERNAME' and 'pdm config pypi.password YOUR_PASSWORD' to configure credentials.",
|
1197
|
+
exit_code=1,
|
1198
|
+
)
|
1199
|
+
handle_error(
|
1200
|
+
error=error,
|
1201
|
+
console=self.console,
|
1202
|
+
verbose=options.verbose,
|
1203
|
+
ai_agent=options.ai_agent,
|
1204
|
+
)
|
1205
|
+
return
|
1206
|
+
|
1207
|
+
# Check the command result and raise an error if it failed
|
1208
|
+
try:
|
1209
|
+
cmd_str = " ".join(publish_cmd)
|
1210
|
+
check_command_result(
|
1211
|
+
publish_result,
|
1212
|
+
cmd_str,
|
1213
|
+
"Package publication failed",
|
1214
|
+
ErrorCode.PUBLISH_ERROR,
|
1215
|
+
"Ensure you have the correct PyPI credentials configured. Check your internet connection and that the package name is available on PyPI.",
|
1216
|
+
)
|
1217
|
+
except ExecutionError as e:
|
1218
|
+
# Convert to PublishError for consistent error handling
|
1219
|
+
error = PublishError(
|
1220
|
+
message=e.message,
|
1221
|
+
error_code=ErrorCode.PUBLISH_ERROR,
|
1222
|
+
details=e.details,
|
1223
|
+
recovery=e.recovery,
|
1224
|
+
exit_code=1,
|
1225
|
+
)
|
1226
|
+
handle_error(
|
1227
|
+
error=error,
|
1228
|
+
console=self.console,
|
1229
|
+
verbose=options.verbose,
|
1230
|
+
ai_agent=options.ai_agent,
|
1231
|
+
)
|
1232
|
+
|
1233
|
+
def _publish_project(self, options: OptionsProtocol) -> None:
|
1234
|
+
"""Publish the package to PyPI."""
|
1235
|
+
from .errors import ErrorCode, PublishError, handle_error
|
1236
|
+
|
1237
|
+
if not options.publish:
|
1238
|
+
return
|
1239
|
+
|
1240
|
+
try:
|
1241
|
+
# Step 1: Ensure keyring is installed (on macOS)
|
1242
|
+
self._ensure_keyring_installed(options)
|
1243
|
+
|
1244
|
+
# Step 2: Build the package
|
1245
|
+
self._build_package(options)
|
1246
|
+
|
1247
|
+
# Print debug info before executing publish command
|
1248
|
+
self.console.print(
|
1249
|
+
"[yellow]Debug: About to execute PDM publish command[/yellow]"
|
1250
|
+
)
|
1251
|
+
|
1252
|
+
# Step 3: Setup environment variables for authentication
|
1253
|
+
os.environ["PDM_USE_KEYRING"] = "1"
|
1254
|
+
|
1255
|
+
# Step 4: Prepare publish command with appropriate flags
|
1256
|
+
publish_cmd = self._prepare_publish_command()
|
1257
|
+
|
1258
|
+
# Step 5: Check keyring credentials
|
1259
|
+
self._check_keyring_credentials()
|
1260
|
+
|
1261
|
+
# Step 6: Execute publish command and handle OIDC errors
|
1262
|
+
self._execute_publish_command(publish_cmd, options)
|
1263
|
+
|
1264
|
+
# Success message
|
1265
|
+
self.console.print("[green]✅ Package published successfully![/green]")
|
1266
|
+
|
1267
|
+
except Exception as e:
|
1268
|
+
# Catch any other unexpected errors
|
1269
|
+
self.console.print(f"[red]Debug: Unexpected error: {e}[/red]")
|
1270
|
+
error = PublishError(
|
1271
|
+
message="Package publication failed",
|
1272
|
+
error_code=ErrorCode.PUBLISH_ERROR,
|
1273
|
+
details=f"Unexpected error: {e}",
|
1274
|
+
recovery="This is an unexpected error. Please report this issue.",
|
1275
|
+
exit_code=1,
|
1276
|
+
)
|
1277
|
+
handle_error(
|
1278
|
+
error=error,
|
1279
|
+
console=self.console,
|
1280
|
+
verbose=options.verbose,
|
1281
|
+
ai_agent=options.ai_agent,
|
1282
|
+
)
|
1083
1283
|
|
1084
1284
|
def _commit_and_push(self, options: OptionsProtocol) -> None:
|
1085
1285
|
if options.commit:
|
@@ -4,7 +4,7 @@ requires = [ "pdm-backend" ]
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "crackerjack"
|
7
|
-
version = "0.20.
|
7
|
+
version = "0.20.6"
|
8
8
|
description = "Crackerjack: code quality toolkit"
|
9
9
|
readme = "README.md"
|
10
10
|
keywords = [
|
@@ -42,6 +42,7 @@ classifiers = [
|
|
42
42
|
]
|
43
43
|
dependencies = [
|
44
44
|
"autotyping>=24.9",
|
45
|
+
"keyring>=25.6",
|
45
46
|
"pdm>=2.24.2",
|
46
47
|
"pdm-bump>=0.9.12",
|
47
48
|
"pre-commit>=4.2",
|
@@ -216,6 +217,7 @@ exclude-deps = [
|
|
216
217
|
"tomli-w",
|
217
218
|
"google-crc32c",
|
218
219
|
"pytest-timeout",
|
220
|
+
"keyring",
|
219
221
|
]
|
220
222
|
|
221
223
|
[tool.refurb]
|
@@ -6,7 +6,7 @@ requires = [
|
|
6
6
|
|
7
7
|
[project]
|
8
8
|
name = "crackerjack"
|
9
|
-
version = "0.20.
|
9
|
+
version = "0.20.7"
|
10
10
|
description = "Crackerjack: code quality toolkit"
|
11
11
|
readme = "README.md"
|
12
12
|
keywords = [
|
@@ -42,6 +42,7 @@ classifiers = [
|
|
42
42
|
]
|
43
43
|
dependencies = [
|
44
44
|
"autotyping>=24.9",
|
45
|
+
"keyring>=25.6",
|
45
46
|
"pdm>=2.24.2",
|
46
47
|
"pdm-bump>=0.9.12",
|
47
48
|
"pre-commit>=4.2",
|
@@ -241,6 +242,7 @@ exclude-deps = [
|
|
241
242
|
"tomli-w",
|
242
243
|
"google-crc32c",
|
243
244
|
"pytest-timeout",
|
245
|
+
"keyring",
|
244
246
|
]
|
245
247
|
|
246
248
|
[tool.refurb]
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import os
|
2
|
+
import subprocess
|
2
3
|
import typing as t
|
3
4
|
from contextlib import suppress
|
4
5
|
from dataclasses import dataclass
|
@@ -12,8 +13,10 @@ from crackerjack.crackerjack import (
|
|
12
13
|
CodeCleaner,
|
13
14
|
ConfigManager,
|
14
15
|
Crackerjack,
|
16
|
+
OptionsProtocol,
|
15
17
|
ProjectManager,
|
16
18
|
)
|
19
|
+
from crackerjack.errors import ErrorCode
|
17
20
|
|
18
21
|
|
19
22
|
class BumpOption(str, Enum):
|
@@ -180,7 +183,7 @@ class TestCrackerjackProcess:
|
|
180
183
|
options = options_factory(commit=True, no_config_updates=True)
|
181
184
|
with patch.object(Crackerjack, "_update_project") as mock_update_project:
|
182
185
|
|
183
|
-
def side_effect(opts:
|
186
|
+
def side_effect(opts: OptionsProtocol) -> None:
|
184
187
|
if opts.no_config_updates:
|
185
188
|
mock_console_print("Skipping config updates.")
|
186
189
|
|
@@ -580,7 +583,9 @@ class TestCrackerjackProcess:
|
|
580
583
|
with patch("platform.system", return_value="Linux"):
|
581
584
|
with patch.object(Crackerjack, "execute_command") as mock_cj_execute:
|
582
585
|
|
583
|
-
def mock_execute_side_effect(
|
586
|
+
def mock_execute_side_effect(
|
587
|
+
*args: t.Any, **kwargs: t.Any
|
588
|
+
) -> subprocess.CompletedProcess[str]:
|
584
589
|
cmd = args[0][0]
|
585
590
|
if cmd == "pdm" and "build" in args[0]:
|
586
591
|
return MagicMock(
|
@@ -596,55 +601,271 @@ class TestCrackerjackProcess:
|
|
596
601
|
|
597
602
|
mock_handle_error.assert_called()
|
598
603
|
|
599
|
-
def
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
tmp_path: Path,
|
604
|
-
tmp_path_package: Path,
|
605
|
-
create_package_dir: None,
|
606
|
-
options_factory: t.Callable[..., OptionsForTesting],
|
607
|
-
) -> None:
|
608
|
-
options = options_factory(publish="micro", no_config_updates=True)
|
609
|
-
with patch("platform.system", return_value="Darwin"):
|
610
|
-
with patch.object(Crackerjack, "execute_command") as mock_cj_execute:
|
611
|
-
mock_cj_execute.side_effect = [
|
612
|
-
MagicMock(returncode=0, stdout="Success"),
|
613
|
-
MagicMock(returncode=0, stdout="Success"),
|
614
|
-
MagicMock(returncode=0, stdout="Success"),
|
615
|
-
MagicMock(returncode=0, stdout="Success"),
|
616
|
-
]
|
617
|
-
with patch.object(Crackerjack, "_update_project"):
|
618
|
-
cj = Crackerjack(dry_run=True)
|
619
|
-
cj.process(options)
|
620
|
-
mock_cj_execute.assert_any_call(
|
621
|
-
["pdm", "self", "add", "keyring"],
|
622
|
-
capture_output=True,
|
623
|
-
text=True,
|
624
|
-
)
|
604
|
+
def test_keyring_check_install_darwin(self) -> None:
|
605
|
+
"""Test checking for keyring on Darwin (macOS) and installing it."""
|
606
|
+
# Create a minimal options object
|
607
|
+
options = OptionsForTesting(publish=BumpOption.micro)
|
625
608
|
|
626
|
-
|
627
|
-
|
628
|
-
mock_execute: MagicMock,
|
629
|
-
mock_console_print: MagicMock,
|
630
|
-
tmp_path: Path,
|
631
|
-
tmp_path_package: Path,
|
632
|
-
create_package_dir: None,
|
633
|
-
options_factory: t.Callable[..., OptionsForTesting],
|
634
|
-
) -> None:
|
635
|
-
with patch("crackerjack.errors.handle_error") as mock_handle_error:
|
636
|
-
options = options_factory(publish="micro", no_config_updates=True)
|
637
|
-
with patch("platform.system", return_value="Darwin"):
|
638
|
-
with patch.object(Crackerjack, "execute_command") as mock_cj_execute:
|
639
|
-
mock_cj_execute.return_value = MagicMock(
|
640
|
-
returncode=1, stdout="", stderr="Authorization failed"
|
641
|
-
)
|
642
|
-
with patch.object(Crackerjack, "_update_project"):
|
643
|
-
with suppress(SystemExit):
|
644
|
-
cj = Crackerjack(dry_run=True)
|
645
|
-
cj.process(options)
|
609
|
+
# Use a list to track actual calls
|
610
|
+
actual_calls = []
|
646
611
|
|
647
|
-
|
612
|
+
# Create a Crackerjack instance for testing
|
613
|
+
crackerjack = Crackerjack(dry_run=False)
|
614
|
+
|
615
|
+
# Mock platform.system to return 'Darwin'
|
616
|
+
with patch("platform.system", return_value="Darwin"):
|
617
|
+
# Mock keyring module to avoid NoKeyringError
|
618
|
+
mock_keyring = MagicMock()
|
619
|
+
# Make get_password return None to simulate no credentials
|
620
|
+
mock_keyring.get_password.return_value = None
|
621
|
+
|
622
|
+
with patch.dict("sys.modules", {"keyring": mock_keyring}):
|
623
|
+
# Mock environment variables
|
624
|
+
with patch.dict("os.environ", {}, clear=True):
|
625
|
+
# Create a custom execute_command function that tracks calls
|
626
|
+
def mock_execute_side_effect(
|
627
|
+
*args: t.Any, **kwargs: t.Any
|
628
|
+
) -> subprocess.CompletedProcess[str]:
|
629
|
+
cmd = args[0]
|
630
|
+
actual_calls.append(cmd) # Track each command
|
631
|
+
|
632
|
+
if cmd == ["pdm", "self", "list"]:
|
633
|
+
# Important: This MUST NOT contain the string "keyring" to trigger installation
|
634
|
+
return MagicMock(
|
635
|
+
returncode=0, stdout="packages installed: pytest, black"
|
636
|
+
)
|
637
|
+
elif cmd == ["pdm", "self", "add", "keyring"]:
|
638
|
+
return MagicMock(returncode=0, stdout="installed keyring")
|
639
|
+
elif cmd == ["pdm", "build"]:
|
640
|
+
return MagicMock(returncode=0, stdout="build output")
|
641
|
+
elif "pdm" in cmd and "publish" in cmd:
|
642
|
+
return MagicMock(returncode=0, stdout="publish output")
|
643
|
+
return MagicMock(returncode=0, stdout="")
|
644
|
+
|
645
|
+
# Mock execute_command with our custom side_effect
|
646
|
+
with patch.object(
|
647
|
+
crackerjack,
|
648
|
+
"execute_command",
|
649
|
+
side_effect=mock_execute_side_effect,
|
650
|
+
):
|
651
|
+
# Mock console.print
|
652
|
+
with patch.object(crackerjack.console, "print"):
|
653
|
+
# Call the method we're testing
|
654
|
+
crackerjack._publish_project(options)
|
655
|
+
|
656
|
+
# Check that all the expected commands were called
|
657
|
+
assert ["pdm", "self", "list"] in actual_calls
|
658
|
+
assert ["pdm", "self", "add", "keyring"] in actual_calls
|
659
|
+
assert ["pdm", "build"] in actual_calls
|
660
|
+
|
661
|
+
# The publish command might have additional flags now
|
662
|
+
publish_cmd_found = False
|
663
|
+
for cmd in actual_calls:
|
664
|
+
if "pdm" in cmd and "publish" in cmd and "--no-build" in cmd:
|
665
|
+
publish_cmd_found = True
|
666
|
+
break
|
667
|
+
assert publish_cmd_found, "Expected pdm publish command was not called"
|
668
|
+
|
669
|
+
def test_keyring_already_installed_darwin(self) -> None:
|
670
|
+
"""Test behavior when keyring is already installed on Darwin (macOS)."""
|
671
|
+
# Create a minimal options object
|
672
|
+
options = OptionsForTesting(publish=BumpOption.micro)
|
673
|
+
|
674
|
+
# Use a list to track actual calls
|
675
|
+
actual_calls = []
|
676
|
+
|
677
|
+
# Create a Crackerjack instance for testing
|
678
|
+
crackerjack = Crackerjack(dry_run=False)
|
679
|
+
|
680
|
+
# Mock platform.system to return 'Darwin'
|
681
|
+
with patch("platform.system", return_value="Darwin"):
|
682
|
+
# Mock keyring module to avoid NoKeyringError
|
683
|
+
mock_keyring = MagicMock()
|
684
|
+
# Make get_password return None to simulate no credentials
|
685
|
+
mock_keyring.get_password.return_value = None
|
686
|
+
|
687
|
+
with patch.dict("sys.modules", {"keyring": mock_keyring}):
|
688
|
+
# Mock environment variables
|
689
|
+
with patch.dict("os.environ", {}, clear=True):
|
690
|
+
# Create a custom execute_command function that tracks calls
|
691
|
+
def mock_execute_side_effect(
|
692
|
+
*args: t.Any, **kwargs: t.Any
|
693
|
+
) -> subprocess.CompletedProcess[str]:
|
694
|
+
cmd = args[0]
|
695
|
+
actual_calls.append(cmd) # Track each command
|
696
|
+
|
697
|
+
if cmd == ["pdm", "self", "list"]:
|
698
|
+
return MagicMock(
|
699
|
+
returncode=0, stdout="keyring 25.6.0"
|
700
|
+
)
|
701
|
+
elif cmd == ["pdm", "build"]:
|
702
|
+
return MagicMock(returncode=0, stdout="build output")
|
703
|
+
elif "pdm" in cmd and "publish" in cmd:
|
704
|
+
return MagicMock(returncode=0, stdout="publish output")
|
705
|
+
return MagicMock(returncode=0, stdout="")
|
706
|
+
|
707
|
+
# Mock execute_command with our custom side_effect
|
708
|
+
with patch.object(
|
709
|
+
crackerjack,
|
710
|
+
"execute_command",
|
711
|
+
side_effect=mock_execute_side_effect,
|
712
|
+
):
|
713
|
+
# Mock console.print
|
714
|
+
with patch.object(crackerjack.console, "print"):
|
715
|
+
# Call the method we're testing
|
716
|
+
crackerjack._publish_project(options)
|
717
|
+
|
718
|
+
# Check that keyring installation was skipped
|
719
|
+
assert ["pdm", "self", "list"] in actual_calls
|
720
|
+
assert ["pdm", "self", "add", "keyring"] not in actual_calls
|
721
|
+
assert ["pdm", "build"] in actual_calls
|
722
|
+
|
723
|
+
# The publish command might have additional flags now
|
724
|
+
publish_cmd_found = False
|
725
|
+
for cmd in actual_calls:
|
726
|
+
if "pdm" in cmd and "publish" in cmd and "--no-build" in cmd:
|
727
|
+
publish_cmd_found = True
|
728
|
+
break
|
729
|
+
assert publish_cmd_found, "Expected pdm publish command was not called"
|
730
|
+
|
731
|
+
def test_keyring_install_failure_darwin(self) -> None:
|
732
|
+
"""Test handling of keyring installation failure on Darwin (macOS)."""
|
733
|
+
# Create a minimal options object with ai_agent False to avoid extra mocking
|
734
|
+
options = OptionsForTesting(publish=BumpOption.micro, ai_agent=False)
|
735
|
+
|
736
|
+
# Create a Crackerjack instance for testing
|
737
|
+
crackerjack = Crackerjack(dry_run=False)
|
738
|
+
|
739
|
+
# Mock the PublishError class to capture its creation
|
740
|
+
with patch("crackerjack.errors.PublishError") as mock_publish_error:
|
741
|
+
# Set up the mock to return a MagicMock that we can track
|
742
|
+
error_instance = MagicMock()
|
743
|
+
mock_publish_error.return_value = error_instance
|
744
|
+
|
745
|
+
# Mock handle_error to avoid actual exit
|
746
|
+
with patch("crackerjack.errors.handle_error") as mock_handle_error:
|
747
|
+
# Mock platform.system to return 'Darwin'
|
748
|
+
with patch("platform.system", return_value="Darwin"):
|
749
|
+
# Mock keyring module to avoid NoKeyringError
|
750
|
+
mock_keyring = MagicMock()
|
751
|
+
# Make get_password return None to simulate no credentials
|
752
|
+
mock_keyring.get_password.return_value = None
|
753
|
+
|
754
|
+
with patch.dict("sys.modules", {"keyring": mock_keyring}):
|
755
|
+
# Mock environment variables
|
756
|
+
with patch.dict("os.environ", {}, clear=True):
|
757
|
+
# Create a custom execute_command function with failure scenario
|
758
|
+
def mock_execute_side_effect(
|
759
|
+
*args: t.Any, **kwargs: t.Any
|
760
|
+
) -> subprocess.CompletedProcess[str]:
|
761
|
+
cmd = args[0]
|
762
|
+
|
763
|
+
if cmd == ["pdm", "self", "list"]:
|
764
|
+
# This must NOT contain the string "keyring" to trigger installation
|
765
|
+
return MagicMock(
|
766
|
+
returncode=0,
|
767
|
+
stdout="packages installed: pytest, black",
|
768
|
+
)
|
769
|
+
elif cmd == ["pdm", "self", "add", "keyring"]:
|
770
|
+
# Return a failure for keyring installation
|
771
|
+
mock_result = MagicMock()
|
772
|
+
mock_result.returncode = 1
|
773
|
+
mock_result.stdout = ""
|
774
|
+
mock_result.stderr = "Installation failed"
|
775
|
+
return mock_result
|
776
|
+
return MagicMock(returncode=0, stdout="")
|
777
|
+
|
778
|
+
# Mock execute_command with our custom side_effect
|
779
|
+
with patch.object(
|
780
|
+
crackerjack,
|
781
|
+
"execute_command",
|
782
|
+
side_effect=mock_execute_side_effect,
|
783
|
+
):
|
784
|
+
# Mock console.print to avoid terminal output
|
785
|
+
with patch.object(crackerjack.console, "print"):
|
786
|
+
# Call the method we're testing with error suppression
|
787
|
+
with suppress(SystemExit):
|
788
|
+
crackerjack._publish_project(options)
|
789
|
+
|
790
|
+
# Check that the error handler was called with the right error
|
791
|
+
mock_handle_error.assert_called_once_with(
|
792
|
+
error=error_instance,
|
793
|
+
console=crackerjack.console,
|
794
|
+
verbose=options.verbose,
|
795
|
+
ai_agent=options.ai_agent,
|
796
|
+
)
|
797
|
+
|
798
|
+
def test_publish_oidc_error_handling(self) -> None:
|
799
|
+
"""Test OIDC error detection and handling."""
|
800
|
+
# Create a minimal options object
|
801
|
+
options = OptionsForTesting(publish=BumpOption.micro)
|
802
|
+
|
803
|
+
# Use a list to track actual calls
|
804
|
+
actual_calls = []
|
805
|
+
|
806
|
+
# Create a Crackerjack instance for testing
|
807
|
+
crackerjack = Crackerjack(dry_run=False)
|
808
|
+
|
809
|
+
# Mock platform.system to return a non-Darwin platform to skip keyring install
|
810
|
+
with patch("platform.system", return_value="Linux"):
|
811
|
+
# Mock keyring module to avoid NoKeyringError
|
812
|
+
mock_keyring = MagicMock()
|
813
|
+
# Make get_password return None to simulate no credentials
|
814
|
+
mock_keyring.get_password.return_value = None
|
815
|
+
|
816
|
+
with patch.dict("sys.modules", {"keyring": mock_keyring}):
|
817
|
+
with patch.dict("os.environ", {}, clear=True):
|
818
|
+
# Create a custom execute_command function that simulates OIDC failure
|
819
|
+
def mock_execute_side_effect(
|
820
|
+
*args: t.Any, **kwargs: t.Any
|
821
|
+
) -> subprocess.CompletedProcess[str]:
|
822
|
+
cmd = args[0]
|
823
|
+
actual_calls.append(cmd) # Track each command
|
824
|
+
|
825
|
+
if cmd == ["pdm", "build"]:
|
826
|
+
return MagicMock(returncode=0, stdout="build output")
|
827
|
+
elif "pdm" in cmd and "publish" in cmd:
|
828
|
+
# Simulate OIDC error
|
829
|
+
return MagicMock(
|
830
|
+
returncode=1,
|
831
|
+
stdout="",
|
832
|
+
stderr="Getting PyPI token via OIDC...\nThis platform is not supported for trusted publishing via OIDC\n[PdmUsageError]: Username and password are required",
|
833
|
+
)
|
834
|
+
return MagicMock(returncode=0, stdout="")
|
835
|
+
|
836
|
+
# Mock execute_command with our custom side_effect
|
837
|
+
with patch.object(
|
838
|
+
crackerjack,
|
839
|
+
"execute_command",
|
840
|
+
side_effect=mock_execute_side_effect,
|
841
|
+
):
|
842
|
+
# Mock console.print
|
843
|
+
with patch.object(crackerjack.console, "print"):
|
844
|
+
# Mock handle_error to prevent sys.exit
|
845
|
+
with patch(
|
846
|
+
"crackerjack.errors.handle_error"
|
847
|
+
) as mock_handle_error:
|
848
|
+
# Call the method we're testing
|
849
|
+
crackerjack._publish_project(options)
|
850
|
+
|
851
|
+
# Verify the error handler was called with the right error type
|
852
|
+
mock_handle_error.assert_called_once()
|
853
|
+
error = mock_handle_error.call_args[1]["error"]
|
854
|
+
assert (
|
855
|
+
error.error_code == ErrorCode.AUTHENTICATION_ERROR
|
856
|
+
)
|
857
|
+
assert "OIDC authentication" in error.message
|
858
|
+
|
859
|
+
# Check that the build command was called
|
860
|
+
assert ["pdm", "build"] in actual_calls
|
861
|
+
|
862
|
+
# There should be exactly one publish command called (no retry)
|
863
|
+
publish_cmds = [
|
864
|
+
cmd for cmd in actual_calls if "pdm" in cmd and "publish" in cmd
|
865
|
+
]
|
866
|
+
assert len(publish_cmds) == 1, (
|
867
|
+
"Expected only one publish command (no retry)"
|
868
|
+
)
|
648
869
|
|
649
870
|
def test_process_with_commit_input(
|
650
871
|
self,
|
Binary file
|
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
|
File without changes
|
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.11/3256171999636029978
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.4/10355199064880463147
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.6/15140459877605758699
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.9/17041001205004563469
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.11/18187162184424859798
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.12/16869036553936192448
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.12/1867267426380906393
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.12/4240757255861806333
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.12/4441409093023629623
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.2/4070660268492669020
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.3/9818742842212983150
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.4/9818742842212983150
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.6/3557596832929915217
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.7/10386934055395314831
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.7/3557596832929915217
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.2.0/10047773857155985907
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.2.2/18053836298936336950
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.3.0/12548816621480535786
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.3.3/11081883392474770722
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.3.5/16311176246009842383
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.0/11982804814124138945
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.0/12055761203849489982
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.7.3/16061516852537040135
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.8.4/16354268377385700367
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.9.10/12813592349865671909
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.9.3/13948373885254993391
RENAMED
File without changes
|
{crackerjack-0.20.2 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.9.9/12813592349865671909
RENAMED
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
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|