forked from benda/packagehowto
added venv intro
This commit is contained in:
parent
6424ecaaf5
commit
84fe4b56a3
40
README.md
40
README.md
@ -5,6 +5,8 @@ to install them, how to upload them, how to maintain them, how to work
|
|||||||
with them. For details consult https://packaging.python.org, or
|
with them. For details consult https://packaging.python.org, or
|
||||||
https://py-pkgs.org, or other sites.
|
https://py-pkgs.org, or other sites.
|
||||||
|
|
||||||
|
Before you create a package please consider building a [virtual
|
||||||
|
environment](./venv.md) for your project.
|
||||||
|
|
||||||
## Why packages?
|
## Why packages?
|
||||||
|
|
||||||
@ -17,12 +19,14 @@ other scripts in the very same directory.
|
|||||||
|
|
||||||
For example, consider a module `addition.py` and a script `analyze.py`
|
For example, consider a module `addition.py` and a script `analyze.py`
|
||||||
both in the same directory:
|
both in the same directory:
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
├── addition.py
|
├── addition.py
|
||||||
└── analyze.py
|
└── analyze.py
|
||||||
```
|
```
|
||||||
|
|
||||||
In `addition.py` we define a function `add_two()`:
|
In `addition.py` we define a function `add_two()`:
|
||||||
|
|
||||||
```
|
```
|
||||||
def add_two(x):
|
def add_two(x):
|
||||||
return x + 2
|
return x + 2
|
||||||
@ -30,6 +34,7 @@ def add_two(x):
|
|||||||
|
|
||||||
We can use this function in `analyze.py` by importing it from
|
We can use this function in `analyze.py` by importing it from
|
||||||
`addition.py`:
|
`addition.py`:
|
||||||
|
|
||||||
```
|
```
|
||||||
from addition import add_two
|
from addition import add_two
|
||||||
|
|
||||||
@ -42,7 +47,6 @@ directory (or if modules are in sub-directories). To make modules
|
|||||||
available in other places of your file system or even for other
|
available in other places of your file system or even for other
|
||||||
people, you need to turn the modules into packages.
|
people, you need to turn the modules into packages.
|
||||||
|
|
||||||
|
|
||||||
## Minimal package
|
## Minimal package
|
||||||
|
|
||||||
First we make a project directory for our new package. Usually the
|
First we make a project directory for our new package. Usually the
|
||||||
@ -87,16 +91,20 @@ on your machine and import it from wherever you want.
|
|||||||
|
|
||||||
To install the project, go to the project root, here `packagehowto/` and run
|
To install the project, go to the project root, here `packagehowto/` and run
|
||||||
from your shell
|
from your shell
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pip3 install .
|
pip3 install .
|
||||||
```
|
```
|
||||||
|
|
||||||
This installs the package of the current project folder `.` somewhere
|
This installs the package of the current project folder `.` somewhere
|
||||||
in your home directory. From anywhere in your home file system you now
|
in your home directory. From anywhere in your home file system you now
|
||||||
can import this package. The import line of our `analyze.py` file
|
can import this package. The import line of our `analyze.py` file
|
||||||
needs to look like this:
|
needs to look like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
from numerix.addition import add_two
|
from numerix.addition import add_two
|
||||||
```
|
```
|
||||||
|
|
||||||
From the addition module of the numerix package the function add_two
|
From the addition module of the numerix package the function add_two
|
||||||
is imported.
|
is imported.
|
||||||
|
|
||||||
@ -105,13 +113,14 @@ package, for exmple when you add a new function or when you just fix
|
|||||||
some package code. This is tedious, and that is why there is a `-e`
|
some package code. This is tedious, and that is why there is a `-e`
|
||||||
option ("editable install") for `pip install`. So install your package
|
option ("editable install") for `pip install`. So install your package
|
||||||
with
|
with
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pip3 install -e .
|
pip3 install -e .
|
||||||
```
|
```
|
||||||
|
|
||||||
Then all future changes on your package are immediately available
|
Then all future changes on your package are immediately available
|
||||||
without the need to reinstall the package again.
|
without the need to reinstall the package again.
|
||||||
|
|
||||||
|
|
||||||
## The `__init__.py` file
|
## The `__init__.py` file
|
||||||
|
|
||||||
This file is your package. In fact, you could write a package that
|
This file is your package. In fact, you could write a package that
|
||||||
@ -119,11 +128,14 @@ only has an `__init__.py` file in the package directory. All code you
|
|||||||
want to make available in you package could be placed in the
|
want to make available in you package could be placed in the
|
||||||
`__init__.py` file. For example, if you define an `add_four()` function
|
`__init__.py` file. For example, if you define an `add_four()` function
|
||||||
in `__init__.py` like this:
|
in `__init__.py` like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
def add_four(x):
|
def add_four(x):
|
||||||
return x + 4
|
return x + 4
|
||||||
```
|
```
|
||||||
|
|
||||||
then it can be imported directly from the package:
|
then it can be imported directly from the package:
|
||||||
|
|
||||||
```
|
```
|
||||||
from numerix import add_four
|
from numerix import add_four
|
||||||
```
|
```
|
||||||
@ -132,34 +144,41 @@ Alternatively, you can define all your functions in module files, like
|
|||||||
we did with the function `add_two()` in the `addition.py` module. In
|
we did with the function `add_two()` in the `addition.py` module. In
|
||||||
the `__init__.py` file you can import this function and this way make
|
the `__init__.py` file you can import this function and this way make
|
||||||
it available directly from the package. With this line in `__init__.py`
|
it available directly from the package. With this line in `__init__.py`
|
||||||
|
|
||||||
```
|
```
|
||||||
from .addition import add_two
|
from .addition import add_two
|
||||||
```
|
```
|
||||||
|
|
||||||
(note the `.` in front of `addition` - this makes it a relative
|
(note the `.` in front of `addition` - this makes it a relative
|
||||||
import) you can import `add_two()` without specifying the module:
|
import) you can import `add_two()` without specifying the module:
|
||||||
|
|
||||||
```
|
```
|
||||||
from numerix import add_two
|
from numerix import add_two
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Multiple modules within a package
|
## Multiple modules within a package
|
||||||
|
|
||||||
You can have as many modules as you need in your package. Let's add a
|
You can have as many modules as you need in your package. Let's add a
|
||||||
module `numbers.py` to the `numerix` package
|
module `numbers.py` to the `numerix` package
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
└── numerix
|
└── numerix
|
||||||
├── __init__.py
|
├── __init__.py
|
||||||
├── addition.py
|
├── addition.py
|
||||||
└── numbers.py
|
└── numbers.py
|
||||||
```
|
```
|
||||||
|
|
||||||
with the following content:
|
with the following content:
|
||||||
|
|
||||||
```
|
```
|
||||||
from .addition import add_two
|
from .addition import add_two
|
||||||
|
|
||||||
def three():
|
def three():
|
||||||
return add_two(1)
|
return add_two(1)
|
||||||
```
|
```
|
||||||
|
|
||||||
This makes a function `three()` available that can be imported via
|
This makes a function `three()` available that can be imported via
|
||||||
|
|
||||||
```
|
```
|
||||||
from numerix.numbers import three
|
from numerix.numbers import three
|
||||||
```
|
```
|
||||||
@ -171,7 +190,6 @@ Note, that the `numbers.py` module imports a function from the
|
|||||||
And of course you can also import this function in `__init__.py` so
|
And of course you can also import this function in `__init__.py` so
|
||||||
that it can be imported from the package directly.
|
that it can be imported from the package directly.
|
||||||
|
|
||||||
|
|
||||||
## Versioning
|
## Versioning
|
||||||
|
|
||||||
Your package needs a version number (for details see below). But how to
|
Your package needs a version number (for details see below). But how to
|
||||||
@ -187,7 +205,6 @@ by a dot. These numbers are incremented in the following way:
|
|||||||
- `minor`, when functionality is added in a backwards-compatible manner, and
|
- `minor`, when functionality is added in a backwards-compatible manner, and
|
||||||
- `patch`, for backwards-compatible bug fixes.
|
- `patch`, for backwards-compatible bug fixes.
|
||||||
|
|
||||||
|
|
||||||
## Package version
|
## Package version
|
||||||
|
|
||||||
For uploading your package to [PyPi](https://pypi.org/) you need to
|
For uploading your package to [PyPi](https://pypi.org/) you need to
|
||||||
@ -233,7 +250,7 @@ file? It would be a very bad idea to write it there directly and
|
|||||||
update it whenever you change it in the `__init__.py` file. The
|
update it whenever you change it in the `__init__.py` file. The
|
||||||
version number should be set only in one place.
|
version number should be set only in one place.
|
||||||
|
|
||||||
This is possible, of course. You need to specify the `version` field
|
This is possible, of course. You need to specify the `version` field
|
||||||
as `dynamic` in the `[project]` section. This tells the package tool
|
as `dynamic` in the `[project]` section. This tells the package tool
|
||||||
that the version will be set dynamically by some code. There are
|
that the version will be set dynamically by some code. There are
|
||||||
several options to do so. The simplest one is to add a
|
several options to do so. The simplest one is to add a
|
||||||
@ -272,18 +289,21 @@ python3 -m twine upload dist/*
|
|||||||
|
|
||||||
token
|
token
|
||||||
|
|
||||||
|
## Using `poetry`
|
||||||
|
|
||||||
|
You have now seen how you can do everything "from scratch". This is of course
|
||||||
|
good to know. But there are tools to significantly simplify package creation
|
||||||
|
and management, such as [`poetry`](https://python-poetry.org/). Its worth
|
||||||
|
checking it out. Heres a nice demo: https://python-poetry.org/docs/basic-usage/
|
||||||
|
|
||||||
## Unit tests
|
## Unit tests
|
||||||
|
|
||||||
pytest
|
pytest
|
||||||
|
|
||||||
coverage
|
coverage
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pytest -v --cov-report html --cov numerix tests/
|
pytest -v --cov-report html --cov numerix tests/
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
111
venv.md
Normal file
111
venv.md
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
# Why and how to use virtual environments?
|
||||||
|
|
||||||
|
## What is a virtual environment?
|
||||||
|
|
||||||
|
- A venv is **seperated** from your system packages / software, such as
|
||||||
|
firefox, thunderbird, etc.
|
||||||
|
- Packages that are installed cannot talk to system packages and vice versa ->
|
||||||
|
you know what you use because you have to explicitly install **all** packages
|
||||||
|
that your project requires.
|
||||||
|
- They can be build and destroyed easily and quickly to test different versions
|
||||||
|
of packages (or even python itself!).
|
||||||
|
|
||||||
|
## How to build a `venv`?
|
||||||
|
|
||||||
|
- PEP405 (=python style guide) virtual environments should be named "venv" or ".venv".
|
||||||
|
|
||||||
|
To create one, run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python -m venv <name> # e.g. venv or .venv
|
||||||
|
```
|
||||||
|
|
||||||
|
You should now see a new directory in your current working directory called "venv" or ".venv".
|
||||||
|
|
||||||
|
To activate it, run this on linux or macos:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
source <name>/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
On windows, you can run
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./<name>/Scripts/Activate.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
You should now see an indicator in your shell prompt such as:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
(venv) user@host:~/path/to/your/project
|
||||||
|
```
|
||||||
|
|
||||||
|
It is active as long as you do not explicitly deactivate it or close the shell.
|
||||||
|
You can now `pip install <package>` as you normally would.
|
||||||
|
To check which python version it uses, you can run this on linux/macos:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
which python3 && which pip
|
||||||
|
```
|
||||||
|
|
||||||
|
Both paths should point to your virtual environment in the project directory.
|
||||||
|
|
||||||
|
To deactivate, simply run on linux/macos/windows:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
deactivate
|
||||||
|
```
|
||||||
|
|
||||||
|
What we have now seen all comes with python but there are tools that make this
|
||||||
|
more convenient: What if you also want to try another pyhton version? Or use
|
||||||
|
the same venv in multiple projects? For this, there is `pyenv`:
|
||||||
|
|
||||||
|
### `venv` pitfalls
|
||||||
|
|
||||||
|
- You cannot move or rename **any** directory inside the path to your project
|
||||||
|
directory or the `venv` breaks. You can rename a folder _inside_ the project
|
||||||
|
directory as long as you dont change the path to the venv.
|
||||||
|
- You cannot use the venv for multiple projects
|
||||||
|
|
||||||
|
`pyenv` fixes this:
|
||||||
|
|
||||||
|
## Using [`pyenv`](https://github.com/pyenv/pyenv.git) / [`pyenv-virtualenv`](https://github.com/pyenv/pyenv-virtualenv.git)
|
||||||
|
|
||||||
|
Use pyenv for managing python versions and pyenv-virtualenv for a slightly
|
||||||
|
easier way of interacting with venvs.
|
||||||
|
|
||||||
|
To download another python version (without destroying your system), simply run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pyenv install python3.12
|
||||||
|
```
|
||||||
|
|
||||||
|
To see which versions are installed, you can run
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pyenv versions
|
||||||
|
```
|
||||||
|
|
||||||
|
To set this version as the default in a specific directory (and all its subdirectories), run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pyenv local 3.12
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can either use the tutorial above to create a standard venv or use `pyenv-virtualenv` like this:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pyenv virtualenv 3.12 <name>
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can activate it by `pyenv activate <name>` or use it as the standard in the current and subdirectories with
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pyenv local <name>
|
||||||
|
```
|
||||||
|
|
||||||
|
The convenient thing is, that this venv will always be automatically activated
|
||||||
|
when you enter the directory. If you do this in multiple directories, you can
|
||||||
|
have the same venv for multiple projects.
|
||||||
|
|
||||||
|
There are some alternatives to `pyenv` such as
|
Loading…
Reference in New Issue
Block a user