No project description provided
Project description
Python template project
This repository is meant to be a starting point for project work in Python. We have another repository, that serves more as an introduction to project work in Python and contains a lot of useful information. If you are wondering about something Python-related, you could try to check that out.
Table of Contents
- Python template project
Introduction
The intended use of this repository is as a template for when you're creating a new python project repo in Github. As such, instead of cloning down this repo directly, you should instead create a new repo in Github, and then choose this repo as the template for that. Alternatively, on GitHub you can press the "Use this template" button in the upper right corner of the window.
With this template for Python projects, we will attempt to provide a template that is easy to work with and provides sufficient functionality for most projects.
We give a recipe for how to setup your Python project and installing it. This template is based on the use of 'virtual environments' to keep our
required python packages installed separately from the rest of your operating system. And to do this job as well as several other package-related tasks,
we suggest to use the tool poetry.
In addition, we've set up a way to integrate tests that checks your code when it's pushed to Github.
If you wish to see an example of a more fully fledged example python project, you can check out python_project_standard. There we've included more information on how to setup a python development environment in VS Code, as well as examples of interactive scripts and functions implemented in different files and sub-folders of the main project package.
How to create a package?
The layout of this project repository is as follows.
.
├── LICENCE.txt
├── notebooks
│ └── example_notebook.ipynb
├── scripts
│ └── example_script.py
├── poetry.toml
├── pyproject.toml
├── README.md
├── src
│ └── package_name
│ └── __init__.py
│
└── tests
└── test_example.py
We will start by the most important directory in this repository, which is the "src"-folder. All the code within the package you are building should be placed within the "src"-folder. The "src"-folder separated the source code of your package from other scripts such as tests, documentations and notebooks/scripts with examples of how the package can be used. The directory "package_name" in the "src" folder, contains the package that is made. In order for Python to know that package_name is a module, and not only a directory, it must contain a file named "__init__.py". The __init__.py-file can be empty, but signals to Python that the directory is also a module which allows importation of the directory and sub-modules within the directory that otherwise would not be possible.
Steps to creating your own package
-
Change the name of the directory "package_name" inside the "src"-folder to the name of the package you are developing.
-
In the "pyproject.toml"-file, the following changes must made:
a) The information about the package, such as the name of the package, its author(s), the version of the package and so forth must be specified in the top of the file beneath the
[poetry.tool]-header.b) Dependencies of the package are included below the
[tool.poetry.dependencies]header. Here, you can also specify which Python versions the package is compatible with. This can also be left empty, and our package managerpoetrywill update it when new packages are added, more on this later.c) Optional. When developing and using the package, it can be useful to provided examples of how the package can be used. This could e.g. entail running unit tests, or plotting some results of the package which would require the use of a library like
matplotlib. This means that there are packages that are required in development that are not a dependency of the package. Such development dependencies are specified below the header[tool.poetry.group.dev.dependencies], and can again be added using the poetry tool as explained below.
If the above changes have been made, you are ready to install the package and start developing it. In order to do that, we will introduce you to a package manager called "poetry" in the following sections.
Poetry - a Python package manager
In order to ensure that it's easy to get going on a project which uses a certain set of packages, we're recommending the use of a tool called Poetry. This tool makes it relatively easy to add packages you need for your project, and also bundle up your package and either send it to someone else, or publish it online so that others can use it.
In this README we're mostly assuming that you're using the bash terminal on Linux or PowerShell on Windows.
The required commands and configuration are likely to change if that's not the case.
There is however some support for setting the correct $PATH variable and another environment
variable required for working over SSH using a tcsh shell.
Installing Poetry
To install poetry, we recommend using a tool called pipx, which will install Poetry itself in a contained environment where it does not
interfere with any of your other installed python packages. To this end, we assume that you have the regular python package installer
pip available.
If that is the case, you can run the following commands in your terminal to install poetry:
(On windows, you might have to replace 'python3' with 'python' or 'py', depending on how you installed python.)
-
First we install the 'pipx' tool to our 'user' package directory:
python3 -m pip install --user pipx.
-
Then we use
pipxto installpoetryat a given version:-
python3 -m pipx install poetry==1.6.1. -
This command will likely output some warnings telling you that programs where installed into one or two directories "that are not on the
$PATHvariable". This variable is a list of directories stored as a (semi-)colon separated string that the operating system searches through to find a program name when we type it into the terminal. It is helpful to copy down these directories and saving them in a text file for reference later if we need to add them to the$PATHvariable manually. -
When the directories containing our python programs are not in the
$PATHvariable, the operating system will not find eitherpipxorpoetrywhen we try to call them from the terminal. In the next point we will attempt to fix that, first automatically usingpipxitself, and if that does not work then we'll try the more manual route. Regardless of wether or not we add these directories to the$PATHvariable, you should be able to callpipxandpoetryusing thepython3 -m <poetry|pipx> [cmd]syntax.
-
-
To attempt to add the required folders containing our python programs into the
$PATHvariable, we use theensurepathcommand ofpipx:python3 -m pipx ensurepath.- If this does not work, we have to manually add the directories where 'pipx' and 'poetry' have been installed
to our
$PATHvariable. This process is different for Linux and Windows. - On a Windows laptop:
- In a Powershell terminal window, we first find our
$PROFILEfile, which is executed when Powershell starts whenever it exists. We can find this by runningecho $PROFILE. - Open this file by running
notepad $PROFILE. If you get a promt saying that the file does not exist, confirm that you want to create it. - In the notepad window, add the directories that pipx warned about earlier as missing from the
$PATHwith this command: $Env:PATH += ";<python-program-directory>" - In my case I added the following two lines:
- $Env:PATH += ";C:\Users\jvkolsto\.local\bin"
- $Env:PATH += ";C:\Users\jvkolsto\AppData\Local\Programs\Python\Python311\Scripts"
- In a Powershell terminal window, we first find our
- On a (Linux) server:
- To add the path
~/.local/binto your$PATHon the servers, run one of the following commands in a shell: - Linux
bashshell (Each line starting something like:<username>@NR-F0YBHW3:~ $):echo -e "\n# Add ~/.local/bin to PATH for python-apps like poetry:\nexport PATH=\"\$PATH:$(echo ~)/.local/bin\"\n" >> ~/.bashrc
- Linux
tcshshell (Each line starting something like:NR-F0YBHW3:~>):echo "\n# Add ~/.local/bin to PATH for python-apps like poetry:\nsetenv PATH "\"\$PATH\"":`echo ~`/.local/bin\n" >> ~/.tcshrc
- The above commands append a few lines of text to your
~/.bashrcor~/.tcshrcfile, which is like the$PROFILEfile for Powershell, in the sense that your shell runs all the commands in this file every time it starts. - If you need to add other paths, use a terminal text editor (e.g. nano, pico or vim) and open the corresponding configuration
file. Then you can copy the previous line that was pasted to the end of the file and switch out which folder that's
appended to the
$PATHvariable.
- To add the path
-
Lastly: When working over SSH onto e.g. the SAMBA servers, we need to set a variable that informs
poetrynot to check for any other package repository except for the standard one. Run one of the following commands from your shell on a server:- Linux bash shell (Each line starting something like:
<username>@NR-F0YBHW3:~ $):echo -e "\n# Added to fix poetry over SSH:\nexport PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring" >> ~/.bashrc- then either run
source ~/.bashrcor start another terminal where you can begin usingpoetry.
- Linux tcsh shell (Each line starting something like:
NR-F0YBHW3:~>):echo "\n# Added to fix poetry over ssh:\nsetenv PYTHON_KEYRING_BACKEND keyring.backends.null.Keyring" >> ~/.tcshrc- then either run
source ~/.tcshrcor start another terminal where you can begin usingpoetry.
- Linux bash shell (Each line starting something like:
Package setup
We've set up the pyproject.toml file to install the local <package_name> into the project dependency. And all code changes done within
src/<package_name> will be reflected the next time you start python. To install the python virtual environment you run
poetry install
from the top level repo-directory (the folder you cloned the repo into). And then if you want to update all you packages to their newest compatible versions, you run
poetry update.
If you only want to update a specific pacakge, you instead run
poetry update <package-name>
Package dependencies
When you want to add or remove a package from your project's python installation, you use
the poetry commands add and remove followed by the name of the package. For example,
you could do:
poetry add numpypoetry remove scipy
To specify a version for the package to install, one uses this syntax:
poetry add numpy@1.26.3
And then if you wish to revert to the latest version, you can do:
poetry add numpy@latest
And if you want to include a package that you only need when you're developing the package or working interactively,you can specify the group that a package belongs under like this:
poetry add <package> --group devWe have already added some pacakges under thedevgroup, to facilitate unit testing, running jupyter notebooks, and linting/formatting of python code.
Installing package from Github using Poetry
In order to add a package dependency from a Github repository (that you have read access to), you
first go to the repository and copy the SSH-url. This is the string you would usually copy to clone
down the repository yourself through the command git clone <git-ssh-url>.
An example github ssh URL for the package ERApy is
git@github.com:SeasonalForecastingEngine/ERApy.git.
Then we run a poetry command to install that package from Github:
poetry add --editable git+ssh://git@github.com:SeasonalForecastingEngine/ERApy.git
This command will build and install the python package ERApy from the default branch,
usually main or master, and the --editable argument tells poetry to grab the newest
version of the repo whenever you do poetry update.
If you want to specify a specific branch og tag to install from, you can add a signifier
#<tag-or-branch> to the end of the Github SSH URL, resulting in the following command:
poetry add git+ssh://git@github.com:SeasonalForecastingEngine/ERApy.git#<tag-or-branch>
In the above example command I've removed the --editable argument. At least when installing
specific version of a package from Github as specified by a tag (i.e. v0.1.3) the code for that
version should not change, and therefore it makes less sense to grab the "latest" version of the
package from Github each time we run poetry update.
Installing package from Github using pip
It is also possible to install a package from Github using pip, if for example a
customer does not use poetry, but then we need to slightly modify the Github SSH URL.
Specifically, we replace the : after 'github.com' with a / in the URL. As an example,
to install ERApy from Github using pip, we'd run this command:
pip install git+ssh://git@github.com/SeasonalForecastingEngine/ERApy.git
Installing package from local folder
Say you're working on two different packages at the same time, and the second package uses the first one as a dependency. Then it might be useful to use the development version of the first package in your second package as you're developing both packages.
We can achieve this by using poetry to add a package directly from a folder on your local machine. Say the ERApy package is stored in the same folder as the second package we're working on. To install ERApy then, we could use the command
poetry add ..\ERApy\ --group dev
where I've added to argument to group ERApy under development dependencies, to show that this is possible as well in this context.
Running unit tests
When building a package it can be helpful to write tests to check that
the functionality is as expected. One tool to do this for python-packages
is called pytest, and we've added this tool as a development dependency
in this python project template.
Tests are defined by function with names starting with test_, in python files starting with 'test_' under the 'tests/' folder. There are example
tests already present that showcase some of the functionality of pytest.
To run these tests, we invoke pytest through poetry, which ensures that
pytest runs under the virtual environment managed by poetry.
poetry run pytestAnd this produces a test-summary in the terminal showing wether or not tests passed.
Poetry configuration files
This section is more for the people that are especially interested in how poetry works, and can probably be skipped on
a first reading.
Essentially the idea is that all the configuration needed to specify the dependencies and tooling required for a
python package should be specified in the pyproject.toml file. This file contains the headers [tool.poetry],
[tool.poetry.dependencies] and several others, and different tools use the lines under each header to specify
how they work. We've already added a 'template' poetry project configuration at the top of this file, but this
needs to be changed before you start using this package template yourself.
Inside this pyproject.toml file we've also added configuration that make it easy to make and run tests
on the functionality of your package. This is under the [tool.pytest.ini_options]. And yet another useful tool
for minimizing small typos and errors in your python code is the ruff package, which you'll find configured under
the [tool.ruff] and [tool.ruff.format] headers.
Now while the pyproject.toml contains the list of the packages that you want available in your python project,
those packages might again depend on other packages, and so on. Therefore, to save a reproducible snapshot of
your python project/virtual environment, poetry generates the file poetry.lock. This file is machine generated,
and not meant for human consumption. However, if there is such a file in your repository when you run
poetry install, you should in principle get the exact same set of packages as another developer.
Lastly, the poetry.toml file contains configuration specific to the poetry tool. Here we've set the options
such that poetry generates the virtual environment within the repo-directory. And such that the cache directory
used by poetry is also within the repo-directory. In this way the entire python project becomes quite well
contained within the repository.
Virtual environments and python versions
When working with Python, it can often be necessary to use different versions of packages in different projects, and it is also important to maintain dependencies between packages. For reproducibility, it is important to keep track of which package versions are used within a project. In order to achieve this, and more, it can be useful to work with a virtual environment. A virtual environment is a tool that helps to keep dependencies required by different projects separate by creating isolated spaces for them. This is particularly useful when different projects require different versions of the same package or dependency.
A virtual environment is a tool that helps to keep dependencies required by different projects separate by creating isolated spaces for them. This is particularly useful when different projects require different versions of the same package or dependency.
In the context of Python, a virtual environment (often abbreviated as "venv") is a self-contained directory tree that contains a Python installation for a particular version of Python, plus a number of additional packages. When you create a virtual environment, a fresh Python binary is installed along with a copy of the pip installer.
By activating a virtual environment, you can work with specific versions of Python and installed packages without affecting other projects that might be using different versions. This allows for a much cleaner and more manageable development environment. It can be useful to have a separate virtual environment for each Python project you are working on.
And with poetry installed, the easiest way to activate the project virtual environment is to run poetry shell.
To sum it up, virtual environments provide a way to avoid conflicts between system-wide packages and project-specific packages, and to ensure reproducibility and reliability of software projects.
When a poetry package is initialized, a virtual environment is created, and it's by default put in the .venv/ folder.
Building a python package
When we want to distribute a package, we build / bundle up the code using this command:
poetry build
which should give a similiar output to this:
Building package_name (0.1.0)
- Building sdist
- Built package_name-0.1.0.tar.gz
- Building wheel
- Built package_name-0.1.0-py3-none-any.whl
The resulting files package_name-0.1.0.tar.gz and package_name-0.1.0-py3-none-any.whl are placed in the dist/ folder. They are, respectively,
the sdist and wheel distribution formats. We recommend using the wheel
distribution formats as the standard file to share to consumers of your package.
It is not necessary to know all the
differences between these two formats, but at a course level the sdist format
is a "source distribution" which leaves it up to the people that install the
package to compile any source code in e.g. c++, fortran, or rust.
The wheel format on the other hand moves the compilation work to the
person building the package (i.e. us). This should hopefully make for an easier
installation process for the end user, and should decrease installation times.
Publishing a package to PyPi (public server)
If you want to publish your python package to the public such that everyone can install your package through pip, we need to publish to the Python Package Index (PyPi).
There is a good tutorial on how to do this here.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file test_poetry_nr-0.1.1.tar.gz.
File metadata
- Download URL: test_poetry_nr-0.1.1.tar.gz
- Upload date:
- Size: 9.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.8.1 CPython/3.10.12 Linux/5.15.133.1-microsoft-standard-WSL2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b35ab5baa4fd577d7707f570c5e1403110e51a987c4efc77a31029f778439e7c
|
|
| MD5 |
9356064fc6880e7d2faabc1ebebd1351
|
|
| BLAKE2b-256 |
9cd301162fe309be649113257f392c7b317ce3fbe99338c194a59a3cdb462bc2
|
File details
Details for the file test_poetry_nr-0.1.1-py3-none-any.whl.
File metadata
- Download URL: test_poetry_nr-0.1.1-py3-none-any.whl
- Upload date:
- Size: 8.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.8.1 CPython/3.10.12 Linux/5.15.133.1-microsoft-standard-WSL2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0c11f50fde5f3a553b22d244af910cf2df125b756d56cfaa0a7560ed8c767d48
|
|
| MD5 |
0e2bb2eb5de6c376670933fc4e9ba0f0
|
|
| BLAKE2b-256 |
bbafb89d2daa5901553c492b28f862a4b0c7abf31ea4299f51f173fbcc2915da
|