Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
pull_request:
branches: [main]

permissions:
contents: read

jobs:
# Check version synchronization
version-check:
Expand All @@ -17,7 +20,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14"
python-version: "3.13"

- name: Check version sync
run: python scripts/sync-versions.py --check
Expand All @@ -32,15 +35,18 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14"
python-version: "3.13"

- name: Check feature parity
run: python scripts/check-feature-parity.py

# TypeScript CI
typescript:
name: TypeScript
name: TypeScript (Node ${{ matrix.node-version }})
runs-on: ubuntu-latest
strategy:
matrix:
node-version: ["18", "20", "22"]
defaults:
run:
working-directory: ts
Expand All @@ -50,7 +56,7 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
node-version: ${{ matrix.node-version }}
cache: "npm"
cache-dependency-path: ts/package-lock.json

Expand All @@ -64,15 +70,15 @@ jobs:
run: npm run build

- name: Run tests
run: npm test
run: npm run test:coverage

# Python CI
python:
name: Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.14"]
python-version: ["3.11", "3.12", "3.13"]
defaults:
run:
working-directory: python
Expand All @@ -96,7 +102,7 @@ jobs:
run: mypy numbersprotocol_capture --ignore-missing-imports

- name: Run tests
run: pytest -v
run: pytest -v --cov-fail-under=80

# All checks passed
ci-success:
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14"
python-version: "3.13"

- name: Extract version from tag
id: version
Expand Down Expand Up @@ -92,7 +92,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.14"
python-version: "3.13"

- name: Install build dependencies
run: |
Expand All @@ -119,7 +119,7 @@ jobs:
- uses: actions/checkout@v4

- name: Create Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
name: v${{ needs.validate.outputs.version }}
body: |
Expand Down
26 changes: 21 additions & 5 deletions python/numbersprotocol_capture/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ def __init__(
*,
testnet: bool = False,
base_url: str | None = None,
history_api_url: str | None = None,
merge_tree_api_url: str | None = None,
asset_search_api_url: str | None = None,
nft_search_api_url: str | None = None,
options: CaptureOptions | None = None,
):
"""
Expand All @@ -143,19 +147,31 @@ def __init__(
token: Authentication token for API access.
testnet: Use testnet environment (default: False).
base_url: Custom base URL (overrides testnet setting).
history_api_url: Override URL for the history API endpoint.
merge_tree_api_url: Override URL for the merge-tree API endpoint.
asset_search_api_url: Override URL for the asset search API endpoint.
nft_search_api_url: Override URL for the NFT search API endpoint.
options: CaptureOptions object (alternative to individual args).
"""
if options:
token = options.token
testnet = options.testnet
base_url = options.base_url
history_api_url = options.history_api_url
merge_tree_api_url = options.merge_tree_api_url
asset_search_api_url = options.asset_search_api_url
nft_search_api_url = options.nft_search_api_url

if not token:
raise ValidationError("token is required")

self._token = token
self._testnet = testnet
self._base_url = base_url or DEFAULT_BASE_URL
self._history_api_url = history_api_url or HISTORY_API_URL
self._merge_tree_api_url = merge_tree_api_url or MERGE_TREE_API_URL
self._asset_search_api_url = asset_search_api_url or ASSET_SEARCH_API_URL
self._nft_search_api_url = nft_search_api_url or NFT_SEARCH_API_URL
self._client = httpx.Client(timeout=30.0)

def __enter__(self) -> Capture:
Expand Down Expand Up @@ -444,7 +460,7 @@ def get_history(self, nid: str) -> list[Commit]:
if self._testnet:
params["testnet"] = "true"

url = f"{HISTORY_API_URL}?{urlencode(params)}"
url = f"{self._history_api_url}?{urlencode(params)}"

headers = {
"Content-Type": "application/json",
Expand Down Expand Up @@ -518,7 +534,7 @@ def get_asset_tree(self, nid: str) -> AssetTree:

try:
response = self._client.post(
MERGE_TREE_API_URL,
self._merge_tree_api_url,
headers=headers,
json=commit_data,
)
Expand Down Expand Up @@ -680,14 +696,14 @@ def search_asset(
try:
if files_data:
response = self._client.post(
ASSET_SEARCH_API_URL,
self._asset_search_api_url,
headers=headers,
data=form_data,
files=files_data,
)
else:
response = self._client.post(
ASSET_SEARCH_API_URL,
self._asset_search_api_url,
headers=headers,
data=form_data,
)
Expand Down Expand Up @@ -747,7 +763,7 @@ def search_nft(self, nid: str) -> NftSearchResult:

try:
response = self._client.post(
NFT_SEARCH_API_URL,
self._nft_search_api_url,
headers=headers,
json={"nid": nid},
)
Expand Down
22 changes: 21 additions & 1 deletion python/numbersprotocol_capture/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ class CaptureOptions:
base_url: str | None = None
"""Custom base URL (overrides testnet setting)."""

history_api_url: str | None = None
"""Override URL for the history API endpoint."""

merge_tree_api_url: str | None = None
"""Override URL for the merge-tree API endpoint."""

asset_search_api_url: str | None = None
"""Override URL for the asset search API endpoint."""

nft_search_api_url: str | None = None
"""Override URL for the NFT search API endpoint."""


@dataclass
class SignOptions:
Expand Down Expand Up @@ -115,7 +127,11 @@ class Commit:
"""Address that made this commit."""

timestamp: int
"""Unix timestamp of the commit."""
"""Unix timestamp of the commit in **seconds**.

Note: ``IntegrityProof.created_at`` uses milliseconds; this field uses seconds.
To convert to a ``datetime`` object: ``datetime.fromtimestamp(commit.timestamp)``.
"""

action: str
"""Description of the action."""
Expand Down Expand Up @@ -203,6 +219,10 @@ class IntegrityProof:
proof_hash: str
asset_mime_type: str
created_at: int
"""Creation timestamp in **milliseconds** since Unix epoch.

Note: ``Commit.timestamp`` from the API uses seconds; this field uses milliseconds.
"""


@dataclass
Expand Down
10 changes: 6 additions & 4 deletions python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ version = "0.2.1"
description = "Python SDK for Numbers Protocol Capture API"
readme = "README.md"
license = "MIT"
requires-python = ">=3.14"
requires-python = ">=3.11"
authors = [
{ name = "Numbers Protocol" }
]
Expand All @@ -26,7 +26,9 @@ classifiers = [
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development :: Libraries :: Python Modules",
"Typing :: Typed",
]
Expand Down Expand Up @@ -61,7 +63,7 @@ packages = ["numbersprotocol_capture"]

[tool.ruff]
line-length = 100
target-version = "py313" # Use py313 until ruff supports py314
target-version = "py311"

[tool.ruff.lint]
select = [
Expand All @@ -81,7 +83,7 @@ ignore = [
known-first-party = ["numbersprotocol_capture"]

[tool.mypy]
python_version = "3.14"
python_version = "3.11"
strict = true
warn_return_any = true
warn_unused_ignores = true
Expand Down
Loading