@@ -282,6 +282,7 @@ class _StubVisitor(ast.NodeVisitor):
282
282
def __init__ (self ):
283
283
self ._submodules = set ()
284
284
self ._submod_attrs = {}
285
+ self ._all = None
285
286
286
287
def visit_ImportFrom (self , node : ast .ImportFrom ):
287
288
if node .level != 1 :
@@ -300,6 +301,39 @@ def visit_ImportFrom(self, node: ast.ImportFrom):
300
301
else :
301
302
self ._submodules .update (alias .name for alias in node .names )
302
303
304
+ def visit_Assign (self , node : ast .Assign ):
305
+ assigned_list = None
306
+ for name in node .targets :
307
+ if name .id == "__all__" :
308
+ assigned_list = node .value
309
+
310
+ if assigned_list is None :
311
+ return # early
312
+ elif not isinstance (assigned_list , ast .List ):
313
+ msg = (
314
+ f"expected a list assigned to `__all__`, found { type (assigned_list )!r} "
315
+ )
316
+ raise ValueError (msg )
317
+
318
+ if self ._all is not None :
319
+ msg = "expected only one definition of `__all__` in stub"
320
+ raise ValueError (msg )
321
+ self ._all = set ()
322
+
323
+ for constant in assigned_list .elts :
324
+ if (
325
+ not isinstance (constant , ast .Constant )
326
+ or not isinstance (constant .value , str )
327
+ or assigned_list == ""
328
+ ):
329
+ msg = (
330
+ "expected `__all__` to contain only non-empty strings, "
331
+ f"got { constant !r} "
332
+ )
333
+ raise ValueError (msg )
334
+ self ._all .add (constant .value )
335
+
336
+
303
337
304
338
def attach_stub (package_name : str , filename : str ):
305
339
"""Attach lazily loaded submodules, functions from a type stub.
@@ -308,6 +342,10 @@ def attach_stub(package_name: str, filename: str):
308
342
infer ``submodules`` and ``submod_attrs``. This allows static type checkers
309
343
to find imports, while still providing lazy loading at runtime.
310
344
345
+ If the stub file defines `__all__`, it must contain a simple list of
346
+ non-empty strings. In this case, the content of `__dir__()` may be
347
+ intentionally different from `__all__`.
348
+
311
349
Parameters
312
350
----------
313
351
package_name : str
@@ -339,4 +377,10 @@ def attach_stub(package_name: str, filename: str):
339
377
340
378
visitor = _StubVisitor ()
341
379
visitor .visit (stub_node )
342
- return attach (package_name , visitor ._submodules , visitor ._submod_attrs )
380
+
381
+ __getattr__ , __dir__ , __all__ = attach (
382
+ package_name , visitor ._submodules , visitor ._submod_attrs
383
+ )
384
+ if visitor ._all is not None :
385
+ __all__ = visitor ._all
386
+ return __getattr__ , __dir__ , __all__
0 commit comments