Skip to main content

CloseYourIt Python SDK

SDK Python ufficiale per inviare errori, log e metriche a CloseYourIt da applicazioni server-side. Il repository è privato e il package è in fase pre-alpha.

Stato

Lo scaffold, la configurazione fail-safe, lo scope isolato, lo scrubbing PII, i builder tipizzati, il transport asincrono, gli hook Python standard, la telemetria d'uso e le integrazioni Celery, WSGI, ASGI, Django, Flask, FastAPI, Starlette, HTTP e SQLAlchemy sono disponibili. Le altre integrazioni vengono aggiunte in TDD attraverso i ticket del progetto CloseYourIt CYPY.

I quattro client CloseYourIt — Ruby, JavaScript, Dart e questo — parlano lo stesso contratto wire, ma non fanno le stesse cose: qui alcune funzioni sono parziali e altre esistono soltanto negli altri SDK. Cosa c'è dove, con la versione minima e il file sorgente di ogni casella, sta in compatibility/sdk-feature-parity.md nel repository closeyourit-docs: è l'unico posto in cui quel confronto viene tenuto, ed è una rilevazione datata sulle versioni che dichiara in testa — non viene ricopiata qui.

Requisiti

  • Python 3.11 o successivo
  • mise per il runtime locale

Setup

mise install
mise exec -- python -m venv .venv
.venv/bin/python -m pip install --upgrade pip
.venv/bin/python -m pip install -e ".[dev]"

Il core WSGI/ASGI non aggiunge dipendenze runtime. Le integrazioni opzionali si installano per singolo stack:

python -m pip install "closeyourit[django]"
python -m pip install "closeyourit[flask]"
python -m pip install "closeyourit[fastapi]"
python -m pip install "closeyourit[starlette]"
python -m pip install "closeyourit[celery]"
python -m pip install "closeyourit[requests]"
python -m pip install "closeyourit[httpx]"
python -m pip install "closeyourit[sqlalchemy]"

Verifica

.venv/bin/ruff check .
.venv/bin/ruff format --check .
.venv/bin/mypy
.venv/bin/python -m pytest
.venv/bin/python -m pip check
.venv/bin/python -m build
.venv/bin/python -m twine check dist/*
actionlint

La suite applica coverage line e branch con soglia minima del 90%.

Configurazione

Le variabili seguenti appartengono alle applicazioni che consumano l'SDK e non sono necessarie per installare o sviluppare il package:

Variabile Descrizione
CLOSEYOURIT_ENDPOINT_URL URL del servizio ingest
CLOSEYOURIT_TOKEN Token Bearer server-side; non deve essere esposto in client pubblici
CLOSEYOURIT_PROJECT_ID Identificativo del progetto CloseYourIt
CLOSEYOURIT_ENVIRONMENT Ambiente applicativo
CLOSEYOURIT_RELEASE Versione dell'applicazione monitorata
CLOSEYOURIT_USAGE_ENABLED Accende la telemetria d'uso, spenta per default (1, true, yes, on)

I valori reali devono essere gestiti fuori dal repository. CYPY non ha ancora ambienti: quando serviranno, configurarli in CloseYourIt e usare cyi run; niente provider alternativi come fallback.

from closeyourit import Configuration

configuration = Configuration()
if not configuration.enabled:
    print(configuration.disabled_reason)

Configurazioni incomplete o non valide disabilitano l'SDK senza sollevare eccezioni. In production l'endpoint deve usare HTTPS; il token deve essere server-side e iniziare con cyi_.

Scope e protezione dati

Lo scope usa ContextVar: task asincroni e thread non condividono mutazioni accidentali, mentre with_scope() eredita lo stato corrente e lo ripristina al termine del blocco.

from closeyourit import current_scope, with_scope

current_scope().set_user({"id": "account-123", "email": "private@example.com"})
current_scope().set_tag("tenant", "acme")

with with_scope() as scope:
    scope.set_extra("operation", "checkout")
    event_context = scope.snapshot().to_event_data()

Per default lo snapshot conserva soltanto user.id; send_pii=True deve essere una scelta esplicita dell'applicazione consumer. Password, token, credenziali, cookie, dati personali, query sensibili e header di autenticazione vengono filtrati ricorsivamente prima del trasporto. Gli snapshot sono copie profondamente immutabili e non cambiano se il consumer modifica gli oggetti originali.

Anche un header di accesso citato dentro il testo di un errore viene redatto, non solo l'header strutturato. La regola vale per Bearer, Basic, Digest, Token e ApiKey in ogni combinazione di maiuscole e spazi: una coppia con chiave sensibile diventa Authorization=[FILTERED] — schema compreso — e uno schema che compare da solo diventa Bearer [FILTERED]. Dopo lo schema si redige tutto ciò che non è riconoscibile come parola di frase («the bearer of bad news», «Basic authentication failed»): la forma del valore non distingue una credenziale minuscola da una parola comune, e nel dubbio si preferisce redigere.

Eventi, log e metriche

EventBuilder produce payload conformi al contratto wire senza effettuare rete. Client applica sampling, before_send, breadcrumb e protezione dati prima di consegnare il payload a un EventSink. Con una configurazione valida, il sink predefinito è il transport asincrono di produzione; una configurazione incompleta resta un no-op.

from closeyourit import Configuration, EventBuilder

configuration = Configuration(environment="production", release="v1.4.2")
builder = EventBuilder(configuration)

try:
    raise RuntimeError("checkout failed")
except RuntimeError as error:
    payload = builder.exception(error, handled=True)

Le eccezioni conservano cause, stack frame, handled, runtime, release e scope. Messaggi e log vengono scrubbati prima della consegna; le metriche slow_method usano UUID idempotenti e durata monotona. I breadcrumb sono limitati, isolati tramite ContextVar e mantengono soltanto gli ultimi N elementi.

Transport e shutdown

Il transport usa soltanto la standard library, serializza il payload prima dell'accodamento e non blocca il thread applicativo sulla rete. La coda è limitata, il worker parte soltanto al primo evento accettato e gli eventi eccedenti vengono scartati in modo diagnosticabile. Retry limitati coprono errori di rete, timeout, 408, 425, 429 con Retry-After e risposte 5xx.

from closeyourit import Client, Configuration, Transport

configuration = Configuration()
transport = Transport(configuration, max_queue=100)
client = Client(configuration, sink=transport)

client.capture_message("worker started")
client.flush(timeout=2.0)

print(transport.stats)
client.close(timeout=2.0)

flush() attende che ogni evento accettato raggiunga uno stato terminale; close() impedisce nuovi accodamenti, drena la coda ed è idempotente. Durante un fork lo stato ereditato viene scartato e il processo figlio crea una nuova coda e un nuovo worker. I redirect conservano metodo e body, ma il Bearer viene inviato soltanto alla stessa authority e mai dopo un cambio host, porta o protocollo.

Logging ed errori non gestiti

CloseYourItHandler inoltra i record del modulo logging a partire da una soglia configurabile. Gli attributi aggiunti con extra vengono scrubbati come ogni altro payload; i logger interni closeyourit sono esclusi per impedire loop. Installazione, rimozione e chiusura sono idempotenti.

import logging

from closeyourit import Client, CloseYourItHandler, Configuration

client = Client(Configuration())
handler = CloseYourItHandler(client, level=logging.WARNING).install()
logging.getLogger().warning("retry", extra={"attempt": 2})

handler.close()

ErrorHooks integra sys.excepthook, threading.excepthook e, quando fornito, l'exception handler di un loop asyncio. Ogni errore viene registrato come fatal e non gestito, poi il gestore preesistente viene sempre richiamato. Cancellazioni e terminazioni intenzionali non vengono catturate; gli errori interni dell'SDK restano fail-safe.

import asyncio

from closeyourit import Client, Configuration, ErrorHooks

client = Client(Configuration())
hooks = ErrorHooks(client).install()


async def main() -> None:
    hooks.install(asyncio.get_running_loop())
    # avvio dell'applicazione


asyncio.run(main())
hooks.close(timeout=2.0)

uninstall() ripristina soltanto i gestori ancora posseduti dall'istanza, senza sovrascrivere hook installati successivamente dall'applicazione. close() esegue anche la chiusura idempotente del client e del transport.

Celery

L'integrazione Celery è opzionale e usa esclusivamente i signal ufficiali. Registra durata di esecuzione e latenza di coda come metriche slow_method, con task name, queue, retry count, stato, release e soli header di correlazione esplicitamente allowlisted.

from closeyourit import CeleryIntegration, Client, Configuration

client = Client(Configuration())
celery_integration = CeleryIntegration(
    client,
    task_threshold_ms=500,
    queue_threshold_ms=250,
    correlation_headers=("traceparent", "x-request-id"),
    lifecycle_logs=False,  # Opt-in observed attempt outcomes.
).install()

# Allo shutdown dell'applicazione:
celery_integration.close()
client.close(timeout=2.0)

Errori, retry e task revocati vengono catturati senza modificare la politica di retry Celery. args, kwargs e risultati non vengono mai letti né inviati. Un timestamp tecnico aggiunto al messaggio permette di misurare la coda anche tra processi. La scadenza è max(published_at, eta): il ritardo programmato resta separato dall’attesa in coda. Eventuale clock skew negativo viene azzerato e dichiarato nel contesto versionato. In modalità eager, dove non avviene una pubblicazione sul broker, resta disponibile la durata del task ma non viene inventata una latenza di coda.

Metriche e log portano il contesto additivo job con schema_version: 1, tempi UTC, durata monotona, tentativo e contatore retry distinti. Tempi mancanti restano null. Con lifecycle_logs=True ogni esito osservato produce un log info fisso, senza promuoverlo a errore: retry ha terminal=False, uno stato sconosciuto terminal=None. Il valore predefinito è False. Sampling, configurazione o perdita del transport impediscono di usarli come censimento completo. Il backend mantiene kind/label legacy e applica il nuovo raggruppamento solo al contesto v1 valido.

I correlation ID accettano soltanto identificatori ASCII con forma limitata a 128 byte; traceparent deve rispettare il formato W3C versione 00, inclusi trace ID e parent ID non nulli. Valori duplicati, malformati, sovradimensionati o con forma compatibile con PII vengono scartati. Installazione, rimozione e chiusura sono idempotenti; se Celery non è installato l'adapter resta un no-op fail-safe. La chiusura dell'integrazione non chiude il client, che può essere condiviso con gli adapter web, HTTP e SQLAlchemy.

Applicazioni web

WSGIMiddleware e ASGIMiddleware usano esclusivamente la standard library. Creano uno scope per richiesta, mantengono lo streaming lazy, misurano la durata monotona e catturano le eccezioni non gestite senza alterare risposta o propagazione. Il body non viene mai letto. Il contesto include metodo, path e URL senza query; gli header provengono soltanto da Configuration.request_header_allowlist e quelli sensibili restano esclusi anche se aggiunti per errore all'allowlist.

from closeyourit import ASGIMiddleware, Client, Configuration, WSGIMiddleware

client = Client(Configuration())
wsgi_application = WSGIMiddleware(wsgi_application, client)
asgi_application = ASGIMiddleware(asgi_application, client)

Il middleware riusa X-Request-ID come trace_id soltanto se è un identificatore ASCII sicuro e limitato a 128 caratteri; altrimenti genera un UUID. A risposta completata registra status e durata nello scope; oltre slow_request_threshold_ms emette una metrica slow_request, usando la route templated fornita dal framework quando esiste. L'URL della metrica viene sempre ricostruito senza userinfo, query o fragment.

Per Django inserire il middleware tra i primi elementi, così che avvolga l'applicazione:

MIDDLEWARE = [
    "closeyourit.django.DjangoMiddleware",
    # middleware dell'applicazione
]

Per Flask l'integrazione avvolge lo stack WSGI corrente e registra route ed eccezioni tramite gli hook ufficiali del framework:

from closeyourit import Client, Configuration, FlaskIntegration

client = Client(Configuration())
FlaskIntegration(app, client=client)

Gli adapter sono importabili anche quando Django o Flask non sono installati; le dipendenze opzionali vengono caricate soltanto quando l'integrazione corrispondente viene inizializzata.

Integrazioni HTTP e SQLAlchemy

Requests, HTTPX e SQLAlchemy restano dipendenze opzionali. Installare soltanto gli extra usati dall'applicazione:

python -m pip install "closeyourit[requests]"
python -m pip install "closeyourit[httpx]"
python -m pip install "closeyourit[sqlalchemy]"

L'instrumentation è locale all'istanza, usa gli hook/eventi ufficiali disponibili e restituisce sempre un handle uninstrument() idempotente. Non vengono applicati monkeypatch globali.

import httpx
import requests

from closeyourit import Client, Configuration, instrument_httpx, instrument_requests

client = Client(Configuration())
session = requests.Session()
requests_instrumentation = instrument_requests(session, client)

http_client = httpx.Client()
httpx_instrumentation = instrument_httpx(http_client, client)

requests_instrumentation.uninstrument()
httpx_instrumentation.uninstrument()

Ogni chiamata produce un breadcrumb HTTP; timeout e 5xx sono marcati come warning. Le chiamate oltre slow_external_threshold_ms generano slow_external_http, mentre richieste ripetute allo stesso metodo, host e path templatizzato generano un solo repeated_http alla soglia. URL, query, fragment e credenziali non arrivano mai nel payload: UUID, identificativi numerici e token-like nel path vengono normalizzati. http_capture_hosts permette di limitare ulteriormente gli host osservati.

La propagazione W3C traceparent è disabilitata per default e richiede sia trace_propagation_enabled=True sia una trace_propagation_hosts esplicita. L'allowlist viene ricontrollata a ogni redirect e il trace header viene rimosso passando a un host non consentito.

configuration = Configuration(
    trace_propagation_enabled=True,
    trace_propagation_hosts=("api.example.com",),
)

SQLAlchemy usa before_cursor_execute, after_cursor_execute e handle_error sull'engine sync o async. Ogni query viene trasformata in un fingerprint privo di literal, commenti e bind raw. Le query lente sono puntuali; profile() delimita la finestra per conteggio totale e rilevazione N+1.

from closeyourit import instrument_sqlalchemy

sqlalchemy_instrumentation = instrument_sqlalchemy(engine, client)

with sqlalchemy_instrumentation.profile(route="orders.index"):
    load_orders()

sqlalchemy_instrumentation.uninstrument()

I profili usano ContextVar, quindi richieste concorrenti, task async e profili annidati non condividono conteggi. Le API adottate sono documentate dai progetti upstream: Requests hooks, HTTPX event hooks e SQLAlchemy connection events.

Query Django (opt-in)

La raccolta query è spenta per default. Per ogni richiesta Django può registrare query lente, N+1 e conteggio totale alto, mantenendo ambiente, release applicativa, versione SDK e trace ID. Usa il protocollo execute_wrapper di Django: non modifica cursor, ORM o parametri SQL.

from closeyourit import Client, Configuration, DjangoMiddleware


class MonitoringMiddleware(DjangoMiddleware):
    def __init__(self, get_response):
        super().__init__(
            get_response,
            client=Client(Configuration()),
            query_monitoring=True,
        )


# settings.py, prima dei middleware dell'applicazione:
MIDDLEWARE = ["myapp.monitoring.MonitoringMiddleware", ...]

Per finestre esplicite, anche fuori da HTTP:

from closeyourit import instrument_django

queries = instrument_django(client)
with queries.profile(route="orders.index"):
    load_orders()

async with queries.async_profile(route="orders.async_index"):
    await load_orders_async()

queries.uninstrument()

slow_query_threshold_ms è inclusiva e 0 disabilita il singolo campione lento. N+1 e query_count_threshold scattano solo quando il conteggio supera la soglia, come SQLAlchemy. executemany conta una chiamata SQL, senza enumerare i bind. Le query fallite contano una volta senza cambiare l'eccezione originale. Profili annidati attribuiscono ogni esecuzione soltanto al profilo interno. Profili concorrenti restano isolati; i task figli creati dentro una richiesta condividono intenzionalmente il suo conteggio.

Il middleware mantiene il profilo durante streaming sync/async e lo chiude una sola volta anche con risposta non consumata, chiusura anticipata o errore. uninstrument() rimuove solo gli hook posseduti e lascia attivo il client condiviso; dopo la rimozione il middleware continua a servire richieste. Alla chiusura dell'applicazione rimuovere gli hook e poi chiamare client.close().

Installare prima dell'avvio dei worker. I signal ufficiali collegano nuove connessioni e thread Django; async_profile() collega anche il thread ORM già esistente tramite sync_to_async. Per thread manuali preesistenti aprire profile() nel thread che esegue le query. Connessioni create fuori da Django, ORM async nativo di terze parti e chiamate dirette al driver non passano attraverso questo hook. Il broker usa il registro execute_wrappers e rimuove per identità il proprio callable, perché il context manager Django usa uno stack pop() non adatto a richieste ASGI sovrapposte; i wrapper dell'applicazione restano intatti.

Il fingerprint condiviso elimina literal/commenti/bind, e il normale scrub SDK protegge anche le forme credentiali riconosciute nei campi nuovi. La release deve essere un identificatore di versione privo di PII: lo scrub producer non elimina email arbitrarie da questa stringa di configurazione. Non vengono letti parametri, risultati, credenziali DB o body HTTP. Testo SQL non stringa o oltre 16.384 caratteri viene scartato con diagnostica; ogni profilo mantiene al massimo 1.000 fingerprint distinti. La finestra deve terminare dopo le query: task lasciati proseguire oltre la risposta non vengono osservati. Il trasporto resta asincrono; invii falliti sono visibili in client.sink.stats e nel logger closeyourit.

Matrice automatica dedicata: Python 3.11 / Django 4.2.30, Python 3.14 / Django 5.2.17 e Python 3.14 / Django 6.0.8. Le prove eseguono SQLite e ORM reali, connessioni multiple, thread/async, streaming, soglie e installazione/rimozione. Non equivalgono a certificare tutti i backend DB supportati da Django. Django 4.2 è una compatibilità legacy, non una raccomandazione per nuove installazioni. Il consumer closeyourit-labs/bin/certify-django-queries installa una wheel isolata e verifica il percorso reale Django→Rails→worker→lettura privata, più un guasto HTTP controllato. Nessuna pubblicazione o certificazione universale è implicita.

Fonti: strumentazione database Django, ORM e thread async.

Telemetria d'uso

Risponde a «quali parti dell'applicazione girano davvero». È opt-in: senza CLOSEYOURIT_USAGE_ENABLED (o Configuration(usage_enabled=True)) il registro non tiene nulla in memoria e non parte alcun thread. Una volta accesa, l'SDK accumula i simboli visti e li spedisce in una sola richiesta per finestra su /api/v1/projects/{project_id}/usages; cyi usage list -p CYPY li elenca.

from closeyourit import Client, Configuration

client = Client(Configuration(usage_enabled=True, usage_flush_interval=300))

client.used("billing.export")  # kind `custom`, chiave letterale
client.record_usage("job", "billing.charge")  # kind `job`
client.close(timeout=2.0)  # ferma il timer e flusha l'ultima finestra

I simboli si registrano da soli dove l'SDK conosce già l'identità del codice: i middleware web registrano il template di rotta risolto dal framework, gli adapter Celery il nome del task all'avvio. orders/<int:pk> e /orders/{order_id} diventano /orders/:pk e /orders/:order_id, forma condivisa con gli altri SDK; una rotta non risolta non produce alcun simbolo, perché il path concreto è un URL e nel censimento non entra mai. Niente utenti, parametri, IP o query string.

Impostazione Default Significato
usage_enabled False Accende il canale; da CLOSEYOURIT_USAGE_ENABLED
usage_flush_interval 300.0 Secondi fra due invii; un valore non positivo torna a 300
usage_max_symbols 2000 Simboli distinti per finestra; oltre il tetto la finestra è truncated

I conteggi sono indicativi: l'unico dato portante è last_seen_at, quindi una finestra persa costa una finestra su un simbolo che si rivede subito dopo. Non esiste sampling — campionare una rotta chiamata tre volte al mese fabbricherebbe proprio il falso «mai vista» che il canale esiste per evitare. Il registro si svuota a ogni invio, un errore di invio non propaga mai nell'applicazione, il flush non passa da before_send e dopo un fork il processo figlio riparte da una finestra vuota.

Packaging e release

Il progetto usa pyproject.toml, build backend Hatchling e layout src/. I tag stabili vMAJOR.MINOR.PATCH avviano .github/workflows/publish.yml, che verifica versione, changelog, compatibilità Python, wheel e source distribution prima di pubblicare su PyPI.

Il gate esegue anche il contratto golden vendorizzato in contracts/ingest/v1: schema, fixture dei producer, classificazione HTTP e checksum devono restare allineati allo snapshot canonico del repository closeyourit-docs, identificato da contracts/ingest/LOCK.json.

La pubblicazione usa il GitHub environment pypi e Trusted Publishing OIDC: non esistono token PyPI permanenti nel repository. Prima di creare un tag, spostare le modifiche rilevanti da [Unreleased] alla sezione della versione corrispondente.

Repository e tracker

FastAPI e Starlette

Gli extra [fastapi] e [starlette] espongono adapter opzionali con import lazy. Installa il wrapper dopo aver registrato route e handler, prima di avviare l'applicazione:

from fastapi import FastAPI
from closeyourit import Client, Configuration, instrument_fastapi

api = FastAPI()
client = Client(Configuration())


@api.get("/items/{item_id}")
async def item(item_id: int):
    return {"id": item_id}


app = instrument_fastapi(api, client)

Per Starlette usa instrument_starlette(api, client). Esporta il wrapper app al server ASGI; continua a configurare il framework attraverso api. app.uninstall() disattiva il wrapper e ripristina soltanto gli handler ancora posseduti dall'integrazione. Non sostituisce middleware aggiunti da terzi. L'installazione ripetuta con lo stesso client riusa il wrapper; un client o una policy handled esplicitamente diversi sollevano ValueError. Configura prima del serving; non installare nuovi hook mentre vengono aggiunti handler o middleware concorrenti.

Il client resta di proprietà dell'applicazione: esegui client.close(timeout=...) nel tuo lifecycle di shutdown, eventualmente in un thread per non bloccare l'event loop. Il wrapper inoltra lifespan e WebSocket senza telemetria e non legge il body né accumula chunk di risposta. Attende il completamento dello streaming e dei background task del framework. Una cancellazione si propaga senza essere trasformata in errore; gli errori del trasporto send non vengono attribuiti all'applicazione. Gli errori dei task avviati dall'applicazione e lasciati fuori dal lifecycle ASGI richiedono gli hook asyncio espliciti.

Gli errori non gestiti vengono acquisiti una volta e rilanciati. Per quelli gestiti abilita capture_handled_errors=True: mechanism.handled resta true, ma le HTTPException contengono solo lo status, senza detail o header dinamici. Le RequestValidationError di FastAPI sono sempre escluse dagli errori perché il testo contiene input della richiesta; la metrica 422 rimane disponibile, anche se un handler rilancia l’errore. Le ResponseValidationError vengono riassunte in un errore ResponseValidationFailure con messaggio stabile e traceback originale, senza input della risposta; la protezione copre anche handler custom e catene causali. Gli altri messaggi di eccezione usano lo scrubber SDK esistente: evita PII nel testo delle eccezioni personalizzate e nell'identificatore release.

Le metriche performance_issue/slow_request usano template, ambiente, release configurata, versione SDK e trace ID per richiesta. Sono emesse dalla soglia slow_request_threshold_ms (compreso tutto il lifecycle ASGI); 0 le abilita per ogni richiesta. I router standard e i Mount conservano i template composti senza rieseguire convertitori. Router personalizzati, Host routing e applicazioni ASGI opache possono produrre /<unmatched>; il path concreto non viene usato come template di ripiego. Lo scope è isolato con ContextVar, anche tra stream concorrenti. Un middleware ASGI CloseYourIt annidato riusa il lifecycle per evitare duplicati.

Tuple verificate localmente e fissate nella CI:

  • Python 3.11.15, FastAPI 0.115.0, Starlette 0.38.6.
  • Python 3.14.6, FastAPI 0.142.2, Starlette 1.7.0.

Queste prove non certificano automaticamente tutte le combinazioni comprese nei requisiti minimi degli extra. La prova end-to-end riproducibile è closeyourit-labs/bin/certify-asgi-frameworks: wheel installato fuori dal checkout, applicazioni reali, transport HTTP reale, Rails e worker, lettura autenticata e guasto HTTP reale. Le chiamate ASGI dei framework avvengono nello stesso processo Python; non è una prova di deployment Uvicorn o di release pubblicata.

Riferimenti: Starlette exceptions, Starlette middleware, FastAPI middleware.

Metadata

Release files for closeyourit 0.4.0

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

Source distribution (sdist)

Source distribution for closeyourit 0.4.0
File Size Uploaded
closeyourit-0.4.0.tar.gz 135.1 kB Details

Built distribution (wheel)

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

Total release size: 203.9 kB

Release files / closeyourit-0.4.0.tar.gz

Download URL closeyourit-0.4.0.tar.gz
Size 135.1 kB
Tags Source
SHA-256 checksum
How to use checksums
db084850c0b60569e5ac51d65a1fb2eadc508b66e31c4e7b059ca6ac88e29299
BLAKE2b-256 checksum
How to use checksums
25a904e786a99b0a8041d6b44585147426e01935cabee1693b2247874b4169ac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

Release files / closeyourit-0.4.0-py3-none-any.whl

Download URL closeyourit-0.4.0-py3-none-any.whl
Size 68.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
28c704a934d05205b14d7c78f6d231dfb01daf20140f772a53dceaf267b3c7de
BLAKE2b-256 checksum
How to use checksums
7a93d432931d997be10c412ec71bb1bf17977d09cbdc1e5345f10429d70e0236
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.2

2 release files

0.1.0

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