From d06e2ac3d66a933cad319b46a7eae36c0a021066 Mon Sep 17 00:00:00 2001 From: Andrey Lebedev Date: Thu, 9 Feb 2023 23:01:22 +0200 Subject: [PATCH] Fix metaclass conflict This fixes the regression when #88 combined with #89: mypy-1.0 started producing the error: ``` Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases ``` --- src/mypy_zope/plugin.py | 9 ++----- tests/test_mro_calculation.py | 50 ----------------------------------- 2 files changed, 2 insertions(+), 57 deletions(-) diff --git a/src/mypy_zope/plugin.py b/src/mypy_zope/plugin.py index 44a5d51..fcf0999 100644 --- a/src/mypy_zope/plugin.py +++ b/src/mypy_zope/plugin.py @@ -719,13 +719,8 @@ def _apply_interface(self, impl: TypeInfo, iface: TypeInfo) -> None: # there is a decorator for the class that will create a "type promotion", # but ensure this only gets applied a single time per interface. promote = Instance(iface, []) - if not any(promote in ti._promote for ti in impl.mro): - faketi = TypeInfo(SymbolTable(), iface.defn, iface.module_name) - faketi._promote = [promote] - faketi.metaclass_type = iface.metaclass_type - # Insert the TypeInfo before the builtins.object that's at the end. - assert impl.mro[-1].fullname == 'builtins.object' - impl.mro.insert(len(impl.mro) - 1, faketi) + if promote not in impl._promote: + impl._promote.append(promote) def plugin(version: str) -> PyType[Plugin]: diff --git a/tests/test_mro_calculation.py b/tests/test_mro_calculation.py index eb9cf5d..9ab496f 100644 --- a/tests/test_mro_calculation.py +++ b/tests/test_mro_calculation.py @@ -16,53 +16,3 @@ def mypy_cache_dir(tmp_path_factory): tdir = tmp_path_factory.mktemp('.mypy_cahe') print("Setup cache", str(tdir)) return str(tdir) - - -def test_mro_computation_in_forward_reference_to_implementer(mypy_cache_dir: str) -> None: - sample_name = "forward_reference_to_implementer" - - opts = options.Options() - opts.show_traceback = True - opts.namespace_packages = True - opts.cache_dir = mypy_cache_dir - opts.plugins = ['mypy_zope:plugin'] - # Config file is needed to load plugins, it doesn't not exist and is not - # supposed to. - opts.config_file = 'not_existing_config.ini' - - samplefile = os.path.join(SAMPLES_DIR, f"{sample_name}.py") - base_dir = os.path.dirname(samplefile) - with open(samplefile) as f: - source = BuildSource( - None, - module=sample_name, - text=f.read(), - base_dir=base_dir, - ) - result = build.build(sources=[source], options=opts) - assert result.errors == [] - - # Result.graph is a map from module name to state objects. - state: State = result.graph[sample_name] - - # Find Mypy's representation of the Protocol class. - node: Optional[SymbolTableNode] = None - for fullname, symbol_table_node, _type_info in state.tree.local_definitions(): - # Use startswith(...) rather than a direct comparison - # because the typename includes a line number at the end - if fullname.startswith(f"{sample_name}.Protocol"): - node = symbol_table_node - break - - assert node is not None, f"Failed to find `Protocol` class in mypy's state for {samplefile}" - - mro: List[TypeInfo] = node.node.mro - # Expected: [ - # , - # , - # , - # ] - assert len(mro) == 3 - assert mro[0].fullname.startswith(f"{sample_name}.Protocol") - assert mro[1].fullname == f"{sample_name}.IProtocol" - assert mro[2].fullname == "builtins.object"