(original) (raw)
\[..\]
Rationale
=========
It is sometimes convenient to customize or otherwise have control over
access to module attributes. A typical example is managing deprecation
warnings. Typical workarounds are assigning \`\`\_\_class\_\_\`\` of a module object
to a custom subclass of \`\`types.ModuleType\`\` or replacing the \`\`sys.modules\`\`
item with a custom wrapper instance. It would be convenient to simplify this
procedure by recognizing \`\`\_\_getattr\_\_\`\` defined directly in a module that
would act like a normal \`\`\_\_getattr\_\_\`\` method, except that it will be defined
on module \*instances\*. For example::
# lib.py
from warnings import warn
deprecated\_names = \["old\_function", ...\]
def \_deprecated\_old\_function(arg, other):
...
def \_\_getattr\_\_(name):
if name in deprecated\_names:
warn(f"{name} is deprecated", DeprecationWarning)
return globals()\[f"\_deprecated\_{name}"\]
raise AttributeError(f"module {\_\_name\_\_} has no attribute {name}")
# main.py
from lib import old\_function # Works, but emits the warning
Deprecating functions is already possible, so I assume the reason for this would be performance? If so, are you sure this would help for performance?
Deprecating module attributes / globals is indeed difficult to do at present. This PEP would allow deprecation warnings for accessing attributes, which is nice! However, as thread-unsafe as it is, many modules use module attributes to configure the state of the module. In that case, the user is more likely to \*set\* the attribute that to \*get\* it. Is this outside the scope of the PEP?
\[..\]
There is a related proposal PEP 549 that proposes to support instance
properties for a similar functionality. The difference is this PEP proposes
a faster and simpler mechanism, but provides more basic customization.
I'm not surprised that the comparison is in favor of this PEP ;-).
\[..\]
Specification
=============
The \`\`\_\_getattr\_\_\`\` function at the module level should accept one argument
which is the name of an attribute and return the computed value or raise
an \`\`AttributeError\`\`::
def \_\_getattr\_\_(name: str) -> Any: ...
This function will be called only if \`\`name\`\` is not found in the module
through the normal attribute lookup.
The Rationale (quoted in the beginning of this email) easily leaves a different impression of this.
\[..\]
Discussion
==========
Note that the use of module \`\`\_\_getattr\_\_\`\` requires care to keep the referred
objects pickleable. For example, the \`\`\_\_name\_\_\`\` attribute of a function
should correspond to the name with which it is accessible via
\`\`\_\_getattr\_\_\`\`::
def keep\_pickleable(func):
func.\_\_name\_\_ = func.\_\_name\_\_.replace('\_deprecated\_', '')
func.\_\_qualname\_\_ = func.\_\_qualname\_\_.replace('\_deprecated\_', '')
return func
@keep\_pickleable
def \_deprecated\_old\_function(arg, other):
...
One should be also careful to avoid recursion as one would do with
a class level \`\`\_\_getattr\_\_\`\`.
Off-topic: In some sense, I'm happy to hear something about pickleability. But in some sense not.
I think there are three kinds of people regarding pickleability:
1\. Those who don't care about anything being pickleable
2\. Those who care about some things being picklable
3\. Those who care about all things being picklable
Personally, I'd like to belong to group 3, but because group 3 cannot even attempt to coexist with groups 1 and 2, I actually belong to group 1 most of the time.
––Koos
References
==========
.. \[1\] PEP 484 section about \`\`\_\_getattr\_\_\`\` in stub files
(https://www.python.org/dev/peps/pep-0484/#stub-files)
.. \[2\] The reference implementation
(https://github.com/ilevkivskyi/cpython/pull/3/ files)
Copyright
=========
This document has been placed in the public domain.
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
sentence-end-double-space: t
fill-column: 70
coding: utf-8
End:
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: https://mail.python.org/mailman/options/python-dev/ k7hoven%40gmail.com
+ Koos Zevenhoven + http://twitter.com/k7hoven +