Skip to content

    Chapter 6, Environment

    Poetry: projects, dependencies and lockfile

    Poetry combines environment creation, dependency resolution, lock file, and publishing in a single command. I will show how it works today, in version 2.

    Code for this chapter: ambiente/cap06_poetry.py

    What Poetry solves

    The pip installs. Poetry manages: declares the project's dependencies in the pyproject.toml, resolves the complete tree of indirect dependencies, records the exact versions in poetry.lock and creates the virtual environment for you. Two colleagues running poetry install get exactly the same packages.

    A point of attention in version 2: Poetry started to respect the section [project] of the pyproject.toml, which is the community standard (PEP 621). The command poetry shell has been moved to a plugin, and the replacement is poetry env activate.

    Install

    The path I recommend is to install Poetry with pipx, so it stays isolated:

    Terminal
    pipx install poetry
    poetry --version
    

    The official installer also works:

    Terminal
    curl -sSL https://install.python-poetry.org | python3 -
    

    Create a project

    Poetry creates the structure and the pyproject.toml. In current versions, the code goes to the folder src/ by default, which is the layout I recommend in the packaging chapter. The option --src, which you still see in old tutorials, is being deprecated precisely because it has become the default behavior:

    Terminal
    poetry new meu-projeto
    cd meu-projeto
    

    To transform an existing folder into a Poetry project, use poetry init, which asks questions in the terminal. After poetry add requests and poetry add --group dev pytest, the file looks like this. Note that the development group goes to [dependency-groups], the same standard that uv uses, and that the author fields come with example values for you to change:

    exemplos/poetry/pyproject.toml
    [project]
    name = "meu-projeto"
    version = "0.1.0"
    description = ""
    authors = [
        {name = "Your Name",email = "you@example.com"}
    ]
    readme = "README.md"
    requires-python = ">=3.12"
    dependencies = [
        "requests (>=2.34.2,<3.0.0)"
    ]
    
    [tool.poetry]
    packages = [{include = "meu_projeto", from = "src"}]
    
    [build-system]
    requires = ["poetry-core>=2.0.0,<3.0.0"]
    build-backend = "poetry.core.masonry.api"
    
    [dependency-groups]
    dev = [
        "pytest (>=9.1.1,<10.0.0)"
    ]
    

    Everyday commands

    Terminal
    poetry add requests
    poetry add --group dev pytest
    poetry remove requests
    poetry install
    poetry sync
    poetry run python -m meu_projeto
    poetry run pytest
    poetry show --tree
    poetry update
    poetry lock
    

    The difference between install and sync: the install ensures that the necessary is installed. The sync makes the environment identical to the lockfile, removing what remains.

    Activate the environment

    You can always use poetry run, without activating anything. If you prefer to activate the environment in your session, in Poetry 2 the command prints the activation instruction, and you execute it. On macOS and Linux, eval $(poetry env activate) does both things at once:

    Terminal
    poetry env activate
    poetry env info --path
    poetry env use 3.13
    
    Output
    . /caminho/do/projeto/.venv/bin/activate
    

    The block above shows what the first command prints in a project with .venv inside the folder.

    For Poetry to create the .venv inside the project folder (which editors detect on their own), configure it once:

    Terminal
    poetry config virtualenvs.in-project true
    

    Read the pyproject.toml with Python

    Since Python 3.11 there is the module tomllib in the standard library, which reads TOML:

    ambiente/cap06_poetry.pylinhas 10 a 20
    import tomllib
    
    texto = """
    [project]
    name = "meu-projeto"
    dependencies = ["requests>=2.32"]
    """
    
    dados = tomllib.loads(texto)
    print(dados["project"]["name"])
    print(dados["project"]["dependencies"])
    
    Output
    meu-projeto
    ['requests>=2.32']
    

    Commit the poetry.lock

    In applications, the poetry.lock goes to Git. It ensures that your production server installs the same as you tested. In published libraries, the discussion is more subtle, and I will return to it in the packaging chapter.