Interoperability issue with module level inline annotations

Maybe. I think it’s a little different because modules are more obviously intended to be accessed by multiple consumers and the examples highlight cases that should be caught. There are also examples in the other thread going the other direction where it isn’t a coding error. So it’s false negatives and false positives all in one issue.

I also think the examples above show how it’s different because it isn’t equivalent entirely to that. With it being at the module scope, this would be like a container subclass itself defined it’s own variable invariants and then ignored them when assigning to itself.

But even if that’s all it is, shouldn’t we fix that as the pile of things clearly effected by this grows? Shouldn’t we fix this before the next lazy-import idea? Shouldn’t we fix this before free-threading becomes the norm?

@Liz The rephrasing to generalize you have above has a single issue I could find: a lack of explicitly defined behavior without an annotation. The implied “No request for a specific enforcement, but type checkers still need to check for consistency” is probably the only valid interpretation that doesn’t conflict with some more important rule or principle for type checking. I’d be happy to continue discussion of this elsewhere where it’s more constructive.

I don’t want to have a long pyright specific discussion right now, but here’s the problems with the assumptions inherent behind pyright’s behavior there.

It’s not safe in python from the minimum interface to simplify a generic value based on variance. Python allows the variance to change in subtypes (even happening automatically with autovariance rather than being an LSP violation)

Python’s protocols are basically just a worse version of rust’s traits, but because in python, type checkers attach variance to them at the class level, when they should only attach to the capabilities they express.

Type checkers also arent compiling a full program, they are checking isolated fragments that are meant to be combined.

Type checkers also don’t error for variance mismatches at assignment. I understand this is a consequence of all the other things that would have to change first to do this usefully, but it’s still part of why it’s never safe for a type checker in python to see a covariant int | str + a specific use that’s just int and simplify it to int It’s always possible that in some not currently visible scope, this was narrowed by explicit runtime check and that the simplification made for covariance isn’t safe.

This goes beyond just mutable containers,. What’s interesting, is I’m sure that you’ve come across this before with the type of SupportsWrite and contravariance based on pyright’s internal typing of this, and that other people trying to get safer use of typing have some across it, such as optype’s callables

After working through various ways such a loader could work, I’ve concluded that a module loader that allowed this to cause a problem on the other side of an import statement would exhibit other unsafe behaviors at runtime. It’s possible I missed an option when working through possible behaviors, but I don’t think I did; It’s probably worth taking the time to type up my notes on this later and post them somewhere, but it wouldn’t be for typing’s sake.

With that said:

  • I still think it’s a problem that people can’t just learn pythons type system. They have to learn a type checker’s interpretation of that to do anything with it. This definitely contributes to friction with contributions.

  • I still think it’s a problem that the specification allows objectively incorrect behavior.

  • I think it’s worth codifying something that states that annotations are for both enforcing type safety and enforcing the expressed developer intent of types and interfaces, as that comes with other safety people care about.

  • I still think this is underspecified even if only considering type safety.

I’m going to drop this for now as clearly other people don’t see this as enough of an issue, and I’ll just take a more pessimistic approach for the other things I’m currently working on that would benefit from this being actually safe to trust type checkers with.

1 Like