-
-
Notifications
You must be signed in to change notification settings - Fork 57
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] Automagical Import Hook #43
Comments
Having this usable as a PyTest extension to decorate all PyTest testsuite calls like TypeGuard does would be automagical. One of our projects used to use Typeguard's PyTest setup for the CI, but we had to drop it because it was too slow with our PyTorch tests. |
Oh. Oh, yes. Thanks for the instructive heads up on typeguard's I did notice A I've added yet another feature request tracking a pytest extension so I feel slightly less overwhelmed. It probably won't help. 😅 |
This commit is the first in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit crudely outlines the new `beartype.claw` subpackage housing this API and a new private `beartype.claw._clawcore` submodule of that subpackage implementing this API. Naturally, nothing works. Unrelatedly, this commit also disables previously enabled testing of pre-release builds of Python 3.11 under our GitHub Actions-based continuous integration (CI) workflow. Why? Because doing so foolishly attempts to compile NumPy from source, which unsurprisingly fails with catastrophic fireworks. (*Inimitable indomitable table!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit internally describes a possible avenue forward resolving all outstanding issues with import hook-based abstract syntax tree (AST) transformations performed by other competing packages (e.g., `typeguard`, `ideas`). Unrelatedly, this commit yet again disables previously enabled testing of pre-release builds of Python 3.11 under our GitHub Actions-based continuous integration (CI) workflow. Why? Because doing so foolishly attempts to compile NumPy from source, which unsurprisingly fails with catastrophic fireworks. (*Descriptively proactive proscription!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit defines a new private PEP 302- and 451-compliant `beartype.claw._clawcore._BeartypeSourceFileLoader` class compatible with standard `importlib` machinery for specifying import path hooks. Naturally, nothing works. (*Tacit tactics!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit: * Defines a new private `beartype.claw._clawast` submodule intended to house our our as-yet-undefined abstract syntax tree (AST) transformation. * Renames `beartype.claw._clawcore` to `beartype.claw._clawimportlib` for disambiguity. * Begins implementing our previously defined `beartype.claw._clawimportlib._BeartypeSourceFileLoader` class. Notably, the `source_to_code()` method now sports a reasonable facsimile of a working implementation invoking our as-yet-undefined abstract syntax tree (AST) transformation. (*Discretionary n-ary diversions!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit: * Renames the existing private `beartype._util.func.utilfuncstack` submodule to `utilfuncframe` as well as all functions declared by that submodule for clarity and readability. * Documents a medley of proposed improvements to the existing private non-working `beartype.claw._clawimportlib` submodule, particularly with respect to rendering the currently mandatory `package_names` parameter accepted by the public `beartype_package_submodules_when_imported()` function optional. (*Acyclicly tricky tricycles!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit: * Renames a sweeping number of unrelated private submodules for disambiguity, clarity, and usability. * Extensively documents the private `beartype.claw._clawimportlib._package_basename_to_subpackages` global internally backing this API. * Renders the ``package_names`` parameter accepted by this API optional through the magic of magical call stack inspection. * Validates the same parameter with extensive manual type-checking. * Documents further prospective changes needed to fully realize this tumescent dream of automated ``O(1)`` runtime type-checking. (*Effervescent scent of verdant fervency!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit: * Isolates all private functionality pertaining to package name caching to a new `beartype.claw._clawpackagenames` submodule. * In this submodule: * Defines a new `register_package_names()` function caching the passed package names and associated beartype configuration for subsequent thread-safe lookup by beartype import path hooks. * Defines a new `_PackageBasenameToSubpackagesDict` subclass embodying each item of the recursive nested dictionary structure caching these package names and associated beartype configuration. (*Wayward awkward skyward ward city!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit finalizes a draft implementation of our newly implemented `beartype.claw._clawpackagenames.register_package_names()` function. (*Humbly bumbling bumblebee!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit: * Finalizes a draft implementation of the public `beartype.claw._clawimportlib.beartype_submodules_on_import()` function, which now (purports to) install a beartype import path hook implicitly decorating all callables defined by all submodules defined by all packages with the passed names in an optimally efficient manner. And... we're spent. * Monkey-patches the private `beartype.claw._clawimportlib._BeartypeSourceFileLoader.get_code()` method to apply a beartype-specific optimization marker. (*Undecidable quandaries of unquantifiable friable friars without boundaries!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit extensively documents the mostly implemented private `beartype.claw._clawimportlib._BeartypeSourceFileLoader` class core to our import hook implementation. (*Derogatory interrogatory purgatory!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit: * Defines a new public `beartype.claw.beartype_all()` hook intended to be called only from proprietary application stacks. * Splits our prior private `beartype.claw._clawimportlib` submodule into two private `beartype.claw._claw{loader,pathhooks}` submodules. (*Eminent immanence feeds impertinent impermanence!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit (yet again) mostly just documents a path forward through the dark machinations of the `importlib` module's machinery. (*Abiotic allometric allergen!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit defines a new private `beartype.claw._clawpackagenames.is_package_registered()` tester enabling our higher-level private `beartype.claw._clawloader.BeartypeSourceFileLoader` class to decide whether the module currently being loaded should be subject to implicit `@beartype` decoration or not. (*Saucy innocuous insouciance!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit: * Renames a number of private submodules for disambiguity. * Finalizes our draft implementations of the private `beartype.claw._clawloader` and `beartype.claw._clawregistrar` submodules. * Defines a new private `beartype._decor.decorcore.beartype_object_safe` decorator reducing all fatal exceptions raised by the `@beartype` decorator to non-fatal warnings, intended to be applied *only* when recursively applying `@beartype` to an entire package at importation time. Doing so should substantially reduce the fragility of our import hook API... in theory. (*Coddled codpiece? Codswallop!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit finalizes our draft implementations of the private `beartype.claw._clawast` submodule. The only remaining portion of this API to be implemented is the related private `@beartype._decor.decorcore.beartype_object_safe` decorator; while technically implemented, output emitted by that decorator is currently unsuitable for use in large codebases. Nearly there! (*Dearly hair!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit: * Finalizes our draft implementation of the public `beartype.claw.beartype_all()` function. * Continues implementing our private `@beartype._decor.decorcore.beartype_object_safe` decorator, which remains woefully inadequate against real-world errors. (*Jocular gesticulation!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit defines a new private `beartype._util.mod.utilmodget.get_object_module_line_number_begin()` retrieving the line number at which the passed callable or class is defined in its parent module, which our private `@beartype._decor.decorcore.beartype_object_safe` decorator will subsequently embed in warning messages. (*Plangent plan, gentlemen!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit generalizes our existing private `beartype._util.text.utiltextlabel.label_callable()` function with an optional `is_contextualized` parameter -- which, when enabled, contextualizes the returned label with the filename and line number declaring the passed callable. (*Tentative representative!*)
This commit is the next in a commit chain defining our new **all-at-once API** (i.e., an import hook enabling external callers to trivially decorate well-typed third-party packages and modules with runtime type-checking dynamically generated by the `beartype.beartype` decorator in a single line of code), en-route to resolving issue #43 kindly submitted by sentimental investor extraordinaire @zeevox. Specifically, this commit improves unit tests exercising our `beartype._util.text.utiltextlabel.label_callable()` function with improved coverage over noteworthy edge cases – including coroutine, asynchronous generator, and synchronous generator factory callables. (*Fractuous fractalizations distil the tillerman's generalizations!*)
So, I'm not sure if this is a bug, or if I'm just doing something wrong - I haven't used beartype before (or I did, and forgot?), and thought I'd give it a try on qutebrowser. For a full reproducer:
results in: /home/florian/tmp/qutebrowser/.venv/lib/python3.11/site-packages/beartype/_util/hint/pep/utilpeptest.py:311: BeartypeDecorHintPep585DeprecationWarning: PEP 484 type hint typing.Dict[str, str] deprecated by PEP 585. This hint is scheduled for removal in the first Python version released after October 5th, 2025. To resolve this, import this hint from "beartype.typing" rather than "typing". For further commentary and alternatives, see also:
https://beartype.readthedocs.io/en/latest/api_roar/#pep-585-deprecations
warn(
Traceback (most recent call last):
File "/home/florian/tmp/qutebrowser/.venv/lib/python3.11/site-packages/beartype/door/_doorcheck.py", line 147, in die_if_unbearable
_check_object(obj)
File "<@beartype(beartype.door._doorcheck._get_type_checker._die_if_unbearable) at 0x7efc2e9ad940>", line 18, in _die_if_unbearable
beartype.roar.BeartypeCallHintReturnViolation: Function beartype.door._doorcheck._get_type_checker._die_if_unbearable() return "Field(name=None,type=None,default=<dataclasses._MISSING_TYPE object at 0x7efc2ea7f390>,defa..._ violates type hint typing.Dict[str, str] under non-default configuration BeartypeConf(claw_is_pep526=True, is_color=None, is_debug=False, is_pep484_tower=False, reduce_decorator_exception_to_warning_category=<class 'beartype.roar._roarwarn.BeartypeClawDecorWarning'>, strategy=<BeartypeStrategy.O1: 2>), as <class "dataclasses.Field"> "Field(name=None,type=None,default=<dataclasses._MISSING_TYPE object at 0x7efc2ea7f390>,defa..._ not instance of dict.
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/home/florian/tmp/qutebrowser/qutebrowser/__main__.py", line 23, in <module>
import qutebrowser.qutebrowser
File "/home/florian/tmp/qutebrowser/qutebrowser/qutebrowser.py", line 54, in <module>
from qutebrowser.misc import earlyinit
File "/home/florian/tmp/qutebrowser/qutebrowser/misc/earlyinit.py", line 47, in <module>
from qutebrowser.qt import machinery
File "/home/florian/tmp/qutebrowser/qutebrowser/qt/machinery.py", line 86, in <module>
class SelectionInfo:
File "/home/florian/tmp/qutebrowser/qutebrowser/qt/machinery.py", line 90, in SelectionInfo
outcomes: Dict[str, str] = dataclasses.field(default_factory=dict)
File "/home/florian/tmp/qutebrowser/.venv/lib/python3.11/site-packages/beartype/door/_doorcheck.py", line 177, in die_if_unbearable
raise BeartypeDoorHintViolation(
beartype.roar.BeartypeDoorHintViolation: Object "Field(name=None,type=None,default=<dataclasses._MISSING_TYPE object at 0x7efc2ea7f390>,defa..._ violates type hint typing.Dict[str, str] under non-default configuration BeartypeConf(claw_is_pep526=True, is_color=None, is_debug=False, is_pep484_tower=False, reduce_decorator_exception_to_warning_category=<class 'beartype.roar._roarwarn.BeartypeClawDecorWarning'>, strategy=<BeartypeStrategy.O1: 2>), as <class "dataclasses.Field"> "Field(name=None,type=None,default=<dataclasses._MISSING_TYPE object at 0x7efc2ea7f390>,defa..._ not instance of dict. which got me stumped. For one, But also, it points here: which seems to be correct... I tried extracting things into a more minimal example, by creating a import beartype.claw
beartype.claw.beartype_all() and an import dataclasses
import enum
from typing import Dict, Optional
class SelectionReason(enum.Enum):
"""Reasons for selecting a Qt wrapper."""
#: The wrapper was selected via --qt-wrapper.
cli = "--qt-wrapper"
#: The wrapper was selected via the QUTE_QT_WRAPPER environment variable.
env = "QUTE_QT_WRAPPER"
#: The wrapper was selected via autoselection.
auto = "autoselect"
#: The default wrapper was selected.
default = "default"
#: The wrapper was faked/patched out (e.g. in tests).
fake = "fake"
#: The reason was not set.
unknown = "unknown"
@dataclasses.dataclass
class SelectionInfo:
"""Information about outcomes of importing Qt wrappers."""
wrapper: Optional[str] = None
outcomes: Dict[str, str] = dataclasses.field(default_factory=dict)
reason: SelectionReason = SelectionReason.unknown
s = SelectionInfo() and then running Traceback (most recent call last):
File "/home/florian/tmp/.venv/lib/python3.11/site-packages/beartype/claw/_importlib/clawimpcache.py", line 101, in __getitem__
return super().__getitem__(module_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyError: '__main__'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/home/florian/tmp/pkg/x.py", line 5, in <module>
class SelectionReason(enum.Enum):
File "/home/florian/tmp/.venv/lib/python3.11/site-packages/beartype/claw/_importlib/clawimpcache.py", line 105, in __getitem__
raise BeartypeClawImportConfException(
beartype.roar.BeartypeClawImportConfException: Beartype configuration associated with module "__main__" hooked by "beartype.claw" import hooks not found. Existing beartype configurations associated with such modules include:
{'pkg.x': BeartypeConf(claw_is_pep526=True, is_color=None, is_debug=False, is_pep484_tower=False, reduce_decorator_exception_to_warning_category=<class 'beartype.roar._roarwarn.BeartypeClawDecorWarning'>, strategy=<BeartypeStrategy.O1: 2>)} |
You are getting the same issue I had with pydantic, but pydantic uses
It's all fine and dandy but it's just misleading the static type checker. The type returned is always, always Since dataclasses doesn't provide the annotated option, I think the only way to fix the dataclass type mistake in the stdlib and declare the real types is this: from dataclasses import dataclass, field, Field
@dataclass
class MyClass:
my_list: list[str] | Field = field(default_factory=list) @leycec this could also go in the bearpedia I believe |
OMG. The
And so much more unexpected madness. I'm eternally grateful, everyone! I can confidently say that @beartype 0.15.0 is now due by September 2057 at the latest. My forehead is crinkling. 😦 |
@The-Compiler: Brilliantness! Forcefully ramming This also gives me a reasonable release milestone for @beartype 0.15.0. Specifically, @beartype 0.15.0 will be ready when ...the fascinating traceback you submitted for your minimal-length |
This commit is the next in a commit chain resolving all outstanding issues with our currently unpublished `beartype.claw` API, en-route to resolving feature request #43 kindly submitted a literal lifetime ago by @zeevox Beeblebrox the gentle typing giant. Specifically, this commit resolves yet another horrible microissue discovered by @qutebrowser maestro @The-Compiler (Florian Bruhin) preventing `beartype.claw` from properly hooking the **main module** (i.e., the first entry-point imported into the active Python interpreter, typically via a command resembling `python -m {muh_package}.{muh_module}`). Okay... so, that's actually a megaissue. All signs are green for community testing by the unruly mob squatting on the front lawn outside the filthy bear cave. Clean this cave, unruly mob! (*Bulbous Mandelbulb in a flighty lightbulb!*)
@The-Compiler: Commit 15291ba resolves the unseemly Up Next, @leycec Self-flagellates Himself...is what I'd say if I had an itchy burlap sack with which to self-flagellate myself. Thankfully, I don't. Therefore, my next action item is to instead hack on As @gabrieldemarmiesse wisely observed, the standard from dataclasses import dataclass, field
from typing import Union
from typing_extensions import Annotated
from dataclass_wizard import property_wizard
@dataclass
class Vehicle(metaclass=property_wizard):
wheels: Annotated[Union[int, str], field(default=4)] # <-- woah Still, nobody should have to require yet another third-party dependency just to get |
@leycec I haven't tested yet, but it's very likely that you will get similar errors with sqlalchemy annotations, they use a similar syntax: class Address(Base):
__tablename__ = "address"
id = Column(Integer, primary_key=True)
user_id: int = Column(ForeignKey("user.id"))
user: User = relationship(User) |
ohgodsohgods. Head-pounding pain continues and @leycec continues clutching his head. 🤧 Thanks so much for that rapid heads-up, though. As always, @gabrieldemarmiesse delivers! And what is it with all these popular APIs (including the standard I suspect the latter. If ignorance is bliss, I now wish to become re-ignorant. |
Fascinating. At least in the case of SQLAlchemy, it would thankfully seem that SQLAlchemy's older PEP-noncompliant syntax is officially deprecated and to be removed shortly:
Instead, SQLAlchemy now mandates use of supposedly PEP-compliant type hints ala: from typing import List
from typing import Optional
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "user_account"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(30), nullable=False)
fullname: Mapped[Optional[str]] = mapped_column(String)
addresses: Mapped[List["Address"]] = relationship("Address", back_populates="user")
class Address(Base):
__tablename__ = "address"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
email_address: Mapped[str] = mapped_column(String, nullable=False)
user_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"), nullable=False)
user: Mapped["User"] = relationship("User", back_populates="addresses") Greatness! The only obvious question now is:
Is My face continues sighing dolefully. 😮💨 |
You're humbled? I'm... uuuh... humbleder! Note I don't have any plans to ship beartype to qutebrowser users - but I'd perhaps consider enabling it when running the testsuite. For now, it all remains a giant experiment! As for...
I have no idea what sqlalchemy's However, as for
and apparently a Welp. Maybe you had a "this shouldn't be too hard!" moment when starting beartype, as I had with qutebrowser when I started it. I, for one, commend your efforts on competing with the big type checkers in this space! |
My opinion is my own on this, but I would totally understand if the first version of beartype.claw didn't support those weird default argument for dataclasses. A workaround is available for users in every library and raising awareness about this very common bad typing pattern seems like a good thing to me. If no one complains about this issue about dataclasses, it will never change and it will continue to confuse newcomers and type checker maintainers forever. I have very high hopes for beartype.claw as a CI tool. And if it gets the success it deserves, I believe people will change their code so that the type hints are actually correct. In our work codebase I already fixed 50+ type errors thanks to beartype.claw and 3 real bugs. This has a lot of potential. Mypy has too many false positive, beartype.claw has none. Fastapi is actively pushing for this, I believe pydantic will too, we can also do our part to raise awareness by raising an exception and provide a very helpful error message with examples and documentation links. If this goes well, the standard library dataclasses can even deprecate the |
Wowza! Thank you both so much, @The-Compiler and @gabrieldemarmiesse, for being so awesome and accepting of @beartype's rapidly accumulating pile of festering bugs that suspiciously resemble alien facehuggers when you squint at them shortly before running. I spent the better part of a dangerous bicycle trip through the uncharted Canadian wilderness contemplating this topic rather than watching for man-eating fishers and wolverines. My conclusion is...
muh_local: int = 0xBABECAFE # <-- is this...
from dataclasses import dataclass
@dataclass
class MuhDataclass(object):
muh_field: int = field(default=0xCAFEBABE) # <-- ...*really* the same as this? From the low-level perspective of abstract syntax tree (AST) transformations fuelled by the secretive and deadly art of From the higher-level perspective of sanity, semantics, B-b-but... What About Type-checking Dataclass Fields?Yup. Type-checking dataclass fields is absolutely something that a future version of @beartype should do. Indeed, @beartype 0.14.1 already type-checks dataclass fields at But that's sorta outside the scope of |
This commit is the next in a commit chain resolving all outstanding issues with our currently unpublished `beartype.claw` API, en-route to resolving feature request #43 kindly submitted a literal lifetime ago by @zeevox Beeblebrox the gentle typing giant. Specifically, this commit *hopefully* resolves a veritably vomitous slew of horrible microissues simultaneously uncovered by both @qutebrowser maestro @The-Compiler (Florian Bruhin) *and* Python-on-Whales mastermind @gabrieldemarmiesse (Gabriel de Marmiesse) preventing `beartype.claw` from properly hooking modules containing **dataclasses** (i.e., PEP 557-compliant dataclasses decorated by the standard `@dataclasses.dataclass` decorator as well as PEP 681-compliant third-party dataclasses decorated by the equally standard `@typing.dataclass_transform` decorator) that contain one or more **annotated dataclass fields** (i.e., class variables whose values are instances of the `dataclass.field` type and whose types are annotated with PEP-compliant type hints). If anyone actually read this, please confirm with an animated GIF of a jiggling bear on the @beartype issue tracker. (*Hospitable spit on a hospital table!*)
Ah-ha! Rejoice, bear fellows, for it is Summer. The Sun is sweltering, the water is warm, and GitHub is banging. @leycec's back with a brand new commit promising to resolve some – possibly even many, but doubtfully all – of your pressing issues with the oppressive Let's type this: pip install git+https://github.com/beartype/beartype.git@7bfa5c61598a21b6024ba1ab43a0a0bc76818b88 Specifically, this commit relaxes @beartype's prior death-grip on dataclasses. Whether standard Back to the fun stuff! @The-Compiler, you said this fun stuff...
ohthankgods I was sweating bullets there, thinking that I'd accidentally destroyed my favourite browser by my own ill-gotten hands. Dear beloved users: Please do not embed Of course, I myself will violate my own sound advice by embedding
This is the way.
I am still having that moment. 🤣 🤭 😭 It all seemed so simple, once. "Just a hundred lines of code, innit?" I said. "What could possibly go wrong?" they said. I am here to tell you that some dreams are better left under the pillow. Qutebrowser also probably seemed so simple, once. By harnessing the ferocious browser power of Honestly, I was incredibly impressed by qutebrowser a decade ago. I'm even more impressed now. Against all economic odds, against the mainstream grain, you've hand-built a part-time – and possibly someday full-time – career around your life's passion. You win GitHub.
They will all submit to the Bear! Okay. They probably won't. We'll probably be left biting the dust of their exhaust trails as the proudly jet ahead under profitable corporate and non-profit funding models while I toil quietly in the dark man cave that has yet to be cleaned in over a decade. But... seriously. I can't stand those big type-checkers. They may be big, but they either lie profusely about everything (mypy, I'm imagining you trying to use vanilla Firefox or Chromium on an unfamiliar laptop at the family snow chalet in the Swiss Alps, @The-Compiler – only to curse the sky as muscle memory and cherished keyboard shortcuts all fail to do anything. This is why you qutebrowser. More fun stuff! @gabrieldemarmiesse, you said this fun stuff...
I share your correct opinion. @beartype users are usually right about everything. This is why You are right about everything is what I'm saying.
Awwwww! You so nice, Gabriel. These sweet words are like a torrent of optimism jacked directly into my brainstem. 🤗
Yes! Change your incorrectly typed code, people. @beartype
😮 ...wow. That many? That's amazing. Well, horrifying. But also amazing! I'm delighted that
Right? Riiiiiiight!? My wife refuses to even touch mypy or I feel like my wife has an unhealthy attachment to the Thanks for all the laughs, everybody. We'll all summit the mountain of a better, bolder, safer Python world... together. muhahahahah— gurgling sounds as @leycec passes out |
Welp, I believe I ran into another interesting corner case - here is a minimal example: from collections.abc import Iterator
from contextlib import contextmanager
from beartype import beartype
@beartype # this breaks, and is what beartype.claw does
@contextmanager
# @beartype # this works
def ctx() -> Iterator[None]:
yield
with ctx():
pass which results in (newlines inserted for readability): Traceback (most recent call last):
File ".../test.py", line 11, in <module>
with ctx():
File "<@beartype(__main__.ctx) at 0x7f6e76793be0>", line 19, in ctx
beartype.roar.BeartypeCallHintReturnViolation: Function __main__.ctx()
return <contextlib._GeneratorContextManager object at 0x7f6e767a1570>
violates type hint collections.abc.Iterator[None],
as <protocol ABC "contextlib._GeneratorContextManager"> <contextlib._GeneratorContextManager object at 0x7f6e767a1570>
not instance of <protocol ABC "collections.abc.Iterator">. Which is true when looking at the decorated function. However, we're annotating the undecorated one. Moving the |
Blarghitty! Your fascinating example is causing my body to sweat with anxiety. Ordinarily, that would be bad. But thanks to the extreme Canadian heat wave that has caused even the cats to put their paws up in the air, I'm grateful. This cottage was built before air conditioning was invented. So. You have uncovered an ordering issue in decoration chaining. That's awful. @beartype will probably need to accommodate this like it does similar ordering issues in decorating chaining with the standard Thankfully, this means that this is actually an issue with the
This implies a bit of decoration-time inefficiency. But... we currently do that with I've opened a new feature request for this. Make the pain go away, issue tracker... |
Feels good. We're there. We're finally there. 😌 I mean, we're probably not actually there. We're probably still years away from there. But The tentative gameplan is to:
Prepare for incoming |
Hey no pressure, we are all consenting adults here, we know the consequences of what we're doing haha :) |
I think we may want to get involved in this discussion about annotated and dataclasses: https://discuss.python.org/t/dataclasses-make-use-of-annotated/30028/15 @leycec your feedback here will likely be very appreciated |
Thanks so much for pinging me on, @gabrieldemarmiesse. I should definitely weigh in. You're so right. But I have deep bags under my weary eyes and @beartype 0.15.0 is due to be released tonight, so... the exhaustion is real. 😩 I'm utterly shocked shocked, I say! that there's nonsensical pushback from all of the usual suspects for a sensible improvement to the existing I am facepalming myself on a Friday night. |
Nifty work. I too have been somewhat interested in runtime type checking as a CI-time sanity check of my codebase, but I have hit an issue that seems difficult and possibly somewhat contradictory to work around. Specifically, we make pretty heavy use of imports guarded by mypy can be a dirty cheater and just analyze every file without worrying about what point in time each reference becomes resolvable, or actually trying to execute the results. I'm not sure what the options are for a runtime checker... |
Gah! I totally neglected to account for As an obvious workaround that you've almost certainly considered and already given up on, would simple forward references (e.g., |
I'll need to read up on forward references and circular imports
…On Mon, Jul 24, 2023, 07:11 Cecil Curry ***@***.***> wrote:
*Gah!* I *totally* neglected to account for typing.TYPE_CHECKING
shenanigans while testing beartype.claw. I feel shame. Thankfully, you're
not the only one to hit that obvious minefield; please see issue #259
<#259>, where everybody else
face-planted into the same deadly trap too.
As an obvious workaround that you've almost certainly considered and
already given up on, would simple forward references (e.g., def
muh_func(expensive_type: 'muh_package.muh_module.muh_expensive_type'): ...
suffice for your use case? @beartype <https://github.com/beartype>
supports simple forward references as a means of circumventing circular
import dependencies, partially initialized modules, and other
chicken-and-egg conundrums. Since we're having this discussion, I suspect
you need a more powerful bazooka than just that. Typing in the real world
is indeed a lesson in pain and futility. 🤕
—
Reply to this email directly, view it on GitHub
<#43 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAGERUGYLVT6B4WWAXFZND3XRYGXLANCNFSM5BF3N6DQ>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
I suppose the hope here is that rooting in the top-level namespace means you can optimistically use names that haven't actually been imported, because if e.g. There are two problems with this:
|
When trying to imagine what "a more powerful bazooka" might look like, the only thing that immediately leaps to mind is an import hook that parses |
@zeevox of SentiPy and Sentiment Investor fame kindly requested automation to automagically decorate all callables in a given package with
@beartype
without needing to explicitly do so:This feature request tracks our eventual (hopefully not too eventual) solution: a beartype import hook. Specifically:
beartype.hook.beartype_everything()
import hook inspired by typeguard's comparabletypeguard.importhook.install_import_hook()
function. That's only 162 lines of pure Python, which means this is mostly trivial, but it's a dense 162 lines, which means this might take an undefinable quantum of space-time.__init__
submodules to globally and transparently apply@beartype
across their entire codebases.Grab that popcorn! @leycec's Wild Ride is now boarding for an improved
@beartype
user experience (UX).The text was updated successfully, but these errors were encountered: