I’m not saying anything about whether the conventions will remain or not. I’m just saying that I understand why people don’t want yet another change. That’s why for such proposals, in general, I’d be much more convinced if runtime checks were considered rather than conventions.
Yes, and I think that’s what should be preferred IMO. I would rather see changes in imports (import $package instead of import package to access internals for instance) rather than having more namespacing. Adding namespacing is tricky and really annoying in the interpreter. Because you will need to also implement this functionalits on ModuleType itself. I don’t know how easy it’s to always add an __internal__ field to it, or some other magical field on the object.
Now it’s me thinking loud:
Dedicated import syntax
I suggest from module import $my_internal_function (with the $), e.g.:
import module # regular import
module.$my_internal_function() # access internal function
import $module # expose internals automatically
$module.my_internal_function()
from module import $my_internal_function
$my_internal_function()
I don’t have a better syntax suggestion than that and I understand it can be weird. I used $ to avoid confusion with ‘@’ possibly, and I can’t use ‘#’ as it’s for comments. But feel free to propose a better syntax here (we could use ! as well to stress that "this object must exists so don’t complain about it unless it doesn’t) or accept any local currency symbol).
Dedicated declaration syntax
In general, objects have a single dictionary, namely __dict__, and I think this assumption is spreaded around the interpreter’s code so it may not be as a transition.
Currently __dict__ is expected to be a Mapping[str, Any]. However, I’d like to offer another built-in type that would be a “scoped” dictionary. That scoped dictionary is also a way to achieve C++/Java-like scopes from an inheritance perspective. More precisely, I suggest adding a __attributes__ field as follows:
class DictByScope(NamedTuple):
public: Mapping[str, Any]
private: Mapping[str, Any]
When accessing something regularly, you would access __attributes__.public. If it’s package private, you would query __attributes__.private.
The above approach could be extended to classes and inheritance towers by also adding a protected field so that protected declared fields can only by accessed by subclasses (same as C++/Java).
When accessing __dict__ directly, it would simply be the merge of __attributes__.public | __attributes__.private so that we don’t break anything (i.e., it’s still possibe to bypass any accesses when directly accessing __dict__, but not via a simple x.y where the . is implicitly "query __attributes__.public only and x.$y is "query __attributes__.public or __attributes__.private, starting with __attributes__.private). I don’t know if it’s possible to have a mappingproxytype based on multiple dicts (for instance public’s and private’s keys should have empty intersections).