Hatch is a productivity tool designed to make your workflow easier and more efficient, while also reducing the number of other tools you need to know. It aims to make the 90% use cases as pleasant as possible.
For me personally, Hatch has entirely replaced the manual (or entire!) use of these:
- Cookiecutter PyPackage
- pip
- virtualenv
- pyenv
- Pew
- inve
- pytest
- Coverage.py
- twine
- bumpversion
- zest.releaser
- Incremental
python setup.py ...
/^\/^\ _|__| O| \/ /~ \_/ \ \____|__________/ \ \_______ \ `\ \ \ | | \ / / \ / / \ / / \ \ / / \ \ / / _----_ \ \ / / _-~ ~-_ | | ( ( _-~ _--_ ~-_ _/ | \ ~-____-~ _-~ ~-_ ~-_-~ / ~-_ _-~ ~-_ _-~ ~--______-~ ~-___-~
Table of Contents
- Completely cross-platform \(*_^)/
- Configurable project creation! CI service files, readme format/badges, licenses, etc.
- Easiest virtual environment management available
- Activation of virtual envs without disruption of current environments
- Ability to send commands to virtual envs without the need for activation
- Changed prompts when in a virtual env
- Installing/updating packages defaults to a per-user basis, allowing global usage with elevated privileges
- Configurable semantic version bumping
- Robust build/package cleanup
- Easy testing with code coverage
- Simple building and releasing for PyPI
- All commands are environment-aware w.r.t. python/pip
- Virtual envs can be the target for relevant commands
- Editable packages can be the target for relevant commands
Hatch is distributed on PyPI as a universal wheel and is available on Linux/macOS and Windows and supports Python 3.5+ and PyPy.
$ pip3 install --user hatch
or simply pip
if that already points to a Python 3 version.
If hatch
doesn't work on your system immediately after that, please
run this command.
After the first installation, you may want to run hatch config --restore
to
ensure your config file is available.
For your convenience, anything after a --
will be treated as arguments.
Locates, updates, or restores the config file.
$ hatch config
Settings location: /home/ofek/.local/share/hatch/settings.json
Options:
- -u/--update
- Updates the config file with any new fields.
- --restore
- Restores the config file to default settings.
Creates a new Python project. Think of an "egg" as a new idea.
Values from your config file such as name
and pyversions
will be used
to help populate fields. You can also specify things like the readme format
and which CI service files to create. All options override the config file.
Here is an example using an unmodified config file:
$ hatch egg my-app
Created project `my-app`
$ tree --dirsfirst my-app
my-app
├── my_app
│ └── __init__.py
├── tests
│ └── __init__.py
├── LICENSE-APACHE
├── LICENSE-MIT
├── MANIFEST.in
├── README.rst
├── requirements.txt
├── setup.py
└── tox.ini
2 directories, 8 files
Arguments:
- name
- The desired name of package.
Options:
- --basic
- Disables third-party services and readme badges.
- --cli
- Creates a
cli.py
in the package directory and an entry point insetup.py
pointing to the properly named function within. Also, a__main__.py
is created so it can be invoked viapython -m pkg_name
. - -l/--licenses
- Comma-separated list of licenses to use.
Same as egg
but the project target is the current directory.
Increments a project's version number using semantic versioning.
Valid choices for the part are major
, minor
, patch
(fix
alias), pre
, and build
.
The path to the project is derived in the following order:
- The optional argument, which should be the name of a package
that was installed via
hatch install -l
orpip install -e
. - The option --path, which can be a relative or absolute path.
- The current directory.
If the path is a file, it will be the target. Otherwise, the path, and
every top level directory within, will be checked for a __version__.py
,
__about__.py
, and __init__.py
, in that order. The first encounter of
a __version__
variable that also appears to equal a version string will
be updated. Probable package paths will be given precedence.
The default tokens for the prerelease and build parts, rc and build
respectively, can be altered via the options --pre
and --build
, or
the config entry semver
.
$ git clone -q https://github.com/requests/requests && cd requests
$ hatch grow build
Updated /home/ofek/requests/requests/__version__.py
2.18.4 -> 2.18.4+build.1
$ hatch grow fix
Updated /home/ofek/requests/requests/__version__.py
2.18.4+build.1 -> 2.18.5
$ hatch grow pre
Updated /home/ofek/requests/requests/__version__.py
2.18.5 -> 2.18.5-rc.1
$ hatch grow minor
Updated /home/ofek/requests/requests/__version__.py
2.18.5-rc.1 -> 2.19.0
$ hatch grow major
Updated /home/ofek/requests/requests/__version__.py
2.19.0 -> 3.0.0
Arguments:
- part
- The part of version to bump.
- package
- The editable package to target (optional).
Options:
- -p/--path
- A relative or absolute path to a project or file.
- --pre
- The token to use for
pre
part, overriding the config file. Default: rc - --build
- The token to use for
build
part, overriding the config file. Default: build
Runs tests using pytest
, optionally checking coverage.
The path is derived in the following order:
- The optional argument, which should be the name of a package
that was installed via
hatch install -l
orpip install -e
. - The option --path, which can be a relative or absolute path.
- The current directory.
If the path points to a package, it should have a tests
directory.
$ git clone https://github.com/ofek/privy && cd privy
$ hatch test -c
========================= test session starts ==========================
platform linux -- Python 3.5.2, pytest-3.2.1, py-1.4.34, pluggy-0.4.0
rootdir: /home/ofek/privy, inifile:
plugins: xdist-1.20.0, mock-1.6.2, httpbin-0.0.7, forked-0.2, cov-2.5.1
collected 10 items
tests/test_privy.py ..........
====================== 10 passed in 4.34 seconds =======================
Tests completed, checking coverage...
Name Stmts Miss Branch BrPart Cover Missing
-----------------------------------------------------------------
privy/__init__.py 1 0 0 0 100%
privy/core.py 30 0 0 0 100%
privy/utils.py 13 0 4 0 100%
tests/__init__.py 0 0 0 0 100%
tests/test_privy.py 57 0 0 0 100%
-----------------------------------------------------------------
TOTAL 101 0 4 0 100%
Arguments:
- package
- The editable package to target (optional).
Options:
- -p/--path
- A relative or absolute path to a project or test directory.
- -c/--cov
- Computes, then outputs coverage after testing.
- -m/--merge
- If --cov, coverage will run using --parallel-mode and combine the results.
- -ta/--test-args
- Pass through to
pytest
, overriding defaults. Example:hatch test -ta "-k test_core.py -vv"
- -ca/--cov-args
- Pass through to
coverage run
, overriding defaults. Example:hatch test -ca "--timid --pylib"
- -e/--env-aware
- Invokes
pytest
andcoverage
as modules instead of directly, i.e.python -m pytest
.
Names an absolute path to a Python executable. You can also modify
these in the config file entry pypaths
.
Hatch can then use these paths by name when creating virtual envs, building packages, etc.
$ hatch pypath -l
There are no saved Python paths. Add one via `hatch pypath NAME PATH`.
$ hatch pypath py2 /usr/bin/python
Successfully saved Python `py2` located at `/usr/bin/python`.
$ hatch pypath py3 /usr/bin/python3
Successfully saved Python `py3` located at `/usr/bin/python3`.
$ hatch pypath -l
py2 -> /usr/bin/python
py3 -> /usr/bin/python3
Arguments:
- name
- The desired name of the Python path.
- path
- An absolute path to a Python executable.
Options:
- -l/--list
- Shows available Python paths.
Creates a new virtual env that can later be utilized with the use
command.
$ hatch pypath -l
py2 -> /usr/bin/python
py3 -> /usr/bin/python3
$ hatch env -l
No virtual environments found in /home/ofek/.local/share/hatch/venvs. To create one do `hatch env NAME`.
$ hatch env -q my-app
Already using interpreter /usr/bin/python3
Successfully saved virtual env `my-app` to `/home/ofek/.local/share/hatch/venvs/my-app`.
$ hatch env -q -py py2 old
Successfully saved virtual env `old` to `/home/ofek/.local/share/hatch/venvs/old`.
$ hatch env -q -pp ~/pypy3/bin/pypy fast
Successfully saved virtual env `fast` to `/home/ofek/.local/share/hatch/venvs/fast`.
$ hatch env -l
Virtual environments found in /home/ofek/.local/share/hatch/venvs:
fast ->
Version: 3.5.3
Implementation: PyPy
my-app ->
Version: 3.5.2
Implementation: CPython
old ->
Version: 2.7.12
Implementation: CPython
Arguments:
- name
- The desired name of the virtual environment.
Options:
- -py/--python
- The named Python path to use. This overrides --pypath.
- -pp/--pypath
- An absolute path to a Python executable.
- -c/--clone
- Specifies an existing virtual env to clone. (Experimental)
- -r/--restore
- Attempts to make all virtual envs in the venvs directory usable by fixing the
executable paths in scripts and removing all compiled
*.pyc
files. (Experimental) - -q/--quiet
- Decreases verbosity.
- -l/--list
- Shows available virtual envs.
Removes named Python paths or virtual environments.
$ hatch pypath -l
py2 -> /usr/bin/python
py3 -> /usr/bin/python3
invalid -> :\/:
$ hatch env -l
Virtual environments found in /home/ofek/.local/share/hatch/venvs:
duplicate ->
Version: 3.5.2
Implementation: CPython
fast ->
Version: 3.5.3
Implementation: PyPy
my-app ->
Version: 3.5.2
Implementation: CPython
old ->
Version: 2.7.12
Implementation: CPython
$ hatch shed -p invalid -e duplicate,old
Successfully removed Python path named `invalid`.
Successfully removed virtual env named `duplicate`.
Successfully removed virtual env named `old`.
Options:
- -p/-py/--pypath
- Comma-separated list of named Python paths.
- -e/--env
- Comma-separated list of named virtual envs.
Activates or sends a command to a virtual environment. A default shell
name (or command) can be specified in the config file entry shell
. If
there is no entry nor shell option provided, a system default will be used:
cmd
on Windows, bash
otherwise.
Any arguments provided after the first will be sent to the virtual env as a command without activating it. If there is only the env without args, it will be activated similarly to how you are accustomed.
Activation will not do anything to your current shell, but will rather
spawn a subprocess to avoid any unwanted strangeness occurring in your
current environment. If you would like to learn more about the benefits
of this approach, be sure to read https://gist.github.com/datagrok/2199506.
To leave a virtual env, type exit
, or you can do Ctrl-D
on non-Windows
machines.
Non-nesting:
$ hatch env -l
Virtual environments found in `/home/ofek/.local/share/hatch/venvs`:
fast ->
Version: 3.5.3
Implementation: PyPy
my-app ->
Version: 3.5.2
Implementation: CPython
old ->
Version: 2.7.12
Implementation: CPython
$ python -c "import sys;print(sys.executable)"
/usr/bin/python
$ hatch use my-app
(my-app) $ python -c "import sys;print(sys.executable)"
/home/ofek/.local/share/hatch/venvs/my-app/bin/python
(my-app) $ hatch use fast
(my-app) $ exit
(fast) $ python -c "import sys;print(sys.executable)"
/home/ofek/.local/share/hatch/venvs/fast/bin/python
(fast) $ exit
$
Nesting:
$ hatch use my-app
(my-app) $ hatch use -n fast
2 (fast) $ hatch use -n old
3 (old) $ exit
2 (fast) $ exit
(my-app) $ exit
$
Commands:
$ hatch use my-app pip list --format=columns
Package Version
---------- -------
pip 9.0.1
setuptools 36.3.0
wheel 0.29.0
$ hatch use my-app hatch install -q requests six
$ hatch use my-app pip list --format=columns
Package Version
---------- -----------
certifi 2017.7.27.1
chardet 3.0.4
idna 2.6
pip 9.0.1
requests 2.18.4
setuptools 36.3.0
six 1.10.0
urllib3 1.22
wheel 0.29.0
Arguments:
- env_name
- The name of the desired virtual environment to use.
- command
- The command to send to the virtual environment (optional).
Options:
- -s/--shell
- The name of shell to use e.g.
bash
. If the shell name is not supported, e.g.bash -O
, it will be treated as a command and no custom prompt will be provided. This overrides the config file entryshell
. - -n, --nest / -k, --kill
- Whether or not to nest shells, instead of killing them to mirror the
infamous activate script's behavior. Some shells can only be nested. By
default the shell will not be nested if possible. This flag overrides
the config file entry
nest_shells
.
Removes a project's build artifacts.
The path to the project is derived in the following order:
- The optional argument, which should be the name of a package
that was installed via
hatch install -l
orpip install -e
. - The option --path, which can be a relative or absolute path.
- The current directory.
All *.pyc
/*.pyd
files and __pycache__
directories will be removed.
Additionally, the following patterns will be removed from the root of the path:
.cache
, .coverage
, .eggs
, .tox
, build
, dist
, and *.egg-info
.
If the path was derived from the optional package argument, the pattern
*.egg-info
will not be applied so as to not break that installation.
Arguments:
- package
- The editable package to target (optional).
Options:
- -p/--path
- A relative or absolute path to a project.
- -c/--compiled-only
- Removes only .pyc files.
- -v/--verbose
- Shows removed paths.
Builds a project, producing a source distribution and a wheel.
The path to the project is derived in the following order:
- The optional argument, which should be the name of a package
that was installed via
hatch install -l
orpip install -e
. - The option --path, which can be a relative or absolute path.
- The current directory.
The path must contain a setup.py
file.
Arguments:
- package
- The editable package to target (optional).
Options:
- -p/--path
- A relative or absolute path to a project.
- -py/--python
- The named Python path to use. This overrides --pypath.
- -pp/--pypath
- An absolute path to a Python executable.
- -u/--universal
- Indicates compatibility with both Python 2 and 3.
- -n/--name
- Forces a particular platform name, e.g. linux_x86_64.
- -d/--build-dir
- An absolute path to the desired build directory.
- -c/--clean
- Removes build artifacts before building.
Uploads all files in a directory to PyPI using Twine.
The path to the build directory is derived in the following order:
- The optional argument, which should be the name of a package
that was installed via
hatch install -l
orpip install -e
. - The option --path, which can be a relative or absolute path.
- The current directory.
If the path was derived from the optional package argument, the
files must be in a directory named dist
.
The PyPI username can be saved in the config file entry pypi_username
.
If the TWINE_PASSWORD
environment variable is not set, a hidden prompt
will be provided for the password.
Arguments:
- package
- The editable package to target (optional).
Options:
- -p/--path
- A relative or absolute path to a build directory.
- -u/--username
- The PyPI username to use.
- -t/--test
- Uses the test version of PyPI.
- -s/--strict
- Aborts if a distribution already exists.
If the option --env is supplied, the install will be applied using
that named virtual env. Unless the option --global is selected, the
install will only affect the current user. Of course, this will have
no effect if a virtual env is in use. The desired name of the admin
user can be set with the _DEFAULT_ADMIN_
environment variable.
With no packages selected, this will install using a setup.py
in the
current directory.
Arguments:
- packages
- The packages to install (optional).
Options:
- -e/--env
- The named virtual env to use.
- -l/--local
- Corresponds to
pip
's --editable option, allowing a local package to be automatically updated when modifications are made. - -g/--global
- Installs globally, rather than on a per-user basis. This has no effect if a virtual env is in use.
- -q/--quiet
- Decreases verbosity.
If the option --env is supplied, the uninstall will be applied using
that named virtual env. Unless the option --global is selected, the
uninstall will only affect the current user. Of course, this will have
no effect if a virtual env is in use. The desired name of the admin
user can be set with the _DEFAULT_ADMIN_
environment variable.
With no packages selected, this will uninstall using a requirements.txt
or a dev version of that in the current directory.
Arguments:
- packages
- The packages to uninstall (optional).
Options:
- -e/--env
- The named virtual env to use.
- -g/--global
- Uninstalls globally, rather than on a per-user basis. This has no effect if a virtual env is in use.
- -d/--dev
- When locating a requirements file, only use the dev version.
- -y/--yes
- Confirms the intent to uninstall without a prompt.
- -q/--quiet
- Decreases verbosity.
If the option --env is supplied, the update will be applied using
that named virtual env. Unless the option --global is selected, the
update will only affect the current user. Of course, this will have
no effect if a virtual env is in use. The desired name of the admin
user can be set with the _DEFAULT_ADMIN_
environment variable.
When performing a global update, your system may use an older version of pip that is incompatible with some features such as --eager. To force the use of these features, use --force.
With no packages nor options selected, this will update packages by looking
for a requirements.txt
or a dev version of that in the current directory.
To update this tool, use the --self flag. After the update, you may want
to press Enter. All other methods of updating will ignore hatch
. See:
pypa/pip#1299
Arguments:
- packages
- The packages to update (optional).
Options:
- -e/--env
- The named virtual env to use.
- --eager
- Updates all dependencies regardless of whether they still satisfy the new parent requirements. See: pypa/pip#3972
- --all
- Updates all currently installed packages. The packages
pip
,setuptools
, andwheel
are excluded. - --infra
- Updates only the packages
pip
,setuptools
, andwheel
. - -g/--global
- Updates globally, rather than on a per-user basis. This has no effect if a virtual env is in use.
- -f/--force
- Forces the use of newer features in global updates.
- -d/--dev
- When locating a requirements file, only use the dev version.
- -m/--module
- Invokes
pip
as a module instead of directly, i.e.python -m pip
. - --self
- Updates
hatch
itself. - -q/--quiet
- Decreases verbosity.
Hatch will always try to use the correct python/pip, however, when a virtual
env is not in use, things get a bit ambiguous. Therefore, you can set the
_DEFAULT_PYTHON_
and _DEFAULT_PIP_
environment variables to a command
name (recommended) or absolute path so the correct executable gets called. If
a virtual env is not in use and no env var is detected, the Python 3 versions
will be used on non-Windows machines.
Here is the literal implementation:
def get_proper_python():
if not venv_active():
default_python = os.environ.get('_DEFAULT_PYTHON_')
if default_python:
return default_python
elif not ON_WINDOWS:
return 'python3'
return 'python'
def get_proper_pip():
if not venv_active():
default_pip = os.environ.get('_DEFAULT_PIP_')
if default_pip:
return default_pip
elif not ON_WINDOWS:
return 'pip3'
return 'pip'
- shell
- The shell name or command to use when activating virtual envs.
- nest_shells
- Whether or not to nest shells, instead of killing them to prevent stacking.
- pypaths
- Maps names to an absolute path to a Python executable.
- semver
- Maps
pre
andbuild
semver parts to a textual representation. - pypi_username
- The username to use when uploading to PyPI.
- name
- Your name e.g. Bob Saget.
- Your email.
- basic
- If true, disables third-party services and readme badges during project creation.
- pyversions
- The default versions of Python to support. Must be in the form major.minor e.g.
3.7
. The valuespypy
andpypy3
are also accepted. - licenses
The default licenses to use. Defaults to dual MIT/Apache-2.0, which is desirable. Hatch currently supports:
mit
, which represents the MIT Licenseapache2
, which represents the Apache License, Version 2.0mpl
, which represents the Mozilla Public License 2.0cc0
, which represents the Creative Commons Zero v1.0 Universal
- readme
Mapping which helps construct your readme file. Hatch currently supports
rst
andmd
for theformat
key.Badges have the attributes
image
,target
, andalt
. Any others you add will become url parameters for theimage
. Also, if a{}
appears in theimage
ortarget
, the name of the created package will be formatted there.- vc
- The version control system to initialize when creating a project. Hatch
currently only supports
git
. - vc_url
- Your version control url e.g.
https://github.com/ofek
. - ci
- A list of third-party service files to create. Hatch currently only supports
travis
. Can be empty. - coverage
- A code coverage service to use. Hatch currently only supports
codecov
. Can be null. - extras
- A list of glob patterns to copy to new projects.
- meta
- start using AppVeyor
- next to the snake ascii art, put a
hatch
ed egg
- issues
- I really, really need help with this
- third-party services
- Support AppVeyor and CircleCI.
- Commands
bench
- use https://github.com/ionelmc/pytest-benchmarkstyle
- maybe not needed. use https://github.com/PyCQA/flake8docs
- maybe not needed. use https://github.com/sphinx-doc/sphinx/
Hatch is distributed under the terms of both
at your option.
- All the people who work on Click
- All the people involved in the Python packaging ecosystem
- All the people involved in the pytest ecosystem
- Ned Batchelder, for his Coverage.py is the unsung heroic tool of the Python community. Without it, users would be exposed to more bugs before we are.
- Te-jé Rodgers for bestowing me the name
hatch
on PyPI
Important changes are emphasized.
- Initial release