Command line utility for creating a python project skeleton. Requires python 3 to run.
Examples:
python3 python-new-project.py myproject --app # create a new application based project in ./myproject
python3 python-new-project.py /path/to/myproject --lib # create a library project in /path/to/myproject
python3 python-new-project.py myproject --app --force # create the new project overwriting any existing skeleton files
Libraries specify abstract dependencies (including dev-packages) in setup.py
but may use Pipenv to create a virtual env.
Applications specify exact dependencies in a Pipfile
+ Pipfile.lock
that are checked into the source code. A setup.py
file is not needed as there need not be any packaging.
It's easy to get library/tool version mismatches when not working in an isolated environment on a development machine. It is more feasible to install globally if using something like jails/docker.
- Pipenv is the latest python tooling for creating virtual environments and dealing with dependencies.
- Anaconda is a tool for creating isolated environment management. Virtualenv could also be used but anaconda has better support for building non-python code. When using Pipenv you probably are not going to need it, though pipenv might be able to use an arbritrary environment.
pip
andvirtualenv
can be avoided when using Pipenv - pipenv uses them in its implementation.pip-tools
is a good option for carefully managing production and development dependencies if not usingpipenv
.
A Pipfile
and Pipfile.lock
should not be committed to the project for library based projects - only abstract
dependencies should be specified in the setup.py
for libraries.
Prefer absolute imports, otherwise I find at least one of flask or pytest or the editor/IDE have some import problem. Pep-008 recommends absolute imports or maybe explicit relative imports. Implicit relative imports are removed from python 3.
import mypkg.sibling
import mypkg.sibling as sibling
from mypkg import sibling
from mypkg.sibling import example
from . import sibling # relative
from .sibling import example # relative
Most tools (e.g. coverage, pytest, flake8, mypy) now allow their config to go in the one file setup.cfg
instead of
requiring their own file (e.g. mypy.ini
, .flake
, and pytest.ini
).
[tool:pytest]
addopts = --doctest-modules --ignore build
[mypy]
python_version = 3.6
ignore_missing_imports = true
Makes mypy quiet when it finds 3rd party code that does not have any type annotations.
Running something like app/main.py
within a project results in import errors as the main file does not know it is part of a package. Both setting the PYTHONPATH
and doing sys.path.insert
stuff seems ugly. See Stackoverflow.
A shell script per runnable main file seems a reasonable approach, for example:
#!/bin/bash
BASEDIR=$(dirname "$0")
PYTHONPATH=$BASEDIR python3 app/main.py
Install the python dependency pytest-cov
into the virtual environment.
See pytest --help
. For example, to run coverage in html or xml on mymodule
and submodules.
pytest --cov-report=html|xml --cov=mymodule
or pytest --cov=mymodule
to print to terminal.
If using python3 and specificaly python3.5+ we can use type hints, that at least serves as minimal documentation and provides some support for type driven development / domain modelling.
The MyPy project is a linter that will try to check those types statically. It is however, an alpha status project, despite being > 5 years old.
Putting this pytest conftest.py
file in the main package directory(s) to fix this pytest/doctest/flask combination issue.
"""Pytest configuration module to fix pytest/doctest-discovery/flask interaction issue."""
import importlib
def pytest_configure():
patch_flask_for_doctest()
def patch_flask_for_doctest():
"""
Patch flask magic objects to keep them from raising
RuntimeErrors during doctest discovery.
https://github.com/pallets/flask/issues/1680
"""
flask = importlib.import_module('flask')
object.__setattr__(flask.request, '__wrapped__', None)
object.__setattr__(flask.session, '__wrapped__', None)
object.__setattr__(flask.current_app, '__wrapped__', None)