You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Support validation/initialization with types without the need to subclass #3200
The only work-around I've found for my problem is to subclass types (including primitive/immutable types with the _new_ method). However, this doesn't help with collection or union types, or even boolean types, which cannot be subclassed.
Say I want to map input strings to output variables of explicit types: simplest case validate 'yes' or 'no' strings and convert to a bool.
However, while running mypy with this approach, YesNo is invalid since MyPy follows NewType explicitly.
Here, I have to separate the validation step from the construction of the object. What I'd really like to do is write an extra step in the type/value conversion so that this sort of validation is possible.
I'm having a hard time following this. Is the example the code you want to work or the code you're writing to work around your issue? What is the exact error you're getting from mypy?
This is a potential work-around to defining non-standard constructors for well-defined types, and I haven't found a general solution that works out of the box. I've commented the line with the error (print_yesno's function definition). YesNo isn't recognized as a type, since mypy merely checks for NewTypes, top-level class definitions, and a few other typing constructs.
If I wanted to work with strings, my solution of YesNo would be:
classYesNo(str):
'''A string guaranteed to be yes or no'''def__new__(cls, value: Any):
ifvaluenotin {'yes', 'no'}:
raiseValueError('Must be yes or no')
returnsuper().__new__(cls, value) # type: ignore
And then I can use the constructor of YesNo like the string constructor with some added validation, knowing anywhere in my code that the string will either be 'yes' or 'no' since there will be a ValueError on instantiation otherwise.
This breaks down if I want to use a non-standard constructor that validates input (e.g. a string), yielding an output for some basic type (e.g. bool, with no subclassing abilities). I'd also like to not have to subclass string, but in either case I'd have to make trade-offs.
An example of such a trade-off: Use NewType across the board. This prevents no one from casting the constructor unsafely, without validation:
fromtypingimportNewTypeYesNo=NewType('YesNo', str)
defyes_no_constructor(value: str) ->YesNo:
'''Here, I am very hopeful no one calls the default YesNo() constructor anywhere outside of this function, since I don't know if they will have validated the string'''ifvaluenotin {'yes', 'no'}:
raiseValueError('Must be yes or no')
returnYesNo(value)
input_value=input() # say it's '42'unsafe_yes_no=YesNo(input_value) # YesNo('42')good_yes_no=yes_no_constructor(input_value) # ValueError
One thought could be to let the user configure aliases that assign new type variables? That way a custom solution such as my ValidatedType function would just be recognized as generating a new type similarly to NewType.
Not sure if that would be a simple addition, though
Realizing that I can use a different design pattern with generics and input/output class properties. Still would be nice if the above were possible, though.
fromtypingimportTypeVar, GenericfromabcimportABCMeta, abstractmethodInputType=TypeVar('InputType')
OutputType=TypeVar('OutputType')
classValidator(Generic[InputType, OutputType], metaclass=ABCMeta):
def__init__(self, value: InputType) ->None:
'''Can also do runtime validation with the is_valid prop'''self.value=value@property@abstractmethoddefis_valid(self) ->bool:
'''Check validity of input'''@property@abstractmethoddefoutput(self) ->OutputType:
'''Handle the output'''
Activity
JelleZijlstra commentedon Apr 20, 2017
I'm having a hard time following this. Is the example the code you want to work or the code you're writing to work around your issue? What is the exact error you're getting from mypy?
davidroeca commentedon Apr 20, 2017
This is a potential work-around to defining non-standard constructors for well-defined types, and I haven't found a general solution that works out of the box. I've commented the line with the error (print_yesno's function definition). YesNo isn't recognized as a type, since mypy merely checks for NewTypes, top-level class definitions, and a few other typing constructs.
If I wanted to work with strings, my solution of YesNo would be:
And then I can use the constructor of YesNo like the string constructor with some added validation, knowing anywhere in my code that the string will either be 'yes' or 'no' since there will be a ValueError on instantiation otherwise.
This breaks down if I want to use a non-standard constructor that validates input (e.g. a string), yielding an output for some basic type (e.g. bool, with no subclassing abilities). I'd also like to not have to subclass string, but in either case I'd have to make trade-offs.
An example of such a trade-off: Use NewType across the board. This prevents no one from casting the constructor unsafely, without validation:
davidroeca commentedon Apr 20, 2017
One thought could be to let the user configure aliases that assign new type variables? That way a custom solution such as my ValidatedType function would just be recognized as generating a new type similarly to NewType.
Not sure if that would be a simple addition, though
davidroeca commentedon Apr 21, 2017
Realizing that I can use a different design pattern with generics and input/output class properties. Still would be nice if the above were possible, though.
JelleZijlstra commentedon Mar 19, 2022
Closing as I don't think this issue is going anywhere. You may be interested in https://pypi.org/project/phantom-types/