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
Modify check_type to work without memo #24
Comments
How would that work? Where would the annotation come from? |
Also, what is the use case here? |
@agronholm In my case I'm working with PEP 526 annotations and get them directly from a class which is like |
Ok, what about type variables? I'm not certain how they work with variable annotations. |
class X:
a: int = 10
print(X.__annotations__['a']) # int |
My request is general though: assuming one has both |
Yes, I understand, but I want to make sure type variables are handled correctly. That is the entire reason for the |
I don't think I understand well enough the problem |
Do you know how type variables work? Have you ever used them? |
My best guess they are alike C++'s template parameters. |
Something like that. Consider the following code: T_Element = TypeVar('T_Element')
def extract(list_: List[T_Element], index: int) -> T_Element:
return list_[index]
|
You can with |
You do know about generic classes, right? Classes like: class Foo(Generic[T_Element]):
... |
Indeed, without T = typing.TypeVar('T')
typeguard.check_type('', ('', 1, ''), typing.Tuple[T, int, T], None) But seems that |
Why are you talking about functions now? I thought you were after variable annotations here. |
@agronholm I'm trying to understand if |
That seems to work: class _Memo:
def __init__(self):
self.typevars = {} # type: Dict[Any, type]
T = typing.TypeVar('T')
typeguard.check_type('', ('', 1, '123'), typing.Tuple[T, int, T], _Memo())
typeguard.check_type('', ('', 1, 123), typing.Tuple[T, int, T], _Memo()) # TypeError |
Where would those type variables get their bound values? |
In the case of classes, type variables in variable annotations would get their values from the type used to instantiate the class, if I'm correct. I need to further study the PEP to be sure. |
Ok, consider the following: from typing import TypeVar, Generic, List
T = TypeVar('T')
class Foo(Generic[T]):
bar: T
def __init__(self, bars: List[T]):
self.bar = bars[0]
foo = Foo(['xyz']) Now according to PEP 484, the type of |
I could maybe modify the code to work without a memo and just skip the type variable binding if the information is not available. |
It doesn't seem that the resolved type is stored anywhere at runtime and needs to be manually resolved. |
Yes, so it seems. I recall pytypes has an ugly workaround for this, but I don't want to do that. Oh well, I guess treating type variables as their bound types (or |
@agronholm By bound you mean first encountered type? |
No, those are erased at run time. I mean: |
What about constraints? |
This workaround is only applied to older typing module, before |
Note that pytypes gets examples like in #21 right (feature not released yet). However, I recognized faulty behavior with examples like in #24 (comment) where the typevar is not bound a-priori. I'm looking into this right now and hope to fix it soon. |
Now that attrs 17.3 has been released with PEP 526 support, this feature will become much more important. |
What do you think how should the following behave?
Should the calls fail because int and float are not the same?
|
An interesting read from the C++ world: Deduction from a function call. I think either Python's typing syntax must be extend to allow explicit resolve of the parameters:
or interpreter implementation specific forward analysis of the byte code (compilation?) needs to be done. The latter will not cover cases because Python :)
I think type checker should warn unless user explicitly configured e.g. using pseudocode as above:
Class annotations can be used for that in a hope that instance of the class has some of the class variables assigned: T = TypeVar('T')
class Foo(Generic[T]):
bar: T
def __init__(self, bars: List[T]):
self.bar = bars[0]
foo = Foo([42])
resolved_params = {}
for param in foo.__parameters__:
for attr, attr_param in get_type_hints(foo).items():
if attr_param is param:
try:
param_type = type(getattr(foo, 'bar'))
except AttributeError:
continue
else:
break
else:
param_type = Any
resolved_params[param] = param_type
assert resolved_params == {T: int} Don't know how subclasses should be treated though. |
@Stewori In your example, I believe both calls should fail because the |
@agronholm I also had this idea regarding covariant, but I'm not sure whether that is actually the meaning of covariant. AFAIK covariant means for a parameter of a generic class that if the parameter becomes more specific, the generic class also becomes more specific. A function call is - however - not a generic type, so I'm not sure if the principle should be applied here. (Should variance direction be inverted regarding return type? In subclassing sense, return types behave contravariantly.) Anyway, doing it like you suggest is probably the most consistent approach for this. |
@agronholm Another pice that I found while re-reading PEP 526 is that ClassVar annotations cannot be parametrized. |
Yep, that I know but non-classvar variable annotations can be. |
Just wanted to update here that the above examples concerning TypeVars are now supported in pytypes. This version was released to PyPI yesterday. |
Here is the complete use case where I wanted to use typeguard: async-app.config. |
But that should work now that None is acceptable for memo, shouldn't it? You can alternatively use pytypes. Replace
by
I suppose this is a kind of thing both frameworks can handle. Or is the type specifically nasty, e.g. containing TypeVars or whatever? In that case maybe file another issue about that case. |
I'm okay with typeguard for now as I'm not using TypeVars. It's very simple to add support for pytypes. Supply will follow demand and PRs are welcome :) |
In my project I would like to check arbitrary object against PEP 484 definition.
check_type
seems to be what I need, but it requires a memo object which I do not have in my use case.The text was updated successfully, but these errors were encountered: