Skip to content

Commit

Permalink
It's possible to re-export stuff from a class.
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanlatr committed Nov 1, 2023
1 parent f00cda1 commit 1c4343b
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 18 deletions.
7 changes: 3 additions & 4 deletions pydoctor/astbuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,15 +186,14 @@ def _handleReExport(info:'ReExport', elsewhere:Collection['ReExport']) -> None:
target = info.target
as_name = info.as_name
target_parent = target.parent
assert isinstance(target_parent, model.Module)

# Remember that this name is re-exported
target_parent.elsewhere_contents[target.name] = target
target_parent.exported[target.name] = target

extra_msg = ''

for e in elsewhere:
e.new_parent.elsewhere_contents[e.as_name] = target
e.new_parent.exported[e.as_name] = target

if not extra_msg:
extra_msg += ', also available at '
Expand Down Expand Up @@ -325,7 +324,7 @@ def processReExports(system:'model.System') -> None:
f"because {target.name!r} is already exported in public module {target.parent.fullName()!r}")

for e in _exports:
e.new_parent.elsewhere_contents[e.as_name] = target
e.new_parent.exported[e.as_name] = target

continue

Expand Down
18 changes: 11 additions & 7 deletions pydoctor/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,17 @@ class CanContainImportsDocumentable(Documentable):
def setup(self) -> None:
super().setup()
self._localNameToFullName_map: Dict[str, str] = {}
"""
Mapping from local names to fullnames: Powers name resolving.
"""

self.exported: Dict[str, 'Documentable'] = {}
"""
When pydoctor re-export objects, it leaves references to object in this dict
so they can still be listed in childtable of origin modules or classes. This attribute belongs
to the "view model" part of Documentable interface and should only be used to present
links to these objects. Not to do name resolving.
"""

def isNameDefined(self, name: str) -> bool:
name = name.split('.')[0]
Expand Down Expand Up @@ -479,13 +490,6 @@ def setup(self) -> None:
self._docformat: Optional[str] = None

self.imports: List[Import] = []
self.elsewhere_contents: Dict[str, 'Documentable'] = {}
"""
When pydoctor re-export objects, it leaves references to object in this dict
so they can still be listed in childtable of origin modules. This attribute belongs
to the "view model" part of Documentable interface and should only be used to present
links to these objects. Not to do name resolving.
"""

def _localNameToFullName(self, name: str) -> str:
if name in self.contents:
Expand Down
2 changes: 1 addition & 1 deletion pydoctor/templatewriter/pages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ def extras(self) -> List["Flattenable"]:
def _iter_reexported_members(self, predicate: Optional[Callable[[model.Documentable], bool]]=None) -> Iterator[Tuple[str, model.Documentable]]:
if not predicate:
predicate = lambda v:True
return ((n,o) for n,o in self.ob.elsewhere_contents.items() if o.isVisible and predicate(o))
return ((n,o) for n,o in self.ob.exported.items() if o.isVisible and predicate(o))

def children(self) -> Sequence[Union[model.Documentable, Tuple[str, model.Documentable]]]:
return sorted(chain(
Expand Down
34 changes: 28 additions & 6 deletions pydoctor/test/test_astbuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2915,7 +2915,7 @@ class cls2:...
"moving 'pack._src.cls' into 'pack'\n"
"not moving pack.src.cls2 into 'pack', because 'cls2' is already exported in public module 'pack.src'\n")

assert system.allobjects['pack.cls'] is system.allobjects['pack._src'].elsewhere_contents['cls'] # type:ignore
assert system.allobjects['pack.cls'] is system.allobjects['pack._src'].exported['cls'] # type:ignore

@systemcls_param
def test_do_not_reparent_to_existing_name(systemcls: Type[model.System], capsys:CapSys) -> None:
Expand Down Expand Up @@ -2949,7 +2949,7 @@ class Cls:...
"pack:1: duplicate Class 'pack.Slc'\n"
"pack._src1:1: introduced by re-exporting Class 'pack._src1.Slc' into Package 'pack'\n")

assert system.allobjects['pack.Slc'] is system.allobjects['pack._src1'].elsewhere_contents['Slc'] # type:ignore
assert system.allobjects['pack.Slc'] is system.allobjects['pack._src1'].exported['Slc'] # type:ignore

@systemcls_param
def test_multiple_re_exports(systemcls: Type[model.System], capsys:CapSys) -> None:
Expand Down Expand Up @@ -2979,8 +2979,8 @@ class Cls:...
assert capsys.readouterr().out == ("moving 'pack.subpack.src.Cls' into 'pack', "
"also available at 'pack.subpack.Cls'\n")

assert system.allobjects['pack.Cls'] is system.allobjects['pack.subpack'].elsewhere_contents['Cls'] # type:ignore
assert system.allobjects['pack.Cls'] is system.allobjects['pack.subpack.src'].elsewhere_contents['Cls'] # type:ignore
assert system.allobjects['pack.Cls'] is system.allobjects['pack.subpack'].exported['Cls'] # type:ignore
assert system.allobjects['pack.Cls'] is system.allobjects['pack.subpack.src'].exported['Cls'] # type:ignore

@systemcls_param
def test_multiple_re_exports_alias(systemcls: Type[model.System], capsys:CapSys) -> None:
Expand All @@ -3007,5 +3007,27 @@ class DistinguishedName:...
assert capsys.readouterr().out == ("moving 'pack.subpack.src.DistinguishedName' into 'pack' as 'DisName', "
"also available at 'pack.DN'\n")

assert system.allobjects['pack.DisName'] is system.allobjects['pack'].elsewhere_contents['DN'] # type:ignore
assert system.allobjects['pack.DisName'] is system.allobjects['pack.subpack.src'].elsewhere_contents['DistinguishedName'] # type:ignore
assert system.allobjects['pack.DisName'] is system.allobjects['pack'].exported['DN'] # type:ignore
assert system.allobjects['pack.DisName'] is system.allobjects['pack.subpack.src'].exported['DistinguishedName'] # type:ignore

@systemcls_param
def test_re_export_method(systemcls: Type[model.System], capsys:CapSys) -> None:
src = '''\
class Thing:
def method(self):...
method = Thing.method
'''
subpack = ''
pack = '''
from pack.subpack.src import method
__all__=['method']
'''

system = systemcls()
builder = system.systemBuilder(system)
builder.addModuleString(pack, 'pack', is_package=True)
builder.addModuleString(subpack, 'subpack', is_package=True, parent_name='pack')
builder.addModuleString(src, 'src', parent_name='pack.subpack')
builder.buildModules()
assert capsys.readouterr().out == "moving 'pack.subpack.src.Thing.method' into 'pack'\n"

0 comments on commit 1c4343b

Please sign in to comment.