No project description provided
Reason this release was yanked:
First test of publishing a package to PyPi
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 managerpoetry
will 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
pipx
to installpoetry
at 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
$PATH
variable". 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$PATH
variable manually. -
When the directories containing our python programs are not in the
$PATH
variable, the operating system will not find eitherpipx
orpoetry
when we try to call them from the terminal. In the next point we will attempt to fix that, first automatically usingpipx
itself, 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$PATH
variable, you should be able to callpipx
andpoetry
using thepython3 -m <poetry|pipx> [cmd]
syntax.
-
-
To attempt to add the required folders containing our python programs into the
$PATH
variable, we use theensurepath
command 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
$PATH
variable. This process is different for Linux and Windows. - On a Windows laptop:
- In a Powershell terminal window, we first find our
$PROFILE
file, 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
$PATH
with 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/bin
to your$PATH
on the servers, run one of the following commands in a shell: - Linux
bash
shell (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
tcsh
shell (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
~/.bashrc
or~/.tcshrc
file, which is like the$PROFILE
file 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
$PATH
variable.
- To add the path
-
Lastly: When working over SSH onto e.g. the SAMBA servers, we need to set a variable that informs
poetry
not 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 ~/.bashrc
or 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 ~/.tcshrc
or 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 numpy
poetry 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 dev
We have already added some pacakges under thedev
group, 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 pytest
And 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
Hashes for test_poetry_package_name_nr-0.1.0.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | 5a91a1b13bbfb3750eed4b9eaebba8b1ef58cd9dbc22da9543986ce9bd9c4011 |
|
MD5 | ce35e46e3ca6d9c197dd473f39d6b7dc |
|
BLAKE2b-256 | 36bdd2e126286880943a5983f784c40126046c2c8165a5a5f1eae6c5f2d5ed10 |
Hashes for test_poetry_package_name_nr-0.1.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 7aef9b7e5e2915bb157d2ea6f8829a2f1d17dfb47e6d4d868cbb16353956a550 |
|
MD5 | 7124801f6e333d6f01e1b9f967ffb6ab |
|
BLAKE2b-256 | 47b0f513d343081e379a6bb9bc2afd10f3ae037d93c64e6ff475c18f80066174 |