Skip to content

Automatically update __all__ statements in python libraries.

License

Notifications You must be signed in to change notification settings

tjsmart/allways

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

allways

Automatically update __all__ statements.

Installation

pip install allways

Command line interface

allways <file1.py> <file2.py> ...

As a pre-commit hook

See pre-commit for instructions.

Sample .pre-commit-config.yaml

-   repo: https://github.com/tjsmart/allways-pre-commit
    rev: v0.0.2
    hooks:
    -   id: allways

Note: by default the pre-commit hook will run only against __init__.py files.

What does it do?

Add __all__ statements to your python files

from ._foo import bar
from ._x import y as z

def foo():
    ...

becomes

from ._foo import bar
from ._x import y as z

def foo():
    ...


# allways: start
__all__ = [
    "bar",
    "foo",
    "z",
]
# allways: end

Ignore private variables

from . import _foo
from . import bar

becomes

from . import _foo
from . import bar


# allways: start
__all__ = [
    "bar",
]
# allways: end

Update pre-existing __all__ statements

from . import bar
from . import baz


# allways: start
__all__ = [
    "bar",
    "foo",
]
# allways: end

becomes

from . import bar
from . import baz


# allways: start
__all__ = [
    "bar",
    "baz",
]
# allways: end

Why?

the problem

I choose to organize python libraries with:

  • PEP 561 compliance
  • private module files, public (sub-)package folders
  • using __init__.py to define the public interface of a (sub-)package

For example, I might layout my project as such:

pkg/
├── bar/
│   ├── _bar.py
│   └── __init__.py
├── _foo.py
├── __init__.py
└── py.typed

Contained in the files pkg/_foo.py and pkg/bar/_bar.py there will be some portion that I would like to expose publicly via pkg/__init__.py and pkg/bar/__init__.py, respectively.

For example, perhaps I would like to expose a function do_something from the module file pkg/_foo.py by adding the following line to pkg/__init__.py:

from ._foo import do_something

And here is where the problem arises! (I know... a lot of setup...)

When a user of our package turns to use do_something they will be slapped on the wrist by the type-checker.

  • pyright output:
t.py:1:18 - error: "do_something" is not exported from module "pkg"
Import from "pkg._foo" instead (reportPrivateImportUsage)
  • mypy --strict output:
t.py:1: error: Module "pkg" does not explicitly export attribute "do_something"  [attr-defined] 

And if you aren't concerned that users will have to ignore this type error, know that it get's worse! Language servers will not display any hint that the object pkg.do_something exists. 😱

For small projects maintaining this by hand is no big deal. But for large projects with several contributors this becomes a complete wast of time! 😠

the solution

According to pyright documentation, a typed library can choose to explicitly re-export symbols by adding it to the __all__ of the corresponding module.

allways mission is to automate the process of updating __all__ statements in __init__.py files. 🤗

but also

My personal goal here is to contribute something to open source and write some more rust! 🦀

About

Automatically update __all__ statements in python libraries.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages