Skip to content

Commit 9628dd6

Browse files
committed
Collect imports as scoped types
"from imports" are collected as types within the scope of the containing module. E.g. "from pathlib import Path" will allow using "Path" inside the modules scope. "import" without "from" are collected as scoped type prefixes. Meaning if a module contains "import scipy as sp", "sp." can be used as a valid type prefix in that module.
1 parent 2bd7076 commit 9628dd6

12 files changed

Lines changed: 262 additions & 74 deletions

File tree

docs/user_guide.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,9 @@ To translate a type from a docstring into a valid type annotation, docstub needs
111111
Out of the box, docstub will know about builtin types such as `int` or `bool` that don't need an import, and types in `typing`, `collections.abc` from Python's standard library.
112112
It will source these from the Python environment it is installed in.
113113
In addition to that, docstub will collect all types in the package directory you are running it on.
114+
This also includes imported types, which you can then use within the scope of the module that imports them.
114115
115-
However, if you want to use types from third-party libraries you can tell docstub about them in a configuration file.
116+
However, you can also tell docstub directly about external types in a configuration file.
116117
Docstub will look for a `pyproject.toml` or `docstub.toml` in the current working directory.
117118
Or, you can point docstub at TOML file(s) explicitly using the `--config` option.
118119
In these configuration file(s) you can declare external types directly with
@@ -134,8 +135,9 @@ ski = "skimage"
134135
135136
which will enable any type that is prefixed with `ski.` or `sklearn.tree.`, e.g. `ski.transform.AffineTransform` or `sklearn.tree.DecisionTreeClassifier`.
136137
137-
In both of these cases, docstub doesn't check that these types actually exist.
138-
Testing the generated stubs with a type checker is recommended.
138+
> [!IMPORTANT]
139+
> Docstub doesn't check that types actually exist or if a symbol is a valid type.
140+
> We always recommend validating the generated stubs with a full type checker!
139141

140142
> [!TIP]
141143
> Docstub currently collects types statically.

examples/example_pkg-stubs/__init__.pyi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ __all__ = [
1010

1111
class CustomException(Exception):
1212
pass
13+
14+
class AnotherType:
15+
pass

examples/example_pkg-stubs/_basic.pyi

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
# File generated with docstub
22

3-
import configparser
43
import logging
54
from collections.abc import Sequence
5+
from configparser import ConfigParser
6+
from configparser import ConfigParser as Cfg
67
from typing import Any, Literal, Self, Union
78

89
from _typeshed import Incomplete
910

10-
from . import CustomException
11+
from . import AnotherType, CustomException
1112

12-
logger: Incomplete
13+
logger: logging.Logger
1314

1415
__all__ = [
1516
"func_empty",
@@ -39,6 +40,7 @@ def func_use_from_elsewhere(
3940
a3: ExampleClass.NestedClass,
4041
a4: ExampleClass.NestedClass,
4142
) -> tuple[CustomException, ExampleClass.NestedClass]: ...
43+
def func_use_from_import(a1: AnotherType, a2: Cfg) -> None: ...
4244

4345
class ExampleClass:
4446

@@ -58,6 +60,6 @@ class ExampleClass:
5860
@some_property.setter
5961
def some_property(self, value: str) -> None: ...
6062
@classmethod
61-
def method_returning_cls(cls, config: configparser.ConfigParser) -> Self: ...
63+
def method_returning_cls(cls, config: ConfigParser) -> Self: ...
6264
@classmethod
63-
def method_returning_cls2(cls, config: configparser.ConfigParser) -> Self: ...
65+
def method_returning_cls2(cls, config: ConfigParser) -> Self: ...

examples/example_pkg/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@
1111

1212
class CustomException(Exception):
1313
pass
14+
15+
16+
class AnotherType:
17+
pass

examples/example_pkg/_basic.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
"""Basic docstring examples.
22
33
Docstrings, including module-level ones, are stripped.
4+
5+
Attributes
6+
----------
7+
logger : logging.Logger
48
"""
59

610
# Existing imports are preserved
711
import logging
12+
from configparser import ConfigParser as Cfg # noqa: F401
813
from typing import Literal
914

15+
from . import AnotherType # noqa: F401
16+
1017
# Assign-statements are preserved
1118
logger = logging.getLogger(__name__) # Inline comments are stripped
1219

@@ -88,6 +95,16 @@ def func_use_from_elsewhere(a1, a2, a3, a4):
8895
"""
8996

9097

98+
def func_use_from_import(a1, a2):
99+
"""Check using symbols made available in this module with from imports.
100+
101+
Parameters
102+
----------
103+
a1 : AnotherType
104+
a2 : Cfg
105+
"""
106+
107+
91108
class ExampleClass:
92109
"""Dummy.
93110

pyproject.toml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,8 @@ run.source = ["docstub"]
119119
".*maintenance.*" = "Maintenance"
120120

121121

122-
[tool.docstub.types]
123-
Path = "pathlib"
124-
125-
[tool.docstub.type_prefixes]
126-
re = "re"
127-
cst = "libcst"
128-
lark = "lark"
129-
numpydoc = "numpydoc"
122+
[tool.docstub.type_nicknames]
123+
Path = "pathlib.Path"
130124

131125

132126
[tool.mypy]

0 commit comments

Comments
 (0)