binary_ensemble.bundle

The bundle module is the recommended high-level API. It writes and reads .bendl files: single-file containers that hold an assignment stream plus graph, metadata, permutation maps, and custom assets.

When to use it

Use this module when you want the file to be self-describing. That is the normal case for redistricting ensembles because an assignment is only meaningful with the graph node order it was written against.

Task

API

Create a new bundle

BendlEncoder(path, overwrite=True)

Attach a dual graph

encoder.add_graph(graph, sort=...)

Stream assignments while sampling

with encoder.ben_stream() as ensemble: ...

Read assignments and assets

BendlDecoder(path)

Reorder/relabel an existing bundle

relabel_bundle(...)

Recompress a bundle to XBEN

compress_stream(...)

from binary_ensemble import BendlDecoder

decoder = BendlDecoder("ensemble.bendl")
assert len(decoder) == decoder.count_samples()
assert decoder.assignment_format() in {"ben", "xben"}

The .bendl bundle format: the recommended single-file container.

A bundle wraps a BEN/XBEN assignment stream together with front-loaded assets: a dual graph.json, a node_permutation_map.json, a metadata.json, and arbitrary custom blobs. BendlEncoder writes one; BendlDecoder reads and iterates one.

Typical write:

with BendlEncoder(path, overwrite=True) as enc:
    enc.add_graph(graph, sort="rcm")                # sort=None => store raw
    enc.add_metadata({"seed": 1234})
    with enc.ben_stream() as ensemble:
        for assignment in chain:
            ensemble.write(assignment)

Typical read:

dec = BendlDecoder(path)
graph = dec.read_graph()
for assignment in dec:
    ...

Encoder

BendlEncoder has two modes:

Mode

Open with

Stream writes

Asset writes

Create

BendlEncoder(path, overwrite=True)

one stream

before or after the stream

Append

BendlEncoder.append(path)

unavailable

immediate appends to a finalized bundle

The stream context finalizes the bundle when it closes cleanly. You only need to use the encoder itself as a context manager for assets-only bundles or if that style is clearer in your code.

from binary_ensemble import BendlEncoder

encoder = BendlEncoder("api-demo.bendl", overwrite=True)
encoder.add_metadata({"sampler": "demo"})

with encoder.ben_stream() as ensemble:
    ensemble.write([1, 1, 2, 2])
    ensemble.write([1, 2, 2, 2])

Graph handling

add_graph() accepts NetworkX adjacency JSON, a path to that JSON, raw bytes, or a readable object. By default it reorders with sort="mlc" for better compression and returns the reordered NetworkX graph. Write assignments in the returned graph’s node order.

sort

Meaning

Needs key?

Stores permutation map?

"mlc"

Multi-level clustering; topology-based default

no

yes

"rcm"

Reverse Cuthill-McKee topology ordering

no

yes

"key"

Sort nodes by a node attribute

yes

yes

None

Store the graph as-is

no

no

import networkx as nx

from binary_ensemble import BendlEncoder

graph = nx.convert_node_labels_to_integers(nx.path_graph(4))
for node in graph.nodes:
    graph.nodes[node]["GEOID20"] = f"{node:04d}"

encoder = BendlEncoder("api-graph.bendl", overwrite=True)
ordered_graph = encoder.add_graph(nx.adjacency_data(graph), sort="key", key="GEOID20")

with encoder.ben_stream() as ensemble:
    ensemble.write([1, 1, 2, 2])

assert ordered_graph.number_of_nodes() == 4
class BendlEncoder(file_path, overwrite=False)[source]

Bases: object

Writer for a .bendl bundle (create mode) or an asset appender (append mode).

In create mode (the constructor), assets may be added before or after a single-use ben_stream(). You do not need to use BendlEncoder itself as a context manager: closing the ben_stream() context finalizes the bundle, so the common pattern is:

enc = BendlEncoder(path, overwrite=True)
graph = enc.add_graph(my_graph)          # MLC-reordered by default
with enc.ben_stream() as ensemble:             # only the stream needs ``with``
    for assignment in chain:
        ensemble.write(assignment)
# bundle is finalized here

The encoder is still usable as a context manager if you prefer, and that is the easy way to finalize an assets-only bundle (one written with no ben_stream()): either with BendlEncoder(...) as enc: ... or an explicit close(). In append mode (append()), an existing finalized bundle is grown with new assets and ben_stream() is unavailable.

Parameters:
  • file_path (StrPath) – Output path for the new bundle (str or os.PathLike, e.g. pathlib.Path). Must not exist unless overwrite=True.

  • overwrite (bool, optional) – Replace an existing file at file_path. Default is False. Unlike the one-shot transforms, this truncates the existing file when the encoder opens it, so an interrupted write leaves a truncated, unfinalized bundle (recoverable with allow_unfinalized) rather than the original file. Write to a fresh path and rename if the existing file is precious.

Raises:

OSError – If file_path exists and overwrite is False, or it cannot be created.

classmethod append(file_path)[source]

Open an existing finalized bundle to append new assets.

ben_stream() is unavailable in append mode; each add_* commits immediately.

Parameters:

file_path (StrPath) – Path to an existing, finalized .bendl bundle (str or os.PathLike).

Returns:

BendlEncoder – An encoder in append mode.

Raises:

Exception – If the file is missing, is not a bundle, or is not finalized.

Return type:

BendlEncoder

add_graph(graph, sort='mlc', key=None, *, compress=None, compression_level=None)[source]

Embed the dual graph.json and return the (possibly reordered) graph.

When reordering, both graph.json and node_permutation_map.json are stored and the reordered graph is returned so the chain runs on that ordering. Reordering is pre-stream only; a raw graph (sort=None) may also be attached post-stream / in append mode.

Parameters:
  • graph (GraphInput) – The dual graph (GraphInput): a live networkx.Graph (subclasses such as gerrychain.Graph count; its node iteration order is preserved), or adjacency-format JSON as a parsed dict or list, raw bytes, a file-like object with .read(), or a str / os.PathLike path to a JSON file. A plain str is a path here.

  • sort (SortMethod | None, optional) – How to order the nodes (SortMethod or None): "mlc" (multi-level clustering that reorders the graph for better compression), "rcm" (reverse Cuthill-McKee), "key" (sort by the node attribute named in key), or None to store the graph as-is with no permutation map. Default is "mlc".

  • key (str | None, optional) – Node attribute to sort by, e.g. key="GEOID"; key="id" sorts by the NetworkX node id. Required with (and only valid with) sort="key". Default is None.

  • compress (bool | None)

  • compression_level (int | None)

Returns:

networkx.Graph – The stored graph after any reordering (matching BendlDecoder.read_graph()). Its node iteration order is the order the chain must write assignments in.

Raises:
  • ValueError – If sort / key is invalid.

  • Exception – If a reordering graph is added after the stream has started.

Return type:

nx.Graph

add_metadata(metadata, *, compress=None, compression_level=None)[source]

Embed the canonical metadata.json asset (run provenance).

Parameters:
  • metadata (MetadataInput) – The JSON payload (MetadataInput): a dict or list (serialized for you), raw JSON bytes, a file-like object with .read(), or a str / os.PathLike path to a JSON file. A plain str is a path here, never inline JSON.

  • compress (bool | None)

  • compression_level (int | None)

Raises:

Exception – If the payload cannot be converted to JSON bytes, or the encoder is in an invalid state.

Return type:

None

add_asset(name: str, payload: dict[str, Any] | list[Any] | bytes | bytearray | memoryview | str | SupportsRead | PathLike[str], content_type: Literal['json'], *, compress: bool | None = None, compression_level: int | None = None) None[source]
add_asset(name: str, payload: bytes | bytearray | memoryview | str | SupportsRead | PathLike[str], content_type: Literal['text'], *, compress: bool | None = None, compression_level: int | None = None) None
add_asset(name: str, payload: bytes | bytearray | memoryview | str | SupportsRead | PathLike[str], content_type: Literal['binary'], *, compress: bool | None = None, compression_level: int | None = None) None
add_asset(name: str, payload: str | PathLike[str], content_type: Literal['file'], *, compress: bool | None = None, compression_level: int | None = None) None

Embed a custom asset under name.

Every asset carries a CRC32C integrity checksum, and payloads of 1 KiB or more are xz-compressed on disk by default (both transparent on read).

Parameters:
  • name (str) – Asset name, the key used to read it back (e.g. "params.json").

  • payload (JsonAssetPayload | TextAssetPayload | BinaryAssetPayload | StrPath) –

    The asset content; the accepted shapes depend on content_type:

    • for "json" (JsonAssetPayload): a dict / list (serialized via json.dumps), a JSON str, bytes-like JSON, a file-like object with .read(), or an os.PathLike whose file is read. Must yield valid UTF-8 JSON; the decoder will auto-parse it.

    • for "text" (TextAssetPayload): the same shapes, minus dict / list; must yield valid UTF-8.

    • for "binary" (BinaryAssetPayload): the same shapes as "text"; stored verbatim (e.g. a zipped shapefile or a GeoPackage).

    • for "file" (StrPath): a str or os.PathLike naming a file whose contents are read and stored as binary.

    Outside content_type="file", a plain str is always content, never a path; pass a pathlib.Path to read from disk (e.g. a Path with content_type="json" stores a JSON file the decoder will auto-parse).

  • content_type (AssetContentType) – One of "json", "text", "binary", or "file" (AssetContentType).

  • compress (bool | None, optional) – True requests xz storage compression, False stores the payload raw, None (default) follows the size policy. Even when requested, compression is kept only if it makes the stored form smaller, and very large payloads are probed on a prefix first so an already-compressed blob skips the full pass.

  • compression_level (int | None, optional) – xz preset 0–9 for the compression pass. Default is the writer’s preset (6). Assets are write-once and read-many, so the level only trades one-time write CPU against permanent file size.

Raises:
  • ValueError – If the payload does not satisfy content_type (e.g. malformed JSON, non-UTF-8 text, an unknown content type).

  • TypeError – If the payload shape is not accepted (e.g. a dict with content_type="text", or a non-path with content_type="file").

Return type:

None

remove_asset(name)[source]

Remove a named asset from a finalized bundle, reclaiming its bytes.

Available wherever add_asset() commits immediately: append mode, or create mode after the stream has closed. The directory drop and the compaction commit as one operation, so the asset’s payload bytes are actually gone from the file (not just unreferenced), and on any error the bundle is left untouched, the asset still present for a retry. The name (and any singleton-type claim, e.g. metadata.json) becomes free again, so remove-then-add is the way to replace an asset’s payload. For the canonical assets, re-add through the typed methods (add_metadata(), add_graph()); a generic add_asset() under a standardized name is refused, because the result would be invisible to the type-keyed readers.

Removing appended (post-stream) assets is cheap at any scale: the compaction rebuilds only the small post-stream tail and never touches the assignment stream, even when the stream is tens of gigabytes. Removing a pre-stream asset (the graph, or metadata added before streaming) costs one whole-file rewrite instead. Note that each immediate-commit add_asset (append mode, or create mode after the stream) leaves the superseded directory behind as a few dead bytes; the compaction here reclaims those too. For a bundle that arrives with dead space from other tooling, the raw _core.compact_bundle_in_place reclaims it directly, and the raw _core.BendlEncoder.remove_asset drops only the directory entry if you specifically need that form.

Parameters:

name (str) – The asset’s name, as listed by asset_names().

Raises:
  • KeyError – If no asset with that name exists in the bundle.

  • Exception – If the encoder is in create mode before the stream (just don’t add the asset), is currently streaming, or is closed.

Return type:

None

ben_stream(*, variant='twodelta')[source]

Open the single-use assignment stream context manager.

The embedded stream is always written in the BEN wire format; produce an XBEN bundle with compress_stream() after writing (XBEN is a whole-stream LZMA2 wrap, so it cannot be written live sample-by-sample).

Parameters:

variant (Variant, optional) – BEN encoding variant (Variant): "standard", "mkv_chain", or "twodelta". Default is "twodelta".

Returns:

BendlStreamSession – A single-use context manager. write each assignment inside the with block; a clean close finalizes the bundle, an exception leaves it unfinalized.

Raises:
  • ValueError – If variant is invalid.

  • Exception – If a stream was already written, append mode is active, or the encoder is closed.

Return type:

BendlStreamSession

close()[source]

Finalize (create mode) or finish (append mode) the bundle. Idempotent.

Return type:

None

The stream session

BendlEncoder.ben_stream() returns a BendlStreamSession. It is intentionally small: write assignments, then close. A bundle can have only one assignment stream.

from binary_ensemble import BendlEncoder

encoder = BendlEncoder("api-session.bendl", overwrite=True)
with encoder.ben_stream(variant="twodelta") as ensemble:
    for assignment in [[1, 1, 2, 2], [1, 2, 2, 2]]:
        ensemble.write(assignment)
class BendlStreamSession

Bases: object

Single-use context manager over a bundle’s assignment stream.

Obtained from binary_ensemble.bundle.BendlEncoder.ben_stream(); you don’t construct it directly. Write assignments with write() inside a with block. Closing the context cleanly finalizes the bundle; if the block exits via an exception the bundle is left unfinalized (recoverable, rather than stamped complete over a truncated stream).

close()

Finalize the bundle and close the stream. Idempotent after a clean close.

You usually do not call this directly; leaving the stream with block cleanly calls it. If the finalize fails (e.g. the disk fills while the directory is written), the encoder is poisoned: this and every later call keeps reporting the failure instead of claiming success, and the bundle on disk stays unfinalized.

write(assignment)

Encode a single assignment into the bundle’s stream.

Parameters:

assignment (Sequence[int]) – The plan as a sequence of district ids (e.g. a list[int]), one per node in dual-graph node order.

Returns:

None.

Raises:
  • ValueError – If the bundle carries a pre-stream graph and the assignment length does not equal the graph’s node count.

  • OSError – If the session is already closed, or the write fails.

Example

>>> ensemble.write([1, 1, 2, 2])

Decoder

BendlDecoder iterates the embedded stream and exposes bundle inspection methods.

Method

Use

len(decoder) / count_samples()

Expanded number of samples

assignment_format()

"ben" or "xben" for the embedded stream

version() / is_complete()

Bundle header inspection

asset_names() / list_assets()

Asset directory inspection

verify()

Check every asset and stream checksum; raises on corruption

read_graph()

networkx.Graph rebuilt from graph.json, or None

read_metadata()

Parsed metadata.json, or None

read_node_permutation_map()

Parsed permutation map, or None

read_json_asset(name)

Parse a JSON asset

read_asset_bytes(name)

Raw bytes for any asset

extract_stream(path)

Copy the embedded stream out as .ben or .xben bytes

subsample_*()

Iterate only selected samples

from binary_ensemble import BendlDecoder

decoder = BendlDecoder("ensemble.bendl")

print(decoder.asset_names())
print(decoder.read_metadata())

for assignment in decoder.subsample_range(1, 3):
    print(assignment[:4])

Iteration rewinds on a fresh for loop. Do not drive two simultaneous loops from the same decoder object; open a second decoder if you need independent cursors.

class BendlDecoder(file_path)

Bases: object

Reader and iterator for a .bendl bundle.

Iterate the decoder to yield the embedded assignment stream one plan at a time (each a list[int] of district ids), and use len() for the sample count. Alongside the stream, a bundle carries assets (the dual graph, metadata, a node permutation map, and any custom blobs) exposed through the canonical getters (read_graph(), read_metadata(), read_node_permutation_map()) and the generic read_asset_bytes() / read_json_asset(). Inspect the directory with asset_names(), list_assets(), version(), and is_complete().

A decoder is a snapshot of the file it opened: if the bundle changes on disk afterwards (an in-place transform swaps in a rewritten file, or an append rewrites the directory), every data-reading call refuses with a clear error rather than mixing old and new bytes; open a fresh decoder to read the current file.

This decoder is bundle-only: opening it on a plain .ben/.xben stream raises and points the caller at BenDecoder. A finalized assets-only bundle (one written with no assignment stream) iterates to nothing with len() == 0.

Parameters:

file_path (StrPath) – Path to the input .bendl file (str or os.PathLike). Whether the embedded stream is BEN or XBEN is read from the bundle header; an XBEN stream warns about a one-time decompression startup cost.

Raises:
  • Exception – If file_path is not a bundle (use BenDecoder for plain streams), or its header cannot be parsed.

  • OSError – If the file cannot be opened.

Example

>>> from binary_ensemble import BendlDecoder
>>> dec = BendlDecoder("ensemble.bendl")
>>> graph = dec.read_graph()
>>> for assignment in dec:
...     ...
asset_names()

Names of every entry in the bundle’s directory, in directory order.

Returns:

list[str] – Asset names such as "graph.json" and "metadata.json".

asset_size(name, /)

Return the on-disk byte length of a named asset’s stored payload.

Read straight from the bundle directory; no decoding or copying. For assets stored xz-compressed (the "xz" flag in list_assets()), this is the compressed size; the decoded payload can be larger, so use len(read_asset_bytes(name)) for that.

Parameters:

name (str) – The asset’s name, as listed by asset_names().

Returns:

int – Stored byte length of the asset’s payload region.

Raises:

KeyError – If no asset with that name exists in the bundle.

assignment_format()

Return the container format of the embedded assignment stream.

Returns:

str"ben" or "xben".

count_samples()

Count the samples in the embedded stream.

The result is the expanded sample count (a frame repeating five identical samples contributes five). On a finalized bundle the count is read from the bundle header, so it never requires scanning the stream; it is cached either way, so repeated calls and len() are cheap.

Returns:

int – The number of samples in the bundle’s stream.

extract_stream(out_path, overwrite=False, allow_unfinalized=False)

Copy the embedded assignment stream out to a standalone .ben/.xben file.

The bytes are copied verbatim, so the result can be opened directly with BenDecoder(out_path, mode=dec.assignment_format()).

Parameters:
  • out_path (StrPath) – Path to write the extracted stream to (str or os.PathLike).

  • overwrite (bool, optional) – Replace out_path if it already exists. Default is False.

  • allow_unfinalized (bool, optional) – Permit extraction from a bundle that was never finalized (recovering a partial stream). Default is False.

Raises:

OSError – If out_path exists and overwrite is False, or the copy fails.

is_complete()

Whether the bundle was successfully finalized.

Returns:

boolTrue for a complete bundle, False for a recoverable partial bundle.

list_assets()

Return the full bundle directory.

Returns:

list[dict] – Each dict has name, type, offset, len, and flags. flags is a list of string tags such as "json", "xz", and "checksum".

read_asset_bytes(name, /)

Read the (decoded) bytes of a named asset as a Python bytes object.

Parameters:

name (str) – The asset’s name, as listed by asset_names().

Returns:

bytes – The asset’s decoded payload.

Raises:

KeyError – If no asset with that name exists in the bundle.

read_graph()

Read the bundle’s graph.json asset as a NetworkX graph, or None if absent.

The stored adjacency-format JSON is rebuilt into a live graph via networkx.readwrite.json_graph.adjacency_graph, so its node order matches the order assignments were written in and it can be handed straight to consumers like GerryChain’s Partition. The result is a networkx.Graph, or a networkx.MultiGraph if the stored adjacency declares itself a multigraph. The raw JSON is still available through read_json_asset(“graph.json”).

read_json_asset(name, /)

Parse a JSON asset into a Python object (dict, list, …).

Parameters:

name (str) – The asset’s name, as listed by asset_names().

Returns:

The parsed JSON value.

Raises:
  • KeyError – If no asset with that name exists in the bundle.

  • Exception – If the asset is not valid UTF-8 JSON.

read_metadata()

Read the bundle’s metadata.json asset as parsed JSON, or None if absent.

read_node_permutation_map()

Read the bundle’s node_permutation_map.json asset as parsed JSON, or None if absent.

stream_size()

Return the on-disk byte length of the embedded assignment stream.

Read straight from the bundle header’s stream_len field; no decoding or copying. This is the size of the stream region as stored (BEN bytes, or compressed XBEN bytes), the same bytes extract_stream would copy out. For an unfinalized bundle the stream is taken to extend to the directory (or EOF), matching recovery extraction.

Returns:

int – Byte length of the embedded stream region; 0 for an assets-only bundle.

Example

>>> BendlDecoder("ensemble.bendl").stream_size()
40110
subsample_every(step, offset=1)

Restrict iteration to every step-th sample.

Parameters:
  • step (int) – Stride between kept samples (e.g. 10 keeps every tenth sample).

  • offset (int, optional) – 1-indexed position of the first kept sample. Default is 1.

Returns:

BendlDecoderself, for chaining into a for loop.

Raises:

Exception – If step or offset is 0 (both are 1-based).

subsample_indices(indices, /)

Restrict iteration to the samples at the given 1-indexed positions.

Skipped samples are never materialized as Python lists, and where the encoding variant allows it (standard, mkv_chain) whole frames are skipped without being unpacked.

Parameters:

indices (Sequence[int]) – The 1-indexed sample numbers to keep. Duplicates are dropped; an unsorted list is sorted, with a UserWarning.

Returns:

BendlDecoderself, so the call can be chained into a for loop.

Raises:

Exception – If indices is empty, contains 0 (indices are 1-based), or contains an index greater than the number of samples in the stream.

subsample_range(start, end, /)

Restrict iteration to a contiguous, 1-indexed inclusive range of samples.

Parameters:
  • start (int) – First sample number to keep (1-indexed, inclusive).

  • end (int) – Last sample number to keep (1-indexed, inclusive).

Returns:

BendlDecoderself, for chaining into a for loop.

Raises:

Exception – If start is 0, end is less than start, or end is greater than the number of samples in the stream.

Example

>>> list(BendlDecoder("ensemble.bendl").subsample_range(10, 15))
# samples 10, 11, 12, 13, 14, and 15
verify()

Verify the bundle’s integrity: asset and stream checksums, plus the header sample count.

Scans the raw on-disk bytes of every asset and of the assignment stream and compares them against the CRC32C checksums recorded when the bundle was written, then walks the stream’s frame boundaries to confirm the decoded sample count matches the (unchecksummed) header sample_count. Iterating or subsampling a decoder reads the stream without checking the checksums (partial reads cannot prove a whole-stream checksum) and trusts the header count for finalized bundles, so call this when integrity matters, e.g. after downloading a bundle or before an important run.

Raises:

Exception – If any asset checksum or the stream checksum does not match the on-disk bytes, if the header sample_count disagrees with the decoded stream, or if the bundle is unfinalized (an unfinalized bundle’s stream checksum and sample count are not authoritative).

Example

>>> dec = BendlDecoder("ensemble.bendl")
>>> dec.verify()  # raises on any corruption
version()

Return the bundle’s format version as a (major, minor) tuple.

Returns:

tuple[int, int] – Bundle format version.

Whole-bundle transforms

These functions preserve bundle assets while rewriting the embedded stream.

from binary_ensemble import compress_stream, relabel_bundle

relabel_bundle("ensemble.bendl", out_file="api-sorted.bendl", sort="mlc")
compress_stream("api-sorted.bendl", out_file="api-archive.bendl")

Both transforms take an optional out_file: pass one to create a new file (overwrite=True replaces an existing one), or leave it off to atomically replace the input in place.

compress_stream(path, out_file=None, overwrite=False)[source]

Recompress a bundle’s embedded BEN stream to XBEN, preserving every asset.

All assets (graph, metadata, node_permutation_map, custom blobs) are preserved by decoded payload, name, type, and JSON flag; storage compression is normalized to the writer’s default policy. An assets-only bundle (empty stream) recompresses to an empty XBEN bundle.

Parameters:
  • path (StrPath) – Path to the source .bendl bundle (str or os.PathLike).

  • out_file (StrPath | None, optional) – Destination path for the recompressed bundle (str or os.PathLike), leaving path untouched. Default is None which recompresses in place: the result is written to a temp file and atomically swapped over path.

  • overwrite (bool, optional) – Replace out_file if it already exists. Irrelevant in place, which always replaces path. Default is False.

Raises:

OSError – If out_file exists and overwrite is False.

Return type:

None

relabel_bundle(path, out_file=None, sort='mlc', key=None, overwrite=False)[source]

Reorder a BEN bundle’s graph and relabel its stream to match.

Reorders the embedded graph.json, rewrites every assignment into the new node order, and writes a fresh bundle storing the reordered graph and a node_permutation_map.json (so the reordering is reversible). Metadata and custom assets are preserved. This is the bundle-level form of the CLI’s ben relabel ordering flow, typically run to shrink a bundle before an XBEN recompress.

Only BEN bundles are supported (relabel before compressing to XBEN); the source must carry a graph.

Parameters:
  • path (StrPath) – Path to the source .bendl bundle (str or os.PathLike). Must hold a BEN (not XBEN) stream and a graph.json.

  • out_file (StrPath | None, optional) – Destination path for the relabeled bundle (str or os.PathLike), leaving path untouched. Default is None which relabels in place: the result is written to a temp file and atomically swapped over path.

  • sort (SortMethod, optional) – The ordering (SortMethod): "mlc" (multi-level clustering), "rcm" (reverse Cuthill-McKee), or "key" (sort by the node attribute named in key). Default is "mlc".

  • key (str | None, optional) – Node attribute to sort by, e.g. key="GEOID". Required with (and only valid with) sort="key". Default is None.

  • overwrite (bool, optional) – Replace out_file if it already exists. Irrelevant in place, which always replaces path. Default is False.

Raises:
  • ValueError – If sort / key is invalid, or if the bundle has no graph or a non-BEN stream.

  • OSError – If out_file exists and overwrite is False.

Return type:

None