https://github.com/posit-dev/picopip
Embed-friendly Python env/package scanner with zero deps
https://github.com/posit-dev/picopip
Last synced: 5 days ago
JSON representation
Embed-friendly Python env/package scanner with zero deps
- Host: GitHub
- URL: https://github.com/posit-dev/picopip
- Owner: posit-dev
- License: mit
- Created: 2025-05-19T14:13:19.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2026-06-01T13:55:29.000Z (21 days ago)
- Last Synced: 2026-06-01T15:26:39.620Z (21 days ago)
- Language: Python
- Size: 48.8 KB
- Stars: 2
- Watchers: 2
- Forks: 1
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# picopip
Embed-friendly Python env/package scanner with zero deps.
This is primarily designed so that you can take `src/picopip.py`
and copy it into your own project to use it without having
to install dependencies.
## Usage
### Get list of installed packages
You can use `get_packages_from_env` to list all installed packages in a Python virtual environment. Here's how to do it using Python's built-in `venv` module:
```python
>>> from picopip import get_packages_from_env
>>>
>>> pkgs = get_packages_from_env(venvdir)
>>> print(pkgs)
[('certifi', '2025.4.26'), ('charset-normalizer', '3.4.2'), ('idna', '3.10'),
('pip', '21.2.4'), ('requests', '2.32.3'), ('setuptools', '58.0.4'),
('urllib3', '2.4.0')]
```
- `get_packages_from_env()` returns a list of `(name, version)` tuples for all installed packages in the given virtual environment.
- You can use any venv path, and the function will find all packages, including those installed via pip.
### Scan a target directory (`pip install --target`)
By default `get_packages_from_env` expects a full virtual environment layout
(`lib/pythonX.Y/site-packages`, `.pth` expansion, system and `PYTHONPATH`
discovery). If you instead installed packages into a flat directory with
`pip install --target `, pass `path_as_target=True` to scan that directory
directly.
```sh
pip install --target ./vendor requests
```
```python
>>> from picopip import get_packages_from_env
>>>
>>> pkgs = get_packages_from_env("./vendor", path_as_target=True)
>>> print(pkgs)
[('certifi', '2025.4.26'), ('charset-normalizer', '3.4.2'), ('idna', '3.10'),
('requests', '2.32.3'), ('urllib3', '2.4.0')]
```
With `path_as_target=True`:
- `venv_path` is treated as the site-packages directory itself, so no
`lib/pythonX.Y/site-packages` structure is required.
- `.pth` files are not expanded and `PYTHONPATH` is not read.
- `ignore_system_packages` is ignored (system packages are never scanned).
### Get version of a package
If you only need the version of a specific package, you can get it
using `get_package_version_from_env`
```python
>>> from picopip import get_package_version_from_env
>>>
>>> version = get_package_version_from_env(venvdir, "pip")
>>> print(version)
'21.2.4'
```
### Parse a version string
`parse_version` normalizes a version into a tuple that follows the same ordering
rules used by `pip`/`packaging`. The first element is the release components,
the second is an offset encoding pre/dev/post markers so you can compare the
tuples with standard operators.
```python
>>> from picopip import parse_version
>>>
>>> parse_version("1.13.5")
((1, 13, 5), 0)
>>> parse_version("1.13.5a1") < parse_version("1.13.5")
True
>>> parse_version("1.13.5.post2") > parse_version("1.13.5")
True
```
### Check a version against a constraint
`parse_constraints` parses a requirement spec like `"aiokafka >= 0.8, < 1.0"`
into the package name and a list of `(comparator, parsed_version)` pairs.
Apply every pair to a parsed version to decide whether it satisfies the spec.
```python
>>> from picopip import parse_constraints, parse_version
>>>
>>> name, constraints = parse_constraints("aiokafka >= 0.8, < 1.0")
>>> name
'aiokafka'
>>> v = parse_version("0.9.1")
>>> all(op(v, cv) for op, cv in constraints)
True
>>> v = parse_version("1.0.0")
>>> all(op(v, cv) for op, cv in constraints)
False
```
The name is `None` for bare specs (`">= 0.8, < 1.0"`).
## Releasing
Releases are cut by pushing a `N.N.N` git tag. The `Release` workflow runs
lint and tests, then publishes a GitHub release with auto-generated notes.
1. Bump the `Version:` line in `src/picopip.py` on `main` and merge.
2. Tag the release commit with the matching version and push the tag:
```sh
git tag 0.5.1
git push origin 0.5.1
```
The workflow fails if the tag does not match `N.N.N` or does not match the
`Version:` line in `src/picopip.py`, so both must be kept in sync.