Currently, at a module level with a publically exported symbol, pyright has unsafe behavior (edit see below, there are some questions as to whether pyright is actually unsafe, but the rules pyright is using are similarly not specified and this is a point where type checkers need a way to all be on the same page for what is allowed for a symbol)
Split off from a larger thread about inference, this is an example that contains an actual unsafety at a point of interoperability.
This allows assignability to names.NAME_TO_ID
with an interface that is less specific than names.py actually requires.
For instance
import types
import names
names.NAME_TO_ID = types.MappingProxyType({})
names.populate()
It was noted in that thread that inline annotations do not have a precise meaning in the specification.
To better match developer intent and improve interoperability in this and future such issues, Iâm proposing a concrete definition for inline annotations with assignment, as well as proposing that symbols that are part of a moduleâs namespace MUST be treated as invariant unless further assignment is forbidden with Final. It is also not enough for pyrightâs special casing of SCREAMING_CASE being equivalent to Final, as this is visible to users of other type checkers that may not honor that convention as it is outside of the typing specification.
Justification for invariance: Assignment to names at a module scope is the same as updating a moduleâs mutable namespace dict.
The rules Iâm proposing for inline annotations with
- An inline annotation annotates an expectation of consistency with the expression on the RHS of the assignment.
- An inline annotation specifies that the symbol on the LHS should be treated as no more specific than the annotation at the point of assignment. Further narrowing behavior is left up to type checker implementations but should be consistent with variance.
The second point here preserves developer intent in being able to state a less-specific public guaranteed type in modules without having to resort to a stub because a typechecker narrowed at time of assignment for an inline annotation.