What would be needed to be able to express SimpleNamespace using python typings?
from types import SimpleNamespace
my_obj = SimpleNamespace(A=1, B='abc')
print(my_obj.B) # mypy and pyright can't infer the type of B right now, it's Any
I’d love to use SimpleNamespace as an inline single frozen instance, but the editors don’t know the attributes of my_obj. If I never reuse the class, it’s extremely verbose. If I use dict then the keys and types are not known either.
Everything is constant, static and straightforward in the above example, yet it seems to be impossible to make it work (using types).
From your description SimpleNamespace is not at all what you actually want, what you want is a NamedTuple and type checkers perfectly understand those, exactly because they are actually frozen. A SimpleNamespace is more like a dict with attribute access instead of item access. You can freely assign new members and change or remove existing ones.
The fundamental problem with SimpleNamespace from a type checker’s perspective is that from the type on its own you don’t actually know what it contains. Every SimpleNamespace instance has the same type, NamedTuple on the other hand is a type constructor that first constructs a concrete type, the layout of which is known and each instance of it has this distinct type, instead of sharing one with every other possible layout of a named tuple.
That’s not to say that making SimpleNamespace generic in the future is not a possibility. If we ever get a TypeVarMapping that can capture key value type pairs, it might be possible to express __getattr__ of SimpleNamespace accurately based on what __init__ was called with. But even then I would be opposed to making that change, because that’s not what SimpleNamespace is meant to be. The documentation is very clear that you’re supposed to be able to freely add new attributes to existing instances. Making SimpleNamespace generic would make that needlessly difficult.
I don’t want classes and constructors at all. I want instances, objects which have typed attribute access, so your description of A SimpleNamespace is more like a dict with attribute access instead of item access. sounds perfect.
Right now I can create a dataclass and create a single instance of it. It’s verbose, error prone and it’s slower runtime than SimpleNamespace.
So this is why I asked if SimpleNamespace attribute can be derived from the constructor types using some generics. If I can express the same with my own class that’s good as well.
Making SimpleNamespace generic would only get you part of the way in this example. dict is already generic, but given:
my_obj = {"A": 1, "B": "abc"}
reveal_type(my_obj["B"]) # Revealed type is "builtins.object"
I do think it would make sense for SimpleNamespace to get similar analysis as dict does, but if you want to mix types and have the type checker understand which attribute has which type, something more specific is going to be needed.
Then I’d suggest use a typed dict. Typed diff instances are normal dicts at runtime and typed dict class is fake. If you ask type(x) where x is a typed dict it will just give you dict.
You still need to define schema once but after you do it,
class Foo(TypedDict):
…
x: Foo = {…}
No runtime cost at all.
There has been idea sometimes of inferring typeddicts automatically in some inline manner which would let you not specify schema explicitly. That does not exist in type system today but is plausible extension.
SimpleNamespace’s intended use is not a typable construct.
Theoretically, In a compiled language that allowed for dynamic boxed types and tracked what type something was at different points in time, this could be supported by the compiler in some way tracking what was added and removed from it, but this is one of those things you just shouldn’t use if you want to retain type information in python.