logister-python
Python SDK for sending errors, logs, metrics, transactions, spans, and scheduled-job check-ins to Logister.
Install it from PyPI as logister-python.
Use it in APIs, workers, schedulers, command-line jobs, and internal services. It includes:
- a shared
LogisterClient - native Python
loggingintegration FastAPIrequest instrumentationDjangorequest middlewareCelerytask instrumentationFlaskrequest instrumentation
Supports Python 3.11 and newer.
- Main Logister app: https://github.com/taimoorq/logister
- Product docs: https://logister.org/docs/
- Insights guide: https://logister.org/docs/product/#insights
- Python integration docs: https://logister.org/docs/integrations/python/
- PyPI package: https://pypi.org/project/logister-python/
Quick start
Create a project in Logister and generate a project API key under Project settings → API keys.
python -m pip install logister-python
export LOGISTER_API_KEY="<project-api-key>"
export LOGISTER_BASE_URL="https://logister.example.com"
export LOGISTER_ENVIRONMENT="development"
Send a test error:
from logister import LogisterClient
with LogisterClient.from_env(default_context={"service": "checkout-api"}) as client:
try:
raise RuntimeError("README test error")
except RuntimeError as error:
client.capture_exception(
error,
fingerprint="readme-test-error",
context={"component": "checkout"},
)
Open the project inbox and confirm that README test error appears. A 401 response usually means the key or base URL is wrong; the Python integration guide covers framework setup and troubleshooting.
Table Of Contents
- Quick start
- What This Package Is For
- Package Links
- Install From PyPI
- Environment Variables
- Privacy and redaction
- Failure isolation
- Core Client
- Python Logging
- Error Capture
- FastAPI
- Celery
- Django
- Flask
- Check-ins
- Using project Insights
- GitHub source context and deployments
- Event Mapping
- Development
- Publishing
- Release Flow
What This Package Is For
Use logister-python when you want a Python service to send operational telemetry into Logister through the published PyPI package instead of wiring raw HTTP calls by hand.
- API and web apps: FastAPI, Django, Flask
- Worker and scheduler processes: Celery, cron-style jobs, CLI tasks
- Standard-library logging pipelines:
loggingto Logister events - Shared custom instrumentation: errors, logs, metrics, transactions, spans, and check-ins
Package Links
- PyPI package: https://pypi.org/project/logister-python/
- GitHub releases: https://github.com/taimoorq/logister-python/releases
- Source repository: https://github.com/taimoorq/logister-python
- Integration docs: https://logister.org/docs/integrations/python/
Install From PyPI
Core client:
pip install logister-python
With uv:
uv add logister-python
FastAPI support:
pip install 'logister-python[fastapi]'
Celery support:
pip install 'logister-python[celery]'
Django support:
pip install 'logister-python[django]'
Flask support:
pip install 'logister-python[flask]'
Package index: https://pypi.org/project/logister-python/
Environment Variables
LogisterClient.from_env() reads:
LOGISTER_API_KEYLOGISTER_BASE_URL(defaults tohttps://logister.org)LOGISTER_TIMEOUT(defaults to5.0)LOGISTER_ENVIRONMENTLOGISTER_RELEASELOGISTER_REPOSITORY(falls back toGITHUB_REPOSITORY)LOGISTER_COMMIT_SHA(falls back toGITHUB_SHA)LOGISTER_BRANCH(falls back toGITHUB_REF_NAME)LOGISTER_CAPTURE_LOCALS(true/false, defaults tofalse)LOGISTER_CAPTURE_CLIENT_IP(true/false, defaults tofalse; 0.6.0+)LOGISTER_CAPTURE_HOSTNAME(true/false, defaults tofalse; 0.6.0+)LOGISTER_REDACT_KEYS(comma-separated extra keys to redact; 0.6.0+)
The same options are available as LogisterClient(capture_client_ip=..., capture_hostname=..., redact_keys=[...]).
A base_url that uses plain http:// to anything other than a loopback host
(localhost, *.localhost, 127.0.0.0/8, ::1) emits a UserWarning, because
the API key and events would travel unencrypted.
Privacy and redaction
Starting with 0.6.0 every event's context is redacted before it is serialized. This uses the same default rules as the other Logister SDKs.
- Keys. A key is sensitive when, lowercased with every non-alphanumeric
character removed, it equals one of
password,passwd,pwd,secret,token,apikey,authorization,proxyauthorization,cookie,setcookie,sessiontoken,sessionkey,credential,credentials,privatekey,clientsecret,accesstoken,refreshtoken,idtoken,authtoken,xapikey,xauthtoken,xaccesstoken,xcsrftoken,xxsrftoken,signature,ssn,creditcard,cardnumberorcvv, or containstoken,secret,password,passwd,apikey,authorization,credentialorcookie. Soapi_key,X-API-Key,accessTokenandstripe_secret_keyall match. Their values become"[REDACTED]". - Values. Strings are scrubbed for
Bearer <x>andBasic <x>credentials, JWT-shaped strings andlgst_-prefixed keys. - URLs. Captured URLs, query strings and
Refererheaders keep scheme, host and path. Userinfo is dropped, and the value of any sensitive query parameter is replaced.
Redaction covers the request URL, query string and headers, path_params and
view_args, default_context and per-call context, log record extra fields,
span context, exception messages and frame locals. It also scrubs the event
message. The SDK's own envelope fields (uuid, event_type, trace, span, request
and session IDs) are added after redaction and are never changed.
Add your own exact-match keys (normalized the same way) with
LogisterClient(redact_keys=["order_ref", "member_number"]) or
LOGISTER_REDACT_KEYS=order_ref,member_number.
Client IP addresses (client_ip and the X-Forwarded-For header) and host names
are no longer sent by default. Opt in with capture_client_ip=True /
LOGISTER_CAPTURE_CLIENT_IP=true and capture_hostname=True /
LOGISTER_CAPTURE_HOSTNAME=true. The Python executable path is no longer sent.
The helpers are public if you want the same rules elsewhere:
from logister.redaction import redact, redact_url, scrub_string
Failure isolation
Automatic captures never raise into your application. That covers the FastAPI,
Django and Flask integrations, Celery signal handlers and the logging handler. A
rejected API key, a 5xx, a network error or a serialization bug is logged at
DEBUG on the logister logger and the event is dropped. When a request handler
raises, your original exception propagates unchanged.
Values that JSON cannot represent (UUIDs, datetimes, Decimal, custom objects)
are serialized with str(), and circular structures are cut. Manual calls such as
client.capture_message(...) still raise LogisterError so scripts can see
delivery failures.
The FastAPI middleware, Django's async middleware path, and log records emitted
inside a running event loop prepare each event on the caller. Delivery then runs on
one background thread with a 1,000-event queue, so a slow Logister never blocks the
event loop. When the queue is full, new events are dropped. Call client.flush()
or client.close() at shutdown to wait for queued events. The HTTP client and the
background thread are recreated after fork(), so pre-forking servers do not share
connections with the parent.
Core Client
Use the shared client when you are wiring a script, worker, CLI task, or framework hook and want one place to send custom events.
from logister import LogisterClient
client = LogisterClient.from_env(default_context={"service": "api"})
client.capture_message("Application booted", level="info")
client.capture_metric(
"cache.hit_rate",
0.98,
unit="ratio",
level="info",
fingerprint="metric:cache.hit_rate",
context={"cache": "primary"},
)
client.capture_transaction("POST /checkout", 182.4, request_id="req_123")
client.capture_span(
"render checkout",
82.1,
kind="render",
status="ok",
trace_id="trace_123",
parent_span_id="span_root",
context={"route": "POST /checkout"},
)
Python Logging
If your app already uses the standard library logging module, this is usually the easiest way to start sending application logs into Logister without rewriting call sites.
import logging
from logister import LogisterClient, instrument_logging
client = LogisterClient.from_env(default_context={"service": "api"})
logger = logging.getLogger("checkout")
instrument_logging(client, logger=logger)
logger.warning("Inventory cache miss", extra={"request_id": "req_123", "sku": "sku_42"})
What this records:
- standard Python log records as
logevents logger.exception(...)and other records withexc_infoaserrorevents- logger metadata like logger name, module, file, function, line number, process, and thread
- extra record fields passed through
extra={...}so request IDs, trace IDs, and app-specific details show up in Logister (redacted by the default rules)
Records from the logister, httpx and httpcore loggers are ignored, and a record
emitted while the handler is already sending is dropped. That makes it safe to attach
the handler to the root logger at INFO: each log line produces exactly one event.
You can also manage the underlying HTTP client explicitly:
from logister import LogisterClient
with LogisterClient.from_env() as client:
client.capture_message("Worker online")
Error Capture
Python error reports are most useful when you include the service or component name and let the SDK send the traceback structure for you.
from logister import LogisterClient
client = LogisterClient.from_env(default_context={"service": "checkout"})
try:
run_checkout()
except Exception as exc:
client.capture_exception(
exc,
fingerprint="checkout-failed",
context={
"component": "checkout",
"order_id": 1234,
},
)
Captured Python exceptions include structured traceback frames, backtrace text, exception module and qualified class name, chained exceptions from raise ... from ..., and runtime metadata like Python version, platform, and process ID (hostname only with capture_hostname=True).
Set LOGISTER_CAPTURE_LOCALS=true if you want frame locals included in error events for the Logister UI.
Frame locals can contain passwords, tokens, request bodies, and personal data. Leave this option off unless you have reviewed what your application keeps in local variables and your retention policy permits collecting it. When it is on, self, cls and request are skipped. Other locals go through the default redaction: a local named password is replaced, and bearer tokens or JWTs in a repr are scrubbed.
FastAPI
This is the cleanest path for modern Python API services.
from fastapi import FastAPI
from logister import LogisterClient, instrument_fastapi
app = FastAPI()
logister = LogisterClient.from_env(default_context={"service": "api"})
instrument_fastapi(app, logister, capture_spans=True)
What this records:
- request duration as a
transaction - optional root
serverspans for request load waterfall charts whencapture_spans=True - uncaught request exceptions as an
error - request metadata like method, path, route, full URL, selected headers, path params, query string,
x-request-id, andx-trace-id(client IP andX-Forwarded-Foronly withcapture_client_ip=True)
Events are prepared on the request task and delivered on a background thread, so the middleware never blocks the event loop on Logister. See Failure isolation.
You can customize transaction naming:
instrument_fastapi(
app,
logister,
transaction_namer=lambda request: f"{request.method} {request.url.path}",
)
Celery
This is the worker-side path when your Python app does meaningful work outside the request cycle.
from celery import Celery
from logister import LogisterClient, instrument_celery
celery_app = Celery("billing")
logister = LogisterClient.from_env(default_context={"service": "worker"})
instrument_celery(
celery_app,
logister,
monitor_slug_factory=lambda task: getattr(task, "name", None),
)
What this records:
- task runtime as a
transaction - task failures as an
error - task retries as a warning
log - optional task-level
check_inevents when you providemonitor_slug_factory - task metadata like queue, module, retry count, and ETA when Celery exposes it, plus the worker hostname with
capture_hostname=True
Django
Use middleware when you want a Django app to report request timing and uncaught view exceptions with very little setup.
Use the built-in middleware directly when env-based configuration is enough:
MIDDLEWARE = [
# ...
"logister.django.LogisterMiddleware",
]
LogisterMiddleware reads the same LOGISTER_* environment variables as LogisterClient.from_env().
If you want to build the client yourself, bind it with build_django_middleware():
from logister import LogisterClient, build_django_middleware
logister = LogisterClient.from_env(default_context={"service": "django-web"})
ConfiguredLogisterMiddleware = build_django_middleware(logister)
ConfiguredLogisterMiddlewareWithSpans = build_django_middleware(logister, capture_spans=True)
What Django middleware records:
- request duration as a
transaction - optional root
serverspans for request load waterfall charts whencapture_spans=True - uncaught view exceptions via
process_exception()as anerror - request metadata like method, path, route, full URL, selected headers, status code, query string,
X-Request-ID, andX-Trace-ID(client IP andX-Forwarded-Foronly withcapture_client_ip=True)
Flask
Use the Flask hooks when you want lightweight request instrumentation without changing your route code.
from flask import Flask
from logister import LogisterClient, instrument_flask
app = Flask(__name__)
logister = LogisterClient.from_env(default_context={"service": "flask-web"})
instrument_flask(app, logister, capture_spans=True)
What Flask instrumentation records:
- request duration as a
transaction - optional root
serverspans for request load waterfall charts whencapture_spans=True - uncaught request exceptions as an
error - request metadata like method, path, full URL, endpoint, blueprint, selected headers, status code, query string,
X-Request-ID, andX-Trace-ID(client IP andX-Forwarded-Foronly withcapture_client_ip=True)
Check-ins
Check-ins are a good fit for scheduled jobs, cron-style imports, and the “did this worker actually run?” questions Python teams usually end up debugging.
from logister import LogisterClient
client = LogisterClient.from_env(default_context={"service": "scheduler"})
client.check_in(
"nightly-import",
"ok",
release="worker@2026.05.21",
expected_interval_seconds=3600,
duration_ms=842.7,
trace_id="trace-123",
request_id="req-123",
)
Using project Insights
The Logister project Insights tab combines Inbox, Activity, and Performance data into live dashboard views. Python services get the most useful Insights view when they send consistent LOGISTER_ENVIRONMENT, LOGISTER_RELEASE, and stable top-level context attributes.
Use default_context for attributes that should be present on most events, and pass per-event context for route, queue, worker, or feature dimensions:
from logister import LogisterClient
client = LogisterClient.from_env(
default_context={
"service": "billing-api",
"region": "us-east-1",
}
)
client.capture_metric(
"queue.depth",
42,
unit="jobs",
context={
"service": "billing-worker",
"queue": "billing",
"tenant_tier": "enterprise",
},
)
client.capture_transaction(
"POST /checkout",
182.4,
context={
"route": "POST /checkout",
"feature_flag": "new_checkout",
"tenant_tier": "enterprise",
},
request_id="req_123",
)
client.capture_span(
"render checkout",
82.1,
kind="render",
status="ok",
trace_id="trace_123",
parent_span_id="span_root",
context={
"route": "POST /checkout",
"tenant_tier": "enterprise",
},
)
client.capture_message(
"payment provider retry",
level="warn",
context={
"service": "billing-worker",
"provider": "stripe",
"queue": "billing",
},
)
client.check_in(
"nightly-reconcile",
"ok",
expected_interval_seconds=3600,
duration_ms=842.7,
context={
"service": "billing-worker",
"queue": "reconcile",
},
)
Practical Insights recipes:
- Release validation: set
LOGISTER_RELEASE, then filter Insights to the new release and compare error count, transaction P95, and custom metrics. - Worker monitoring: report metrics such as
queue.depth,queue.latency,task.retry_count, orcelery.active_taskswith stablequeueandservicecontext keys. - Performance triage: enable
capture_spans=Truefor FastAPI, Django, or Flask instrumentation to feed request load waterfall charts, then add route-level logs and metrics with matchingroutevalues. - Instrumentation audit: open Insights after deploy and confirm errors, logs, metrics, transactions, spans, and check-ins all appear in the recent stream.
Keep custom attributes stable and low-cardinality. Good top-level context keys include service, region, queue, route, tenant_tier, provider, and feature_flag. Avoid raw IDs, emails, request bodies, SQL text, and per-user values as Insights dimensions.
GitHub source context and deployments
When a Logister project is connected to a GitHub repository, LogisterClient.from_env() can attach source context automatically:
export LOGISTER_ENVIRONMENT=production
export LOGISTER_RELEASE=checkout@2026.06.18
export LOGISTER_REPOSITORY=acme/checkout
export LOGISTER_COMMIT_SHA="$(git rev-parse HEAD)"
export LOGISTER_BRANCH="$(git branch --show-current)"
client = LogisterClient.from_env(default_context={"service": "checkout-api"})
client.capture_exception(error)
CI/CD can also record the release-to-commit mapping directly:
client.record_deployment(
release="checkout@2026.06.18",
environment="production",
repository="acme/checkout",
commit_sha="4f8c2d1a9b7e6c5d4a3b2c1d0e9f8a7b6c5d4e3f",
branch="main",
workflow_run_url="https://github.com/acme/checkout/actions/runs/123",
)
Event Mapping
| Application signal | Logister event |
|---|---|
| Web request or task duration | transaction |
| Uncaught or manually captured exception | error |
| Application log or warning | log |
| Counter, gauge, or measurement | metric |
| Request or custom operation segment | span |
| Scheduled-job heartbeat | check_in |
Development
python -m pip install --upgrade pip 'setuptools>=83'
python -m pip install -e '.[dev,fastapi,celery,django,flask]' pip-audit
python -m pip_audit
python -m pytest
python -m build
Publishing
pyproject.toml is the package version source of truth. Update it and CHANGELOG.md together. After CI passes on main, the release-from-main workflow creates a matching vX.Y.Z tag and dispatches publish.yml.
- Merge the version bump to
main, or push a matching tag such asvX.Y.Z - GitHub Actions tests and builds the distributions once
- PyPI Trusted Publishing uploads those distributions with OIDC
- the workflow creates the GitHub Release only after the upload succeeds
Release Flow
CHANGELOG.mdtracks package releases- Git tags trigger the ordered PyPI publish and GitHub Release flow
- This package keeps its own versioning separate from the main Logister app
- PyPI versions are immutable; corrections require a new patch version
Verify both release surfaces:
curl -fsSL https://pypi.org/pypi/logister-python/json | jq -r .info.version
gh release view vX.Y.Z
Dependency verification
The package retains bounded runtime/framework ranges. CI and release builds use
requirements/ci.txt for reproducible tooling and extra dependencies across
Python 3.11–3.14; markers keep Django 5.2 on Python 3.11 and Django 6.1 on newer
runtimes. Weekly CI resolves the declared ranges afresh, then audits and tests
all extras together so new upstream releases are noticed even before lock updates.
Regenerate the constraints with uv 0.12.13 and review the resulting diff:
uv pip compile pyproject.toml requirements/tooling.in --all-extras --universal --python-version 3.11 --no-annotate --output-file requirements/ci.txt
python -m pip install -c requirements/ci.txt -e '.[dev,fastapi,celery,django,flask]' pip-audit hatchling
python -m pip_audit
python -m pytest
python -m build --no-isolation
Constraints apply to maintainer checks and builds; they do not replace the supported dependency ranges installed by SDK consumers.
Coordinated release preparation
For a coordinated ecosystem release, keep the version-changing PR unmerged until the final agreed Rails PR has been published and its deployment verified. Recheck the upstream contract/workflow pin against that final backend commit before merge. Successful source CI, a tag, or a release-impact dispatch alone is not backend readiness. After independent review, merging the new version runs CI, creates an immutable tag, and explicitly dispatches publication. A tag without a package remains incomplete.
To recover an existing reviewed tag, dispatch the publisher workflow from main
with -f tag=vX.Y.Z (Python uses publish.yml; other SDKs use release.yml). The
workflow checks out that exact tag, proves it belongs to main, and verifies public
package identity before creating the GitHub Release. Never move a consumed tag.
Weekly CI audits/tests current dependencies and cannot trigger automatic publication. Dependabot groups compatible minor/patch updates; major toolchain migrations keep separate PRs. Pin Actions to full commits and retain supported runtime floors.
Reliable event delivery
from logister import LogisterClient, RetryPolicy
client = LogisterClient(api_key="your-project-ingest-key", retry_policy=RetryPolicy())
event = client.prepare_event(event_type="log", level="info", message="Job started")
client.send_prepared_event(event)
results = client.send_events([event])
for result in results:
if result.error is not None:
print(result.event_id, type(result.error).__name__)
Each capture serializes its UUID, timestamp and context before the first request.
Retain a PreparedEvent to replay it without creating another logical event.
Ingestion retries network errors, 408/425/429 and transient 5xx up to three attempts;
Retry-After is capped at five seconds. maximum_attempts=1 disables retries.
The 15-second default retry budget is shared across a batch call, including splits
and fallback. HTTP phase timeouts also apply; synchronous HTTP calls can overrun
that budget until their current phase timeout expires. Interrupts propagate.
Batch calls accept at most 1,000 prepared events or dictionaries matching
prepare_event, send at most 100 per HTTP request, and return one ordered result
per event. Failed/unsent events remain visible after a timeout or rejection.
HTTP acceptance means durable ingestion, not completed projection or symbolication.
Deployments and dedicated check-ins retain their single-request behavior.
Request correlation (0.5.0+)
Django (sync and async), Flask, and FastAPI integrations establish a task-local request scope. Automatic and manual captures share the local server identity; strict W3C version 00 incoming headers preserve their remote parent and flags.
import httpx
from logister import outbound_trace_context
trace = outbound_trace_context()
response = httpx.get(
"https://api.example.test/orders",
headers=trace.headers_for("https://api.example.test/orders", allowed_origins=["https://api.example.test"]),
follow_redirects=False,
)
if response.status_code >= 500:
client.capture_exception(RuntimeError("Order request failed"), context=trace.fields())
current_trace_context() returns the immutable current handle. The outbound
helper prepares headers; it does not send a request or automatically record an
HTTP span. Recheck the destination on every redirect and exclude SDK export and
token-issuer traffic. Queue propagation remains explicit.
A linked-project lookup also requires Logister 3.7+, the instance flag
LOGISTER_CROSS_PROJECT_CORRELATIONS=true, and explicit project/environment
connections under Settings → Integrations → Connected projects. Enable related
requests on both projects. A connection never grants project access.
Use the returned request handle when reporting a handled HTTP failure later.
Do not attach the most recent request to an unrelated crash or OS diagnostic.
Configure each app's own release and environment; mobile and backend releases
are independent. The backend shows exact identifier evidence and retention gaps.
See the request correlation guide.
Metadata
Release files for logister-python 0.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| logister_python-0.6.0.tar.gz | 54.7 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| logister_python-0.6.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 89.7 kB
Release files / logister_python-0.6.0.tar.gz
| Download URL | logister_python-0.6.0.tar.gz |
|---|---|
| Size | 54.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
603370a3ba4eb62cfe9aadf2bbf2ddcea3a90cc6480e06d5eff029ea6b3a607a
|
|
BLAKE2b-256 checksum How to use checksums |
3c7d99b1c8c21ec642672b814be5e48385860395c13ff35bb457789bbc112f75
|
| 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 24, 2026.
Transparency logRelease files / logister_python-0.6.0-py3-none-any.whl
| Download URL | logister_python-0.6.0-py3-none-any.whl |
|---|---|
| Size | 35.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
02da73bbc58e6f7ba70cedbe4c4487ade9397faa58bee57196a23be319ddb419
|
|
BLAKE2b-256 checksum How to use checksums |
b5220a57ff3ed623a608a10acb2ea559f12f8ad9232af689a6ccead18ddd4241
|
| 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 24, 2026.
Transparency logSecurity for logister-python 0.6.0
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for logister_python-0.6.0.tar.gz
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 24, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.