zrb 1.0.0b9__py3-none-any.whl → 1.0.0b10__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 (61) hide show
  1. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/.coveragerc +11 -0
  2. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/.gitignore +4 -0
  3. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/add_column_task.py +4 -4
  4. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/config.py +5 -0
  5. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_task.py +107 -1
  6. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +67 -4
  7. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_create_my_entity.py +53 -0
  8. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_delete_my_entity.py +62 -0
  9. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_read_my_entity.py +65 -0
  10. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_update_my_entity.py +61 -0
  11. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/gateway_subroute.py +57 -13
  12. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +2 -2
  13. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/gateway/subroute/my_module.py +6 -1
  14. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/module_task_definition.py +10 -6
  15. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task.py +56 -12
  16. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task_util.py +10 -4
  17. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_service.py +136 -52
  18. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/parser.py +1 -1
  19. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/config.py +1 -0
  20. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_auth_tables.py +46 -43
  21. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/8ed025bcc845_create_permissions.py +69 -0
  22. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_db_repository.py +5 -2
  23. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service.py +16 -21
  24. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +193 -43
  25. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/auth.py +57 -0
  26. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt +6 -1
  27. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py +1 -0
  28. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/user.py +9 -0
  29. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/_util/access_token.py +19 -0
  30. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_create_permission.py +59 -0
  31. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_delete_permission.py +68 -0
  32. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_read_permission.py +71 -0
  33. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_update_permission.py +66 -0
  34. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/test_user_session.py +195 -0
  35. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_health_and_readiness.py +28 -0
  36. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_homepage.py +17 -0
  37. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_not_found_error.py +16 -0
  38. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test.sh +7 -0
  39. zrb/task/base_task.py +10 -10
  40. zrb/util/codemod/modification_mode.py +3 -0
  41. zrb/util/codemod/modify_class.py +58 -0
  42. zrb/util/codemod/modify_class_parent.py +68 -0
  43. zrb/util/codemod/modify_class_property.py +128 -0
  44. zrb/util/codemod/modify_dict.py +75 -0
  45. zrb/util/codemod/modify_function.py +65 -0
  46. zrb/util/codemod/modify_function_call.py +68 -0
  47. zrb/util/codemod/modify_method.py +88 -0
  48. zrb/util/codemod/{prepend_code_to_module.py → modify_module.py} +2 -3
  49. zrb/util/file.py +3 -2
  50. {zrb-1.0.0b9.dist-info → zrb-1.0.0b10.dist-info}/METADATA +1 -1
  51. {zrb-1.0.0b9.dist-info → zrb-1.0.0b10.dist-info}/RECORD +53 -36
  52. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/migrate.py +0 -3
  53. zrb/util/codemod/append_code_to_class.py +0 -35
  54. zrb/util/codemod/append_code_to_function.py +0 -38
  55. zrb/util/codemod/append_code_to_method.py +0 -55
  56. zrb/util/codemod/append_key_to_dict.py +0 -51
  57. zrb/util/codemod/append_param_to_function_call.py +0 -39
  58. zrb/util/codemod/prepend_parent_to_class.py +0 -38
  59. zrb/util/codemod/prepend_property_to_class.py +0 -55
  60. {zrb-1.0.0b9.dist-info → zrb-1.0.0b10.dist-info}/WHEEL +0 -0
  61. {zrb-1.0.0b9.dist-info → zrb-1.0.0b10.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,68 @@
1
+ import libcst as cst
2
+
3
+ from zrb.util.codemod.modification_mode import APPEND, PREPEND, REPLACE
4
+
5
+
6
+ def replace_parent_class(
7
+ original_code: str, class_name: str, parent_class_name: str
8
+ ) -> str:
9
+ return _modify_parent_class(original_code, class_name, parent_class_name, REPLACE)
10
+
11
+
12
+ def append_parent_class(
13
+ original_code: str, class_name: str, parent_class_name: str
14
+ ) -> str:
15
+ return _modify_parent_class(original_code, class_name, parent_class_name, APPEND)
16
+
17
+
18
+ def prepend_parent_class(
19
+ original_code: str, class_name: str, parent_class_name: str
20
+ ) -> str:
21
+ return _modify_parent_class(original_code, class_name, parent_class_name, PREPEND)
22
+
23
+
24
+ def _modify_parent_class(
25
+ original_code: str, class_name: str, parent_class_name: str, mode: int
26
+ ) -> str:
27
+ # Parse the original code into a module
28
+ module = cst.parse_module(original_code)
29
+ # Initialize transformer with the class name and parent class name
30
+ transformer = _ParentClassAdder(class_name, parent_class_name, mode)
31
+ # Apply the transformation
32
+ modified_module = module.visit(transformer)
33
+ # Check if the class was found
34
+ if not transformer.class_found:
35
+ raise ValueError(f"Class {class_name} not found in the provided code.")
36
+ # Return the modified code
37
+ return modified_module.code
38
+
39
+
40
+ class _ParentClassAdder(cst.CSTTransformer):
41
+ def __init__(self, class_name: str, parent_class_name: str, mode: int):
42
+ self.class_name = class_name
43
+ self.parent_class_name = parent_class_name
44
+ self.class_found = False
45
+ self.mode = mode
46
+
47
+ def leave_ClassDef(
48
+ self, original_node: cst.ClassDef, updated_node: cst.ClassDef
49
+ ) -> cst.ClassDef:
50
+ # Check if this is the target class
51
+ if original_node.name.value == self.class_name:
52
+ self.class_found = True
53
+ if self.mode == REPLACE:
54
+ new_bases = (cst.Arg(value=cst.Name(self.parent_class_name)),)
55
+ return updated_node.with_changes(bases=new_bases)
56
+ if self.mode == PREPEND:
57
+ new_bases = (
58
+ cst.Arg(value=cst.Name(self.parent_class_name)),
59
+ *updated_node.bases,
60
+ )
61
+ return updated_node.with_changes(bases=new_bases)
62
+ if self.mode == APPEND:
63
+ new_bases = (
64
+ *updated_node.bases,
65
+ cst.Arg(value=cst.Name(self.parent_class_name)),
66
+ )
67
+ return updated_node.with_changes(bases=new_bases)
68
+ return updated_node
@@ -0,0 +1,128 @@
1
+ import libcst as cst
2
+
3
+ from zrb.util.codemod.modification_mode import APPEND, PREPEND
4
+
5
+
6
+ def append_property_to_class(
7
+ original_code: str,
8
+ class_name: str,
9
+ property_name: str,
10
+ annotation: str,
11
+ default_value: str,
12
+ ) -> str:
13
+ return _modify_class_property(
14
+ original_code, class_name, property_name, annotation, default_value, APPEND
15
+ )
16
+
17
+
18
+ def prepend_property_to_class(
19
+ original_code: str,
20
+ class_name: str,
21
+ property_name: str,
22
+ annotation: str,
23
+ default_value: str,
24
+ ) -> str:
25
+ return _modify_class_property(
26
+ original_code, class_name, property_name, annotation, default_value, PREPEND
27
+ )
28
+
29
+
30
+ def _modify_class_property(
31
+ original_code: str,
32
+ class_name: str,
33
+ property_name: str,
34
+ annotation: str,
35
+ default_value: str,
36
+ mode: int,
37
+ ) -> str:
38
+ # Parse the original code into a module
39
+ module = cst.parse_module(original_code)
40
+ # Initialize transformer with the class name, property name, annotation, and default value
41
+ transformer = _ClassPropertyModifier(
42
+ class_name, property_name, annotation, default_value, mode
43
+ )
44
+ # Apply the transformation
45
+ modified_module = module.visit(transformer)
46
+ # Check if the class was found
47
+ if not transformer.class_found:
48
+ raise ValueError(f"Class {class_name} not found in the provided code.")
49
+ # Return the modified code
50
+ return modified_module.code
51
+
52
+
53
+ class _ClassPropertyModifier(cst.CSTTransformer):
54
+ def __init__(
55
+ self,
56
+ class_name: str,
57
+ property_name: str,
58
+ annotation: str,
59
+ default_value: str,
60
+ mode: int,
61
+ ):
62
+ self.class_name = class_name
63
+ self.property_name = property_name
64
+ self.annotation = cst.Annotation(cst.parse_expression(annotation))
65
+ self.default_value = cst.parse_expression(default_value)
66
+ self.class_found = False
67
+ self.mode = mode
68
+
69
+ def leave_ClassDef(
70
+ self, original_node: cst.ClassDef, updated_node: cst.ClassDef
71
+ ) -> cst.ClassDef:
72
+ # Check if this is the target class
73
+ if original_node.name.value == self.class_name:
74
+ self.class_found = True
75
+ # Create the annotated property with a default value
76
+ new_property = cst.SimpleStatementLine(
77
+ body=[
78
+ cst.AnnAssign(
79
+ target=cst.Name(self.property_name),
80
+ annotation=self.annotation,
81
+ value=self.default_value,
82
+ )
83
+ ]
84
+ )
85
+ if self.mode == PREPEND:
86
+ new_body = cst.IndentedBlock(
87
+ body=(new_property,) + updated_node.body.body
88
+ )
89
+ return updated_node.with_changes(body=new_body)
90
+ if self.mode == APPEND:
91
+ # Identify properties and methods
92
+ properties = []
93
+ methods = []
94
+ for stmt in updated_node.body.body:
95
+ if isinstance(stmt, cst.SimpleStatementLine) and isinstance(
96
+ stmt.body[0], (cst.AnnAssign, cst.Assign)
97
+ ):
98
+ properties.append(stmt)
99
+ elif isinstance(stmt, cst.FunctionDef):
100
+ methods.append(stmt)
101
+ if properties:
102
+ # Class has properties
103
+ last_property_index = updated_node.body.body.index(properties[-1])
104
+ new_body = cst.IndentedBlock(
105
+ body=(
106
+ updated_node.body.body[: last_property_index + 1]
107
+ + (new_property,)
108
+ + updated_node.body.body[last_property_index + 1 :]
109
+ )
110
+ )
111
+ return updated_node.with_changes(body=new_body)
112
+ if methods:
113
+ # Class doesn't have properties but has methods
114
+ first_method_index = updated_node.body.body.index(methods[0])
115
+ new_body = cst.IndentedBlock(
116
+ body=(
117
+ updated_node.body.body[:first_method_index]
118
+ + (new_property,)
119
+ + updated_node.body.body[first_method_index:]
120
+ )
121
+ )
122
+ return updated_node.with_changes(body=new_body)
123
+ # Class is empty, add add the bottom
124
+ new_body = cst.IndentedBlock(
125
+ body=updated_node.body.body + (new_property,)
126
+ )
127
+ return updated_node.with_changes(body=new_body)
128
+ return updated_node
@@ -0,0 +1,75 @@
1
+ import libcst as cst
2
+
3
+ from zrb.util.codemod.modification_mode import APPEND, PREPEND
4
+
5
+
6
+ def prepend_key_to_dict(
7
+ original_code: str, dictionary_name: str, new_key: str, new_value: str
8
+ ) -> str:
9
+ return _modify_dict(original_code, dictionary_name, new_key, new_value, PREPEND)
10
+
11
+
12
+ def append_key_to_dict(
13
+ original_code: str, dictionary_name: str, new_key: str, new_value: str
14
+ ) -> str:
15
+ return _modify_dict(original_code, dictionary_name, new_key, new_value, APPEND)
16
+
17
+
18
+ def _modify_dict(
19
+ original_code: str, dictionary_name: str, new_key: str, new_value: str, mode: int
20
+ ) -> str:
21
+ # Parse the original code into a module
22
+ module = cst.parse_module(original_code)
23
+ # Initialize the transformer with the necessary information
24
+ transformer = _DictionaryModifier(dictionary_name, new_key, new_value, mode)
25
+ # Apply the transformation
26
+ modified_module = module.visit(transformer)
27
+ # Error handling: raise an error if the dictionary is not found
28
+ if not transformer.found:
29
+ raise ValueError(
30
+ f"Dictionary {dictionary_name} not found in the provided code."
31
+ )
32
+ # Return the modified code
33
+ return modified_module.code
34
+
35
+
36
+ class _DictionaryModifier(cst.CSTTransformer):
37
+ def __init__(self, dictionary_name: str, new_key: str, new_value: str, mode: int):
38
+ self.dictionary_name = dictionary_name
39
+ self.new_key = new_key
40
+ self.new_value = new_value
41
+ self.found = False
42
+ self.mode = mode
43
+
44
+ def leave_Assign(
45
+ self, original_node: cst.Assign, updated_node: cst.Assign
46
+ ) -> cst.Assign:
47
+ # Extract the first target from updated_node, which will be an AssignTarget
48
+ target = updated_node.targets[0]
49
+ # Check if the target is a Name (which should represent the dictionary)
50
+ if (
51
+ isinstance(target.target, cst.Name)
52
+ and target.target.value == self.dictionary_name
53
+ ):
54
+ # Check if it's a dictionary initialization (e.g., my_dict = {...})
55
+ if isinstance(updated_node.value, cst.Dict):
56
+ self.found = True
57
+ if self.mode == PREPEND:
58
+ new_entries = (
59
+ cst.DictElement(
60
+ key=cst.SimpleString(f'"{self.new_key}"'),
61
+ value=cst.SimpleString(f'"{self.new_value}"'),
62
+ ),
63
+ ) + updated_node.value.elements
64
+ new_dict = updated_node.value.with_changes(elements=new_entries)
65
+ return updated_node.with_changes(value=new_dict)
66
+ if self.mode == APPEND:
67
+ new_entries = updated_node.value.elements + (
68
+ cst.DictElement(
69
+ key=cst.SimpleString(f'"{self.new_key}"'),
70
+ value=cst.SimpleString(f'"{self.new_value}"'),
71
+ ),
72
+ )
73
+ new_dict = updated_node.value.with_changes(elements=new_entries)
74
+ return updated_node.with_changes(value=new_dict)
75
+ return updated_node
@@ -0,0 +1,65 @@
1
+ import libcst as cst
2
+
3
+ from zrb.util.codemod.modification_mode import APPEND, PREPEND, REPLACE
4
+
5
+
6
+ def replace_function_code(original_code: str, function_name: str, new_code: str) -> str:
7
+ return _modify_function(original_code, function_name, new_code, REPLACE)
8
+
9
+
10
+ def prepend_code_to_function(
11
+ original_code: str, function_name: str, new_code: str
12
+ ) -> str:
13
+ return _modify_function(original_code, function_name, new_code, PREPEND)
14
+
15
+
16
+ def append_code_to_function(
17
+ original_code: str, function_name: str, new_code: str
18
+ ) -> str:
19
+ return _modify_function(original_code, function_name, new_code, APPEND)
20
+
21
+
22
+ def _modify_function(
23
+ original_code: str, function_name: str, new_code: str, mode: int
24
+ ) -> str:
25
+ # Parse the original code into a module
26
+ module = cst.parse_module(original_code)
27
+ # Initialize the transformer with the necessary information
28
+ transformer = _FunctionCodeModifier(function_name, new_code, mode)
29
+ # Apply the transformation
30
+ modified_module = module.visit(transformer)
31
+ # Error handling: raise an error if the class or function is not found
32
+ if not transformer.function_found:
33
+ raise ValueError(f"Function {function_name} not found.")
34
+ # Return the modified code
35
+ return modified_module.code
36
+
37
+
38
+ class _FunctionCodeModifier(cst.CSTTransformer):
39
+ def __init__(self, function_name: str, new_code: str, mode: int):
40
+ self.function_name = function_name
41
+ # Use parse_module to handle multiple statements
42
+ self.new_code = cst.parse_module(new_code).body
43
+ self.function_found = False
44
+ self.mode = mode
45
+
46
+ def leave_FunctionDef(
47
+ self, original_node: cst.ClassDef, updated_node: cst.ClassDef
48
+ ) -> cst.ClassDef:
49
+ # Check if the class matches the target class
50
+ if original_node.name.value == self.function_name:
51
+ self.function_found = True
52
+ if self.mode == REPLACE:
53
+ new_body = updated_node.body.with_changes(body=tuple(self.new_code))
54
+ return updated_node.with_changes(body=new_body)
55
+ if self.mode == PREPEND:
56
+ new_body = updated_node.body.with_changes(
57
+ body=tuple(self.new_code) + updated_node.body.body
58
+ )
59
+ return updated_node.with_changes(body=new_body)
60
+ if self.mode == APPEND:
61
+ new_body = updated_node.body.with_changes(
62
+ body=updated_node.body.body + tuple(self.new_code)
63
+ )
64
+ return updated_node.with_changes(body=new_body)
65
+ return updated_node
@@ -0,0 +1,68 @@
1
+ import libcst as cst
2
+
3
+ from zrb.util.codemod.modification_mode import APPEND, PREPEND, REPLACE
4
+
5
+
6
+ def replace_function_call_param(
7
+ original_code: str, function_name: str, new_param: str
8
+ ) -> str:
9
+ return _modify_function_call(original_code, function_name, new_param, REPLACE)
10
+
11
+
12
+ def prepend_param_to_function_call(
13
+ original_code: str, function_name: str, new_param: str
14
+ ) -> str:
15
+ return _modify_function_call(original_code, function_name, new_param, PREPEND)
16
+
17
+
18
+ def append_param_to_function_call(
19
+ original_code: str, function_name: str, new_param: str
20
+ ) -> str:
21
+ return _modify_function_call(original_code, function_name, new_param, APPEND)
22
+
23
+
24
+ def _modify_function_call(
25
+ original_code: str, function_name: str, new_param: str, mode: int
26
+ ) -> str:
27
+ # Parse the original code into a module
28
+ module = cst.parse_module(original_code)
29
+ # Initialize the transformer with the necessary information
30
+ transformer = _FunctionCallParamModifier(function_name, new_param, mode)
31
+ # Apply the transformation
32
+ modified_module = module.visit(transformer)
33
+ # Error handling: raise an error if the function call is not found
34
+ if not transformer.param_added:
35
+ raise ValueError(
36
+ f"Function call to {function_name} not found in the provided code."
37
+ )
38
+ # Return the modified code
39
+ return modified_module.code
40
+
41
+
42
+ class _FunctionCallParamModifier(cst.CSTTransformer):
43
+ def __init__(self, func_name: str, new_param: str, mode: int):
44
+ self.func_name = func_name
45
+ # Parse the new parameter to ensure it’s a valid CST node
46
+ self.new_param = cst.parse_expression(new_param)
47
+ self.param_added = False
48
+ self.mode = mode
49
+
50
+ def leave_Call(self, original_node: cst.Call, updated_node: cst.Call) -> cst.Call:
51
+ # Check if the function call name matches the target function
52
+ if (
53
+ isinstance(original_node.func, cst.Name)
54
+ and original_node.func.value == self.func_name
55
+ ):
56
+ if self.mode == REPLACE:
57
+ new_args = (cst.Arg(value=self.new_param),)
58
+ self.param_added = True
59
+ return updated_node.with_changes(args=new_args)
60
+ if self.mode == PREPEND:
61
+ new_args = (cst.Arg(value=self.new_param),) + updated_node.args
62
+ self.param_added = True
63
+ return updated_node.with_changes(args=new_args)
64
+ if self.mode == APPEND:
65
+ new_args = updated_node.args + (cst.Arg(value=self.new_param),)
66
+ self.param_added = True
67
+ return updated_node.with_changes(args=new_args)
68
+ return updated_node
@@ -0,0 +1,88 @@
1
+ import libcst as cst
2
+
3
+ from zrb.util.codemod.modification_mode import APPEND, PREPEND, REPLACE
4
+
5
+
6
+ def replace_method_code(
7
+ original_code: str, class_name: str, method_name: str, new_code: str
8
+ ) -> str:
9
+ return _modify_method(original_code, class_name, method_name, new_code, REPLACE)
10
+
11
+
12
+ def prepend_code_to_method(
13
+ original_code: str, class_name: str, method_name: str, new_code: str
14
+ ) -> str:
15
+ return _modify_method(original_code, class_name, method_name, new_code, PREPEND)
16
+
17
+
18
+ def append_code_to_method(
19
+ original_code: str, class_name: str, method_name: str, new_code: str
20
+ ) -> str:
21
+ return _modify_method(original_code, class_name, method_name, new_code, APPEND)
22
+
23
+
24
+ def _modify_method(
25
+ original_code: str, class_name: str, method_name: str, new_code: str, mode: int
26
+ ) -> str:
27
+ # Parse the original code into a module
28
+ module = cst.parse_module(original_code)
29
+ # Initialize the transformer with the necessary information
30
+ transformer = _MethodModifier(class_name, method_name, new_code, mode)
31
+ # Apply the transformation
32
+ modified_module = module.visit(transformer)
33
+ # Error handling: raise an error if the class or function is not found
34
+ if not transformer.class_found:
35
+ raise ValueError(f"Class {class_name} not found in the provided code.")
36
+ if not transformer.method_found:
37
+ raise ValueError(f"Method {method_name} not found in class {class_name}.")
38
+ # Return the modified code
39
+ return modified_module.code
40
+
41
+
42
+ class _MethodModifier(cst.CSTTransformer):
43
+ def __init__(self, class_name: str, method_name: str, new_code: str, mode: int):
44
+ self.class_name = class_name
45
+ self.method_name = method_name
46
+ # Use parse_module to handle multiple statements
47
+ self.new_code = cst.parse_module(new_code).body
48
+ self.class_found = False
49
+ self.method_found = False
50
+ self.mode = mode
51
+
52
+ def leave_ClassDef(
53
+ self, original_node: cst.ClassDef, updated_node: cst.ClassDef
54
+ ) -> cst.ClassDef:
55
+ # Check if the class matches the target class
56
+ if original_node.name.value == self.class_name:
57
+ self.class_found = True
58
+ # Now, modify function definitions inside this class
59
+ new_body = []
60
+ for (
61
+ item
62
+ ) in updated_node.body.body: # Access body.body, not just updated_node.body
63
+ if (
64
+ isinstance(item, cst.FunctionDef)
65
+ and item.name.value == self.method_name
66
+ ):
67
+ # Modify the target function by adding the new code
68
+ if self.mode == REPLACE:
69
+ body_with_new_code = item.body.with_changes(
70
+ body=tuple(self.new_code)
71
+ )
72
+ new_body.append(item.with_changes(body=body_with_new_code))
73
+ if self.mode == PREPEND:
74
+ body_with_new_code = item.body.with_changes(
75
+ body=tuple(self.new_code) + item.body.body
76
+ )
77
+ new_body.append(item.with_changes(body=body_with_new_code))
78
+
79
+ if self.mode == APPEND:
80
+ body_with_new_code = item.body.with_changes(
81
+ body=item.body.body + tuple(self.new_code)
82
+ )
83
+ new_body.append(item.with_changes(body=body_with_new_code))
84
+ self.method_found = True
85
+ else:
86
+ new_body.append(item)
87
+ return updated_node.with_changes(body=cst.IndentedBlock(new_body))
88
+ return updated_node
@@ -5,8 +5,7 @@ def prepend_code_to_module(original_code: str, new_code: str) -> str:
5
5
  stripped_line = line.strip()
6
6
  if stripped_line.startswith("import") or stripped_line.startswith("from"):
7
7
  last_import_index = i
8
- elif stripped_line and not stripped_line.startswith("#"):
8
+ else:
9
9
  break
10
- if last_import_index != -1:
11
- lines.insert(last_import_index + 1, new_code)
10
+ lines.insert(last_import_index + 1, new_code)
12
11
  return "\n".join(lines)
zrb/util/file.py CHANGED
@@ -15,10 +15,11 @@ def write_file(file_path: str, content: str | list[str]):
15
15
  content = "\n".join([line for line in content if line is not None])
16
16
  dir_path = os.path.dirname(file_path)
17
17
  os.makedirs(dir_path, exist_ok=True)
18
+ should_add_eol = content.endswith("\n")
19
+ # Remove trailing newlines, but keep one if the file originally ended up with newline
18
20
  content = re.sub(r"\n{3,}$", "\n\n", content)
19
- # Remove trailing newlines, but keep one if it exists
20
21
  content = content.rstrip("\n")
21
- if content.endswith("\n"):
22
+ if should_add_eol:
22
23
  content += "\n"
23
24
  with open(file_path, "w") as f:
24
25
  f.write(content)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zrb
3
- Version: 1.0.0b9
3
+ Version: 1.0.0b10
4
4
  Summary: Your Automation Powerhouse
5
5
  Home-page: https://github.com/state-alchemists/zrb
6
6
  License: AGPL-3.0-or-later