API reference
Everything documented here is re-exported from the top-level archivey package and
listed in archivey.__all__. Narrative guide: Home. Authoritative
contracts: openspec/specs/.
Opening archives
archivey.open_archive(source, *, format=None, streaming=False, seekable_members=False, concurrent_members=False, password=None, encoding=None, config=None)
Open an archive for reading.
streaming=False (the default) opens for random access and fails fast at open
time on a non-seekable source. streaming=True promises forward-only, single-pass
access (works on any source, but disables random-access methods).
Member streams are forward-only and single-live by default. Two keyword flags opt into more, each unlocking one specific trap:
seekable_members=True—seek()on a member stream from randomopen()works. Without it,seek()raisesio.UnsupportedOperation. A backward seek may re-decompress from the start when there is no index or accelerator.concurrent_members=True— multiple member streams may be open at once (coordinated first-touch materialization, then worker fan-out; draining close). Without it, a second overlappingopen()raisesConcurrentAccessError.
open_stream uses the same vocabulary for the single-stream case
(open_stream(..., seekable=True)); concurrency is meaningless there, so it has
no counterpart. Declared concurrency does not gate solid open-order cost — see
AccessCost / stream_members().
streaming=True combined with concurrent_members=True is rejected
(ArchiveyUsageError): a forward-only pass cannot fan out.
config supplies library tuning knobs (accelerator modes, TAR end-of-archive
strictness via strict_archive_eof, default extraction limits, and listing
resource limits via listing_limits). None selects the module default
:data:~archivey.DEFAULT_ARCHIVEY_CONFIG.
The format is auto-detected from the source's magic bytes (then its extension) unless
format= is passed explicitly. A directory path opens as a directory pseudo-archive.
A non-seekable stream is wrapped in a :class:PeekableStream so detection never
consumes bytes the backend still needs.
A seekable stream source is taken to hold the archive starting at its current
position: detection peeks from there and restores the position, and the opener
then wraps a mid-positioned stream in a zero-origin view so every backend sees the
archive begin at tell() == 0 (an archive embedded mid-file works uniformly,
without manual slicing).
source may be an ordered sequence of paths or binary streams that together form
a multi-volume archive (7z concatenates volumes; RAR opens volume 1 and lets
unrar resolve siblings). A length-1 sequence is treated as a single source.
password accepts a single value, an ordered sequence of candidate passwords, or
a provider callable. List the most likely password first — especially for 7z, where
each wrong candidate pays an expensive key derivation.
With multiple candidates (or a provider), formats whose per-open password check is weak may need a confirmation read before a candidate is accepted. For traditional ZipCrypto this is usually cheap: compressed members are confirmed from a bounded decompressed prefix. STORED ZipCrypto members are the niche exception — roughly 1/256 of wrong passwords pass the one-byte open check, and with no decompressor to reject garbage the reader must scan the member once (CRC over every surviving candidate in parallel) to decide. That full pass is rare in practice (multiple passwords and a colliding wrong candidate and a STORED member) but can matter for very large stored members.
A password supplied for an archive that carries no encryption is accepted, not
refused — it is a resource offered, not a claim about this archive — and recorded
as PASSWORD_ARGUMENT_UNUSED. That is what lets a batch job pass one keyring at
every archive. Diagnostics also log at WARNING by default, so such a job will
log once per unencrypted archive; silence it with
ArchiveyConfig(diagnostic_policy=DiagnosticPolicy(overrides={
DiagnosticCode.PASSWORD_ARGUMENT_UNUSED: DiagnosticDisposition.IGNORE})), which
keeps the count without the log line.
archivey.open_stream(source, *, format=None, seekable=False, config=None)
Open a single-file compressed stream and return a decompressing stream.
This is the compressed-streams entry point for a bare .gz / .bz2 / .xz /
… payload (no archive container). seekable is the same capability
:func:open_archive spells seekable_members; concurrency is not a concept here
— the call returns exactly one stream — so there is no counterpart to
concurrent_members.
seekable=False (the default) returns a forward-only stream: seekable() is
False, seek() raises io.UnsupportedOperation, and no seek index or
accelerator is instantiated. Pass seekable=True to opt into the
seekable-decompressor-streams contract (native indexes, demand-driven accelerator
AUTO, loud slow rewinds on the non-accelerated path).
format accepts a :class:~archivey.StreamFormat, a raw-stream
:class:~archivey.ArchiveFormat (e.g. ArchiveFormat.GZ), or None to
auto-detect. A container format (ZIP, TAR, …) is rejected — use
:func:open_archive for those.
archivey.extract(source, dest, *, policy=ExtractionPolicy.STRICT, overwrite=OverwritePolicy.ERROR, on_error=OnError.STOP, abort_on=(), format=None, password=None, encoding=None, on_progress=None, config=None, limits=None)
Open source, apply safety checks, and write all members to dest.
The one-shot extraction API (see safe-extraction). It deliberately has no
member-selection parameter — selecting a subset requires the member list, which would
force a reopen; use :meth:ArchiveReader.extract_all with members= on an already
open reader instead. Extraction is safe-by-default: ExtractionPolicy.STRICT and
OverwritePolicy.ERROR, with the decompression-bomb guards active.
A non-seekable stream source (a pipe, a socket) is opened in streaming mode automatically: extraction is a single forward pass, so it needs no random access, and failing fast would reject a source it can perfectly well consume. A seekable source keeps random-access mode — that preserves the re-readable second pass that recovers a hardlink whose target failed or preceded it in archive order.
abort_on names events that end the whole call the first time they occur — a
blocked member, a name collision, a portable-name rewrite — raising instead of
returning a report. It is independent of on_error; see
:class:~archivey.AbortOn.
Returns an :class:~archivey.ExtractionReport whose diagnostic summary spans
detection, open, and extraction for this call.
archivey.detect_format(source, *, config=None, collector=None, budget=None, follow_stub_volumes=True)
Identify the archive format of source without fully opening it.
Returns a :class:FormatInfo. Raises :class:FormatDetectionError when no magic
pattern matches and no extension guess is available.
collector, when provided (e.g. from :func:archivey.open_archive), receives
detection diagnostics into the prospective reader's shared collector. When omitted,
a finite standalone collector is created from config (or the library default).
budget caps what detection may spend; the default is
:data:~archivey.detection_cost.BALANCED_BUDGET (import from
archivey.detection_cost — not yet re-exported at the package root).
A stub-only .exe / .sfx (no archive magic) beside a 7-Zip split first
volume is detected as that volume's format when follow_stub_volumes is true
— the default, so detect_format("vol.exe") agrees with open_archive.
open_archive probes with this flag off, then switches the source itself.
archivey.format_availability(fmt)
Public query: the tri-state support level of fmt and its missing components.
fmt must be an :class:~archivey.ArchiveFormat — the (container, stream)
pair. Anything else, a :class:~archivey.StreamFormat included, raises
:class:~archivey.ArchiveyUsageError rather than answering.
archivey.list_supported_formats()
Public query: formats readable now (support FULL or PARTIAL).
archivey.list_known_formats()
Public query: every format the registry knows, including support NONE.
The reader interface
archivey.ArchiveReader
Bases: ABC
The public, read-only interface to an open archive.
Returned by :func:archivey.open_archive. Annotate against this type; concrete
machinery lives in the internal BaseArchiveReader helper. Use in a with
block.
Listing APIs (easy to mix up):
- :meth:
members— complete list or raise; random-access only (fails on streaming). - :meth:
members_report— always returns a report; checkerror is Nonefor completeness (preferred for damaged archives). - :meth:
scan_members— random-access: same asmembers; streaming: start or finish the forward pass and return the resolved list (also OK after a completed pass). - :meth:
members_report_if_available— never scans;Noneif not yet cached.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
format
|
ArchiveFormat
|
(computed property) The detected |
required |
info
|
ArchiveInfo
|
(computed property) Archive-level metadata (format, solidity, counts, encryption, cost). |
required |
cost
|
CostReceipt
|
(computed property) The listing/access cost receipt for this archive (see |
required |
diagnostics
|
DiagnosticSummary
|
(computed property) Fresh immutable cumulative snapshot of diagnostics for this reader. |
required |
__contains__(member)
abstractmethod
Whether member (an :class:ArchiveMember) was yielded by this reader.
Identity-based and O(1) — no scan — so it is valid in any access mode; useful to
disambiguate members when several readers are in play. Name lookup is
:meth:get, and a non-ArchiveMember operand raises TypeError (this also
keeps the in operator from silently falling back to a full iteration, which
would consume a streaming reader's single forward pass).
__iter__()
abstractmethod
Iterate members in archive order (served from cache once materialized).
close()
abstractmethod
Release resources held by the reader. Idempotent.
Member streams do not outlive the reader. Any still open when close()
runs are closed with it, in the order they were opened, the same way
zipfile.ZipFile.close() and tarfile.TarFile.close() behave — reading one
afterwards fails as it would for any closed file, and closing it again is a
no-op. The archive's own source is released after the last of them, never
underneath a stream still reading through it.
Using the reader itself after close() raises ArchiveyUsageError.
extract_all(dest, *, members=None, filter=None, policy=ExtractionPolicy.STRICT, overwrite=OverwritePolicy.ERROR, on_error=OnError.STOP, abort_on=(), on_progress=None, config=None, limits=None)
abstractmethod
Extract members to dest (safe-by-default; see safe-extraction).
members selects which members to extract (names/ArchiveMembers, or a
predicate; None = all). filter runs after the universal safety checks and
the policy transform, and may rename/sanitize a member (return a
.replace()d copy) or skip it (return None). config defaults to the
config the reader was opened with; limits overrides its extraction limits for
this call only. Returns an :class:~archivey.ExtractionReport whose diagnostic
summary is the delta for this extraction call.
abort_on names events that end the whole call the first time they occur —
raising instead of returning a report. It is independent of on_error: see
:class:~archivey.AbortOn.
get(name, default=None)
abstractmethod
Look up a member by its normalized name, returning default if absent.
This is the name-lookup entry point; :meth:open/:meth:read also accept a
name directly. May trigger a scan; on a streaming reader raises
UnsupportedOperationError. With duplicate member names, returns the last
(the one a sequential extraction would leave on disk).
io_stats()
abstractmethod
Return I/O counters if measurement was enabled at open time, else None.
Enable via :func:archivey.measurement.enable_measurement around the
:func:archivey.open_archive call. Counters cover bytes decompressed, compressed
bytes consumed from the outer source, and source seek calls.
members()
abstractmethod
All members as a list. May trigger a scan; raises UnsupportedOperationError
on a streaming reader (use :meth:scan_members or
:meth:members_report_if_available there). Raises terminal archive-level listing
errors instead of returning an incomplete list.
members_report()
abstractmethod
Materialize the member listing and return a report.
report.error is None means report.members is complete. A non-None
error means the tuple is the recovered prefix and the error is the terminal
archive-level listing damage. Unlike :meth:members, this returns the report
instead of raising for those terminal archive-damage errors.
members_report_if_available()
abstractmethod
A member-list report if available without scanning, else None.
Never scans or consumes the forward pass, so it is safe to call on any reader
(including a streaming one).
open(member)
abstractmethod
Open a member as a binary stream, following symlinks/hardlinks. Accepts a
member object or a name (an unknown name raises KeyError; a member object
that was not yielded by this reader raises ArchiveyUsageError — same identity
rule as member in reader). The caller is responsible for closing the returned
stream. Returns an :class:~archivey.ArchiveStream (usable as BinaryIO).
Cost, when reader.cost.access_cost is SOLID (solid 7z/RAR, any
compressed tar): members share one compression run, so opening one decodes
every member before it. Doing that for each member in turn is quadratic in the
archive size. Nothing warns about it — prefer :meth:stream_members, which
decodes the run once.
read(member)
abstractmethod
Read a member's full contents as bytes (unbounded — prefer :meth:open
or :meth:stream_members for anything not known to be small).
Carries :meth:open's solid-archive cost: on a SOLID archive this decodes
every member preceding the requested one, so a loop over all members is
quadratic. Use :meth:stream_members for that.
scan_members()
abstractmethod
Return the fully-resolved member list in either access mode.
In random-access mode this is equivalent to :meth:members and does not
consume the reader. On a streaming reader it finishes the single forward pass
(running it from the start, or completing an interrupted one) and returns the
resolved list; it may also be called after a completed pass to return the
cached list.
stream_members(members=None)
abstractmethod
Yield (member, stream) pairs in archive order with bounded memory.
members is an optional selector (predicate, name/member collection, or
None for all). The yielded stream is valid only until the iterator advances;
it is None for non-file members.
archivey.ArchiveStream
Bases: ReadOnlyIOStream
Public member/codec stream handle (exception translation + optional verify).
Responsibilities (one class, several optional knobs):
- Translate + stamp — raw codec/OS errors →
ArchiveyErrorwith archive context (translate/stamp). - Lazy open —
open_fnmay run on first read;seekableis answered from the hint until then. - Collapse nested
ArchiveStreams —stream_members/ codec opens often return anotherArchiveStream;_collapse_nestedflattens to one wrapper while composing translators and adopting fused verification. - Fused verify — optional
MemberVerifier(digests /expected_size) runs inread/close. Distinct from baresize=(fsspec attribute only — does not enable length checks). - Lease / finalizer —
on_closereleases reader live-stream state; a weakref finalizer is a safety net if the caller neverclose()s.
Prefer constructing via backends / open_codec_stream rather than by hand.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
diagnostics
|
DiagnosticSummary
|
(computed property) Diagnostic snapshot for events emitted since this stream opened, or empty. |
required |
size
|
int | None
|
(computed property) Total decompressed byte length when cheaply known, else The fsspec-style |
required |
nearest_resume_offset(target)
Delegate the cost question inward; ArchiveStreams nest over each other.
archivey.MemberSelector = Collection[str | ArchiveMember] | Callable[[ArchiveMember], bool] | None
module-attribute
archivey.MemberStreams
Bases: Flag
The member-stream capabilities a reader was opened with.
Callers declare these as booleans —
open_archive(..., seekable_members=True, concurrent_members=True) — so there is
no need to construct a MemberStreams value to open an archive. This flag set is
the internal representation those booleans map to at the entry point, and what every
backend receives. It is not carried on :class:~archivey.CostReceipt or in
diagnostics. Concrete readers expose the value they were opened with as
reader.member_streams; that property is not on the
:class:~archivey.ArchiveReader ABC, so it is reachable at runtime but not part of
the typed public contract.
Default (no bits set — MemberStreams(0)) is the cheap contract:
- at most one live member stream at a time
- streams are forward-only (
seek()raises)
CONCURRENT
Multiple overlapping open() calls are allowed. First-touch member
materialization is coordinated (one build; waiters share the snapshot);
close() drains in-flight worker calls. Callers still synchronize any
shared stream objects they hand around. Reader-wide passes
(__iter__ / stream_members / extract_all) remain single-owner.
Does not remove solid open-order cost — see :class:~archivey.AccessCost.
SEEKABLE
Member streams from random open() support seek(). Without this
flag, seek raises. A backward seek may re-decompress from the start
(loud-slow-rewind) when there is no index or accelerator. This is a
guarantee, not a request mask: a backend that can list a file member
must also seek it when the flag is set. stream_members() yields
stay a single-pass decode; this flag does not require those handles
to seek.
Members:
| Name | Value | Description |
|---|---|---|
CONCURRENT |
auto() |
|
SEEKABLE |
auto() |
|
Data model
archivey.ArchiveMember
dataclass
One archive entry.
Mutable on purpose: backends fill late-bound fields in place after the member
is first constructed (link_target_member, digests, attached diagnostics).
Callers must treat instances as read-only — use :meth:replace for edits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
type
|
MemberType
|
What kind of entry this is (file, directory, symlink, …). |
required |
name
|
str
|
Normalized member path, |
required |
raw_name
|
bytes | None
|
The member name exactly as stored in the archive, undecoded. |
None
|
size
|
int | None
|
Uncompressed size in bytes, or |
None
|
compressed_size
|
int | None
|
Compressed size in bytes, or |
None
|
modified
|
datetime | None
|
Last-modified time, if recorded. |
None
|
accessed
|
datetime | None
|
Last-access time, if recorded. |
None
|
created
|
datetime | None
|
The format's creation-time slot, if recorded (rare; most formats store only mtime). Meaning follows the writer, not a cross-format guarantee. A Unix RARLAB
archive stores inode-change time ( |
None
|
mode
|
int | None
|
Unix permission bits, or |
None
|
uid
|
int | None
|
Owner user id, if recorded. |
None
|
gid
|
int | None
|
Owner group id, if recorded. |
None
|
uname
|
str | None
|
Owner user name, if recorded. |
None
|
gname
|
str | None
|
Owner group name, if recorded. |
None
|
link_target
|
str | None
|
For a symlink/hardlink, the raw target path string as stored. |
None
|
link_target_member
|
'ArchiveMember | None'
|
For a link, the resolved target member within this archive, if found. |
None
|
compression
|
tuple[CompressionMethod, ...]
|
Built-in immutable sequence. If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items. If the argument is a tuple, the return value is the same object. |
<dynamic>
|
is_encrypted
|
bool
|
Whether this member's data is encrypted. |
False
|
is_current
|
bool
|
Last-entry-wins: Duplicate names keep earlier rows with |
True
|
is_sparse
|
bool
|
Whether this member is stored as a sparse file. |
False
|
comment
|
str | None
|
Per-member comment, if the format records one. |
None
|
create_system
|
CreateSystem | None
|
The OS that created the entry (drives mode/attribute interpretation). |
None
|
windows_attrs
|
int | None
|
Raw Windows file-attribute bitmask, if recorded. |
None
|
hashes
|
Mapping[HashAlgorithm, bytes]
|
dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2) |
<class 'dict'>
|
extra
|
dict[str, Any]
|
dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2) |
<class 'dict'>
|
diagnostics
|
tuple['Diagnostic', ...]
|
(computed property) Read-only tuple of diagnostics attached to this member (may be empty). |
required |
member_id
|
int
|
(computed property) |
required |
archive_id
|
str
|
(computed property) |
required |
is_file
|
bool
|
(computed property) |
required |
is_dir
|
bool
|
(computed property) |
required |
is_link
|
bool
|
(computed property) |
required |
is_other
|
bool
|
(computed property) |
required |
is_anti
|
bool
|
(computed property) |
required |
is_junction
|
bool
|
(computed property) |
required |
accessed = None
class-attribute
instance-attribute
Last-access time, if recorded.
comment = None
class-attribute
instance-attribute
Per-member comment, if the format records one.
compressed_size = None
class-attribute
instance-attribute
Compressed size in bytes, or None if unknown.
compression = field(default_factory=tuple)
class-attribute
instance-attribute
Codec chain in compress order — pre-filters first, packing codec last.
create_system = None
class-attribute
instance-attribute
The OS that created the entry (drives mode/attribute interpretation).
created = None
class-attribute
instance-attribute
The format's creation-time slot, if recorded (rare; most formats store only mtime).
Meaning follows the writer, not a cross-format guarantee. A Unix RARLAB
archive stores inode-change time (st_ctime) here; those members set
extra["rar.created_is_ctime"] to True. A Win32 RAR stores birth
time and sets the same key to False. Directory listing uses
st_birthtime only and never st_ctime.
extra = field(default_factory=dict, compare=False)
class-attribute
instance-attribute
Format-specific extra fields (e.g. extra["is_junction"]). Excluded from equality.
gid = None
class-attribute
instance-attribute
Owner group id, if recorded.
gname = None
class-attribute
instance-attribute
Owner group name, if recorded.
hashes = field(default_factory=dict, compare=False)
class-attribute
instance-attribute
Stored content digests keyed by :class:HashAlgorithm (values always bytes).
CRC-32 is four big-endian bytes (:func:crc32_digest). Excluded from equality.
is_current = True
class-attribute
instance-attribute
Last-entry-wins: True for the live final state of this path.
Duplicate names keep earlier rows with is_current=False (history /
superseded). :meth:~archivey.ArchiveReader.get returns the current one.
is_encrypted = False
class-attribute
instance-attribute
Whether this member's data is encrypted.
is_sparse = False
class-attribute
instance-attribute
Whether this member is stored as a sparse file.
link_target = None
class-attribute
instance-attribute
For a symlink/hardlink, the raw target path string as stored.
link_target_member = field(default=None, compare=False)
class-attribute
instance-attribute
For a link, the resolved target member within this archive, if found.
mode = None
class-attribute
instance-attribute
Unix permission bits, or None if the format/entry carries no mode.
modified = None
class-attribute
instance-attribute
Last-modified time, if recorded.
name
instance-attribute
Normalized member path, /-separated, decoded for display and lookup.
raw_name = None
class-attribute
instance-attribute
The member name exactly as stored in the archive, undecoded.
size = None
class-attribute
instance-attribute
Uncompressed size in bytes, or None if unknown (e.g. a streaming entry).
type
instance-attribute
What kind of entry this is (file, directory, symlink, …).
uid = None
class-attribute
instance-attribute
Owner user id, if recorded.
uname = None
class-attribute
instance-attribute
Owner user name, if recorded.
windows_attrs = None
class-attribute
instance-attribute
Raw Windows file-attribute bitmask, if recorded.
modified_utc(tz_for_naive=None)
The modification time as a timezone-aware UTC datetime, or None.
modified itself is faithful to what the archive stores: naive when the
format records local wall-clock time (ZIP's DOS field, RAR4), aware when it
records UTC or an offset — so naive and aware values from one archive cannot be
compared or sorted directly. This helper makes that usable: an aware value is
converted to UTC; a naive one first gets tz_for_naive attached (the caller's
explicit assumption about where the archive was created), defaulting to the
local timezone when not given. Whether the stored value was wall-clock remains
visible on the field itself: member.modified.tzinfo is None.
replace(**kwargs)
Return a copy with the given fields changed; never mutates self.
archivey.ArchiveInfo
dataclass
Archive-level metadata, available immediately after open_archive() without
a full member scan.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
format
|
ArchiveFormat
|
The detected |
required |
format_version
|
str | None
|
Format version string, e.g. |
required |
is_solid
|
bool
|
Whether decompressing one member may require decompressing earlier ones. |
required |
member_count
|
int | None
|
Number of members, or |
required |
comment
|
str | None
|
Archive-level comment, if the format records one. |
required |
is_encrypted
|
bool
|
Header-level encryption (7z, RAR5) — not per-member encryption (see |
required |
is_multivolume
|
bool
|
Whether the archive spans multiple volumes. |
required |
cost
|
'CostReceipt'
|
Listing/access cost receipt for the archive (see the |
required |
extra
|
dict[str, Any]
|
dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2) |
<class 'dict'>
|
comment
instance-attribute
Archive-level comment, if the format records one.
cost
instance-attribute
Listing/access cost receipt for the archive (see the access-mode-and-cost capability).
extra = field(default_factory=dict, compare=False)
class-attribute
instance-attribute
Format-specific archive-level metadata, keyed by namespaced strings (mirrors
ArchiveMember.extra). For example the ISO backend records the auto-selected
namespace as extra["iso.namespace"]. Excluded from __eq__.
format
instance-attribute
The detected (container, stream) format of the archive.
format_version
instance-attribute
Format version string, e.g. "4.5" for ZIP or "5" for RAR5; None if unknown.
is_encrypted
instance-attribute
Header-level encryption (7z, RAR5) — not per-member encryption (see ArchiveMember.is_encrypted).
is_multivolume
instance-attribute
Whether the archive spans multiple volumes.
is_solid
instance-attribute
Whether decompressing one member may require decompressing earlier ones.
member_count
instance-attribute
Number of members, or None when a count would require scanning the whole archive.
archivey.ArchiveFormat
dataclass
A (container, stream) pair identifying how an archive is packaged.
Prefer the named class attributes (ArchiveFormat.ZIP, ArchiveFormat.TAR_GZ,
…) over constructing pairs by hand. Those names are assigned immediately below
the class body; the ClassVar declarations exist so type checkers see them
without per-use suppressions. _FORMAT_NAMES is built from the same
assignments so repr / display_name stay in sync automatically.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
container
|
ContainerFormat
|
|
required |
stream
|
StreamFormat
|
|
required |
display_name
|
str
|
(computed property) Human-readable name for this format, e.g. Uses the predefined named-instance attribute name ( |
required |
file_extension()
The on-disk file extension for this format, without a leading dot.
Used for extension-based naming and detection — e.g. choosing the output
filename when converting between formats, or matching by extension in the
detector. Examples: ZIP -> "zip", TAR_GZ -> "tar.gz",
GZ -> "gz". Formats with no on-disk file representation
(DIRECTORY, UNKNOWN) return "".
archivey.ContainerFormat
Bases: str, Enum
Members:
| Name | Value | Description |
|---|---|---|
ZIP |
'zip' |
|
TAR |
'tar' |
|
RAR |
'rar' |
|
SEVEN_Z |
'7z' |
|
ISO |
'iso' |
|
DIRECTORY |
'directory' |
|
RAW_STREAM |
'raw_stream' |
|
UNKNOWN |
'unknown' |
|
archivey.StreamFormat
Bases: str, Enum
Members:
| Name | Value | Description |
|---|---|---|
UNCOMPRESSED |
'uncompressed' |
|
GZIP |
'gz' |
|
BZIP2 |
'bz2' |
|
XZ |
'xz' |
|
ZSTD |
'zst' |
|
LZ4 |
'lz4' |
|
LZIP |
'lz' |
|
LZMA_ALONE |
'lzma' |
|
ZLIB |
'zz' |
|
BROTLI |
'br' |
|
UNIX_COMPRESS |
'Z' |
|
archivey.MemberType
Bases: Enum
Kind of archive entry.
ANTI is a deletion/tombstone (solid 7z incremental updates), not a payload
file — is_file is false and extraction skips it. OTHER covers device
nodes, FIFOs, sockets, etc., and is always rejected by safe extraction.
Members:
| Name | Value | Description |
|---|---|---|
FILE |
'file' |
|
DIRECTORY |
'directory' |
|
SYMLINK |
'symlink' |
|
HARDLINK |
'hardlink' |
|
OTHER |
'other' |
|
ANTI |
'anti' |
|
archivey.HashAlgorithm
Bases: str, Enum
Digest algorithms that may appear as keys in :attr:ArchiveMember.hashes.
Members:
| Name | Value | Description |
|---|---|---|
CRC32 |
'crc32' |
|
BLAKE2SP |
'blake2sp' |
|
ADLER32 |
'adler32' |
|
archivey.crc32_digest(value)
Encode a CRC-32 as four big-endian bytes for :attr:ArchiveMember.hashes.
archivey.CompressionAlgorithm
Bases: Enum
A compression/filter codec. Extensible: codecs Archivey does not recognize
map to UNKNOWN rather than raising, so callers should treat the set as
open-ended. ContainerFormat.RAR and CompressionAlgorithm.RAR are
homonyms (container vs codec), not a new name like RAR_COMPRESSION.
Members:
| Name | Value | Description |
|---|---|---|
STORED |
'stored' |
|
DEFLATE |
'deflate' |
|
DEFLATE64 |
'deflate64' |
|
BZIP2 |
'bzip2' |
|
LZMA |
'lzma' |
|
LZMA2 |
'lzma2' |
|
ZSTD |
'zstd' |
|
LZ4 |
'lz4' |
|
BROTLI |
'brotli' |
|
PPMD |
'ppmd' |
|
BCJ |
'bcj' |
|
BCJ2 |
'bcj2' |
|
DELTA |
'delta' |
|
RAR |
'rar' |
|
UNKNOWN |
'unknown' |
|
archivey.CompressionMethod
dataclass
One codec in a member's filter chain.
Members store tuple[CompressionMethod, ...]. Order matches the compress /
pack direction: pre-filters first, packing codec last (closest to the stored
bytes). Example: 7z (BCJ2, LZMA2) — decompress by applying LZMA2, then BCJ2.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
algo
|
CompressionAlgorithm
|
|
required |
level
|
int | None
|
|
None
|
properties
|
bytes | None
|
|
None
|
archivey.CreateSystem
Bases: Enum
OS that created the archive entry (mirrors ZIP create_system values).
Members:
| Name | Value | Description |
|---|---|---|
FAT |
0 |
|
AMIGA |
1 |
|
OPENVMS |
2 |
|
UNIX |
3 |
|
VM_CMS |
4 |
|
ATARI_ST |
5 |
|
OS2_HPFS |
6 |
|
MACINTOSH |
7 |
|
Z_SYSTEM |
8 |
|
CPM |
9 |
|
WINDOWS_NTFS |
10 |
|
MVS |
11 |
|
VSE |
12 |
|
ACORN_RISC |
13 |
|
VFAT |
14 |
|
ALTERNATE_MVS |
15 |
|
BEOS |
16 |
|
TANDEM |
17 |
|
OS_400 |
18 |
|
OS_X_DARWIN |
19 |
|
UNKNOWN |
255 |
|
Diagnostics
Structured advisories (formerly log-only warnings). See the diagnostics capability
spec for lifecycle, retention, and policy.
archivey.Diagnostic
dataclass
One immutable advisory occurrence.
The two fields carry the same facts for different audiences, and that is why they are treated differently:
messageis display text, and is stored escaped. It interpolates archive-derived values — member names, link targets, paths — which are attacker-controlled, and a name carrying\x1b[2K\rin text that reaches a terminal can erase the line reporting it and write something else in its place. Escaping happens at construction so it holds for every consumer, whatever it does with the text.contextis the structured channel (member_nameand its siblings, surfaced through :meth:to_dict) and stays raw. A caller routing diagnostics to a JSON sink, or matching on a name, needs the real value.
This mirrors :class:~archivey.exceptions.ArchiveyError, which escapes its
message and keeps member_name raw for the same reason.
Escaping here rather than trusting the sites that build messages: every one of them
today interpolates through :func:~archivey.escaping.quoted or !r, so the text
would be inert either way — but that is a property of the current call sites, not of
the type. A future message written as f"...{name}" would pass review looking
exactly like its neighbours while emitting raw control bytes, and only for a hostile
archive.
Escaping runs in __post_init__: on a frozen dataclass that is the hook that
covers every construction path. (A hand-written __init__ would survive the
decorator — it does not overwrite one defined in the class body — but with no
dataclass base to delegate to it would mean spelling out all five fields and keeping
them in step with the declarations above.) Nothing reconstructs a Diagnostic
with :func:dataclasses.replace, which would escape a second time.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
occurrence_id
|
str
|
|
required |
code
|
DiagnosticCode
|
|
required |
severity
|
DiagnosticSeverity
|
|
required |
message
|
str
|
|
required |
context
|
DiagnosticContext
|
|
required |
archivey.DiagnosticCode
Bases: str, Enum
Stable machine codes for advisory events.
Members:
| Name | Value | Description |
|---|---|---|
MEMBER_NAME_NORMALIZED |
'member_name_normalized' |
|
MEMBER_NAME_ENCODING_INFERRED |
'member_name_encoding_inferred' |
|
MEMBER_NAME_BIDI_CONTROL |
'member_name_bidi_control' |
|
FORMAT_EXTENSION_CONFLICT |
'format_extension_conflict' |
|
EXPLICIT_FORMAT_LISTED_EMPTY |
'explicit_format_listed_empty' |
|
EXTENSION_FORMAT_UNCONFIRMED |
'extension_format_unconfirmed' |
|
PROBE_FORMAT_UNCONFIRMED |
'probe_format_unconfirmed' |
|
EMPTY_ARCHIVE |
'empty_archive' |
|
ENCODING_ARGUMENT_UNUSED |
'encoding_argument_unused' |
|
PASSWORD_ARGUMENT_UNUSED |
'password_argument_unused' |
|
SCAN_DIRECTORY_VANISHED |
'scan_directory_vanished' |
|
SCAN_ENTRY_VANISHED |
'scan_entry_vanished' |
|
ARCHIVE_EOF_MARKER_MISSING |
'archive_eof_marker_missing' |
|
ARCHIVE_TRAILING_DATA |
'archive_trailing_data' |
|
MEMBER_TIMESTAMP_INVALID |
'member_timestamp_invalid' |
|
SYMLINK_TARGET_UNAVAILABLE |
'symlink_target_unavailable' |
|
DIGEST_UNVERIFIABLE |
'digest_unverifiable' |
|
SEEK_INDEX_DEGRADED |
'seek_index_degraded' |
|
STREAM_REWIND_REDECOMPRESSES |
'stream_rewind_redecompresses' |
|
archivey.DiagnosticSeverity
Bases: str, Enum
Severity axis on a diagnostic record.
Only WARNING is used initially; the axis remains so a later informational
taxonomy does not require changing the value shape.
Members:
| Name | Value | Description |
|---|---|---|
WARNING |
'warning' |
|
archivey.DiagnosticDisposition
Bases: str, Enum
Per-code policy disposition for an emitted diagnostic.
Members:
| Name | Value | Description |
|---|---|---|
IGNORE |
'ignore' |
|
COLLECT |
'collect' |
|
RAISE |
'raise' |
|
archivey.DiagnosticPolicy
dataclass
Per-code disposition policy; matching is by code only.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
default
|
DiagnosticDisposition
|
|
<DiagnosticDisposition.COLLECT: 'collect'>
|
overrides
|
Mapping[DiagnosticCode, DiagnosticDisposition]
|
|
mappingproxy({})
|
pedantic()
staticmethod
RAISE on every code, including the argument-hygiene and access-pattern ones.
See the taxonomy-growth note on :meth:strict: this policy raises on codes added
after the caller wrote it, by construction.
strict()
staticmethod
RAISE on :data:ARCHIVE_INTEGRITY_CODES, COLLECT on everything else.
The recommended strict mode. Unlike a bare default=RAISE policy it is
version-stable in the way that matters: new codes MAY be added in a minor
release, and a default=RAISE caller starts raising on events their working
program never produced, whereas this set's membership is versioned alongside the
taxonomy and each addition is a deliberate decision.
Adds no resolution axis — the value is an ordinary frozen policy with per-code overrides, and equals the same policy built by hand.
archivey.DiagnosticSummary
dataclass
Immutable point-in-time snapshot of diagnostic counts and retained detail.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
total_count
|
int
|
|
required |
counts
|
Mapping[DiagnosticCode, int]
|
|
mappingproxy({})
|
retained
|
tuple[Diagnostic, ...]
|
|
()
|
dropped_count
|
int
|
|
0
|
archivey.OnDiagnostic = Callable[[Diagnostic], None]
module-attribute
Optional synchronous callback invoked for COLLECT/RAISE diagnostics.
archivey.ExtractionReport
dataclass
Immutable extraction outcome: fixed result tuple plus diagnostic summary.
results is a frozen outcome structure. Each :class:ExtractionResult is frozen,
but ExtractionResult.member refers to the live mutable :class:ArchiveMember
(caller-read-only), whose late-bound metadata and member diagnostics may still be
filled in place.
The report iterates, indexes, and sizes as its results sequence, so the common
for result in extract(...) / len(...) / report[0] idioms keep working while
report.diagnostics exposes the operation's diagnostic summary.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
results
|
tuple[ExtractionResult, ...]
|
|
required |
diagnostics
|
DiagnosticSummary
|
|
required |
archivey.MemberListReport
dataclass
Immutable member-list outcome: recovered members plus listing honesty.
error is None means members is a complete archive listing. A non-None
error means the tuple is the recovered prefix and the error is the terminal
archive-level damage that stopped listing.
Like :class:ExtractionReport, the report iterates, indexes, and sizes as its
primary sequence so common for member in report / len(report) idioms work.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
members
|
tuple[ArchiveMember, ...]
|
|
required |
error
|
ArchiveyError | None
|
|
required |
diagnostics
|
DiagnosticSummary
|
|
required |
Extraction
archivey.ExtractionResult
dataclass
One entry per member processed, returned from extract() / extract_all().
Frozen outcome structure (path / status / error cannot be replaced after
construction). member still refers to the live mutable :class:ArchiveMember
whose late-bound metadata may be filled in place.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
member
|
ArchiveMember
|
|
required |
path
|
Path | None
|
|
required |
status
|
ExtractionStatus
|
|
required |
error
|
ArchiveyError | OSError | None
|
|
None
|
requested_path
|
Path | None
|
|
None
|
presented_name
|
str | None
|
|
None
|
failure_group_id
|
str | None
|
|
None
|
failure_group_size
|
int | None
|
|
None
|
collided_with
|
Path | None
|
|
None
|
archivey.ExtractionStatus
Bases: str, Enum
The outcome recorded for a single member in its :class:ExtractionResult.
Members:
| Name | Value | Description |
|---|---|---|
EXTRACTED |
'extracted' |
|
NOT_OVERWRITTEN |
'not_overwritten' |
|
SUPERSEDED |
'superseded' |
|
OVERWRITTEN |
'overwritten' |
|
BLOCKED |
'blocked' |
|
FAILED |
'failed' |
|
archivey.ExtractionPolicy
Bases: Enum
How much of an archive member to trust when writing it to the destination.
The universal path/symlink/special-file safety checks are enforced under all
policies (see safe-extraction). Beyond those, the policy governs two dimensions:
the permission/ownership transform applied before a member is written, and the
cross-platform name safety keyed off it — collision determinism (O2), reserved/mangled
name rejection (O3/O4), portable-name normalization (O7), and rejection of
deceptive names (bidi overrides). STRICT is portable-by-default; TRUSTED
defers to the local OS (faithful bytes, no name rejection or rewrite). See
dev-docs/decisions/0013-cross-platform-name-safety-policies.md.
What TRUSTED does not relax: anything where the write itself is unsafe — a
name that escapes the destination, carries a NUL, or names a device node. Those are
universal. It does extract a name built to display as something else
(evil<U+202E>gnp.exe), which STRICT/STANDARD refuse with
DeceptiveNameError: such a member lands inside the destination under exactly its
stored bytes, so the risk is to a human reading the directory afterwards, not to the
filesystem. Choosing TRUSTED accepts that, which is what makes faithful
round-tripping possible. See
dev-docs/decisions/0017-bidi-override-rejection-is-policy-keyed.md.
Members:
| Name | Value | Description |
|---|---|---|
STRICT |
'strict' |
|
STANDARD |
'standard' |
|
TRUSTED |
'trusted' |
|
archivey.OverwritePolicy
Bases: Enum
What to do when a destination entry already exists where a member would be written.
ERROR raises an ExtractionError for the member, which is then a per-member
failure governed by the OnError policy — OnError.STOP re-raises and halts,
OnError.CONTINUE records a FAILED ExtractionResult and proceeds. SKIP
is not an error: it records a NOT_OVERWRITTEN result regardless of OnError.
Members:
| Name | Value | Description |
|---|---|---|
ERROR |
'error' |
|
SKIP |
'skip' |
|
REPLACE |
'replace' |
|
RENAME |
'rename' |
|
archivey.OnError
Bases: Enum
What to do when an individual member cannot be extracted.
Governs per-member failures only (corrupt/truncated/undecodable data,
write OSError, overwrite ERROR, etc.). A policy BLOCKED outcome
(FilterRejectionError from a universal path-safety check or a policy
filter) is always recorded and continued, under either value. Aborting the
whole extraction on the first unsafe member is AbortOn.BLOCKED_MEMBER,
an independent opt-in that applies under either OnError value.
Members:
| Name | Value | Description |
|---|---|---|
STOP |
'stop' |
|
CONTINUE |
'continue' |
|
archivey.AbortOn
Bases: str, Enum
Events that abort the whole extraction the first time they occur.
Passed as abort_on= to extract() / extract_all() (a collection; empty by
default). Independent of :class:OnError and of DiagnosticPolicy: an event named
here aborts whatever those are set to, and one not named here never aborts.
Abort is immediate — the triggering member's partial output is removed, no later
member is processed, and no ExtractionReport is returned. Output already
written for earlier members stays on disk, matching OnError.STOP: an abort stops
the run, it does not roll it back.
There is deliberately no member for extraction failures: OnError.STOP already
means "raise on the first failure".
Members:
| Name | Value | Description |
|---|---|---|
BLOCKED_MEMBER |
'blocked_member' |
|
NAME_COLLISION |
'name_collision' |
|
NAME_SANITIZED |
'name_sanitized' |
|
archivey.MemberFilter = Callable[[ArchiveMember], 'ArchiveMember | None']
module-attribute
Configuration
archivey.ArchiveyConfig
dataclass
Library tuning knobs passed as config= to :func:open_archive / :func:extract.
Per-call operationals (format, streaming, password, extraction's
members/filter/policy/…) stay keyword arguments — not fields here.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
use_rapidgzip
|
AcceleratorMode
|
|
<AcceleratorMode.AUTO: 'auto'>
|
use_indexed_bzip2
|
AcceleratorMode
|
|
<AcceleratorMode.AUTO: 'auto'>
|
strict_archive_eof
|
bool
|
|
False
|
zip_unflagged_fallback_encoding
|
str
|
|
'cp437'
|
extraction_limits
|
ExtractionLimits
|
|
ExtractionLimits(max_extracted_bytes=2147483648, max_ratio=1000.0, ratio_activation_threshold=5242880, max_entries=1048576)
|
listing_limits
|
ListingLimits
|
|
ListingLimits(max_members=1048576, max_metadata_bytes=67108864)
|
diagnostic_policy
|
DiagnosticPolicy
|
Per-code disposition policy; matching is by code only. |
<dynamic>
|
max_retained_diagnostic_references
|
int
|
|
256
|
on_diagnostic
|
OnDiagnostic | None
|
|
None
|
archivey.ExtractionLimits
dataclass
Decompression-bomb limits for :func:archivey.extract / :meth:extract_all.
None on a guard field disables that guard. :attr:UNLIMITED disables all four.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_extracted_bytes
|
int | None
|
|
2147483648
|
max_ratio
|
float | None
|
|
1000.0
|
ratio_activation_threshold
|
int
|
|
5242880
|
max_entries
|
int | None
|
|
1048576
|
archivey.ListingLimits
dataclass
Caps for materializing a member list (members / scan_members / extract prep).
Applied from the reader's open :attr:ArchiveyConfig.listing_limits for its lifetime.
None on a field disables that guard. :attr:UNLIMITED disables both.
stream_members / forward-only iteration do not enforce these caps.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_members
|
int | None
|
|
1048576
|
max_metadata_bytes
|
int | None
|
|
67108864
|
archivey.AcceleratorMode
Bases: Enum
Tri-state control for an optional random-access accelerator backend.
ON— always use the accelerator (raisePackageNotInstalledErrorif its package is absent: the caller asked for it explicitly).OFF— never use it; the stream stays sequential-only.AUTO— use it only when seekability was declared (seekable_members=Trueonopen_archive,seekable=Trueonopen_stream, or internal seek demand). Without declared seek demand, AUTO leaves the cheaper sequential backend in place (no index/accelerator work). When AUTO would enable the accelerator but its package is absent, fall back to sequential silently (it is an enhancement, not a requirement). For therapidgzipDEFLATE-family path, AUTO also requires the known compressed input size to reach :data:RAPIDGZIP_AUTO_MIN_COMPRESSED_SIZE(see :meth:enabled_for) and a verifiable decompressed size (StreamConfig.expected_decompressed_size, or gzip ISIZE) so truncation cannot be silently short-read.
Members:
| Name | Value | Description |
|---|---|---|
AUTO |
'auto' |
|
ON |
'on' |
|
OFF |
'off' |
|
enabled_for(*, seekable, available, input_size=None, min_size=None)
Resolve the tri-state to "use the accelerator?".
ON always returns True (the caller checks availability and raises
PackageNotInstalledError if the package is missing — the user asked for it
explicitly; min_size is ignored). AUTO enables it only when
seekability is declared and the package is available, so a missing package
falls back silently. When min_size is set and input_size is known and
strictly below that threshold, AUTO also falls back (tiny members do not
repay per-stream accelerator setup). Unknown input_size keeps the
pre-threshold AUTO behaviour.
archivey.PasswordInput = str | bytes | Sequence[str | bytes] | PasswordProvider | None
module-attribute
Accepted password= shapes: one value, an ordered candidate list, a provider, or None.
archivey.PasswordRequest
dataclass
Context passed to a :data:PasswordProvider when a password is needed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
member
|
ArchiveMember | None
|
The member being decrypted, or |
required |
attempt
|
int
|
1 on the first ask for this unit; increments after a wrong-password retry. |
required |
attempt
instance-attribute
1 on the first ask for this unit; increments after a wrong-password retry.
member
instance-attribute
The member being decrypted, or None for archive-level (header) decryption.
archivey.PasswordProvider = Callable[[PasswordRequest], str | bytes | None]
module-attribute
Callable consulted when static password candidates fail for an encrypted unit.
Access cost
archivey.CostReceipt
dataclass
Machine-readable description of an opened archive's access costs.
The three axes are orthogonal and must not be conflated: listing_cost is about
enumeration, access_cost about the format layout, and stream_capability about
the source bytes. See the access-mode-and-cost capability spec for the full model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
listing_cost
|
ListingCost
|
Cost of enumerating all members. |
required |
access_cost
|
AccessCost
|
Cost of reading one member's data, given the format layout. |
required |
stream_capability
|
StreamCapability
|
Seekability of the underlying source bytes. |
required |
solid_block_count
|
int | None
|
Number of distinct solid blocks (each one decompress pass), or |
None
|
notes
|
tuple[str, ...]
|
Built-in immutable sequence. If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items. If the argument is a tuple, the return value is the same object. |
<dynamic>
|
access_cost
instance-attribute
Cost of reading one member's data, given the format layout.
listing_cost
instance-attribute
Cost of enumerating all members.
notes = field(default_factory=tuple)
class-attribute
instance-attribute
Human-readable caveats about the cost figures.
solid_block_count = None
class-attribute
instance-attribute
Number of distinct solid blocks (each one decompress pass), or None when not
applicable / unknown. is_solid lives on ArchiveInfo, not here, to avoid
duplicating the flag.
stream_capability
instance-attribute
Seekability of the underlying source bytes.
archivey.ListingCost
Bases: Enum
How expensive it is to enumerate all members (list names + metadata).
Members:
| Name | Value | Description |
|---|---|---|
INDEXED |
'indexed' |
Members can be listed without scanning header-to-header or decompressing payload. Examples: ZIP central directory, 7z header, ISO directory tree at open. A
filesystem directory is not indexed (its walk is |
REQUIRES_SCANNING |
'requires_scanning' |
No index, but members can be enumerated by seeking/scanning header-to-header without decompressing payload (e.g. an uncompressed tar, or a filesystem directory walk). |
REQUIRES_DECOMPRESSION |
'requires_decompression' |
The stream must be decompressed to reach the member headers (e.g. a compressed tar). |
archivey.AccessCost
Bases: Enum
How expensive it is to read one member's data, given the format layout.
Members:
| Name | Value | Description |
|---|---|---|
DIRECT |
'direct' |
Any member can be read without touching other members. |
SOLID |
'solid' |
Reading member N may require decompressing earlier members in its solid block. |
archivey.StreamCapability
Bases: Enum
A property of the underlying source bytes, independent of the format layout.
Ordered by strength, weakest first: FORWARD_ONLY < SEEKABLE. There is only
one possible direction — a seekable source can serve every read a forward-only one
can — so the comparison means "is at least as strong as", not a preference. That is
what lets a requirement stated as a capability (FormatAvailability.required_source)
be tested against a source's actual capability (CostReceipt.stream_capability)
with <= instead of a lookup table.
ListingCost and AccessCost are deliberately not ordered: their members name
kinds of work, not strengths of one resource.
Members:
| Name | Value | Description |
|---|---|---|
SEEKABLE |
'seekable' |
The source supports arbitrary |
FORWARD_ONLY |
'forward_only' |
Non-seekable source (pipe/socket): it cannot be rewound at all. Re-reading any earlier position requires a brand-new stream. |
Measurement
archivey.IoStats
dataclass
I/O counters sampled from an archive reader with measurement enabled.
Returned by :meth:~archivey.ArchiveReader.io_stats; None when the reader
was not opened inside :func:enable_measurement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bytes_decompressed
|
int
|
Total decoded / output bytes delivered through member streams so far. |
required |
compressed_bytes_consumed
|
int | None
|
Compressed bytes pulled from the archive's outer source so far, or |
required |
source_seek_count
|
int
|
Number of |
required |
bytes_decompressed
instance-attribute
Total decoded / output bytes delivered through member streams so far.
compressed_bytes_consumed
instance-attribute
Compressed bytes pulled from the archive's outer source so far, or None
when the source size is statically known (the static ratio is used instead).
source_seek_count
instance-attribute
Number of seek() calls on the instrumented archive source.
archivey.enable_measurement()
Enable bytes-decompressed / seek counters for archives opened in this context.
Errors
archivey.ArchiveyError
Bases: Exception
Root of all Archivey exceptions.
The message is escaped. Call sites build messages by interpolating
archive-derived text — a member name, or a destination path built from one — and
those are attacker-controlled. An exception message reaches a terminal by more
routes than any one consumer controls: print(e), logging.exception, a
third-party error reporter, and above all an uncaught exception whose traceback
the interpreter prints itself, whose final line is str(e). Escaping in the
handler that displays it protects only the routes someone remembered to
configure; escaping here protects all of them, with no configuration.
So message is stored escaped, and that escaped form is what
:meth:__str__, args[0] and repr() all render. raw_message keeps the
text as the call site wrote it, for the one job the escaped form cannot do: being
embedded in another message that will escape it in turn.
archive_name, member_name, link_target and source_format stay
raw too, for callers that need the real value to act on rather than to print.
:meth:__str__ renders the names through !r, which escapes them for display in
turn — so a name available as an attribute should not also be interpolated into
the message, or it prints twice. Prefer prose plus attributes:
SymlinkEscapeError("Symlink target escapes destination", member_name=name,
link_target=target).
format_unconfirmed is a boolean (default False): True when the format
claim rested only on a content probe with nothing corroborating it (no matching
extension, no inner-TAR upgrade), so a decode failure should not be read as "this
known format truncated." Confidence is irrelevant to the flag.
Escape exactly once, at the outermost message. Everything a message interpolates should therefore be raw when it goes in — which is what the two helpers are for, and why neither of them is a matter of taste:
- a member name, link target or path → :func:
~archivey.escaping.quoted, not!r.!rescapes first, and this escapes the backslashes it introduced. - a caught exception that might be one of ours → :func:
raw_message_of, not{exc}or{exc!r}.
Note this is the opposite of the rule for logger.* calls, whose records the CLI
does not escape: there, %r is what makes an interpolated name inert and must
stay.
archivey.ResourceLimitError
Bases: ArchiveyError
A configured listing or extraction resource limit was exceeded.
Covers :class:~archivey.config.ListingLimits materialization caps and
:class:~archivey.config.ExtractionLimits bomb guards. Sibling of
:class:ExtractionError (not a subclass): limit trips are not filter/path failures.
archivey.DiagnosticRaisedError
Bases: ArchiveyError
A diagnostic was escalated to an error via :class:~archivey.diagnostics.DiagnosticPolicy.
Always-stop: extraction MUST NOT catch this as a per-member failure under
OnError.CONTINUE. Carries the escalated :class:~archivey.diagnostics.Diagnostic.
archivey.ArchiveyUsageError
Bases: Exception
Caller misuse of the Archivey API — deliberately not an :class:ArchiveyError.
except ArchiveyError wraps archive/environment problems; usage errors indicate
a bug in calling code and must not be swallowed by those handlers.
The message is escaped on the same terms as :class:ArchiveyError's. A usage
error's text is mostly archivey's own, so the escaping is usually a no-op — but
"mostly" is not a property worth carving an exception into, and a usage error is
free to name the member that provoked it.
archivey.ConcurrentAccessError
Bases: ArchiveyUsageError
A second overlapping member stream was opened without concurrent_members=True.
The message includes the open_archive() call site so the error points at where
the capability should have been declared.