<div dir="ltr"><div>So we've seen a real use case for __class__ assignment: deprecating things on access. That use case could also be solved if modules natively supported defining __getattr__ (with the same "only used if attribute not found otherwise" semantics as it has on classes), but it couldn't be solved using @property (or at least it would be quite hacky).<br><br></div>Is there a real use case for @property? Otherwise, if we're going to mess with module's getattro, it makes more sense to add __getattr__, which would have made Nathaniel's use case somewhat simpler. (Except for the __dir__ thing -- what else might we need?)<br></div><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Sep 5, 2017 at 3:52 PM, Nathaniel Smith <span dir="ltr"><<a href="mailto:njs@pobox.com" target="_blank">njs@pobox.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On Tue, Sep 5, 2017 at 3:03 PM, Larry Hastings <<a href="mailto:larry@hastings.org">larry@hastings.org</a>> wrote:<br>
><br>
> I've written a PEP proposing a language change:<br>
><br>
> <a href="https://www.python.org/dev/peps/pep-0549/" rel="noreferrer" target="_blank">https://www.python.org/dev/<wbr>peps/pep-0549/</a><br>
><br>
> The TL;DR summary: add support for property objects to modules.  I've<br>
> already posted a prototype.<br>
<br>
</span>Interesting idea! It's definitely less arcane than the __class__<br>
assignment support that was added in 3.5. I guess the question is<br>
whether to add another language feature here, or to provide better<br>
documentation/helpers for the existing feature.<br>
<br>
If anyone's curious what the __class__ trick looks like in practice,<br>
here's some simple deprecation machinery:<br>
  <a href="https://github.com/njsmith/trio/blob/ee8d909e34a2b28d55b5c6137707e8861eee3234/trio/_deprecate.py#L102-L138" rel="noreferrer" target="_blank">https://github.com/njsmith/<wbr>trio/blob/<wbr>ee8d909e34a2b28d55b5c6137707e8<wbr>861eee3234/trio/_deprecate.py#<wbr>L102-L138</a><br>
<br>
And here's what it looks like in use:<br>
  <a href="https://github.com/njsmith/trio/blob/ee8d909e34a2b28d55b5c6137707e8861eee3234/trio/__init__.py#L91-L115" rel="noreferrer" target="_blank">https://github.com/njsmith/<wbr>trio/blob/<wbr>ee8d909e34a2b28d55b5c6137707e8<wbr>861eee3234/trio/__init__.py#<wbr>L91-L115</a><br>
<br>
Advantages of PEP 549:<br>
- easier to explain and use<br>
<br>
Advantages of the __class__ trick:<br>
- faster (no need to add an extra step to the normal attribute lookup<br>
fast path); only those who need the feature pay for it<br>
<br>
- exposes the full power of Python's class model. Notice that the<br>
above code overrides __getattr__ but not __dir__, so the attributes<br>
are accessible via direct lookup but not listed in dir(mod). This is<br>
on purpose, for two reasons: (a) tab completion shouldn't be<br>
suggesting deprecated attributes, (b) when I did expose them in<br>
__dir__, I had trouble with test runners that iterated through<br>
dir(mod) looking for tests, and ended up spewing tons of spurious<br>
deprecation warnings. (This is especially bad when you have a policy<br>
of running your tests with DeprecationWarnings converted to errors.) I<br>
don't think there's any way to do this with PEP 549.<br>
<br>
- already supported in CPython 3.5+ and PyPy3, and with a bit of care<br>
can be faked on all older CPython releases (including, crucially,<br>
CPython 2). PEP 549 OTOH AFAICT will only be usable for packages that<br>
have 3.7 as their minimum supported version.<br>
<br>
I don't imagine that I would actually use PEP 549 any time in the<br>
foreseeable future, due to the inability to override __dir__ and the<br>
minimum version requirement. If you only need to support CPython 3.5+<br>
and PyPy3 5.9+, then you can effectively get PEP 549's functionality<br>
at the cost of 3 lines of code and a block indent:<br>
<br>
import sys, types<br>
class _MyModuleType(types.<wbr>ModuleType):<br>
    @property<br>
    def ...<br>
<br>
    @property<br>
    def ...<br>
sys.modules[__name__].__class_<wbr>_ = _MyModuleType<br>
<br>
It's definitely true though that they're not the most obvious lines of code :-)<br>
<span class="HOEnZb"><font color="#888888"><br>
-n<br>
<br>
--<br>
Nathaniel J. Smith -- <a href="https://vorpus.org" rel="noreferrer" target="_blank">https://vorpus.org</a><br>
______________________________<wbr>_________________<br>
Python-Dev mailing list<br>
<a href="mailto:Python-Dev@python.org">Python-Dev@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-dev" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>mailman/listinfo/python-dev</a><br>
Unsubscribe: <a href="https://mail.python.org/mailman/options/python-dev/guido%40python.org" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>mailman/options/python-dev/<wbr>guido%40python.org</a><br>
</font></span></blockquote></div><br><br clear="all"><br>-- <br><div class="gmail_signature" data-smartmail="gmail_signature">--Guido van Rossum (<a href="http://python.org/~guido" target="_blank">python.org/~guido</a>)</div>
</div>