Skip to content

Commit 5dead3e

Browse files
authored
Add logging-based error reporting (#79)
Improve output and error reporting. All output of docstub is now controllable with the existing `--verbose` option and the new `--quiet` option. Also indicate the (severity) level of output with a single char, e.g. "I" for information or "W" for warnings. Refactor reporting to use Python's native logging implementation.
1 parent bb7bc2d commit 5dead3e

File tree

15 files changed

+839
-493
lines changed

15 files changed

+839
-493
lines changed

docs/command_line.md

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,25 +37,30 @@ Usage: docstub run [OPTIONS] PACKAGE_PATH
3737
annotations or to override them.
3838
3939
Options:
40-
-o, --out-dir PATH Set output directory explicitly. Stubs will be directly
41-
written into that directory while preserving the
42-
directory structure under `PACKAGE_PATH`. Otherwise,
43-
stubs are generated inplace.
44-
--config PATH Set one or more configuration file(s) explicitly.
45-
Otherwise, it will look for a `pyproject.toml` or
46-
`docstub.toml` in the current directory.
47-
--ignore GLOB Ignore files matching this glob-style pattern. Can be
48-
used multiple times.
49-
--group-errors Group identical errors together and list where they
50-
occurred. Will delay showing errors until all files have
51-
been processed. Otherwise, simply report errors as the
52-
occur.
53-
--allow-errors INT Allow this many or fewer errors. If docstub reports
54-
more, exit with error code '1'. This is useful to adopt
55-
docstub gradually. [default: 0; x>=0]
56-
--no-cache Ignore pre-existing cache and don't create a new one.
57-
-v, --verbose Print more details (repeatable).
58-
-h, --help Show this message and exit.
40+
-o, --out-dir PATH Set output directory explicitly. Stubs will be
41+
directly written into that directory while preserving
42+
the directory structure under `PACKAGE_PATH`.
43+
Otherwise, stubs are generated inplace.
44+
--config PATH Set one or more configuration file(s) explicitly.
45+
Otherwise, it will look for a `pyproject.toml` or
46+
`docstub.toml` in the current directory.
47+
--ignore GLOB Ignore files matching this glob-style pattern. Can be
48+
used multiple times.
49+
--group-errors Group identical errors together and list where they
50+
occurred. Will delay showing errors until all files
51+
have been processed. Otherwise, simply report errors
52+
as the occur.
53+
--allow-errors INT Allow this many or fewer errors. If docstub reports
54+
more, exit with error code '1'. This is useful to
55+
adopt docstub gradually. [default: 0; x>=0]
56+
-W, --fail-on-warning Return non-zero exit code when a warning is raised.
57+
Will add to '--allow-errors'.
58+
--no-cache Ignore pre-existing cache and don't create a new one.
59+
-v, --verbose Print more details. Use once to show information
60+
messages. Use '-vv' to print debug messages.
61+
-q, --quiet Print less details. Use once to hide warnings. Use
62+
'-qq' to completely silence output.
63+
-h, --help Show this message and exit.
5964
```
6065

6166
<!--- end cli-docstub-run --->
@@ -75,7 +80,10 @@ Usage: docstub clean [OPTIONS]
7580
one exists, remove it.
7681
7782
Options:
78-
-v, --verbose Print more details (repeatable).
83+
-v, --verbose Print more details. Use once to show information messages.
84+
Use '-vv' to print debug messages.
85+
-q, --quiet Print less details. Use once to hide warnings. Use '-qq' to
86+
completely silence output.
7987
-h, --help Show this message and exit.
8088
```
8189

src/docstub/_analysis.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from ._utils import accumulate_qualname, module_name_from_path, pyfile_checksum
1616

17-
logger = logging.getLogger(__name__)
17+
logger: logging.Logger = logging.getLogger(__name__)
1818

1919

2020
def _shared_leading_qualname(*qualnames):
@@ -104,6 +104,18 @@ def typeshed_Incomplete(cls):
104104
return import_
105105

106106
def format_import(self, relative_to=None):
107+
"""Format import as valid Python import statement.
108+
109+
Parameters
110+
----------
111+
relative_to : str, optional
112+
If a dot-delimited module name is given, format the import relative
113+
to it.
114+
115+
Returns
116+
-------
117+
formatted : str
118+
"""
107119
if self.implicit:
108120
msg = f"cannot import implicit object: {self.implicit!r}"
109121
raise RuntimeError(msg)
@@ -265,20 +277,6 @@ def common_known_types():
265277
return types
266278

267279

268-
@dataclass(slots=True, kw_only=True)
269-
class TypeCollectionResult:
270-
types: dict[str, PyImport]
271-
type_prefixes: dict[str, PyImport]
272-
273-
@classmethod
274-
def serialize(cls, result):
275-
pass
276-
277-
@classmethod
278-
def deserialize(cls, result):
279-
pass
280-
281-
282280
class TypeCollector(cst.CSTVisitor):
283281
"""Collect types from a given Python file.
284282
@@ -296,7 +294,13 @@ class TypeCollector(cst.CSTVisitor):
296294
"""
297295

298296
class ImportSerializer:
299-
"""Implements the `FuncSerializer` protocol to cache `TypeCollector.collect`."""
297+
"""Implements the `FuncSerializer` protocol to cache `TypeCollector.collect`.
298+
299+
Attributes
300+
----------
301+
suffix : ClassVar[str]
302+
encoding : ClassVar[str]
303+
"""
300304

301305
suffix = ".json"
302306
encoding = "utf-8"
@@ -524,7 +528,7 @@ def _resolve_nickname(self, name):
524528
resolved = name
525529
else:
526530
logger.warning(
527-
"reached limit while resolving nicknames for %r in %s, using %r",
531+
"Reached limit while resolving nicknames for %r in %s, using %r",
528532
original,
529533
self.current_file or "<file not known>",
530534
resolved,
@@ -572,7 +576,7 @@ def match(self, search):
572576
"%r (original %r) in %s matches multiple types %r, using %r",
573577
search,
574578
original_search,
575-
self.current_file or "<file not known>",
579+
self.current_file or "<file?>",
576580
matches.keys(),
577581
shortest_key,
578582
)

src/docstub/_cache.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,21 @@
22
from functools import cached_property
33
from typing import Any, Protocol
44

5-
logger = logging.getLogger(__name__)
5+
logger: logging.Logger = logging.getLogger(__name__)
66

77

8-
CACHE_DIR_NAME = ".docstub_cache"
8+
CACHE_DIR_NAME: str = ".docstub_cache"
99

1010

11-
CACHEDIR_TAG_CONTENT = """\
11+
CACHEDIR_TAG_CONTENT: str = """\
1212
Signature: 8a477f597d28d172789f06886806bc55
1313
# Mark this directory as a cache [1], created by docstub [2]
1414
# [1] https://bford.info/cachedir/
1515
# [2] https://github.com/scientific-python/docstub
1616
"""
1717

1818

19-
GITHUB_IGNORE_CONTENT = """\
19+
GITHUB_IGNORE_CONTENT: str = """\
2020
# Make git ignore this cache directory, created by docstub [1]
2121
# [1] https://github.com/scientific-python/docstub
2222
*
@@ -172,7 +172,7 @@ def cache_dir(self):
172172
create_cache(self._cache_dir)
173173

174174
if _directory_size(self._cache_dir) > 512 * 1024**2:
175-
logger.warning("cache size at %r exceeds 512 MiB", self._cache_dir)
175+
logger.warning("Cache size at %r exceeds 512 MiB", self._cache_dir)
176176

177177
return self._cache_dir
178178

0 commit comments

Comments
 (0)