# Packaging Pdm Datareader

> Package and publish the pdm-datareader library to PyPI using Poetry and the poetry-core build backend. Covers pyproject.toml metadata, version bumps, building sdist + wheel, validating with twine, verifying wheel contents, and publishing. Use when releasing a new version, building distributables, or troubleshooting packaging issues.

- Skill: `equinor/packaging-pdm-datareader` (Agent Skill)
- Install (CLI): `npx skillmds@latest add equinor/packaging-pdm-datareader`
- Raw SKILL.md: https://api.skillmd.com/api/skills/equinor/packaging-pdm-datareader/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: equinor (https://skillmd.com/u/equinor)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/equinor/packaging-pdm-datareader

---


# Packaging pdm-datareader

This project uses **Poetry** with the `poetry-core` build backend (see
`pyproject.toml`), not setuptools/hatchling/uv. Commands below assume Poetry is
installed and you are at the repo root.

## pyproject.toml Essentials

Metadata lives under `[tool.poetry]`:

```toml
[tool.poetry]
name = "pdm-datareader"
version = "2.5.5"
description = "A small python package to execute queries to PDM ..."
authors = ["PDM TEAM <AWL@equinor.com>"]
license = "MIT"
readme = "README.md"
packages = [{ include = "pdm_datareader" }]
repository = "https://github.com/equinor/pdm-datareader"

[tool.poetry.dependencies]
python = "^3.11"
msal-bearer = ">=1.3.0,<2.0.0"
pyodbc = "^5.1.0"
pandas = ">=2.2.2,<4.0.0"
sqlalchemy = "^2.0.36"
urllib3 = "^2.3.0"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
```

- `packages = [{ include = "pdm_datareader" }]` tells poetry-core which package
  directory to ship — keep it in sync with the actual package folder.
- Runtime deps go under `[tool.poetry.dependencies]`; dev-only tools
  (`pytest`, `black`) go under `[tool.poetry.group.dev.dependencies]` and are
  **not** shipped to users.

## Version Bump (do this every release)

Bump `version` under `[tool.poetry]` following SemVer
(`MAJOR.MINOR.MICRO` — see the versioning section in
`.github/copilot-instructions.md`). Either edit the field directly or use:

```bash
poetry version patch   # bug fix:     2.5.5 -> 2.5.6
poetry version minor   # new feature: 2.5.5 -> 2.6.0
poetry version major   # breaking:    2.5.5 -> 3.0.0
```

## Building

```bash
poetry build                 # creates sdist + wheel in dist/
```

## Validate Metadata

```bash
poetry run pip install --quiet twine   # if twine not already available
poetry run twine check dist/*          # validate long_description/metadata
```

## Verify the Built Artifact (a green build is not a correct wheel)

`poetry build` succeeding only means the backend ran — not that the wheel
contains your code. Always inspect wheel contents before publishing:

```bash
python -m zipfile -l dist/*.whl
```

Confirm the whole package ships — `pdm_datareader/__init__.py` **and**
`pdm_datareader/tools.py` — not just a top-level file. `twine check` validates
metadata, not contents, so it won't catch a missing module.

Then prove it from a clean install, outside the repo root (so `import` doesn't
pick up the source tree):

```bash
python -m venv /tmp/verify
/tmp/verify/bin/python -m pip install dist/*.whl
cd /tmp && /tmp/verify/bin/python -c "from pdm_datareader import tools; tools.query"
```

On Windows PowerShell:

```powershell
python -m venv $env:TEMP\verify
& "$env:TEMP\verify\Scripts\python.exe" -m pip install (Get-ChildItem dist\*.whl)
Push-Location $env:TEMP
& "$env:TEMP\verify\Scripts\python.exe" -c "from pdm_datareader import tools; tools.query"
Pop-Location
```

Assert on a real symbol (`tools.query`), never just that the top-level name
imports — a shadowing empty package can make a bare `import` succeed and prove
nothing.

## Publishing to PyPI

Test on TestPyPI first, then publish to production:

```bash
# TestPyPI (one-time config)
poetry config repositories.testpypi https://test.pypi.org/legacy/
poetry publish -r testpypi --build

# Production PyPI (uses a configured token)
poetry publish --build
```

Configure the token once with:

```bash
poetry config pypi-token.pypi <your-token>
```

Never hardcode or commit tokens. Prefer trusted publishing from CI over a
locally stored token where available.

## Dependency Best Practices

```toml
# DO: caret / bounded ranges (poetry style)
msal-bearer = ">=1.3.0,<2.0.0"
pyodbc = "^5.1.0"

# DON'T: exact pins that lock users to one release
pyodbc = "5.1.0"
```

Keep `python = "^3.11"` aligned with the supported versions in the project docs
(3.11–3.13).

## Checklist

```
Before Release:
- [ ] Version bumped under [tool.poetry] (SemVer)
- [ ] README.md updated for any new feature
- [ ] pytest passes and black formatting applied
- [ ] poetry build succeeds
- [ ] poetry run twine check dist/* passes
- [ ] python -m zipfile -l dist/*.whl shows pdm_datareader/__init__.py + tools.py
- [ ] Installed the wheel into a clean venv and imported tools.query from
      a directory outside the repo
- [ ] Tested on TestPyPI first

After Release:
- [ ] pip install pdm-datareader works
- [ ] from pdm_datareader import query works
- [ ] GitHub release / tag created
```

