Skip to main content

Tracktolib

Python versions Latest PyPI version CI

Tracktor Swiss-knife Utility library.

Installation

uv add tracktolib

With specific extras:

uv add tracktolib[pg,api]

Modules

logs

Utility functions to initialize logging formatting and streams.

import logging
from tracktolib.logs import init_logging

logger = logging.getLogger()
formatter, stream_handler = init_logging(logger, 'json', version='0.0.1')

pg

Async PostgreSQL helpers using asyncpg.

uv add tracktolib[pg]
import asyncpg
from tracktolib.pg import insert_one, insert_many, insert_returning, update_one, SQLExpr

conn = await asyncpg.connect('postgresql://user:pass@localhost/db')

# Insert a single row
await insert_one(conn, 'public.test', {'foo': 'bar', 'value': 1})

# Insert multiple rows
await insert_many(conn, 'public.test', [
    {'foo': 'bar', 'value': 1},
    {'foo': 'baz', 'value': 2},
])

# Insert and return generated values
new_id = await insert_returning(conn, 'public.test', {'foo': 'bar'}, returning='id')

# Upsert (insert or update on conflict)
from tracktolib.pg import Conflict
await insert_one(conn, 'public.test', {'id': 1, 'foo': 'updated'}, on_conflict=Conflict(keys=['id']))

# Update a single row
await update_one(conn, 'public.test', {'foo': 'new', 'id': 1}, keys=['id'])

# Use raw SQL expressions as values (e.g. NOW(), gen_random_uuid())
await insert_one(conn, 'public.test', {'foo': 'bar', 'created_at': SQLExpr('NOW()')})
# → INSERT INTO public.test AS t (created_at, foo) VALUES (NOW(), $1)

await update_one(conn, 'public.test', {'updated_at': SQLExpr('NOW()'), 'id': 1}, keys=['id'])
# → UPDATE public.test t SET updated_at = NOW() WHERE id = $1

pg-sync

Sync PostgreSQL helpers using psycopg (v3).

uv add tracktolib[pg-sync]
from psycopg import connect
from tracktolib.pg_sync import insert_many, fetch_one, fetch_count, fetch_all

conn = connect('postgresql://user:pass@localhost/db')

data = [
    {'foo': 'bar', 'value': 1},
    {'foo': 'baz', 'value': 2}
]
insert_many(conn, 'public.test', data)

query = 'SELECT foo from public.test order by value asc'
value = fetch_one(conn, query, required=True)  # {'foo': 'bar'}, raises if not found

assert fetch_count(conn, 'public.test') == 2

query = 'SELECT * from public.test order by value asc'
assert fetch_all(conn, query) == data

s3

Async S3 helpers using aiobotocore.

uv add tracktolib[s3]

s3-minio

S3 helpers using minio.

uv add tracktolib[s3-minio]

s3-niquests

Async S3 helpers using niquests and botocore presigned URLs.

uv add tracktolib[s3-niquests]
from tracktolib.s3.niquests import S3Session

async with S3Session(
    endpoint_url='http://localhost:9000',
    access_key='...',
    secret_key='...',
    region='us-east-1',
) as s3:
    # Object operations
    await s3.put_object('bucket', 'path/file.txt', b'content')
    content = await s3.get_object('bucket', 'path/file.txt')
    await s3.delete_object('bucket', 'path/file.txt')

    # Streaming upload (multipart for large files)
    async def data_stream():
        yield b'chunk1'
        yield b'chunk2'
    await s3.file_upload('bucket', 'large-file.bin', data_stream())

    # Bucket policy management
    policy = {'Version': '2012-10-17', 'Statement': [...]}
    await s3.put_bucket_policy('bucket', policy)
    await s3.get_bucket_policy('bucket')
    await s3.delete_bucket_policy('bucket')

    # Empty a bucket (delete all objects)
    deleted_count = await s3.empty_bucket('bucket')

    # Sync a local directory to S3 (like aws s3 sync)
    from pathlib import Path
    result = await s3.sync_directory('bucket', Path('./local'), 'remote/prefix')
    print(f"Uploaded: {result['uploaded']}, Deleted: {result['deleted']}, Skipped: {result['skipped']}")

    # With delete flag to remove remote files not present locally
    result = await s3.sync_directory('bucket', Path('./local'), 'remote/prefix', delete=True)

api

FastAPI utilities using fastapi and pydantic.

uv add tracktolib[api]

gh

GitHub API helpers using niquests.

uv add tracktolib[gh]
from tracktolib.gh import GitHubClient

async with GitHubClient() as gh:  # Uses GITHUB_TOKEN env var
    # Issue comments
    comments = await gh.get_issue_comments("owner/repo", 123)
    await gh.create_issue_comment("owner/repo", 123, "Hello!")
    await gh.delete_comments_with_marker("owner/repo", 123, "<!-- bot -->")

    # Labels
    labels = await gh.get_issue_labels("owner/repo", 123)
    await gh.add_labels("owner/repo", 123, ["bug", "priority"])
    await gh.remove_label("owner/repo", 123, "wontfix")

    # Pull requests
    pr = await gh.get_pull_request("owner/repo", 42)
    diff = await gh.get_pull_request_diff("owner/repo", 42)
    await gh.create_pull_request_review(
        "owner/repo", 42,
        body="Found a few things",
        event="REQUEST_CHANGES",
        comments=[{"path": "app/main.py", "line": 12, "side": "RIGHT", "body": "This can be None here"}],
    )

    # Permissions
    permission = await gh.get_collaborator_permission("owner/repo", "octocat")  # admin, write, read or none

    # Deployments
    deploys = await gh.get_deployments("owner/repo", environment="production")
    await gh.mark_deployment_inactive("owner/repo", "preview-123")

cf

Cloudflare DNS API helpers using niquests.

uv add tracktolib[cf]
from tracktolib.cf import CloudflareDNSClient

async with CloudflareDNSClient() as cf:  # Uses CLOUDFLARE_API_TOKEN and CLOUDFLARE_ZONE_ID env vars
    # Get a DNS record
    record = await cf.get_dns_record("app.example.com", "CNAME")

    # Create a DNS record
    record = await cf.create_dns_record(
        "app.example.com",
        "target.example.com",
        record_type="CNAME",
        ttl=60,
        proxied=True,
    )

    # Delete by ID or name
    await cf.delete_dns_record(record["id"])
    await cf.delete_dns_record_by_name("app.example.com", "CNAME")

    # Check existence
    exists = await cf.dns_record_exists("app.example.com")

tests

Testing utilities using deepdiff.

uv add tracktolib[tests]

Metadata

Release files for tracktolib 1.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tracktolib 1.1.1
File Size Uploaded
tracktolib-1.1.1.tar.gz 36.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tracktolib 1.1.1
File Interpreter ABI Platform
tracktolib-1.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 80.8 kB

Release files / tracktolib-1.1.1.tar.gz

Download URL tracktolib-1.1.1.tar.gz
Size 36.9 kB
Tags Source
SHA-256 checksum
How to use checksums
cec83a013c4393aae5599c59c8f03a6cb405215fe2df7e68b23150d14f998c10
BLAKE2b-256 checksum
How to use checksums
949c9a9959a3a42a0e00f7a1dd2a782e788b617cc6bb9a32994ab6845a13ccab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / tracktolib-1.1.1-py3-none-any.whl

Download URL tracktolib-1.1.1-py3-none-any.whl
Size 43.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b5378b366ef198156d03b1e407d5bea05bb4281938c4156f7969be248fd19d0b
BLAKE2b-256 checksum
How to use checksums
371d3b274ddfafb50bccadbcb7db4ee1ed54e0a46f0e3266409bf738853a3263
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

1.1.1 This release

2 release files

1.1.0

2 release files

0.74.0

2 release files

0.73.0

2 release files

0.72.0

2 release files

0.68.0

2 release files

0.67.0

2 release files

0.66.3

2 release files

0.66.2

2 release files

0.66.1

2 release files

0.65.1

2 release files

0.65.0

2 release files

0.64.0

2 release files

0.63.0

2 release files

0.62.1

2 release files

0.62.0

2 release files

0.61.2

2 release files

0.61.1

2 release files

0.61.0

2 release files

0.59.0

2 release files

0.58.1

2 release files

0.58.0

2 release files

0.57.0

2 release files

0.55.0

2 release files

0.54.0

2 release files

0.53.1

2 release files

0.53.0

2 release files

0.52.0

2 release files

0.51.1

2 release files

0.51.0

2 release files

0.50.0

2 release files

0.49.1

2 release files

0.49.0

2 release files

0.48.1

2 release files

0.48.0

2 release files

0.47.2

2 release files

0.47.1

2 release files

0.47.0

2 release files

0.46.2

2 release files

0.46.1

2 release files

0.46.0

2 release files

0.45.0

2 release files

0.44.1

2 release files

0.44.0

2 release files

0.43.2

2 release files

0.43.1

2 release files

0.43.0

2 release files

0.42.1

2 release files

0.42.0

2 release files

0.39.1

2 release files

0.39.0

2 release files

0.38.1

2 release files

0.34.0

2 release files

0.33.0

2 release files

0.32.0

2 release files

0.29.1

2 release files

0.29.0

2 release files

0.28.3

2 release files

0.23.1

2 release files

0.23.0

2 release files

0.22.0

2 release files

0.19.0

2 release files

0.18.0

2 release files

0.17.0

2 release files

0.16.2

2 release files

0.16.1

2 release files

0.16.0

2 release files

0.14.0

2 release files

0.13.2

2 release files

0.13.1

2 release files

0.13.0

2 release files

0.12.0

2 release files

0.11.2

2 release files

0.11.1

2 release files

0.11.0

2 release files

0.10.1

2 release files

0.10.0

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page