(original) (raw)

On Tue, Nov 14, 2017 at 10:34 PM, Ivan Levkivskyi <levkivskyi@gmail.com> wrote:
​\[..\]​
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 +