This plugin refines the 'value' attribute in enums to refer to the original underlying value. For example, suppose we have the following: class SomeEnum: FOO = A() BAR = B() By default, mypy will infer that 'SomeEnum.FOO.value' and 'SomeEnum.BAR.valu
(ctx: mypy.plugin.AttributeContext)
| 158 | |
| 159 | |
| 160 | def enum_value_callback(ctx: mypy.plugin.AttributeContext) -> Type: |
| 161 | """This plugin refines the 'value' attribute in enums to refer to |
| 162 | the original underlying value. For example, suppose we have the |
| 163 | following: |
| 164 | |
| 165 | class SomeEnum: |
| 166 | FOO = A() |
| 167 | BAR = B() |
| 168 | |
| 169 | By default, mypy will infer that 'SomeEnum.FOO.value' and |
| 170 | 'SomeEnum.BAR.value' both are of type 'Any'. This plugin refines |
| 171 | this inference so that mypy understands the expressions are |
| 172 | actually of types 'A' and 'B' respectively. This better reflects |
| 173 | the actual runtime behavior. |
| 174 | |
| 175 | This plugin works simply by looking up the original value assigned |
| 176 | to the enum. For example, when this plugin sees 'SomeEnum.BAR.value', |
| 177 | it will look up whatever type 'BAR' had in the SomeEnum TypeInfo and |
| 178 | use that as the inferred type of the overall expression. |
| 179 | |
| 180 | This plugin assumes that the provided context is an attribute access |
| 181 | matching one of the strings found in 'ENUM_VALUE_ACCESS'. |
| 182 | """ |
| 183 | enum_field_name = _extract_underlying_field_name(ctx.type) |
| 184 | if enum_field_name is None: |
| 185 | # We do not know the enum field name (perhaps it was passed to a |
| 186 | # function and we only know that it _is_ a member). All is not lost |
| 187 | # however, if we can prove that the all of the enum members have the |
| 188 | # same value-type, then it doesn't matter which member was passed in. |
| 189 | # The value-type is still known. |
| 190 | if isinstance(ctx.type, Instance): |
| 191 | info = ctx.type.type |
| 192 | |
| 193 | # As long as mypy doesn't understand attribute creation in __new__, |
| 194 | # there is no way to predict the value type if the enum class has a |
| 195 | # custom implementation |
| 196 | if _implements_new(info): |
| 197 | return ctx.default_attr_type |
| 198 | |
| 199 | stnodes = (info.get(name) for name in info.names) |
| 200 | |
| 201 | # Enums _can_ have methods, instance attributes, and `nonmember`s. |
| 202 | # Omit methods and attributes created by assigning to self.* |
| 203 | # for our value inference. |
| 204 | node_types = ( |
| 205 | get_proper_type(n.type) if n else None |
| 206 | for n in stnodes |
| 207 | if n is None or not n.implicit |
| 208 | ) |
| 209 | proper_types = [ |
| 210 | _infer_value_type_with_auto_fallback(ctx, t) |
| 211 | for t in node_types |
| 212 | if t is None |
| 213 | or (not isinstance(t, CallableType) and not is_named_instance(t, "enum.nonmember")) |
| 214 | ] |
| 215 | underlying_type = _first(proper_types) |
| 216 | if underlying_type is None: |
| 217 | return ctx.default_attr_type |
nothing calls this directly
no test coverage detected
searching dependent graphs…