crackerjack 0.20.3__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.3 → 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.3 → crackerjack-0.20.7}/crackerjack/crackerjack.py +245 -56
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/pyproject.toml +3 -1
- {crackerjack-0.20.3 → crackerjack-0.20.7}/pyproject.toml +3 -1
- {crackerjack-0.20.3 → crackerjack-0.20.7}/tests/test_crackerjack.py +192 -74
- crackerjack-0.20.3/crackerjack/.ruff_cache/0.11.13/1867267426380906393 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/LICENSE +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/README.md +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.gitignore +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.libcst.codemod.yaml +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.pdm.toml +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.pre-commit-config.yaml +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.pytest_cache/.gitignore +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.pytest_cache/CACHEDIR.TAG +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.pytest_cache/README.md +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.pytest_cache/v/cache/nodeids +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.pytest_cache/v/cache/stepwise +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/.gitignore +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.11/18187162184424859798 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.12/16869036553936192448 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.12/1867267426380906393 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.12/4240757255861806333 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.12/4441409093023629623 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/CACHEDIR.TAG +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/__init__.py +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/__main__.py +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/errors.py +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/interactive.py +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/py313.py +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/tests/TESTING.md +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/tests/__init__.py +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/tests/conftest.py +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/tests/data/comments_sample.txt +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/tests/data/docstrings_sample.txt +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/tests/data/expected_comments_sample.txt +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/tests/data/init.py +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/tests/test_crackerjack_runner.py +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/tests/test_errors.py +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/tests/test_interactive.py +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/tests/test_interactive_run.py +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/tests/test_main.py +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/tests/test_py313_advanced.py +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/tests/test_py313_features.py +0 -0
- {crackerjack-0.20.3 → crackerjack-0.20.7}/tests/test_pytest_features.py +0 -0
- {crackerjack-0.20.3 → 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,51 +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
|
-
# First check if keyring is already installed in PDM
|
1026
|
-
check_keyring = self.execute_command(
|
1027
|
-
["pdm", "self", "list"], capture_output=True, text=True
|
1028
|
-
)
|
1029
|
-
keyring_installed = "keyring" in check_keyring.stdout
|
1030
|
-
|
1031
|
-
if not keyring_installed:
|
1032
|
-
# Only attempt to install keyring if it's not already installed
|
1033
|
-
self.console.print("Installing keyring for PDM...")
|
1034
|
-
authorize = self.execute_command(
|
1035
|
-
["pdm", "self", "add", "keyring"],
|
1036
|
-
capture_output=True,
|
1037
|
-
text=True,
|
1038
|
-
)
|
1039
|
-
if authorize.returncode > 0:
|
1040
|
-
error = PublishError(
|
1041
|
-
message="Authentication setup failed",
|
1042
|
-
error_code=ErrorCode.AUTHENTICATION_ERROR,
|
1043
|
-
details=f"Failed to add keyring support to PDM.\nCommand output:\n{authorize.stderr}",
|
1044
|
-
recovery="Please manually add your keyring credentials to PDM. Run `pdm self add keyring` and try again.",
|
1045
|
-
exit_code=1,
|
1046
|
-
)
|
1047
|
-
handle_error(
|
1048
|
-
error=error,
|
1049
|
-
console=self.console,
|
1050
|
-
verbose=options.verbose,
|
1051
|
-
ai_agent=options.ai_agent,
|
1052
|
-
)
|
1024
|
+
if platform.system() != "Darwin":
|
1025
|
+
return
|
1053
1026
|
|
1054
|
-
|
1055
|
-
|
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,
|
1056
1040
|
)
|
1057
|
-
|
1058
|
-
|
1059
|
-
if build.returncode > 0:
|
1041
|
+
if authorize.returncode > 0:
|
1060
1042
|
error = PublishError(
|
1061
|
-
message="
|
1062
|
-
error_code=ErrorCode.
|
1063
|
-
details=f"
|
1064
|
-
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.",
|
1065
1047
|
exit_code=1,
|
1066
1048
|
)
|
1067
1049
|
handle_error(
|
@@ -1071,26 +1053,233 @@ class Crackerjack:
|
|
1071
1053
|
ai_agent=options.ai_agent,
|
1072
1054
|
)
|
1073
1055
|
|
1074
|
-
|
1075
|
-
|
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,
|
1076
1076
|
)
|
1077
1077
|
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
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]"
|
1085
1094
|
)
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
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]"
|
1091
1111
|
)
|
1092
|
-
|
1093
|
-
|
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
|
+
)
|
1094
1283
|
|
1095
1284
|
def _commit_and_push(self, options: OptionsProtocol) -> None:
|
1096
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]
|
@@ -16,6 +16,7 @@ from crackerjack.crackerjack import (
|
|
16
16
|
OptionsProtocol,
|
17
17
|
ProjectManager,
|
18
18
|
)
|
19
|
+
from crackerjack.errors import ErrorCode
|
19
20
|
|
20
21
|
|
21
22
|
class BumpOption(str, Enum):
|
@@ -613,40 +614,57 @@ class TestCrackerjackProcess:
|
|
613
614
|
|
614
615
|
# Mock platform.system to return 'Darwin'
|
615
616
|
with patch("platform.system", return_value="Darwin"):
|
616
|
-
#
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
#
|
625
|
-
|
626
|
-
|
627
|
-
)
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
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)
|
644
655
|
|
645
656
|
# Check that all the expected commands were called
|
646
657
|
assert ["pdm", "self", "list"] in actual_calls
|
647
658
|
assert ["pdm", "self", "add", "keyring"] in actual_calls
|
648
659
|
assert ["pdm", "build"] in actual_calls
|
649
|
-
|
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"
|
650
668
|
|
651
669
|
def test_keyring_already_installed_darwin(self) -> None:
|
652
670
|
"""Test behavior when keyring is already installed on Darwin (macOS)."""
|
@@ -661,35 +679,54 @@ class TestCrackerjackProcess:
|
|
661
679
|
|
662
680
|
# Mock platform.system to return 'Darwin'
|
663
681
|
with patch("platform.system", return_value="Darwin"):
|
664
|
-
#
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
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)
|
687
717
|
|
688
718
|
# Check that keyring installation was skipped
|
689
719
|
assert ["pdm", "self", "list"] in actual_calls
|
690
720
|
assert ["pdm", "self", "add", "keyring"] not in actual_calls
|
691
721
|
assert ["pdm", "build"] in actual_calls
|
692
|
-
|
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"
|
693
730
|
|
694
731
|
def test_keyring_install_failure_darwin(self) -> None:
|
695
732
|
"""Test handling of keyring installation failure on Darwin (macOS)."""
|
@@ -709,24 +746,91 @@ class TestCrackerjackProcess:
|
|
709
746
|
with patch("crackerjack.errors.handle_error") as mock_handle_error:
|
710
747
|
# Mock platform.system to return 'Darwin'
|
711
748
|
with patch("platform.system", return_value="Darwin"):
|
712
|
-
#
|
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
|
713
819
|
def mock_execute_side_effect(
|
714
820
|
*args: t.Any, **kwargs: t.Any
|
715
821
|
) -> subprocess.CompletedProcess[str]:
|
716
822
|
cmd = args[0]
|
823
|
+
actual_calls.append(cmd) # Track each command
|
717
824
|
|
718
|
-
if cmd == ["pdm", "
|
719
|
-
|
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
|
720
829
|
return MagicMock(
|
721
|
-
returncode=
|
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",
|
722
833
|
)
|
723
|
-
elif cmd == ["pdm", "self", "add", "keyring"]:
|
724
|
-
# Return a failure for keyring installation
|
725
|
-
mock_result = MagicMock()
|
726
|
-
mock_result.returncode = 1
|
727
|
-
mock_result.stdout = ""
|
728
|
-
mock_result.stderr = "Installation failed"
|
729
|
-
return mock_result
|
730
834
|
return MagicMock(returncode=0, stdout="")
|
731
835
|
|
732
836
|
# Mock execute_command with our custom side_effect
|
@@ -735,19 +839,33 @@ class TestCrackerjackProcess:
|
|
735
839
|
"execute_command",
|
736
840
|
side_effect=mock_execute_side_effect,
|
737
841
|
):
|
738
|
-
# Mock console.print
|
842
|
+
# Mock console.print
|
739
843
|
with patch.object(crackerjack.console, "print"):
|
740
|
-
#
|
741
|
-
with
|
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
|
742
849
|
crackerjack._publish_project(options)
|
743
850
|
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
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
|
+
)
|
751
869
|
|
752
870
|
def test_process_with_commit_input(
|
753
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.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.11/3256171999636029978
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.4/10355199064880463147
RENAMED
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.6/15140459877605758699
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.1.9/17041001205004563469
RENAMED
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.11/18187162184424859798
RENAMED
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.12/16869036553936192448
RENAMED
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.12/1867267426380906393
RENAMED
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.12/4240757255861806333
RENAMED
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.12/4441409093023629623
RENAMED
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.2/4070660268492669020
RENAMED
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.3/9818742842212983150
RENAMED
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.4/9818742842212983150
RENAMED
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.6/3557596832929915217
RENAMED
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.7/10386934055395314831
RENAMED
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.11.7/3557596832929915217
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.2.0/10047773857155985907
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.2.2/18053836298936336950
RENAMED
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.3.0/12548816621480535786
RENAMED
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.3.3/11081883392474770722
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.20.3 → 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.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.6.0/11982804814124138945
RENAMED
File without changes
|
{crackerjack-0.20.3 → 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.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.7.3/16061516852537040135
RENAMED
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.8.4/16354268377385700367
RENAMED
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.9.10/12813592349865671909
RENAMED
File without changes
|
File without changes
|
{crackerjack-0.20.3 → crackerjack-0.20.7}/crackerjack/.ruff_cache/0.9.3/13948373885254993391
RENAMED
File without changes
|
{crackerjack-0.20.3 → 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
|