Skip to content

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=Trueseek() on a member stream from random open() works. Without it, seek() raises io.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 overlapping open() raises ConcurrentAccessError.

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; check error is None for completeness (preferred for damaged archives).
  • :meth:scan_members — random-access: same as members; 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; None if not yet cached.

Parameters:

Name Type Description Default
format ArchiveFormat

(computed property) The detected (container, stream) format of the open archive.

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 access-mode-and-cost).

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):

  1. Translate + stamp — raw codec/OS errors → ArchiveyError with archive context (translate / stamp).
  2. Lazy openopen_fn may run on first read; seekable is answered from the hint until then.
  3. Collapse nested ArchiveStreamsstream_members / codec opens often return another ArchiveStream; _collapse_nested flattens to one wrapper while composing translators and adopting fused verification.
  4. Fused verify — optional MemberVerifier (digests / expected_size) runs in read / close. Distinct from bare size= (fsspec attribute only — does not enable length checks).
  5. Lease / finalizeron_close releases reader live-stream state; a weakref finalizer is a safety net if the caller never close()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 None.

The fsspec-style size convention (see source_byte_size): the creator may supply it up front (a member stream knows member.size from the archive metadata), else an opened inner decompressor with a cheap try_get_size() (index/trailer scan, no decompression) is consulted. Lets a nested open_archive(reader.open("inner.zip")) learn its source size — e.g. for the extraction bomb tracker — without an expensive end-seek. A lazy, still-unopened stream reports None rather than opening itself just to answer.

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:

NameValueDescription
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, /-separated, decoded for display and lookup.

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 if unknown (e.g. a streaming entry).

None
compressed_size int | None

Compressed size in bytes, or None if unknown.

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 (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.

None
mode int | None

Unix permission bits, or None if the format/entry carries no mode.

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: 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.

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.

For a symlink/hardlink, the raw target path string as stored.

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 (container, stream) format of the archive.

required
format_version str | None

Format version string, e.g. "4.5" for ZIP or "5" for RAR5; None if unknown.

required
is_solid bool

Whether decompressing one member may require decompressing earlier ones.

required
member_count int | None

Number of members, or None when a count would require scanning the whole archive.

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 ArchiveMember.is_encrypted).

required
is_multivolume bool

Whether the archive spans multiple volumes.

required
cost 'CostReceipt'

Listing/access cost receipt for the archive (see the access-mode-and-cost capability).

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. "ZIP", "TAR_GZ".

Uses the predefined named-instance attribute name (ZIP, TAR_GZ, …); falls back to repr() for an ad-hoc combination not in the named set. _FORMAT_NAMES is populated just after the class definition — safe at runtime because this property is never called before the module is fully loaded.

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:

NameValueDescription
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:

NameValueDescription
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:

NameValueDescription
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:

NameValueDescription
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:

NameValueDescription
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:

NameValueDescription
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:

  • message is 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\r in 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.
  • context is the structured channel (member_name and 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:

NameValueDescription
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:

NameValueDescription
WARNING 'warning'

archivey.DiagnosticDisposition

Bases: str, Enum

Per-code policy disposition for an emitted diagnostic.

Members:

NameValueDescription
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:

NameValueDescription
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:

NameValueDescription
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:

NameValueDescription
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:

NameValueDescription
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:

NameValueDescription
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 (raise PackageNotInstalledError if 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=True on open_archive, seekable=True on open_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 the rapidgzip DEFLATE-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:

NameValueDescription
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 None for archive-level (header) decryption.

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 when not applicable / unknown. is_solid lives on ArchiveInfo, not here, to avoid duplicating the flag.

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:

NameValueDescription
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). RAR is INDEXED because the reader walks all file headers at open and caches the table — by members() time the list is already in memory.

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:

NameValueDescription
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:

NameValueDescription
SEEKABLE 'seekable'

The source supports arbitrary seek(); positions can be revisited.

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 None when the source size is statically known (the static ratio is used instead).

required
source_seek_count int

Number of seek() calls on the instrumented archive source.

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. !r escapes 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.