Publishing to PyPI
Step-by-step guide for publishing nse-data to TestPyPI and production PyPI.
Prerequisites
- Python 3.9 or higher
buildpackage for creating distributionstwinefor uploading to PyPI- Accounts on TestPyPI and PyPI
pip install build twine
1. Create Accounts
TestPyPI (for testing)
- Go to https://test.pypi.org/account/register/
- Create an account and verify your email
- Enable 2FA (required for uploads)
- Create an API token at https://test.pypi.org/manage/account/#api-tokens
- Scope: “Entire account” (for first upload) or project-specific (after first upload)
PyPI (production)
- Go to https://pypi.org/account/register/
- Create an account and verify your email
- Enable 2FA (required for uploads)
- Create an API token at https://pypi.org/manage/account/#api-tokens
Store tokens in ~/.pypirc
[distutils]
index-servers =
pypi
testpypi
[pypi]
username = __token__
password = pypi-XXXXXXXXXXXXXXXXXXXX
[testpypi]
repository = https://test.pypi.org/legacy/
username = __token__
password = pypi-XXXXXXXXXXXXXXXXXXXX
⚠️ Keep
~/.pypircsecure:chmod 600 ~/.pypirc
2. Version Bumping
Before building, update the version in pyproject.toml:
[project]
name = "nse-data"
version = "0.2.0" # ← bump this
Versioning scheme
| Change | Version bump | Example |
|---|---|---|
| Bug fix, minor patch | Patch | 0.1.0 → 0.1.1 |
| New feature, backward-compatible | Minor | 0.1.1 → 0.2.0 |
| Breaking change | Major | 0.2.0 → 1.0.0 |
Checklist before release
- Update version in
pyproject.toml - Update
CHANGELOG.mdwith release notes - Ensure all tests pass
- Commit changes:
git commit -m "release: v0.2.0" - Tag the release:
git tag v0.2.0
3. Building the Package
Clean any previous builds and create fresh distributions:
# Remove old build artifacts
rm -rf dist/ build/ src/*.egg-info
# Build source distribution and wheel
python -m build
This creates two files in dist/:
nse_data-0.2.0.tar.gz— source distributionnse_data-0.2.0-py3-none-any.whl— wheel (binary distribution)
Verify the build
# Check the distribution for common issues
twine check dist/*
Expected output:
Checking dist/nse_data-0.2.0.tar.gz: PASSED
Checking dist/nse_data-0.2.0-py3-none-any.whl: PASSED
4. Upload to TestPyPI
Upload to TestPyPI first to verify everything works:
twine upload --repository testpypi dist/*
Or with explicit URL:
twine upload --repository-url https://test.pypi.org/legacy/ dist/*
After upload, verify at: https://test.pypi.org/project/nse-archives/
5. Test Installation from TestPyPI
Install from TestPyPI in a fresh virtual environment:
# Create a test environment
python -m venv test-env
source test-env/bin/activate # Linux/macOS
# test-env\Scripts\activate # Windows
# Install from TestPyPI (with fallback to PyPI for dependencies)
pip install --index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
nse-data
Verify the installation
# Check version
nse-data --version
# Test CLI
nse-data reports --type sec_bhavdata --date 2026-04-17
# Test Python API
python -c "from nsedata.reports import download_report; print('Import OK')"
Cleanup
deactivate
rm -rf test-env
6. Publish to Production PyPI
Once TestPyPI testing passes, upload to production:
twine upload dist/*
After upload, verify at: https://pypi.org/project/nse-archives/
Post-release
# Push the tag
git push origin v0.2.0
# Create a GitHub release (optional)
gh release create v0.2.0 dist/* --title "v0.2.0" --notes "See CHANGELOG.md"
7. GitHub Actions — Automated Publishing
Create a workflow that automatically publishes to PyPI when a version tag is pushed.
Workflow file: .github/workflows/publish.yml
name: Publish to PyPI
on:
push:
tags:
- "v*"
permissions:
contents: read
jobs:
build:
name: Build distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install build tools
run: pip install build
- name: Build package
run: python -m build
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
publish-testpypi:
name: Publish to TestPyPI
needs: build
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/p/nse-data
permissions:
id-token: write # Required for trusted publishing
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
publish-pypi:
name: Publish to PyPI
needs: publish-testpypi
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/nse-data
permissions:
id-token: write # Required for trusted publishing
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
Setting up Trusted Publishing (recommended)
Instead of using API tokens, configure Trusted Publishing for keyless authentication:
- TestPyPI: Go to your project → Settings → Publishing → Add a new publisher
- Owner:
your-github-username - Repository:
nse-data - Workflow:
publish.yml - Environment:
testpypi
- Owner:
- PyPI: Same steps with environment
pypi
This eliminates the need to store API tokens as GitHub secrets.
8. Complete Release Workflow
# 1. Update version and changelog
vim pyproject.toml # bump version
vim CHANGELOG.md # add release notes
# 2. Commit and tag
git add pyproject.toml CHANGELOG.md
git commit -m "release: v0.2.0"
git tag v0.2.0
# 3. Push (triggers GitHub Actions)
git push origin main --tags
# 4. GitHub Actions will:
# - Build the package
# - Upload to TestPyPI
# - Upload to PyPI
Manual release (without GitHub Actions)
# 1. Clean and build
rm -rf dist/ build/
python -m build
# 2. Check
twine check dist/*
# 3. Upload to TestPyPI
twine upload --repository testpypi dist/*
# 4. Test installation
pip install --index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ nse-data
# 5. Upload to PyPI
twine upload dist/*
Troubleshooting
| Issue | Solution |
|---|---|
403 Forbidden on upload | Check API token scope; ensure 2FA is enabled |
File already exists | You cannot overwrite a version; bump the version number |
Invalid distribution | Run twine check dist/* to diagnose |
| Dependencies not found on TestPyPI | Use --extra-index-url https://pypi.org/simple/ |
ModuleNotFoundError after install | Verify packages config in pyproject.toml |