diff --git a/pydoctor/extensions/zopeinterface.py b/pydoctor/extensions/zopeinterface.py index a321ae975..19ea64651 100644 --- a/pydoctor/extensions/zopeinterface.py +++ b/pydoctor/extensions/zopeinterface.py @@ -110,7 +110,7 @@ def _handle_implemented( for idx, iface_name in enumerate(implementer.implements_directly): try: - iface = implementer.system.find_object(iface_name) + iface = implementer.system.objForFullName(iface_name, raise_missing=True) except LookupError: implementer.report( 'Interface "%s" not found' % iface_name, diff --git a/pydoctor/model.py b/pydoctor/model.py index e9dd9bfa8..1090189b1 100644 --- a/pydoctor/model.py +++ b/pydoctor/model.py @@ -349,14 +349,12 @@ class E: # should probably either return None or raise LookupError. full_name = f'{obj.fullName()}.{p}' break - nxt = self.system.objForFullName(full_name) + try: + nxt = self.system.objForFullName(full_name) + except RecursionError: + break if nxt is None: - try: - nxt = self.system.find_object(full_name) - except (RecursionError, LookupError): - break - if nxt is None: - break + break obj = nxt return '.'.join([full_name] + parts[i + 1:]) @@ -1118,14 +1116,15 @@ def msg(self, self.needsnl = False print('') - def objForFullName(self, fullName: str) -> Optional[Documentable]: - return self.allobjects.get(fullName) + def objForFullName(self, full_name: str, raise_missing:bool=False) -> Optional[Documentable]: + # return self.allobjects.get(fullName) - def find_object(self, full_name: str) -> Optional[Documentable]: - """Look up an object using a potentially outdated full name. + # def find_object(self, full_name: str) -> Optional[Documentable]: + """Look up an object using a full name. + Works with potentially outdated full anmes as well. A name can become outdated if the object is reparented: - L{objForFullName()} will only be able to find it under its new name, + L{System.allobjects} only contains its new name, but we might still have references to the old name. @param full_name: The fully qualified name of the object. @@ -1134,7 +1133,7 @@ def find_object(self, full_name: str) -> Optional[Documentable]: @raise LookupError: If the object is not found, while its name does match one of the roots of this system. """ - obj = self.objForFullName(full_name) + obj = self.allobjects.get(full_name) if obj is not None: return obj @@ -1143,10 +1142,13 @@ def find_object(self, full_name: str) -> Optional[Documentable]: name_parts = full_name.split('.', 1) for root_obj in self.rootobjects: if root_obj.name == name_parts[0]: - obj = self.objForFullName(root_obj.expandName(name_parts[1])) + obj = self.allobjects.get(root_obj.expandName(name_parts[1])) if obj is not None: return obj - raise LookupError(full_name) + if raise_missing: + raise LookupError(full_name) + else: + break return None diff --git a/pydoctor/test/test_astbuilder.py b/pydoctor/test/test_astbuilder.py index 18600ff1f..9d00bed14 100644 --- a/pydoctor/test/test_astbuilder.py +++ b/pydoctor/test/test_astbuilder.py @@ -3030,3 +3030,28 @@ def method(self):... builder.buildModules() assert capsys.readouterr().out == "moving 'pack.subpack.src.Thing.method' into 'pack'\n" +@systemcls_param +def test_multiple_reexports_with_aliases(systemcls: Type[model.System], capsys:CapSys) -> None: + # the attr.s() case + _make = ''' + """ + Link to L{attr.s}. + """ + + def attrs():... + ''' + top = ''' + from ._make import attrs + s = attributes = attrs + __all__ = ['s', 'attributes', 'attrs'] + ''' + system = systemcls() + builder = system.systemBuilder(system) + builder.addModuleString(top, 'attr', is_package=True) + builder.addModuleString(_make, '_make', parent_name='attr') + builder.buildModules() + + make_mod = system.allobjects['attr._make'] + epydoc2stan.ensure_parsed_docstring(make_mod) + to_html(make_mod.parsed_docstring, make_mod.docstring_linker) + assert 'Cannot find link target' not in capsys.readouterr().out diff --git a/pydoctor/test/test_packages.py b/pydoctor/test/test_packages.py index 37034c655..c8b6913a3 100644 --- a/pydoctor/test/test_packages.py +++ b/pydoctor/test/test_packages.py @@ -142,18 +142,18 @@ def test_reparenting_follows_aliases() -> None: assert mything._localNameToFullName('MyClass') == 'reparenting_follows_aliases.main.MyClass' assert myotherthing._localNameToFullName('MyClass') == 'reparenting_follows_aliases._mything.MyClass' - system.find_object('reparenting_follows_aliases._mything.MyClass') == klass + system.objForFullName('reparenting_follows_aliases._mything.MyClass') == klass # This part of the test cannot pass for now since we don't recursively resolve aliases. # See https://github.com/twisted/pydoctor/pull/414 and https://github.com/twisted/pydoctor/issues/430 try: - assert system.find_object('reparenting_follows_aliases._myotherthing.MyClass') == klass + assert system.objForFullName('reparenting_follows_aliases._myotherthing.MyClass') == klass assert myotherthing.resolveName('MyClass') == klass assert mything.resolveName('MyClass') == klass assert top.resolveName('_myotherthing.MyClass') == klass assert top.resolveName('_mything.MyClass') == klass - except (AssertionError, LookupError): + except AssertionError: return else: raise AssertionError("Congratulation!")