codeshift 0.2.0__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 (65) hide show
  1. codeshift/__init__.py +8 -0
  2. codeshift/analyzer/__init__.py +5 -0
  3. codeshift/analyzer/risk_assessor.py +388 -0
  4. codeshift/api/__init__.py +1 -0
  5. codeshift/api/auth.py +182 -0
  6. codeshift/api/config.py +73 -0
  7. codeshift/api/database.py +215 -0
  8. codeshift/api/main.py +103 -0
  9. codeshift/api/models/__init__.py +55 -0
  10. codeshift/api/models/auth.py +108 -0
  11. codeshift/api/models/billing.py +92 -0
  12. codeshift/api/models/migrate.py +42 -0
  13. codeshift/api/models/usage.py +116 -0
  14. codeshift/api/routers/__init__.py +5 -0
  15. codeshift/api/routers/auth.py +440 -0
  16. codeshift/api/routers/billing.py +395 -0
  17. codeshift/api/routers/migrate.py +304 -0
  18. codeshift/api/routers/usage.py +291 -0
  19. codeshift/api/routers/webhooks.py +289 -0
  20. codeshift/cli/__init__.py +5 -0
  21. codeshift/cli/commands/__init__.py +7 -0
  22. codeshift/cli/commands/apply.py +352 -0
  23. codeshift/cli/commands/auth.py +842 -0
  24. codeshift/cli/commands/diff.py +221 -0
  25. codeshift/cli/commands/scan.py +368 -0
  26. codeshift/cli/commands/upgrade.py +436 -0
  27. codeshift/cli/commands/upgrade_all.py +518 -0
  28. codeshift/cli/main.py +221 -0
  29. codeshift/cli/quota.py +210 -0
  30. codeshift/knowledge/__init__.py +50 -0
  31. codeshift/knowledge/cache.py +167 -0
  32. codeshift/knowledge/generator.py +231 -0
  33. codeshift/knowledge/models.py +151 -0
  34. codeshift/knowledge/parser.py +270 -0
  35. codeshift/knowledge/sources.py +388 -0
  36. codeshift/knowledge_base/__init__.py +17 -0
  37. codeshift/knowledge_base/loader.py +102 -0
  38. codeshift/knowledge_base/models.py +110 -0
  39. codeshift/migrator/__init__.py +23 -0
  40. codeshift/migrator/ast_transforms.py +256 -0
  41. codeshift/migrator/engine.py +395 -0
  42. codeshift/migrator/llm_migrator.py +320 -0
  43. codeshift/migrator/transforms/__init__.py +19 -0
  44. codeshift/migrator/transforms/fastapi_transformer.py +174 -0
  45. codeshift/migrator/transforms/pandas_transformer.py +236 -0
  46. codeshift/migrator/transforms/pydantic_v1_to_v2.py +637 -0
  47. codeshift/migrator/transforms/requests_transformer.py +218 -0
  48. codeshift/migrator/transforms/sqlalchemy_transformer.py +175 -0
  49. codeshift/scanner/__init__.py +6 -0
  50. codeshift/scanner/code_scanner.py +352 -0
  51. codeshift/scanner/dependency_parser.py +473 -0
  52. codeshift/utils/__init__.py +5 -0
  53. codeshift/utils/api_client.py +266 -0
  54. codeshift/utils/cache.py +318 -0
  55. codeshift/utils/config.py +71 -0
  56. codeshift/utils/llm_client.py +221 -0
  57. codeshift/validator/__init__.py +6 -0
  58. codeshift/validator/syntax_checker.py +183 -0
  59. codeshift/validator/test_runner.py +224 -0
  60. codeshift-0.2.0.dist-info/METADATA +326 -0
  61. codeshift-0.2.0.dist-info/RECORD +65 -0
  62. codeshift-0.2.0.dist-info/WHEEL +5 -0
  63. codeshift-0.2.0.dist-info/entry_points.txt +2 -0
  64. codeshift-0.2.0.dist-info/licenses/LICENSE +21 -0
  65. codeshift-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,236 @@
1
+ """Pandas 1.x to 2.0 transformation using LibCST."""
2
+
3
+ import libcst as cst
4
+
5
+ from codeshift.migrator.ast_transforms import BaseTransformer
6
+
7
+
8
+ class PandasTransformer(BaseTransformer):
9
+ """Transform Pandas 1.x code to 2.0."""
10
+
11
+ def __init__(self) -> None:
12
+ super().__init__()
13
+
14
+ def leave_Call(self, original_node: cst.Call, updated_node: cst.Call) -> cst.Call:
15
+ """Transform Pandas function calls."""
16
+ # Handle to_csv line_terminator -> lineterminator
17
+ if isinstance(updated_node.func, cst.Attribute):
18
+ attr_name = updated_node.func.attr.value
19
+
20
+ if attr_name == "to_csv":
21
+ new_args = []
22
+ changed = False
23
+ for arg in updated_node.args:
24
+ if isinstance(arg.keyword, cst.Name) and arg.keyword.value == "line_terminator":
25
+ new_args.append(arg.with_changes(keyword=cst.Name("lineterminator")))
26
+ changed = True
27
+ self.record_change(
28
+ description="Rename to_csv line_terminator to lineterminator",
29
+ line_number=1,
30
+ original="to_csv(line_terminator=...)",
31
+ replacement="to_csv(lineterminator=...)",
32
+ transform_name="line_terminator_rename",
33
+ )
34
+ else:
35
+ new_args.append(arg)
36
+
37
+ if changed:
38
+ return updated_node.with_changes(args=new_args)
39
+
40
+ # Handle swaplevel axis removal
41
+ if attr_name == "swaplevel":
42
+ new_args = []
43
+ changed = False
44
+ for arg in updated_node.args:
45
+ if isinstance(arg.keyword, cst.Name) and arg.keyword.value == "axis":
46
+ changed = True
47
+ self.record_change(
48
+ description="Remove axis parameter from swaplevel",
49
+ line_number=1,
50
+ original="swaplevel(..., axis=...)",
51
+ replacement="swaplevel(...)",
52
+ transform_name="swaplevel_remove_axis",
53
+ )
54
+ continue
55
+ new_args.append(arg)
56
+
57
+ if changed:
58
+ return updated_node.with_changes(args=new_args)
59
+
60
+ # Handle reorder_levels axis removal
61
+ if attr_name == "reorder_levels":
62
+ new_args = []
63
+ changed = False
64
+ for arg in updated_node.args:
65
+ if isinstance(arg.keyword, cst.Name) and arg.keyword.value == "axis":
66
+ changed = True
67
+ self.record_change(
68
+ description="Remove axis parameter from reorder_levels",
69
+ line_number=1,
70
+ original="reorder_levels(..., axis=...)",
71
+ replacement="reorder_levels(...)",
72
+ transform_name="reorder_levels_remove_axis",
73
+ )
74
+ continue
75
+ new_args.append(arg)
76
+
77
+ if changed:
78
+ return updated_node.with_changes(args=new_args)
79
+
80
+ # Handle groupby numeric_only default changes
81
+ if attr_name in ("mean", "sum", "prod", "std", "var", "min", "max"):
82
+ # Check if called on groupby result and no numeric_only specified
83
+ has_numeric_only = any(
84
+ isinstance(arg.keyword, cst.Name) and arg.keyword.value == "numeric_only"
85
+ for arg in updated_node.args
86
+ )
87
+ if not has_numeric_only:
88
+ # Record as potential issue but don't auto-change
89
+ self.record_change(
90
+ description=f"GroupBy.{attr_name}() numeric_only now defaults to False",
91
+ line_number=1,
92
+ original=f".{attr_name}()",
93
+ replacement=f".{attr_name}(numeric_only=True)",
94
+ transform_name=f"groupby_{attr_name}_numeric_only",
95
+ confidence=0.6,
96
+ notes="Add numeric_only=True if you want to exclude non-numeric columns",
97
+ )
98
+
99
+ return updated_node
100
+
101
+ def leave_Attribute(
102
+ self, original_node: cst.Attribute, updated_node: cst.Attribute
103
+ ) -> cst.Attribute:
104
+ """Transform Pandas attribute accesses."""
105
+ attr_name = updated_node.attr.value
106
+
107
+ # Handle iteritems -> items
108
+ if attr_name == "iteritems":
109
+ self.record_change(
110
+ description="Rename iteritems() to items()",
111
+ line_number=1,
112
+ original=".iteritems()",
113
+ replacement=".items()",
114
+ transform_name="iteritems_to_items",
115
+ )
116
+ return updated_node.with_changes(attr=cst.Name("items"))
117
+
118
+ # Handle is_monotonic -> is_monotonic_increasing
119
+ if attr_name == "is_monotonic":
120
+ self.record_change(
121
+ description="Rename is_monotonic to is_monotonic_increasing",
122
+ line_number=1,
123
+ original=".is_monotonic",
124
+ replacement=".is_monotonic_increasing",
125
+ transform_name="is_monotonic_to_increasing",
126
+ )
127
+ return updated_node.with_changes(attr=cst.Name("is_monotonic_increasing"))
128
+
129
+ return updated_node
130
+
131
+
132
+ class PandasAppendTransformer(BaseTransformer):
133
+ """Transform DataFrame.append() to pd.concat()."""
134
+
135
+ def __init__(self) -> None:
136
+ super().__init__()
137
+
138
+ def leave_Call(self, original_node: cst.Call, updated_node: cst.Call) -> cst.BaseExpression:
139
+ """Transform append calls to concat."""
140
+ if not isinstance(updated_node.func, cst.Attribute):
141
+ return updated_node
142
+
143
+ if updated_node.func.attr.value != "append":
144
+ return updated_node
145
+
146
+ # Get the object being called on
147
+ obj = updated_node.func.value
148
+
149
+ # Get the first positional argument (what's being appended)
150
+ if not updated_node.args:
151
+ return updated_node
152
+
153
+ append_arg = updated_node.args[0].value
154
+
155
+ # Check for ignore_index parameter
156
+ ignore_index = False
157
+ other_args = []
158
+ for arg in updated_node.args[1:]:
159
+ if isinstance(arg.keyword, cst.Name) and arg.keyword.value == "ignore_index":
160
+ if isinstance(arg.value, cst.Name) and arg.value.value == "True":
161
+ ignore_index = True
162
+ else:
163
+ other_args.append(arg)
164
+
165
+ # Build pd.concat call
166
+ concat_args = [
167
+ cst.Arg(
168
+ value=cst.List(
169
+ elements=[
170
+ cst.Element(value=obj),
171
+ cst.Element(value=append_arg),
172
+ ]
173
+ )
174
+ )
175
+ ]
176
+
177
+ if ignore_index:
178
+ concat_args.append(
179
+ cst.Arg(
180
+ keyword=cst.Name("ignore_index"),
181
+ value=cst.Name("True"),
182
+ equal=cst.AssignEqual(
183
+ whitespace_before=cst.SimpleWhitespace(""),
184
+ whitespace_after=cst.SimpleWhitespace(""),
185
+ ),
186
+ )
187
+ )
188
+
189
+ self.record_change(
190
+ description="Replace DataFrame.append() with pd.concat()",
191
+ line_number=1,
192
+ original=".append(...)",
193
+ replacement="pd.concat([df1, df2], ...)",
194
+ transform_name="append_to_concat",
195
+ )
196
+
197
+ return cst.Call(
198
+ func=cst.Attribute(
199
+ value=cst.Name("pd"),
200
+ attr=cst.Name("concat"),
201
+ ),
202
+ args=concat_args,
203
+ )
204
+
205
+
206
+ def transform_pandas(source_code: str) -> tuple[str, list]:
207
+ """Transform Pandas code from 1.x to 2.0.
208
+
209
+ Args:
210
+ source_code: The source code to transform
211
+
212
+ Returns:
213
+ Tuple of (transformed_code, list of changes)
214
+ """
215
+ try:
216
+ tree = cst.parse_module(source_code)
217
+ except cst.ParserSyntaxError:
218
+ return source_code, []
219
+
220
+ # Apply main transformer
221
+ transformer = PandasTransformer()
222
+ transformer.set_source(source_code)
223
+
224
+ try:
225
+ transformed_tree = tree.visit(transformer)
226
+ all_changes = list(transformer.changes)
227
+
228
+ # Apply append transformer
229
+ append_transformer = PandasAppendTransformer()
230
+ append_transformer.set_source(transformed_tree.code)
231
+ transformed_tree = transformed_tree.visit(append_transformer)
232
+ all_changes.extend(append_transformer.changes)
233
+
234
+ return transformed_tree.code, all_changes
235
+ except Exception:
236
+ return source_code, []