Skip to content

Commit 177a42e

Browse files
committed
Initial Instapaper API v2 Python SDK (0.1.0)
Signed-off-by: Brian Donohue <brian@team.instapaper.com>
0 parents  commit 177a42e

28 files changed

Lines changed: 3075 additions & 0 deletions

‎.github/workflows/ci.yml‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
strategy:
12+
fail-fast: false
13+
matrix:
14+
python-version: ["3.10", "3.11", "3.12", "3.13"]
15+
steps:
16+
- uses: actions/checkout@v4
17+
- uses: actions/setup-python@v5
18+
with:
19+
python-version: ${{ matrix.python-version }}
20+
cache: pip
21+
- name: Install
22+
run: pip install -e ".[dev]"
23+
- name: Lint
24+
run: |
25+
ruff check .
26+
ruff format --check .
27+
- name: Type check
28+
run: mypy instapaper tests examples
29+
- name: Test
30+
run: pytest --cov=instapaper --cov-report=term-missing
31+
32+
build:
33+
runs-on: ubuntu-latest
34+
steps:
35+
- uses: actions/checkout@v4
36+
- uses: actions/setup-python@v5
37+
with:
38+
python-version: "3.13"
39+
- name: Build
40+
run: |
41+
pip install build twine
42+
python -m build
43+
twine check dist/*

‎.github/workflows/release.yml‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags: ["v*"]
6+
7+
jobs:
8+
build:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
- uses: actions/setup-python@v5
13+
with:
14+
python-version: "3.13"
15+
- name: Check the tag matches the package version
16+
run: |
17+
version=$(python -c "import re, pathlib; print(re.search(r'__version__ = \"(.+?)\"', pathlib.Path('instapaper/__init__.py').read_text()).group(1))")
18+
if [ "v$version" != "$GITHUB_REF_NAME" ]; then
19+
echo "Tag $GITHUB_REF_NAME doesn't match __version__ $version"
20+
exit 1
21+
fi
22+
- name: Install
23+
run: pip install -e ".[dev]" build twine
24+
- name: Test
25+
run: pytest
26+
- name: Build
27+
run: |
28+
python -m build
29+
twine check dist/*
30+
- uses: actions/upload-artifact@v4
31+
with:
32+
name: dist
33+
path: dist/
34+
35+
publish:
36+
needs: build
37+
runs-on: ubuntu-latest
38+
environment: pypi
39+
permissions:
40+
id-token: write
41+
steps:
42+
- uses: actions/download-artifact@v4
43+
with:
44+
name: dist
45+
path: dist/
46+
- uses: pypa/gh-action-pypi-publish@release/v1

‎.gitignore‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
__pycache__/
2+
*.pyc
3+
*.swp
4+
*.swo
5+
*.egg-info/
6+
dist/
7+
build/
8+
venv/
9+
.venv/
10+
.pytest_cache/
11+
.mypy_cache/
12+
.ruff_cache/
13+
.coverage
14+
coverage.xml
15+
htmlcov/

‎BUILD.md‎

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Build Instructions
2+
3+
## Prerequisites
4+
5+
- Python 3.10 or later
6+
- pip
7+
8+
## Setup
9+
10+
Create a virtual environment and install the package with the development extras:
11+
12+
```sh
13+
python3 -m venv venv
14+
source venv/bin/activate
15+
pip install -e ".[dev]"
16+
```
17+
18+
For builds and releases, also install the build extras:
19+
20+
```sh
21+
pip install -e ".[build]"
22+
```
23+
24+
## Running tests
25+
26+
```sh
27+
pytest
28+
```
29+
30+
With coverage:
31+
32+
```sh
33+
pytest --cov=instapaper --cov-report=term-missing
34+
```
35+
36+
## Linting and formatting
37+
38+
```sh
39+
ruff check .
40+
ruff format --check .
41+
```
42+
43+
Use `ruff format .` to apply formatting.
44+
45+
## Type checking
46+
47+
```sh
48+
mypy instapaper tests examples
49+
```
50+
51+
## Building
52+
53+
```sh
54+
python -m build
55+
twine check dist/*
56+
```
57+
58+
This writes a source distribution and a wheel to `dist/`.
59+
60+
## Releasing
61+
62+
Releases are published to PyPI by GitHub Actions when a version tag is pushed.
63+
64+
1. Update `__version__` in `instapaper/__init__.py`.
65+
2. Move the changes under a dated version heading in `CHANGELOG.md`.
66+
3. Commit, then tag and push:
67+
68+
```sh
69+
git tag v0.1.0
70+
git push origin main v0.1.0
71+
```
72+
73+
The `release` workflow checks that the tag matches `__version__`, builds the package, and publishes it.
74+
75+
### One-time PyPI setup
76+
77+
The release workflow uses PyPI trusted publishing, so no API token is stored in GitHub. Before the first release:
78+
79+
1. Sign in to PyPI with the Instapaper account.
80+
2. Go to **Your projects > Publishing** (for a project that doesn't exist yet, use **Add a new pending publisher**).
81+
3. Add a GitHub publisher with:
82+
- PyPI project name: `instapaper-api`
83+
- Owner: `Instapaper`
84+
- Repository: `instapaper-api-python`
85+
- Workflow: `release.yml`
86+
- Environment: `pypi`
87+
4. In the GitHub repository settings, create an environment named `pypi`. Adding required reviewers there makes each release wait for approval.
88+
89+
### Publishing manually
90+
91+
If you need to publish without the workflow:
92+
93+
```sh
94+
python -m build
95+
twine upload dist/*
96+
```

‎CHANGELOG.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [0.1.0] - Unreleased
9+
10+
### Added
11+
12+
- Client for the Instapaper API v2: bookmarks, folders, tags, highlights, and the current user.
13+
- Pagination with `bookmarks.iterate()` and change syncing with `bookmarks.changes()` and `bookmarks.sync()`.
14+
- Parsed article text with `bookmarks.parse()`, including Instaparser keys for non-personal use.
15+
- OAuth 2 helpers for building the authorization URL and exchanging a code for an access token.
16+
- Typed models and an exception hierarchy keyed on HTTP status.

‎CONTRIBUTING.md‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Contributing
2+
3+
Thanks for helping improve the Instapaper API Python library. Bug reports, fixes, and documentation improvements are all welcome.
4+
5+
## Reporting issues
6+
7+
Open a [GitHub issue](https://github.com/Instapaper/instapaper-api-python/issues). Search existing issues first, and include:
8+
9+
- The library version (`instapaper.__version__`) and your Python version
10+
- The method you called and the arguments you passed
11+
- What you expected to happen and what happened instead, including the full traceback
12+
13+
Leave access tokens, client secrets, and Instaparser keys out of issues, logs, and code samples.
14+
15+
For questions about the API itself, such as endpoints, rate limits, or application approval, see the [developer documentation](https://www.instapaper.com/developers/overview/getting-started) or email support@instapaper.com.
16+
17+
### Security issues
18+
19+
Don't open a public issue for a security vulnerability. Email support@instapaper.com instead.
20+
21+
## Making changes
22+
23+
1. Fork the repository and create a branch from `main`.
24+
2. Set up a development environment as described in [BUILD.md](BUILD.md).
25+
3. Make your change, with tests.
26+
4. Run the same checks CI runs:
27+
28+
```sh
29+
ruff check .
30+
ruff format --check .
31+
mypy instapaper tests examples
32+
pytest
33+
```
34+
35+
5. Open a pull request that explains what changed and why.
36+
37+
### Guidelines
38+
39+
- **No runtime dependencies.** The library uses only the Python standard library. Development tools belong in the `dev` extra in `pyproject.toml`.
40+
- **Python 3.10 and later.** CI runs on 3.10 through 3.13, so avoid features newer than 3.10.
41+
- **Everything is typed.** mypy runs with `disallow_untyped_defs`, including on tests and examples.
42+
- **Tests don't touch the network.** Use the `client` and `transport` fixtures from `tests/conftest.py`. `FakeTransport` records each request and returns responses you queue, so a test can assert on the method, URL, and body the client sent.
43+
- **Match the API.** Parameter names, defaults, and validation should follow the [API reference](https://www.instapaper.com/developers/overview/getting-started). This library aims to behave the same as the official TypeScript SDK, so a change to shared behavior may need a matching change there.
44+
- **Update the docs.** Add user-facing changes to the unreleased version at the top of [CHANGELOG.md](CHANGELOG.md), and update [README.md](README.md) when you add or change public API.
45+
46+
## License
47+
48+
By contributing, you agree that your contributions will be licensed under the [MIT License](LICENSE).

‎LICENSE‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Instant Paper, Inc.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)