Try creating an ad-hoc intersection of the given instances. Note that this function does *not* try and create a full-fledged intersection type. Instead, it returns an instance of a new ad-hoc subclass of the given instances. This is mainly useful when you need a way
(
self, instances: tuple[Instance, Instance], errors: list[tuple[str, str]]
)
| 6128 | return cdef, info |
| 6129 | |
| 6130 | def intersect_instances( |
| 6131 | self, instances: tuple[Instance, Instance], errors: list[tuple[str, str]] |
| 6132 | ) -> Instance | None: |
| 6133 | """Try creating an ad-hoc intersection of the given instances. |
| 6134 | |
| 6135 | Note that this function does *not* try and create a full-fledged |
| 6136 | intersection type. Instead, it returns an instance of a new ad-hoc |
| 6137 | subclass of the given instances. |
| 6138 | |
| 6139 | This is mainly useful when you need a way of representing some |
| 6140 | theoretical subclass of the instances the user may be trying to use |
| 6141 | the generated intersection can serve as a placeholder. |
| 6142 | |
| 6143 | This function will create a fresh subclass the first time you call it. |
| 6144 | So this means calling `self.intersect_intersection([inst_1, inst_2], ctx)` |
| 6145 | twice will return the same subclass of inst_1 and inst_2. |
| 6146 | |
| 6147 | Returns None if creating the subclass is impossible (e.g. due to |
| 6148 | MRO errors or incompatible signatures). If we do successfully create |
| 6149 | a subclass, its TypeInfo will automatically be added to the global scope. |
| 6150 | """ |
| 6151 | curr_module = self.scope.stack[0] |
| 6152 | assert isinstance(curr_module, MypyFile) |
| 6153 | |
| 6154 | # First, retry narrowing while allowing promotions (they are disabled by default |
| 6155 | # for isinstance() checks, etc). This way we will still type-check branches like |
| 6156 | # x: complex = 1 |
| 6157 | # if isinstance(x, int): |
| 6158 | # ... |
| 6159 | left, right = instances |
| 6160 | if is_proper_subtype(left, right, ignore_promotions=False): |
| 6161 | return left |
| 6162 | if is_proper_subtype(right, left, ignore_promotions=False): |
| 6163 | return right |
| 6164 | |
| 6165 | def _get_base_classes(instances_: tuple[Instance, Instance]) -> list[Instance]: |
| 6166 | base_classes_ = [] |
| 6167 | for inst in instances_: |
| 6168 | if inst.type.is_intersection: |
| 6169 | expanded = inst.type.bases |
| 6170 | else: |
| 6171 | expanded = [inst] |
| 6172 | |
| 6173 | for expanded_inst in expanded: |
| 6174 | base_classes_.append(expanded_inst) |
| 6175 | return base_classes_ |
| 6176 | |
| 6177 | def _make_fake_typeinfo_and_full_name( |
| 6178 | base_classes_: list[Instance], curr_module_: MypyFile, options: Options |
| 6179 | ) -> tuple[TypeInfo, str]: |
| 6180 | names = [format_type_bare(x, options=options, verbosity=2) for x in base_classes_] |
| 6181 | name = f"<subclass of {pretty_seq(names, 'and')}>" |
| 6182 | if (symbol := curr_module_.names.get(name)) is not None: |
| 6183 | assert isinstance(symbol.node, TypeInfo) |
| 6184 | return symbol.node, name |
| 6185 | cdef, info_ = self.make_fake_typeinfo(curr_module_.fullname, name, name, base_classes_) |
| 6186 | return info_, name |
| 6187 |
no test coverage detected