Astroid (used by pylint) calls this function on each class definition it discovers. cls is an Astroid AST representation of that class. Our purpose here is to extract the schema dict from API model classes so that we can inform pylint about all of the attributes on those models.
(cls: nodes.ClassDef)
| 98 | |
| 99 | |
| 100 | def transform(cls: nodes.ClassDef): |
| 101 | """ |
| 102 | Astroid (used by pylint) calls this function on each class definition it discovers. |
| 103 | cls is an Astroid AST representation of that class. |
| 104 | |
| 105 | Our purpose here is to extract the schema dict from API model classes |
| 106 | so that we can inform pylint about all of the attributes on those models. |
| 107 | We do this by injecting attributes on the class for each property in the schema. |
| 108 | """ |
| 109 | |
| 110 | # This is a class which defines attributes in "schema" variable using json schema. |
| 111 | # Those attributes are then assigned during run time inside the constructor |
| 112 | |
| 113 | # Get the value node for the "schema =" assignment |
| 114 | schema_dict_node = next(cls.igetattr("schema")) |
| 115 | |
| 116 | extra_schema_properties = {} |
| 117 | |
| 118 | # If the "schema =" assignment's value node is not a simple type (like a dictionary), |
| 119 | # then pylint cannot infer exactly what it does. Most of the time, this is actually |
| 120 | # a function call to copy the schema from another class. So, let's find the dictionary. |
| 121 | if schema_dict_node is astroid.Uninferable: |
| 122 | # the assignment probably looks like this: |
| 123 | # schema = copy.deepcopy(ActionAPI.schema) |
| 124 | |
| 125 | # so far we only have the value, but we need the actual assignment |
| 126 | assigns = [n for n in cls.get_children() if isinstance(n, nodes.Assign)] |
| 127 | schema_assign_name_node = cls.local_attr("schema")[0] |
| 128 | schema_assign_node = next( |
| 129 | assign for assign in assigns if assign.targets[0] == schema_assign_name_node |
| 130 | ) |
| 131 | assigns.remove(schema_assign_node) |
| 132 | |
| 133 | # We only care about "schema = copy.deepcopy(...)" |
| 134 | schema_dict_node = infer_copy_deepcopy(schema_assign_node.value) |
| 135 | if not schema_dict_node: |
| 136 | # This is not an API model class, as it doesn't have |
| 137 | # something we can resolve to a dictionary. |
| 138 | return |
| 139 | |
| 140 | # OK, now we need to look for any properties that dynamically modify |
| 141 | # the dictionary that was just copied from somewhere else. |
| 142 | # See the note below for why we only care about "properties" here. |
| 143 | for assign_node in assigns: |
| 144 | # we're looking for assignments like this: |
| 145 | # schema["properties"]["ttl"] = {...} |
| 146 | target = assign_node.targets[0] |
| 147 | try: |
| 148 | if ( |
| 149 | isinstance(target, nodes.Subscript) |
| 150 | and target.value.value.name == "schema" |
| 151 | ): |
| 152 | if ( |
| 153 | isinstance(target.value.slice.value, nodes.Const) |
| 154 | and target.value.slice.value.value == "properties" |
| 155 | ): |
| 156 | # python <3.9 |
| 157 | property_name_node = target.slice.value |
nothing calls this directly
no test coverage detected