-
-
Notifications
You must be signed in to change notification settings - Fork 59
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
[FAQ] Document how to disable @beartype #94
Comments
This question can be succinctly rephrased as follows:
The answer to this and many other questions is:
We sympathesize with your urge to dump mypy into a ditch while wearing vinyl gloves which you then quietly incinerate. Dumping mypy into a ditch is entirely in-scope for Basically, we regard:
So How We Do This, Bro?We go full AST on this mutha. I even have internal Of course, ASTs are a fathomless black hole from which few return alive and even fewer return sane. This may not necessarily happen within the expected lifetime of my life is what I'm saying. A drip-feed of corporate and/or governmental sponsorship would probably get us there sooner than later. Until Money Shangri La materializes, however, this is lower on the priority list than you'd like. Ugh! tl;drMoney. That's what can make this happen. 🤑 |
Disclaimer: I got a little lost on the verbosity, and will nonetheless comment here to avoid potentially opening a duplicate, as this came up a little close in a search of the issues, but knowing that maybe my post in this comment is actually the opposite (or complementary aspect) of this request. I'm not sure how to interpret the idea of static typing which is mentioned in this discussion, as I didn't really get its example code (apologies). I thought that since beartype is PEP compliant and all then when used in an IDE that does static type checking it does result in static type checking, when code is type checked by a static type checker such as in PyCharm or otherwise. As to my own question or feature request, from reading the code to an extent, I may come to understand that if from typing import Dict, Callable, NamedTuple, Tuple
import numpy as np
import functools
import pytest
""" this module defins a decorator for functions that return a numpy tensor, a decorater that asserts the type,
shape and arbitrary properties of a function's return value when the function is supposed to return a numpy tensor """
# (optional) dict of callables per tensor dimension, which may verify that dimension's length,
# which is a feature that for example allows to only verify some of the dimensions but not all.
ShapeSpec = Dict[int, Callable]
# verification specification type
class NPVerifySpec(NamedTuple):
ndim: int = None # number of dimensions
dtype: np.dtype = None # data type of the elements
shape: Tuple[int, ...] = None # length of each dimension
shape_spec: ShapeSpec = None
allow_none: bool = False
def np_verify_do(arr, spec: NPVerifySpec):
# noinspection PyUnreachableCode
if __debug__:
print(spec)
# allow a None return value
if spec.allow_none and arr is None:
return
assert isinstance(arr, np.ndarray), f"{arr} is not a numpy array"
if spec.ndim is not None:
assert arr.ndim == spec.ndim, f'unexpected number of dimensions {arr.ndim}, expected {spec.ndim}'
if spec.dtype is not None:
assert arr.dtype == spec.dtype, f'unexpected dtype {arr.dtype}, expected {spec.dtype}'
if spec.shape is not None:
assert arr.shape == spec.shape, f'unexpected shape {arr.shape}, expected {spec.shape}'
if spec.shape_spec is not None:
for dim in spec.shape_spec:
fn = spec.shape_spec[dim]
assert fn(arr.shape[dim]) is True, f'dimension {dim} fails validation with validation function: {fn}'
def test_np_verify_do():
arr = np.zeros((3, 4))
# verify number of dimensions, data type, and length of each dimension
np_verify_do(arr, NPVerifySpec(ndim = 2, dtype = np.float64, shape = (3, 4)))
# verify that each dimension has length greater than 2
np_verify_do(arr, NPVerifySpec(shape_spec = {0: lambda dim: dim > 2, 1: lambda dim: dim > 3}))
""" following is a decorator wrapper for the above np_verify function, and some tests demonstrating it
(tecnique adopted from https://realpython.com/primer-on-python-decorators/#decorators-with-arguments) """
def np_verify(np_verify_spec: NPVerifySpec):
def decorator(fn):
@functools.wraps(fn)
def with_output_verification(*args, **kwargs):
result = fn(*args, **kwargs)
np_verify_do(result, np_verify_spec)
return result
return with_output_verification
return decorator
def test_np_verify_positive():
""" verifies that verification via the decorator succeeds when it should """
@np_verify(NPVerifySpec(ndim=2, dtype=np.float64, shape_spec={0: lambda dim: dim > 2, 1: lambda dim: dim > 3}))
def foo():
return np.zeros((3, 4))
foo()
def test_np_verify_negative():
""" verifies that verification via the decorator fails when it should """
with pytest.raises(AssertionError):
@np_verify(NPVerifySpec(ndim=10))
def bar():
return np.zeros((3, 4))
bar() Anyway, this code does nothing that beartype doesn't already allow with its So my question or feature request is twofold:
Glad to learn. |
If -O is set, |
Actually if annotations get evaluated when |
What I mean by the annotations being evaluated is that as part of the function definition, the |
Superb repeated saves by @TeamSpen210 – as expected of the greatest issue outfielder of all time. So, we've dramatically leapt off of the original issue of static type checking and are now hip-deep in tangentially unrelated but nonetheless interesting waters: namely, "How do I curbstomp @beartype from doing what it wants to do?" Let's see if we can't incrementally unpack this Gordion knot.
Yup. This is happening already.
Yup. This is happening already. Specifically, our new configuration API released under Example or it didn't happen, so: # Import the requisite machinery.
from beartype import (
beartype,
BeartypeConf,
BeartypeStrategy,
)
# Global runtime type-checking strategy, where
# "I_AM_A_RELEASE_BUILD" is externally defined
# by your app stack to be True for release builds.
# Specifically:
# * If a release build, reduce @beartype to a noop
# via the no-time type-checking strategy.
# * If a debug build, default @beartype to the usual
# constant-time type-checking strategy.
_beartype_strategy = (
BeartypeStrategy.O0
if I_AM_A_RELEASE_BUILD else
BeartypeStrategy.O1
)
# Beartype decorator configured with this strategy.
# Use this rather than the vanilla @beartype
# decorator everywhere throughout your app stack.
goodbeartype = beartype(
conf=BeartypeConf(_beartype_strategy))
# Use @goodbeartype rather than @beartype everywhere.
@goodbeartype
def muh_performance_critical_func(big_list: list[int]) -> int:
return len(big_list) That's actually useful fodder for a new FAQ entry. 🤔 Of course, it's probably pertinent to note here that @beartype is so fast and adds so little runtime overhead that it will never be a performance bottleneck – unless you're implementing a real-time digital signal processing operating system in pure-Python, in which case I concede the point. Seriously. We're unbelievably fast. Profile us if you don't believe us – which you shouldn't, because your app stack depends on us telling the truth. Never trust anyone. Not even me.
Fascinating. Absolutely no snark here, because customer is king. You are customer, ergo king. Still... Have you actually profiled annotations as consuming non-trivial space for your use case or is this a bit of worst-case theory-crafting? The reason I ask is that type hints originating from the standard >>> import typing
>>> typing.List[str] is typing.List[str]
True Of course, PEP 585 deprecates >>> list[str] is list[str]
False You are now possibly cogitating: "Well, this is all a great ball of steaming matter of a nondescript nature." You're not wrong. Fortunately, @beartype solves that issue, too. Internally, @beartype caches all non-self-memoizing PEP 585 type hints. So, when you use @beartype, there's actually no space costs for either PEP 484 or 585 type hints. Great, right? Almost. Remember that no-time type-checking strategy we enabled above? Okay. When we say "no-time," we mean literally that. @beartype immediately reduces to a noop when configured by that strategy. This means it does nothing, because doing something would violate the meaning of "no-time," right? Unfortunately, doing something includes (wait for it) caching all non-self-memoizing PEP 585 type hints. That no longer happens, so PEP 585 type hints will suddenly begin consuming space when you disable @beartype. Of course... that's why you shouldn't disable @beartype unless you're certain you need to. Again, please profile @beartype against your usual real-world use case. If we're surprisingly slow, we'll do our utmost to resolve that for you and the world. Back to the question at hand. Let's suppose you actually have profiled:
One or both of these are probably not going to be the case. But let's suppose they are. In other words, we are now on the worst historical timeline and the Four Arthritic Horsemen of the apocalypse are now rearing outside your bedroom window. Can you do anything about that? You can – but you really don't want to. As @TeamSpen210 yet again correctly suggests, you can manually litter the top of every Python module across your entire app stack with a futures import enabling PEP 563: from __future__ import annotations Please don't do that. Although @beartype technically is the most PEP 563-compliant runtime type checker, enabling PEP 563 will:
tl;drPlease profile. We promise you'll be pleasantly surprised. |
One thing I did notice while looking through the |
That's... an unsurprisingly fantastic idea! But of course it is. It's Spen's. Type-checking QA 🐻 approves. This is clever, because @beartype internally consumes space due to various internal caches and frozen containers – not much, we swear! But still... Doing away with that when unneeded sounds swell. I'll add an internal |
This commit is the next in a commit chain documenting everything added to the excessively corpulent `beartype 0.10.0` release cycle but left undocument due to widespread laziness, corruption, and vice in the @beartype organization. Specifically, this commit introduces our functional API (i.e., `beartype.abby.is_bearable()`, `beartype.abby.die_if_unbearable()`) in the leading example of our front-facing `README.rst` file. Unrelatedly, this commit also internally details several improvements to the design of our core `@beartype` decorator – all thanks to incisive commentary at issue #94 by the quasi-omniscient HAL-like entity known only as @TeamSpen210. (*Unfathomable fetters of a feathered mobile!*)
Let's rename this issue to something I can actually accomplish within a single lifetime. 🥲 |
I think perhaps it would be good to have a feature request for this in full scope that lives somewhere, even if it's closed as "requires funding", just so that people are less likely to ask again for the same thing.
Does this make sense, or have I become confused?
…________________________________
From: Cecil Curry ***@***.***>
Sent: Wednesday, February 23, 2022 6:33:01 AM
To: beartype/beartype ***@***.***>
Cc: William Blake ***@***.***>; Author ***@***.***>
Subject: Re: [beartype/beartype] [Feature Request] Optionally enforce static typing (Issue #94)
Let's rename this issue to something I can actually accomplish within a single lifetime. 🥲
—
Reply to this email directly, view it on GitHub<https://urldefense.com/v3/__https://github.com/beartype/beartype/issues/94*issuecomment-1048484776__;Iw!!KGKeukY!gzY95Z6n87bJzAxNC_6mWtGMF3IwoIpL7NekrTf8VAG64HIHh13xWBhKhY9yAalla-y5$>, or unsubscribe<https://urldefense.com/v3/__https://github.com/notifications/unsubscribe-auth/AISQNI6CUDV2R6KQPSPXL43U4R5R3ANCNFSM5NEMXDOA__;!!KGKeukY!gzY95Z6n87bJzAxNC_6mWtGMF3IwoIpL7NekrTf8VAG64HIHh13xWBhKhY9yAWt4rg6w$>.
Triage notifications on the go with GitHub Mobile for iOS<https://urldefense.com/v3/__https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675__;!!KGKeukY!gzY95Z6n87bJzAxNC_6mWtGMF3IwoIpL7NekrTf8VAG64HIHh13xWBhKhY9yAf3LBFM0$> or Android<https://urldefense.com/v3/__https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign*3Dnotification-email*26utm_medium*3Demail*26utm_source*3Dgithub__;JSUlJSU!!KGKeukY!gzY95Z6n87bJzAxNC_6mWtGMF3IwoIpL7NekrTf8VAG64HIHh13xWBhKhY9yAVoBOMa3$>.
You are receiving this because you authored the thread.Message ID: ***@***.***>
|
It makes sense, because you always do. Let's copy-and-paste my initial response elsewhere to avoid violating DRY... yet again. Ain't nobody got the money to repeat a money-grubbing rant. Once is enough, GitHub! |
This commit improves compatibility with recently released mypy 0.940, resolving issues #111 and #112 dual-reported concurrently by cutting-edge Microsoft luminary @daxpryce and German typing bad-ass @twoertwein. Specifically, this commit fundamentally refactors (and in so doing mildly optimizes) our private `beartype._decor.main` submodule to leverage conditional overloads under the astute tutelage of mypy maestro @cdce8p; the `@beartype.beartype` decorator itself now resides in a new private `beartype._decor.cache.decor.cachedecor` submodule, because obfuscation is the key to all successful open-source efforts. Relatedly, this commit also partially resolves issue #94 (kindly requested by typing monster @matanster) by optimizing the definition of the `@beartype.beartype` decorator when that decorator reduces to a noop (e.g., due to `python3 -O` optimization). Unrelatedly, this commit also resolves a critical defect in our new functional API (i.e., the pair of `beartype.abby.is_bearable()` and `beartype.abby.die_if_unbearable()` functions), which previously reduced to a noop when the `@beartype.beartype` decorator reduced to a noop; extricating the `@beartype.beartype` decorator into the `beartype._decor.cache.decor.cachedecor` submodule enables our functional API to unconditionally depend upon that decorator regardless of what we present to external users. Thanks so much for the rapid response time, everyone! `beartype 0.10.4` will be en-route to a cheeseshop near you shortly. (*Incorrigibly durable didgeridoo!*)
Partially resolved by ccac042. By following the spun gold of @TeamSpen210's prior comment, we've mildly optimized the We still have yet to actually document any of this, however. Let's do that this year, shall we then? 😓 |
This patch release adumbrates with breathless support for **mypy ≥ 0.940,** the static type checker formerly known as "The Static Type Checker Whose Name Shall not Be Spoken." This patch release resolves **5 issues** and merges **0 pull requests.** Noteworthy changes include: ## Compatibility Improved * **mypy ≥ 0.940.** The `beartype` codebase now sports improved compatibility with recently released mypy 0.94x series, which previously outted the `@beartype` decorator with a "Condition can't be inferred, unable to merge overloads [misc]" fatal error at static type-checking time. Specifically, this commit fundamentally refactors (and in so doing mildly optimizes) our private `beartype._decor.main` submodule to leverage conditional overloads under the astute tutelage of mypy maestro @cdce8p; the `@beartype.beartype` decorator itself now resides in a new private `beartype._decor.cache.cachedecor` submodule, because obfuscation is the key to all successful open-source efforts. Doing so resolves issues #111 and #112 dual-reported concurrently by cutting-edge Microsoft luminary @daxpryce and German typing bad-ass @twoertwein. ## Features Optimized * **Importation time.** The first external importation from the `beartype` codebase is now significantly faster when the definition of the `@beartype.beartype` decorator reduces to a noop (e.g., due to `python3 -O` optimization), partially resolves issue #94 kindly requested by the well-tanned and -toned typing star @matanster. ## Issues Resolved * **`beartype.abby` under `python3 -O`.** This release resolves an unreported critical defect in our new functional API (i.e., the pair of `beartype.abby.is_bearable()` and `beartype.abby.die_if_unbearable()` functions), which previously reduced to a noop when the `@beartype.beartype` decorator reduced to a noop (e.g., due to `python3 -O` optimization). By extricating the `@beartype.beartype` decorator into the `beartype._decor.cache.cachedecor` submodule (as described above), our functional API now directly defers to that decorator regardless of what the `beartype` package externally presents to third-party code. (*Ironwrought irony untaught!*)
Resolved by f1e0cdc. The beartype configuration API is now exhaustively documented. I know this, because I'm exhausted. Relevantly, the # Import the requisite machinery.
from beartype import (
beartype,
BeartypeConf,
BeartypeStrategy,
)
# Global runtime type-checking strategy, where
# "I_AM_A_RELEASE_BUILD" is externally defined
# by your app stack to be True for release builds.
# Specifically:
# * If a release build, reduce @beartype to a noop
# via the no-time type-checking strategy.
# * If a debug build, default @beartype to the usual
# constant-time type-checking strategy.
_beartype_strategy = (
BeartypeStrategy.O0
if I_AM_A_RELEASE_BUILD else
BeartypeStrategy.O1
)
# Beartype decorator configured with this strategy.
# Use this rather than the vanilla @beartype
# decorator everywhere throughout your app stack.
goodbeartype = beartype(
conf=BeartypeConf(_beartype_strategy))
# Use @goodbeartype rather than @beartype everywhere.
@goodbeartype
def muh_performance_critical_func(big_list: list[int]) -> int:
return len(big_list) Let's close out this ancient issue to widespread jubilation. 🥳 |
For example, do we ever intend it to detect the issue in either of these:
Or is that out of the scope of the project?
Basically, I would like to be able to completely ditch mypy in favor of this project, and this is what's holding me back.
The text was updated successfully, but these errors were encountered: