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] Generalize beartype.door.is_subhint() to accept type tuples... just 'cause #369

Open
sylvorg opened this issue Apr 18, 2024 · 2 comments

Comments

@sylvorg
Copy link

sylvorg commented Apr 18, 2024

... Two more day 'til you get a deluge of replies!

Would it be possible to use is_subhint like issubclass, with a tuple of types? For example:

issubclass(str, (int, str)) # True

is_subhint(str, (int, str)) # ...

╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ in <module>:1                                                                                    │
│ ╭───────────────────────────── locals ─────────────────────────────╮                             │
│ │      BaseModel = <class 'pydantic.main.BaseModel'>               │                             │
│ │        Generic = <class 'typing.Generic'>                        │                             │
│ │ getfullargspec = <function getfullargspec at 0x000001880578F380> │                             │
│ │        install = <function install at 0x0000018806D27A60>        │                             │
│ │    is_bearable = <function is_bearable at 0x0000018806D0AC00>    │                             │
│ │     is_subhint = <function is_subhint at 0x0000018806D0AB60>     │                             │
│ │        Literal = typing.Literal                                  │                             │
│ │           Test = <class '__main__.Test'>                         │                             │
│ │           test = <function test at 0x0000018806D24A40>           │                             │
│ │      TestModel = <class '__main__.TestModel'>                    │                             │
│ │          Union = typing.Union                                    │                             │
│ ╰──────────────────────────────────────────────────────────────────╯                             │
│                                                                                                  │
│ C:\Users\jeetr\anaconda3\Lib\site-packages\beartype\door\_doorcheck.py:168 in is_subhint         │
│                                                                                                  │
│   165from beartype.door._cls.doorsuper import TypeHint                                      │
│   166 │                                                                                          │
│   167# The one-liner is mightier than the... many-liner.                                    │
│ ❱ 168return TypeHint(subhint).is_subhint(TypeHint(superhint))                               │
│   169                                                                                            │
│   170 # ....................{ TESTERS ~ is_bearable              }....................           │171 def is_bearable(                                                                           │
│                                                                                                  │
│ ╭────────────────────────── locals ───────────────────────────╮                                  │
│ │   subhint = <class 'str'>                                   │                                  │
│ │ superhint = (<class 'int'>, <class 'str'>)                  │                                  │
│ │  TypeHint = <class 'beartype.door._cls.doorsuper.TypeHint'> │                                  │
│ ╰─────────────────────────────────────────────────────────────╯                                  │
│                                                                                                  │
│ C:\Users\jeetr\anaconda3\Lib\site-packages\beartype\door\_cls\doormeta.py:146 in __call__        │
│                                                                                                  │
│   143 │   │   # each hint that evaluates to the same key is wrapped by the same                  │144 │   │   # instance of the "TypeHint" class under this Python interpreter.                  │145 │   │   wrapper = (                                                                        │
│ ❱ 146 │   │   │   _HINT_KEY_TO_WRAPPER.cache_or_get_cached_func_return_passed_arg(               │
│   147 │   │   │   │   # Cache this wrapper singleton under this key.                             │148 │   │   │   │   key=hint_key,                                                              │
│   149 │   │   │   │   # If a wrapper singleton has yet to be instantiated for this               │
│                                                                                                  │
│ ╭────────────────────────── locals ──────────────────────────╮                                   │
│ │      cls = <class 'beartype.door._cls.doorsuper.TypeHint'> │                                   │
│ │     hint = (<class 'int'>, <class 'str'>)                  │                                   │
│ │ hint_key = 1683745922240                                   │                                   │
│ │ TypeHint = <class 'beartype.door._cls.doorsuper.TypeHint'> │                                   │
│ ╰────────────────────────────────────────────────────────────╯                                   │
│                                                                                                  │
│ C:\Users\jeetr\anaconda3\Lib\site-packages\beartype\_util\cache\map\utilmapbig.py:231 in         │
│ cache_or_get_cached_func_return_passed_arg                                                       │
│                                                                                                  │
│   228 │   │   │                                                                                  │
│   229 │   │   │   # Value created by this factory function, localized for negligible             │230 │   │   │   # efficiency to avoid the unnecessary subsequent dictionary lookup.            │
│ ❱ 231 │   │   │   value = value_factory(arg)                                                     │
│   232 │   │   │                                                                                  │
│   233 │   │   │   # Cache this key with this value.                                              │234 │   │   │   self._key_to_value_set(key, value)                                             │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │     _SENTINEL = <beartype._util.utilobject.Iota object at 0x0000018806B6B480>                │ │
│ │           arg = (<class 'int'>, <class 'str'>)                                               │ │
│ │           key = 1683745922240                                                                │ │
│ │          self = <beartype._util.cache.map.utilmapbig.CacheUnboundedStrong object at          │ │
│ │                 0x0000018806D13EC0>                                                          │ │
│ │ value_factory = <bound method _TypeHintMeta._make_wrapper of <class                          │ │
│ │                 'beartype.door._cls.doorsuper.TypeHint'>>                                    │ │
│ │     value_old = <beartype._util.utilobject.Iota object at 0x0000018806B6B480>                │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ C:\Users\jeetr\anaconda3\Lib\site-packages\beartype\door\_cls\doormeta.py:218 in _make_wrapper   │
│                                                                                                  │
│   215 │   │   # Concrete "TypeHint" subclass handling this hint if this hint is                  │216 │   │   # supported by an existing "TypeHint" subclass *OR* raise an exception             │217 │   │   # otherwise (i.e., if this hint is currently unsupported).                         │
│ ❱ 218 │   │   wrapper_subclass = get_typehint_subclass(hint)                                     │
│   219 │   │   # print(f'!!!!!!!!!!!!! [ in {repr(cls)}.__new__() ] !!!!!!!!!!!!!!!')             │220 │   │                                                                                      │
│   221 │   │   # Type hint wrapper wrapping this hint as a new singleton instance of              │
│                                                                                                  │
│ ╭──────────────────────────────────── locals ────────────────────────────────────╮               │
│ │                   cls = <class 'beartype.door._cls.doorsuper.TypeHint'>        │               │
│ │ get_typehint_subclass = <function get_typehint_subclass at 0x0000018806D271A0> │               │
│ │                  hint = (<class 'int'>, <class 'str'>)                         │               │
│ ╰────────────────────────────────────────────────────────────────────────────────╯               │
│                                                                                                  │
│ C:\Users\jeetr\anaconda3\Lib\site-packages\beartype\door\_doordata.py:109 in                     │
│ get_typehint_subclass                                                                            │
│                                                                                                  │
│   106 │   │   │   wrapper_subclass = ClassTypeHint                                               │
│   107 │   │   # Else, raise an exception.                                                        │108 │   │   else:                                                                              │
│ ❱ 109 │   │   │   raise BeartypeDoorNonpepException(                                             │
│   110 │   │   │   │   f'Type hint {repr(hint)} '                                                 │
│   111 │   │   │   │   f'currently unsupported by "beartype.door.TypeHint".'                      │
│   112 │   │   │   )                                                                              │
│                                                                                                  │
│ ╭───────────────────── locals ──────────────────────╮                                            │
│ │             hint = (<class 'int'>, <class 'str'>) │                                            │
│ │        hint_sign = None                           │                                            │
│ │ wrapper_subclass = None                           │                                            │
│ ╰───────────────────────────────────────────────────╯                                            │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
BeartypeDoorNonpepException: Type hint (<class 'int'>, <class 'str'>) currently unsupported by "beartype.door.TypeHint".

Thank you kindly for the help!

@sylvorg sylvorg changed the title Use tuples with is_subhint superhints Use tuples as is_subhint superhints Apr 18, 2024
@leycec
Copy link
Member

leycec commented Apr 18, 2024

.. Two more day 'til you get a deluge of replies!

It's happening! It's happening already and it's only Wednesday. Truly, your dire prophecy is unfolding as we type. 🙀

Would it be possible to use is_subhint like issubclass, with a tuple of types? For example:

Fascinating request. In theory, sure. In practice, is there much point? After all, @beartype already more-or-less supports this exact use case with:

  • Standard PEP 484- and 604-compliant unions: e.g.,

    >>> from beartype.door import is_subhint
    >>> is_subhint(str, int | str)  # <-- PEP 604 union for the win, 'cause you just don't care
    True
  • A non-standard function that you yourself define that dynamically coerces tuples into PEP 484-compliant unions: e.g.,

    >>> from beartype.door import is_subhint
    >>> from beartype.typing import Union
    
    # Function coercing tuples into unions. @beartype actually does this *A LOT* internally.
    # So, you just know that (A) it works and (B) it's questionable dark magic.
    >>> def tuple_to_union(hints: tuple) -> object:
            return Union.__getitem__(hints)
    
    >>> is_subhint(str, tuple_to_union((int, str)))  # <-- tuple of types for the win, 'cause you do actually care
    True

Do either of those two examples support your use case? The second is really close to what you want. There's a tuple of types, which is good. There's also an intermediary conversion function, which is perhaps less good.

And now for the Ultimate Badness™. Since @beartype already more-or-less supports this, this is really low priority stuff for us... I've got five feature requests to feed this week. You wouldn't want to starve an orphan feature request, would you!?

@leycec leycec changed the title Use tuples as is_subhint superhints [Feature Request] Generalize beartype.door.is_subhint() to accept type tuples... just 'cause Apr 19, 2024
@sylvorg
Copy link
Author

sylvorg commented Apr 27, 2024

... Well, seems like it took a week for that deluge; I was more tuckered out than I realized! 😅 Ah, well; you win some, you lose some.

I ended up using Union[*types], but it would be nice to bring is_subhint in line with issinstance, issubclass, and even is_bearable! 😅

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