Skip to content

Formats

anycfg.formats: the contract a format implements and the registry that chooses one. The built-in formats and their dialects are described in the Formats guide.

The contract

anycfg.formats.Format

Base class for a configuration format.

A format turns document text into data and, optionally, data back into text. It never touches a file, a stream or the environment: the loader reads the bytes, decodes them and hands the text over, so confinement, encoding and the policy apply to every format alike.

Subclass it, set the class attributes, override :meth:loads and any of the optional methods, then register the class with :func:anycfg.formats.register_format (or the anycfg.formats entry-point group).

Class attributes

name: The registry name, lower-case ("yaml"). aliases: Other names format= accepts (("yml",)). suffixes: File suffixes this format claims, lower-case with the dot ((".yaml", ".yml")); matched case-insensitively, longest first. priority: Breaks a tie when two formats claim the same suffix. load_options: The keywords :meth:loads accepts; any other is refused with TypeError before the format is called. dump_options: The keywords :meth:dumps accepts, likewise. binary: When true, :meth:loads receives bytes instead of text.

Capabilities follow from what is overridden: :func:capabilities reports "load_all" when :meth:loads_all is and "dump" when :meth:dumps is. A format holds no state, so one instance serves every load.

Errors: a malformed document raises :class:~anycfg.exceptions.ParseError, translated from the parser's own error and raised after the except block, so neither __cause__ nor __context__ keeps the parser's error (which may hold the document), and carrying no document text itself.

aliases = () class-attribute

binary = False class-attribute

dump_options = frozenset() class-attribute

load_options = frozenset() class-attribute

name = '' class-attribute

priority = 0 class-attribute

suffixes = () class-attribute

__repr__()

Source code in src/anycfg/formats/_base.py
103
104
def __repr__(self) -> str:
    return f"<{type(self).__name__} {self.name!r}>"

claims_document(data) classmethod

Whether data, a whole document as str or bytes, names this format.

By default: a #! first line naming it or one of its aliases (#!json, #!/usr/bin/env yaml). A format whose documents carry their own marker (YAML's %YAML) overrides this, calling super(). It must decide from the document's start and never by trying to parse.

Source code in src/anycfg/formats/_base.py
57
58
59
60
61
62
63
64
65
66
67
@classmethod
def claims_document(cls, data: _ty.Union[str, bytes]) -> bool:
    """Whether *data*, a whole document as `str` or `bytes`, names this format.

    By default: a ``#!`` first line naming it or one of its aliases
    (``#!json``, ``#!/usr/bin/env yaml``). A format whose documents carry
    their own marker (YAML's ``%YAML``) overrides this, calling ``super()``.
    It must decide from the document's start and never by trying to parse.
    """
    name = shebang_name(data)
    return name is not None and (name == cls.name or name in cls.aliases)

dumps(obj, /, **options)

Serialize obj. Override for a format that can write.

Raises :class:~anycfg.exceptions.ConfigTypeError for a value the format cannot represent, before anything is written.

Source code in src/anycfg/formats/_base.py
 95
 96
 97
 98
 99
100
101
def dumps(self, obj: _ty.Any, /, **options: _ty.Any) -> str:
    """Serialize *obj*. Override for a format that can write.

    Raises :class:`~anycfg.exceptions.ConfigTypeError` for a value the
    format cannot represent, before anything is written.
    """
    raise NotImplementedError

loads(data, /, *, context=None, **options)

Parse one document from data and return plain data.

context is None for a direct call. A format with includes reads them through context.include(...), and refuses them without one.

Source code in src/anycfg/formats/_base.py
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def loads(
    self,
    data: _ty.Any,
    /,
    *,
    context: "_ty.Optional[LoadContext]" = None,
    **options: _ty.Any,
) -> _ty.Any:
    """Parse one document from *data* and return plain data.

    *context* is `None` for a direct call. A format with includes reads
    them through ``context.include(...)``, and refuses them without one.
    """
    raise NotImplementedError

loads_all(data, /, *, context=None, **options)

Yield every document in data. Override for a multi-document format.

Source code in src/anycfg/formats/_base.py
84
85
86
87
88
89
90
91
92
93
def loads_all(
    self,
    data: _ty.Any,
    /,
    *,
    context: "_ty.Optional[LoadContext]" = None,
    **options: _ty.Any,
) -> _ty.Iterator[_ty.Any]:
    """Yield every document in *data*. Override for a multi-document format."""
    yield self.loads(data, context=context, **options)

anycfg.formats.capabilities(fmt)

What fmt can do: "load", plus "load_all" and "dump" when overridden.

Source code in src/anycfg/formats/_base.py
149
150
151
152
153
154
155
156
157
def capabilities(fmt: "_ty.Union[Format, _ty.Type[Format]]") -> _ty.FrozenSet[str]:
    """What *fmt* can do: ``"load"``, plus ``"load_all"`` and ``"dump"`` when overridden."""
    cls = fmt if isinstance(fmt, type) else type(fmt)
    found = {"load"}
    if _overrides(cls, "loads_all"):
        found.add("load_all")
    if _overrides(cls, "dumps"):
        found.add("dump")
    return frozenset(found)

The registry

anycfg.formats.register_format(cls=None, *, replace=False)

Register a format class; usable bare or as a decorator.

Raises ValueError when another class holds one of its names, or claims one of its suffixes at the same priority, unless replace is true. A registered format can be selected by documents, like a built-in.

Source code in src/anycfg/formats/__init__.py
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
def register_format(
    cls: "_ty.Optional[_ty.Type[Format]]" = None, *, replace: bool = False
) -> _ty.Any:
    """Register a format class; usable bare or as a decorator.

    Raises `ValueError` when another class holds one of its names, or claims
    one of its suffixes at the same priority, unless *replace* is true. A
    registered format can be selected by documents, like a built-in.
    """

    def apply(target: "_ty.Type[Format]") -> "_ty.Type[Format]":
        _ensure_loaded()
        cls = _validate(target)
        _update(lambda t: _add(t, cls, _REGISTERED, replace))
        return target

    return apply if cls is None else apply(cls)

anycfg.formats.unregister_format(name)

Remove the format registered under name; return whether one was.

Source code in src/anycfg/formats/__init__.py
239
240
241
242
243
244
245
246
247
def unregister_format(name: str) -> bool:
    """Remove the format registered under *name*; return whether one was."""
    _ensure_loaded()

    def remove(t: _Tables) -> bool:
        entry = t.by_name.get(name.lower())
        return _remove(t, entry.cls.name) if entry is not None else False

    return _update(remove)

anycfg.formats.names()

Every registered format name, sorted.

Source code in src/anycfg/formats/__init__.py
250
251
252
253
def names() -> "_ty.List[str]":
    """Every registered format name, sorted."""
    _ensure_loaded()
    return sorted(_tables.entries)

anycfg.formats.get(name, *, allowed=None)

The format class registered under name or one of its aliases.

allowed, when given, is the set of format names that may be returned; anything else is refused like an unknown name.

Raises:

Type Description
UnsupportedFormatError

For a name nothing registers, listing the names that are.

Source code in src/anycfg/formats/__init__.py
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
def get(
    name: str, *, allowed: "_ty.Optional[_ty.Collection[str]]" = None
) -> "_ty.Type[Format]":
    """The format class registered under *name* or one of its aliases.

    *allowed*, when given, is the set of format names that may be returned;
    anything else is refused like an unknown name.

    Raises:
        UnsupportedFormatError: For a name nothing registers, listing the names
            that are.
    """
    _ensure_loaded()
    entry = _tables.by_name.get(str(name).lower())
    if entry is None:
        raise UnsupportedFormatError(
            f"unknown format {name!r}; registered: {', '.join(names())}"
        )
    _refuse_unreachable(entry, allowed, name)
    return entry.cls

anycfg.formats.for_filename(filename, *, allowed=None)

Like :func:find_for_filename, but raises when no format claims the name.

Raises:

Type Description
UnsupportedFormatError

Naming the registered formats.

Source code in src/anycfg/formats/__init__.py
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
def for_filename(
    filename: str, *, allowed: "_ty.Optional[_ty.Collection[str]]" = None
) -> "_ty.Type[Format]":
    """Like :func:`find_for_filename`, but raises when no format claims the name.

    Raises:
        UnsupportedFormatError: Naming the registered formats.
    """
    found = find_for_filename(filename, allowed=allowed)
    if found is None:
        raise UnsupportedFormatError(
            f"no format reads {filename}; pass format=<name> "
            f"(registered: {', '.join(names())})"
        )
    return found

anycfg.formats.find_for_filename(filename, *, allowed=None)

The format a file name selects, or None when none claims it.

filename is a base name or a path; only the last component is used.

Source code in src/anycfg/formats/__init__.py
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
def find_for_filename(
    filename: str, *, allowed: "_ty.Optional[_ty.Collection[str]]" = None
) -> "_ty.Optional[_ty.Type[Format]]":
    """The format a file name selects, or `None` when none claims it.

    *filename* is a base name or a path; only the last component is used.
    """
    _ensure_loaded()
    tables = _tables  # one snapshot for the whole lookup
    base = str(filename).replace("\\", "/").rsplit("/", 1)[-1].lower()
    if not base:
        return None
    # Every registered suffix that fits in the name's tail, longest first:
    # "a.tar.json" asks ".tar.json", then ".json". Only the last `longest`
    # characters are ever examined, so the lookup is linear in the name.
    longest = max((len(suffix) for suffix in tables.by_suffix), default=0)
    tail = base[-longest:] if longest else ""
    position = tail.find(".")
    while position != -1:
        claims = tables.by_suffix.get(tail[position:])
        if claims and base.endswith(tail[position:]):
            return _first_reachable(claims, allowed, filename)
        position = tail.find(".", position + 1)
    for entry in tables.entries.values():
        claims_name = getattr(entry.cls, "claims_name", None)
        if callable(claims_name) and claims_name(base):
            _refuse_unreachable(entry, allowed, filename)
            return entry.cls
    return None

anycfg.formats.document_formats()

The formats a document may select by default: built-in and code-registered.

Source code in src/anycfg/formats/__init__.py
256
257
258
259
260
261
def document_formats() -> "_ty.FrozenSet[str]":
    """The formats a document may select by default: built-in and code-registered."""
    _ensure_loaded()
    return frozenset(
        name for name, entry in _tables.entries.items() if entry.origin != _ENTRY_POINT
    )

anycfg.formats.ENTRY_POINT_GROUP = 'anycfg.formats' module-attribute