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.
