---
description: Definitive guidelines for using Poetry for Python dependency management, packaging, and project structure, emphasizing modern best practices as of 2025.
globs: **/*.py
---
# poetry Best Practices

Poetry is the de-facto standard for modern Python project management. This guide outlines our team's definitive best practices for leveraging Poetry's full power, ensuring reproducible builds, streamlined development, and robust packaging.

## 1. Project Initialization & Structure

Always start new projects with `poetry new` to establish a correct `pyproject.toml` and project structure.

### ✅ GOOD: Use `src` layout by default

Poetry 2.1+ defaults to the `src` layout, which prevents common import issues and improves package isolation.

```bash
# Initialize a new project with the recommended src layout
poetry new my-project
```

This creates:

```
my-project/
├── pyproject.toml
├── README.md
├── my_project/  # Your package source code lives here
│   └── __init__.py
└── tests/
    └── __init__.py
    └── test_my_project.py
```

## 2. `pyproject.toml` Configuration

`pyproject.toml` is your project's single source of truth.

### ✅ GOOD: Prioritize PEP 621 `[project]` section

Poetry 2.0+ fully supports PEP 621. Use the `[project]` section for standard metadata (name, version, description, authors, etc.). Only use `[tool.poetry]` for Poetry-specific features not covered by PEP 621.

**❌ BAD: Using deprecated `tool.poetry` fields**

```toml
# pyproject.toml
[tool.poetry]
name = "my-project" # Deprecated in favor of [project].name
version = "0.1.0"   # Deprecated in favor of [project].version
description = "A bad example"
authors = ["Bad Developer <bad@example.com>"]
```

**✅ GOOD: Using `[project]` for standard metadata**

```toml
# pyproject.toml
[project]
name = "my-project"
version = "0.1.0"
description = "A good example using PEP 621"
authors = [{name = "Good Developer", email = "good@example.com"}]
readme = "README.md"
requires-python = ">=3.9,<4.0" # Always specify Python version
license = { text = "MIT" }     # Or { file = "LICENSE" }
keywords = ["python", "poetry", "best-practices"]
classifiers = [
    "Programming Language :: Python :: 3",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
]

[tool.poetry]
# Only Poetry-specific configurations here, e.g., packages, scripts
packages = [{ include = "my_project" }] # Required for src layout
```

### ✅ GOOD: Explicitly declare `[build-system]`

Always define your build backend explicitly, even if using `poetry-core`. This ensures consistent builds and prepares for alternative backends.

```toml
# pyproject.toml
[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"
```

### ✅ GOOD: Use alternative build backends when appropriate

For projects with Rust extensions (`maturin`) or other specific needs, declare the alternative build backend.

```toml
# pyproject.toml
[build-system]
requires = ["maturin>=0.8.1,<0.9"]
build-backend = "maturin"
```

## 3. Dependency Management

### ✅ GOOD: Add dependencies with explicit constraints

Always use `poetry add` and specify PEP 440-compliant version constraints. Avoid unconstrained dependencies (`*`) to prevent unexpected breakage.

**❌ BAD: Unconstrained or overly broad dependencies**

```bash
poetry add requests # Adds requests with ^ constraint, but better to be explicit
poetry add my-lib@* # Never do this
```

**✅ GOOD: Explicit, well-defined dependency constraints**

```bash
poetry add requests@^2.30.0    # Caret (compatible with 2.30.0 but not 3.0.0)
poetry add rich@~13.0          # Tilde (compatible with 13.0.x but not 13.1.0)
poetry add pandas@">=2.0.0,<2.1.0" # Specific range
```

### ✅ GOOD: Group development dependencies

Isolate development tools (linters, formatters, test runners) into a `dev` group. This keeps production environments lean.

```bash
poetry add --group dev pytest black isort flake8 mypy
```

```toml
# pyproject.toml
[project]
# ...

[tool.poetry.group.dev.dependencies]
pytest = "^7.0"
black = "^23.0"
isort = "^5.0"
flake8 = "^6.0"
mypy = "^1.0"
```

### ✅ GOOD: Commit `poetry.lock`

Always commit `poetry.lock` to ensure reproducible environments across all development, CI/CD, and production systems.

```bash
git add pyproject.toml poetry.lock
git commit -m "feat: Add initial project setup"
```

**❌ BAD: Manually editing `poetry.lock`**

Never manually edit `poetry.lock`. It's generated by Poetry and should only be updated via `poetry add`, `poetry remove`, or `poetry update`.

## 4. Virtual Environments & Execution

### ✅ GOOD: Use `pipx` for Poetry installation

Install Poetry itself in an isolated environment using `pipx` to avoid conflicts with project dependencies.

```bash
pipx install poetry
pipx upgrade poetry # To update Poetry
```

### ✅ GOOD: Activate environments with `poetry env activate`

Use `poetry env activate` to get the shell command for activating the virtual environment. This is more robust than the deprecated `poetry shell`.

```bash
# In your shell, to activate for the current session:
eval $(poetry env activate)

# Or, if you need to specify a Python version:
poetry python install 3.11 # (Experimental)
poetry env use 3.11
eval $(poetry env activate)
```

### ✅ GOOD: Execute commands with `poetry run`

For one-off commands or scripts, use `poetry run` to execute them within the project's virtual environment without explicit activation.

```bash
poetry run pytest
poetry run black .
```

## 5. Code Quality & Automation

### ✅ GOOD: Integrate pre-commit hooks

Enforce code quality standards (formatting, linting, typing) on every commit using `pre-commit` hooks.

1.  **Install `pre-commit`**:
    ```bash
    poetry add --group dev pre-commit
    poetry run pre-commit install
    ```
2.  **Configure `.pre-commit-config.yaml`**:

    ```yaml
    # .pre-commit-config.yaml
    repos:
      - repo: https://github.com/pre-commit/pre-commit-hooks
        rev: v4.5.0
        hooks:
          - id: trailing-whitespace
          - id: end-of-file-fixer
          - id: check-yaml
          - id: check-added-large-files
      - repo: https://github.com/psf/black
        rev: 23.12.1
        hooks:
          - id: black
      - repo: https://github.com/PyCQA/isort
        rev: 5.13.2
        hooks:
          - id: isort
      - repo: https://github.com/PyCQA/flake8
        rev: 6.1.0
        hooks:
          - id: flake8
      - repo: https://github.com/pre-commit/mirrors-mypy
        rev: v1.8.0
        hooks:
          - id: mypy
            args: [--ignore-missing-imports]
    ```

## 6. Packaging & Publishing

### ✅ GOOD: Build packages with `poetry build`

Use `poetry build` to create `sdist` and `wheel` distributions. Ensure your `pyproject.toml`'s `[build-system]` is correctly configured.

```bash
poetry build
```

### ✅ GOOD: Publish to PyPI or private repositories

Use `poetry publish` to distribute your package. Configure credentials securely.

```bash
# Publish to PyPI (default)
poetry publish --build # Build and publish in one step

# Publish to a private repository
poetry publish -r my-private-repo --build
```

## 7. Performance & Troubleshooting

### ✅ GOOD: Update dependencies regularly

Run `poetry update` to refresh dependencies, but always review changelogs and run tests before committing.

```bash
poetry update
```

### ✅ GOOD: Clear cache for resolution issues

If dependency resolution is slow or problematic, clear Poetry's cache.

```bash
poetry cache clear --all pypi
```
