Skip to content

Commit a5dd8de

Browse files
MarkDaoustcopybara-github
authored andcommitted
Clarify docs for explicit_package_contents_filter.
PiperOrigin-RevId: 438274763
1 parent 63d3f5b commit a5dd8de

File tree

2 files changed

+78
-15
lines changed

2 files changed

+78
-15
lines changed

tools/tensorflow_docs/api_generator/public_api.py

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818
import inspect
1919
import os
2020
import pathlib
21+
import textwrap
22+
import types
2123
import typing
22-
from typing import Any, Callable, List, Sequence, Tuple
24+
from typing import Any, Callable, List, Sequence, Tuple, Union
25+
2326

2427
from tensorflow_docs.api_generator import doc_controls
2528
from tensorflow_docs.api_generator import doc_generator_visitor
@@ -167,7 +170,7 @@ def util_2
167170
return filtered_children
168171

169172

170-
def _get_imported_symbols(obj):
173+
def _get_imported_symbols(obj: Union[str, types.ModuleType]):
171174
"""Returns a list of symbol names imported by the given `obj`."""
172175

173176
class ImportNodeVisitor(ast.NodeVisitor):
@@ -177,15 +180,24 @@ def __init__(self):
177180
self.imported_symbols = []
178181

179182
def _add_imported_symbol(self, node):
180-
self.imported_symbols.extend([alias.name for alias in node.names])
183+
for alias in node.names:
184+
name = alias.asname or alias.name
185+
if name == '*':
186+
continue
187+
if '.' in name:
188+
continue
189+
self.imported_symbols.append(name)
181190

182191
def visit_Import(self, node): # pylint: disable=invalid-name
183192
self._add_imported_symbol(node)
184193

185194
def visit_ImportFrom(self, node): # pylint: disable=invalid-name
186195
self._add_imported_symbol(node)
187196

188-
source = get_source.get_source(obj)
197+
if isinstance(obj, str):
198+
source = textwrap.dedent(obj)
199+
else:
200+
source = get_source.get_source(obj)
189201
if source is None:
190202
return []
191203

@@ -197,21 +209,57 @@ def visit_ImportFrom(self, node): # pylint: disable=invalid-name
197209

198210
def explicit_package_contents_filter(path: Sequence[str], parent: Any,
199211
children: Children) -> Children:
200-
"""Filter modules to only include explicit contents.
201-
202-
This function returns the children explicitly included by this module, meaning
203-
that it will exclude:
212+
"""Filter submodules, only keep what's explicitly included.
204213
205-
* Modules in a package not explicitly imported by the package (submodules
206-
are implicitly injected into their parent's namespace).
207-
* Modules imported by a module that is not a package.
214+
This filter only affects the visibility of **modules**. Other objects are not
215+
affected.
208216
209217
This filter is useful if you explicitly define your API in the packages of
210-
your library, but do not expliticly define that API in the `__all__` variable
211-
of each module. The purpose is to make it easier to maintain that API.
218+
your library (the __init__.py files), but do not expliticly define that API
219+
in the `__all__` variable of each module. The purpose is to make it easier to
220+
maintain that API.
221+
222+
**This filter makes it so that modules are only documented where they are
223+
explicitly imported in an __init__.py**
224+
225+
### Packages
226+
227+
Lots of imports **indirectly** inject modules into package namespaces, this
228+
filter helps you ignore those. Anywhere you `import pkg.sub1` it will inject
229+
`sub1` into the `pkg` namsspace.
230+
231+
When filtering a package it only keeps modules that are **directly**
232+
impotrted in the package. This code, injects `[sub0, sub1, sub2, sub3, sub4,
233+
sub_sub1, *]` into the pkg namespace:
234+
235+
pkg/__init__.py
236+
237+
```
238+
import sub0
239+
import pkg.sub1
240+
from pkg import sub2
241+
from pkg.sub3 import sub_sub1
242+
from pkg.sub4 import *
243+
```
244+
245+
But this filter will only keep the modules `[sub0, sub2, sub_sub1]` in the
246+
docs for `pkg`.
247+
248+
### Regular modules
249+
250+
For regular modules all modules in the namespace are assumed to be
251+
implementation details and/or documented in their source location. For example
252+
in this package:
253+
254+
```
255+
pkg/
256+
__init__.py
257+
sub1.py
258+
sub2.py
259+
```
212260
213-
Note: This filter does work with wildcard imports, however it is generally not
214-
recommended to use wildcard imports.
261+
If you `import sub2` in `__init__.py` `sub2` will documented in `pkg`
262+
But if you `import sub2` in `sub1.py` `sub2` will not be documented in `sub1`
215263
216264
Args:
217265
path: A tuple of names forming the path to the object.

tools/tensorflow_docs/api_generator/public_api_test.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,21 @@ def test_explicit_package_contents_filter_removes_modules_imported_by_modules(
172172
# Assert that the filtered_members do not include a module named `inspect`.
173173
self.assertNotIn('inspect', [name for name, _ in filtered_members])
174174

175+
def test_get_imported_symbols(self):
176+
source = """
177+
import sub0
178+
import pkg.sub1
179+
from pkg import sub2
180+
from pkg.sub3 import sub_sub1
181+
from pkg.sub4 import *
182+
from pkg import sub5 as r1
183+
from pkg import sub6 as r2, sub7, sub8 as r3
184+
185+
"""
186+
imported = public_api._get_imported_symbols(source)
187+
self.assertCountEqual(
188+
['sub0', 'sub2', 'sub_sub1', 'r1', 'r2', 'sub7', 'r3'], imported)
189+
175190
def test_ignore_typing(self):
176191
children_before = [('a', 1), ('b', 3), ('c', typing.List)]
177192
children_after = public_api.ignore_typing('ignored', 'ignored',

0 commit comments

Comments
 (0)