Metadata-Version: 2.1
Name: creosote
Version: 2.3.6
Summary: Identify unused dependencies and avoid a bloated virtual environment.
Project-URL: Source, https://github.com/fredrikaverpil/creosote
Project-URL: Tracker, https://github.com/fredrikaverpil/creosote/issues
Author-email: Fredrik Averpil <fredrik.averpil@gmail.com>
License-Expression: MIT
License-File: LICENSE
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=3.7
Requires-Dist: distlib<0.4,>=0.3.4
Requires-Dist: dotty-dict<1.4,>=1.3.1
Requires-Dist: loguru<0.7,>=0.6.0
Requires-Dist: pip-requirements-parser<33.1,>=32.0.1
Requires-Dist: toml<0.11,>=0.10.2
Provides-Extra: dev
Requires-Dist: creosote[lint,test,types]; extra == 'dev'
Provides-Extra: lint
Requires-Dist: black; extra == 'lint'
Requires-Dist: ruff; extra == 'lint'
Provides-Extra: test
Requires-Dist: pytest; extra == 'test'
Provides-Extra: types
Requires-Dist: loguru-mypy; extra == 'types'
Requires-Dist: mypy; extra == 'types'
Requires-Dist: types-toml; extra == 'types'
Description-Content-Type: text/markdown

# creosote

[![check](https://github.com/fredrikaverpil/creosote/actions/workflows/check.yml/badge.svg)](https://github.com/fredrikaverpil/creosote/actions/workflows/check.yml)
[![test](https://github.com/fredrikaverpil/creosote/actions/workflows/test.yml/badge.svg)](https://github.com/fredrikaverpil/creosote/actions/workflows/test.yml)

Identify unused dependencies and avoid a bloated virtual environment.

## :zap: Quickstart

Install creosote in separate virtual environment (using e.g. [`pipx`](https://github.com/pypa/pipx)):

```bash
pipx install creosote
```

Scan virtual environment for unused packages ([PEP-621](https://peps.python.org/pep-0621/) example below, but [Poetry](https://python-poetry.org/), [Pipenv](https://github.com/pypa/pipenv) and `requirements.txt` files are also supported, [see this table](#which-dependency-specification-toolingstandards-are-supported)):


```
$ creosote
Found packages in pyproject.toml: PyYAML, distlib, loguru, protobuf, toml
Oh no! 💥 💔 💥
Unused packages found: PyYAML, protobuf
```

And after having removed/uninstalled `PyYAML` and `protobuf`:

```
$ creosote
Found packages in pyproject.toml: distlib, loguru, toml
No unused packages found! ✨ 🍰 ✨
```

Get help:

```bash
creosote --help
```

## 🤔 How this works

Some data is required as input:

| Argument      | Default value          | Description                                                                                            |
| ------------- | ---------------------- | ------------------------------------------------------------------------------------------------------ |
| `--venv`      | `.venv`                | The path to your virtual environment.                                                                  |
| `--paths`     | `src`                  | The path to your source code, one or more files/folders.                                               |
| `--deps-file` | `pyproject.toml`       | The path to the file specifying your dependencies, like `pyproject.toml`, `requirements_*.txt \| .in`. |
| `--sections`  | `project.dependencies` | One or more toml sections to parse, e.g. `project.dependencies`.                                       |


The creosote tool will first scan the given python file(s) for all its imports. Then it fetches all package names (from the dependencies spec file). Finally, all imports are associated with their corresponding package name (requires the virtual environment for resolving). If a package does not have any imports associated, it will be considered to be unused.

### :triumph: Known limitations

- `importlib` imports are not detected by the AST parser (a great first contribution for anyone inclined 😄, reach out or start [here](https://github.com/fredrikaverpil/creosote/blob/72d4ce0a8a983725a704decce9083702aa2312cc/src/creosote/parsers.py#L138-L156)).

## 🥧 History and ambition

The idea of a package like this was born from having gotten security vulnerability
reports about production dependencies (shipped into production) which turned out to not not
even be in use.

The goal would be to be able to run this tool in CI, which will catch cases where the developer
forgets to remove unused dependencies. An example of such a case could be when doing refactorings.

Note: The creosote tool supports identifying both unused production dependencies and developer dependencies. It all depends on what you would like to achieve.

## :raised_eyebrow: FAQ

### Which dependency specification tooling/standards are supported?

| Tool/standard                                                                                                               |     Supported      | `--deps-file` value | Example `--sections` values                                                                                         |
| --------------------------------------------------------------------------------------------------------------------------- | :----------------: | ------------------- | ------------------------------------------------------------------------------------------------------------------- |
| [PEP-621](https://peps.python.org/pep-0621/) ⭐                                                                              | :white_check_mark: | `pyproject.toml`    | `project.dependencies`,<br>`project.optional-dependencies.<GROUP>`                                                  |
| [Poetry](https://python-poetry.org/)                                                                                        | :white_check_mark: | `pyproject.toml`    | `tool.poetry.dependencies`,<br>`tool.poetry.dev-dependencies` (legacy),<br>`tool.poetry.group.<GROUP>.dependencies` |
| [Pipenv](https://pipenv.pypa.io/en/latest/)                                                                                 | :white_check_mark: | `pyproject.toml`    | `packages`,<br>`dev-packages`                                                                                       |
| [PEP-508](https://peps.python.org/pep-0508/) (`requirements.txt`, [pip-tools](https://pip-tools.readthedocs.io/en/latest/)) | :white_check_mark: | `*.[txt\|in]`       | N/A                                                                                                                 |
| Legacy Setuptools (`setup.py`)                                                                                              |         ❌          |                     |                                                                                                                     |

#### 📔 Notes on [PEP-508](https://peps.python.org/pep-0508) (`requirements.txt`)

When using `requirements.txt` files to specify dependencies, there is no way to tell which part of `requirements.txt` specifies production vs developer dependencies. Therefore, you have to break your `requirements.txt` file into e.g. `requirements-prod.txt` and `requirements-dev.txt` and use any of them as input. When using [pip-tools](https://pip-tools.readthedocs.io/en/latest/), you likely want to point Creosote to scan your `*.in` file(s).

### Can I specify multiple toml sections?

Yes, you can specify a list of sections after the `--sections` argument. It all depends on what your setup looks like and what you set out to achieve.

### Can I run Creosote in a GitHub Action workflow?

Yes, please see the `action` job in [`.github/workflows/test.yml`](.github/workflows/test.yml) for a working example.

### Can I run Creosote with [pre-commit](https://pre-commit.com)?

Yes, you can either use Cresosote by specifying the exact, desired version (a very common workflow), or you can piggy-back on the Creosote already installed via e.g. `pipx`.

Examples:

```yaml
# .pre-commit-config.yaml

repos:
  - repo: https://github.com/fredrikaverpil/creosote
    rev: v2.3.6
    hooks:
      - id: creosote
        args:
          - "--venv=.venv"
          - "--paths=$MY_PROJECT_PATH"
          - "--deps-file=pyproject.toml"
          - "--sections=project.dependencies"
```

```yaml
# .pre-commit-config.yaml

repos:
  - repo: local
    hooks:
      - id: system
        name: creosote
        entry: creosote --venv .venv --paths src --deps-file pyproject.toml --sections project.dependencies
        pass_filenames: false
        files: \.(py|toml|txt|in|lock)$
        language: system
```

### What's with the name "creosote"?

This tool has borrowed its name from the [Monty Python scene about Mr. Creosote](https://www.youtube.com/watch?v=aczPDGC3f8U).

## :woman_scientist: Development/debugging info
### Install in-development builds

You can run in-development versions of Creosote. Examples below:

```bash
# Creosote build from main branch
$ pipx install --suffix=@main --force git+https://github.com/fredrikaverpil/creosote.git@main
$ creosote@main --venv .venv ...
$ pipx uninstall creosote@main

# Creosote build from PR #123
$ pipx install --suffix=@123 --force git+https://github.com/fredrikaverpil/creosote.git@refs/pull/123/head
$ creosote@123 --venv .venv ...
$ pipx uninstall creosote@123
```

### Releasing

1. Bump version in `src/creosote/__about__.py`.
2. GitHub Action will run automatically on creating [a release](https://github.com/fredrikaverpil/creosote/releases) and deploy the release onto PyPi.
