-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature Request] A new utility function returning True only if "autodoc" is currently running #9805
Comments
This patch release delivers obstreperous support for **Sphinx** (*curse ye and yer little side effects too, `autodoc` extension!*), **generator callables, readable exception messages,** and **Python 3.10 CI.** This release resolves **4 issues** and merges **0 pull requests.** Fearsome changes include: ## Compatibility Improved * **Sphinx.** `@beartype` now explicitly provides first-class support for Sphinx's `autodoc` extension (i.e., `sphinx.ext.autodoc`), resolving issue #61, kindly submitted by SeldonIO/alibi ML dev maestro supremum Janis Klaise (@jklaise). `@beartype` now transparently ignores `autodoc`-mocked module attributes (e.g., produced by the `autodoc_mock_imports` list global in Sphinx-specific `doc{s,}/conf.py` configuration files of downstream documentation trees) used as type hints annotating `@beartype`-decorated callables, rendering `@beartype` compatible with `autodoc`-documented codebases. Since Sphinx lacks a public API for detecting when `autodoc` is currently generating documentation (see: sphinx-doc/sphinx#9805), `@beartype` now performs its own ad-hoc detection at decoration time with micro-optimized runtime complexity: * `O(1)` in the common case (i.e., Sphinx's `autodoc` extension has *not* been previously imported under the active Python interpreter). * `O(n)` in the worst case (i.e., Sphinx's `autodoc` extension has been previously imported under the active Python interpreter), where `n` is the height of the call stack leading to `@beartype`. * **Generator callables.** `@beartype` now relaxes the requirement that [a]synchronous generator callables be annotated by [PEP 484][PEP 484] -compliant `typing.{Async,}Generator[...]` or [PEP 585][PEP 585]-compliant `collections.abc.{Async,}Generator[...]` type hints to additionally accept [PEP 484][PEP 484]-compliant `typing.{Async,}Iterable[...]` and `typing.{Async,}Iterator[...]` as well as [PEP 585][PEP 585]-compliant `collections.abc.{Async,}Iterable[...]` and `collections.abc.{Async,}Iterator[...]` type hints, resolving issue #65 kindly submitted by the @posita the positronic brain emitter. ## Features Improved * **Exception message readability.** `@beartype` now emits more human-readable, disambiguous, and useful exception messages. Notably: * `@beartype`-decorated callables annotated by one or more numeric types (e.g., `int`, `float`, `complex`) now raise exceptions on type-checking failures whose exception messages disambiguate those numbers from strings, resolving issue #63 kindly submitted by @jefcolbi. Previously, these messages double-quoted all numbers for disambiguity with the possibly preceding sequence index of those numbers in their parent containers -- which then introduced yet another unanticipated ambiguity between numbers and strings. Numbers are now preserve in their vanilla unquoted form; meanwhile, sequence items are additionally disambiguated from sequence values with additional explanatory substrings. * Edge-case exception messages emitted by our memoized code generator no longer contain the mostly inappropriate substring `"wrapper parameter" *except* where absolutely appropriate. * Annotation-centric exception messages now guaranteeably contain the critical substring `"type hint"`. ## Issues Resolved * **#61,** kindly submitted by SeldonIO/alibi ML dev maestro supremum Janis Klaise (@jklaise). See "Compatibility Improved" above. * **#62,** kindly submitted by Cal Leeming (@foxx), may his serious eyebrow-raising monochrome GitHub avatar be a guiding light to us all. See "Documentation Revised" below. (Thanks again to the foxy @foxx for appraising us all of hitherto unknown typing horrors. And just in time for Halloween too.) * **#63,** kindly submitted by @jefcolbi. See "Features Improved" above. * **#65,** kindly submitted by @posita the positronic brain emitter. See "Compatibility Improved" above. ## Tests Improved * **Python 3.10 CI.** This release resurrects Python 3.10 support in our GitHub Actions-based continuous integration (CI) configuration, circumventing upstream issue actions/setup-python#160 by coercively double-quoting *all* Python version strings specified as the values of "python-version:" keys. *Ya!* ## Documentation Revised * **Reverse dependency showcase.** The introduction of our front-facing `README.rst` documentation now gratefully advertises popular reverse dependencies (i.e., downstream open-source Python projects directly leveraging `beartype`) with a new on-brand icon bar. Gaze upon iconic icons and know the stylish face of unabashed beauty. * **Type hint elision.** The new "Type Hint Connectives" subsection of our front-facing `README.rst` documentation documents various means of circumventing counter-intuitive historicity in Python's core type hierarchy with (*wait for it*) beartype validators, resolving issue #62 kindly submitted by Cal Leeming (@foxx). Specifically, this subsection documents how to effectively declare: * A new PEP-compliant `int - bool` type hint as a beartype validator matching the set of all integers that are *not* booleans. * A new PEP-compliant `Sequence - str` type hint as a beartype validator matching the set of all sequences that are *not* strings. [PEP 484]: https://www.python.org/dev/peps/pep-0484 [PEP 585]: https://www.python.org/dev/peps/pep-0585 (*Unleaded laden jelly-like underbellies!*)
To add another hack to the mix, we could also do the following to find out if our package is currently being imported as part of a Sphinx build: try:
import sphinx
sphinx_build = hasattr(sphinx, 'application')
except ImportError:
sphinx_build = False This takes advantage of the fact that Sphinx imports next to nothing when we just do Or check |
Is your feature request related to a problem? Please describe.
Firstly, my thanks to the tireless Sphinx crew (this means you!) for your dedication to the just cause of exhaustively well-documented Python APIs.
Secondly, hi! I'm @leycec, the author of
@beartype
, Python's constant-time runtime type-checking decorator. You can probably see where this is going already – but let's go there anyway.Like most decorators,
@beartype
is typically applied at module scope. Theautodoc
extension imports the modules it imports, thus implicitly calling@beartype
for each@beartype
-decorated callable. This isn't a problem in and of itself. Problems arise, however, whenever a@beartype
-decorated callable is annotated with one more classes mocked byautodoc_mock_imports
. When that happens,@beartype
raises exceptions at decoration time, because mocking subverts our assumptions and expectations about classes used as annotations.Describe the solution you'd like
A new public utility function
isautodoc()
(or something) in the Sphinx API returningTrue
only ifautodoc
is currently running. Given that, it would be trivial for@beartype
to conditionally reduce to a noop (i.e., the identity decorator returning the passed callable undecorated) whenautodoc
is running.I was quite surprised not to find such a function. There exist a multitude of StackOverflow posts on the subject – but none with anything resembling a satisfactory answer. For example:
Path(argv[0]).name == "sphinx-build" or Path(argv[0]).name == "build.py"
) to detect whether Sphinx is currently running. Of course, that's horrifyingly non-portable, fragile, and overly coarse-grained. Leveraging that test in@beartype
would have the unintended side effect of prohibiting third-party Sphinx extension authors from type-checking their APIs with@beartype
. Moreover, it fails to generalize to build scripts, systems, and process not satisfying that finite list of basenames. This can't be the way.SPHINX_BUILD
,GENERATING_DOCS
) from within aconf.py
configuration and then subsequently detecting that variable from within a decorator. Of course, we can't do that;@beartype
doesn't control third-partyconf.py
configurations.builtins.__sphinx_build__
global dunder attribute (guaranteed to break the back of Python and/or Sphinx at some future date) from within aconf.py
configuration and then subsequently detecting that variable from within a decorator. Of course, we can't do that;@beartype
doesn't control third-partyconf.py
configurations.Indeed, one of the above answers notes that:
In short, nothing works.
Describe alternatives you've considered
I briefly considered the Sphinx Event API. The
autodoc
extension fires off a number ofautodoc
-specific events. In theory,@beartype
could connect an internal event handler to Sphinx's currentapp
singleton on one or more of these events being fired – which would then notify@beartype
that it should then reduce to a noop.Of course, that's horribly convoluted, fragile, and non-trivial for something that should ideally just reduce to a trivial one-liner. I also have no idea how to actually implement that, because I have no idea how to retrieve Sphinx's current
app
singleton. I suppose I could violate sanity by searching up the call stack for a script namedconf.py
and then introspect theapp
attribute out of the stack frame encapsulating that call, but... now we've really violated sanity, because that wouldn't even be portable across Python interpreters.Moreover, there isn't necessarily a one-to-one relationship between
autodoc
-specific events and whether or notautodoc
is currently running. I can easily envision cases in which noautodoc
-specific event fires – yetautodoc
is running.The fragile plenum of the
@beartype
API is now rupturing. 💔Additional context
Thanks again, one and all. We cannot emphasize enough how amazing everyone here is. Viva la reStructuredText revolution! 🎆
The text was updated successfully, but these errors were encountered: