In general, if your Python project contains multiple packages that are native to the project, then there should probably be a single top-level package that all other packages are subpackages of (after all they are part of the same project so there should be some unifying justification for their existence). Since a directory counts as a package only when it has an __init__.py
, the other way to say this is that if any two sibling directories in your project both have an __init__.py
, then their parent should have one as well. So assuming that you have good reasons to have a "scripts" package and a "modules" package directly within your project root, which you very well might have, your project root directory probably should be a package as well.
If your top-level package is not the project root, it is usually considered fine to have some "loose" Python scripts adjacent to the top-level package. Like this:
project_root/
top_level_package/
__init__.py
module.py
subpackage/
__init__.py
anothermodule.py
adjacent_script.py
adjacent_script_2.py
The "loose" scripts can import directly from the packages and modules because they are in the same directory as the top-level package. The assumption with this construction is that the top-level package contains all "interesting" code of your project (your "selling point", or the "meat" of your project if you want), while the "loose" adjacent scripts function only as entry points to specific functionality that you may want to access from the top-level package. For example, you may have an adjacent script for starting the test suite, for installing the software, or to start the application if your project is an application.
Concerning your particular pattern, I'm under the impression that you distinguish "scripts" from "modules" because the scripts are meant as an interface between you and the modules. If it is only for you as a developer and you are using the "scripts" infrequently, as you said, it is probably fine to stick with your recipe as long as the root directory has an __init__.py
. If, however, you (or someone else) also use the "scripts" as an end user, or if you use them frequently, it is neater to provide a script adjacent to the top-level package that bundles the functionality from your "scripts" directory in a single command (or maybe a few if they offer very dissimilar functionality) with a subcommand for each "script". You might want to use the argparse standard module for that adjacent script. In either case, it would probably be more appropriate to call the "scripts" tools.
If you have only a few tools and they don't contain any code other than glue between the command line and your modules, you may also consider to just move them to the top directory.
Some sources: