-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: page on setting __all__/__dir__
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
- Loading branch information
Showing
1 changed file
with
71 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
--- | ||
layout: page | ||
title: Exports | ||
permalink: /patterns/exports/ | ||
nav_order: 30 | ||
parent: Backports | ||
--- | ||
|
||
What objects in a module can you use? One common convention is that starting a | ||
name with a single underscore makes it "private" (though really "hidden" might | ||
be a better term for how it's usually handled - most tools hide these objects | ||
from auto-complete unless you start typing an `_`). | ||
|
||
Naming anything you don't expect outside users to use in this way is a good | ||
practice. In practical terms for a library author, it means you can modify or | ||
remove any `_*` objects without worrying about who it might break. And for a | ||
library user, try to never use anything starting with a single `_`, as that | ||
could be changed at any time. | ||
|
||
However, this convention has limits. What about imports? You usually don't (and | ||
shouldn't) rename your imports. But they don't start with an underscore, so does | ||
that make them public? Even `from __future__ import anotations` will add an | ||
`annotations` object, publicly visible, to your project! | ||
|
||
A second solution sometimes attempted is deleting things after using them. This | ||
can cause surprising problems in some cases, though, due to Python's late | ||
binding. It's also easy to forget to delete something like an import, due to the | ||
fact the `del` statements are at the end of the module, far away from the usage. | ||
|
||
## Setting all | ||
|
||
The solution to this is the `__all__` attribute. This is a public declaration of | ||
your exported API. It looks like this: | ||
|
||
```python | ||
__all__ = ["object1", "Class1", "some_reexport"] | ||
``` | ||
|
||
Setting this does several things: | ||
|
||
- It controls what is imported if a user does `from module import *`. | ||
- It provides a human readable list of the module's public API without looking | ||
at the entire file (ideally place it near the top of the file). | ||
- It informs static tools like type checkers about the public API, including | ||
re-exports of things you import. | ||
- It _can_ be used to control what `dir(module)` (and therefore tab completion) | ||
sees. | ||
|
||
If you want to improve tab completion / `dir()` calls, you can add this small | ||
boilerplate function to your modules: | ||
|
||
```python | ||
def __dir__() -> list[str]: | ||
return __all__ | ||
``` | ||
|
||
This causes tab completion to only show your public API! You can still access | ||
everything in the module, it just won't be shown to the user. | ||
|
||
This `__dir__()` trick doesn't work very well on `__init__.py` modules, since | ||
ideally you want submodules to be shown if they have been imported. It's best to | ||
keep `__init__.py` modules minimal. It's tempting to import contents from your | ||
submodules in `__init__.py`, but keep in mind importing any submodule always | ||
runs all parent `__init__.py`s, so you'll likely take an import performance hit | ||
and might have to deal with circular import issues in order to save a user a few | ||
keystrokes. | ||
|
||
There are some dynamic solutions to building your `__all__` variable without | ||
having to list all the items in a list near the top of your file. However, you | ||
lose several features doing so, such as the human readable list of module | ||
contents and static type checker support. |