How do I tell the type checker that any subclass of ast.expr
is ok?
#1996
Replies: 2 comments 3 replies
-
The complaint of the type checker is perfectly valid, since what you're doing is not type safe. I think what's tripping you up here is variance. While a return type is covariant, i.e. you can assign a function with a return type that is a subtype of the original function, the inverse is true for argument types, since they are contravariant. Consider the following example for covariance and contravariance:
Or in other terms What you can do however to solve this is to make def funcAttribute[T: ast.expr](action: Callable[[T], T]) -> Callable[[hasDOTfunc], hasDOTfunc]: This way it will accept any callable that processes any node type that is either |
Beta Was this translation helpful? Give feedback.
-
I greatly appreciate how much information you have given me. While I think I understand the stack and how the interpreter handles identifiers, objects, and execution, type checking is still a black box to me. Therefore, I struggle to understand where the disconnections are. Understanding different options is challenging, too. I am working through what you wrote to understand the terms of art and technical options. I've also decided to make this less complicated by requiring Python >= 3.12 in the package. Disorganized thoughts, reactions, and information:"Type safe" is a strange term of art to me. When I or other people use the package, I want the type checkers (and language servers) to be helpful guides. With type checkers, I too often feel like I'm a student being "taught" in a 1935 Catholic school by a nun holding a paddle. Prototype of an old idea to subclass composable methods so that typing information can extend beyond the top level of the ast node.class ImaCallToName(ast.Call):
func: ast.Name I made that subclass prototype before I made class ClassIsAndAttribute:
...
@staticmethod
def funcIs(astClass: type[hasDOTfunc], attributeCondition: Callable[[ast.expr], bool]) -> Callable[[ast.AST], TypeGuard[hasDOTfunc] | bool]:
def workhorse(node: ast.AST) -> TypeGuard[hasDOTfunc] | bool:
return isinstance(node, astClass) and attributeCondition(node.func)
return workhorse
... I can't remember how I used it, but it's in the repo, of course (in mapFolding, here's a commit that includes it). I used the class in a TypeGuard antecedent, and ... (I can't communicate very well right now.) In short, it seemed to work very well. If identifier, class IfThis:
...
@staticmethod
def isCallToName(node: ast.AST) -> TypeGuard[ImaCallToName]:
return Be.Call(node) and Be.Name(DOT.func(node))
... If I used the above antecedent, and used the That exact way of implementing is a combinatorics nightmare, though. To cover all cases, the quantity of necessary classes = sum of (for each class: for each attribute: attribute * number of valid types). If an attribute has type TypeGuard / TypeIsI don't know if I am using TypeGuard correctly or to its full potential. I have no clue how to use TypeIs. Other Python toolsWith help from this forum, I implemented my first generic after many failed attempts. I have implemented TypeVar with some success exactly twice despite dozens of attempts. Protocol? pfft. A little contextI didn't mention in this thread that I don't have formal training as a programmer or professional programming experience. I started with line basic on a Vic-20 when I was 10 years old, but I am not a programmer. I did get a 53rd-percentile score on the Comp Sci GRE Subject Exam 20 years ago, which is strong evidence that I know the difference between a programmer and a person with a lot of computer knowledge. I worked in IT for 9 years, but sys/net admin, webmaster!, and teaching. |
Beta Was this translation helpful? Give feedback.
-
I'm so confused and frustrated that I don't know if I am asking the right questions.
Here is an example problem:
The relevant code
Offending statement
From this module.
The function
From this module.
The types
Mostly from this module.
One "fix"
Without cast, the type is effectively
Callable[[ast.Name], ast.Name]
. SinceName
is a subclass ofexpr
, I want the type checker to tell me that this is a valid statement. I mean,cast
is not a fix here because there isn't an error. The problem is that I haven't defined something in such a way that the type checker understands thatCallable[[ast.expr], ast.expr]
includes subclasses ofast.expr
.Beta Was this translation helpful? Give feedback.
All reactions