On Tue, Nov 14, 2017 at 12:24 AM, Serhiy Storchaka <storchaka@gmail.com> wrote:
10.09.17 21:48, Ivan Levkivskyi пише:
   # 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

I think the PEP should provide better examples, because they will be copied in third-party code.

For keeping all functionality (in particularly be pickleable) the deprecated function should preserve its name.

   def old_function(arg, other):
       ...
   _deprecated_old_function = old_function
   del old_function

or

   def _deprecated_old_function(arg, other):
       ...
   _deprecated_old_function.__name__ = 'old_function'
   _deprecated_old_function.__qualname__ = 'old_function'

(I prefer the former variant.)

However the latter could be done by a decorator.
 
I'm wondering if it is worth to provide a special helper that will rename the deprecated function and create the corresponding __getattr__ function.

It could be possible to create helpers that implement module-level properties too.

OTOH not everyone cares about whether their functions are picklable. Maybe a discussion of how to make deprecated functions picklable could be in an Appendix? And another example of how to implement more functional lazy loading. Or even more complete examples could be maintained as a separate GitHub repo (like Ethan did for PEP 561).

--
--Guido van Rossum (python.org/~guido)