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

Runtime type validation? #174

Closed
crypdick opened this issue Nov 27, 2020 · 9 comments
Closed

Runtime type validation? #174

crypdick opened this issue Nov 27, 2020 · 9 comments

Comments

@crypdick
Copy link

We are assessing a switch from typeguard to icontract for stricter runtime validation. However, I don't see anything akin to typeguard's @typechecked decorator. Correct me if I'm wrong, is the only way to do runtime type validation by making an @icontract.require(lambda arg: isinstance(arg, <type>) for all args?

@mristin
Copy link
Collaborator

mristin commented Nov 27, 2020

Hi @crypdick ,
That is correct. We used type annotations in our code base so we didn't really have many issues with unknown types at runtime. In those rare cases, isinstance seemed like a bit verbose, but an explicit solution (the reader immediately sees what is being checked as type checks can implemented in many different ways).

Typeguard seems like a good solution for the use case when all you want is to check the type -- can you maybe elaborate why you wouldn't use it? (You might also want to check if it supports subtyping properly.)

@crypdick
Copy link
Author

crypdick commented Nov 27, 2020

@mristin we make extensive use of @typechecked to strictly constrain arg types, which has been great. However, typeguard doesn't let us put strict constraints on the arg values. We were hoping to use a single argument validation library.

@mristin
Copy link
Collaborator

mristin commented Nov 27, 2020

@crypdick I personally don't find the following snippet too terrible:

@icontract.require(lambda arg: isinstance(arg, int) and 0 <= arg <= 100)
def some_func(arg: Any) -> ...:
    ...

You can also use material implication if you need to condition constraints on the type:

@icontract.require(lambda arg: not isinstance(arg, int) or arg > 10)
@icontract.require(lambda arg: not isinstance(arg, str) or arg.startswith("some-prefix-"))
def some_func(arg: Any) -> ...:
    ...

You can also alias isinstance simply with is_a to abbreviate it:

is_a = isinstance
@icontract.require(lambda arg: is_a(arg, int) and 0 <= arg <= 100)

but I would personally recommend readability over brevity and avoid abbreviations.

@crypdick
Copy link
Author

Thanks @mristin. One issue with that solution is that it violates the DRY principle -- we're already type hinting our args, and we'd have to maintain consistency with the types again in each decorator. It's also a ton of boilerplate: I can slap a @typechecked decorator on a class and it'll validate all the methods' args, but with your approach I'd have to add hundreds of lines of code to some of our classes to get the same result.

@mristin
Copy link
Collaborator

mristin commented Nov 27, 2020

@crypdick I see -- I missed that part that you already do type annotations but want them enforced at runtime. I think that a decorator like typechecked would be out-of-scope for icontract and an unnecessary duplication of work. We would basically need to copy/paste typechecked code to icontract, wouldn't we?

Apart from depending on two libraries instead of one, do I understand it correctly that there are actually no other blockers to using icontract and typechecked in conjunction?

Please do check if typechecked supports subtyping correctly in your use cases. If it does not, that would be a strong case for its adoption in icontract. I could come up with an extension given a couple of concrete use cases (which I'd also like to use in testing).

@crypdick
Copy link
Author

there are actually no other blockers to using icontract and typechecked in conjunction

I am not sure, I've never used icontract.

check if typechecked supports subtyping correctly in your use cases

I am not sure if this qualifies, but we use PyDantic dataclasses and they play nicely with typeguard.

@crypdick
Copy link
Author

Update: I tested icontract with typeguard and I haven't found blockers.

@mristin
Copy link
Collaborator

mristin commented Nov 27, 2020

Hi @crypdick ,
I added a couple of explicit unit tests (see #175) to make sure that icontract and typeguard work together in the future as well. I also added a short paragraph in the Readme (https://github.com/Parquery/icontract/pull/new/mristin/Tested-with-typechecked) to recommend typeguard to the users.

Should we close the issue or there are still open points?

@crypdick
Copy link
Author

@mristin I think the explicit tests are great. Thanks!

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