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

Implements validates_with_beartype #128

Merged
merged 14 commits into from
Oct 9, 2021
Merged

Implements validates_with_beartype #128

merged 14 commits into from
Oct 9, 2021

Conversation

rsokl
Copy link
Contributor

@rsokl rsokl commented Oct 8, 2021

image

(Illustration by Keith Negley)

As promised, this PR adds validates_with_beartype, which is designed to be used with the new zen-wrappers feature. Everything that you instantiate can now be validated by beartype!

The Basics

>>> from hydra_zen.third_party.beartype import validates_with_beartype
>>> from beartype.cave import ScalarTypes
>>> def f(x: ScalarTypes): return x  # a scalar is any real-valued number
>>> val_f = validates_with_beartype(f)
>>> f([1, 2])  # doesn't catch bad input
[1, 2]
>>> val_f([1, 2])
BeartypeCallHintPepParamException: @beartyped f() parameter x=[1, 2] violates type hint [...]

>>> class A:
...     def __init__(self, x: ScalarTypes): ...
>>> validates_with_beartype(A)  # wrapping occurs in-place
__main__.A
>>> A([1, 2])
BeartypeCallHintPepParamException: @beartyped A.__init__() parameter x=[1, 2] violates type hint [...]

This is designed to be used with the zen_wrappers feature of builds.

>>> from hydra_zen import builds, instantiate
>>> # instantiations of `conf` will be validated by beartype
>>> conf = builds(f, populate_full_signature=True, zen_wrappers=validates_with_beartype)
>>> instantiate(conf, x=10)  # 10 is a scalar: ok!
10
>>> instantiate(conf, x=[1, 2])  # [1, 2] is not a scalar: roar!
BeartypeCallHintPepParamException: @beartyped f() parameter x=[1, 2] violates type hint [...]

Note that sequence-coercion is enabled to ensure smooth compatibility with Hydra.

>>> def g(x: tuple): return x  # note the annotation
>>> validates_with_beartype(g)([1, 2, 3])  # input: list, output: tuple
(1, 2, 3)

Some Implementation Details

This would have been a really tiny PR except...

@beartype will not coerce sequence-type data based on their annotations. This is totally fair/expected, but it is a show-stopper for use with Hydra, as all sequential data will be pass from configs as lists. Thus validates_with_beartype leverages hydra_zen._utils.coerce.coerce_sequences.

This is a wrapper that checks if a function has any fields in its signature that are annotated with a (non-string) sequence-type annotation; if it does, then it will wrap the function so that list (or list-config)-inputs will be cast to the annotated sequence (e.g a tuple). Only list/ListConfig values will be converted; others will be passed as-is so that beartype will flag them appropriately.

@coerce_sequences
def f(x: Tuple[int, int]): 
    return x
>>> f([1, 2])  # coerced!
(1, 2)
>>> f(1)  # no coercion (let beartype roar!)
1

Functions with no such annotations are passed-through unchanged.

>>> def g(x: int): ...
>>> coerce_sequences(g) is g
True

Writing coerce_sequences sucked! Testing coerce_sequences sucked! It was important to make sure that coerce_sequences never errors-out or produces unexpected results, so we test the *&!@ out of it! Shout out to Hypothesis for giving me the power to test against everything with st.fromtype(type).flatmap(st.fromtype) #ihavespenttoomuchtimewithhypothesis

@rsokl rsokl added the enhancement New feature or request label Oct 8, 2021
@rsokl rsokl added this to the hydra-zen 0.3.0 milestone Oct 8, 2021
@rsokl rsokl requested a review from jgbos October 8, 2021 19:30
@rsokl rsokl merged commit e7838e3 into main Oct 9, 2021
@rsokl rsokl deleted the support-beartype branch January 9, 2022 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants