codeshift 0.3.7__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {codeshift-0.3.7 → codeshift-0.5.0}/LICENSE +1 -1
- {codeshift-0.3.7 → codeshift-0.5.0}/PKG-INFO +4 -16
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/__init__.py +2 -2
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/cli/__init__.py +1 -1
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/cli/commands/__init__.py +1 -1
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/cli/commands/auth.py +46 -30
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/cli/commands/scan.py +2 -5
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/cli/commands/upgrade.py +69 -61
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/cli/commands/upgrade_all.py +1 -1
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/cli/main.py +2 -2
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge/generator.py +6 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/libraries/aiohttp.yaml +3 -3
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/libraries/httpx.yaml +4 -4
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/libraries/pytest.yaml +1 -1
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/models.py +1 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/llm_migrator.py +8 -12
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/transforms/marshmallow_transformer.py +50 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/transforms/pydantic_v1_to_v2.py +191 -22
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/scanner/code_scanner.py +22 -2
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/utils/__init__.py +1 -1
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/utils/api_client.py +155 -15
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/utils/cache.py +1 -1
- codeshift-0.5.0/codeshift/utils/credential_store.py +393 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/utils/llm_client.py +111 -9
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift.egg-info/PKG-INFO +4 -16
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift.egg-info/SOURCES.txt +2 -16
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift.egg-info/requires.txt +2 -16
- {codeshift-0.3.7 → codeshift-0.5.0}/pyproject.toml +16 -9
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_marshmallow_transforms.py +79 -0
- codeshift-0.5.0/tests/test_pydantic_type_inference.py +235 -0
- codeshift-0.3.7/codeshift/api/__init__.py +0 -1
- codeshift-0.3.7/codeshift/api/auth.py +0 -182
- codeshift-0.3.7/codeshift/api/config.py +0 -73
- codeshift-0.3.7/codeshift/api/database.py +0 -215
- codeshift-0.3.7/codeshift/api/main.py +0 -103
- codeshift-0.3.7/codeshift/api/models/__init__.py +0 -55
- codeshift-0.3.7/codeshift/api/models/auth.py +0 -108
- codeshift-0.3.7/codeshift/api/models/billing.py +0 -92
- codeshift-0.3.7/codeshift/api/models/migrate.py +0 -42
- codeshift-0.3.7/codeshift/api/models/usage.py +0 -116
- codeshift-0.3.7/codeshift/api/routers/__init__.py +0 -5
- codeshift-0.3.7/codeshift/api/routers/auth.py +0 -440
- codeshift-0.3.7/codeshift/api/routers/billing.py +0 -395
- codeshift-0.3.7/codeshift/api/routers/migrate.py +0 -304
- codeshift-0.3.7/codeshift/api/routers/usage.py +0 -291
- codeshift-0.3.7/codeshift/api/routers/webhooks.py +0 -289
- {codeshift-0.3.7 → codeshift-0.5.0}/README.md +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/analyzer/__init__.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/analyzer/risk_assessor.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/cli/commands/apply.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/cli/commands/diff.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/cli/package_manager.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/cli/quota.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge/__init__.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge/cache.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge/models.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge/parser.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge/sources.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/__init__.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/libraries/attrs.yaml +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/libraries/celery.yaml +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/libraries/click.yaml +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/libraries/django.yaml +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/libraries/fastapi.yaml +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/libraries/flask.yaml +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/libraries/marshmallow.yaml +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/libraries/numpy.yaml +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/libraries/pandas.yaml +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/libraries/pydantic.yaml +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/libraries/requests.yaml +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/libraries/sqlalchemy.yaml +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/knowledge_base/loader.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/__init__.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/ast_transforms.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/engine.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/transforms/__init__.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/transforms/aiohttp_transformer.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/transforms/attrs_transformer.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/transforms/celery_transformer.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/transforms/click_transformer.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/transforms/django_transformer.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/transforms/fastapi_transformer.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/transforms/flask_transformer.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/transforms/httpx_transformer.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/transforms/numpy_transformer.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/transforms/pandas_transformer.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/transforms/pytest_transformer.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/transforms/requests_transformer.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/migrator/transforms/sqlalchemy_transformer.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/scanner/__init__.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/scanner/dependency_parser.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/utils/config.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/validator/__init__.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/validator/syntax_checker.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift/validator/test_runner.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift.egg-info/dependency_links.txt +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift.egg-info/entry_points.txt +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/codeshift.egg-info/top_level.txt +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/setup.cfg +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_aiohttp_transforms.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_attrs_transforms.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_celery_transforms.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_click_transforms.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_code_scanner.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_django_transforms.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_fastapi_transforms.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_flask_transforms.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_httpx_transforms.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_knowledge_base.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_numpy_transforms.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_pandas_transforms.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_pydantic_transforms.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_pytest_transforms.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_requests_transforms.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_risk_assessor.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_sqlalchemy_transforms.py +0 -0
- {codeshift-0.3.7 → codeshift-0.5.0}/tests/test_syntax_checker.py +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeshift
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: AI-powered CLI tool that migrates Python code to handle breaking dependency changes
|
|
5
|
-
Author:
|
|
5
|
+
Author: Ragab Technologies
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/Ragab-Technologies/Codeshift
|
|
8
8
|
Project-URL: Repository, https://github.com/Ragab-Technologies/Codeshift
|
|
@@ -27,19 +27,9 @@ Requires-Dist: rich>=13.0
|
|
|
27
27
|
Requires-Dist: toml>=0.10
|
|
28
28
|
Requires-Dist: packaging>=23.0
|
|
29
29
|
Requires-Dist: httpx>=0.25
|
|
30
|
-
Requires-Dist: pytest>=8.4.2
|
|
31
|
-
Requires-Dist: nox>=2025.11.12
|
|
32
30
|
Requires-Dist: black>=24.10.0
|
|
33
|
-
Requires-Dist:
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist: pre-commit>=4.5.1
|
|
36
|
-
Provides-Extra: api
|
|
37
|
-
Requires-Dist: fastapi>=0.109.0; extra == "api"
|
|
38
|
-
Requires-Dist: uvicorn>=0.27.0; extra == "api"
|
|
39
|
-
Requires-Dist: supabase>=2.3.0; extra == "api"
|
|
40
|
-
Requires-Dist: stripe>=7.0.0; extra == "api"
|
|
41
|
-
Requires-Dist: pydantic-settings>=2.1.0; extra == "api"
|
|
42
|
-
Requires-Dist: email-validator>=2.0.0; extra == "api"
|
|
31
|
+
Requires-Dist: cryptography>=41.0
|
|
32
|
+
Requires-Dist: nox>=2025.11.12
|
|
43
33
|
Provides-Extra: dev
|
|
44
34
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
45
35
|
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
@@ -50,8 +40,6 @@ Requires-Dist: nox>=2024.0; extra == "dev"
|
|
|
50
40
|
Requires-Dist: pre-commit>=3.6.0; extra == "dev"
|
|
51
41
|
Requires-Dist: types-toml>=0.10; extra == "dev"
|
|
52
42
|
Requires-Dist: types-PyYAML>=6.0; extra == "dev"
|
|
53
|
-
Provides-Extra: all
|
|
54
|
-
Requires-Dist: codeshift[api,dev]; extra == "all"
|
|
55
43
|
Dynamic: license-file
|
|
56
44
|
|
|
57
45
|
# Codeshift
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Codeshift - AI-powered CLI tool for migrating Python code to handle breaking dependency changes.
|
|
3
3
|
|
|
4
4
|
Don't just flag the update. Fix the break.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
__version__ = "0.2.0"
|
|
8
|
-
__author__ = "
|
|
8
|
+
__author__ = "Codeshift Team"
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"""Authentication commands for Codeshift CLI."""
|
|
2
2
|
|
|
3
|
-
import json
|
|
4
3
|
import os
|
|
5
4
|
import time
|
|
6
5
|
import webbrowser
|
|
7
6
|
from pathlib import Path
|
|
8
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
9
8
|
|
|
10
9
|
import click
|
|
11
10
|
import httpx
|
|
@@ -15,11 +14,16 @@ from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
|
15
14
|
from rich.prompt import Confirm, Prompt
|
|
16
15
|
from rich.table import Table
|
|
17
16
|
|
|
17
|
+
from codeshift.utils.credential_store import (
|
|
18
|
+
CredentialDecryptionError,
|
|
19
|
+
get_credential_store,
|
|
20
|
+
)
|
|
21
|
+
|
|
18
22
|
console = Console()
|
|
19
23
|
|
|
20
|
-
# Config directory for storing credentials
|
|
24
|
+
# Config directory for storing credentials (kept for backward compatibility reference)
|
|
21
25
|
CONFIG_DIR = Path.home() / ".config" / "codeshift"
|
|
22
|
-
CREDENTIALS_FILE = CONFIG_DIR / "credentials.json"
|
|
26
|
+
CREDENTIALS_FILE = CONFIG_DIR / "credentials.json" # Legacy path
|
|
23
27
|
|
|
24
28
|
|
|
25
29
|
def get_api_url() -> str:
|
|
@@ -28,34 +32,44 @@ def get_api_url() -> str:
|
|
|
28
32
|
|
|
29
33
|
|
|
30
34
|
def load_credentials() -> dict[str, Any] | None:
|
|
31
|
-
"""Load saved credentials from
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
"""Load saved credentials from secure storage.
|
|
36
|
+
|
|
37
|
+
Automatically handles migration from plaintext to encrypted storage.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Dictionary of credentials, or None if not found.
|
|
41
|
+
"""
|
|
42
|
+
store = get_credential_store()
|
|
34
43
|
try:
|
|
35
|
-
return
|
|
36
|
-
except
|
|
44
|
+
return store.load()
|
|
45
|
+
except CredentialDecryptionError as e:
|
|
46
|
+
console.print(
|
|
47
|
+
Panel(
|
|
48
|
+
f"[red]Could not decrypt credentials:[/] {e}\n\n"
|
|
49
|
+
"This may happen if credentials were created on a different machine.\n"
|
|
50
|
+
"Please run [cyan]codeshift login[/] to re-authenticate.",
|
|
51
|
+
title="Credential Error",
|
|
52
|
+
)
|
|
53
|
+
)
|
|
37
54
|
return None
|
|
38
55
|
|
|
39
56
|
|
|
40
57
|
def save_credentials(credentials: dict) -> None:
|
|
41
|
-
"""Save credentials to
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
# Set restrictive permissions
|
|
45
|
-
CREDENTIALS_FILE.write_text(json.dumps(credentials, indent=2))
|
|
46
|
-
os.chmod(CREDENTIALS_FILE, 0o600)
|
|
58
|
+
"""Save credentials to secure encrypted storage."""
|
|
59
|
+
store = get_credential_store()
|
|
60
|
+
store.save(credentials)
|
|
47
61
|
|
|
48
62
|
|
|
49
63
|
def delete_credentials() -> None:
|
|
50
|
-
"""Delete saved credentials."""
|
|
51
|
-
|
|
52
|
-
|
|
64
|
+
"""Delete saved credentials securely."""
|
|
65
|
+
store = get_credential_store()
|
|
66
|
+
store.delete()
|
|
53
67
|
|
|
54
68
|
|
|
55
69
|
def get_api_key() -> str | None:
|
|
56
70
|
"""Get API key from environment or saved credentials."""
|
|
57
71
|
# Check environment first
|
|
58
|
-
api_key = os.environ.get("
|
|
72
|
+
api_key = os.environ.get("CODESHIFT_API_KEY")
|
|
59
73
|
if api_key:
|
|
60
74
|
return api_key
|
|
61
75
|
|
|
@@ -99,7 +113,7 @@ def login(
|
|
|
99
113
|
api_key: str | None,
|
|
100
114
|
device: bool,
|
|
101
115
|
) -> None:
|
|
102
|
-
"""Login to
|
|
116
|
+
"""Login to Codeshift to enable cloud features.
|
|
103
117
|
|
|
104
118
|
\b
|
|
105
119
|
Authentication methods:
|
|
@@ -107,7 +121,8 @@ def login(
|
|
|
107
121
|
2. API key: codeshift login -k pyr_xxxxx
|
|
108
122
|
3. Device flow: codeshift login --device
|
|
109
123
|
|
|
110
|
-
Your credentials are stored in ~/.config/codeshift/credentials.
|
|
124
|
+
Your credentials are stored securely in ~/.config/codeshift/credentials.enc
|
|
125
|
+
using AES encryption.
|
|
111
126
|
|
|
112
127
|
Don't have an account? Run: codeshift register
|
|
113
128
|
"""
|
|
@@ -148,13 +163,14 @@ def register(
|
|
|
148
163
|
password: str | None,
|
|
149
164
|
name: str | None,
|
|
150
165
|
) -> None:
|
|
151
|
-
"""Create a new
|
|
166
|
+
"""Create a new Codeshift account.
|
|
152
167
|
|
|
153
168
|
\b
|
|
154
169
|
Example:
|
|
155
170
|
codeshift register -e user@example.com -p yourpassword
|
|
156
171
|
|
|
157
|
-
Your credentials are stored in ~/.config/codeshift/credentials.
|
|
172
|
+
Your credentials are stored securely in ~/.config/codeshift/credentials.enc
|
|
173
|
+
using AES encryption.
|
|
158
174
|
"""
|
|
159
175
|
# Check if already logged in
|
|
160
176
|
existing = load_credentials()
|
|
@@ -211,7 +227,7 @@ def _register_account(email: str, password: str, full_name: str | None) -> None:
|
|
|
211
227
|
if response.status_code == 200:
|
|
212
228
|
data = response.json()
|
|
213
229
|
|
|
214
|
-
# Save credentials
|
|
230
|
+
# Save credentials securely
|
|
215
231
|
save_credentials(
|
|
216
232
|
{
|
|
217
233
|
"api_key": data["api_key"],
|
|
@@ -228,7 +244,7 @@ def _register_account(email: str, password: str, full_name: str | None) -> None:
|
|
|
228
244
|
f"[green]Account created successfully![/]\n\n"
|
|
229
245
|
f"Email: [cyan]{data['user']['email']}[/]\n"
|
|
230
246
|
f"Tier: [cyan]{data['user'].get('tier', 'free')}[/]\n\n"
|
|
231
|
-
f"[dim]You are now logged in and ready to use
|
|
247
|
+
f"[dim]You are now logged in and ready to use Codeshift.[/]",
|
|
232
248
|
title="Registration Successful",
|
|
233
249
|
)
|
|
234
250
|
)
|
|
@@ -275,7 +291,7 @@ def _login_with_api_key(api_key: str) -> None:
|
|
|
275
291
|
if response.status_code == 200:
|
|
276
292
|
user = response.json()
|
|
277
293
|
|
|
278
|
-
# Save credentials
|
|
294
|
+
# Save credentials securely
|
|
279
295
|
save_credentials(
|
|
280
296
|
{
|
|
281
297
|
"api_key": api_key,
|
|
@@ -327,7 +343,7 @@ def _login_with_password(email: str, password: str) -> None:
|
|
|
327
343
|
if response.status_code == 200:
|
|
328
344
|
data = response.json()
|
|
329
345
|
|
|
330
|
-
# Save credentials
|
|
346
|
+
# Save credentials securely
|
|
331
347
|
save_credentials(
|
|
332
348
|
{
|
|
333
349
|
"api_key": data["api_key"],
|
|
@@ -422,7 +438,7 @@ def _login_with_device_code() -> None:
|
|
|
422
438
|
if response.status_code == 200:
|
|
423
439
|
data = response.json()
|
|
424
440
|
|
|
425
|
-
# Save credentials
|
|
441
|
+
# Save credentials securely
|
|
426
442
|
save_credentials(
|
|
427
443
|
{
|
|
428
444
|
"api_key": data["api_key"],
|
|
@@ -466,7 +482,7 @@ def _login_with_device_code() -> None:
|
|
|
466
482
|
|
|
467
483
|
@click.command()
|
|
468
484
|
def logout() -> None:
|
|
469
|
-
"""Logout from
|
|
485
|
+
"""Logout from Codeshift and remove saved credentials."""
|
|
470
486
|
creds = load_credentials()
|
|
471
487
|
|
|
472
488
|
if not creds:
|
|
@@ -487,7 +503,7 @@ def logout() -> None:
|
|
|
487
503
|
except httpx.RequestError:
|
|
488
504
|
pass
|
|
489
505
|
|
|
490
|
-
# Delete local credentials
|
|
506
|
+
# Delete local credentials securely
|
|
491
507
|
delete_credentials()
|
|
492
508
|
|
|
493
509
|
console.print("[green]Successfully logged out[/]")
|
|
@@ -11,10 +11,7 @@ from rich.panel import Panel
|
|
|
11
11
|
from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn
|
|
12
12
|
from rich.table import Table
|
|
13
13
|
|
|
14
|
-
from codeshift.knowledge import
|
|
15
|
-
generate_knowledge_base_sync,
|
|
16
|
-
is_tier_1_library,
|
|
17
|
-
)
|
|
14
|
+
from codeshift.knowledge import generate_knowledge_base_sync, is_tier_1_library
|
|
18
15
|
from codeshift.scanner import DependencyParser
|
|
19
16
|
from codeshift.utils.config import ProjectConfig
|
|
20
17
|
|
|
@@ -148,7 +145,7 @@ def scan(
|
|
|
148
145
|
console.print(
|
|
149
146
|
Panel(
|
|
150
147
|
"[bold]Scanning project for possible migrations[/]\n\n" f"Path: {project_path}",
|
|
151
|
-
title="
|
|
148
|
+
title="Codeshift Scan",
|
|
152
149
|
)
|
|
153
150
|
)
|
|
154
151
|
|
|
@@ -10,12 +10,7 @@ from rich.panel import Panel
|
|
|
10
10
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
11
11
|
from rich.table import Table
|
|
12
12
|
|
|
13
|
-
from codeshift.cli.quota import
|
|
14
|
-
QuotaError,
|
|
15
|
-
check_quota,
|
|
16
|
-
record_usage,
|
|
17
|
-
show_quota_exceeded_message,
|
|
18
|
-
)
|
|
13
|
+
from codeshift.cli.quota import QuotaError, check_quota, record_usage, show_quota_exceeded_message
|
|
19
14
|
from codeshift.knowledge import (
|
|
20
15
|
Confidence,
|
|
21
16
|
GeneratedKnowledgeBase,
|
|
@@ -23,12 +18,8 @@ from codeshift.knowledge import (
|
|
|
23
18
|
is_tier_1_library,
|
|
24
19
|
)
|
|
25
20
|
from codeshift.knowledge_base import KnowledgeBaseLoader
|
|
26
|
-
from codeshift.
|
|
27
|
-
from codeshift.migrator.
|
|
28
|
-
from codeshift.migrator.transforms.pandas_transformer import transform_pandas
|
|
29
|
-
from codeshift.migrator.transforms.pydantic_v1_to_v2 import transform_pydantic_v1_to_v2
|
|
30
|
-
from codeshift.migrator.transforms.requests_transformer import transform_requests
|
|
31
|
-
from codeshift.migrator.transforms.sqlalchemy_transformer import transform_sqlalchemy
|
|
21
|
+
from codeshift.knowledge_base.models import LibraryKnowledge
|
|
22
|
+
from codeshift.migrator.ast_transforms import TransformResult, TransformStatus
|
|
32
23
|
from codeshift.scanner import CodeScanner, DependencyParser
|
|
33
24
|
from codeshift.utils.config import ProjectConfig
|
|
34
25
|
|
|
@@ -86,6 +77,11 @@ def save_state(project_path: Path, state: dict) -> None:
|
|
|
86
77
|
is_flag=True,
|
|
87
78
|
help="Show detailed output",
|
|
88
79
|
)
|
|
80
|
+
@click.option(
|
|
81
|
+
"--force-llm",
|
|
82
|
+
is_flag=True,
|
|
83
|
+
help="Force LLM migration even for libraries with AST transforms",
|
|
84
|
+
)
|
|
89
85
|
def upgrade(
|
|
90
86
|
library: str,
|
|
91
87
|
target: str,
|
|
@@ -93,6 +89,7 @@ def upgrade(
|
|
|
93
89
|
file: str | None,
|
|
94
90
|
dry_run: bool,
|
|
95
91
|
verbose: bool,
|
|
92
|
+
force_llm: bool,
|
|
96
93
|
) -> None:
|
|
97
94
|
"""Analyze your codebase and propose changes for a library upgrade.
|
|
98
95
|
|
|
@@ -105,34 +102,44 @@ def upgrade(
|
|
|
105
102
|
project_path = Path(path).resolve()
|
|
106
103
|
project_config = ProjectConfig.from_pyproject(project_path)
|
|
107
104
|
|
|
108
|
-
# Check quota before starting (allow offline for Tier 1 libraries)
|
|
105
|
+
# Check quota before starting (allow offline for Tier 1 libraries unless force-llm)
|
|
109
106
|
is_tier1 = is_tier_1_library(library)
|
|
110
107
|
try:
|
|
111
|
-
check_quota("file_migrated", quantity=1, allow_offline=is_tier1)
|
|
108
|
+
check_quota("file_migrated", quantity=1, allow_offline=is_tier1 and not force_llm)
|
|
112
109
|
except QuotaError as e:
|
|
113
110
|
show_quota_exceeded_message(e)
|
|
114
111
|
raise SystemExit(1) from e
|
|
115
112
|
|
|
116
|
-
# Load knowledge base
|
|
113
|
+
# Load knowledge base (optional - YAML may not exist for all libraries)
|
|
117
114
|
loader = KnowledgeBaseLoader()
|
|
115
|
+
knowledge: LibraryKnowledge | None = None
|
|
118
116
|
|
|
119
117
|
try:
|
|
120
118
|
knowledge = loader.load(library)
|
|
121
|
-
except FileNotFoundError
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
except FileNotFoundError:
|
|
120
|
+
if verbose:
|
|
121
|
+
console.print(
|
|
122
|
+
f"[dim]No knowledge base YAML for {library} - using generated knowledge[/]"
|
|
123
|
+
)
|
|
125
124
|
|
|
126
|
-
#
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
125
|
+
# Display migration info with fallback for missing YAML
|
|
126
|
+
if knowledge:
|
|
127
|
+
console.print(
|
|
128
|
+
Panel(
|
|
129
|
+
f"[bold]Upgrading {knowledge.display_name}[/] to version [cyan]{target}[/]\n\n"
|
|
130
|
+
f"{knowledge.description}\n"
|
|
131
|
+
f"Migration guide: {knowledge.migration_guide_url or 'N/A'}",
|
|
132
|
+
title="Codeshift Migration",
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
else:
|
|
136
|
+
console.print(
|
|
137
|
+
Panel(
|
|
138
|
+
f"[bold]Upgrading {library}[/] to version [cyan]{target}[/]\n\n"
|
|
139
|
+
"Using AI-powered migration (no static knowledge base available)",
|
|
140
|
+
title="Codeshift Migration",
|
|
141
|
+
)
|
|
134
142
|
)
|
|
135
|
-
)
|
|
136
143
|
|
|
137
144
|
# Step 1: Parse dependencies
|
|
138
145
|
with Progress(
|
|
@@ -276,7 +283,25 @@ def upgrade(
|
|
|
276
283
|
console.print(f"\n[yellow]No {library} imports found in the codebase.[/]")
|
|
277
284
|
return
|
|
278
285
|
|
|
279
|
-
# Step 4: Apply transforms
|
|
286
|
+
# Step 4: Apply transforms using MigrationEngine
|
|
287
|
+
# Import here to avoid circular dependency (upgrade.py -> migrator -> llm_migrator -> api_client -> auth -> cli -> upgrade.py)
|
|
288
|
+
from codeshift.migrator import get_migration_engine
|
|
289
|
+
|
|
290
|
+
engine = get_migration_engine()
|
|
291
|
+
|
|
292
|
+
# Check auth for non-Tier1 libraries or force-llm mode
|
|
293
|
+
llm_required = force_llm or not is_tier1
|
|
294
|
+
if llm_required and not engine.llm_migrator.is_available:
|
|
295
|
+
console.print(
|
|
296
|
+
Panel(
|
|
297
|
+
f"[yellow]LLM migration required for {library}[/]\n\n"
|
|
298
|
+
"Run [cyan]codeshift login[/] and upgrade to Pro tier for LLM features.",
|
|
299
|
+
title="Authentication Required",
|
|
300
|
+
)
|
|
301
|
+
)
|
|
302
|
+
if not is_tier1:
|
|
303
|
+
raise SystemExit(1)
|
|
304
|
+
|
|
280
305
|
with Progress(
|
|
281
306
|
SpinnerColumn(),
|
|
282
307
|
TextColumn("[progress.description]{task.description}"),
|
|
@@ -291,45 +316,28 @@ def upgrade(
|
|
|
291
316
|
|
|
292
317
|
results: list[TransformResult] = []
|
|
293
318
|
|
|
319
|
+
def migration_progress(msg: str) -> None:
|
|
320
|
+
progress.update(task, description=msg)
|
|
321
|
+
|
|
294
322
|
for file_path in files_to_transform:
|
|
295
323
|
try:
|
|
296
324
|
source_code = file_path.read_text()
|
|
297
325
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
"
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if transform_func:
|
|
308
|
-
transformed_code, changes = transform_func(source_code)
|
|
309
|
-
# Create TransformResult from the function output
|
|
310
|
-
result = TransformResult(
|
|
311
|
-
file_path=file_path,
|
|
312
|
-
status=TransformStatus.SUCCESS if changes else TransformStatus.NO_CHANGES,
|
|
313
|
-
original_code=source_code,
|
|
314
|
-
transformed_code=transformed_code,
|
|
315
|
-
changes=[
|
|
316
|
-
TransformChange(
|
|
317
|
-
description=c.description,
|
|
318
|
-
line_number=c.line_number,
|
|
319
|
-
original=c.original,
|
|
320
|
-
replacement=c.replacement,
|
|
321
|
-
transform_name=c.transform_name,
|
|
322
|
-
confidence=getattr(c, "confidence", 1.0),
|
|
323
|
-
)
|
|
324
|
-
for c in changes
|
|
325
|
-
],
|
|
326
|
-
)
|
|
327
|
-
else:
|
|
328
|
-
console.print(f"[yellow]Warning:[/] No transformer available for {library}")
|
|
329
|
-
continue
|
|
326
|
+
result = engine.run_migration(
|
|
327
|
+
code=source_code,
|
|
328
|
+
file_path=file_path,
|
|
329
|
+
library=library,
|
|
330
|
+
old_version=current_version or "1.0",
|
|
331
|
+
new_version=target,
|
|
332
|
+
knowledge_base=generated_kb,
|
|
333
|
+
progress_callback=migration_progress if verbose else None,
|
|
334
|
+
)
|
|
330
335
|
|
|
331
336
|
if result.has_changes:
|
|
332
337
|
results.append(result)
|
|
338
|
+
elif result.errors:
|
|
339
|
+
for error in result.errors:
|
|
340
|
+
console.print(f"[yellow]Warning ({file_path.name}):[/] {error}")
|
|
333
341
|
|
|
334
342
|
except Exception as e:
|
|
335
343
|
console.print(f"[red]Error processing {file_path}:[/] {e}")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Main CLI entry point for
|
|
1
|
+
"""Main CLI entry point for Codeshift."""
|
|
2
2
|
|
|
3
3
|
import click
|
|
4
4
|
from rich.console import Console
|
|
@@ -26,7 +26,7 @@ console = Console()
|
|
|
26
26
|
@click.version_option(version=__version__, prog_name="codeshift")
|
|
27
27
|
@click.pass_context
|
|
28
28
|
def cli(ctx: click.Context) -> None:
|
|
29
|
-
"""
|
|
29
|
+
"""Codeshift - AI-powered Python dependency migration tool.
|
|
30
30
|
|
|
31
31
|
Don't just flag the update. Fix the break.
|
|
32
32
|
|
|
@@ -69,7 +69,7 @@ breaking_changes:
|
|
|
69
69
|
|
|
70
70
|
# connector_owner default change
|
|
71
71
|
- symbol: "ClientSession(connector=..., connector_owner=None)"
|
|
72
|
-
change_type:
|
|
72
|
+
change_type: behavior_changed
|
|
73
73
|
severity: medium
|
|
74
74
|
from_version: "3.7"
|
|
75
75
|
to_version: "3.9"
|
|
@@ -133,7 +133,7 @@ breaking_changes:
|
|
|
133
133
|
|
|
134
134
|
# Middleware signature changes (old-style to new-style)
|
|
135
135
|
- symbol: "@middleware"
|
|
136
|
-
change_type:
|
|
136
|
+
change_type: signature_changed
|
|
137
137
|
severity: high
|
|
138
138
|
from_version: "3.7"
|
|
139
139
|
to_version: "3.9"
|
|
@@ -154,7 +154,7 @@ breaking_changes:
|
|
|
154
154
|
transform_name: ws_connect_timeout_rename
|
|
155
155
|
|
|
156
156
|
- symbol: "ws_connect(receive_timeout=...)"
|
|
157
|
-
change_type:
|
|
157
|
+
change_type: behavior_changed
|
|
158
158
|
severity: low
|
|
159
159
|
from_version: "3.7"
|
|
160
160
|
to_version: "3.9"
|
|
@@ -119,7 +119,7 @@ breaking_changes:
|
|
|
119
119
|
|
|
120
120
|
# Response.iter_lines() behavior change
|
|
121
121
|
- symbol: "Response.iter_lines()"
|
|
122
|
-
change_type:
|
|
122
|
+
change_type: behavior_changed
|
|
123
123
|
severity: medium
|
|
124
124
|
from_version: "0.23"
|
|
125
125
|
to_version: "0.24"
|
|
@@ -130,7 +130,7 @@ breaking_changes:
|
|
|
130
130
|
|
|
131
131
|
# NetRC authentication change
|
|
132
132
|
- symbol: "trust_env=True"
|
|
133
|
-
change_type:
|
|
133
|
+
change_type: behavior_changed
|
|
134
134
|
severity: medium
|
|
135
135
|
from_version: "0.23"
|
|
136
136
|
to_version: "0.24"
|
|
@@ -141,7 +141,7 @@ breaking_changes:
|
|
|
141
141
|
|
|
142
142
|
# Query parameter encoding change
|
|
143
143
|
- symbol: "params encoding"
|
|
144
|
-
change_type:
|
|
144
|
+
change_type: behavior_changed
|
|
145
145
|
severity: low
|
|
146
146
|
from_version: "0.23"
|
|
147
147
|
to_version: "0.24"
|
|
@@ -173,7 +173,7 @@ breaking_changes:
|
|
|
173
173
|
|
|
174
174
|
# Follow redirects default change
|
|
175
175
|
- symbol: "follow_redirects"
|
|
176
|
-
change_type:
|
|
176
|
+
change_type: behavior_changed
|
|
177
177
|
severity: high
|
|
178
178
|
from_version: "0.19"
|
|
179
179
|
to_version: "0.20"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""LLM-based migration for complex cases.
|
|
2
2
|
|
|
3
|
-
LLM migrations (Tier 2/3) are routed through the
|
|
3
|
+
LLM migrations (Tier 2/3) are routed through the Codeshift API,
|
|
4
4
|
which handles authentication, quota checking, and billing.
|
|
5
5
|
Users must have a Pro or Unlimited subscription to use LLM features.
|
|
6
6
|
|
|
@@ -11,12 +11,8 @@ import re
|
|
|
11
11
|
from dataclasses import dataclass
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
|
|
14
|
-
from codeshift.migrator.ast_transforms import
|
|
15
|
-
|
|
16
|
-
TransformResult,
|
|
17
|
-
TransformStatus,
|
|
18
|
-
)
|
|
19
|
-
from codeshift.utils.api_client import PyResolveAPIClient, get_api_client
|
|
14
|
+
from codeshift.migrator.ast_transforms import TransformChange, TransformResult, TransformStatus
|
|
15
|
+
from codeshift.utils.api_client import CodeshiftAPIClient, get_api_client
|
|
20
16
|
from codeshift.utils.cache import LLMCache, get_llm_cache
|
|
21
17
|
from codeshift.validator.syntax_checker import quick_syntax_check
|
|
22
18
|
|
|
@@ -35,10 +31,10 @@ class LLMMigrationResult:
|
|
|
35
31
|
|
|
36
32
|
|
|
37
33
|
class LLMMigrator:
|
|
38
|
-
"""Handles complex migrations using LLM via the
|
|
34
|
+
"""Handles complex migrations using LLM via the Codeshift API.
|
|
39
35
|
|
|
40
36
|
LLM migrations require authentication and a Pro or Unlimited subscription.
|
|
41
|
-
All LLM calls are routed through the
|
|
37
|
+
All LLM calls are routed through the Codeshift API, which handles:
|
|
42
38
|
- Authentication and authorization
|
|
43
39
|
- Quota checking and billing
|
|
44
40
|
- Server-side Anthropic API calls
|
|
@@ -49,7 +45,7 @@ class LLMMigrator:
|
|
|
49
45
|
|
|
50
46
|
def __init__(
|
|
51
47
|
self,
|
|
52
|
-
client:
|
|
48
|
+
client: CodeshiftAPIClient | None = None,
|
|
53
49
|
cache: LLMCache | None = None,
|
|
54
50
|
use_cache: bool = True,
|
|
55
51
|
validate_output: bool = True,
|
|
@@ -80,7 +76,7 @@ class LLMMigrator:
|
|
|
80
76
|
to_version: str,
|
|
81
77
|
context: str | None = None,
|
|
82
78
|
) -> LLMMigrationResult:
|
|
83
|
-
"""Migrate code using the LLM via
|
|
79
|
+
"""Migrate code using the LLM via Codeshift API.
|
|
84
80
|
|
|
85
81
|
Args:
|
|
86
82
|
code: Source code to migrate
|
|
@@ -114,7 +110,7 @@ class LLMMigrator:
|
|
|
114
110
|
used_cache=True,
|
|
115
111
|
)
|
|
116
112
|
|
|
117
|
-
# Call
|
|
113
|
+
# Call Codeshift API (which calls Anthropic server-side)
|
|
118
114
|
response = self.client.migrate_code(
|
|
119
115
|
code=code,
|
|
120
116
|
library=library,
|