Getting Started with Python Poetry
Tags: python, poetry
Published: 2020-08-03 01:02:09

The [Poetry] packaging and dependency management tool is the somewhat-new hotness for creating and maintaining a Python project.

This article is an introductory tutorial (also known as "I am writing this down so I learn it better now and have somewhere to look when I forget it later").

## Install Poetry

To install Poetry on Windows, launch a powershell window, then:

```powershell
(iwr https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py -UseBasicParsing).Content | python
```

Then make sure `%USERPROFILE%\.poetry\bin` is on your `PATH` variable. Search your start menu for "environment" and you should find somthing like "Edit environment variables for your account."

To do the same on Mac, Linux, etc.:

```
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
```

Then make sure `$HOME/.poetry/bin` is in your `PATH` variable. Most likely, Poetry has already modified your `~/.profile` to `source $HOME/.poetry/env`, but you can double-check this or configure the `PATH` in the way you choose.

The [official Poetry docs](https://python-poetry.org/docs/#installation) provide further detail.

## Initialize the project with `poetry`

Poetry can create the directory and initial structure.

```bash
poetry new --name greet --src pygreet
```

This will create a directory `pygreet` with the package `greet` in a `src` directory.

While a variety of ways to structure your distribution package exist, I like the approach of [placing Python code in a `src` directory](https://blog.ionelmc.ro/2014/05/25/python-packaging/). Poetry supports this with the `--src` flag, as shown above.

## Add code

There are several decisions to be made about structuring code, and you are welcome to read [my article on Python modules and packages](https://dev.to/bowmanjd/python-module-vs-package-4m8e).

For now, I am simply adding another module to the `greet` package, and will call it `location.py`. It contains the following code:

```python
"""Send greetings."""

import arrow

def greet(tz):
    """Greet a location."""
    now = arrow.now(tz)
    friendly_time = now.format("h:mm a")
    location = tz.split("/")[-1].replace("_"," ") 
    return f"Hello, {location}! The time is {friendly_time}."
```

So now the project directory looks like this:

```bash
pygreet/
├── README.rst
├── pyproject.toml
├── src
│   └── greet
│       ├── __init__.py
│       └── location.py
└── tests
    ├── __init__.py
    └── test_greet.py
```

## Adding dependencies

That `import arrow` in our code requires the date library [arrow](https://pypi.org/project/arrow/) to be installed as a dependency. To do so, use `poetry add`:

```bash
poetry add arrow
```

If you need to hunt for more packages, Poetry has a convenient search utility in `poetry search`, so that `poetry search arrow` will return a list of all [PyPI] packages with "arrow" in the name. 

## Install package and dependencies

To install the package in developer mode, along with its dependencies:

```bash
poetry install
```

## Using the virtual environment

The `poetry run` command will execute the command in the virtual environment. For instance, try the following:

```python
[pygreet]$ poetry run python
>>> from greet.location import greet
>>> greet("Asia/Shanghai")
'Hello, Shanghai! The time is 4:05 am.'
>>>
```

Executing a shell in the virtual environment (i.e. _activating_ it) is also possible with `poetry shell` after which we can execute python or other commands.

```
[pygreet]$ poetry shell
Spawning shell within
~/.cache/pypoetry/virtualenvs/pygreet-abcd1234-py3.8
[pygreet]$ . ~/.cache/pypoetry/virtualenvs/pygreet-abcd1234-py3.8/bin/activate
(pygreet-abcd1234-py3.8) [pygreet]$ python
>>> from greet.location import greet
>>>
```

And that is a great way to test the module we wrote... Uh, no. No, that's not it at all.

## Write tests and use [pytest] with Poetry

Tests can be written while developing the project. Poetry has already partly scaffolded the initial tests by creating a `tests` directory and an initial `test_greet.py` file. Edit that file and add an additional test. Something like

```python
from greet.location import greet

def test_greet():
    result = greet("America/New_York")
    assert "New York!" in result
```

Then either run `poetry run pytest` or, if already in the shell launched by `poetry shell`, just running `pytest` will do.

If the tests passed, so have we. Nicely done.

Feel free to take a look at a [follow-up article on building a command line tool with Poetry][PoetryCLI].



[PoetryCLI]: https://dev.to/bowmanjd/build-command-line-tools-with-python-poetry-4mnc
[Poetry]: https://python-poetry.org/
[PyPI]: https://pypi.org/
[pytest]: https://docs.pytest.org/