Skip to content
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

[Upstream Bug] VSCode + Pylance + Pyright linter errors on "beartype.typing.TypeAlias" #127

Closed
rbroderi opened this issue Apr 19, 2022 · 17 comments
Labels
upstreamwontfix Someone else broke something, they refuse to fix it, and now they blame us for it like usual whatyougonnado We wish we could fix this for you, but we can't, because wishes are like magical unicorns

Comments

@rbroderi
Copy link

I get errors when trying to use the beartype.typing.TypeAlias instead of typing.TypeAlias,
here are some of the error messages I get:
Key: TypeAlias = str
Expression of type "Type[str]" cannot be assigned to declared type "TypeAlias"
"Type[type]" is incompatible with "Type[TypeAlias]"PylancereportGeneralTypeIssues

CallbackFunction: TypeAlias = Callable[[Key], Any]
Expression of type "(Unknown) -> Any" cannot be assigned to declared type "TypeAlias"
"object" is incompatible with "TypeAlias"PylancereportGeneralTypeIssues

def press(key: Key):
print(f"'{key}' pressed")
Expected class type but received "TypeAlias"PylancereportGeneralTypeIssues

@leycec
Copy link
Member

leycec commented Apr 19, 2022

Clearly, Pylance popped too many benzos last night.

beartype.typing.TypeAlias is literally just typing.TypeAlias. The former is a convenient synonym for the latter; the two are the exact same object. Specifically, this is how the beartype.typing subpackage declares TypeAlias

            from typing import (  # type: ignore[attr-defined]
                ...
                TypeAlias as TypeAlias,
                ...
            )

That's it. beartype.typing just imports typing.TypeAlias into itself under the exact same name. This can't be simplified any further, because this is already maximally simple. It's just a simple import, which means that Pylance can't handle even simple imports. This is where I facepalm myself repeatedly on a Reddit livestream to farm upvotes.

What We Goin' Do Is This

Would you mind filing an upstream issue against Pyright's issue tracker, @rbroderi?

You are probably now wondering why I am now talking about "Pyright," because weren't we just talking about Pylance? Right. So, Pyright is Microsoft's static type-checker. Under the hood, Pylance leverages Pyright to statically check types. The type-checking failures you've exhibited are actually being directly emitted by Pyright – not Pylance. Pylance is just forwarding Pyright complaints to you.

This is a critical issue with Pyright, which appears to be unable to cope with PEP 484-compliant implicit type aliases. That's bad, because PEP 484 is nearly a decade old. Every type checker should at least superficially support PEP 484.

To quote the PEP 484 section on type aliases:

Type aliases are defined by simple variable assignments:

Url = str
def retry(url: Url, retry_count: int) -> None: ...

Type aliases may be as complex as type hints in annotations – anything that is acceptable as a type hint is acceptable in a type alias:

from typing import TypeVar, Iterable, Tuple

T = TypeVar('T', int, float, complex)
Vector = Iterable[Tuple[T, T]]

def inproduct(v: Vector[T]) -> T:
    return sum(x*y for x, y in v)

Pyright appears to be failing at that. 🤦 🤦‍♂️ 🤦‍♀️

@leycec
Copy link
Member

leycec commented Apr 19, 2022

Please feel free to reopen this if there's anything tangible we can do for you, Pylance, or Pyright to make this work. I loathe closing issues unresolved – but this deflated ball is almost certainly in Pyright's court.

Oh, and thanks for the recent spate of issues! Drop us a line at the bear cave if you hit any further snags or come up with any h0t feature requests.

@rbroderi
Copy link
Author

Thank you for the information and I will open an issue with Pyrights upstream issue tracker.

@leycec
Copy link
Member

leycec commented Apr 19, 2022

Thanks so much, @rbroderi. I know how time-consuming merely filing GitHub issues can be, because that's pretty much all I did last night. Monday evening, I want you back! 😅

@beartype applauds the amazing work you're doing for us all.

@rbroderi
Copy link
Author

opened as issue microsoft/pyright#3363 on pyright. Documenting here for for discoverability.

@agucova
Copy link

agucova commented Apr 21, 2022

Issue was closed upstream because Pyright only checks for typing.TypeAlias and won't implement deeper checking:

TypingAlias is one of a handful of special forms that pyright needs to handle early in the "binding" phase, prior to type evaluation. As such, if the symbol is imported by another module and re-exported, pyright does not know that it's the same type until it's too late. You need to import it directly from typing or typing_extensions.

Why was beartype.typing.TypeAlias created in the first place?

@leycec
Copy link
Member

leycec commented Apr 21, 2022

...heh. Knew it. Pyright devs seem toxic, so it's little surprise they refuse to fix their blatantly broken and PEP-noncompliant behaviour. As detailed above, PEP 484 requires type checkers to support implicit type aliases:

Type aliases may be as complex as type hints in annotations – anything that is acceptable as a type hint is acceptable in a type alias.

typing.TypeAlias is a type hint. So, any type alias of typing.TypeAlias is also acceptable as a type hint:

# Pyright should support this simple type alias.
from typing import TypeAlias
MyTypeAlias = TypeAlias
my_str_type: MyTypeAlias = str

# Pyright should support this simple type alias, too.
from typing import TypeAlias as MyTypeAlias
my_str_type: MyTypeAlias = str

Pyright doesn't support either. Ergo, Pyright violates PEP 484 – Python's core typing standard. To no one's surprise, Pyright then refuses to fix their standards violations. 😮‍💨

And Pyright's excuse for violating PEP 484...

TypingAlias is one of a handful of special forms that pyright needs to handle early in the "binding" phase, prior to type evaluation.

...is not a valid excuse. Mypy complies with PEP standards. Pyright needs to, too.

If Pyright currently can't because they're improperly handling TypeAlias "early in the 'binding' phase," then they need to stop doing that and start doing what mypy does. Ironically, Pyright is wrong; mypy is right. Low-level implementation details are no excuse for perpetuating wrong behaviour.

Why was beartype.typing.TypeAlias created in the first place?

The beartype.typing submodule is a one-for-one mapping of the typing module. Everything that typing provides, beartype.typing also provides and vice versa.

beartype.typing is a drop-in replacement for typing. Ideally, @beartype users should globally replace all importations of typing throughout their codebases with beartype.typing.

This is a good thing – not a bad thing. beartype.typing is demonstrably superior to typing in increasingly many ways. mypy supports these ways, because mypy is PEP-compliant; Pyright violates these ways, because Pyright is PEP-noncompliant.

This is why standards matter, people. Come on, Microsoft! Throw the community a bone here.

@leycec leycec changed the title Pylance errors with beartype.typing.TypeAlias [Upstream Bug] "beartype.typing.TypeAlias" unsupported by Pyright + Pylance + VSCode Apr 21, 2022
@leycec
Copy link
Member

leycec commented Apr 21, 2022

Reluctantly reopening, because upstream refuses to resolve their broken behaviour. It's unclear whether we can circumvent this on behalf of Pylance VSCode users – but maybe we can.

At the least, reopening this will improve discoverability and hopefully provoke some fruitful discussion.

tl;dr

Pylance VSCode users, please prefer typing.Alias to beartype.typing.TypeAlias for the moment. This isn't @beartype's fault, because we comply with PEP 484.

@leycec leycec reopened this Apr 21, 2022
@leycec leycec changed the title [Upstream Bug] "beartype.typing.TypeAlias" unsupported by Pyright + Pylance + VSCode [Upstream Bug] VSCode + Pylance + Pyright linter errors on "beartype.typing.TypeAlias" Apr 21, 2022
@leycec
Copy link
Member

leycec commented Apr 21, 2022

"Clever" yet paradoxically stupid idea: @beartype could probably silence this VSCode complaint by:

  1. Detecting when it's being statically type-checked by Pyright.
  2. If so, replacing beartype.typing.TypeAlias with typing.Any. TypeAlias is mostly semantically meaningless anyway, so just aliasing TypeAlias to Any mostly does little harm. Of course, that entirely defeats the purpose of using TypeAlias in the first place... but false positives would still be better than what's happening now.

So, something like this is what we're saying:

# In "beartype.typing.__init__":
if TYPE_CHECKING and IS_TYPE_CHECKER_PYRIGHT:
    TypeAlias = Any
else:
    from typing import TypeAlias as TypeAlias

Should work – maybe, in theory. But Pyright's kinda cray-cray, so maybe it'd still expurgate everywhere even if we feed it that.

B-b-but... How Do We Detect Pyright?

I have no idea. I doubt anyone else does, either. The usual approach is something like:

IS_TYPE_CHECKER_PYRIGHT = 'pyright' in sys.modules

Of course, that's fragile and error-prone. Thanks alot, Redmond.

B-b-but... None of the Above Works

Update: No, of course that doesn't work. Static type checkers don't actually run code. That's the whole point; they only parse.

So, @beartype has no opportunity to selectively intervene at static type-checking time in a safe way that would be apply only to Pyright. We don't want to destroy mypy as well, because mypy is fine with beartype.typing.TypeAlias. Our keyboard is hog-tied with a zip tie here.

2022: the year that just keeps on giving. </le_sigh>

@leycec
Copy link
Member

leycec commented Apr 22, 2022

Okay. Just begging Pyright to "go away already" is clearly an insufficient solution, despite being my preferred solution. Let's brainstorm.

I don't ❤️ VSCode, because I ❤️‍🔥 Vim. But if I did ❤️ VSCode, here's what I personally would do:

  • Disable Pylance type-checking, because Pylance uses Pyright to statically type-check. Pyright violates core Python type-checking standards like PEP 484, which is bad. Specifically:
    1. Open the User Settings dialog.
    2. Search for Type Checking Mode.
    3. Find Python › Analysis: Type Checking Mode in the resulting search hits.
    4. Switch the "default rule set for type checking" to off.
  • Enable mypy type-checking, because mypy complies with literally all Python type-checking standards. In fact, mypy is the de facto Python type-checking standard against which all other type checkers (including @beartype and Pyright) are measured. Specifically:
    1. Install mypy.
    2. Install the Mypy VSCode extension.
    3. Rejoice as things actually work for once.

Data scientists and machine learning specialists especially want to switch from Pyright to mypy, because many scientific Python packages ship mypy plugins describing dynamic runtime behaviour. Naturally, Pyright devs refuse to implement a comparable plugin API. 🤦

Time to Open a Can of Whoopie

Because Pylance's bad type-checking makes us look and feel bad, this is mission critical. I'm going to immediately add a new FAQ entry with this exact advice.

If someone who does ❤️ VSCode could verify that the above instructions are both valid and make this issue go away, I'll happily close this after amending our FAQ.

Thanks for all the heads up, all. Everyone rocks! 🪨

@leycec leycec added upstreamwontfix Someone else broke something, they refuse to fix it, and now they blame us for it like usual whatyougonnado We wish we could fix this for you, but we can't, because wishes are like magical unicorns labels Apr 22, 2022
leycec added a commit that referenced this issue Apr 22, 2022
This commit adds an entry to the frequently asked questions (FAQ)
section of our monolithic front-facing `README.rst` documentation
dedicated to VSCode **+** Pylance **+** pyright, *effectively* resolving
companion issues #126 and #127 kindly submitted by @rbroderi the
recursively broad-minded VSCode enthusiast. Specifically, this entry
advises @beartype users to switch wholesale from Pylance's incorrect
pyright-based static type-checking to the Mypy extension's correct
mypy-based static type-checking. Since pyright violates community typing
standards required by beartype *and* refuses to resolve these
violations, our advice as your pro-bono typing attorney is to abandon
pyright for brighter shores elsewhere. You who abandon us, we abandon
*you*! (*Geriatric gymnastics!*)
@leycec
Copy link
Member

leycec commented Apr 22, 2022

Glorious FAQ entry that solves everything is up.

Since pyright refuses to fix its violations of typing standards required by @beartype, our only recourse is to advise all VSCoders (...this means you) to embrace the VSCode Mypy extension. Let's pretend I actually tested those instructions. 😅

Big thanks to @rbroderi for this and the companion issue #126!

@leycec leycec closed this as completed Apr 22, 2022
@rsokl
Copy link

rsokl commented Jun 15, 2022

Pyright devs seem toxic, so it's little surprise they refuse to fix their blatantly broken and PEP-noncompliant behaviour.

You might want to pump your brakes here a bit, my friend. pyright is basically a one-man show, which is pretty incredible when you think about how many people depend on pyright via VSCode... it probably has more (inadvertent) users than any other static type checker in the python ecosystem. Moreover, Eric is an indispensable member of the typing community, and I have found him to be quite generous with his time.

our only recourse is to advise all VSCoders (...this means you) to embrace the VSCode Mypy extension

Simply opting for the mypy VSCode extension comes with some serious downsides. Beyond pyright being much more performant than mypy speed wise (it takes mypy 60s to scan the src/ of hydra-zen vs pyright taking <6s!), pyright also supports some important typing features that mypy lacks (which would make them non-compliant with several PEPs...). These include:

It is absolutely bonkers how quickly (and correctly) pyright implements new typing features! For all of these reasons, I prefer pyright over mypy, even outside of the context of VSCode. (Also, did you know that pyright has a really cool tool for measuring the type completeness of a library?)

I just wanted to share this because I don't think that pyright should be underestimated in its importance nor its utility. And more generally, I just want peace and love among the various Python type-checkers, static and runtime alike ☮️

P.S. the whole time I was writing this I kept thinking of the ad

image

but I liked Nintendo as a kid! So I felt like a bad guy who was peddling rude, in-your-face advertising lines from the 90s while writing this 😞

@leycec
Copy link
Member

leycec commented Jun 15, 2022

...heh. I ❤️‍🔥 the SEGA trash talk. Also, I'm pretty sure I heard the canonical SEGA startup sound while reading that.

I hear and abide by everything my beautiful users are saying. You are not wrong, @rsokl and everyone else. In fact, you're all absolutely right. Like beartype, mypy is deficient in obvious respects. Recursive type hints are the pain point I share with you. But our deficiencies kinda make sense, right?

Eric's a Technical Fellow at Microsoft. It's his job to get Pythonic typing correct. Meanwhile, most of the rest of us in the open-source typing community are mostly unpaid open-source volunteers. It's hard to compete. We're just here for the sheer joy of type hints. ...how is that even a thing

Ain't Nobody Got Time for This

Ultimately, my conundrum is that open-source volunteer time is scarce. I'd fervently love to openly embrace mypy, pyright, Google's pytype, and every other static type checker. B-b-but... I lack the bandwidth for that. Even supporting mypy is an ongoing manly struggle with destiny, deliverance, and # type: ignore[EVERYTHING-JUST-IGNORE-EVERYTHING-MYPY] debacles.

Mypy is Python's official static type checker. It's clunky, it's chunky, and it's funky. But it's also community-maintained, CLI-friendly, continuous integration (CI)-friendly, pure-Python, lightweight, and the de facto typing standard.

Pyright is none of those things. I don't know how to properly integrate pyright into either our local pytest + tox test suite or our remote GitHub Actions-based CI testing workflow – if pyright even can be. I don't even know how to properly install pyright as a system-wide package under Gentoo Linux, because pyright is crazily implemented in TypeScript (i.e., typed JavaScript) rather than pure-Python. Pyright requires node, which requires npm. Pyright is heavyweight. Pyright requires that I throw scarce resources at non-standard solutions that I personally will never leverage, because it's Vim all the way down at the @leycec cottage.

Pyright is hostile to my interests. Mypy isn't.

Honesty Rears Ugly Head on GitHub

Honestly? I truly abhor all static type checkers. Python isn't statically typed. It's dynamically typed. Trying to pretend otherwise is an exercise in futility and # type: ignore[...] spam.

I don't use static type checking in any real-world projects, because neither I nor my colleagues (...okay, it's just my beautiful wife) would tolerate the maintenance burden. I use @beartype, because @beartype imposes no burdens whatsoever. That's sorta the point, right? That's why we're here. @beartype just gets out of your way so you can get actually meaningful work done. 🧑‍🏭

The real reason @beartype supports mypy is that I can force mypy to get out @beartype's way with minimal to no effort in 2022. Sadly, the same reasoning doesn't extend to pyright.

But Reality Always Bats Last

Of course... that isn't the social reality of the situation. The social reality is, as you say, that pyright is deployed across a huge horizontal slice of Python's dev community. But the personal reality is that Microsoft isn't paying me to support pyright; they're paying Eric to support runtime type checkers like @beartype.

I want everyone to use @beartype. But everyone uses static type checkers. So, I want @beartype to support all actively maintained static type checkers. If I can enable pyright to trivially support @beartype without a vast expenditure of torturous effort on my part, I happily – nay, gleefully! – will. But... I don't know how to do that yet.

Thus, our conundrum continues. </sigh>

@leycec
Copy link
Member

leycec commented Jun 16, 2022

Gah! I submit; I concede; I accede. I can't let these shameful pyright incompatibilities lie. Even if it's not @beartype's fault, pyright (like mypy before it) has gracelessly made false positives @beartype's fault.

In the best interests of not looking bad and supporting the best bear bros that GitHub has to offer, I'm officially reopening both this and tangentially related issue #126. My stalwart 5-Day Plan towards a stunning victory against the tyranny of static type-checking is as follows:

  1. Package the newest stable release of pyright for Gentoo Linux. Done. I can now actually run pyright, everyone. Rejoice! I even made a Gentoo eclass trivially automating packaging of arbitrary Node.js packages, should anyone be interested. Let's nerd out together. 🤓
  2. Silence pyright-specific error codes. Thankfully, pyright supports a # pyright: ignore[{PyrightErrorCode1},...,{PyrightErrorCodeN}] comment pragma for doing so on a line-by-line basis. I note the following "errors":
$ pyright beartype
No configuration file found.
No pyproject.toml file found.
stubPath /home/leycec/py/beartype/typings is not a valid directory.
Assuming Python platform Linux
Searching for source files
Found 178 source files
pyright 1.1.254
/home/leycec/py/beartype/beartype/meta.py
/home/leycec/py/beartype/beartype/_decor/decorcore.py
  /home/leycec/py/beartype/beartype/_decor/decorcore.py:159:6 - error: Function with declared type of "BeartypeableT@beartype_object_safe" must return value
    Type "None" cannot be assigned to type "BeartypeableT@beartype_object_safe" (reportGeneralTypeIssues)
/home/leycec/py/beartype/beartype/_decor/decormain.py
  /home/leycec/py/beartype/beartype/_decor/decormain.py:88:14 - warning: TypeVar "BeartypeableT" appears only once in generic function signature (reportInvalidTypeVarUse)
/home/leycec/py/beartype/beartype/_decor/_code/codemain.py
  /home/leycec/py/beartype/beartype/_decor/_code/codemain.py:636:12 - error: "func_wrapper_code" is possibly unbound (reportUnboundVariable)
  /home/leycec/py/beartype/beartype/_decor/_code/codemain.py:91:12 - warning: "STAR_IMPORTS_CONSIDERED_HARMFUL" is specified in __all__ but is not present in module (reportUnsupportedDunderAll)
/home/leycec/py/beartype/beartype/_decor/_code/codesnip.py
/home/leycec/py/beartype/beartype/_decor/_error/errormain.py
  /home/leycec/py/beartype/beartype/_decor/_error/errormain.py:292:20 - error: Argument of type "int | None" cannot be assigned to parameter "random_int" of type "int" in function "__init__"
    Type "int | None" cannot be assigned to type "int"
      Type "None" cannot be assigned to type "int" (reportGeneralTypeIssues)
/home/leycec/py/beartype/beartype/_util/cache/utilcachecall.py
  /home/leycec/py/beartype/beartype/_util/cache/utilcachecall.py:315:23 - error: Invalid exception class or object
    "Iota" does not derive from BaseException (reportGeneralTypeIssues)
/home/leycec/py/beartype/beartype/_util/cache/pool/utilcachepoollistfixed.py
/home/leycec/py/beartype/beartype/_util/func/utilfunccode.py
  /home/leycec/py/beartype/beartype/_util/func/utilfunccode.py:143:17 - error: Argument of type "str" cannot be assigned to parameter "message" of type "Warning" in function "warn"
    "str" is incompatible with "Warning" (reportGeneralTypeIssues)
  /home/leycec/py/beartype/beartype/_util/func/utilfunccode.py:243:17 - error: Argument of type "str" cannot be assigned to parameter "message" of type "Warning" in function "warn"
    "str" is incompatible with "Warning" (reportGeneralTypeIssues)
  /home/leycec/py/beartype/beartype/_util/func/utilfunccode.py:319:33 - error: Argument of type "str" cannot be assigned to parameter "message" of type "Warning" in function "warn"
    "str" is incompatible with "Warning" (reportGeneralTypeIssues)
  /home/leycec/py/beartype/beartype/_util/func/utilfunccode.py:364:41 - error: Argument of type "str" cannot be assigned to parameter "message" of type "Warning" in function "warn"
    "str" is incompatible with "Warning" (reportGeneralTypeIssues)
  /home/leycec/py/beartype/beartype/_util/func/utilfunccode.py:389:33 - error: Argument of type "str" cannot be assigned to parameter "message" of type "Warning" in function "warn"
    "str" is incompatible with "Warning" (reportGeneralTypeIssues)
  /home/leycec/py/beartype/beartype/_util/func/utilfunccode.py:405:21 - error: Argument of type "str" cannot be assigned to parameter "message" of type "Warning" in function "warn"
    "str" is incompatible with "Warning" (reportGeneralTypeIssues)
/home/leycec/py/beartype/beartype/_util/hint/utilhintconv.py
  /home/leycec/py/beartype/beartype/_util/hint/utilhintconv.py:395:22 - error: Expected class type but received "object" (reportGeneralTypeIssues)
/home/leycec/py/beartype/beartype/_util/hint/pep/proposal/utilpep544.py
  /home/leycec/py/beartype/beartype/_util/hint/pep/proposal/utilpep544.py:559:29 - error: Illegal type annotation: variable not allowed unless it is a type alias (reportGeneralTypeIssues)
  /home/leycec/py/beartype/beartype/_util/hint/pep/proposal/utilpep544.py:52:1 - error: Declaration "_Pep544IO" is obscured by a declaration of the same name (reportGeneralTypeIssues)
  /home/leycec/py/beartype/beartype/_util/hint/pep/proposal/utilpep544.py:112:1 - error: Declaration "_Pep544TextIO" is obscured by a declaration of the same name (reportGeneralTypeIssues)
/home/leycec/py/beartype/beartype/_util/hint/pep/proposal/utilpep585.py
  /home/leycec/py/beartype/beartype/_util/hint/pep/proposal/utilpep585.py:79:9 - error: Function declaration "is_hint_pep585_generic" is obscured by a declaration of the same name (reportGeneralTypeIssues)
/home/leycec/py/beartype/beartype/_util/hint/pep/proposal/pep484/utilpep484union.py
  /home/leycec/py/beartype/beartype/_util/hint/pep/proposal/pep484/utilpep484union.py:55:18 - error: Cannot access member "__getitem__" for type "Type[Union]"
    Member "__getitem__" is unknown (reportGeneralTypeIssues)
/home/leycec/py/beartype/beartype/_util/hint/pep/proposal/pep484585/utilpep484585ref.py
  /home/leycec/py/beartype/beartype/_util/hint/pep/proposal/pep484585/utilpep484585ref.py:185:11 - error: Illegal type annotation: variable not allowed unless it is a type alias (reportGeneralTypeIssues)
  /home/leycec/py/beartype/beartype/_util/hint/pep/proposal/pep484585/utilpep484585ref.py:124:11 - error: Illegal type annotation: variable not allowed unless it is a type alias (reportGeneralTypeIssues)
  /home/leycec/py/beartype/beartype/_util/hint/pep/proposal/pep484585/utilpep484585ref.py:242:11 - error: Illegal type annotation: variable not allowed unless it is a type alias (reportGeneralTypeIssues)
/home/leycec/py/beartype/beartype/_util/hint/pep/proposal/pep484585/utilpep484585type.py
  /home/leycec/py/beartype/beartype/_util/hint/pep/proposal/pep484585/utilpep484585type.py:55:6 - error: Illegal type annotation: variable not allowed unless it is a type alias (reportGeneralTypeIssues)
/home/leycec/py/beartype/beartype/_util/mod/utilmodtest.py
  /home/leycec/py/beartype/beartype/_util/mod/utilmodtest.py:247:52 - error: Argument of type "Requirement | None" cannot be assigned to parameter "dist" of type "Requirement | str | Distribution" in function "get_distribution"
    Type "Requirement | None" cannot be assigned to type "Requirement | str | Distribution"
      Type "None" cannot be assigned to type "Requirement | str | Distribution"
        Type "None" cannot be assigned to type "Requirement"
        Type "None" cannot be assigned to type "str"
        Type "None" cannot be assigned to type "Distribution" (reportGeneralTypeIssues)
/home/leycec/py/beartype/beartype/claw/_clawloader.py
  /home/leycec/py/beartype/beartype/claw/_clawloader.py:29:5 - error: "_bootstrap_external" is unknown import symbol (reportGeneralTypeIssues)
  /home/leycec/py/beartype/beartype/claw/_clawloader.py:468:39 - error: No parameter named "_optimize" (reportGeneralTypeIssues)
/home/leycec/py/beartype/beartype/typing/__init__.py
  /home/leycec/py/beartype/beartype/typing/__init__.py:176:17 - error: "reveal_type" is unknown import symbol (reportGeneralTypeIssues)
/home/leycec/py/beartype/beartype/typing/_typingcache.py
  /home/leycec/py/beartype/beartype/typing/_typingcache.py:120:23 - error: Invalid exception class or object
    "object" does not derive from BaseException (reportGeneralTypeIssues)
/home/leycec/py/beartype/beartype/typing/_typingpep544.py
  /home/leycec/py/beartype/beartype/typing/_typingpep544.py:38:9 - error: "EXCLUDED_ATTRIBUTES" is unknown import symbol (reportGeneralTypeIssues)
  /home/leycec/py/beartype/beartype/typing/_typingpep544.py:195:13 - warning: __new__ override should take a "cls" parameter (reportSelfClsParameterName)
  /home/leycec/py/beartype/beartype/typing/_typingpep544.py:360:9 - error: "Generic" requires at least one type argument
  /home/leycec/py/beartype/beartype/typing/_typingpep544.py:430:32 - error: Cannot access member "__class_getitem__" for type "Type[Protocol]"
    Member "__class_getitem__" is unknown (reportGeneralTypeIssues)
  /home/leycec/py/beartype/beartype/typing/_typingpep544.py:459:37 - error: Cannot access member "__class_getitem__" for type "Type[Protocol]"
    Member "__class_getitem__" is unknown (reportGeneralTypeIssues)
  /home/leycec/py/beartype/beartype/typing/_typingpep544.py:467:37 - error: Cannot access member "__class_getitem__" for type "Type[Protocol]"
    Member "__class_getitem__" is unknown (reportGeneralTypeIssues)
/home/leycec/py/beartype/beartype/vale/_core/_valecore.py
/home/leycec/py/beartype/beartype/vale/_is/_valeisabc.py
  /home/leycec/py/beartype/beartype/vale/_is/_valeisabc.py:45:23 - error: Metaclass cannot be generic (reportGeneralTypeIssues)
31 errors, 100 warnings, 0 informations 
Completed in 8.737sec

Kinda getting the feeling that pyright isn't respecting my time here. Most or all of those appear to be false positives, especially as mypy insists everything is fine.

I exhale slowly without raising my disastrous blood pressure any higher. 😮‍💨

@leycec leycec reopened this Jun 16, 2022
leycec added a commit that referenced this issue Jun 19, 2022
This commit is the first in a commit chain officially supporting pyright
static type-checking of @beartype by iteratively resolving each true
positive (i.e., error or warning correctly identifying an error
condition in the @beartype codebase) and ignoring each false positive
(i.e., error or warning incorrectly misidentifying an error condition
due to a static type analysis failure), resolving issues #126 and #127
kindly submitted by fellow Zelda aficionado @rbroderi. Specifically,
this commit resolves 9 false warnings and 1 false error issued by
pyright. It is, indeed, dangerous to go alone. `</sigh>` (*Dour sourdough!*)
leycec added a commit that referenced this issue Jun 20, 2022
This commit is the first in a commit chain officially supporting pyright
static type-checking of @beartype by iteratively resolving each true
positive (i.e., error or warning correctly identifying an error
condition in the @beartype codebase) and ignoring each false positive
(i.e., error or warning incorrectly misidentifying an error condition
due to a static type analysis failure), resolving issues #126 and #127
kindly submitted by fellow Zelda aficionado @rbroderi. Specifically,
this commit resolves 11 false warnings and 7 false errors issued by
pyright. It is, indeed, dangerous to go alone. `</sigh>`
(*Little brittle bricks rumble while crumbling!*)
leycec added a commit that referenced this issue Jun 21, 2022
This commit is the next in a commit chain officially supporting pyright
static type-checking of @beartype by iteratively resolving each true
positive (i.e., error or warning correctly identifying an error
condition in the @beartype codebase) and ignoring each false positive
(i.e., error or warning incorrectly misidentifying an error condition
due to a static type analysis failure), resolving issues #126 and #127
kindly submitted by fellow Zelda aficionado @rbroderi. Specifically,
this commit resolves 48 false warnings and 12 false errors issued by
pyright. It is, indeed, dangerous to go alone. `</sigh>` (*Brazen raisins!*)
leycec added a commit that referenced this issue Jun 22, 2022
This commit is the next in a commit chain officially supporting pyright
static type-checking of @beartype by iteratively resolving each true
positive (i.e., error or warning correctly identifying an error
condition in the @beartype codebase) and ignoring each false positive
(i.e., error or warning incorrectly misidentifying an error condition
due to a static type analysis failure), resolving issues #126 and #127
kindly submitted by fellow Zelda aficionado @rbroderi. Specifically,
this commit resolves 35 false warnings and 9 false errors issued by
pyright. It is, indeed, dangerous to go alone. `</sigh>` (*Brash rashes!*)
@leycec
Copy link
Member

leycec commented Jun 23, 2022

I have good news. I have bad news.

Give It to Me Straight, Doc

The good news is @beartype now officially supports pyright! Rejoice, typing gentleman – for we have vanquished a vacuous evil this day:

$ pyright beartype
No configuration file found.
No pyproject.toml file found.
stubPath /home/leycec/py/beartype/typings is not a valid directory.
Assuming Python platform Linux
Searching for source files
Found 177 source files
pyright 1.1.254
0 errors, 0 warnings, 0 informations 
Completed in 13.868sec

0 errors, 0 warnings, 0 informations, people.

Yes, pyright wants us to believe that "informations" is the plural form of "information." It isn't; like the few remaining hairs on my shining head, the English noun "information" is uncountable and thus has no plural. And... that should tell you everything you need to know about pyright.

But I digress. Ultimately, this means we have resolved the related issue #126. pyright should no longer say this (...or anything, really) when confronted with @beartype in real-world code:

Type of "beartype" is partially unknown
Type of "beartype" is "Overload[(obj: BeartypeableT@beartype) ->
    BeartypeableT@beartype, (*, conf: BeartypeConf) -> 
   ((BeartypeableT@BeartypeConfedDecorator) -> 
   BeartypeableT@BeartypeConfedDecorator),
   (obj: BeartypeableT@beartype, *, conf: BeartypeConf = BeartypeConf()) -> 
   (BeartypeableT@BeartypeReturn | ((BeartypeableT@BeartypeConfedDecorator) ->
   BeartypeableT@BeartypeConfedDecorator))]"

Our upcoming beartype 0.11.0 release thus extends static type-checking support to both mypy and pyright. Pretty sure that makes us the first large-scale Python package to explicitly support both – but I'm pretty sure someone in the peanut gallery is about to prove me wrong. Do it!

Until then, please dangerously install @beartype from git to silence pyright complaints:

python -m pip install git+https://github.com/beartype/beartype.git@720d779a1a4fdb830d6374cc5dc40e6e54153042

So... How Bad Is this Bad News?

Not that bad, actually! How often does that happen, guys?

The bad news is that this issue that we are discussing here still isn't a @beartype issue, as expected. It's an upstream pyright issue. Fairly certain we can't resolve this on our end. I'd happily monkey-patch this if we could with a sketchy kludge that makes our test suite sweat bullets – but pyright is TypeScript, so there's nothing to monkey-patch.

Instead, we need pyright to do something about this. But Eric isn't inclined to do anything about this, because he's Eric. Sadly, the upstreamwontfix tag is more appropriate than even I suspected.

Do Something, You! Do Something or Face Wrath!

...yeah. I totally get it. I'd be annoyed, too.

The only consolation here is that beartype.typing.TypeAlias is just a trivial alias for typing.TypeAlias. So, please just do this for the moment:

# Instead of this...
from beartype.typing import TypeAlias

# ...please do this!
from typing import TypeAlias

# Pyright should no longer complain about this.
MyTypeHint: TypeAlias = bool | int | str | None

With apologies, I hang my head as tears stream down my dirt-streaked cheeks. Thus ends another tale of woe and debauchery in the pits of Python typing. 🥲

@leycec leycec closed this as completed Jun 23, 2022
@rsokl
Copy link

rsokl commented Jun 23, 2022

I have bad news.

# Instead of this...
from beartype.typing import TypeAlias

# ...please do this!
from typing import TypeAlias

With apologies...

This is the least bad bad news ever; I am used to doing far worse just to get certain typing features to work across Python versions. But this just goes to show that @beartype's standards are sky high! 🚀 🚀 🚀

This is awesome! @beartype is looking real

image

right now. Thank you for this incredible work and for suffering through the herculean effort of supporting both pyright and mypy.

Pretty sure that makes us the first large-scale Python package to explicitly support both

You may be right! At least the first large-scale Python package that is heavily steeped in Python's typing system!

@leycec
Copy link
Member

leycec commented Jun 24, 2022

OMG. That apropos promo shot of a 128-in-1 MegaDrive cart stacked on a classic trash Data East SNES cart stacked on an obscure NES cart with a boss logo even I have never seen before but am now desperately scavenging Google Image for.

I see your 8- and 16-bit childhood nostalgia bit-packing and raise you... the MegaDrive Tower of Doom.

shit just went down

Unrelatedly, GitHub's OpenAI Codex-fueled Copilot just launched. What is this nightmare fuel in IDE form? Now paired programming is an abstract cloud-hosted quasi-daemonic entity critiquing everything I code in real time against everything Guido and Soklaski are coding in real time. That's just great, guys.

Pretty sure this timeline ends with a creepy yet wise Alan Watts AI snarkily suggesting that He will catalyze the Singularity sooner than I can release beartype 1.0.0. And what will gall the most is... Alan Watts AI will not be wrong.

leycec added a commit that referenced this issue Sep 18, 2022
This minor release unleashes a major firestorm of support for **class
decoration,** **colourful exceptions,** **pyright + PyLance + VSCode,**
[PEP 484][PEP 484], [PEP 544][PEP 544], [PEP 561][PEP 561],
[PEP 563][PEP 563], [PEP 585][PEP 585], [PEP 604][PEP 604],
[PEP 612][PEP 612], and [PEP 647][PEP 647].

This minor release resolves a mammoth **29 issues** and merges **12 pull
requests.** Noteworthy changes include:

## Compatibility Improved

* **Class decoration.** The `@beartype` decorator now decorates both
  higher-level classes *and* lower-level callables (i.e., functions,
  methods), resolving feature request #152 kindly submitted by @posita
  the positively sublime. All possible edge cases are supported,
  including:
  * Classes defining methods decorated by builtin decorators: i.e.,
    * Class methods via `@classmethod`.
    * Static methods via `@staticmethod`.
    * Property getters, setters, and deleters via `@property`.
  * Arbitrarily deeply nested (i.e., inner) classes.
  * Arbitrarily deeply nested (i.e., inner) classes whose type hints are
    postponed under [PEP 563][PEP 563].
  Since this was surprisingly trivial, @leycec
  probably should have done this a few years ago. He didn't. This is why
  he laments into his oatmeal in late 2022.
* **[PEP 484][PEP 484]- and [PEP 585][PEP 585]-compliant nested
  generics.** @beartype now supports arbitrarily complex [PEP 484][PEP
  484]- and [PEP 585][PEP 585]-compliant inheritance trees subclassing
  non-trivial combinations of the `typing.Generic` superclass and other
  `typing` pseudo-superclasses, resolving issue #140 kindly submitted by
  @langfield (William Blake – yes, *that* William Blake). Notably,
  this release extricated our transitive visitation of the tree of all
  pseudo-superclasses of any PEP 484- and 585-compliant generic type
  hint (*...don't ask*) from its prior hidden sacred cave deep within
  the private `beartype._decor._code._pep._pephint` submodule into a new
  reusable `iter_hint_pep484585_generic_bases_unerased_tree()`
  generator, which is now believed to be the most fully-compliant
  algorithm for traversing generic inheritance trees at runtime. This
  cleanly resolved all lingering issues surrounding generics,
  dramatically reduced the likelihood of more issues surrounding
  generics, and streamlined the resolution of any more issues
  surrounding generics should they arise... *which they won't.*
  Generics: we have resoundingly beaten you. Stay down, please.
* **[PEP 544][PEP 544] compatibility.** @beartype now supports
  arbitrarily complex [PEP 544][PEP 544]-compliant inheritance trees
  subclassing non-trivial combinations of the `typing.Protocol` +
  `abc.ABC` superclasses, resolving #117 kindly submitted by
  too-entertaining pun master @twoertwein (Torsten Wörtwein).
  Notably, `@beartype` now:
  * Correctly detects non-protocols as non-protocols. Previously,
    @beartype erroneously detected a subset of non-protocols as
    PEP 544-compliant protocols. It's best not to ask why.
  * Ignores both the unsubscripted `beartype.typing.Protocol` superclass
    *and* parametrizations of that superclass by one or more type
    variables (e.g., `beartype.typing.Protocol[typing.TypeVar('T')]`) as
    semantically meaningless in accordance with similar treatment of the
    `typing.Protocol` superclass.
  * Permits caller-defined abstract protocols subclassing our caching
    `beartype.typing.Protocol` superclass to themselves be subclassed by
    one or more concrete subclasses. Previously, attempting to do so
    would raise non-human-readable exceptions from the `typing` module;
    now, doing so behaves as expected.
  * Relaxed our prior bad assumption that the second-to-last superclass
    of all generics – and thus protocols – is the `typing.Generic`
    superclass. That assumption *only* holds for standard generics and
    protocols; non-standard protocols subclassing non-`typing`
    superclasses (e.g., the `abc.ABC` superclass) *after* the list
    `typing` superclass in their method resolution order (MRO)
    flagrantly violate this assumption. Well, that's fine. We're fine
    with that. What's not fine about that? **Fine. This is fine.**
  * Avoids a circular import dependency. Previously, our caching
    `beartype.typing.Protocol` superclass leveraged the general-purpose
    `@beartype._util.cache.utilcachecall.callable_cached decorator` to
    memoize its subscription; however, since that decorator transitively
    imports from the `beartype.typing` subpackage, doing so induced a
    circular import dependency. To circumvent this, a new
    `@beartype.typing._typingcache.callable_cached_minimal` decorator
    implementing only the minimal subset of the full
    `@beartype._util.cache.utilcachecall.callable_cached` decorator has
    been defined; the `beartype.typing` subpackage now safely defers to
    this minimal variant for all its caching needs.
* **[PEP 563][PEP 563] compatibility.** @beartype now resolves [PEP
  563][PEP 563]-postponed **self-referential type hints** (i.e., type
  hints circularly referring to the class currently being decorated).
  **Caveat:** this support requires that external callers decorate the
  *class* being referred to (rather than the *method* doing the
  referring) by the `@beartype` decorator. For this and similar reasons,
  users are advised to begin refactoring their object-oriented codebases
  to decorate their *classes* rather than *methods* with `@beartype`.
* **[PEP 612][PEP 612] partial shallow compatibility.** @beartype now
  shallowly detects [PEP 612][PEP 612]-compliant `typing.ParamSpec`
  objects by internally associating such objects with our
  `beartype._data.hint.pep.sign.datapepsigns.HintSignParamSpec`
  singleton, enabling @beartype to portably introspect
  `Callable[typing.ParamSpec(...), ...]` type hints.
* **Static type-checking.** @beartype is now substantially more
  compliant with static type-checkers, including:
  * **Microsoft [pyright](https://github.com/microsoft/pyright) +
    [PyLance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance)
    + [VSCode](https://visualstudio.com).** @beartype now officially
    supports pyright, Microsoft's in-house static type-checker oddly
    implemented in pure-TypeScript, <sup>*gulp*</sup> resolving issues
    #126 and #127 kindly submitted by fellow Zelda aficionado @rbroderi.
    Specifically, this release resolves several hundred false warnings
    and errors issued by pyright against the @beartype codebase. It is,
    indeed, dangerous to go alone – but we did it anyway.
  * **mypy `beartype.typing.Protocol` compatibility.** The
    @beartype-specific `beartype.typing.Protocol` superclass implementing
    [PEP 544][PEP 544]-compliant fast caching protocols is now fully
    compatible with mypy, Python's official static type-checker.
    Specifically, `beartype.typing.Protocol` now circumvents:
    * python/mypy#11013 by explicitly annotating the type of its
      `__slots__` as `Any`.
    * python/mypy#9282 by explicitly setting the `typing.TypeVar()`
      `bounds` parameter to this superclass.
* **[PEP 647][PEP 647] compatibility.** @beartype now supports
  arbitrarily complex **[type
  narrowing](https://mypy.readthedocs.io/en/latest/type_narrowing.html)**
  in [PEP 647][PEP 647]-compliant static type-checkers (e.g., mypy,
  pyright), resolving issues #164 and #165 kindly submitted in parallel
  by foxy machine learning gurus @justinchuby (Justin Chuby) and @rsokl
  (Ryan Soklaski). Thanks to their earnest dedication, @beartype is now
  believed to be the most fully complete type narrower. Specifically,
  the return of both the `beartype.door.is_bearable()` function and
  corresponding `beartype.door.TypeHint.is_bearable()` method are now
  annotated by the [PEP 647][PEP 647]-compliant `typing.TypeGuard[...]`
  type hint under both Python ≥ 3.10 *and* Python < 3.10 when the
  optional third-party `typing_extensions` dependency is installed.
  Doing so substantially reduces false positives from static type
  checkers on downstream codebases deferring to these callables.
  Thanks so much for improving @beartype so much, @justinchuby and
  @rsokl!
* **`@{classmethod,staticmethod,property}` chaining.** The `@beartype`
  decorator now implicitly supports callables decorated by both
  `@beartype` *and* one of the builtin method decorators `@classmethod`,
  `@staticmethod`, or `@property` regardless of decoration order,
  resolving issue #80 kindly requested by @qiujiangkun (AKA, Type
  Genius-kun). Previously, `@beartype` explicitly raised an exception
  when ordered *after* one of those builtin method decorators. This
  releseae relaxes this constraint, enabling callers to list `@beartype`
  either before or after one of those builtin method decorators.
* **`beartype.vale.Is[...]` integration.** Functional validators (i.e.,
  `beartype.vale.Is[...]`) now integrate more cleanly with the remainder
  of the Python ecosystem, including:
  * **IPython.** Functional validators localized to a sufficiently
    intelligent REPL (e.g., IPython) that caches locally defined
    callables to the standard `linecache` module now raise
    human-readable errors on type-checking, resolving issue #123 kindly
    submitted by typing brain-child @braniii. Relatedly, @beartype now
    permissively accepts both physical on-disk files and dynamic
    in-memory fake files cached with `linecache` as the files defining
    an arbitrary callable.
  * **NumPy,** which publishes various **bool-like tester functions**
    (i.e., functions returning a non-`bool` object whose class defines
    at least one of the `__bool__()` or `__len__()` dunder methods and
    is thus implicitly convertible into a `bool`). Functional validators
    now support subscription by these functions, resolving issue #153
    kindly submitted by molecular luminary @braniii (Daniel Nagel).
    Specifically, @beartype now unconditionally wraps *all* tester
    callables subscripting (indexing) `beartype.vale.Is` with a new
    private `_is_valid_bool()` closure that (in order):
    1. Detects when those tester callables return bool-like objects.
    2. Coerces those objects into corresponding `bool` values.
    3. Returns those `bool` values instead.
* **Moar fake builtin types.**@beartype now detects all known **fake
  builtin types** (i.e., C-based types falsely advertising themselves as
  being builtin and thus *not* require explicit importation), succinctly
  resolving issue #158 kindly submitted by the decorous typing gentleman
  @langfield. Specifically, @beartype now recognizes instances of all of
  the following as fake builtin types:
  * `beartype.cave.AsyncCoroutineCType`.
  * `beartype.cave.AsyncGeneratorCType`.
  * `beartype.cave.CallableCodeObjectType`.
  * `beartype.cave.CallableFrameType`.
  * `beartype.cave.ClassDictType`.
  * `beartype.cave.ClassType`.
  * `beartype.cave.ClosureVarCellType`.
  * `beartype.cave.EllipsisType`.
  * `beartype.cave.ExceptionTracebackType`.
  * `beartype.cave.FunctionType`.
  * `beartype.cave.FunctionOrMethodCType`.
  * `beartype.cave.GeneratorCType`.
  * `beartype.cave.MethodBoundInstanceDunderCType`.
  * `beartype.cave.MethodBoundInstanceOrClassType`.
  * `beartype.cave.MethodDecoratorBuiltinTypes`.
  * `beartype.cave.MethodUnboundClassCType`.
  * `beartype.cave.MethodUnboundInstanceDunderCType`.
  * `beartype.cave.MethodUnboundInstanceNondunderCType`.
  * `beartype.cave.MethodUnboundPropertyNontrivialCExtensionType`.
  * `beartype.cave.MethodUnboundPropertyTrivialCExtensionType`.

## Compatibility Broken

* **Python 3.6.x support dropped.** This release unilaterally drops
  support for the Python 3.6.x series, which somnambulantly collided
  with its End-of-Life (EOL) a year ago and now constitutes a compelling
  security risk. Doing so substantially streamlines the codebase, whose
  support for Python 3.6.x required an unmaintainable writhing nest of
  wicked corner cases. We all now breathe a sigh of contentment in the
  temporary stillness of morning.
* **`beartype.cave` deprecation removals.** This release removes all
  deprecated third-party attributes from the `beartype.cave` submodule.
  The continued existence of these attributes substantially increased
  the cost of importing *anything* from our mostly undocumented
  `beartype.cave` submodule, rendering that submodule even less useful
  than it already is. Specifically, this release removes these
  previously deprecated attributes:
  * `beartype.cave.NumpyArrayType`.
  * `beartype.cave.NumpyScalarType`.
  * `beartype.cave.SequenceOrNumpyArrayTypes`.
  * `beartype.cave.SequenceMutableOrNumpyArrayTypes`.
  * `beartype.cave.SetuptoolsVersionTypes`.
  * `beartype.cave.VersionComparableTypes`.
  * `beartype.cave.VersionTypes`.

## Exceptions Improved

* **Colour** – the sensation formerly known as "color." @beartype now
  emits colourized type-checking violations (i.e.,
  `beartype.roar.BeartypeCallHintViolation` exceptions) raised by both
  `@beartype`-decorated callables *and* statement-level type-checkers
  (e.g., `beartype.door.die_if_unbearable()`,
  `beartype.door.TypeHint.die_if_unbearable()`), resolving issue #161
  kindly submitted by foxy machine learning expert @justinchuby (Justin
  Chu). When standard output is attached to an interactive terminal
  (TTY), ANSII-flavoured colours now syntactically highlight various
  substrings of those violations for improved visibility, readability,
  and debuggability. Since *all* actively maintained versions of Windows
  (i.e., Windows ≥ 10) now widely support ANSII escape sequences across
  both Microsoft-managed terminals (e.g., Windows Terminal) and
  Microsoft-managed Integrated Development Environments (IDEs) (e.g.,
  VSCode), this supports extends to Windows as well. The bad old days of
  non-standard behaviour are behind us all. Thanks *so* much to
  @justinchuby for his immense contribution to the righteous cause of
  eye-pleasing user experience (UX)!
* **Types disambiguated.** @beartype now explicitly disambiguates the
  types of parameters and returns that violate type-checking in
  exception messages raised by the `@beartype` decorator, resolving
  issue #124 kindly submitted by typing brain-child @braniii. Thus was
  justice restored to the QAverse.
* **Stack frame squelched.** @beartype now intentionally squelches
  (i.e., hides) the ignorable stack frame encapsulating the call to our
  private `beartype._decor._error.errormain.get_beartype_violation()`
  getter from the parent type-checking wrapper function generated by the
  :mod:`beartype.beartype` decorator, resolving issue #140 kindly
  submitted by @langfield (William Blake – yes, *that* William Blake).
  That stack frame only needlessly complicated visual inspection of
  type-checking violations in tracebacks – especially from testing
  frameworks like :mod:`pytest` that recapitulate the full definition of
  the `get_beartype_violation()` getter (including verbose docstring) in
  those tracebacks. Specifically, this release:
  * Renamed the poorly named `raise_pep_call_exception()` function to
    `get_beartype_violation()` for clarity.
  * Refactored `get_beartype_violation()` to return rather than raise
    `BeartypeCallHintViolation` exceptions (while still raising all
    other types of unexpected exceptions for robustness).
  * Refactored type-checking wrapper functions to directly raise the
    exception returned by calling `get_beartype_violation()`.
* **``None`` type.** The type of the ``None`` singleton is no longer
  erroneously labelled as a PEP 544-compliant protocol in type-checking
  violations. Let's pretend that never happened.
* **`beartype.abby.die_if_unbearable()` violations.** The
  `beartype.abby.die_if_unbearable()` validator function no longer
  raises non-human-readable exception messages prefixed by the
  unexpected substring `"@beartyped
  beartype.abby._abbytest._get_type_checker._die_if_unbearable()
  return"`. "Surely that never happened, @beartype!"

## Features Added

* **`beartype.door.** @beartype now provides a new public framework for
  introspecting, sorting, and type-checking type hints at runtime in
  constant time. N-n-now... hear me out here. @leycec came up with a
  ludicrous acronym and we're going to have to learn to live with it:
  the **D**ecidedly **O**bject-**O**rientedly **R**ecursive (DOOR) API.
  Or, `beartype.door` for short. Open the door to a whole new
  type-hinting world, everyone. `beartype.door` enables type hint
  arithmetic via an object-oriented type hint class hierarchy
  encapsulating the crude non-object-oriented type hint declarative API
  standardized by the :mod:`typing` module, resolving issues #133 and
  #138 kindly submitted by Harvard microscopist and general genius
  @tlambert03. The new `beartype.door` subpackage defines a public:
  * `TypeHint({type_hint})` superclass, enabling rich comparisons
    between pairs of arbitrary type hints. Altogether, this class
    implements a partial ordering over the countably infinite set of all
    type hints. Pedagogical excitement ensues. Instances of this class
    efficiently satisfy both the `collections.abc.Sequence` and
    `collections.abc.FrozenSet` abstract base classes (ABC) and thus
    behave just like tuples and frozen sets over child type hints.
    Public attributes defined by this class include:
    * A pair of `die_if_unbearable()` and `is_bearable()` runtime
      type-checking methods, analogous in behaviour to the existing
      `beartype.abby.die_if_unbearable()` and
      `beartype.abby.is_bearable()` runtime type-checking functions.
    * `TypeHint.is_bearable()`, currently implemented in terms of the
      procedural `beartype.abby.is_bearable()` tester.
    * An `is_ignorable` property evaluating to `True` only if the
      current type hint is semantically ignorable (e.g., `object`,
      `typing.Any`). There exist a countably infinite number of
      semantically ignorable type hints. The more you know, the less you
      want to read this changeset.
    * The equality comparison operator (e.g., `==`), enabling type hints
      to be compared according to semantic equivalence.
    * Rich comparison operators (e.g., `<=`, `>`), enabling type hints
      to be compared and sorted according to semantic narrowing.
    * A sane `__bool__()` dunder method, enabling type hint wrappers to
      be trivially evaluated as booleans according to the child type
      hints subscripting the wrapped type hints.
    * A sane `__len__()` dunder method, enabling type hint wrappers to
      be trivially sized according to the child type hints subscripting
      the wrapped type hints.
    * A sane `__contains__()` dunder method, enabling type hint wrappers
      to be tested for child type hint membership – just like builtin
      sets, frozen sets, and dictionaries.
    * A sane `__getindex__()` dunder method, enabling type hint wrappers
      to be subscripted by both positive and negative indices as well as
      slices of such indices – just like builtin tuples.
  * `beartype.door.AnnotatedTypeHint` subclass.
  * `beartype.door.CallableTypeHint` subclass.
  * `beartype.door.LiteralTypeHint` subclass.
  * `beartype.door.NewTypeTypeHint` subclass.
  * `beartype.door.TupleTypeHint` subclass.
  * `beartype.door.TypeVarTypeHint` subclass.
  * `beartype.door.UnionTypeHint` subclass.
  * `is_subtype({type_hint_a}, {type_hint_b})` function, enabling
    @beartype users to decide whether any type hint is a **subtype**
    (i.e., narrower type hint) of any other type hint.
  * `beartype.roar.BeartypeDoorNonpepException` type, raised when the
    `beartype.door.TypeHint` constructor is passed an object that is
    *not* a PEP-compliant type hint currently supported by the DOOR API.
  Thanks so much to @tlambert03 for his phenomenal work here. He ran
  GitHub's PR gauntlet so that you did not have to. Praise be to him.
  Some people are the living embodiment of quality. @tlambert03 is one
  such people.
* **`beartype.peps`.** @beartype now publicizes runtime support for
  `typing`-centric Python Enhancement Proposals (PEPs) that currently
  lack official runtime support via a new public subpackage:
  `beartype.peps`. Notably, @beartype now provides:
  . Specifically, this commit:
  * A new public `beartype.peps.resolve_pep563()` function resolving
    [PEP 563][PEP 563]-postponed type hints on behalf of third-party
    Python packages. This function is intended to be "the final word" on
    runtime resolution of [PEP 563][PEP 563]. May no other third-party
    package suffer as we have suffered. This commit is for you,
    everyone. And "by everyone," we of course mostly mean @wesselb of
    [Plum](github.com/wesselb/plum) fame. See also beartype/plum#53.
* **`beartype.vale.Is*[...] {&,|}` short-circuiting.** `&`- and
  `|`-chained beartype validators now explicitly short-circuit when
  raising human-readable exceptions from type-checking violations
  against those validators, resolving issue #125 kindly submitted by
  typing brain-child @braniii.

## Features Optimized

* **`beartype.abby.is_bearable()` when returning `False`.** Previously,
  the public `beartype.abby.is_bearable()` runtime type-checker behaved
  reasonably optimally when the passed object satisfied the passed type
  hint but *extremely* suboptimally when that object violated that hint;
  this was due to our current naive implementation of that tester using
  the standard Easier to Ask for Permission than Forgiveness (EAFP)
  approach. This release fundamentally refactored
  `beartype.abby.is_bearable()` in terms of our new private
  `beartype._check.checkmake.make_func_tester()` type-checking tester
  function factory function. Ad-hoc profiling shows a speedup on
  the order of eight orders of magnitude – the single most intense
  optimization @beartype has ever brought to bear (*heh*). Our core code
  generation API now transparently generates both:
  * **Runtime type-checking testers** (i.e., functions merely returning
    ``False`` on type-checking violations).
  * **Runtime type-checking validators** (i.e., functions raising
    exceptions on type-checking violations).
* **[PEP 604][PEP 604]-compliant new unions** (e.g., `int | str |
  None`). Since these unions are **non-self-caching type hints** (i.e.,
  hints that do *not* implicitly cache themselves to reduce space and
  time consumption), @beartype now efficiently coerces these unions into
  singletons in the same manner as [PEP 585][PEP 585]-compliant type
  hints – which are similarly non-self-caching.

## Features Deprecated

* **`beartype.abby` → `beartype.door`.** This release officially
  deprecates the poorly named `beartype.abby` subpackage in favour of
  the sorta less poorly named `beartype.door` subpackage, whose name
  actually means something – even if that something is a punny acronym
  no one will ever find funny. Specifically:
  * `beartype.abby.die_if_unbearable()` has been moved to
    `beartype.door.die_if_unbearable()`.
  * `beartype.abby.is_bearable()` has been moved to
    `beartype.door.is_bearable()`.
  To preserve backward compatibility, the `beartype.abby` subpackage
  continues to dynamically exist (and thus be importable from) – albeit
  as a deprecated alias of the `beartype.door` subpackage.

## Deprecations Resolved

* **Setuptools licensing.** This release resolves a mostly negligible
  `setuptools` deprecation warning concerning the deprecated
  `license_file` setting in the top-level `setup.cfg` file. *Next!*

## Tests Improved

* **[PEP 544][PEP 544] compatibility.** All [PEP 544][PEP 544]-specific
  test type hints have been generalized to apply to both the non-caching
  `typing.Protocol` superclass *and* our caching
  `beartype.typing.Protocol` superclass.
* **[PEP 561][PEP 561] compatibility via pyright.** Our test suite now
  enforces static type-checking with `pyright`. Notably:
  * A new `test_pep561_pyright` functional test statically type-checks
    the @beartype codebase against the external `pyright` command in the
    current `${PATH}` (if available) specific to the version of the
    active Python interpreter currently being tested. For personal
    sanity, this test is currently ignored on remote continuous
    integration (CI) workflows. Let this shrieking demon finally die!
  * The private `beartype_test.util.cmd.pytcmdrun` submodule underlying
    our cross-platform portable forking of testing subprocesses now
    transparently supports vanilla Windows shells (e.g., `CMD.exe`,
    PowerShell).
* **Tarball compatibility.** `beartype` may now be fully tested from
  non-`git` repositories, including source tarballs containing the
  `beartype_test` package. Previously, three functional tests making
  inappropriate assumptions about the existence of a top-level `.git/`
  directory failed when exercised from a source tarball.
* **Sphinx documentation.** Our test suite now exercises that our
  documentation successfully builds with Sphinx via a new
  `test_sphinx_build()` functional test. This was surprisingly
  non-trivial – thanks to the `pytest`-specific `sphinx.testing`
  subpackage being mostly undocumented, behaving non-orthogonally, and
  suffering a host of unresolved issues that required we monkey-patch
  the core `pathlib.Path` class. Insanity, thy name is Sphinx.
* **GitHub Actions dependencies bumped.** This release bumps our GitHub
  Actions-based continuous integration (CI) workflows to both the
  recently released `checkout@v3` and `setup-python@v3` actions,
  inspired by a pair of sadly closed PRs by @RotekHandelsGmbH CTO
  @bitranox (Robert Nowotny). Thanks so much for the great idea,
  @bitranox!
* **`beartype.door` conformance.** A new smoke test guarantees
  conformance between our DOOR API and abstract base classes (ABCs)
  published by the standard `typing` module.
* **python/mypy#13627 circumvention.** This release pins our GitHub
  Actions-based CI workflow to Python 3.10.6 rather than 3.10.7,
  resolving a mypy-specific complaint inducing spurious test failures.

## Documentation Improved

* **[`beartype.abby`
  documented](https://github.com/beartype/beartype#beartype-at-any-time-api).**
  The new "Beartype At Any Time API" subsection of our front-facing
  `README.rst` file now documents our public `beartype.abby` API,
  resolving issue #139 kindly submitted by @gelatinouscube42 (i.e., the
  user whose username is the answer to the question: "What is the
  meaning of collagen sustainably harvested from animal body parts?").
* **[GitHub Sponsors activated](https://github.com/sponsors/leycec).**
  @beartype is now proudly financially supported by **GitHub Sponsors.**
  Specifically, this release:
  * Defines a new GitHub-specific funding configuration (i.e.,
    `.github/FUNDING.yml`).
  * Injects a hopefully non-intrusive advertising template
    <sup>*gulp*</sup> at the head of our `README.rst` documentation.
* **Sphinx configuration sanitized.** As the first tentative step
  towards chain refactoring our documentation from its current
  monolithic home in our top-level `README.rst` file to its eventual
  modular home at [ReadTheDocs (RTD)](https://beartype.readthedocs.io),
  en-route to resolving issue #8 (!) kindly submitted a literal lifetime
  ago by visionary computer vision export and long-standing phenomenal
  Finn @felix-hilden (Felix Hildén):
  * Our core Sphinx configuration has been resurrected from its early
    grave – which now actually builds nothing without raising errors. Is
    this an accomplishment? In 2022, mere survival is an accomplishment!
    So... *yes.* Significant improvements include:
    * Activation and configuration of the effectively mandatory
      `autosectionlabels` builtin Sphinx extension.
  * Our `doc/source/404.rst` file has been temporarily moved aside,
    resolving a non-fatal warning pertaining to that file. Look, we're
    not here to actually solve deep issues; we're here to just get
    documentation building, which it's not. Sphinx, you have much to
    answer for.
  * Our top-level `sphinx` entry point now:
    * Temporarily disables Sphinx's nit-picky mode (i.e., the `-n`
      option previously passed to `sphinx-build`) due to Sphinx's
      `autodoc` extension locally failing to generate working
      references.
    * Unconditionally disables Sphinx caching by forcing *all* target
      documentation files to be rebuilt regardless of whether their
      underlying source files have since been modified or not, obviating
      spurious build issues.

  [PEP 484]: https://www.python.org/dev/peps/pep-0484/
  [PEP 544]: https://www.python.org/dev/peps/pep-0544/
  [PEP 561]: https://www.python.org/dev/peps/pep-0561/
  [PEP 563]: https://www.python.org/dev/peps/pep-0563/
  [PEP 585]: https://www.python.org/dev/peps/pep-0585/
  [PEP 604]: https://www.python.org/dev/peps/pep-0604/
  [PEP 612]: https://www.python.org/dev/peps/pep-0612/
  [PEP 647]: https://www.python.org/dev/peps/pep-0647/

(*Impossible journey on an implacable placard-studded gurney!*)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
upstreamwontfix Someone else broke something, they refuse to fix it, and now they blame us for it like usual whatyougonnado We wish we could fix this for you, but we can't, because wishes are like magical unicorns
Projects
None yet
Development

No branches or pull requests

4 participants