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

[Feature Request] Resuscitate PyPy support #324

Open
jvesely opened this issue Feb 13, 2024 · 5 comments
Open

[Feature Request] Resuscitate PyPy support #324

jvesely opened this issue Feb 13, 2024 · 5 comments

Comments

@jvesely
Copy link

jvesely commented Feb 13, 2024

beartype crashes when run on the following annotated function:

    @beartype
    def print_entries(self,
                      entries: Optional[Union[str, list, 'Component']] = 'all',
                      width: int = 120,
                      display: Union[Literal['time', 'context', 'value', 'all'], list] = 'all',
                      contexts=NotImplemented,
                      exclude_sims=False,
                      # long_context=False
                      ):

with the following stack trace:

(py39-venv) root@5e79b71ebfdb:~/PsyNeuLink# pytest
ImportError while loading conftest '/root/PsyNeuLink/conftest.py'.
conftest.py:10: in <module>
    import psyneulink
psyneulink/__init__.py:35: in <module>
    from . import core  # noqa: E402
psyneulink/core/__init__.py:1: in <module>
    from . import components
psyneulink/core/components/__init__.py:26: in <module>
    from psyneulink.core.globals.keywords import PROJECTION_SENDER, PROJECTION_TYPE
psyneulink/core/globals/__init__.py:3: in <module>
    from . import mdf
psyneulink/core/globals/mdf.py:107: in <module>
    from psyneulink.core.globals.parameters import ParameterAlias
psyneulink/core/globals/parameters.py:320: in <module>
    from psyneulink.core.globals.log import LogCondition, LogEntry, LogError
psyneulink/core/globals/log.py:584: in <module>
    class Log:
psyneulink/core/globals/log.py:1123: in Log
    def print_entries(self,
../py39-venv/lib/python3.9/site-packages/beartype/_decor/decorcache.py:77: in beartype
    return beartype_object(obj, conf)
../py39-venv/lib/python3.9/site-packages/beartype/_decor/decorcore.py:83: in beartype_object
    _beartype_object_fatal(obj, conf=conf, **kwargs)
../py39-venv/lib/python3.9/site-packages/beartype/_decor/decorcore.py:132: in _beartype_object_fatal
    beartype_nontype(obj, **kwargs)  # type: ignore[return-value]
../py39-venv/lib/python3.9/site-packages/beartype/_decor/_decornontype.py:177: in beartype_nontype
    return beartype_func(obj, **kwargs)  # type: ignore[return-value]
../py39-venv/lib/python3.9/site-packages/beartype/_decor/_decornontype.py:242: in beartype_func
    func_wrapper_code = generate_code(bear_call)
../py39-venv/lib/python3.9/site-packages/beartype/_decor/wrap/wrapmain.py:186: in generate_code
    code_check_params = _code_check_args(bear_call)
../py39-venv/lib/python3.9/site-packages/beartype/_decor/wrap/wrapmain.py:496: in _code_check_args
    reraise_exception_placeholder(
../py39-venv/lib/python3.9/site-packages/beartype/_util/error/utilerrorraise.py:215: in reraise_exception_placeholder
    raise exception.with_traceback(exception.__traceback__)
../py39-venv/lib/python3.9/site-packages/beartype/_decor/wrap/wrapmain.py:466: in _code_check_args
    ) = make_code_raiser_func_pith_check(
../py39-venv/lib/python3.9/site-packages/beartype/_util/cache/utilcachecall.py:259: in _callable_cached
    raise exception
../py39-venv/lib/python3.9/site-packages/beartype/_util/cache/utilcachecall.py:251: in _callable_cached
    return_value = args_flat_to_return_value[args_flat] = func(
../py39-venv/lib/python3.9/site-packages/beartype/_check/checkmake.py:290: in make_code_raiser_func_pith_check
    ) = make_check_expr(hint, conf, cls_stack)
../py39-venv/lib/python3.9/site-packages/beartype/_util/cache/utilcachecall.py:259: in _callable_cached
    raise exception
../py39-venv/lib/python3.9/site-packages/beartype/_util/cache/utilcachecall.py:251: in _callable_cached
    return_value = args_flat_to_return_value[args_flat] = func(
../py39-venv/lib/python3.9/site-packages/beartype/_check/code/codemake.py:844: in make_check_expr
    express_func_scope_type_ref(
../py39-venv/lib/python3.9/site-packages/beartype/_check/code/codescope.py:518: in express_func_scope_type_ref
    ref_module_name, ref_name = get_hint_pep484585_ref_names(
../py39-venv/lib/python3.9/site-packages/beartype/_util/hint/pep/proposal/pep484585/utilpep484585ref.py:236: in get_hint_pep484585_ref_names
    hint_module_name = hint.__forward_module__
E   AttributeError: 'ForwardRef' object has no attribute '__forward_module__'

The issue appeared with beartyp-0.17.1[0], the issue doesn't manifest in beartype-0.17.0 [1].

[0] https://ci.appveyor.com/project/jvesely/psyneulink-cuda/build/job/aiafois7rd9pefx8
[1] https://ci.appveyor.com/project/jvesely/psyneulink-cuda/builds/49179845/job/8d10aojadmta8le5

@jvesely
Copy link
Author

jvesely commented Feb 13, 2024

Running in the same setup/container, python 3.8 doesn't have the same issue:

(py38-venv) root@5e79b71ebfdb:~/PsyNeuLink# pip list | grep beartype
beartype                  0.17.1
(py38-venv) root@5e79b71ebfdb:~/PsyNeuLink# pytest
==================================================================== test session starts =====================================================================
platform linux -- Python 3.8.10, pytest-8.0.0, pluggy-1.4.0 -- /root/py38-venv/bin/python3.8
cachedir: .pytest_cache
benchmark: 4.0.0 (defaults: timer=time.perf_counter disable_gc=True min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=True warmup_iterations=2)
rootdir: /root/PsyNeuLink
configfile: setup.cfg
plugins: anyio-4.2.0, xdist-3.5.0, pydocstyle-2.3.2, pycodestyle-2.3.1, profiling-1.7.0, helpers-namespace-2021.12.29, forked-1.6.0, cov-4.1.0, benchmark-4.0.0
initialized: 4/4 worker

@leycec
Copy link
Member

leycec commented Feb 13, 2024

Huh. Oddness intensifies. For unknown reasons that are probably boring, there seems to be a discrepancy here between the standard typing module that ships with my Python 3.9 and the standard typing module that ships with your Python 3.9. My Python 3.9-specific implementation of the typing.ForwardRef class definitely defines the __forward_module__ dunder attribute: e.g.,

# In the "typing" module for Python 3.9:
class ForwardRef(_Final, _root=True):
    """Internal wrapper to hold a forward reference."""

    __slots__ = ('__forward_arg__', '__forward_code__',
                 '__forward_evaluated__', '__forward_value__',
                 '__forward_is_argument__', '__forward_is_class__',
                 '__forward_module__')

    def __init__(self, arg, is_argument=True, module=None, *, is_class=False):
        if not isinstance(arg, str):
            raise TypeError(f"Forward reference must be a string -- got {arg!r}")
        try:
            code = compile(arg, '<string>', 'eval')
        except SyntaxError:
            raise SyntaxError(f"Forward reference must be an expression -- got {arg!r}")
        self.__forward_arg__ = arg
        self.__forward_code__ = code
        self.__forward_evaluated__ = False
        self.__forward_value__ = None
        self.__forward_is_argument__ = is_argument
        self.__forward_is_class__ = is_class
        self.__forward_module__ = module

My current patch release of Python 3.9 is Python 3.9.18. That's fairly late in the Python 3.9 release cycle. I'm now suspecting that earlier patch releases of Python 3.9 (...which you're probably on, maybe?) omit the __forward_module__ attribute from the typing.ForwardRef class. Yikes.

I'll tighten this up and push out a new stable beartype 0.17.2 release tonight. Until then, I sigh. 😮‍💨

@leycec leycec changed the title [python3.9, Linux, 0.17.1] E AttributeError: 'ForwardRef' object has no attribute '__forward_module__' [Bug] typing.ForwardRef fails to reliably define __forward_module__ under Python 3.9 Feb 13, 2024
leycec added a commit that referenced this issue Feb 14, 2024
This commit relaxes the assumption that *all* Python 3.9 patch releases
unconditionally define the standard
`typing.ForwardRef.__forward_module__` dunder attribute, resolving issue
#324 kindly submitted by stone-cold typonista @jvesely (Jan Vesely).
Although Python ≥ 3.9.18 definitively defines this attribute, an unknown
range of older Python 3.9 patch releases fail to do so. This commit
resolves this by naively pretending that *all* Python 3.9 patch releases
fail to do so. Although somewhat non-ideal, it's unclear whether this
attribute is ever used (i.e., set to a non-empty string) under Python
3.9. (*Rhapsodic aphrodisiac over a melodic afro!*)
@leycec
Copy link
Member

leycec commented Feb 14, 2024

Resolved by 74eefd2. Thanks for the fast heads-up, @jvesely. Let's goooooooo, @beartype 0.17.2! 🥳

@leycec leycec closed this as completed Feb 14, 2024
leycec added a commit that referenced this issue Feb 14, 2024
**Beartype 0.17.2** nervously skitters about on thin ice. Cracks form,
yet beartype 0.17.2 fails to return to shore. "What are you even
doing!?", the crowd exclaims. Verily, it is best not to ask questions:

```bash
pip install --upgrade beartype
```

This patch release comes courtesy these proud [GitHub
Sponsors](https://github.com/sponsors/leycec), without whom @leycec's
cats would currently be eating grasshoppers:

* @sesco-llc (SESCO Enterprises), "The Power of Innovation in Trading":
  <sup>*this inspires me to get out of the house and do something*</sup>
  https://sescollc.com
* @DylanModesitt (Dylan Modesitt), quantitative strategies energy
  trading associate: <sup>*...wikipedia, don't fail me now!*</sup>
  https://dylanmodesitt.com

Thanks so much, masters of fintech.

## This Release Kinda Sucks, Huh?

**Alright, alright.** You found us out already.

Beartype 0.17.2 is an *extremely* minor patch release that exists purely
to relax the bad assumption that *all* Python 3.9 releases
unconditionally define the standard
`typing.ForwardRef.__forward_module__` dunder attribute, resolving issue
#324 kindly submitted by stone-cold typonista @jvesely (Jan Vesely).
Although Python ≥ 3.9.18 definitively defines this attribute, an unknown
range of older Python 3.9 patch releases fail to do so.

Beartype 0.17.2 resolves this by naively pretending that *all* Python
3.9 releases fail to do so. Although kinda non-ideal, it's unclear
whether this attribute is even used (i.e., set to a string) under
Python 3.9. In fact, it's unclear whether this attribute is even used
anywhere, ever. It probably will be under Python ≥ 3.13, but that's
putting the proverbial cart before the horse. *Anyyyyyyway.*

We now return to your regularly scheduled Python hackathon.
(*Sappy haptics tickle happy tick-tock clocks!*)
jvesely added a commit to jvesely/PsyNeuLink that referenced this issue Feb 14, 2024
beartype/beartype#324

Signed-off-by: Jan Vesely <jan.vesely@rutgers.edu>
jvesely added a commit to jvesely/PsyNeuLink that referenced this issue Feb 14, 2024
Hit circular import on pypy3
beartype/beartype#324

Signed-off-by: Jan Vesely <jan.vesely@rutgers.edu>
jvesely added a commit to PrincetonUniversity/PsyNeuLink that referenced this issue Feb 14, 2024
beartype/beartype#324

Signed-off-by: Jan Vesely <jan.vesely@rutgers.edu>
jvesely added a commit to jvesely/PsyNeuLink that referenced this issue Feb 15, 2024
Hit circular import on pypy3
beartype/beartype#324

Signed-off-by: Jan Vesely <jan.vesely@rutgers.edu>
@jvesely
Copy link
Author

jvesely commented Feb 15, 2024

@leycec thank you. 0.17.2 fixes the cpython3.9 issue. However, the pypy issue is still there in 0.17.2. should I open a new bug for that?

@leycec
Copy link
Member

leycec commented Feb 16, 2024

...heh. PyPy, huh? Now there is a name I have not heard in a very long time. The short answer is that you probably want to conditionally avoid applying @beartype when the current Python interpreter is PyPy. Please consider boilerplate like this for the foreseeable future:

# In your "{your_package}.__init__" submodule:
from platform import python_implementation

# If the current Python interpreter is *NOT* PyPy, apply @beartype.
if python_implementation() != 'PyPy':  # <-- standard way to detect pypy
    from beartype.claw import beartype_this_package
    beartype_this_package()

The long answer is...

Nobody Wants to Hear This, But PyPy Kinda Sucks

PyPy kinda sucks. I used to be Team PyPy. Once, I was all for PyPy. In theory, PyPy sounds great. In theory, PyPy should be perfectly compatible with pure-Python packages like @beartype. Right? Sadly, that's just theory.

In practice, PyPy simply doesn't work for any definition of "work." PyPy breaks compatibility with literally everything – even pure-Python packages like @beartype. A few years ago, @beartype fully supported PyPy. As @beartype grew taller, stronger, and more ribbed with muscle, however, supporting PyPy basically became impossible.

The core issue is that PyPy isn't Python. PyPy is PyPy. Much like Cython or Mondo or Numba or Pyodide or whatevah, PyPy is a distinct language that only masquerades as Python. PyPy developers reading this are now attempting to burn me alive at the stake. Still, it is true. At the end of the day, PyPy's mimicry of Python is just a masquerade. It's not Python: it just looks alot like Python.

PyPy fundamentally breaks compatibility with low-level public types and APIs standardized by CPython. @beartype really cares about those types and APIs... but PyPy really doesn't. I mean, PyPy should. But PyPy doesn't and PyPy probably never will. This is why nothing works under PyPy.

Personally, I've never actually been able to install or run anything meaningful under PyPy – especially heavy-set ML frameworks like JAX or PyTorch. And I'm a Gentoo Linux compiler bro! We compile everything by hand in Gentoo Land. Why? Because masochists. That is the answer. But even I – an unabashed "fight me" masochist – never succeeded in getting PyPy to do anything.

Personally, I strongly prefer Nuitka. Even when PyPy works, so, never? PyPy comes with significant startup costs. When performance is at a premium, I usually just want to compile to C in a CPython-compatible manner. Enter Nuitka. Unlike PyPy, it's mostly CPython-compatible. Unlike PyPy, @beartype officially supports Nuitka. Unlike PyPy, Nuitka-compiled binaries exhibit fast startups. tl;dr: just Nuitka.

So, You Hate PyPy. We're Agreed. What Now?

I... don't know. I'm painfully behind on the @beartype roadmap for 2024. It'd be irresponsible to devote a month or so to PyPy, when what everybody wants is deep type-checking of standard containers via dict[...], set[...], and collections.abc.Iterable[...] type hints.

I'm definitely committed to restoring PyPy support to @beartype at some point. I just don't know when that might be. If this issue attracts attention, I'll happily reprioritize this for rapid resolution. Will that happen? Dunno. Does anyone still use PyPy? Dunno.

Let's see if anybody starts yelling at me.

truth hurts

@leycec leycec reopened this Feb 16, 2024
@leycec leycec changed the title [Bug] typing.ForwardRef fails to reliably define __forward_module__ under Python 3.9 [Feature Request] Restore official PyPy support Feb 16, 2024
@leycec leycec changed the title [Feature Request] Restore official PyPy support [Feature Request] Resuscitate PyPy support Feb 16, 2024
jvesely added a commit to jvesely/PsyNeuLink that referenced this issue Feb 22, 2024
Hit circular import on pypy3
beartype/beartype#324

Signed-off-by: Jan Vesely <jan.vesely@rutgers.edu>
jvesely added a commit to jvesely/PsyNeuLink that referenced this issue Feb 26, 2024
Hit circular import on pypy3
beartype/beartype#324

Signed-off-by: Jan Vesely <jan.vesely@rutgers.edu>
jvesely added a commit to jvesely/PsyNeuLink that referenced this issue Mar 3, 2024
Hit circular import on pypy3
beartype/beartype#324

Signed-off-by: Jan Vesely <jan.vesely@rutgers.edu>
jvesely added a commit to jvesely/PsyNeuLink that referenced this issue Mar 4, 2024
Hit circular import on pypy3
beartype/beartype#324

Signed-off-by: Jan Vesely <jan.vesely@rutgers.edu>
jvesely added a commit to jvesely/PsyNeuLink that referenced this issue Mar 9, 2024
Hit circular import on pypy3
beartype/beartype#324

Signed-off-by: Jan Vesely <jan.vesely@rutgers.edu>
jvesely added a commit to jvesely/PsyNeuLink that referenced this issue Mar 26, 2024
Hit circular import on pypy3
beartype/beartype#324

Signed-off-by: Jan Vesely <jan.vesely@rutgers.edu>
jvesely added a commit to jvesely/PsyNeuLink that referenced this issue Apr 5, 2024
Hit circular import on pypy3
beartype/beartype#324

Signed-off-by: Jan Vesely <jan.vesely@rutgers.edu>
jvesely added a commit to jvesely/PsyNeuLink that referenced this issue Apr 25, 2024
Hit circular import on pypy3
beartype/beartype#324

Signed-off-by: Jan Vesely <jan.vesely@rutgers.edu>

fixup beartype
jvesely added a commit to jvesely/PsyNeuLink that referenced this issue May 14, 2024
Hit circular import on pypy3
beartype/beartype#324

Signed-off-by: Jan Vesely <jan.vesely@rutgers.edu>

fixup beartype
jvesely added a commit to jvesely/PsyNeuLink that referenced this issue May 17, 2024
Hit circular import on pypy3
beartype/beartype#324

Signed-off-by: Jan Vesely <jan.vesely@rutgers.edu>

fixup beartype
jvesely added a commit to jvesely/PsyNeuLink that referenced this issue May 21, 2024
Hit circular import on pypy3
beartype/beartype#324

Signed-off-by: Jan Vesely <jan.vesely@rutgers.edu>

fixup beartype
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

2 participants