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

Cannot use numpy.typing.NDArray type hints #42

Closed
Antyos opened this issue Jul 11, 2021 · 12 comments
Closed

Cannot use numpy.typing.NDArray type hints #42

Antyos opened this issue Jul 11, 2021 · 12 comments

Comments

@Antyos
Copy link

Antyos commented Jul 11, 2021

Numpy v1.21.0 introduced runtime subscriptable aliases for ndarrays. The feature is really cool and I really like the direction that it seems to be headed in.

However, these new annotations do not seem compatible with the @beartype decorator.

System info

OS: Windows 10, 20H2
Python: 3.8.10
Beartype: 0.71.0
Numpy: 1.21.0

Sample Code

Running this sample code:

from beartype import beartype
import numpy as np
import numpy.typing as npt

@beartype
def foo(arr: npt.NDArray[np.float64]) -> npt.NDArray[np.float64]:
    return arr

foo(np.zeros(3))

Yields this error:

Traceback (most recent call last):
  File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_util\cls\utilclstest.py", line 174, in die_unless_type_isinstanceable
    isinstance(None, cls)  # type: ignore[arg-type]
  File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\numpy\typing\_generic_alias.py", line 137, in __instancecheck__
    raise TypeError("isinstance() argument 2 cannot be a "
TypeError: isinstance() argument 2 cannot be a parameterized generic

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_decor\main.py", line 268, in beartype
    func_wrapper_code = generate_code(func_data)
  File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_decor\_code\codemain.py", line 162, in generate_code
    code_params = _code_check_params(data)
  File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_decor\_code\codemain.py", line 324, in _code_check_params
    hint = coerce_hint_pep(
  File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_decor\_cache\cachehint.py", line 254, in coerce_hint_pep
    die_unless_hint(hint=hint, hint_label=hint_label)
  File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_util\hint\utilhinttest.py", line 104, in die_unless_hint
    die_unless_hint_nonpep(hint=hint, hint_label=hint_label)
  File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_util\hint\nonpep\utilhintnonpeptest.py", line 111, in die_unless_hint_nonpep
    die_unless_hint_nonpep_type(
  File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_util\hint\nonpep\utilhintnonpeptest.py", line 200, in die_unless_hint_nonpep_type
    die_unless_type_isinstanceable(
  File "C:\Users\Antyos\Code\Project\.venv\lib\site-packages\beartype\_util\cls\utilclstest.py", line 208, in die_unless_type_isinstanceable
    raise exception_cls(exception_message) from exception
beartype.roar.BeartypeDecorHintNonPepException: @beartyped foo() parameter "arr" type hint numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]] uncheckable at runtime (i.e., not passable as 
second parameter to isinstance() due to raising "isinstance() argument 2 cannot be a parameterized generic" from metaclass __instancecheck__() method).

Knowing that Beartype leverages the Python built-in Annotated type, it roughly makes sense why this error would be occurring. While creating a Beartype numpy array using Annotated and lambda still has its uses, static type checkers such as Pylance/Pyright are now equipped to recognize this new Numpy typing syntax (that's how I initially found out about this change; my code had a ton of new errors I didn't understand), but I don't know if they can recognize the Beartype version in the same way.

@leycec
Copy link
Member

leycec commented Jul 13, 2021

Fantastic find. Thanks so much for the heads up on this, Antyos. Tensor typing is in our critical section, which makes this a high-priority feature request for us.

Fortunately, NumPy developers sensibly implemented their high-level numpy.typing.NDArray type hint in terms of the standard types.GenericAlias type hint factory first introduced with Python 3.9.0. That's great, because it means @beartype can trivially detect numpy.typing.NDArray type hints under Python ≥ 3.9.0 using well-established standards we already support:

$ ipython3.9
>>> import numpy as np
>>> import numpy.typing as npt
>>> hint = npt.NDArray[np.float64]
>>> print(f'NDArray[n.float64] type: {type(hint)}')
NDArray[n.float64] class: <class 'types.GenericAlias'>

Unfortunately, NumPy developers kinda non-sensibly reimplemented the same numpy.typing.NDArray type hint in terms of their own private non-standard numpy.typing._generic_alias._GenericAlias type hint factory under Python < 3.9.0:

$ ipython3.8
>>> import numpy as np
>>> import numpy.typing as npt
>>> hint = npt.NDArray[np.float64]
>>> print(f'NDArray[n.float64] type: {type(hint)}')
NDArray[n.float64] class: <class 'numpy.typing._generic_alias._GenericAlias'>

If they'd just made numpy.typing._generic_alias._GenericAlias public rather than private, it would've been trivial for @beartype to detect that too. Instead, they privatized it. @beartype never violates privacy encapsulation in third-party packages, because @beartype doesn't want to become a flying pile of burning wreckage out on the tarmac of PyPI. So, that's a no-fly zone.

But... that's mostly okay. Under:

  • Python ≥ 3.9.0, I'd be delighted to add conditional support for numpy.typing.NDArray to the next stable beartype release!
  • Python < 3.9.0, we'll probably continue raising an unpleasant exception with all of the apologetic tears I can shed. We can't even reliably detect numpy.typing.NDArray type hints under Python < 3.9.0, because their type is private, obfuscated, and really not intended to be externally accessed.

Curious Bear hopes that sounds like a reasonable compromise.

curious bear is curious

@Antyos
Copy link
Author

Antyos commented Jul 13, 2021

This sounds great! Glad to see this will be supported!

It's unfortunate that this won't work with Python < 3.9, but I understand why and agree with your decision. Is there a __futures__ import that would make it work in 3.8?

Unfortunately, the project I'm working on is stuck on 3.8 due to certain dependencies but being compatible with later versions. However, I'm looking forward to implementing beartype in future projects or when my current project can be updated.

@leycec
Copy link
Member

leycec commented Jul 14, 2021

It's unfortunate that this won't work with Python < 3.9...

...I share this disappointment. Gatekeeping numpy.typing behind bleeding-edge Python versions isn't a good look.

Come to think, there might actually be a circuitous way for us to slyly detect numpy.typing.NDArray type hints under Python < 3.9:

$ ipython3.8
>>> import numpy as np
>>> import numpy.typing as npt
>>> hint = npt.NDArray[np.float64]
>>> getattr(hint, '__origin__', None) is np.ndarray
True

My eyes... they bleed.

So, getattr(hint, '__origin__', None) is np.ndarray may be a hacky, cumbersome, and inefficient kludge – but it has the benefit of actually reliably detecting numpy.typing.NDArray type hints under Python < 3.9 without violating privacy encapsulation. That means we're going to do it. 🤯

Is there a __futures__ import that would make it work in 3.8?

Sadly, no! But at least I get to blame someone else again. In this case, PEP 585 authors are to blame; they failed to backport support for the __class_getitem__() dunder method underlying magical syntax like npt.NDArray[np.float64] to the __futures__ mechanism under Python < 3.9, despite fraudulently claiming in PEP 585 they had done just that. Mostly everything in the "Backwards Compatibility" "Implementation" sections of PEP 585 is a deceptive masquerade that doesn't work at runtime.

But this must be your lucky GitHub day. @beartype can just stealthily hack around the lack of __futures__ support with the above stupidity cleverness under Python < 3.9, which means you won't even have to import anything additional (from __futures__ or otherwise) to get this to work on your side. It'll just work.

Unfortunately, the project I'm working on is stuck on 3.8 due to certain dependencies but being compatible with later versions.

I definitely get that and feel your radiating dev pain. Would @beartype transparently supporting numpy.typing.NDArray type hints across all Python versions via the above stealth hack change your mind about adopting @beartype?

We aim to please data scientists here. If we ever stop doing that, we have failed in our primary mission... and the bear doesn't tolerate failure. Let me know what you need and we'll make it so.

@Antyos
Copy link
Author

Antyos commented Jul 14, 2021

You weren't kidding about this being my lucky GitHub day, wow! All of this sounds amazing and will absolutely be adding beartype to this project and probably all my future projects too. I'm looking forward to the update!

P.S. Why are you so amazing??? 🤩

@leycec
Copy link
Member

leycec commented Jul 15, 2021

awwww 🤗

Why are you so incredibly nice, that's what I want to know! Relatedly, I actually thought of an even awfuller cleverer one-liner for portably detecting numpy.typing.NDArray type hints across Python versions: repr(hint).startswith('numpy.ndarray['). Despite being hideously obfuscatory and basically ineffable, it actually works:

$ ipython3.8
>>> import numpy as np
>>> import numpy.typing as npt
>>> hint = npt.NDArray[np.float64]
>>> repr(hint).startswith('numpy.ndarray[')
True

$ ipython3.9
>>> import numpy as np
>>> import numpy.typing as npt
>>> hint = npt.NDArray[np.float64]
>>> repr(hint).startswith('numpy.ndarray[')
True

I horrify even myself. 🙀

@Antyos
Copy link
Author

Antyos commented Jul 15, 2021

That has to be one of the most elegant hideous line of code I have ever laid eyes on. 😂

leycec added a commit that referenced this issue Jul 16, 2021
This commit is the first in a commit chain adding portable support for
the third-party PEP-noncompliant `numpy.typing.NDArray` type hint newly
introduced with NumPy >= 1.21.0, en route to resolving issue #42 kindly
submitted by NumPy extraordinaire @Antyos. Specifically, this commit
improves the robustness of type hint classification performed by the
`beartype._util.hint.pep.utilhintpepget.get_hint_pep_sign_or_none()`
getter, which now supports classification of third-party type hints in
general `numpy.typing.NDArray` type hints in specific. (*Disastrous asters!*)
@leycec
Copy link
Member

leycec commented Jul 16, 2021

So... It Begins. fcb24d3 now detects numpy.typing.NDArray type hints, but how do we handle them?

We don't. Yes, the solution is both brilliant and yet stupid at the same time! We just internally map numpy.typing.NDArray type hints that we don't want to explicitly handle into equivalent typing.Annotated type hints powered by beartype.vale.IsAttr and beartype.vale.IsEqual validators that we already recursively handle across the entire codebase with extensive unit testing. That means we're mostly already done here:

# Beartype 0.8.0 will internally map this "hard"
# annotated callable we don't want to handle...
@beartype
def rock_the_data_science(the_array_dont_like_it:
    npt.NDArray[np.float64]) -> None: ...

# ...to look this this "easy" annotated callable we
# already handle! Because what's not easy about
# a triply-nested type hint requiring imports across
# three wildly divergent open-source frameworks?
@beartype
def rock_the_data_science(the_array_dont_like_it:
    Annotated[np.ndarray, IsAttr['dtype', IsAttr['type', IsEqual[np.float64]]]]
) -> None: ...

The last trick up my hipster flannel will be differentiating between:

  • numpy.dtype.type objects like np.float64, which actually isn't a dtype even though NumPy externally pretends it is. Gracias, NumPy.
  • numpy.dtype objects like np.dtype('>i4'), which actually is a dtype even though nobody has ever seen one of those in real life.

Let's see how much caffeine I can inject straight into my varicose veins this Friday evening. ☕ ☕ ☕

leycec added a commit that referenced this issue Jul 20, 2021
This commit is the next in a commit chain adding portable support for
the third-party PEP-noncompliant `numpy.typing.NDArray` type hint newly
introduced with NumPy >= 1.21.0, en route to resolving issue #42 kindly
submitted by NumPy extraordinaire @Antyos. Specifically, this commit
defines a new `beartype._util.hint.pep.utilpepattr` submodule and
`import_typing_attr()` function dynamically importing and returning
type hint factories like `Annotated` from whichever of the `typing` and
`typing_extensions` modules (in that order) declare those factories.
(*Basic acidification edifies efficacious eddies!*)
leycec added a commit that referenced this issue Jul 22, 2021
This commit is the next in a commit chain adding portable support for
the third-party PEP-noncompliant `numpy.typing.NDArray` type hint newly
introduced with NumPy >= 1.21.0, en route to resolving issue #42 kindly
submitted by NumPy extraordinaire @Antyos. Specifically, this commit
refactors the pivotal
`beartype._util.hint.pep.utilpeptest.is_hint_pep()` tester to defer to
the even-more-pivotal
`beartype._util.hint.pep.utilpeptest.get_hint_pep_sign_or_none()`
getter, dramatically streamlining detection of PEP-compliant type hints.
(*Misbegotten hierophants misfire triumphantly!*)
leycec added a commit that referenced this issue Jul 24, 2021
This commit is the next in a commit chain adding portable support for
the third-party PEP-noncompliant `numpy.typing.NDArray` type hint newly
introduced with NumPy >= 1.21.0, en route to resolving issue #42 kindly
submitted by NumPy extraordinaire @Antyos. Specifically, this commit
defines a new `beartype._util.hint.get_hint_reduced()` function
centralizing type hint reduction logic previously duplicated (*gulp*)
throughout the codebase as well as declaring a new
`beartype._util.hint.nonpep.mod` subpackage,
`beartype._util.hint.nonpep.mod.utilnonpepnumpy` submodule, and
`get_hint_numpy_ndarray_dtype()` getter in that submodule safely
retrieving the dtype subscripting `numpy.typing.NDArray` type hints.
(*Quixotic coterie!*)
@leycec leycec closed this as completed in 9794558 Jul 28, 2021
@leycec
Copy link
Member

leycec commented Jul 29, 2021

Great success. I accidentally closed this with a commit message yesterday, but that's possibly okay because I'm fairly certain that @beartype now fully supports all possible numpy.typing.NDArray type hints! We rejoice!

...at least, we would rejoice if I hadn't used weasel words like "accidentally," "possibly okay", and "fairly certain" above. I should really exhaustively test this a bit more and then write readable documentation. Why is that always a challenge? ...the struggle is real

I'm aiming to release both this and #34 – yup, it's full typing_extensions support – as beartype 0.8.0 this Friday. But that might get pushed to early next week if I decide to play trashy Japanese video games instead. Let's see what happens! The excitement is palpable.

leycec added a commit that referenced this issue Jul 31, 2021
This commit is the next in a commit chain adding portable support for
the third-party PEP-noncompliant `numpy.typing.NDArray` type hint newly
introduced with NumPy >= 1.21.0, en route to resolving issue #42 kindly
submitted by NumPy extraordinaire @Antyos. Specifically, this commit
resolves all pending issues in our private
`beartype._util.hint.nonpep.mod.utilmodnumpy.reduce_hint_numpy_ndarray()`
function as well as testing this function with a rudimentary unit test.
Additional testing and documentation is still warranted, because safety
first saves codebases. (*Scorched excoriation!*)
leycec added a commit that referenced this issue Aug 4, 2021
This commit is the next in a commit chain adding portable support for
the third-party PEP-noncompliant `numpy.typing.NDArray` type hint newly
introduced with NumPy >= 1.21.0, en route to resolving issue #42 kindly
submitted by NumPy extraordinaire @Antyos. Specifically, this commit
significantly improves testing of our private
`beartype._util.hint.nonpep.mod.utilmodnumpy.reduce_hint_numpy_ndarray()`
function. Additional testing and documentation is still warranted,
because safety first saves codebases. (*Decorative oratory!*)
leycec added a commit that referenced this issue Aug 10, 2021
This commit is the next in a commit chain adding portable support for
the third-party PEP-noncompliant `numpy.typing.NDArray` type hint newly
introduced with NumPy >= 1.21.0, en route to resolving issue #42 kindly
submitted by NumPy extraordinaire @Antyos. Specifically, this commit
finalizes improves testing of our private
`beartype._util.hint.nonpep.mod.utilmodnumpy.reduce_hint_numpy_ndarray()`
function. Additional documentation is still warranted, because safety
first saves codebears. (*Betides eventide!*)
leycec added a commit that referenced this issue Aug 12, 2021
This commit is the next in a commit chain adding portable support for
the third-party PEP-noncompliant `numpy.typing.NDArray` type hint newly
introduced with NumPy >= 1.21.0, en route to resolving issue #42 kindly
submitted by NumPy extraordinaire @Antyos. Specifically, this commit
safeguards our critical
`beartype._util.mod.utilmodimport.import_module_attr_or_none()` importer
against common edge cases, including the unimportability of the passed
module – which now safely emits the new non-fatal
`BeartypeModuleUnimportableWarning` rather than unsafely raising a fatal
exception. Additional documentation is still warranted, because safety
first saves codebears. (*Dramatically rambling llamas!*)
leycec added a commit that referenced this issue Aug 18, 2021
This release brings undeniable support for **typed NumPy arrays** (i.e.,
`numpy.typed.NDArray` type hints), **typing backports** (i.e., public
public attributes of the third-party `typing_extensions` package,
enabling typing_ types introduced by newer Python versions to be used
under older Python versions), and **portable beartype validators**
(i.e., `beartype.vale` type hints usable under *all* Python versions via
the `typing_extensions.Annotated` backport). This release resolves **4
issues** and merges **2 pull requests.** Changes include:

## Compatibility Improved

* **Typed NumPy arrays** (i.e., `numpy.typed.NDArray` type hints). The
  `@beartype` decorator now transparently supports the third-party
  `numpy.typing.NDArray` type hint newly introduced by NumPy >= 1.21.0,
  resolving issue #42 kindly submitted by NumPy extraordinaire @Antyos.
  Note that usage of typed NumPy arrays under Python < 3.9.0 requires
  installation of the third-party `typing-extensions` package, which
  `@beartype` will then automagically detect and leverage internally.
* **Typing backports** (i.e., `typing_extensions` type hints). The
  `@beartype` decorator now transparently supports all public type hints
  of the third-party `typing_extensions` package, resolving issue #34
  kindly submitted by Ryan Soklaski (@rsokl) of considerable *MIT Beaver
  Works Summer Institute* and *Python Like You Mean It* fame.
  Note that usage of typed NumPy arrays under Python < 3.9.0 requires
  installation of the third-party `typing-extensions` package, which
  `@beartype` will then automagically detect and leverage internally.
* **Python < 3.9.0 beartype validators.** The `@beartype` decorator now
  portably supports **beartype validators** (i.e., `beartype.vale`
  objects annotating `typing{_extensions}.Annotated` type hints) across
  all Python versions, *also* resolving issue #34 kindly submitted by
  Ryan Soklaski (@rsokl). <sup>hallowed be his username</sup> Note that
  usage of beartype validators under Python < 3.9.0 requires:
  * Installation of the third-party `typing-extensions` package.
  * Annotation of `beartype.vale` objects by
    `typing_extensions.Annotated` (rather than `typing.Annotated`).
* **Python >= 3.10.0 `typing.NewType` type hints.** This release
  resolves a minor incompatibility recently introduced by Python
  3.10.0rc1, which (*waitforit*) broke backward compatibility with prior
  implementations of the public `typing.NewType` type hint. Previously,
  that hint was implemented as a closure; Python ≥ 3.10 fundamentally
  refactored that hint to a pure-Python class instance – significantly
  complicating cross-version detection. *yuwastemytime,* CPython?
* **Binary dunder method return annotations** (i.e., dunder methods
  accepting exactly two arguments). Previously, `@beartype` adopted
  mypy's permissive approach of implicitly coercing the return
  annotations of binary dunder methods returning only booleans to
  instead return `typing.Union[bool, type(NotImplemented)]`. `beartype`
  now expands that approach to *all* binary dunder methods regardless of
  return annotation. Thanks to pull request #40 from Matt Bogosian
  (@posita), Dropbox's positive code genius!

## Compatibility Broken

* **None.** This release preserves backward compatibility with the prior
  stable release.

## Issues Resolved

* **#44,** documenting beartype's new project shield generously ginned
  up by Matt Bogosian (@posita), Dropbox's positive code genius.
* **#42,** enabling typed NumPy arrays to be used under both Python ≥
  3.9.0 natively *and* Python < 3.9.0 via the
  `typing_extensions.Annotated` backport.
* **#40,** including:
  * **Fake builtin types** (i.e., non-builtin classes erroneously
    masquerading as builtin). Previously, `@beartype` incorrectly
    generated syntactically invalid code for decorated callables
    annotated by one or more fake builtin types – which, miraculously,
    there are hardly any of. Still... *it's not a good look, beartype.*
    Thanks to Matt Bogosian (@posita), Dropbox's positive code genius,
    for first detecting this horrifying edge case in pull request #40.
* **#34,** enabling beartype validators to be used under Python < 3.9.0
  via the `typing_extensions.Annotated` backport.

## Documentation Revised

* **Badge** (i.e., shield intended for display by downstream projects).
  Our front-facing `README.rst` documentation now provides a
  post-installation advisory suggesting public declaration of @beartype
  project compatibility with reST- and Markdown-friendly text exhibiting
  a beautiful @beartype badge. Unsurprisingly, @posita made that too...
  because what doesn't @posita do? Nuthin'! I'm pretty sure that means
  @posita does everything.

## Tests Improved

* **Optional dependencies.** Our GitHub Actions-based continuous
  integration (CI) configuration now installs optional test-time
  dependencies. Although `beartype` has *no* mandatory runtime
  dependencies, fully exercising all tests necessitates installing these
  test-time dependencies:

  * mypy, enabling tests for mypy-based PEP 561 compliance.
  * NumPy, enabling tests for `numpy.typing.NDArray` type hints. This
    proved surprisingly non-trivial. Apple's patently broken
    "Accelerate" BLAS replacement (as documented at numpy/numpy#15947)
    blocks NumPy ≥ 1.18.0 installation under default CI configurations,
    necessitating that we:
    * Only conditionally install `pip` packages with `--force-reinstall`
      under macOS. CI gonna CI.
    * Avoid installing NumPy altogether under macOS + PyPy, where even
      the `--force-reinstall` option fails to improve matters. You can
      only do so much when your core platform is fundamentally broken.
      Thanks, Apple.
  * `typing_extensions`, enabling tests for `typing` attributes
    unavailable under the active Python interpreter.

Thanks yet again to Badge Connoisseur @posita for his tremendous
efforts – which we are now eternally indebted to and can only repay
across the span of several gruelling lifetimes. "It will all be worth
it," I tell my future self.

(*Clogged clogs and unlogged cogs!*)
@braniii
Copy link

braniii commented Aug 18, 2021

It is so nice to see the impressive pace at which beartype is developing! Now that NDArray is supported by the latest release, one could add the support of ArrayLike to the roadmap. Just in case you are running out of todos ;)

Here an example code executed with py3.8 and beartype 0.8.0:

import numpy as np
from beartype import beartype
from numpy.typing import ArrayLike

@beartype
def foo(arr: ArrayLike[np.float64]) -> None:
    return


foo(np.zeros(3).astype(np.float64))

Raises following error:

Traceback (most recent call last):
  File "asarray.py", line 6, in <module>
    def foo(arr: ArrayLike[np.float64]) -> None:
  File "/home/***/anaconda3/envs/py3.8/lib/python3.8/typing.py", line 261, in inner
    return func(*args, **kwds)
  File "/home/***/anaconda3/envs/py3.8/lib/python3.8/typing.py", line 686, in __getitem__
    _check_generic(self, params)
  File "/home/***/anaconda3/envs/py3.8/lib/python3.8/typing.py", line 211, in _check_generic
    raise TypeError(f"{cls} is not a generic class")
TypeError: typing.Union[typing.Sequence[typing.Sequence[typing.Sequence[typing.Sequence[typing.Sequence[typing.Any]]]]], numpy.typing._array_like._SupportsArray[ForwardRef('dtype[Any]')], typing.Sequence[numpy.typing._array_like._SupportsArray[ForwardRef('dtype[Any]')]], typing.Sequence[typing.Sequence[numpy.typing._array_like._SupportsArray[ForwardRef('dtype[Any]')]]], typing.Sequence[typing.Sequence[typing.Sequence[numpy.typing._array_like._SupportsArray[ForwardRef('dtype[Any]')]]]], typing.Sequence[typing.Sequence[typing.Sequence[typing.Sequence[numpy.typing._array_like._SupportsArray[ForwardRef('dtype[Any]')]]]]], bool, int, float, complex, str, bytes, typing.Sequence[typing.Union[bool, int, float, complex, str, bytes]], typing.Sequence[typing.Sequence[typing.Union[bool, int, float, complex, str, bytes]]], typing.Sequence[typing.Sequence[typing.Sequence[typing.Union[bool, int, float, complex, str, bytes]]]], typing.Sequence[typing.Sequence[typing.Sequence[typing.Sequence[typing.Union[bool, int, float, complex, str, bytes]]]]]] is not a generic class

@leycec
Copy link
Member

leycec commented Aug 19, 2021

It is so nice to see the impressive pace at which beartype is developing!

aww, u so nice! 😊

Now that NDArray is supported by the latest release, one could add the support of ArrayLike to the roadmap.

Yes. Let's add ever more back-breaking features onto my drooping shoulders. Actually, I think that's just my bursitis acting up. Curse this geriatric body. 👴

In all seriousness, this is a great idea. So it shall be! I was hoping the remainder of the numpy.typing subpackage would work out-of-the-box. I am a utopian dreamer.

Lucky for me, the traceback is suggestive of an upstream issue. numpy.typing.ArrayLike appears to be inducing infinite recursion from within the typing module at runtime under at least Python < 3.9.0. This is reaaal sus af:

TypeError: typing.Union[typing.Sequence[typing.Sequence[typing.Sequence[
typing.Sequence[typing.Sequence[typing.Any]]]]], 
numpy.typing._array_like._SupportsArray[ForwardRef('dtype[Any]')], 
typing.Sequence[numpy.typing._array_like._SupportsArray[ForwardRef('dtype[Any]')]], 
typing.Sequence[typing.Sequence[numpy.typing._array_like._SupportsArray[
ForwardRef('dtype[Any]')]]], 
typing.Sequence[typing.Sequence[typing.Sequence[
numpy.typing._array_like._SupportsArray[ForwardRef('dtype[Any]')]]]], 
typing.Sequence[typing.Sequence[typing.Sequence[
typing.Sequence[numpy.typing._array_like._SupportsArray[
ForwardRef('dtype[Any]')]]]]], bool, int, float, complex, str, bytes, typing.Sequence[
typing.Union[bool, int, float, complex, str, bytes]], 
typing.Sequence[typing.Sequence[typing.Union[
bool, int, float, complex, str, bytes]]], typing.Sequence[typing.Sequence[typing.Sequence[
typing.Union[bool, int, float, complex, str, bytes]]]], 
typing.Sequence[typing.Sequence[typing.Sequence[
typing.Sequence[typing.Union[bool, int, float, complex, str, bytes]]]]]]
is not a generic class

Yikes. Let's grep around the NumPy issue tracker and see if we can't find anyone shouting vociferously about similar issues. That exception scares me like only grizzly bears snuffling through our compost pile in October can. 😮

@skumargupta-1
Copy link

👋

Has this support been added to the awesome DOOR api?

>>> from beartype import beartype
>>> @beartype
... def foo(t: npt.NDArray[np.float64]):
...     print(t)
...
>>> n = np.array([1, 2, 3], dtype=np.float64)
>>> foo(n)
[1. 2. 3.]

works great! but...

>>> from beartype.door import TypeHint
>>> TypeHint(npt.NDArray[np.float64])
Traceback (most recent call last):
  Cell In[40], line 1
    TypeHint(npt.NDArray[np.float64])
...
BeartypeDoorNonpepException: Type hint numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]] invalid
(i.e., either PEP-noncompliant or PEP-compliant but currently unsupported by "beartype.door.TypeHint").

Thanks for the great library!

@leycec
Copy link
Member

leycec commented Jun 7, 2023

Agh! Your astute observation is correct; the beartype.door API currently lacks support for NumPy type hints. I didn't actually realize that anyone except @wesselb and his wonderful multiple-dispatch Plum crowd was even aware of our secretive DOOR API. The more I know, the more powerful @beartype becomes.

Fortunately, we already have an issue tracking this feature request! That's the good news. Unfortunately, I'm inclined to play Japanese role-playing games rather than do actual work... That's the bad news. Tune in to issue #212 to see which wins. 🐻

Lnaden added a commit to Lnaden/QCElemental that referenced this issue Aug 11, 2023
…ping and pre Python3.9 in tandem with typing_extensions Annotation

Source information: beartype/beartype#42

Annotated checks against instances of _GenericAlias to see if you can support Data[type_info], e.g. NDArray[int] (in Python types since 3.9, and kinda of as _GenricAlias before that.) Prior to 3.9, Numpy implemented their own version of _GenericAlias which isn't Python _GenericAlias, so the types are not considered "Generic" when Annotated[NDArray, Metadata][type_info] does its thing. So. This fix code does the heavy lifting to re-cast the NDArray type with _GenericAlias from python typing. I've tried to reuse as much data from NDArray as I possibly can and still use np.ndarray (which is not np.typing.NDArray) to still correctly type hint np.ndarrays.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants