codeshift 0.3.3__py3-none-any.whl → 0.3.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. codeshift/cli/commands/apply.py +24 -2
  2. codeshift/cli/package_manager.py +102 -0
  3. codeshift/knowledge/generator.py +11 -1
  4. codeshift/knowledge_base/libraries/aiohttp.yaml +186 -0
  5. codeshift/knowledge_base/libraries/attrs.yaml +181 -0
  6. codeshift/knowledge_base/libraries/celery.yaml +244 -0
  7. codeshift/knowledge_base/libraries/click.yaml +195 -0
  8. codeshift/knowledge_base/libraries/django.yaml +355 -0
  9. codeshift/knowledge_base/libraries/flask.yaml +270 -0
  10. codeshift/knowledge_base/libraries/httpx.yaml +183 -0
  11. codeshift/knowledge_base/libraries/marshmallow.yaml +238 -0
  12. codeshift/knowledge_base/libraries/numpy.yaml +429 -0
  13. codeshift/knowledge_base/libraries/pytest.yaml +192 -0
  14. codeshift/knowledge_base/libraries/sqlalchemy.yaml +2 -1
  15. codeshift/migrator/engine.py +60 -0
  16. codeshift/migrator/transforms/__init__.py +2 -0
  17. codeshift/migrator/transforms/aiohttp_transformer.py +608 -0
  18. codeshift/migrator/transforms/attrs_transformer.py +570 -0
  19. codeshift/migrator/transforms/celery_transformer.py +546 -0
  20. codeshift/migrator/transforms/click_transformer.py +526 -0
  21. codeshift/migrator/transforms/django_transformer.py +852 -0
  22. codeshift/migrator/transforms/fastapi_transformer.py +12 -7
  23. codeshift/migrator/transforms/flask_transformer.py +505 -0
  24. codeshift/migrator/transforms/httpx_transformer.py +419 -0
  25. codeshift/migrator/transforms/marshmallow_transformer.py +515 -0
  26. codeshift/migrator/transforms/numpy_transformer.py +413 -0
  27. codeshift/migrator/transforms/pydantic_v1_to_v2.py +53 -8
  28. codeshift/migrator/transforms/pytest_transformer.py +351 -0
  29. codeshift/migrator/transforms/requests_transformer.py +74 -1
  30. codeshift/migrator/transforms/sqlalchemy_transformer.py +692 -39
  31. {codeshift-0.3.3.dist-info → codeshift-0.3.5.dist-info}/METADATA +46 -4
  32. {codeshift-0.3.3.dist-info → codeshift-0.3.5.dist-info}/RECORD +36 -15
  33. {codeshift-0.3.3.dist-info → codeshift-0.3.5.dist-info}/WHEEL +0 -0
  34. {codeshift-0.3.3.dist-info → codeshift-0.3.5.dist-info}/entry_points.txt +0 -0
  35. {codeshift-0.3.3.dist-info → codeshift-0.3.5.dist-info}/licenses/LICENSE +0 -0
  36. {codeshift-0.3.3.dist-info → codeshift-0.3.5.dist-info}/top_level.txt +0 -0
@@ -10,6 +10,7 @@ from rich.panel import Panel
10
10
  from rich.prompt import Confirm
11
11
 
12
12
  from codeshift.cli.commands.upgrade import load_state, save_state
13
+ from codeshift.cli.package_manager import get_install_commands, get_sync_command
13
14
  from codeshift.cli.quota import (
14
15
  QuotaError,
15
16
  check_quota,
@@ -226,11 +227,32 @@ def apply(
226
227
  state_file.unlink()
227
228
  console.print("\n[green]Migration complete![/]")
228
229
 
229
- # Next steps
230
+ # Next steps - generate dynamic dependency sync command
230
231
  console.print("\n[bold]Recommended next steps:[/]")
231
232
  console.print(" 1. Review the changes in your editor")
232
233
  console.print(" 2. Run your test suite: [cyan]pytest[/]")
233
- console.print(" 3. Update your dependencies: [cyan]pip install pydantic>=2.0[/]")
234
+
235
+ # Generate dependency update commands based on library/libraries and package manager
236
+ sync_command = get_sync_command(project_path)
237
+
238
+ # Check if this is a multi-library migration (upgrade-all)
239
+ migrations = state.get("migrations", [])
240
+ if library == "multiple" and migrations:
241
+ # Multi-library case: show sync command and list all libraries
242
+ console.print(f" 3. Sync your dependencies: [cyan]{sync_command}[/]")
243
+ console.print("\n [dim]Upgraded libraries:[/]")
244
+ for migration in migrations:
245
+ lib_name = migration.get("library", "unknown")
246
+ to_version = migration.get("to_version", "unknown")
247
+ console.print(f" • {lib_name} → {to_version}")
248
+ else:
249
+ # Single library case: show sync command with specific library info
250
+ console.print(f" 3. Sync your dependencies: [cyan]{sync_command}[/]")
251
+ if library != "unknown" and target_version != "unknown":
252
+ libraries = [{"name": library, "version": target_version}]
253
+ install_commands = get_install_commands(project_path, libraries)
254
+ if install_commands:
255
+ console.print(f"\n [dim]Or install directly:[/] {install_commands[0]}")
234
256
 
235
257
 
236
258
  @click.command()
@@ -0,0 +1,102 @@
1
+ """Package manager detection and sync command generation utilities."""
2
+
3
+ import shutil
4
+ from pathlib import Path
5
+ from typing import Literal
6
+
7
+ PackageManager = Literal["uv", "poetry", "pip"]
8
+
9
+
10
+ def detect_package_manager(project_path: Path) -> PackageManager:
11
+ """Detect the package manager used in a project.
12
+
13
+ Detection order:
14
+ 1. uv.lock file presence → uv
15
+ 2. poetry.lock file presence → poetry
16
+ 3. Default → pip
17
+
18
+ Args:
19
+ project_path: Path to the project root directory.
20
+
21
+ Returns:
22
+ The detected package manager name.
23
+ """
24
+ # Check for uv (uv.lock file)
25
+ if (project_path / "uv.lock").exists():
26
+ return "uv"
27
+
28
+ # Check for poetry (poetry.lock file)
29
+ if (project_path / "poetry.lock").exists():
30
+ return "poetry"
31
+
32
+ # Default to pip
33
+ return "pip"
34
+
35
+
36
+ def get_sync_command(project_path: Path) -> str:
37
+ """Get the appropriate dependency sync command for the project.
38
+
39
+ Args:
40
+ project_path: Path to the project root directory.
41
+
42
+ Returns:
43
+ The shell command to sync/install dependencies.
44
+ """
45
+ manager = detect_package_manager(project_path)
46
+
47
+ if manager == "uv":
48
+ return "uv sync"
49
+ elif manager == "poetry":
50
+ return "poetry install"
51
+ else:
52
+ # pip - check what kind of project it is
53
+ if (project_path / "pyproject.toml").exists():
54
+ return "pip install -e ."
55
+ elif (project_path / "requirements.txt").exists():
56
+ return "pip install -r requirements.txt"
57
+ else:
58
+ return "pip install -e ."
59
+
60
+
61
+ def is_package_manager_available(manager: PackageManager) -> bool:
62
+ """Check if a package manager is available in the system.
63
+
64
+ Args:
65
+ manager: The package manager to check.
66
+
67
+ Returns:
68
+ True if the package manager is available, False otherwise.
69
+ """
70
+ return shutil.which(manager) is not None
71
+
72
+
73
+ def get_install_commands(
74
+ project_path: Path,
75
+ libraries: list[dict[str, str]],
76
+ ) -> list[str]:
77
+ """Generate install commands for the upgraded libraries.
78
+
79
+ This is a fallback for when users want to install specific packages
80
+ rather than sync the entire project.
81
+
82
+ Args:
83
+ project_path: Path to the project root directory.
84
+ libraries: List of dicts with 'name' and 'version' keys.
85
+
86
+ Returns:
87
+ List of install commands for each library.
88
+ """
89
+ manager = detect_package_manager(project_path)
90
+
91
+ commands = []
92
+ for lib in libraries:
93
+ name = lib["name"]
94
+ version = lib["version"]
95
+ if manager == "uv":
96
+ commands.append(f"uv add {name}>={version}")
97
+ elif manager == "poetry":
98
+ commands.append(f"poetry add {name}>={version}")
99
+ else:
100
+ commands.append(f"pip install {name}>={version}")
101
+
102
+ return commands
@@ -162,7 +162,17 @@ class KnowledgeGenerator:
162
162
 
163
163
 
164
164
  # Tier 1 libraries with deterministic AST transforms
165
- TIER_1_LIBRARIES = {"pydantic", "fastapi", "sqlalchemy", "pandas", "requests"}
165
+ TIER_1_LIBRARIES = {
166
+ "pydantic",
167
+ "fastapi",
168
+ "sqlalchemy",
169
+ "pandas",
170
+ "requests",
171
+ "numpy",
172
+ "pytest",
173
+ "marshmallow",
174
+ "flask",
175
+ }
166
176
 
167
177
 
168
178
  def is_tier_1_library(library: str) -> bool:
@@ -0,0 +1,186 @@
1
+ # aiohttp Knowledge Base
2
+ # Breaking changes from v3.7 to v3.9+
3
+
4
+ name: aiohttp
5
+ display_name: aiohttp
6
+ description: Async HTTP client/server framework for asyncio
7
+ migration_guide_url: https://docs.aiohttp.org/en/stable/changes.html
8
+
9
+ supported_migrations:
10
+ - from: "3.7"
11
+ to: "3.9"
12
+
13
+ breaking_changes:
14
+ # Loop parameter removal - ClientSession
15
+ - symbol: "ClientSession(loop=...)"
16
+ change_type: removed
17
+ severity: high
18
+ from_version: "3.7"
19
+ to_version: "3.8"
20
+ description: "loop parameter removed from ClientSession constructor"
21
+ replacement: "ClientSession()"
22
+ has_deterministic_transform: true
23
+ transform_name: remove_loop_param_client_session
24
+ notes: "Event loop is now obtained automatically via asyncio.get_event_loop()"
25
+
26
+ # Loop parameter removal - TCPConnector
27
+ - symbol: "TCPConnector(loop=...)"
28
+ change_type: removed
29
+ severity: high
30
+ from_version: "3.7"
31
+ to_version: "3.8"
32
+ description: "loop parameter removed from TCPConnector constructor"
33
+ replacement: "TCPConnector()"
34
+ has_deterministic_transform: true
35
+ transform_name: remove_loop_param_tcp_connector
36
+
37
+ # Loop parameter removal - UnixConnector
38
+ - symbol: "UnixConnector(loop=...)"
39
+ change_type: removed
40
+ severity: medium
41
+ from_version: "3.7"
42
+ to_version: "3.8"
43
+ description: "loop parameter removed from UnixConnector constructor"
44
+ replacement: "UnixConnector()"
45
+ has_deterministic_transform: true
46
+ transform_name: remove_loop_param_unix_connector
47
+
48
+ # Loop parameter removal - web.Application
49
+ - symbol: "web.Application(loop=...)"
50
+ change_type: removed
51
+ severity: high
52
+ from_version: "3.7"
53
+ to_version: "3.8"
54
+ description: "loop parameter removed from web.Application constructor"
55
+ replacement: "web.Application()"
56
+ has_deterministic_transform: true
57
+ transform_name: remove_loop_param_web_application
58
+
59
+ # Loop parameter removal - ClientTimeout
60
+ - symbol: "ClientTimeout(loop=...)"
61
+ change_type: removed
62
+ severity: medium
63
+ from_version: "3.7"
64
+ to_version: "3.8"
65
+ description: "loop parameter removed from ClientTimeout constructor"
66
+ replacement: "ClientTimeout()"
67
+ has_deterministic_transform: true
68
+ transform_name: remove_loop_param_client_timeout
69
+
70
+ # connector_owner default change
71
+ - symbol: "ClientSession(connector=..., connector_owner=None)"
72
+ change_type: behavior_change
73
+ severity: medium
74
+ from_version: "3.7"
75
+ to_version: "3.9"
76
+ description: "connector_owner now defaults to True; explicitly set to False if connector is shared"
77
+ replacement: "ClientSession(connector=..., connector_owner=False)"
78
+ has_deterministic_transform: false
79
+ notes: "Manual review needed - depends on whether connector is shared across sessions"
80
+
81
+ # BasicAuth.encode() removal
82
+ - symbol: "BasicAuth.encode()"
83
+ change_type: removed
84
+ severity: medium
85
+ from_version: "3.7"
86
+ to_version: "3.9"
87
+ description: "BasicAuth.encode() method removed"
88
+ replacement: "str(BasicAuth(...))"
89
+ has_deterministic_transform: true
90
+ transform_name: basicauth_encode_to_str
91
+
92
+ # Deprecated timeout parameters
93
+ - symbol: "ClientSession(read_timeout=...)"
94
+ change_type: removed
95
+ severity: medium
96
+ from_version: "3.7"
97
+ to_version: "3.8"
98
+ description: "read_timeout parameter deprecated, use ClientTimeout instead"
99
+ replacement: "ClientSession(timeout=ClientTimeout(total=...))"
100
+ has_deterministic_transform: true
101
+ transform_name: read_timeout_to_client_timeout
102
+
103
+ - symbol: "ClientSession(conn_timeout=...)"
104
+ change_type: removed
105
+ severity: medium
106
+ from_version: "3.7"
107
+ to_version: "3.8"
108
+ description: "conn_timeout parameter deprecated, use ClientTimeout instead"
109
+ replacement: "ClientSession(timeout=ClientTimeout(connect=...))"
110
+ has_deterministic_transform: true
111
+ transform_name: conn_timeout_to_client_timeout
112
+
113
+ # app.loop, request.loop, client.loop deprecation
114
+ - symbol: "app.loop"
115
+ change_type: deprecated
116
+ severity: medium
117
+ from_version: "3.7"
118
+ to_version: "3.8"
119
+ description: "app.loop property deprecated"
120
+ replacement: "asyncio.get_event_loop()"
121
+ has_deterministic_transform: true
122
+ transform_name: app_loop_to_get_event_loop
123
+
124
+ - symbol: "request.app.loop"
125
+ change_type: deprecated
126
+ severity: medium
127
+ from_version: "3.7"
128
+ to_version: "3.8"
129
+ description: "request.app.loop property deprecated"
130
+ replacement: "asyncio.get_event_loop()"
131
+ has_deterministic_transform: true
132
+ transform_name: request_loop_to_get_event_loop
133
+
134
+ # Middleware signature changes (old-style to new-style)
135
+ - symbol: "@middleware"
136
+ change_type: signature_change
137
+ severity: high
138
+ from_version: "3.7"
139
+ to_version: "3.9"
140
+ description: "Old-style middleware decorator deprecated; use @web.middleware with async def(request, handler)"
141
+ replacement: "@web.middleware async def middleware(request, handler)"
142
+ has_deterministic_transform: false
143
+ notes: "Middleware function signature changed; manual review recommended"
144
+
145
+ # ws_connect timeout deprecation
146
+ - symbol: "ws_connect(timeout=...)"
147
+ change_type: deprecated
148
+ severity: low
149
+ from_version: "3.7"
150
+ to_version: "3.9"
151
+ description: "timeout parameter in ws_connect deprecated"
152
+ replacement: "ws_connect(receive_timeout=...)"
153
+ has_deterministic_transform: true
154
+ transform_name: ws_connect_timeout_rename
155
+
156
+ - symbol: "ws_connect(receive_timeout=...)"
157
+ change_type: behavior_change
158
+ severity: low
159
+ from_version: "3.7"
160
+ to_version: "3.9"
161
+ description: "Default websocket receive_timeout changed from None to 10.0 seconds"
162
+ replacement: "ws_connect(receive_timeout=None)"
163
+ has_deterministic_transform: false
164
+ notes: "Set receive_timeout=None explicitly if you want no timeout"
165
+
166
+ # Response URL attribute change
167
+ - symbol: "response.url_obj"
168
+ change_type: renamed
169
+ severity: low
170
+ from_version: "3.7"
171
+ to_version: "3.9"
172
+ description: "url_obj attribute renamed to url (now returns yarl.URL)"
173
+ replacement: "response.url"
174
+ has_deterministic_transform: true
175
+ transform_name: url_obj_to_url
176
+
177
+ # WebSocket protocol attribute rename
178
+ - symbol: "ws_response.protocol"
179
+ change_type: renamed
180
+ severity: low
181
+ from_version: "3.7"
182
+ to_version: "3.9"
183
+ description: "WebSocketResponse.protocol renamed to ws_protocol"
184
+ replacement: "ws_response.ws_protocol"
185
+ has_deterministic_transform: true
186
+ transform_name: ws_protocol_rename
@@ -0,0 +1,181 @@
1
+ # attrs Knowledge Base
2
+ # Breaking changes from 21.x to 23.x+
3
+
4
+ name: attrs
5
+ display_name: attrs
6
+ description: Python Classes Without Boilerplate - declarative classes with validation
7
+ migration_guide_url: https://www.attrs.org/en/stable/changelog.html
8
+
9
+ supported_migrations:
10
+ - from: "21.0"
11
+ to: "23.0"
12
+
13
+ breaking_changes:
14
+ # Decorator changes - attr.s to attrs.define
15
+ - symbol: "@attr.s"
16
+ change_type: renamed
17
+ severity: high
18
+ from_version: "21.0"
19
+ to_version: "23.0"
20
+ description: "@attr.s decorator replaced with @attrs.define (modern API)"
21
+ replacement: "@attrs.define"
22
+ has_deterministic_transform: true
23
+ transform_name: attr_s_to_attrs_define
24
+ notes: "The modern API has slots=True and auto_attribs=True as defaults"
25
+
26
+ - symbol: "@attr.attrs"
27
+ change_type: renamed
28
+ severity: high
29
+ from_version: "21.0"
30
+ to_version: "23.0"
31
+ description: "@attr.attrs decorator replaced with @attrs.define"
32
+ replacement: "@attrs.define"
33
+ has_deterministic_transform: true
34
+ transform_name: attr_attrs_to_attrs_define
35
+
36
+ - symbol: "@attr.s(auto_attribs=True)"
37
+ change_type: renamed
38
+ severity: high
39
+ from_version: "21.0"
40
+ to_version: "23.0"
41
+ description: "@attr.s with auto_attribs=True becomes @attrs.define (auto_attribs is default)"
42
+ replacement: "@attrs.define"
43
+ has_deterministic_transform: true
44
+ transform_name: attr_s_auto_attribs_to_define
45
+ notes: "auto_attribs=True is the default in @attrs.define"
46
+
47
+ - symbol: "@attr.s(slots=True)"
48
+ change_type: renamed
49
+ severity: medium
50
+ from_version: "21.0"
51
+ to_version: "23.0"
52
+ description: "@attr.s with slots=True becomes @attrs.define (slots is default)"
53
+ replacement: "@attrs.define"
54
+ has_deterministic_transform: true
55
+ transform_name: attr_s_slots_to_define
56
+ notes: "slots=True is the default in @attrs.define"
57
+
58
+ - symbol: "@attr.s(frozen=True)"
59
+ change_type: renamed
60
+ severity: medium
61
+ from_version: "21.0"
62
+ to_version: "23.0"
63
+ description: "@attr.s with frozen=True becomes @attrs.frozen"
64
+ replacement: "@attrs.frozen"
65
+ has_deterministic_transform: true
66
+ transform_name: attr_s_frozen_to_attrs_frozen
67
+
68
+ # Field/attribute definition changes
69
+ - symbol: "attr.ib()"
70
+ change_type: renamed
71
+ severity: high
72
+ from_version: "21.0"
73
+ to_version: "23.0"
74
+ description: "attr.ib() replaced with attrs.field()"
75
+ replacement: "attrs.field()"
76
+ has_deterministic_transform: true
77
+ transform_name: attr_ib_to_attrs_field
78
+
79
+ - symbol: "attr.attrib()"
80
+ change_type: renamed
81
+ severity: high
82
+ from_version: "21.0"
83
+ to_version: "23.0"
84
+ description: "attr.attrib() replaced with attrs.field()"
85
+ replacement: "attrs.field()"
86
+ has_deterministic_transform: true
87
+ transform_name: attr_attrib_to_attrs_field
88
+
89
+ - symbol: "attr.Factory"
90
+ change_type: renamed
91
+ severity: medium
92
+ from_version: "21.0"
93
+ to_version: "23.0"
94
+ description: "attr.Factory replaced with attrs.Factory"
95
+ replacement: "attrs.Factory"
96
+ has_deterministic_transform: true
97
+ transform_name: attr_factory_to_attrs_factory
98
+
99
+ # Function changes
100
+ - symbol: "attr.asdict()"
101
+ change_type: renamed
102
+ severity: medium
103
+ from_version: "21.0"
104
+ to_version: "23.0"
105
+ description: "attr.asdict() replaced with attrs.asdict()"
106
+ replacement: "attrs.asdict()"
107
+ has_deterministic_transform: true
108
+ transform_name: attr_asdict_to_attrs_asdict
109
+
110
+ - symbol: "attr.astuple()"
111
+ change_type: renamed
112
+ severity: medium
113
+ from_version: "21.0"
114
+ to_version: "23.0"
115
+ description: "attr.astuple() replaced with attrs.astuple()"
116
+ replacement: "attrs.astuple()"
117
+ has_deterministic_transform: true
118
+ transform_name: attr_astuple_to_attrs_astuple
119
+
120
+ - symbol: "attr.fields()"
121
+ change_type: renamed
122
+ severity: low
123
+ from_version: "21.0"
124
+ to_version: "23.0"
125
+ description: "attr.fields() replaced with attrs.fields()"
126
+ replacement: "attrs.fields()"
127
+ has_deterministic_transform: true
128
+ transform_name: attr_fields_to_attrs_fields
129
+
130
+ - symbol: "attr.has()"
131
+ change_type: renamed
132
+ severity: low
133
+ from_version: "21.0"
134
+ to_version: "23.0"
135
+ description: "attr.has() replaced with attrs.has()"
136
+ replacement: "attrs.has()"
137
+ has_deterministic_transform: true
138
+ transform_name: attr_has_to_attrs_has
139
+
140
+ - symbol: "attr.evolve()"
141
+ change_type: renamed
142
+ severity: medium
143
+ from_version: "21.0"
144
+ to_version: "23.0"
145
+ description: "attr.evolve() replaced with attrs.evolve()"
146
+ replacement: "attrs.evolve()"
147
+ has_deterministic_transform: true
148
+ transform_name: attr_evolve_to_attrs_evolve
149
+
150
+ # Parameter deprecations
151
+ - symbol: "cmp"
152
+ change_type: removed
153
+ severity: high
154
+ from_version: "21.0"
155
+ to_version: "22.1"
156
+ description: "cmp parameter removed from @attr.s and attr.ib(), use eq and order instead"
157
+ replacement: "eq=..., order=..."
158
+ has_deterministic_transform: true
159
+ transform_name: cmp_to_eq_order
160
+ notes: "cmp=True becomes eq=True, order=True; cmp=False becomes eq=False, order=False"
161
+
162
+ # Import path changes for validators and converters
163
+ - symbol: "attr.validators"
164
+ change_type: renamed
165
+ severity: medium
166
+ from_version: "21.0"
167
+ to_version: "23.0"
168
+ description: "attr.validators module replaced with attrs.validators"
169
+ replacement: "attrs.validators"
170
+ has_deterministic_transform: true
171
+ transform_name: attr_validators_to_attrs_validators
172
+
173
+ - symbol: "attr.converters"
174
+ change_type: renamed
175
+ severity: medium
176
+ from_version: "21.0"
177
+ to_version: "23.0"
178
+ description: "attr.converters module replaced with attrs.converters"
179
+ replacement: "attrs.converters"
180
+ has_deterministic_transform: true
181
+ transform_name: attr_converters_to_attrs_converters