GitHub - enerqi/python-skeleton: Utility for creating a python project skeleton
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
Notes
Libraries vs Applications
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.
Virtual Environment
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.
pipandvirtualenvcan be avoided when using Pipenv - pipenv uses them in its implementation.pip-toolsis 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.
Import Style
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
Tool config
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).
Pytest
[tool:pytest] addopts = --doctest-modules --ignore build
MyPy
[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 'main' type python files (if part of package)
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
Coverage reports on tests
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.
Type hint linting and documentation
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.
Enabling doctests in combination with pytest & flask
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)