Sorry if this has already been discussed before. I would like to propose adding a protected
decorator to typing.
Use case
Currently, we can already express a method as protected by naming it with a _
prefix. However, we cannot distinguish a “public” protected method(a stable API) from an “internal” protected method in this way.
Say, I’m a library author writing a remote job system. I want the library users to inherit a JobBase
class to implement their jobs. To do some extra initialization, users can override the _post_init
method. The method is named_post_init
because it’s a protected method. We don’t want users to call it, otherwise the instance may be initialized incorrectly. The JobBase
also has a method named _cleanup
to do some internal clean up after the job is done. But we don’t want the users to override the _cleanup
method because it’s an “internal” method, meaning it may be refactored away in the future version of the library. Also, we want to avoid the users overriding this method without calling the super()._cleanup(),
which will cause some resource leaks.
The users can not easily tell which of these two methods is “public” and which is “internal” because they both start with the _
.
A real-world example is that mkdocstrings filters out all methods that start with a _
by default. If we want it to generate docs for the protected methods, we have to remove the filter. But then all the “internal” methods will also be included in the generated docs, which is not ideal.
Similar to the final
decorator, we can add a protected
decorator to typing. Then we can write the JobBase
like this:
from typing import protected
class JobBase:
@protected
def post_init(self) -> None:
"""Override this method to do some extra initialization."""
pass
def _cleanup(self) -> None:
# User-land code should not override or call this method.
Why not use final
An “internal” protected method is not necessarily a final
method. The library itself may override it in a subclass of JobBase
like:
class ProcessJobBase(JobBase):
def _cleanup(self) -> None:
...
The library authors can safely refactor all the _cleanup
methods away as long as no user-land code overrides them.
Why not use private method
The same as above. An “internal” protected method is not necessarily a private method. Also, some developers may not used to name all internal methods with the double _
prefix.
Implementation
Like the final
decorator, we can add a __protected__
attribute to the wrapped method. The type checker can issue an error if a protected method is called externally.
Is this idea worth a PEP? If so I’d happy to prepare one and try to do a reference mypy implementation. Thanks.