Modules#

An overview of the packages that make up peta. The API reference below is generated automatically from the source docstrings.

Core (peta.core)#

Core data models for peta.

peta.core.models.VULNERABILITY_FIELD = 'result.vulnerabilities'#

The result path advisory evidence is written to.

Declared here rather than in peta.core.providers so a package can be asked whether its advisory lookup failed without importing the provider layer.

class peta.core.models.DependencyNode(name, version_spec, installed_version=None, children=None, circular=None, source=None, retrieved_at=None, freshness=None, resolution_failure=None, *, selected_version=None, state='satisfied', conflict_reason=None)[source]#

Bases: object

A node in a package’s recursive dependency tree.

Initialize a DependencyNode.

Explicit state takes precedence over the deprecated circular constructor argument.

Parameters:
  • name (str)

  • version_spec (str)

  • installed_version (str | None)

  • children (list[DependencyNode] | None)

  • circular (bool | None)

  • source (str | None)

  • retrieved_at (str | None)

  • freshness (Freshness | None)

  • resolution_failure (DependencyResolutionFailure | None)

  • selected_version (str | None)

  • state (Literal['satisfied', 'conflicting', 'unresolved', 'circular', 'depth_limited'])

  • conflict_reason (Literal['version', 'target'] | None)

installed_version: str | None = None#

Deprecated compatibility alias for selected_version.

freshness: Freshness | None = None#

the source, or peta’s cache.

Carried per node because a tree can be assembled from a mix — some dependencies served from cache, others fetched — so one figure for the whole command would be a fiction.

Type:

Where this node’s metadata came from

property circular: bool#

Whether this is a cycle leaf (deprecated; use state).

class peta.core.models.DependencyResolutionFailure(source, state, reason, retrieved_at, freshness=None)[source]#

Bases: object

A failed or unavailable transitive dependency lookup.

Parameters:
  • source (str)

  • state (Literal['empty', 'unavailable', 'unsupported', 'failed'])

  • reason (str)

  • retrieved_at (str | None)

  • freshness (Freshness | None)

retrieved_at: str | None#

When the source answered, or None if it was never contacted.

Required but nullable, so a failure always states whether a retrieval happened at all rather than leaving it to a forgotten default. An offline miss deliberately makes no request, and claiming a retrieval time for one would make the provenance misleading.

freshness: Freshness | None = None#

Where the answer came from, when there was one.

An empty result is a completed retrieval — the source was asked and holds nothing — so it reports its origin like any other completed lookup. A failure or an offline refusal has no origin to report.

class peta.core.models.EnrichmentFailure(source, reason, field)[source]#

Bases: object

A non-fatal failure from an optional metadata source.

Parameters:
  • source (str)

  • reason (str)

  • field (str | None)

field: str | None#

The result path the failed source would have written to.

Required but nullable, so a failure always states what is missing — including stating explicitly that it cannot be attributed to a result field, rather than leaving that to a forgotten default.

class peta.core.models.PackageInfo(name, version, source, summary=None, author=None, author_email=None, maintainer=None, license=None, python_requires=None, homepage=None, project_urls=<factory>, dependencies=<factory>, classifiers=<factory>, keywords=<factory>, files=None, vulnerabilities=<factory>, download_count=None, dependent_count=None, license_source=None, enrichment_failures=<factory>, enrichment_conflicts=<factory>, provider_warnings=<factory>, retrieved_at=None, freshness=None, enrichment_sources=<factory>)[source]#

Bases: object

Package metadata from a local installation or PyPI.

Parameters:
freshness: Freshness | None = None#

the source, or peta’s cache.

None for a package read from the installed environment, which has no retrieval to be fresh or stale relative to.

Type:

Where the metadata came from

property vulnerabilities_unknown: bool#

Whether the advisory lookup failed, leaving the count unknown.

Derived from the failed field rather than a provider name, so an alternate advisory source is never reported as a clean zero.

Returns:

True when a source that supplies advisories failed.

class peta.core.models.ProviderConflict(field, kept, discarded)[source]#

Bases: object

Two providers offered different evidence for the same field.

Both sources are named so the disagreement stays visible; kept records which one the deterministic merge order selected.

Parameters:
property description: str#

Human-readable summary of the disagreement.

Returns:

The single-source conflict reason used by every output renderer.

class peta.core.models.ProviderWarning(source, code, message)[source]#

Bases: object

An advisory message a provider returned alongside its evidence.

Parameters:
class peta.core.models.Vulnerability(id, aliases, summary, fixed_in, severity=None)[source]#

Bases: object

A known security vulnerability for a package.

Parameters:

Typed models for peta’s versioned output contract.

peta.core.output.SOURCE_STATES: frozenset[SourceState] = frozenset({'empty', 'failed', 'skipped', 'success', 'unavailable', 'unsupported'})#

Every documented source state, as a runtime-checkable set.

Lets a runtime-supplied state be checked against the schema before it reaches the envelope, since an annotation alone does not stop one. Kept in step with SourceState by test_source_states_match_the_alias.

peta.core.output.TARGET_ENVIRONMENT_KEY = 'target_environment'#

How a command hands make_envelope() its resolved target.

Transport only. It travels in the arguments mapping because that is the one channel every command already threads through to the envelope, and make_envelope() removes it again before serialization: arguments is the contract’s record of the CLI invocation, so publishing the environment there as well would both duplicate query.target_environment and put an undocumented nested object in front of exact consumers.

class peta.core.output.OutputEnvelope(query, status, result, generated_at, sources=<factory>, warnings=<factory>, errors=<factory>, schema_version='2', peta_version='0.3.0')[source]#

Bases: object

Stable top-level shape for machine-readable output.

Parameters:
to_dict()[source]#

Return a JSON-compatible mapping without absent optional fields.

Returns:

The serialized contract mapping.

Return type:

dict[str, object]

class peta.core.output.OutputMessage(code, message, source=None)[source]#

Bases: object

A structured warning or error.

Credentials are stripped from the message on construction. This is the one channel every diagnostic reaches the user through — fatal errors and per-source warnings alike — so redacting here covers the ones peta builds today and the ones added later, which redacting at each call site would not. Result fields are deliberately left alone: a query string a package declared is metadata to report, not a secret to hide.

Parameters:
  • code (MessageCode)

  • message (str)

  • source (str | None)

class peta.core.output.OutputQuery(command, arguments, target_environment)[source]#

Bases: object

The command invocation represented by an output envelope.

Parameters:
class peta.core.output.SourceRecord(name, state, target=None, retrieved_at=None, reason=None, freshness=None, fields=<factory>)[source]#

Bases: object

Retrieval state for one metadata source.

Parameters:
  • name (str)

  • state (SourceState)

  • target (str | None)

  • retrieved_at (str | None)

  • reason (str | None)

  • freshness (Freshness | None)

  • fields (list[str])

freshness: Freshness | None = None#

Whether this record’s data came from the source or from peta’s cache.

Absent when the question does not apply — a source read from the local environment, or one that never completed a retrieval.

class peta.core.output.TargetEnvironment(implementation, python_version, platform, interpreter=None, paths=(), markers=<factory>)[source]#

Bases: object

Interpreter and platform used to evaluate the query.

Parameters:
classmethod current()[source]#

Describe the current runtime.

markers is populated even though no target was named: an untargeted run still evaluates dependency markers against the running interpreter, and leaving the field empty would hide from consumers which values actually decided what the result contains.

Returns:

The active interpreter and operating-system platform.

Return type:

TargetEnvironment

peta.core.output.make_envelope(command, *, arguments, status, result, sources=None, warnings=None, errors=None, generated_at=None)[source]#

Build an output envelope using the current runtime as the target.

Returns:

A populated, typed output envelope.

Parameters:
Return type:

OutputEnvelope

peta.core.output.utc_from(timestamp)[source]#

Format a Unix timestamp in the same RFC 3339 form as utc_now().

Lets a response served from cache report when it was actually retrieved rather than when it was replayed.

Parameters:
  • timestamp – Seconds since the epoch.

  • timestamp (float)

Returns:

A second-precision UTC timestamp ending in Z.

Return type:

str

peta.core.output.utc_now()[source]#

Return the current UTC time in RFC 3339 form.

Returns:

A second-precision UTC timestamp ending in Z.

Return type:

str

The one outbound HTTP client every peta source shares.

Each source used to call the top-level httpx.get/httpx.post helpers, which build a throwaway client per call. A dependency tree or a comparison therefore paid for a fresh connection and TLS handshake on every request to the same host. Routing every request through a single pooled client lets them reuse connections, and leaves one place for the caching and concurrency layers to wrap instead of four call sites in three modules.

Only transport lives here. Status handling, decoding, and validation stay with each source, because they map failures onto different error types: a PyPI lookup is fatal (NetworkError) while an enrichment lookup is not (EnrichmentError).

Caching also lives here rather than in each source, so one implementation of “is the stored copy still good, and if not can the source just confirm it” serves every URL peta fetches. Whether a given URL is cacheable at all, and for how long, stays with the caller: see peta.core.cache.

peta.core.http.DEFAULT_TIMEOUT = 10.0#

Per-request timeout, in seconds, applied to every outbound request.

peta.core.http.MAX_RESPONSE_BYTES = 67108864#

Largest response accepted from an untrusted source, in bytes.

Sized against real PyPI data, not a round guess: grpcio’s JSON document is already 9 MiB and its Simple API page 6.7 MiB, and both grow with every release. A 10 MiB cap was 87% used by one popular package. This one leaves room for years of growth while still bounding what a hostile index can make peta hold in memory.

peta.core.http.USER_AGENT = 'peta/0.3.0'#

Identifies peta to the APIs it queries, as their usage guidelines ask.

class peta.core.http.Fetched(response, provenance, cache_key=None)[source]#

Bases: object

A response, where it came from, and whether it may still be cached.

Parameters:
  • response (Response)

  • provenance (Provenance)

  • cache_key (str | None)

cache_key: str | None = None#

Set only for a live response the caller may still choose to keep.

None for anything already stored, or for a response that must never be stored, so keep() is safe to call unconditionally.

exception peta.core.http.OfflineError(url)[source]#

Bases: Exception

Raised when offline mode is on and the cache cannot answer.

Distinct from NetworkError: nothing went wrong, peta was told not to use the network and does not hold the answer. Conflating the two would tell a user to check their connection when the fix is to drop --offline or warm the cache.

Name the request that could not be served offline.

The URL is redacted here rather than at each raise site, so no future caller can leak a credential by forgetting to. Libraries.io takes its API key in the query string, and this message is surfaced to users: for a fatal lookup it becomes the offline_unavailable error in the envelope, and for an optional one it survives as the __cause__ of the source failure, where any traceback would print it.

Parameters:

url (str)

Return type:

None

exception peta.core.http.ResponseTooLargeError(message, *, request=None)[source]#

Bases: TransportPolicyError

Raised when an untrusted source exceeds the response-size limit.

Parameters:
  • message (str)

  • request (Request | None)

Return type:

None

exception peta.core.http.TransportPolicyError(message, *, request=None)[source]#

Bases: RequestError

A request peta refused, or could not start, before any reply existed.

Derived from httpx.RequestError so that these checks join the contract every source already handles. Each one maps a failed request onto its own error type by catching httpx.RequestError; an exception outside that hierarchy reaches the CLI unhandled, which prints a traceback and no message at all. A request peta declined to send has failed in exactly the sense those handlers already describe.

Parameters:
  • message (str)

  • request (Request | None)

Return type:

None

exception peta.core.http.UnsafeURLError(message, *, request=None)[source]#

Bases: TransportPolicyError

Raised before a request to a non-HTTPS URL can be sent.

Parameters:
  • message (str)

  • request (Request | None)

Return type:

None

exception peta.core.http.UnsupportedEncodingError(message, *, request=None)[source]#

Bases: TransportPolicyError

Raised when a response uses a content encoding peta did not ask for.

Parameters:
  • message (str)

  • request (Request | None)

Return type:

None

peta.core.http.client()[source]#

Return the process-wide pooled client, building it on first use.

Built on demand rather than at import time because constructing a client loads the system CA bundle, which costs tens of milliseconds — a price peta files and peta info --local should not pay to import a module they never send a request through.

Returns:

The shared httpx.Client.

Return type:

Client

peta.core.http.get(url, *, params=None, headers=None, ttl=None, scope='')[source]#

Send a GET request, using the cache when the caller allows it.

A transport-level httpx.RequestError propagates untouched, and an OfflineError propagates when offline mode is on and the cache cannot answer; each source maps them onto its own error type.

A live result is not cached until the caller confirms the body was usable by passing it to keep().

Parameters:
  • url – The absolute URL to request.

  • params – Optional query-string parameters.

  • headers – Extra request headers merged onto the shared client’s defaults, for content negotiation or similar per-source needs.

  • ttl – How long a stored response stays usable, in seconds. None bypasses the cache entirely, for a URL whose answer should never be reused.

  • scope – Distinguishes consumers that fetch the same URL but validate different parts of it, so neither replays a body the other accepted. See peta.core.cache.key_for().

  • url (str)

  • params (dict[str, str] | None)

  • headers (dict[str, str] | None)

  • ttl (int | None)

  • scope (str)

Returns:

The response and where it came from.

Return type:

Fetched

peta.core.http.keep(fetched)[source]#

Store a live response the caller has confirmed it could actually use.

Storing on arrival would cache a body the source-specific decoder then rejects — a 200 carrying an error page, or truncated JSON — and every later request would replay that same unusable response until the TTL expired, up to a month for pinned metadata. A transient upstream fault would become a persistent one.

So nothing is written until the caller has decoded and validated the body and says so by calling this. Failing to call it means the response is simply not cached, which costs a refetch; the opposite default would poison the cache.

Parameters:
  • fetched – The result to store. Ignored unless it is a live 200 still carrying a cache key, so callers need not check.

  • fetched (Fetched)

Return type:

None

peta.core.http.post(url, *, json)[source]#

Send a JSON POST request over the shared client.

Never cached: a POST body carries the question, and peta’s only POST source is an advisory query whose request shape is about to change with batching. Offline mode still refuses it rather than pretending.

A transport-level httpx.RequestError propagates untouched, and an OfflineError propagates when offline mode is on.

Parameters:
  • url – The absolute URL to request.

  • json – The request body, serialized as JSON.

  • url (str)

  • json (dict[str, object])

Returns:

The response and where it came from, always live.

Return type:

Fetched

On-disk cache for successful responses, and the settings that govern it.

Peta re-asks the same questions constantly: a dependency tree resolves the same transitive packages, and a pinned release’s metadata cannot change once it is published. Caching those answers turns a repeat query into a local read, and makes working without a network possible rather than merely degraded.

Only 200 responses are stored. An error is a fact about right now, not about the package, so caching one would turn a transient outage into a persistent wrong answer.

How long an entry stays usable is the caller’s decision, not this module’s: the code building a URL is the only code that knows what kind of answer it asked for. This module supplies the vocabulary — LATEST, DAILY — and each source picks from it.

peta.core.cache.DAILY = 21600#

Download and dependent counts are recomputed about once a day.

Shorter than a day so a query late in the cycle does not serve a count from two refreshes ago.

peta.core.cache.FRESHNESS_VALUES: frozenset[Freshness] = frozenset({'cached', 'live', 'revalidated'})#

Every documented freshness value, as a runtime-checkable set.

Kept in step with Freshness by test_freshness_values_match_the_alias.

peta.core.cache.LATEST = 3600#

A package’s metadata response, whether a version was pinned or not.

It is tempting to keep a pinned release for far longer, since its metadata is settled once published. The response is not: PyPI’s package payload also carries a vulnerabilities array, and advisories are published against releases long after they ship. A month-long entry would let info name==version --no-osv report a package as clear after PyPI had already published an advisory against it, so the whole response expires on the advisory data’s schedule rather than the metadata’s.

peta.core.cache.MAX_AGE = 2592000#

How long any entry may sit on disk, however long its TTL.

A TTL decides whether an entry may still be served; without a separate bound nothing would ever delete one, so querying many packages — a dependency tree especially — would leave every file behind for good. Comfortably longer than any TTL, so pruning never discards an entry that could still have been served, and short enough that an abandoned cache does not grow without limit.

peta.core.cache.MAX_ENTRY_BYTES = 167772160#

Largest entry file read back, in bytes; anything bigger is a miss.

Replayed entries never pass through the transport, so its response-size limit does not apply to them, and load reads and decodes a whole file at once. Entries written by earlier versions — which had no transport limit at all — and entries tampered with on disk would otherwise be read in full whatever their size. Set at two and a half times http.MAX_RESPONSE_BYTES because an entry stores its body as an escaped JSON string inside an envelope, so an entry for the largest accepted body is bigger than the body. Too small a value only costs a refetch, never a failure.

class peta.core.cache.CacheSettings(directory, offline=False, refresh=False, enabled=True)[source]#

Bases: object

How the cache behaves for this invocation.

Parameters:
offline: bool = False#

Never contact a source; serve from cache or fail.

refresh: bool = False#

Ignore stored entries and replace them with a fresh response.

enabled: bool = True#

Whether to read or write the cache at all.

class peta.core.cache.CachedResponse(status, body, headers, stored_at)[source]#

Bases: object

A stored response and the moment it was stored.

Parameters:
age(at)[source]#

Report how long ago this entry was stored.

Parameters:
  • at – The time to measure from.

  • at (float)

Returns:

Age in seconds, never negative — a clock that moved backwards reads as brand new rather than as expired long ago.

Return type:

float

is_fresh(ttl, at)[source]#

Whether this entry may still be served without asking the source.

Parameters:
  • ttl – How long entries of this kind stay usable, in seconds.

  • at – The time to judge freshness at.

  • ttl (int)

  • at (float)

Returns:

True while the entry is within ttl.

Return type:

bool

property validators: dict[str, str]#

Conditional-request headers that would revalidate this entry.

Returns:

If-None-Match/If-Modified-Since headers, empty when the source supplied no validator to revalidate against.

type peta.core.cache.Freshness = Literal['live', 'cached', 'revalidated']#

Where a response came from.

live means the source answered, cached means the entry was read from disk without contacting the source, and revalidated means the source confirmed a stored entry was still current without resending its body.

class peta.core.cache.Provenance(freshness, retrieved_at)[source]#

Bases: object

Where a source’s answer came from, and when it was retrieved.

Carried separately from the answer so a caller can report accurate provenance without knowing whether the cache was involved. retrieved_at is the moment the source answered, not the moment it was replayed: a response stored days ago and served from disk must not claim it was retrieved just now, or the provenance is worse than useless.

Parameters:
peta.core.cache.configure(*, directory=None, offline=False, refresh=False, enabled=True)[source]#

Install the settings for this process.

Held in module state rather than threaded through every signature: these are one decision made once at startup, and passing them down would mean adding parameters to every source, to the resolver, to the dependency walker, and to the provider protocol — all of which would then carry transport configuration they have no use for.

Parameters:
  • directory – Cache location; the platform default when None.

  • offline – Refuse to contact any source.

  • refresh – Replace stored entries with fresh responses.

  • enabled – Whether the cache is consulted at all.

  • directory (Path | None)

  • offline (bool)

  • refresh (bool)

  • enabled (bool)

Return type:

None

peta.core.cache.default_directory()[source]#

Locate the cache directory for the current platform.

Resolved by hand rather than through platformdirs: it is four branches, and peta’s runtime dependencies are deliberately few.

Returns:

The per-user cache directory for peta.

Return type:

Path

peta.core.cache.entries_directory()[source]#

Locate the directory the entries themselves live in.

Returns:

The subdirectory of the configured location that peta owns.

Return type:

Path

peta.core.cache.key_for(method, url, scope='')[source]#

Derive the cache key for one request.

scope separates consumers that fetch the same URL but validate different parts of it. Nothing is stored until its caller confirms the body was usable, and “usable” means usable to that caller: one command accepting a payload whose other half is malformed must not hand it to a command that reads that other half.

Parameters:
  • method – HTTP method, case-insensitive.

  • url – The full request URL, credentials included; they are redacted before hashing.

  • scope – Which consumer’s view of the response this entry holds.

  • method (str)

  • url (str)

  • scope (str)

Returns:

A hex digest usable as a file name.

Return type:

str

peta.core.cache.load(key)[source]#

Read one entry, treating any unreadable entry as absent.

A cache is disposable, so a truncated write, a partial disk, or a file from a future format must degrade to a miss. Raising here would make a corrupt byte on disk fatal to a command that could simply refetch.

Parameters:
  • key – The cache key.

  • key (str)

Returns:

The stored entry, or None on a miss or a damaged entry.

Return type:

CachedResponse | None

peta.core.cache.now()[source]#

Return the current wall-clock time as a Unix timestamp.

Indirected through a function so tests can drive expiry from a fixed clock instead of sleeping.

Returns:

Seconds since the epoch.

Return type:

float

peta.core.cache.redacted(url)[source]#

Rewrite a URL with credential-bearing query parameters removed.

Public because the cache is not the only place a request URL is retained: an error that names the URL it could not answer would otherwise carry the credential into a message, a log, or a traceback.

Parameters:
  • url – A request URL, possibly carrying a credential.

  • url (str)

Returns:

The URL without userinfo, and with any parameter named in _CREDENTIAL_PARAMS dropped.

Return type:

str

peta.core.cache.redacted_text(value)[source]#

Redact credentials from every URL embedded in an arbitrary string.

Returns:

The original text with credentials removed from each HTTP(S) URL.

Parameters:

value (str)

Return type:

str

peta.core.cache.reset()[source]#

Discard configured settings, restoring the defaults.

Return type:

None

peta.core.cache.settings()[source]#

Return the active settings, defaulting until configure() runs.

Defaults apply so importing a source module and making a request works without the CLI having wired anything up, which is what the unit tests and any library use rely on.

Returns:

The active cache settings.

Return type:

CacheSettings

peta.core.cache.store(key, *, url, status, body, headers)[source]#

Store one successful response.

Parameters:
  • key – The cache key.

  • url – The request URL, stored with credentials redacted.

  • status – The response status.

  • body – The decoded response body.

  • headers – Response headers; only an allowlist is kept.

  • key (str)

  • url (str)

  • status (int)

  • body (str)

  • headers (dict[str, str])

Return type:

None

Running independent lookups at the same time, without losing their order.

Peta’s slow work is waiting on other people’s servers. A package’s three enrichment sources have no dependency on each other, and neither do the two packages of a comparison, yet each waited for the last: measured against the live services, roughly 44% of a package’s enrichment latency was one source idling while another was queried.

Order is not incidental here. The enrichment merge is first-writer-wins by provider order, so a conflict between two sources is resolved by which was consulted first. Results must therefore come back in the order the caller asked for them, whatever order they actually complete in.

peta.core.concurrency.MAX_WORKERS = 8#

How many lookups may be in flight from one gather() call.

Threads are not the scarce resource — sockets are — so the real bound on concurrent requests is the connection limit on the shared client in peta.core.http, which applies however many callers are running. This number keeps any single fan-out from creating threads without limit, and is set above peta’s widest fan-out (three enrichment sources, two compared packages) so the bound never shapes ordinary work.

peta.core.concurrency.gather(tasks)[source]#

Run independent tasks concurrently and return results in input order.

Nothing is threaded for a single task, so the common case — one package, one source — costs no pool, no threads, and no change to a traceback.

A task that raises propagates, and because results are consumed in input order the exception surfaced is the first task’s, not the first to fail. That keeps error reporting identical to running them one after another. Tasks after a failing one still run, since they were already started; peta’s callers treat that as a wasted request rather than a problem.

Parameters:
  • tasks – Callables to run. Each must be independent of the others’ results, and safe to call from another thread.

  • tasks (Sequence[Callable[[], _T]])

Returns:

One result per task, in the order the tasks were given.

Return type:

list[_T]

Local package metadata fetcher using importlib.metadata.

peta.core.local.METADATA_READ_ERRORS: tuple[type[Exception], ...] = (<class 'OSError'>, <class 'RuntimeError'>, <class 'ValueError'>, <class 'zipfile.BadZipFile'>, <class 'zipfile.LargeZipFile'>, <class '_lzma.LZMAError'>, <class 'zlib.error'>, <class 'compression.zstd.ZstdError'>)#

What reading an unreadable metadata file or archive member can raise.

A zipped member can also fail to decompress: RuntimeError when it is encrypted, NotImplementedError (a RuntimeError) for a compression method zipfile lacks, and LZMAError, zlib.error or ZstdError for a corrupt stream.

exception peta.core.local.InvalidTargetError[source]#

Bases: ValueError

Raised when an explicit local-environment target cannot be used.

class peta.core.local.LocalTarget(paths, interpreter, marker_environment, prefix=None)[source]#

Bases: object

Metadata paths and marker values used for a local inspection.

Parameters:
prefix: str | None = None#

The environment’s installation prefix, when it is known.

Bounds which files origin inspection may read: a RECORD legitimately lists console scripts outside site-packages but inside the prefix. Unknown for a --path-only target, which names metadata directories rather than an environment.

classmethod create(python=None, paths=(), python_version=None, platform=None)[source]#

Build a target without implicitly discovering a virtual environment.

Returns:

The selected metadata paths and marker environment.

Raises:

InvalidTargetError – If a path or interpreter cannot be inspected.

Parameters:
  • python (str | None)

  • paths (tuple[str, ...])

  • python_version (str | None)

  • platform (str | None)

Return type:

LocalTarget

describe()[source]#

Return a concise human-readable target description.

Includes the marker values that decide which dependencies a tree contains, since with --path alone they come from the running interpreter rather than from anything named on the command line.

Returns:

A one-line target summary.

Return type:

str

output_environment()[source]#

Return the target details stored in machine-readable output.

Returns:

JSON-compatible target details.

Return type:

dict[str, object]

exception peta.core.local.PackageNotFoundError(name)[source]#

Bases: Exception

Raised when a package is not installed locally.

Store the missing package name.

Parameters:

name (str)

Return type:

None

peta.core.local.find_distribution(name, *, target=None, name_of=None)[source]#

Find an installed distribution in the selected environment.

Parameters:
  • name – Package name to look up.

  • target – The environment to search; the running one when None.

  • name_of – How to read each candidate’s Name when the target’s search path is enumerated; the stdlib parser when None. A caller that vets metadata files before reading them passes its own, so no candidate is read unvetted.

  • name (str)

  • target (LocalTarget | None)

  • name_of (Callable[[importlib_metadata.Distribution], str | None] | None)

Returns:

The first matching distribution on the target’s search path.

Raises:

PackageNotFoundError – If the package is not installed there.

Return type:

importlib_metadata.Distribution

peta.core.local.get_package(name, *, target=None)[source]#

Get metadata for a locally installed package.

Parameters:
Returns:

A PackageInfo with source="local".

Return type:

PackageInfo

peta.core.local.legacy_installed_files(dist, *, skip_missing=True, listing=None)[source]#

Read a legacy .egg-info’s installed-files.txt, as Python 3.12 does.

Python 3.11’s importlib.metadata never reads this file and falls back to SOURCES.txt, which lists the project’s source tree rather than what was installed. Reading it here, relative to the search-path root and skipping files that are gone, keeps files the same on every supported version.

A non-empty RECORD still wins, as it does in the stdlib, so a .dist-info that also carries a stray installed-files.txt keeps its authoritative listing.

skip_missing=False keeps entries whose file is gone, for callers that report a missing file rather than hide it.

listing is the file’s text for a caller that has already read and vetted it, and has found no RECORD: nothing is read a second time, so nothing swapped in meanwhile is read unchecked.

Returns:

The installed files, or None when dist has no such listing.

Parameters:
Return type:

list[str] | None

PyPI JSON API client for remote package metadata.

exception peta.core.remote.NetworkError(message)[source]#

Bases: Exception

Raised when a network request fails.

Wrap a network failure message.

Parameters:

message (str)

Return type:

None

exception peta.core.remote.PackageNotFoundError(name, version=None)[source]#

Bases: Exception

Raised when a package is not found on PyPI.

Store the missing package name and optional version.

Parameters:
  • name (str)

  • version (str | None)

Return type:

None

class peta.core.remote.PyPIInfo[source]#

Bases: TypedDict

The info object of a PyPI package payload.

class peta.core.remote.PyPIReleaseFile[source]#

Bases: TypedDict

A single distribution file within a release entry.

class peta.core.remote.PyPIResponse[source]#

Bases: TypedDict

The top-level PyPI JSON payload for a package.

class peta.core.remote.PyPIVulnerability[source]#

Bases: TypedDict

A single entry of the vulnerabilities array.

peta.core.remote.get_package(name, version=None)[source]#

Get metadata for a package from PyPI.

Parameters:
  • name – Package name to look up.

  • version – Optional specific version; if None the latest is fetched.

  • name (str)

  • version (str | None)

Returns:

A PackageInfo with source="remote". Not-found and network failures propagate from _fetch().

Return type:

PackageInfo

peta.core.remote.get_package_matching(name, specifier, marker_environment)[source]#

Get the newest PyPI release satisfying a dependency requirement.

The project endpoint lists releases but only a version endpoint supplies historical Requires-Python and dependency metadata, so candidates are checked newest-first until one also supports the requested target.

Returns:

The newest compatible package, or the current release when no release satisfies the requirement so callers can surface a conflict.

Raises:

PackageNotFoundError – If a matching current release has no usable files.

Parameters:
  • name (str)

  • specifier (SpecifierSet)

  • marker_environment (dict[str, str] | None)

Return type:

PackageInfo

Shared package-resolution logic for the info and compare commands.

peta.core.resolve.not_found_source(exc)[source]#

Name the provider that reported a package as missing.

Returns:

"local" for the installed environment, otherwise "pypi".

Parameters:

exc (BaseException)

Return type:

str

peta.core.resolve.parse_package_arg(package)[source]#

Split a name or name==version argument.

Returns:

A (name, version) tuple; version is None when unspecified.

Raises:

typer.BadParameter – If the package name is empty, or the argument contains == but the version text after it is empty.

Parameters:

package (str)

Return type:

tuple[str, str | None]

peta.core.resolve.resolve_package(package, *, local, remote, target=None, specifier=None, select_compatible=False)[source]#

Resolve a package argument to its metadata.

Checks the local environment first and falls back to PyPI, unless --local/--remote force a source or a name==version specifier is given (which always queries PyPI and rejects --local).

Returns:

The resolved package metadata.

Raises:

typer.BadParameter – If source-selection options conflict.

Parameters:
Return type:

PackageInfo

Recursive dependency-tree construction and deps --why path lookup.

peta.core.deptree.build_tree(name, *, local, remote, target=None, max_depth=10, extras=())[source]#

Recursively resolve name and its dependency tree.

Only the root resolution can raise (propagated from peta.core.resolve.resolve_package()); unresolvable transitive dependencies become leaf nodes with selected_version=None instead.

Returns:

The root DependencyNode, with children expanded recursively.

Parameters:
Return type:

DependencyNode

peta.core.deptree.find_why(root, target)[source]#

Find every root-to-target path in the dependency tree.

Returns:

A list of name paths (each starting with root.name); empty if target is not present anywhere in the tree.

Parameters:
Return type:

list[list[str]]

Best-effort enrichment orchestration over peta.core.providers.

Providers are consulted independently and merged deterministically, so one source failing can never discard another’s evidence, and two sources answering the same field disagree loudly rather than silently overwriting each other.

peta.core.enrich.enrich(pkg, *, no_osv, no_stats, providers=None)[source]#

Best-effort enrich a package from every configured provider.

Never raises: providers report failure as a result state, so an enrichment problem stays visible in the output without affecting exit codes.

Parameters:
  • pkg – The resolved package to enrich.

  • no_osv – Skip the vulnerability provider group.

  • no_stats – Skip the usage-statistics provider group.

  • providers – The providers to consult, in conflict-priority order. Injectable so callers and tests can substitute sources; defaults to DEFAULT_PROVIDERS. Resolved at call time rather than bound as a default argument, so the registry stays swappable.

  • pkg (PackageInfo)

  • no_osv (bool)

  • no_stats (bool)

  • providers (Sequence[EnrichmentProvider] | None)

Returns:

The enriched package metadata.

Return type:

PackageInfo

The internal seam between enrichment orchestration and concrete sources.

A provider answers exactly one Capability for one package and reports the outcome as a ProviderResult, so orchestration never needs to know which HTTP service produced the evidence, how it failed, or how it is configured.

This seam is deliberately internal. It is not a third-party plugin API, and it carries no compatibility guarantees outside this package.

peta.core.providers.base.CAPABILITY_FIELDS: dict[Capability, str] = {'dependent_count': 'result.dependent_count', 'download_count': 'result.download_count', 'vulnerabilities': 'result.vulnerabilities'}#

The output-contract fields path each capability writes to.

peta.core.providers.base.CAPABILITY_GROUPS: dict[Capability, ProviderGroup] = {'dependent_count': 'stats', 'download_count': 'stats', 'vulnerabilities': 'vulnerabilities'}#

The opt-out family each capability falls under.

Derived from the capability rather than declared per provider: the CLI’s opt-out flags select kinds of data, so a provider cannot place itself in a group that does not match what it supplies and thereby escape its own flag.

type peta.core.providers.base.Capability = Literal['vulnerabilities', 'download_count', 'dependent_count']#

The single result field a provider contributes evidence for.

class peta.core.providers.base.CountEvidence(count)[source]#

Bases: object

A single scalar count, such as downloads or dependents.

Parameters:

count (int)

property is_empty: bool#

Whether this counts as no answer.

Always False: a count of zero is a real value, and a source with no count to give returns no evidence at all.

Returns:

False.

class peta.core.providers.base.EnrichmentProvider(*args, **kwargs)[source]#

Bases: Protocol

An optional metadata source that enriches one package.

Implementations expose their identity as name and the single result field they contribute as capability; the opt-out family follows from the capability via CAPABILITY_GROUPS.

Implementations must not raise: every outcome, including failure and missing configuration, is reported as a ProviderResult so one source can never abort another.

The member bodies below raise rather than using a bare ...: a protocol body is never executed, and an explicit raise says so without leaving a statement that has no effect.

property name: str#

Identify the source in provenance and warnings.

Returns:

The provider’s stable source name.

Raises:

NotImplementedError – Always; implementations supply the value.

property capability: Capability#

Name the single result field this provider contributes.

Returns:

The provider’s capability.

Raises:

NotImplementedError – Always; implementations supply the value.

fetch(pkg)[source]#

Look up this provider’s capability for pkg.

Parameters:
Returns:

The outcome, whether evidence, an empty answer, or a failure.

Raises:

NotImplementedError – Always; implementations perform the lookup.

Return type:

ProviderResult

type peta.core.providers.base.Evidence = VulnerabilityEvidence | CountEvidence#

Typed payload a provider returns, carried separately from its provenance.

type peta.core.providers.base.ProviderGroup = Literal['vulnerabilities', 'stats']#

The --no-osv / --no-stats family a capability belongs to.

class peta.core.providers.base.ProviderResult(provider, capability, state, subject, retrieved_at=None, freshness=None, upstream_id=None, evidence=None, reason=None, warnings=<factory>)[source]#

Bases: object

One provider’s answer for one package, evidence and provenance together.

state reuses the output contract’s SourceState so a result maps onto a source record without a translation table.

Parameters:
property field: str | None#

Name the result path this provider contributes to.

None when the capability is not one peta knows, which an injected provider can declare at runtime despite the annotation. Such a result has no attributable field rather than an invented one.

Returns:

The output-contract field path, or None if unattributable.

class peta.core.providers.base.VulnerabilityEvidence(vulnerabilities)[source]#

Bases: object

Advisories a provider found for the subject.

Parameters:

vulnerabilities (list[Vulnerability])

property is_empty: bool#

Whether the source confirmed it has no advisories.

Returns:

True when no advisories were returned.

Adapters wrapping peta’s built-in enrichment sources as providers.

Each adapter owns its own failure and configuration handling, so orchestration neither catches source-specific exceptions nor reads source-specific settings.

peta.core.providers.builtin.DEFAULT_PROVIDERS: tuple[EnrichmentProvider, ...] = (OsvProvider(name='osv', capability='vulnerabilities'), PypiStatsProvider(name='pypistats', capability='download_count'), LibrariesIoProvider(name='libraries.io', capability='dependent_count'))#

The built-in providers, in the order orchestration consults them.

Order is the conflict tie-breaker: for a given capability the first provider to return evidence wins.

class peta.core.providers.builtin.LibrariesIoProvider(name='libraries.io', capability='dependent_count')[source]#

Bases: object

Look up a package’s dependent count on libraries.io.

Parameters:
fetch(pkg)[source]#

Query libraries.io for the package’s dependent count.

Reports unavailable without making a request when no API key is configured, keeping “not set up” distinct from “returned nothing”.

Returns:

The dependent count, an empty or unavailable answer, or the lookup failure.

Parameters:

pkg (PackageInfo)

Return type:

ProviderResult

class peta.core.providers.builtin.OsvProvider(name='osv', capability='vulnerabilities')[source]#

Bases: object

Look up advisories for a package on OSV.dev.

Parameters:
fetch(pkg)[source]#

Query OSV for the package’s known vulnerabilities.

Returns:

The advisories found, an empty answer, or the lookup failure.

Parameters:

pkg (PackageInfo)

Return type:

ProviderResult

class peta.core.providers.builtin.PypiStatsProvider(name='pypistats', capability='download_count')[source]#

Bases: object

Look up a package’s last-month download count on pypistats.org.

Parameters:
fetch(pkg)[source]#

Query pypistats.org for the package’s recent downloads.

Returns:

The download count, an empty answer, or the lookup failure.

Parameters:

pkg (PackageInfo)

Return type:

ProviderResult

OSV.dev vulnerability lookups (best-effort enrichment).

class peta.core.osv.OsvAffected[source]#

Bases: TypedDict

A single affected entry of an OSV vulnerability.

class peta.core.osv.OsvEvent[source]#

Bases: TypedDict

A single event within an affected range (only fixed is read).

class peta.core.osv.OsvRange[source]#

Bases: TypedDict

A range of affected versions.

class peta.core.osv.OsvResponse[source]#

Bases: TypedDict

The top-level OSV JSON payload for a query.

class peta.core.osv.OsvSeverity[source]#

Bases: TypedDict

A single severity entry of an OSV vulnerability (only score is read).

class peta.core.osv.OsvVuln[source]#

Bases: TypedDict

A single entry of the vulns array.

peta.core.osv.get_vulnerabilities(name, version=None)[source]#

Look up known vulnerabilities for a package on OSV.dev.

The caller may treat this as best-effort by catching EnrichmentError and retaining its source and reason.

Parameters:
  • name – Package name to query (assumed to be a PyPI package).

  • version – Optional specific version; if None all versions are queried.

  • name (str)

  • version (str | None)

Returns:

The list of vulnerabilities reported by OSV.

Return type:

list[Vulnerability]

Download and dependent count lookups (best-effort enrichment).

class peta.core.stats.LibrariesIoResponse[source]#

Bases: TypedDict

The fields peta reads from a libraries.io project JSON payload.

class peta.core.stats.PypiStatsData[source]#

Bases: TypedDict

The data object of a pypistats.org “recent” response.

class peta.core.stats.PypiStatsResponse[source]#

Bases: TypedDict

The top-level pypistats.org “recent” JSON payload.

peta.core.stats.get_dependent_count(name, *, api_key)[source]#

Look up a package’s dependent count on libraries.io.

The enrichment coordinator catches source-specific failures so they remain non-fatal while still being visible to users.

A key is required rather than optional. Deciding what “not configured” means is the provider’s job — it reports unavailable without making a request — and the previous no-key branch here had to invent a provenance for a request that never happened, which is exactly the fabrication Provenance exists to prevent.

Parameters:
  • name – Package name to query (assumed to be a PyPI package).

  • api_key – The libraries.io API key.

  • name (str)

  • api_key (str)

Returns:

The dependent count and where it came from.

Return type:

tuple[int | None, Provenance]

peta.core.stats.get_download_count(name)[source]#

Look up a package’s last-month download count on pypistats.org.

The enrichment coordinator catches source-specific failures so they remain non-fatal while still being visible to users.

Parameters:
  • name – Package name to query (assumed to be a PyPI package).

  • name (str)

Returns:

The last-month download count, and where it came from.

Return type:

tuple[int | None, Provenance]

peta.core.stats.libraries_io_api_key()[source]#

Read the libraries.io API key from the environment.

Returns:

The LIBRARIES_IO_API_KEY value, or None if unset or empty.

Return type:

str | None

Dedup/merge of vulnerabilities from multiple sources.

peta.core.vulns.merge_vulnerabilities(existing, extra)[source]#

Merge two vulnerability lists, deduping by identity.

Two vulnerabilities are the same if they share their id or any overlapping alias. When a candidate’s identity bridges several existing entries (e.g. it carries aliases of two otherwise-unrelated entries), ALL of them are folded into one cumulative entry rather than merging into only the first match. The result prefers a non-None severity and unions aliases and fixed_in.

Parameters:
  • existing – Vulnerabilities already known (e.g. from PyPI).

  • extra – Additional vulnerabilities to merge in (e.g. from OSV).

  • existing (list[Vulnerability])

  • extra (list[Vulnerability])

Returns:

A deduped list, with existing entries first, in order.

Return type:

list[Vulnerability]

Small runtime validators for decoded external API responses.

exception peta.core.validation.EnrichmentError(source, reason, *, contacted=True)[source]#

Bases: Exception

A source-specific failure from an optional external API.

Store the failed source, safe diagnostic reason, and whether it ran.

Parameters:
  • source (str) – The source that failed.

  • reason (str) – A diagnostic to show a user. Credentials are stripped from any URL it names, here rather than at each call site: most of these reasons are str(exc) on a transport error, which quotes the request URL, and Libraries.io carries its API key in the query string.

  • contacted (bool) – Whether a request was actually made. False for a refusal that never reached the network — being offline — so the resulting record can omit a retrieval time instead of claiming one for a source nothing was sent to.

Return type:

None

exception peta.core.validation.ResponseLimitError(source, path, limit)[source]#

Bases: ResponseValidationError

Raised when a response is well formed but past one of peta’s limits.

Separate from its parent so that a refusal on size does not claim the value had the wrong type. An array of 10,001 items is an array, and reporting it as “must be an array” sends whoever debugs it to check the response shape, find it correct, and lose the trail.

Describe the source, JSON path, and the limit that was exceeded.

Parameters:
Return type:

None

exception peta.core.validation.ResponseValidationError(source, path, expected)[source]#

Bases: ValueError

Raised when an external response does not match its consumed contract.

Describe the source, JSON path, and expected value type.

Parameters:
Return type:

None

peta.core.validation.expect_int(value, *, source, path)[source]#

Require an integer while rejecting JSON booleans.

Returns:

The validated integer.

Raises:

ResponseValidationError – If the value is not a genuine integer.

Parameters:
Return type:

int

peta.core.validation.expect_list(value, *, source, path)[source]#

Require a JSON array.

Returns:

The validated list.

Raises:
Parameters:
Return type:

list[object]

peta.core.validation.expect_mapping(value, *, source, path)[source]#

Require a JSON object with string keys.

Returns:

The validated mapping.

Raises:
Parameters:
Return type:

dict[str, object]

peta.core.validation.expect_string(value, *, source, path)[source]#

Require a string value.

Returns:

The validated string.

Raises:
Parameters:
Return type:

str

peta.core.validation.json_body(response, *, source)[source]#

Decode a JSON response, refusing one shaped to exhaust the process.

Returns:

The decoded body.

Raises:

ResponseLimitError – If the body nests past MAX_JSON_DEPTH, holds more than MAX_JSON_VALUES values or MAX_JSON_CONTAINERS objects and arrays, has any array or object with more than MAX_COLLECTION_ITEMS items, or any string longer than MAX_STRING_LENGTH.

Parameters:
  • response (httpx.Response)

  • source (str)

Return type:

object

peta.core.validation.optional_string(mapping, key, *, source, path)[source]#

Validate an optional nullable string field.

Returns:

The string, or None when the field is absent or null.

Parameters:
Return type:

str | None

peta.core.validation.optional_string_list(mapping, key, *, source, path)[source]#

Validate an optional nullable array of strings.

Returns:

The string list, or None when the field is absent or null.

Raises:

ResponseValidationError – If a present value is not a list of strings.

Parameters:
Return type:

list[str] | None

peta.core.validation.optional_string_mapping(mapping, key, *, source, path)[source]#

Validate an optional nullable string-to-string object.

Returns:

The validated mapping, or None when the field is absent or null.

Raises:

ResponseValidationError – If a present value is not a string mapping.

Parameters:
Return type:

dict[str, str] | None

CLI (peta.cli)#

Typer application and command registration.

peta.cli.app.artifacts(ctx, package, use_json=False, output_format=OutputFormat.RICH, python=None, detailed=False, provenance=False)[source]#

Show a release’s distribution files, compatibility, and provenance.

Parameters:
  • ctx (Context)

  • package (Annotated[str, <typer.models.ArgumentInfo object at 0x7f49bb0f1e50>])

  • use_json (Annotated[bool, <typer.models.OptionInfo object at 0x7f49bb0f1f90>])

  • output_format (Annotated[OutputFormat, <typer.models.OptionInfo object at 0x7f49bb0f20d0>])

  • python (Annotated[str | None, <typer.models.OptionInfo object at 0x7f49bb0f2210>])

  • detailed (Annotated[bool, <typer.models.OptionInfo object at 0x7f49bb0f2350>])

  • provenance (Annotated[bool, <typer.models.OptionInfo object at 0x7f49bb0f2490>])

Return type:

None

peta.cli.app.compare(ctx, a, b, use_json=False, output_format=OutputFormat.RICH, local=False, remote=False, no_osv=False, no_stats=False, python=None, path=None, changes_only=False, artifacts=False)[source]#

Compare two packages, or two releases of one, and explain what changed.

Parameters:
  • ctx (Context)

  • a (Annotated[str, <typer.models.ArgumentInfo object at 0x7f49bb0f34d0>])

  • b (Annotated[str, <typer.models.ArgumentInfo object at 0x7f49bb0f3610>])

  • use_json (Annotated[bool, <typer.models.OptionInfo object at 0x7f49bb0f3750>])

  • output_format (Annotated[OutputFormat, <typer.models.OptionInfo object at 0x7f49bb0f3890>])

  • local (Annotated[bool, <typer.models.OptionInfo object at 0x7f49bb0f39d0>])

  • remote (Annotated[bool, <typer.models.OptionInfo object at 0x7f49bb0f3b10>])

  • no_osv (Annotated[bool, <typer.models.OptionInfo object at 0x7f49bb0f3c50>])

  • no_stats (Annotated[bool, <typer.models.OptionInfo object at 0x7f49bb0f3d90>])

  • python (Annotated[str | None, <typer.models.OptionInfo object at 0x7f49bb0f3ed0>])

  • path (Annotated[list[str] | None, <typer.models.OptionInfo object at 0x7f49ba594050>])

  • changes_only (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba594190>])

  • artifacts (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba5942d0>])

Return type:

None

peta.cli.app.deps(ctx, package, use_json=False, output_format=OutputFormat.RICH, local=False, remote=False, why=None, depth=10, python=None, python_version=None, platform=None, extra=None, path=None)[source]#

Show a package’s recursive dependency tree.

Parameters:
  • ctx (Context)

  • package (Annotated[str, <typer.models.ArgumentInfo object at 0x7f49ba595310>])

  • use_json (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba595450>])

  • output_format (Annotated[OutputFormat, <typer.models.OptionInfo object at 0x7f49ba595590>])

  • local (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba5956d0>])

  • remote (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba595810>])

  • why (Annotated[str | None, <typer.models.OptionInfo object at 0x7f49ba595950>])

  • depth (Annotated[int, <typer.models.OptionInfo object at 0x7f49ba595a90>])

  • python (Annotated[str | None, <typer.models.OptionInfo object at 0x7f49ba595bd0>])

  • python_version (Annotated[str | None, <typer.models.OptionInfo object at 0x7f49ba595d10>])

  • platform (Annotated[str | None, <typer.models.OptionInfo object at 0x7f49ba595e50>])

  • extra (Annotated[list[str] | None, <typer.models.OptionInfo object at 0x7f49ba595f90>])

  • path (Annotated[list[str] | None, <typer.models.OptionInfo object at 0x7f49ba5960d0>])

Return type:

None

peta.cli.app.files(ctx, package, use_json=False, output_format=OutputFormat.RICH, python=None, path=None)[source]#

List files installed by a local package.

Parameters:
  • ctx (Context)

  • package (Annotated[str, <typer.models.ArgumentInfo object at 0x7f49ba596850>])

  • use_json (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba596990>])

  • output_format (Annotated[OutputFormat, <typer.models.OptionInfo object at 0x7f49ba596ad0>])

  • python (Annotated[str | None, <typer.models.OptionInfo object at 0x7f49ba596c10>])

  • path (Annotated[list[str] | None, <typer.models.OptionInfo object at 0x7f49ba596d50>])

Return type:

None

peta.cli.app.info(ctx, package, use_json=False, output_format=OutputFormat.RICH, local=False, remote=False, no_osv=False, no_stats=False, python=None, path=None)[source]#

Show detailed package metadata.

Parameters:
  • ctx (Context)

  • package (Annotated[str, <typer.models.ArgumentInfo object at 0x7f49ba5979d0>])

  • use_json (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba597b10>])

  • output_format (Annotated[OutputFormat, <typer.models.OptionInfo object at 0x7f49ba597c50>])

  • local (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba597d90>])

  • remote (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba597ed0>])

  • no_osv (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba5dc050>])

  • no_stats (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba5dc190>])

  • python (Annotated[str | None, <typer.models.OptionInfo object at 0x7f49ba5dc2d0>])

  • path (Annotated[list[str] | None, <typer.models.OptionInfo object at 0x7f49ba5dc410>])

Return type:

None

peta.cli.app.main(ctx, _version=False, no_color=False, offline=False, refresh=False, cache_dir=None)[source]#

Human-friendly Python package metadata viewer.

Parameters:
  • ctx (Context)

  • _version (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba5dcb90>])

  • no_color (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba5dccd0>])

  • offline (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba5dce10>])

  • refresh (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba5dcf50>])

  • cache_dir (Annotated[Path | None, <typer.models.OptionInfo object at 0x7f49ba5dd090>])

Return type:

None

peta.cli.app.origin(ctx, package, use_json=False, output_format=OutputFormat.RICH, python=None, path=None, verify=False)[source]#

Show how a local package was installed and whether its files match.

Parameters:
  • ctx (Context)

  • package (Annotated[str, <typer.models.ArgumentInfo object at 0x7f49ba5dd950>])

  • use_json (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba5dda90>])

  • output_format (Annotated[OutputFormat, <typer.models.OptionInfo object at 0x7f49ba5ddbd0>])

  • python (Annotated[str | None, <typer.models.OptionInfo object at 0x7f49ba5ddd10>])

  • path (Annotated[list[str] | None, <typer.models.OptionInfo object at 0x7f49ba5dde50>])

  • verify (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba5ddf90>])

Return type:

None

peta.cli.app.run()[source]#

Entry point; peta <package> is shorthand for peta info <package>.

Return type:

None

peta.cli.app.versions(ctx, package, use_json=False, output_format=OutputFormat.RICH, limit=20)[source]#

Show published versions of a package from PyPI.

Parameters:
  • ctx (Context)

  • package (Annotated[str, <typer.models.ArgumentInfo object at 0x7f49ba5de5d0>])

  • use_json (Annotated[bool, <typer.models.OptionInfo object at 0x7f49ba5de710>])

  • output_format (Annotated[OutputFormat, <typer.models.OptionInfo object at 0x7f49ba5de850>])

  • limit (Annotated[int, <typer.models.OptionInfo object at 0x7f49ba5de990>])

Return type:

None

Shared CLI state resolved once by the root callback.

class peta.cli.state.CliState(color)[source]#

Bases: object

Options resolved once at the root callback and shared by commands.

Parameters:

color (bool)

Color resolution and Rich console rendering.

peta.cli.output.console.inline(value)[source]#

Make one untrusted value safe to place on a line of terminal output.

sanitize_terminal() keeps newlines and tabs because formatters build their layout from them, so a value inserted into that layout has to give up its own: folded to spaces, they can neither start a forged line nor shift a column.

Returns:

The value on one line, with terminal control characters removed.

Parameters:

value (object)

Return type:

str

peta.cli.output.console.render(renderable, *, color, width=100)[source]#

Render a Rich renderable to a string, with or without ANSI color.

Rendering happens in two steps so that untrusted metadata can be made inert after Rich has laid it out but before Rich turns styles into escape sequences. Sanitizing the finished string instead would have to tell peta’s own color codes from an attacker’s, which it cannot; sanitizing every renderer’s inputs instead would leave each new call site free to forget.

Markup and emoji substitution are disabled because both let metadata change which characters are printed: a [bold] or a :pile_of_poo: in a package summary is data, not an instruction. Highlighting stays on — it only colors what is already there, and peta’s output has always had it.

Returns:

ANSI-colored when color is True, plain otherwise. Has no trailing newline; callers (e.g. typer.echo) supply exactly one.

Return type:

The rendered text

Parameters:
  • renderable (RenderableType)

  • color (bool)

  • width (int)

peta.cli.output.console.resolve_color(*, no_color)[source]#

Decide whether Rich output should include color.

Precedence: an explicit --no-color flag always wins; otherwise the NO_COLOR environment variable (any non-empty value) disables color; otherwise color follows whether stdout is a terminal.

Returns:

True if color should be rendered, False otherwise.

Parameters:

no_color (bool)

Return type:

bool

peta.cli.output.console.sanitize_terminal(value)[source]#

Make untrusted text inert when it is written to a terminal.

Newlines and tabs stay, because the formatters use them; every other C0 or C1 control character is dropped, which is what stops ANSI escape and OSC-8 hyperlink injection from package metadata. Dropping rather than escaping them is deliberate: Rich measures a control character as occupying no cells, so a visible stand-in would be one cell wider than the space Rich reserved and would push a table’s border out of line. Nothing is lost that a reader needs — the JSON output still carries the original bytes.

Returns:

The value with terminal control characters removed.

Parameters:

value (str)

Return type:

str

Rich text output formatters.

peta.cli.output.tables.render_artifacts(release, *, color, detailed=False)[source]#

Render a release’s artifacts as a Rich summary, optionally per file.

Returns:

The summary panel, the file table when asked for, and any notes.

Parameters:
  • release (ReleaseArtifacts)

  • color (bool)

  • detailed (bool)

Return type:

str

peta.cli.output.tables.render_compare(a, b, diff=None, *, color, changes_only=False)[source]#

Render two PackageInfo objects as a side-by-side Rich table.

The grouped semantic changes follow the table; changes_only drops the table and shows only them.

Returns:

The comparison rendered as text.

Parameters:
Return type:

str

peta.cli.output.tables.render_dep_tree(node, *, color)[source]#

Render a recursive dependency tree as a Rich tree string.

Returns:

The dependency tree rendered as text.

Parameters:
Return type:

str

peta.cli.output.tables.render_files(pkg, *, color)[source]#

Render a package’s file listing as a string.

color is accepted for signature parity with the other renderers but unused: this output is plain lines, not a Rich renderable.

Returns:

The file listing rendered as text.

Parameters:
Return type:

str

peta.cli.output.tables.render_info(pkg, *, color)[source]#

Render PackageInfo as a Rich panel string.

Returns:

The formatted panel text, with any vulnerability block appended.

Parameters:
Return type:

str

peta.cli.output.tables.render_origin(installation, *, color)[source]#

Render how a distribution was installed as a Rich summary.

Returns:

The summary panel, any flagged files, and explanatory notes.

Parameters:
  • installation (Installation)

  • color (bool)

Return type:

str

peta.cli.output.tables.render_versions(name, versions, *, color)[source]#

Render a version list as a Rich table string.

Returns:

The version table rendered as text.

Parameters:
Return type:

str

peta.cli.output.tables.render_why(target, paths, *, color)[source]#

Render root-to-target dependency chains as arrow-joined lines.

color is accepted for signature parity with the other renderers but unused: this output is plain lines, not a Rich renderable.

Returns:

Each path rendered as a -> b -> target, one per line.

Parameters:
Return type:

str

Versioned JSON output formatters.

peta.cli.output.json.format_artifacts(release, *, arguments=None, generated_at=None, retrieved_at=None, freshness=None, publishers=False)[source]#

Format a release’s artifacts in the versioned JSON envelope.

Returns:

An indented JSON string.

Parameters:
  • release (ReleaseArtifacts)

  • arguments (dict[str, object] | None)

  • generated_at (str | None)

  • retrieved_at (str | None)

  • freshness (Freshness | None)

  • publishers (bool)

Return type:

str

peta.cli.output.json.format_compare(a, b, *, diff=None, releases=None, arguments=None, generated_at=None)[source]#

Format two packages and their semantic diff in the versioned envelope.

result.diff.changes lists only what changed, each record with a stable kind and its before/after values; result.diff.unknown names the groups a side had no evidence for. With releases, each side’s artifact listing is recorded as a pypi source, and a failed listing makes the envelope partial rather than failing the comparison.

Returns:

An indented JSON string.

Parameters:
Return type:

str

peta.cli.output.json.format_dep_tree(node, *, arguments=None, generated_at=None)[source]#

Format a dependency tree in the versioned JSON envelope.

Returns:

An indented JSON string.

Parameters:
Return type:

str

peta.cli.output.json.format_error(command, *, arguments, code, message, source=None, generated_at=None)[source]#

Format a fatal command error in the versioned JSON envelope.

Returns:

An indented JSON string.

Parameters:
  • command (CommandName)

  • arguments (dict[str, object] | None)

  • code (MessageCode)

  • message (str)

  • source (str | None)

  • generated_at (str | None)

Return type:

str

peta.cli.output.json.format_files(pkg, *, arguments=None, generated_at=None)[source]#

Format an installed file list in the versioned JSON envelope.

Returns:

An indented JSON string.

Parameters:
Return type:

str

peta.cli.output.json.format_info(pkg, *, arguments=None, generated_at=None)[source]#

Format package metadata in the versioned JSON envelope.

Returns:

An indented JSON string.

Parameters:
Return type:

str

peta.cli.output.json.format_origin(installation, *, arguments=None, generated_at=None)[source]#

Format a distribution’s origin and file integrity in the JSON envelope.

File states are data, not diagnostics: a changed file is what the command was asked to find, so it never makes the envelope partial.

Returns:

An indented JSON string.

Parameters:
  • installation (Installation)

  • arguments (dict[str, object] | None)

  • generated_at (str | None)

Return type:

str

peta.cli.output.json.format_versions(name, versions, *, arguments=None, generated_at=None, retrieved_at=None, freshness=None)[source]#

Format published versions in the versioned JSON envelope.

Returns:

An indented JSON string.

Parameters:
Return type:

str

peta.cli.output.json.format_why(target, paths, *, arguments=None, generated_at=None, tree=None)[source]#

Format dependency paths in the versioned JSON envelope.

Returns:

An indented JSON string.

Parameters:
Return type:

str

Markdown output formatters.

peta.cli.output.markdown.format_artifacts(release, *, detailed=False)[source]#

Format a release’s artifacts as Markdown.

Returns:

A summary table, an optional per-file table, and any notes.

Parameters:
  • release (ReleaseArtifacts)

  • detailed (bool)

Return type:

str

peta.cli.output.markdown.format_banner(text)[source]#

Escape a line printed above a Markdown document for inertness.

Public for the target-environment banner, which the CLI prepends to the document rather than building through a formatter here, and which names paths that may carry link or image syntax.

Returns:

The line as inert inline Markdown.

Parameters:

text (str)

Return type:

str

peta.cli.output.markdown.format_compare(a, b, diff=None, *, changes_only=False)[source]#

Format a package comparison as Markdown.

Returns:

A Markdown comparison table followed by the grouped semantic changes; only the changes with changes_only.

Parameters:
Return type:

str

peta.cli.output.markdown.format_dep_tree(node)[source]#

Format a dependency tree as Markdown.

Returns:

A heading and nested Markdown list.

Parameters:

node (DependencyNode)

Return type:

str

peta.cli.output.markdown.format_files(pkg)[source]#

Format an installed file list as Markdown.

Returns:

A heading and Markdown list.

Parameters:

pkg (PackageInfo)

Return type:

str

peta.cli.output.markdown.format_info(pkg)[source]#

Format package metadata as Markdown.

Returns:

A Markdown heading and field table.

Parameters:

pkg (PackageInfo)

Return type:

str

peta.cli.output.markdown.format_origin(installation)[source]#

Format how a distribution was installed as Markdown.

Returns:

A summary table, a table of flagged files, and any notes.

Parameters:

installation (Installation)

Return type:

str

peta.cli.output.markdown.format_versions(name, versions)[source]#

Format published versions as Markdown.

Returns:

A heading and Markdown table.

Parameters:
Return type:

str

peta.cli.output.markdown.format_why(target, paths)[source]#

Format dependency paths as Markdown.

Returns:

A heading and one list item per path.

Parameters:
Return type:

str

Plain-text output formatters.

peta.cli.output.text.format_artifacts(release, *, detailed=False)[source]#

Format a release’s artifacts as plain text.

Unlike the Rich view this carries the full SHA-256 of every file, since plain text is what a script or a reviewer pipes somewhere else.

Returns:

A summary block, an optional tab-separated file table, and any notes.

Parameters:
  • release (ReleaseArtifacts)

  • detailed (bool)

Return type:

str

peta.cli.output.text.format_compare(a, b, diff=None, *, changes_only=False)[source]#

Format a package comparison as tab-separated text.

Returns:

A header and one tab-separated row per field, then the grouped semantic changes; only the changes with changes_only.

Parameters:
Return type:

str

peta.cli.output.text.format_dep_tree(node)[source]#

Format a dependency tree as indented plain text.

Returns:

One dependency per line.

Parameters:

node (DependencyNode)

Return type:

str

peta.cli.output.text.format_files(pkg)[source]#

Format installed files as plain text.

Returns:

A heading and one path per line.

Parameters:

pkg (PackageInfo)

Return type:

str

peta.cli.output.text.format_info(pkg)[source]#

Format package metadata as plain text.

Returns:

One labeled field per line.

Parameters:

pkg (PackageInfo)

Return type:

str

peta.cli.output.text.format_origin(installation)[source]#

Format how a distribution was installed as plain text.

Returns:

A heading, one label: value line per row, flagged files as tab-separated state/path pairs, and any notes.

Parameters:

installation (Installation)

Return type:

str

peta.cli.output.text.format_versions(name, versions)[source]#

Format published versions as tab-separated text.

Returns:

A heading and one version per line.

Parameters:
Return type:

str

peta.cli.output.text.format_why(target, paths)[source]#

Format dependency paths as plain text.

Returns:

A heading and one path per line.

Parameters:
Return type:

str

Central dispatch for CLI output representations.

peta.cli.output.render.render_artifacts(output_format, release, *, arguments, color, detailed, retrieved_at, freshness=None, publishers=False)[source]#

Render a release’s artifacts in the selected format.

Returns:

The rendered output.

Parameters:
Return type:

str

peta.cli.output.render.render_compare(output_format, a, b, *, arguments, color, diff=None, releases=None, changes_only=False)[source]#

Render a package comparison in the selected format.

changes_only affects the human formats only: JSON always carries both packages and the change list, which is already changed-only.

Returns:

The rendered output.

Parameters:
Return type:

str

peta.cli.output.render.render_dep_tree(output_format, tree, *, arguments, color)[source]#

Render a dependency tree in the selected format.

Returns:

The rendered output.

Parameters:
Return type:

str

peta.cli.output.render.render_files(output_format, pkg, *, arguments, color)[source]#

Render installed files in the selected format.

Returns:

The rendered output.

Parameters:
Return type:

str

peta.cli.output.render.render_info(output_format, pkg, *, arguments, color)[source]#

Render package information in the selected format.

Returns:

The rendered output.

Parameters:
Return type:

str

peta.cli.output.render.render_origin(output_format, installation, *, arguments, color)[source]#

Render a distribution’s origin and file integrity in the selected format.

Returns:

The rendered output.

Parameters:
Return type:

str

peta.cli.output.render.render_target(output_format, target)[source]#

Render the target-environment banner shown above human output.

Printed outside the formatters, so it would otherwise skip the boundary every other human string passes through. It names paths from --path and from the target interpreter’s own sys.path, and a directory name can carry an escape sequence like any other untrusted text.

Line breaks are escaped visibly first. sanitize_terminal() keeps \\n because the multi-line formatters need it, but the banner is one line, and a directory named with a newline would otherwise add lines of its own choosing to the output. Above a Markdown document it is also escaped as Markdown: a directory named ![x](https://...) would otherwise become an image that loads wherever the output is rendered.

Returns:

The banner on one line, inert in the selected format.

Parameters:
Return type:

str

peta.cli.output.render.render_versions(output_format, package, versions, *, arguments, color, retrieved_at, freshness=None)[source]#

Render published versions in the selected format.

Returns:

The rendered output.

Parameters:
Return type:

str

peta.cli.output.render.render_why(output_format, target, paths, tree, *, arguments, color)[source]#

Render dependency paths in the selected format.

Returns:

The rendered output.

Parameters:
Return type:

str

Application-boundary formatting for parser validation failures.

class peta.cli.output.errors.StructuredErrorGroup(*, name=None, commands=None, rich_markup_mode='rich', rich_help_panel=None, suggest_commands=True, invoke_without_command=False, no_args_is_help=False, subcommand_metavar=None, result_callback=None, **attrs)[source]#

Bases: TyperGroup

Typer group that preserves the JSON contract for parser errors.

Parameters:
  • name (str | None)

  • commands (dict[str, Command] | Sequence[Command] | None)

  • rich_markup_mode (Literal['markdown', 'rich', None])

  • rich_help_panel (str | None)

  • suggest_commands (bool)

  • invoke_without_command (bool)

  • no_args_is_help (bool)

  • subcommand_metavar (str | None)

  • result_callback (Callable[[...], Any] | None)

  • attrs (Any)

main(args=None, prog_name=None, complete_var=None, standalone_mode=True, windows_expand_args=True, **extra)[source]#

Run the CLI and envelope pre-handler failures when JSON was requested.

Returns:

The command callback result when standalone mode is disabled.

Raises:
  • SystemExit – When a standalone JSON invocation exits.

  • TyperException – When validation fails outside standalone mode.

Parameters:
  • args (Sequence[str] | None)

  • prog_name (str | None)

  • complete_var (str | None)

  • standalone_mode (bool)

  • windows_expand_args (bool)

  • extra (object)

Return type:

object

Output-format selection and fatal error rendering.

class peta.cli.output.selection.OutputFormat(*values)[source]#

Bases: StrEnum

Supported CLI output representations.

peta.cli.output.selection.fail(command, *, arguments, code, message, output_format, exit_code, source=None)[source]#

Render a fatal error consistently and exit.

Raises:

Exit – Always, using exit_code.

Parameters:
Return type:

NoReturn

peta.cli.output.selection.resolve_or_fail(command, arguments, output_format, *, use_json)[source]#

Resolve output selection, using a JSON envelope for JSON conflicts.

Returns:

The selected output format.

Parameters:
Return type:

OutputFormat

peta.cli.output.selection.resolve_output_format(output_format, *, use_json)[source]#

Resolve the deprecated JSON flag against the unified format option.

output_format is None when --format was left at its default, so --json alone selects JSON while any explicit non-JSON --format conflicts with it.

Returns:

The selected output format.

Raises:

BadParameter – If --json conflicts with an explicit non-JSON format.

Parameters:
Return type:

OutputFormat

The peta info command.

peta.cli.commands.info.info(package, *, use_json=False, output_format=None, local=False, remote=False, color=False, no_osv=False, no_stats=False, python=None, paths=())[source]#

Show detailed package metadata.

Parameters:
Return type:

None

The peta compare command.

peta.cli.commands.compare.compare(a, b, *, use_json=False, output_format=None, local=False, remote=False, color=False, no_osv=False, no_stats=False, python=None, paths=(), changes_only=False, artifacts=False)[source]#

Compare two packages, or two releases of one, and explain what changed.

Parameters:
Return type:

None

The peta deps command.

peta.cli.commands.deps.deps(package, *, use_json=False, output_format=None, local=False, remote=False, color=False, why=None, depth=10, python=None, python_version=None, platform=None, extras=(), paths=())[source]#

Show a package’s recursive dependency tree, or why a target is pulled in.

Parameters:
Return type:

None

The peta files command.

peta.cli.commands.files.files(package, *, use_json=False, output_format=None, color=False, python=None, paths=())[source]#

List files installed by a local package.

Parameters:
Return type:

None

The peta versions command.

peta.cli.commands.versions.get_versions(name)[source]#

Fetch all published versions for a package from the Simple API.

Returns:

A list of {"version", "upload_time"} dicts newest first, and where the listing came from; the list is empty if the package is not found (HTTP 404).

Parameters:

name (str)

Return type:

tuple[list[dict[str, str]], Provenance]

peta.cli.commands.versions.versions(package, *, use_json=False, output_format=None, limit=20, color=False)[source]#

Show published versions of a package from PyPI.

Parameters:
Return type:

None