44
44
45
45
MANIM_ROOT = Path (__file__ ).resolve ().parent .parent .parent
46
46
47
+ # In the following, we will use ``type(xyz) is xyz_type`` instead of
48
+ # isinstance checks to make sure no subclasses of the type pass the
49
+ # check
50
+
47
51
48
52
def parse_module_attributes () -> tuple [AliasDocsDict , DataDict ]:
49
53
"""Read all files, generate Abstract Syntax Trees from them, and
@@ -70,7 +74,7 @@ def parse_module_attributes() -> tuple[AliasDocsDict, DataDict]:
70
74
for module_path in MANIM_ROOT .rglob ("*.py" ):
71
75
module_name = module_path .resolve ().relative_to (MANIM_ROOT )
72
76
module_name = list (module_name .parts )
73
- module_name [- 1 ] = module_name [- 1 ][: - 3 ] # remove .py
77
+ module_name [- 1 ] = module_name [- 1 ]. removesuffix ( ".py" )
74
78
module_name = "." .join (module_name )
75
79
76
80
module_content = module_path .read_text (encoding = "utf-8" )
@@ -107,53 +111,84 @@ def parse_module_attributes() -> tuple[AliasDocsDict, DataDict]:
107
111
data_list .append (data_name )
108
112
continue
109
113
110
- # If we encounter an assignment annotated as "TypeAlias":
114
+ # if it's defined under if TYPE_CHECKING
115
+ # go through the body of the if statement
111
116
if (
112
- type (node ) is ast .AnnAssign
113
- and type (node .annotation ) is ast .Name
114
- and node .annotation .id == "TypeAlias"
115
- and type (node .target ) is ast .Name
116
- and node .value is not None
117
+ # NOTE: This logic does not (and cannot)
118
+ # check if the comparison is against a
119
+ # variable called TYPE_CHECKING
120
+ # It also says that you cannot do the following
121
+ # import typing as foo
122
+ # if foo.TYPE_CHECKING:
123
+ # BAR: TypeAlias = ...
124
+ type (node ) is ast .If
125
+ and (
126
+ (
127
+ # if TYPE_CHECKING
128
+ type (node .test ) is ast .Name
129
+ and node .test .id == "TYPE_CHECKING"
130
+ )
131
+ or (
132
+ # if typing.TYPE_CHECKING
133
+ type (node .test ) is ast .Attribute
134
+ and type (node .test .value ) is ast .Name
135
+ and node .test .value .id == "typing"
136
+ and node .test .attr == "TYPE_CHECKING"
137
+ )
138
+ )
117
139
):
118
- alias_name = node .target .id
119
- def_node = node .value
120
- # If it's an Union, replace it with vertical bar notation
140
+ inner_nodes = node .body
141
+ else :
142
+ inner_nodes = [node ]
143
+
144
+ for node in inner_nodes :
145
+ # If we encounter an assignment annotated as "TypeAlias":
121
146
if (
122
- type (def_node ) is ast .Subscript
123
- and type (def_node .value ) is ast .Name
124
- and def_node .value .id == "Union"
147
+ type (node ) is ast .AnnAssign
148
+ and type (node .annotation ) is ast .Name
149
+ and node .annotation .id == "TypeAlias"
150
+ and type (node .target ) is ast .Name
151
+ and node .value is not None
125
152
):
126
- definition = " | " .join (
127
- ast .unparse (elem ) for elem in def_node .slice .elts
128
- )
153
+ alias_name = node .target .id
154
+ def_node = node .value
155
+ # If it's an Union, replace it with vertical bar notation
156
+ if (
157
+ type (def_node ) is ast .Subscript
158
+ and type (def_node .value ) is ast .Name
159
+ and def_node .value .id == "Union"
160
+ ):
161
+ definition = " | " .join (
162
+ ast .unparse (elem ) for elem in def_node .slice .elts
163
+ )
164
+ else :
165
+ definition = ast .unparse (def_node )
166
+
167
+ definition = definition .replace ("npt." , "" )
168
+ if category_dict is None :
169
+ module_dict ["" ] = {}
170
+ category_dict = module_dict ["" ]
171
+ category_dict [alias_name ] = {"definition" : definition }
172
+ alias_info = category_dict [alias_name ]
173
+ continue
174
+
175
+ # If here, the node is not a TypeAlias definition
176
+ alias_info = None
177
+
178
+ # It could still be a module attribute definition.
179
+ # Does the assignment have a target of type Name? Then
180
+ # it could be considered a definition of a module attribute.
181
+ if type (node ) is ast .AnnAssign :
182
+ target = node .target
183
+ elif type (node ) is ast .Assign and len (node .targets ) == 1 :
184
+ target = node .targets [0 ]
129
185
else :
130
- definition = ast .unparse (def_node )
131
-
132
- definition = definition .replace ("npt." , "" )
133
- if category_dict is None :
134
- module_dict ["" ] = {}
135
- category_dict = module_dict ["" ]
136
- category_dict [alias_name ] = {"definition" : definition }
137
- alias_info = category_dict [alias_name ]
138
- continue
139
-
140
- # If here, the node is not a TypeAlias definition
141
- alias_info = None
142
-
143
- # It could still be a module attribute definition.
144
- # Does the assignment have a target of type Name? Then
145
- # it could be considered a definition of a module attribute.
146
- if type (node ) is ast .AnnAssign :
147
- target = node .target
148
- elif type (node ) is ast .Assign and len (node .targets ) == 1 :
149
- target = node .targets [0 ]
150
- else :
151
- target = None
186
+ target = None
152
187
153
- if type (target ) is ast .Name :
154
- data_name = target .id
155
- else :
156
- data_name = None
188
+ if type (target ) is ast .Name :
189
+ data_name = target .id
190
+ else :
191
+ data_name = None
157
192
158
193
if len (module_dict ) > 0 :
159
194
ALIAS_DOCS_DICT [module_name ] = module_dict
0 commit comments