From c7687772b077a8fbd45c3085a1603fde14bae2aa Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Thu, 16 Mar 2023 21:57:21 +0100 Subject: [PATCH] Update compare.py docs --- implementations/python/preserves/compare.py | 54 ++++++++-- python/compare/index.html | 113 ++++++++++++++------ python/search/search_index.json | 2 +- 3 files changed, 130 insertions(+), 39 deletions(-) diff --git a/implementations/python/preserves/compare.py b/implementations/python/preserves/compare.py index b8cc685..58a5042 100644 --- a/implementations/python/preserves/compare.py +++ b/implementations/python/preserves/compare.py @@ -1,4 +1,33 @@ -"""TODO""" +"""Preserves specifies a [total ordering](https://preserves.dev/preserves.html#total-order) and +an [equivalence](https://preserves.dev/preserves.html#equivalence) between terms. This module +implements the ordering and equivalence relations. + +```python +>>> cmp("bzz", "c") +-1 +>>> cmp(True, []) +-1 +>>> lt("bzz", "c") +True +>>> eq("bzz", "c") +False + +``` + +Note that the ordering relates more values than Python's built-in ordering: + +```python +>>> [1, 2, 2] < [1, 2, "3"] +Traceback (most recent call last): + .. +TypeError: '<' not supported between instances of 'int' and 'str' + +>>> lt([1, 2, 2], [1, 2, "3"]) +True + +``` + +""" import numbers from enum import Enum @@ -52,19 +81,23 @@ def type_number(v): return TypeNumber.SEQUENCE def cmp(a, b): - """TODO""" + """Returns `-1` if `a` < `b`, or `0` if `a` = `b`, or `1` if `a` > `b` according to the + [Preserves total order](https://preserves.dev/preserves.html#total-order).""" return _cmp(preserve(a), preserve(b)) def lt(a, b): - """TODO""" + """Returns `True` iff `a` < `b` according to the [Preserves total + order](https://preserves.dev/preserves.html#total-order).""" return cmp(a, b) < 0 def le(a, b): - """TODO""" + """Returns `True` iff `a` ≤ `b` according to the [Preserves total + order](https://preserves.dev/preserves.html#total-order).""" return cmp(a, b) <= 0 def eq(a, b): - """TODO""" + """Returns `True` iff `a` = `b` according to the [Preserves equivalence + relation](https://preserves.dev/preserves.html#equivalence).""" return _eq(preserve(a), preserve(b)) key = cmp_to_key(cmp) @@ -72,11 +105,18 @@ _key = key _sorted = sorted def sorted(iterable, *, key=lambda x: x, reverse=False): - """TODO""" + """Returns a sorted list built from `iterable`, extracting a sort key using `key`, and + ordering according to the [Preserves total + order](https://preserves.dev/preserves.html#total-order). Directly analogous to the + [built-in Python `sorted` + routine](https://docs.python.org/3/library/functions.html#sorted), except uses the + Preserves order instead of Python's less-than relation. + + """ return _sorted(iterable, key=lambda x: _key(key(x)), reverse=reverse) def sorted_items(d): - """TODO""" + """Given a dictionary `d`, yields a list of `(key, value)` tuples sorted by `key`.""" return sorted(d.items(), key=_item_key) def _eq_sequences(aa, bb): diff --git a/python/compare/index.html b/python/compare/index.html index 2bad84d..cb86621 100644 --- a/python/compare/index.html +++ b/python/compare/index.html @@ -529,7 +529,27 @@
-

TODO

+

Preserves specifies a total ordering and +an equivalence between terms. This module +implements the ordering and equivalence relations.

+
>>> cmp("bzz", "c")
+-1
+>>> cmp(True, [])
+-1
+>>> lt("bzz", "c")
+True
+>>> eq("bzz", "c")
+False
+
+

Note that the ordering relates more values than Python's built-in ordering:

+
>>> [1, 2, 2] < [1, 2, "3"]
+Traceback (most recent call last):
+  ..
+TypeError: '<' not supported between instances of 'int' and 'str'
+
+>>> lt([1, 2, 2], [1, 2, "3"])
+True
+
@@ -555,14 +575,17 @@
-

TODO

+

Returns -1 if a < b, or 0 if a = b, or 1 if a > b according to the +Preserves total order.

Source code in preserves/compare.py -
54
-55
-56
def cmp(a, b):
-    """TODO"""
+        
83
+84
+85
+86
def cmp(a, b):
+    """Returns `-1` if `a` < `b`, or `0` if `a` = `b`, or `1` if `a` > `b` according to the
+    [Preserves total order](https://preserves.dev/preserves.html#total-order)."""
     return _cmp(preserve(a), preserve(b))
 
@@ -582,14 +605,17 @@
-

TODO

+

Returns True iff a = b according to the Preserves equivalence +relation.

Source code in preserves/compare.py -
66
-67
-68
def eq(a, b):
-    """TODO"""
+        
 98
+ 99
+100
+101
def eq(a, b):
+    """Returns `True` iff `a` = `b` according to the [Preserves equivalence
+    relation](https://preserves.dev/preserves.html#equivalence)."""
     return _eq(preserve(a), preserve(b))
 
@@ -609,14 +635,17 @@
-

TODO

+

Returns True iff ab according to the Preserves total +order.

Source code in preserves/compare.py -
62
-63
-64
def le(a, b):
-    """TODO"""
+        
93
+94
+95
+96
def le(a, b):
+    """Returns `True` iff `a` ≤ `b` according to the [Preserves total
+    order](https://preserves.dev/preserves.html#total-order)."""
     return cmp(a, b) <= 0
 
@@ -636,14 +665,17 @@
-

TODO

+

Returns True iff a < b according to the Preserves total +order.

Source code in preserves/compare.py -
58
-59
-60
def lt(a, b):
-    """TODO"""
+        
88
+89
+90
+91
def lt(a, b):
+    """Returns `True` iff `a` < `b` according to the [Preserves total
+    order](https://preserves.dev/preserves.html#total-order)."""
     return cmp(a, b) < 0
 
@@ -663,14 +695,33 @@
-

TODO

+

Returns a sorted list built from iterable, extracting a sort key using key, and +ordering according to the Preserves total +order. Directly analogous to the +built-in Python sorted +routine, except uses the +Preserves order instead of Python's less-than relation.

Source code in preserves/compare.py -
74
-75
-76
def sorted(iterable, *, key=lambda x: x, reverse=False):
-    """TODO"""
+        
107
+108
+109
+110
+111
+112
+113
+114
+115
+116
def sorted(iterable, *, key=lambda x: x, reverse=False):
+    """Returns a sorted list built from `iterable`, extracting a sort key using `key`, and
+    ordering according to the [Preserves total
+    order](https://preserves.dev/preserves.html#total-order). Directly analogous to the
+    [built-in Python `sorted`
+    routine](https://docs.python.org/3/library/functions.html#sorted), except uses the
+    Preserves order instead of Python's less-than relation.
+
+    """
     return _sorted(iterable, key=lambda x: _key(key(x)), reverse=reverse)
 
@@ -690,14 +741,14 @@
-

TODO

+

Given a dictionary d, yields a list of (key, value) tuples sorted by key.

Source code in preserves/compare.py -
78
-79
-80
def sorted_items(d):
-    """TODO"""
+        
118
+119
+120
def sorted_items(d):
+    """Given a dictionary `d`, yields a list of `(key, value)` tuples sorted by `key`."""
     return sorted(d.items(), key=_item_key)
 
diff --git a/python/search/search_index.json b/python/search/search_index.json index e06c1f2..8377f5c 100644 --- a/python/search/search_index.json +++ b/python/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Overview","text":"
pip install preserves\n

This package (preserves on pypi.org) implements Preserves for Python 3.x. It provides the core semantics as well as both the human-readable text syntax (a superset of JSON) and machine-oriented binary format (including canonicalization) for Preserves. It also implements Preserves Schema and Preserves Path.

  • Main package API: preserves
"},{"location":"#what-is-preserves","title":"What is Preserves?","text":"

Preserves is a data model, with associated serialization formats.

It supports records with user-defined labels, embedded references, and the usual suite of atomic and compound data types, including binary data as a distinct type from text strings. Its annotations allow separation of data from metadata such as comments, trace information, and provenance information.

Preserves departs from many other data languages in defining how to compare two values. Comparison is based on the data model, not on syntax or on data structures of any particular implementation language.

"},{"location":"#mapping-between-preserves-values-and-python-values","title":"Mapping between Preserves values and Python values","text":"

Preserves Values are categorized in the following way:

                      Value = Atom\n                            | Compound\n                            | Embedded\n\n                       Atom = Boolean\n                            | Float\n                            | Double\n                            | SignedInteger\n                            | String\n                            | ByteString\n                            | Symbol\n\n                   Compound = Record\n                            | Sequence\n                            | Set\n                            | Dictionary\n

Python's strings, byte strings, integers, booleans, and double-precision floats stand directly for their Preserves counterparts. Small wrapper classes for Float and Symbol complete the suite of atomic types.

Python's lists and tuples correspond to Preserves Sequences, and dicts and sets to Dictionary and Set values, respectively. Preserves Records are represented by a Record class. Finally, embedded values are represented by a small Embedded wrapper class.

"},{"location":"api/","title":"The top-level preserves package","text":"
import preserves\n

The main package re-exports a subset of the exports of its constituent modules:

  • From preserves.values:

    • Annotated
    • Embedded
    • Float
    • ImmutableDict
    • Record
    • Symbol
    • annotate
    • is_annotated
    • preserve
    • strip_annotations
  • From preserves.error:

    • DecodeError
    • EncodeError
    • ShortPacket
  • From preserves.binary:

    • Decoder
    • Encoder
    • canonicalize
    • decode
    • decode_with_annotations
    • encode
  • From preserves.text:

    • Formatter
    • Parser
    • parse
    • parse_with_annotations
    • stringify
  • From preserves.compare:

    • cmp
  • From preserves.merge:

    • merge

It also exports the compare and fold modules themselves, permitting patterns like

>>> from preserves import *\n>>> compare.cmp(123, 234)\n-1\n

Finally, it provides a few utility aliases for common tasks:

"},{"location":"api/#preserves.dumps","title":"dumps = stringify module-attribute","text":"

This alias for stringify provides a familiar pythonesque name for converting a Preserves Value to a string.

"},{"location":"api/#preserves.loads","title":"loads = parse module-attribute","text":"

This alias for parse provides a familiar pythonesque name for converting a string to a Preserves Value.

"},{"location":"binary/","title":"Machine-oriented binary syntax","text":"

The preserves.binary module implements the Preserves machine-oriented binary syntax.

The main entry points are functions encode, canonicalize, decode, and decode_with_annotations.

>>> encode(Record(Symbol('hi'), []))\nb'\\xb4\\xb3\\x02hi\\x84'\n>>> decode(b'\\xb4\\xb3\\x02hi\\x84')\n#hi()\n
"},{"location":"binary/#preserves.binary.Decoder","title":"Decoder(packet=b'', include_annotations=False, decode_embedded=lambda x: x)","text":"

Bases: BinaryCodec

Implementation of a decoder for the machine-oriented binary Preserves syntax.

Parameters:

Name Type Description Default packet bytes

initial contents of the input buffer; may subsequently be extended by calling extend.

b'' include_annotations bool

if True, wrap each value and subvalue in an Annotated object.

False decode_embedded

function accepting a Value and returning a possibly-decoded form of that value suitable for placing into an Embedded object.

lambda x: x

Normal usage is to supply a buffer, and keep calling next until a ShortPacket exception is raised:

>>> d = Decoder(b'\\xa0{\\xb1\\x05hello\\x85\\xb3\\x01x\\xb5\\x84')\n>>> d.next()\n123\n>>> d.next()\n'hello'\n>>> d.next()\n()\n>>> d.next()\nTraceback (most recent call last):\n  ...\npreserves.error.ShortPacket: Short packet\n

Alternatively, keep calling try_next until it yields None, which is not in the domain of Preserves Values:

>>> d = Decoder(b'\\xa0{\\xb1\\x05hello\\x85\\xb3\\x01x\\xb5\\x84')\n>>> d.try_next()\n123\n>>> d.try_next()\n'hello'\n>>> d.try_next()\n()\n>>> d.try_next()\n

For convenience, Decoder implements the iterator interface, backing it with try_next, so you can simply iterate over all complete values in an input:

>>> d = Decoder(b'\\xa0{\\xb1\\x05hello\\x85\\xb3\\x01x\\xb5\\x84')\n>>> list(d)\n[123, 'hello', ()]\n
>>> for v in Decoder(b'\\xa0{\\xb1\\x05hello\\x85\\xb3\\x01x\\xb5\\x84'):\n...     print(repr(v))\n123\n'hello'\n()\n

Supply include_annotations=True to read annotations alongside the annotated values:

>>> d = Decoder(b'\\xa0{\\xb1\\x05hello\\x85\\xb3\\x01x\\xb5\\x84', include_annotations=True)\n>>> list(d)\n[123, 'hello', @#x ()]\n

If you are incrementally reading from, say, a socket, you can use extend to add new input as if comes available:

>>> d = Decoder(b'\\xa0{\\xb1\\x05he')\n>>> d.try_next()\n123\n>>> d.try_next() # returns None because the input is incomplete\n>>> d.extend(b'llo')\n>>> d.try_next()\n'hello'\n>>> d.try_next()\n

Attributes:

Name Type Description packet bytes

buffered input waiting to be processed

index int

read position within packet

Source code in preserves/binary.py
def __init__(self, packet=b'', include_annotations=False, decode_embedded=lambda x: x):\n    super(Decoder, self).__init__()\n    self.packet = packet\n    self.index = 0\n    self.include_annotations = include_annotations\n    self.decode_embedded = decode_embedded\n
"},{"location":"binary/#preserves.binary.Decoder.extend","title":"extend(data)","text":"

Appends data to the remaining bytes in self.packet, trimming already-processed bytes from the front of self.packet and resetting self.index to zero.

Source code in preserves/binary.py
def extend(self, data):\n\"\"\"Appends `data` to the remaining bytes in `self.packet`, trimming already-processed\n    bytes from the front of `self.packet` and resetting `self.index` to zero.\"\"\"\n    self.packet = self.packet[self.index:] + data\n    self.index = 0\n
"},{"location":"binary/#preserves.binary.Decoder.next","title":"next()","text":"

Reads the next complete Value from the internal buffer, raising ShortPacket if too few bytes are available, or DecodeError if the input is invalid somehow.

Source code in preserves/binary.py
def next(self):\n\"\"\"Reads the next complete `Value` from the internal buffer, raising\n    [ShortPacket][preserves.error.ShortPacket] if too few bytes are available, or\n    [DecodeError][preserves.error.DecodeError] if the input is invalid somehow.\n\n    \"\"\"\n    tag = self.nextbyte()\n    if tag == 0x80: return self.wrap(False)\n    if tag == 0x81: return self.wrap(True)\n    if tag == 0x82: return self.wrap(Float.from_bytes(self.nextbytes(4)))\n    if tag == 0x83: return self.wrap(struct.unpack('>d', self.nextbytes(8))[0])\n    if tag == 0x84: raise DecodeError('Unexpected end-of-stream marker')\n    if tag == 0x85:\n        a = self.next()\n        v = self.next()\n        return self.unshift_annotation(a, v)\n    if tag == 0x86:\n        if self.decode_embedded is None:\n            raise DecodeError('No decode_embedded function supplied')\n        return self.wrap(Embedded(self.decode_embedded(self.next())))\n    if tag >= 0x90 and tag <= 0x9f: return self.wrap(tag - (0xa0 if tag > 0x9c else 0x90))\n    if tag >= 0xa0 and tag <= 0xaf: return self.wrap(self.nextint(tag - 0xa0 + 1))\n    if tag == 0xb0: return self.wrap(self.nextint(self.varint()))\n    if tag == 0xb1: return self.wrap(self.nextbytes(self.varint()).decode('utf-8'))\n    if tag == 0xb2: return self.wrap(self.nextbytes(self.varint()))\n    if tag == 0xb3: return self.wrap(Symbol(self.nextbytes(self.varint()).decode('utf-8')))\n    if tag == 0xb4:\n        vs = self.nextvalues()\n        if not vs: raise DecodeError('Too few elements in encoded record')\n        return self.wrap(Record(vs[0], vs[1:]))\n    if tag == 0xb5: return self.wrap(tuple(self.nextvalues()))\n    if tag == 0xb6: return self.wrap(frozenset(self.nextvalues()))\n    if tag == 0xb7: return self.wrap(ImmutableDict.from_kvs(self.nextvalues()))\n    raise DecodeError('Invalid tag: ' + hex(tag))\n
"},{"location":"binary/#preserves.binary.Decoder.try_next","title":"try_next()","text":"

Like next, but returns None instead of raising ShortPacket.

Source code in preserves/binary.py
def try_next(self):\n\"\"\"Like [next][preserves.binary.Decoder.next], but returns `None` instead of raising\n    [ShortPacket][preserves.error.ShortPacket].\"\"\"\n    start = self.index\n    try:\n        return self.next()\n    except ShortPacket:\n        self.index = start\n        return None\n
"},{"location":"binary/#preserves.binary.Encoder","title":"Encoder(encode_embedded=lambda x: x, canonicalize=False, include_annotations=None)","text":"

Bases: BinaryCodec

Implementation of an encoder for the machine-oriented binary Preserves syntax.

>>> e = Encoder()\n>>> e.append(123)\n>>> e.append('hello')\n>>> e.append(annotate([], Symbol('x')))\n>>> e.contents()\nb'\\xa0{\\xb1\\x05hello\\x85\\xb3\\x01x\\xb5\\x84'\n

Parameters:

Name Type Description Default encode_embedded

function accepting an Embedded.embeddedValue and returning a Value for serialization.

lambda x: x canonicalize bool

if True, ensures the serialized data are in canonical form. This is slightly more work than producing potentially-non-canonical output.

False include_annotations bool | None

if None, includes annotations in the output only when canonicalize is False, because canonical serialization of values demands omission of annotations. If explicitly True or False, however, annotations will be included resp. excluded no matter the canonicalize setting. This can be used to get canonical ordering (canonicalize=True) and annotations (include_annotations=True).

None

Attributes:

Name Type Description buffer bytearray

accumulator for the output of the encoder

Source code in preserves/binary.py
def __init__(self,\n             encode_embedded=lambda x: x,\n             canonicalize=False,\n             include_annotations=None):\n    super(Encoder, self).__init__()\n    self.buffer = bytearray()\n    self._encode_embedded = encode_embedded\n    self._canonicalize = canonicalize\n    if include_annotations is None:\n        self.include_annotations = not self._canonicalize\n    else:\n        self.include_annotations = include_annotations\n
"},{"location":"binary/#preserves.binary.Encoder.append","title":"append(v)","text":"

Extend self.buffer with an encoding of v.

Source code in preserves/binary.py
def append(self, v):\n\"\"\"Extend `self.buffer` with an encoding of `v`.\"\"\"\n    v = preserve(v)\n    if hasattr(v, '__preserve_write_binary__'):\n        v.__preserve_write_binary__(self)\n    elif v is False:\n        self.buffer.append(0x80)\n    elif v is True:\n        self.buffer.append(0x81)\n    elif isinstance(v, float):\n        self.buffer.append(0x83)\n        self.buffer.extend(struct.pack('>d', v))\n    elif isinstance(v, numbers.Number):\n        if v >= -3 and v <= 12:\n            self.buffer.append(0x90 + (v if v >= 0 else v + 16))\n        else:\n            self.encodeint(v)\n    elif isinstance(v, bytes):\n        self.encodebytes(2, v)\n    elif isinstance(v, basestring_):\n        self.encodebytes(1, v.encode('utf-8'))\n    elif isinstance(v, list):\n        self.encodevalues(5, v)\n    elif isinstance(v, tuple):\n        self.encodevalues(5, v)\n    elif isinstance(v, set):\n        self.encodeset(v)\n    elif isinstance(v, frozenset):\n        self.encodeset(v)\n    elif isinstance(v, dict):\n        self.encodedict(v)\n    else:\n        try:\n            i = iter(v)\n        except TypeError:\n            i = None\n        if i is None:\n            self.cannot_encode(v)\n        else:\n            self.encodevalues(5, i)\n
"},{"location":"binary/#preserves.binary.Encoder.contents","title":"contents()","text":"

Returns a bytes constructed from the contents of self.buffer.

Source code in preserves/binary.py
def contents(self):\n\"\"\"Returns a `bytes` constructed from the contents of `self.buffer`.\"\"\"\n    return bytes(self.buffer)\n
"},{"location":"binary/#preserves.binary.Encoder.reset","title":"reset()","text":"

Clears self.buffer to a fresh empty bytearray.

Source code in preserves/binary.py
def reset(self):\n\"\"\"Clears `self.buffer` to a fresh empty `bytearray`.\"\"\"\n    self.buffer = bytearray()\n
"},{"location":"binary/#preserves.binary.canonicalize","title":"canonicalize(v, **kwargs)","text":"

As encode, but sets canonicalize=True in the Encoder constructor.

Source code in preserves/binary.py
def canonicalize(v, **kwargs):\n\"\"\"As [encode][preserves.binary.encode], but sets `canonicalize=True` in the\n    [Encoder][preserves.binary.Encoder] constructor.\n\n    \"\"\"\n    return encode(v, canonicalize=True, **kwargs)\n
"},{"location":"binary/#preserves.binary.decode","title":"decode(bs, **kwargs)","text":"

Yields the first complete encoded value from bs, passing kwargs through to the Decoder constructor. Raises exceptions as per next.

Parameters:

Name Type Description Default bs bytes

encoded data to decode

required Source code in preserves/binary.py
def decode(bs, **kwargs):\n\"\"\"Yields the first complete encoded value from `bs`, passing `kwargs` through to the\n    [Decoder][preserves.binary.Decoder] constructor. Raises exceptions as per\n    [next][preserves.binary.Decoder.next].\n\n    Args:\n        bs (bytes): encoded data to decode\n\n    \"\"\"\n    return Decoder(packet=bs, **kwargs).next()\n
"},{"location":"binary/#preserves.binary.decode_with_annotations","title":"decode_with_annotations(bs, **kwargs)","text":"

Like decode, but supplying include_annotations=True to the Decoder constructor.

Source code in preserves/binary.py
def decode_with_annotations(bs, **kwargs):\n\"\"\"Like [decode][preserves.binary.decode], but supplying `include_annotations=True` to the\n    [Decoder][preserves.binary.Decoder] constructor.\"\"\"\n    return Decoder(packet=bs, include_annotations=True, **kwargs).next()\n
"},{"location":"binary/#preserves.binary.encode","title":"encode(v, **kwargs)","text":"

Encode a single Value v to a byte string. Any supplied kwargs are passed on to the underlying Encoder constructor.

Source code in preserves/binary.py
def encode(v, **kwargs):\n\"\"\"Encode a single `Value` `v` to a byte string. Any supplied `kwargs` are passed on to the\n    underlying [Encoder][preserves.binary.Encoder] constructor.\"\"\"\n    e = Encoder(**kwargs)\n    e.append(v)\n    return e.contents()\n
"},{"location":"compare/","title":"Comparing Values","text":"

TODO

"},{"location":"compare/#preserves.compare.cmp","title":"cmp(a, b)","text":"

TODO

Source code in preserves/compare.py
def cmp(a, b):\n\"\"\"TODO\"\"\"\n    return _cmp(preserve(a), preserve(b))\n
"},{"location":"compare/#preserves.compare.eq","title":"eq(a, b)","text":"

TODO

Source code in preserves/compare.py
def eq(a, b):\n\"\"\"TODO\"\"\"\n    return _eq(preserve(a), preserve(b))\n
"},{"location":"compare/#preserves.compare.le","title":"le(a, b)","text":"

TODO

Source code in preserves/compare.py
def le(a, b):\n\"\"\"TODO\"\"\"\n    return cmp(a, b) <= 0\n
"},{"location":"compare/#preserves.compare.lt","title":"lt(a, b)","text":"

TODO

Source code in preserves/compare.py
def lt(a, b):\n\"\"\"TODO\"\"\"\n    return cmp(a, b) < 0\n
"},{"location":"compare/#preserves.compare.sorted","title":"sorted(iterable, *, key=lambda x: x, reverse=False)","text":"

TODO

Source code in preserves/compare.py
def sorted(iterable, *, key=lambda x: x, reverse=False):\n\"\"\"TODO\"\"\"\n    return _sorted(iterable, key=lambda x: _key(key(x)), reverse=reverse)\n
"},{"location":"compare/#preserves.compare.sorted_items","title":"sorted_items(d)","text":"

TODO

Source code in preserves/compare.py
def sorted_items(d):\n\"\"\"TODO\"\"\"\n    return sorted(d.items(), key=_item_key)\n
"},{"location":"error/","title":"Codec errors","text":"

TODO

"},{"location":"error/#preserves.error.DecodeError","title":"DecodeError","text":"

Bases: ValueError

TODO

"},{"location":"error/#preserves.error.EncodeError","title":"EncodeError","text":"

Bases: ValueError

TODO

"},{"location":"error/#preserves.error.ShortPacket","title":"ShortPacket","text":"

Bases: DecodeError

TODO

"},{"location":"fold/","title":"Traversing values","text":"

TODO

"},{"location":"fold/#preserves.fold.map_embeddeds","title":"map_embeddeds(f, v)","text":"

TODO

Source code in preserves/fold.py
def map_embeddeds(f, v):\n\"\"\"TODO\"\"\"\n    def walk(v):\n        if isinstance(v, Embedded):\n            return f(v.embeddedValue)\n        elif isinstance(v, (list, tuple)):\n            return tuple(walk(w) for w in v)\n        elif isinstance(v, (set, frozenset)):\n            return frozenset(walk(w) for w in v)\n        elif isinstance(v, dict):\n            return ImmutableDict.from_kvs(walk(w) for w in dict_kvs(v))\n        elif isinstance(v, Record):\n            return Record(walk(v.key), walk(v.fields))\n        else:\n            return v\n    return walk(v)\n
"},{"location":"merge/","title":"Merging values","text":"

TODO

"},{"location":"merge/#preserves.merge.merge","title":"merge(v0, *vs, merge_embedded=None)","text":"

TODO

Source code in preserves/merge.py
def merge(v0, *vs, merge_embedded=None):\n\"\"\"TODO\"\"\"\n    v = v0\n    for vN in vs:\n        v = merge2(v, vN, merge_embedded=merge_embedded)\n    return v\n
"},{"location":"merge/#preserves.merge.merge2","title":"merge2(a, b, merge_embedded=None)","text":"

TODO

Source code in preserves/merge.py
def merge2(a, b, merge_embedded=None):\n\"\"\"TODO\"\"\"\n    if a == b:\n        return a\n    if isinstance(a, (list, tuple)) and isinstance(b, (list, tuple)):\n        return merge_seq(a, b)\n    if isinstance(a, (set, frozenset)) and isinstance(b, (set, frozenset)):\n        _die()\n    if isinstance(a, dict) and isinstance(b, dict):\n        r = {}\n        for (ak, av) in a.items():\n            bv = b.get(ak, None)\n            r[ak] = av if bv is None else merge2(av, bv, merge_embedded=merge_embedded)\n        for (bk, bv) in b.items():\n            if bk not in r:\n                r[bk] = bv\n        return r\n    if isinstance(a, Record) and isinstance(b, Record):\n        return Record(merge2(a.key, b.key, merge_embedded=merge_embedded),\n                      merge_seq(a.fields, b.fields, merge_embedded=merge_embedded))\n    if isinstance(a, Embedded) and isinstance(b, Embedded):\n        m = (merge_embedded or merge_embedded_id)(a.embeddedValue, b.embeddedValue)\n        if m is None: _die()\n        return Embedded(m)\n    _die()\n
"},{"location":"path/","title":"Preserves Path","text":"

TODO (document main behaviour)

"},{"location":"path/#preserves.path.Predicate","title":"Predicate = syntax.Predicate module-attribute","text":"

TODO

"},{"location":"path/#preserves.path.Selector","title":"Selector = syntax.Selector module-attribute","text":"

TODO

"},{"location":"path/#preserves.path.syntax","title":"syntax = load_schema_file(pathlib.Path(__file__).parent / 'path.prb').path module-attribute","text":"

TODO

"},{"location":"path/#preserves.path.parse","title":"parse(s)","text":"

TODO

Source code in preserves/path.py
def parse(s):\n\"\"\"TODO\"\"\"\n    return parse_selector(Parser(s))\n
"},{"location":"schema/","title":"Preserves Schema","text":"

This is an implementation of Preserves Schema for Python 3.

TODO

"},{"location":"schema/#preserves.schema.meta","title":"meta = load_schema_file(__metaschema_filename).schema module-attribute","text":"

TODO

"},{"location":"schema/#preserves.schema.Compiler","title":"Compiler()","text":"

TODO

Source code in preserves/schema.py
def __init__(self):\n    self.root = Namespace(())\n
"},{"location":"schema/#preserves.schema.Definition","title":"Definition(*args, **kwargs)","text":"

Bases: SchemaObject

TODO

Source code in preserves/schema.py
def __init__(self, *args, **kwargs):\n    self._fields = args\n    if self.SIMPLE:\n        if self.EMPTY:\n            if len(args) != 0:\n                raise TypeError('%s takes no arguments' % (self._constructor_name(),))\n        else:\n            if len(args) != 1:\n                raise TypeError('%s needs exactly one argument' % (self._constructor_name(),))\n            self.value = args[0]\n    else:\n        i = 0\n        for arg in args:\n            if i >= len(self.FIELD_NAMES):\n                raise TypeError('%s given too many positional arguments' % (self._constructor_name(),))\n            setattr(self, self.SAFE_FIELD_NAMES[i], arg)\n            i = i + 1\n        for (argname, arg) in kwargs.items():\n            if hasattr(self, argname):\n                raise TypeError('%s given duplicate attribute: %r' % (self._constructor_name, argname))\n            if argname not in self.SAFE_FIELD_NAMES:\n                raise TypeError('%s given unknown attribute: %r' % (self._constructor_name, argname))\n            setattr(self, argname, arg)\n            i = i + 1\n        if i != len(self.FIELD_NAMES):\n            raise TypeError('%s needs argument(s) %r' % (self._constructor_name(), self.FIELD_NAMES))\n
"},{"location":"schema/#preserves.schema.Enumeration","title":"Enumeration()","text":"

Bases: SchemaObject

TODO

Source code in preserves/schema.py
def __init__(self):\n    raise TypeError('Cannot create instance of Enumeration')\n
"},{"location":"schema/#preserves.schema.Namespace","title":"Namespace(prefix)","text":"

TODO

Source code in preserves/schema.py
def __init__(self, prefix):\n    self._prefix = prefix\n
"},{"location":"schema/#preserves.schema.SchemaDecodeFailed","title":"SchemaDecodeFailed(cls, p, v, failures=None)","text":"

Bases: ValueError

TODO

Source code in preserves/schema.py
def __init__(self, cls, p, v, failures=None):\n    super().__init__()\n    self.cls = cls\n    self.pattern = p\n    self.value = v\n    self.failures = [] if failures is None else failures\n
"},{"location":"schema/#preserves.schema.SchemaObject","title":"SchemaObject","text":"

TODO

"},{"location":"schema/#preserves.schema.SchemaObject.__preserve__","title":"__preserve__()","text":"

TODO

Source code in preserves/schema.py
def __preserve__(self):\n\"\"\"TODO\"\"\"\n    raise NotImplementedError('Subclass responsibility')\n
"},{"location":"schema/#preserves.schema.SchemaObject.decode","title":"decode(v) classmethod","text":"

TODO

Source code in preserves/schema.py
@classmethod\ndef decode(cls, v):\n\"\"\"TODO\"\"\"\n    raise NotImplementedError('Subclass responsibility')\n
"},{"location":"schema/#preserves.schema.SchemaObject.try_decode","title":"try_decode(v) classmethod","text":"

TODO

Source code in preserves/schema.py
@classmethod\ndef try_decode(cls, v):\n\"\"\"TODO\"\"\"\n    try:\n        return cls.decode(v)\n    except SchemaDecodeFailed:\n        return None\n
"},{"location":"schema/#preserves.schema.encode","title":"encode(p, v)","text":"

TODO

Source code in preserves/schema.py
def encode(p, v):\n\"\"\"TODO\"\"\"\n    if hasattr(v, '__escape_schema__'):\n        return preserve(v.__escape_schema__())\n    if p == ANY:\n        return v\n    if p.key == NAMED:\n        return encode(p[1], safegetattr(v, p[0].name))\n    if p.key == ATOM:\n        return v\n    if p.key == EMBEDDED:\n        return Embedded(v)\n    if p.key == LIT:\n        return p[0]\n    if p.key == SEQOF:\n        return tuple(encode(p[0], w) for w in v)\n    if p.key == SETOF:\n        return set(encode(p[0], w) for w in v)\n    if p.key == DICTOF:\n        return dict((encode(p[0], k), encode(p[1], w)) for (k, w) in v.items())\n    if p.key == REF:\n        return preserve(v)\n    if p.key == REC:\n        return Record(encode(p[0], v), encode(p[1], v))\n    if p.key == TUPLE:\n        return tuple(encode(pp, v) for pp in p[0])\n    if p.key == TUPLE_PREFIX:\n        return tuple(encode(pp, v) for pp in p[0]) + encode(p[1], v)\n    if p.key == DICT:\n        return dict((k, encode(pp, v)) for (k, pp) in p[0].items())\n    if p.key == AND:\n        return merge(*[encode(pp, v) for pp in p[0]])\n    raise ValueError(f'Bad schema {p}')\n
"},{"location":"schema/#preserves.schema.extend","title":"extend(cls)","text":"

TODO

Source code in preserves/schema.py
def extend(cls):\n\"\"\"TODO\"\"\"\n    def extender(f):\n        setattr(cls, f.__name__, f)\n        return f\n    return extender\n
"},{"location":"schema/#preserves.schema.load_schema_file","title":"load_schema_file(filename)","text":"

TODO

Source code in preserves/schema.py
def load_schema_file(filename):\n\"\"\"TODO\"\"\"\n    c = Compiler()\n    c.load(filename)\n    return c.root\n
"},{"location":"text/","title":"Human-readable text syntax","text":"

TODO

"},{"location":"text/#preserves.text.Formatter","title":"Formatter(format_embedded=lambda x: x, indent=None, with_commas=False, trailing_comma=False, include_annotations=True)","text":"

Bases: TextCodec

TODO

TODO

Source code in preserves/text.py
def __init__(self,\n             format_embedded=lambda x: x,\n             indent=None,\n             with_commas=False,\n             trailing_comma=False,\n             include_annotations=True):\n\"\"\"TODO\"\"\"\n    super(Formatter, self).__init__()\n    self.indent_delta = 0 if indent is None else indent\n    self.indent_distance = 0\n    self.with_commas = with_commas\n    self.trailing_comma = trailing_comma\n    self.chunks = []\n    self._format_embedded = format_embedded\n    self.include_annotations = include_annotations\n
"},{"location":"text/#preserves.text.Formatter.append","title":"append(v)","text":"

TODO

Source code in preserves/text.py
def append(self, v):\n\"\"\"TODO\"\"\"\n    v = preserve(v)\n    if hasattr(v, '__preserve_write_text__'):\n        v.__preserve_write_text__(self)\n    elif v is False:\n        self.chunks.append('#f')\n    elif v is True:\n        self.chunks.append('#t')\n    elif isinstance(v, float):\n        if math.isnan(v) or math.isinf(v):\n            self.chunks.append('#xd\"' + struct.pack('>d', v).hex() + '\"')\n        else:\n            self.chunks.append(repr(v))\n    elif isinstance(v, numbers.Number):\n        self.chunks.append('%d' % (v,))\n    elif isinstance(v, bytes):\n        self.chunks.append('#[%s]' % (base64.b64encode(v).decode('ascii'),))\n    elif isinstance(v, basestring_):\n        self.chunks.append('\"')\n        for c in v:\n            if c == '\"': self.chunks.append('\\\\\"')\n            else: self.write_stringlike_char(c)\n        self.chunks.append('\"')\n    elif isinstance(v, list):\n        self.write_seq('[', ']', v, self.append)\n    elif isinstance(v, tuple):\n        self.write_seq('[', ']', v, self.append)\n    elif isinstance(v, set):\n        self.write_seq('#{', '}', v, self.append)\n    elif isinstance(v, frozenset):\n        self.write_seq('#{', '}', v, self.append)\n    elif isinstance(v, dict):\n        def append_kv(kv):\n            self.append(kv[0])\n            self.chunks.append(': ')\n            self.append(kv[1])\n        self.write_seq('{', '}', v.items(), append_kv)\n    else:\n        try:\n            i = iter(v)\n        except TypeError:\n            i = None\n        if i is None:\n            self.cannot_format(v)\n        else:\n            self.write_seq('[', ']', i, self.append)\n
"},{"location":"text/#preserves.text.Formatter.contents","title":"contents()","text":"

TODO

Source code in preserves/text.py
def contents(self):\n\"\"\"TODO\"\"\"\n    return u''.join(self.chunks)\n
"},{"location":"text/#preserves.text.Parser","title":"Parser(input_buffer='', include_annotations=False, parse_embedded=lambda x: x)","text":"

Bases: TextCodec

TODO

TODO

Source code in preserves/text.py
def __init__(self, input_buffer=u'', include_annotations=False, parse_embedded=lambda x: x):\n\"\"\"TODO\"\"\"\n    super(Parser, self).__init__()\n    self.input_buffer = input_buffer\n    self.index = 0\n    self.include_annotations = include_annotations\n    self.parse_embedded = parse_embedded\n
"},{"location":"text/#preserves.text.Parser.__iter__","title":"__iter__()","text":"

TODO

Source code in preserves/text.py
def __iter__(self):\n\"\"\"TODO\"\"\"\n    return self\n
"},{"location":"text/#preserves.text.Parser.extend","title":"extend(text)","text":"

TODO

Source code in preserves/text.py
def extend(self, text):\n\"\"\"TODO\"\"\"\n    self.input_buffer = self.input_buffer[self.index:] + text\n    self.index = 0\n
"},{"location":"text/#preserves.text.Parser.next","title":"next()","text":"

TODO

Source code in preserves/text.py
def next(self):\n\"\"\"TODO\"\"\"\n    self.skip_whitespace()\n    c = self.peek()\n    if c == '\"':\n        self.skip()\n        return self.wrap(self.read_string('\"'))\n    if c == '|':\n        self.skip()\n        return self.wrap(Symbol(self.read_string('|')))\n    if c in ';@':\n        annotations = self.gather_annotations()\n        v = self.next()\n        if self.include_annotations:\n            v.annotations = annotations + v.annotations\n        return v\n    if c == ':':\n        raise DecodeError('Unexpected key/value separator between items')\n    if c == '#':\n        self.skip()\n        c = self.nextchar()\n        if c == 'f': return self.wrap(False)\n        if c == 't': return self.wrap(True)\n        if c == '{': return self.wrap(frozenset(self.upto('}')))\n        if c == '\"': return self.wrap(self.read_literal_binary())\n        if c == 'x':\n            c = self.nextchar()\n            if c == '\"': return self.wrap(self.read_hex_binary())\n            if c == 'f': return self.wrap(self.read_hex_float(4))\n            if c == 'd': return self.wrap(self.read_hex_float(8))\n            raise DecodeError('Invalid #x syntax')\n        if c == '[': return self.wrap(self.read_base64_binary())\n        if c == '=':\n            old_ann = self.include_annotations\n            self.include_annotations = True\n            bs_val = self.next()\n            self.include_annotations = old_ann\n            if len(bs_val.annotations) > 0:\n                raise DecodeError('Annotations not permitted after #=')\n            bs_val = bs_val.item\n            if not isinstance(bs_val, bytes):\n                raise DecodeError('ByteString must follow #=')\n            return self.wrap(Decoder(bs_val, include_annotations = self.include_annotations).next())\n        if c == '!':\n            if self.parse_embedded is None:\n                raise DecodeError('No parse_embedded function supplied')\n            return self.wrap(Embedded(self.parse_embedded(self.next())))\n        raise DecodeError('Invalid # syntax')\n    if c == '<':\n        self.skip()\n        vs = self.upto('>')\n        if len(vs) == 0:\n            raise DecodeError('Missing record label')\n        return self.wrap(Record(vs[0], vs[1:]))\n    if c == '[':\n        self.skip()\n        return self.wrap(self.upto(']'))\n    if c == '{':\n        self.skip()\n        return self.wrap(self.read_dictionary())\n    if c in '>]}':\n        raise DecodeError('Unexpected ' + c)\n    self.skip()\n    return self.wrap(self.read_raw_symbol_or_number([c]))\n
"},{"location":"text/#preserves.text.Parser.try_next","title":"try_next()","text":"

TODO

Source code in preserves/text.py
def try_next(self):\n\"\"\"TODO\"\"\"\n    start = self.index\n    try:\n        return self.next()\n    except ShortPacket:\n        self.index = start\n        return None\n
"},{"location":"text/#preserves.text.TextCodec","title":"TextCodec","text":"

Bases: object

TODO

"},{"location":"text/#preserves.text.parse","title":"parse(bs, **kwargs)","text":"

TODO

Source code in preserves/text.py
def parse(bs, **kwargs):\n\"\"\"TODO\"\"\"\n    return Parser(input_buffer=bs, **kwargs).next()\n
"},{"location":"text/#preserves.text.parse_with_annotations","title":"parse_with_annotations(bs, **kwargs)","text":"

TODO

Source code in preserves/text.py
def parse_with_annotations(bs, **kwargs):\n\"\"\"TODO\"\"\"\n    return Parser(input_buffer=bs, include_annotations=True, **kwargs).next()\n
"},{"location":"text/#preserves.text.stringify","title":"stringify(v, **kwargs)","text":"

TODO

Source code in preserves/text.py
def stringify(v, **kwargs):\n\"\"\"TODO\"\"\"\n    e = Formatter(**kwargs)\n    e.append(v)\n    return e.contents()\n
"},{"location":"values/","title":"Representations of Values","text":"

TODO

"},{"location":"values/#preserves.values.Annotated","title":"Annotated(item)","text":"

Bases: object

TODO

TODO

Source code in preserves/values.py
def __init__(self, item):\n\"\"\"TODO\"\"\"\n    self.annotations = []\n    self.item = item\n
"},{"location":"values/#preserves.values.Annotated.peel","title":"peel()","text":"

TODO

Source code in preserves/values.py
def peel(self):\n\"\"\"TODO\"\"\"\n    return strip_annotations(self, 1)\n
"},{"location":"values/#preserves.values.Annotated.strip","title":"strip(depth=inf)","text":"

TODO

Source code in preserves/values.py
def strip(self, depth=inf):\n\"\"\"TODO\"\"\"\n    return strip_annotations(self, depth)\n
"},{"location":"values/#preserves.values.Embedded","title":"Embedded(value)","text":"

TODO

TODO

Source code in preserves/values.py
def __init__(self, value):\n\"\"\"TODO\"\"\"\n    self.embeddedValue = value\n
"},{"location":"values/#preserves.values.Float","title":"Float(value)","text":"

Bases: object

TODO

TODO

Source code in preserves/values.py
def __init__(self, value):\n\"\"\"TODO\"\"\"\n    self.value = value\n
"},{"location":"values/#preserves.values.Float.from_bytes","title":"from_bytes(bs) staticmethod","text":"

TODO

Source code in preserves/values.py
@staticmethod\ndef from_bytes(bs):\n\"\"\"TODO\"\"\"\n    vf = struct.unpack('>I', bs)[0]\n    if (vf & 0x7f800000) == 0x7f800000:\n        # NaN or inf. Preserve quiet/signalling bit by manually expanding to double-precision.\n        sign = vf >> 31\n        payload = vf & 0x007fffff\n        dbs = struct.pack('>Q', (sign << 63) | 0x7ff0000000000000 | (payload << 29))\n        return Float(struct.unpack('>d', dbs)[0])\n    else:\n        return Float(struct.unpack('>f', bs)[0])\n
"},{"location":"values/#preserves.values.ImmutableDict","title":"ImmutableDict(*args, **kwargs)","text":"

Bases: dict

TODO

TODO

Source code in preserves/values.py
def __init__(self, *args, **kwargs):\n\"\"\"TODO\"\"\"\n    if hasattr(self, '__hash'): raise TypeError('Immutable')\n    super(ImmutableDict, self).__init__(*args, **kwargs)\n    self.__hash = None\n
"},{"location":"values/#preserves.values.ImmutableDict.from_kvs","title":"from_kvs(kvs) staticmethod","text":"

TODO

Source code in preserves/values.py
@staticmethod\ndef from_kvs(kvs):\n\"\"\"TODO\"\"\"\n    i = iter(kvs)\n    result = ImmutableDict()\n    result_proxy = super(ImmutableDict, result)\n    try:\n        while True:\n            k = next(i)\n            try:\n                v = next(i)\n            except StopIteration:\n                raise DecodeError(\"Missing dictionary value\")\n            result_proxy.__setitem__(k, v)\n    except StopIteration:\n        pass\n    return result\n
"},{"location":"values/#preserves.values.Record","title":"Record(key, fields)","text":"

Bases: object

TODO

TODO

Source code in preserves/values.py
def __init__(self, key, fields):\n\"\"\"TODO\"\"\"\n    self.key = key\n    self.fields = tuple(fields)\n    self.__hash = None\n
"},{"location":"values/#preserves.values.Record.makeBasicConstructor","title":"makeBasicConstructor(label, fieldNames) staticmethod","text":"

TODO

Source code in preserves/values.py
@staticmethod\ndef makeBasicConstructor(label, fieldNames):\n\"\"\"TODO\"\"\"\n    if type(fieldNames) == str:\n        fieldNames = fieldNames.split()\n    arity = len(fieldNames)\n    def ctor(*fields):\n        if len(fields) != arity:\n            raise Exception(\"Record: cannot instantiate %r expecting %d fields with %d fields\"%(\n                label,\n                arity,\n                len(fields)))\n        return Record(label, fields)\n    ctor.constructorInfo = RecordConstructorInfo(label, arity)\n    ctor.isClassOf = lambda v: \\\n                     isinstance(v, Record) and v.key == label and len(v.fields) == arity\n    def ensureClassOf(v):\n        if not ctor.isClassOf(v):\n            raise TypeError(\"Record: expected %r/%d, got %r\" % (label, arity, v))\n        return v\n    ctor.ensureClassOf = ensureClassOf\n    for fieldIndex in range(len(fieldNames)):\n        fieldName = fieldNames[fieldIndex]\n        # Stupid python scoping bites again\n        def getter(fieldIndex):\n            return lambda v: ensureClassOf(v)[fieldIndex]\n        setattr(ctor, '_' + fieldName, getter(fieldIndex))\n    return ctor\n
"},{"location":"values/#preserves.values.Record.makeConstructor","title":"makeConstructor(labelSymbolText, fieldNames) staticmethod","text":"

TODO

Source code in preserves/values.py
@staticmethod\ndef makeConstructor(labelSymbolText, fieldNames):\n\"\"\"TODO\"\"\"\n    return Record.makeBasicConstructor(Symbol(labelSymbolText), fieldNames)\n
"},{"location":"values/#preserves.values.RecordConstructorInfo","title":"RecordConstructorInfo(key, arity)","text":"

Bases: object

TODO

TODO

Source code in preserves/values.py
def __init__(self, key, arity):\n\"\"\"TODO\"\"\"\n    self.key = key\n    self.arity = arity\n
"},{"location":"values/#preserves.values.Symbol","title":"Symbol(name)","text":"

Bases: object

TODO

TODO

Source code in preserves/values.py
def __init__(self, name):\n\"\"\"TODO\"\"\"\n    self.name = name.name if isinstance(name, Symbol) else name\n
"},{"location":"values/#preserves.values.annotate","title":"annotate(v, *anns)","text":"

TODO

Source code in preserves/values.py
def annotate(v, *anns):\n\"\"\"TODO\"\"\"\n    if not is_annotated(v):\n        v = Annotated(v)\n    for a in anns:\n        v.annotations.append(a)\n    return v\n
"},{"location":"values/#preserves.values.cmp_floats","title":"cmp_floats(a, b)","text":"

TODO

Source code in preserves/values.py
def cmp_floats(a, b):\n\"\"\"TODO\"\"\"\n    a = float_to_int(a)\n    b = float_to_int(b)\n    if a & 0x8000000000000000: a = a ^ 0x7fffffffffffffff\n    if b & 0x8000000000000000: b = b ^ 0x7fffffffffffffff\n    return a - b\n
"},{"location":"values/#preserves.values.dict_kvs","title":"dict_kvs(d)","text":"

TODO

Source code in preserves/values.py
def dict_kvs(d):\n\"\"\"TODO\"\"\"\n    for k in d:\n        yield k\n        yield d[k]\n
"},{"location":"values/#preserves.values.is_annotated","title":"is_annotated(v)","text":"

TODO

Source code in preserves/values.py
def is_annotated(v):\n\"\"\"TODO\"\"\"\n    return isinstance(v, Annotated)\n
"},{"location":"values/#preserves.values.preserve","title":"preserve(v)","text":"

TODO

Source code in preserves/values.py
def preserve(v):\n\"\"\"TODO\"\"\"\n    while hasattr(v, '__preserve__'):\n        v = v.__preserve__()\n    return v\n
"},{"location":"values/#preserves.values.strip_annotations","title":"strip_annotations(v, depth=inf)","text":"

TODO

Source code in preserves/values.py
def strip_annotations(v, depth=inf):\n\"\"\"TODO\"\"\"\n    if depth == 0: return v\n    if not is_annotated(v): return v\n\n    next_depth = depth - 1\n    def walk(v):\n        return strip_annotations(v, next_depth)\n\n    v = v.item\n    if isinstance(v, Record):\n        return Record(strip_annotations(v.key, depth), tuple(walk(f) for f in v.fields))\n    elif isinstance(v, list):\n        return tuple(walk(f) for f in v)\n    elif isinstance(v, tuple):\n        return tuple(walk(f) for f in v)\n    elif isinstance(v, set):\n        return frozenset(walk(f) for f in v)\n    elif isinstance(v, frozenset):\n        return frozenset(walk(f) for f in v)\n    elif isinstance(v, dict):\n        return ImmutableDict.from_kvs(walk(f) for f in dict_kvs(v))\n    elif is_annotated(v):\n        raise ValueError('Improper annotation structure')\n    else:\n        return v\n
"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Overview","text":"
pip install preserves\n

This package (preserves on pypi.org) implements Preserves for Python 3.x. It provides the core semantics as well as both the human-readable text syntax (a superset of JSON) and machine-oriented binary format (including canonicalization) for Preserves. It also implements Preserves Schema and Preserves Path.

  • Main package API: preserves
"},{"location":"#what-is-preserves","title":"What is Preserves?","text":"

Preserves is a data model, with associated serialization formats.

It supports records with user-defined labels, embedded references, and the usual suite of atomic and compound data types, including binary data as a distinct type from text strings. Its annotations allow separation of data from metadata such as comments, trace information, and provenance information.

Preserves departs from many other data languages in defining how to compare two values. Comparison is based on the data model, not on syntax or on data structures of any particular implementation language.

"},{"location":"#mapping-between-preserves-values-and-python-values","title":"Mapping between Preserves values and Python values","text":"

Preserves Values are categorized in the following way:

                      Value = Atom\n                            | Compound\n                            | Embedded\n\n                       Atom = Boolean\n                            | Float\n                            | Double\n                            | SignedInteger\n                            | String\n                            | ByteString\n                            | Symbol\n\n                   Compound = Record\n                            | Sequence\n                            | Set\n                            | Dictionary\n

Python's strings, byte strings, integers, booleans, and double-precision floats stand directly for their Preserves counterparts. Small wrapper classes for Float and Symbol complete the suite of atomic types.

Python's lists and tuples correspond to Preserves Sequences, and dicts and sets to Dictionary and Set values, respectively. Preserves Records are represented by a Record class. Finally, embedded values are represented by a small Embedded wrapper class.

"},{"location":"api/","title":"The top-level preserves package","text":"
import preserves\n

The main package re-exports a subset of the exports of its constituent modules:

  • From preserves.values:

    • Annotated
    • Embedded
    • Float
    • ImmutableDict
    • Record
    • Symbol
    • annotate
    • is_annotated
    • preserve
    • strip_annotations
  • From preserves.error:

    • DecodeError
    • EncodeError
    • ShortPacket
  • From preserves.binary:

    • Decoder
    • Encoder
    • canonicalize
    • decode
    • decode_with_annotations
    • encode
  • From preserves.text:

    • Formatter
    • Parser
    • parse
    • parse_with_annotations
    • stringify
  • From preserves.compare:

    • cmp
  • From preserves.merge:

    • merge

It also exports the compare and fold modules themselves, permitting patterns like

>>> from preserves import *\n>>> compare.cmp(123, 234)\n-1\n

Finally, it provides a few utility aliases for common tasks:

"},{"location":"api/#preserves.dumps","title":"dumps = stringify module-attribute","text":"

This alias for stringify provides a familiar pythonesque name for converting a Preserves Value to a string.

"},{"location":"api/#preserves.loads","title":"loads = parse module-attribute","text":"

This alias for parse provides a familiar pythonesque name for converting a string to a Preserves Value.

"},{"location":"binary/","title":"Machine-oriented binary syntax","text":"

The preserves.binary module implements the Preserves machine-oriented binary syntax.

The main entry points are functions encode, canonicalize, decode, and decode_with_annotations.

>>> encode(Record(Symbol('hi'), []))\nb'\\xb4\\xb3\\x02hi\\x84'\n>>> decode(b'\\xb4\\xb3\\x02hi\\x84')\n#hi()\n
"},{"location":"binary/#preserves.binary.Decoder","title":"Decoder(packet=b'', include_annotations=False, decode_embedded=lambda x: x)","text":"

Bases: BinaryCodec

Implementation of a decoder for the machine-oriented binary Preserves syntax.

Parameters:

Name Type Description Default packet bytes

initial contents of the input buffer; may subsequently be extended by calling extend.

b'' include_annotations bool

if True, wrap each value and subvalue in an Annotated object.

False decode_embedded

function accepting a Value and returning a possibly-decoded form of that value suitable for placing into an Embedded object.

lambda x: x

Normal usage is to supply a buffer, and keep calling next until a ShortPacket exception is raised:

>>> d = Decoder(b'\\xa0{\\xb1\\x05hello\\x85\\xb3\\x01x\\xb5\\x84')\n>>> d.next()\n123\n>>> d.next()\n'hello'\n>>> d.next()\n()\n>>> d.next()\nTraceback (most recent call last):\n  ...\npreserves.error.ShortPacket: Short packet\n

Alternatively, keep calling try_next until it yields None, which is not in the domain of Preserves Values:

>>> d = Decoder(b'\\xa0{\\xb1\\x05hello\\x85\\xb3\\x01x\\xb5\\x84')\n>>> d.try_next()\n123\n>>> d.try_next()\n'hello'\n>>> d.try_next()\n()\n>>> d.try_next()\n

For convenience, Decoder implements the iterator interface, backing it with try_next, so you can simply iterate over all complete values in an input:

>>> d = Decoder(b'\\xa0{\\xb1\\x05hello\\x85\\xb3\\x01x\\xb5\\x84')\n>>> list(d)\n[123, 'hello', ()]\n
>>> for v in Decoder(b'\\xa0{\\xb1\\x05hello\\x85\\xb3\\x01x\\xb5\\x84'):\n...     print(repr(v))\n123\n'hello'\n()\n

Supply include_annotations=True to read annotations alongside the annotated values:

>>> d = Decoder(b'\\xa0{\\xb1\\x05hello\\x85\\xb3\\x01x\\xb5\\x84', include_annotations=True)\n>>> list(d)\n[123, 'hello', @#x ()]\n

If you are incrementally reading from, say, a socket, you can use extend to add new input as if comes available:

>>> d = Decoder(b'\\xa0{\\xb1\\x05he')\n>>> d.try_next()\n123\n>>> d.try_next() # returns None because the input is incomplete\n>>> d.extend(b'llo')\n>>> d.try_next()\n'hello'\n>>> d.try_next()\n

Attributes:

Name Type Description packet bytes

buffered input waiting to be processed

index int

read position within packet

Source code in preserves/binary.py
def __init__(self, packet=b'', include_annotations=False, decode_embedded=lambda x: x):\n    super(Decoder, self).__init__()\n    self.packet = packet\n    self.index = 0\n    self.include_annotations = include_annotations\n    self.decode_embedded = decode_embedded\n
"},{"location":"binary/#preserves.binary.Decoder.extend","title":"extend(data)","text":"

Appends data to the remaining bytes in self.packet, trimming already-processed bytes from the front of self.packet and resetting self.index to zero.

Source code in preserves/binary.py
def extend(self, data):\n\"\"\"Appends `data` to the remaining bytes in `self.packet`, trimming already-processed\n    bytes from the front of `self.packet` and resetting `self.index` to zero.\"\"\"\n    self.packet = self.packet[self.index:] + data\n    self.index = 0\n
"},{"location":"binary/#preserves.binary.Decoder.next","title":"next()","text":"

Reads the next complete Value from the internal buffer, raising ShortPacket if too few bytes are available, or DecodeError if the input is invalid somehow.

Source code in preserves/binary.py
def next(self):\n\"\"\"Reads the next complete `Value` from the internal buffer, raising\n    [ShortPacket][preserves.error.ShortPacket] if too few bytes are available, or\n    [DecodeError][preserves.error.DecodeError] if the input is invalid somehow.\n\n    \"\"\"\n    tag = self.nextbyte()\n    if tag == 0x80: return self.wrap(False)\n    if tag == 0x81: return self.wrap(True)\n    if tag == 0x82: return self.wrap(Float.from_bytes(self.nextbytes(4)))\n    if tag == 0x83: return self.wrap(struct.unpack('>d', self.nextbytes(8))[0])\n    if tag == 0x84: raise DecodeError('Unexpected end-of-stream marker')\n    if tag == 0x85:\n        a = self.next()\n        v = self.next()\n        return self.unshift_annotation(a, v)\n    if tag == 0x86:\n        if self.decode_embedded is None:\n            raise DecodeError('No decode_embedded function supplied')\n        return self.wrap(Embedded(self.decode_embedded(self.next())))\n    if tag >= 0x90 and tag <= 0x9f: return self.wrap(tag - (0xa0 if tag > 0x9c else 0x90))\n    if tag >= 0xa0 and tag <= 0xaf: return self.wrap(self.nextint(tag - 0xa0 + 1))\n    if tag == 0xb0: return self.wrap(self.nextint(self.varint()))\n    if tag == 0xb1: return self.wrap(self.nextbytes(self.varint()).decode('utf-8'))\n    if tag == 0xb2: return self.wrap(self.nextbytes(self.varint()))\n    if tag == 0xb3: return self.wrap(Symbol(self.nextbytes(self.varint()).decode('utf-8')))\n    if tag == 0xb4:\n        vs = self.nextvalues()\n        if not vs: raise DecodeError('Too few elements in encoded record')\n        return self.wrap(Record(vs[0], vs[1:]))\n    if tag == 0xb5: return self.wrap(tuple(self.nextvalues()))\n    if tag == 0xb6: return self.wrap(frozenset(self.nextvalues()))\n    if tag == 0xb7: return self.wrap(ImmutableDict.from_kvs(self.nextvalues()))\n    raise DecodeError('Invalid tag: ' + hex(tag))\n
"},{"location":"binary/#preserves.binary.Decoder.try_next","title":"try_next()","text":"

Like next, but returns None instead of raising ShortPacket.

Source code in preserves/binary.py
def try_next(self):\n\"\"\"Like [next][preserves.binary.Decoder.next], but returns `None` instead of raising\n    [ShortPacket][preserves.error.ShortPacket].\"\"\"\n    start = self.index\n    try:\n        return self.next()\n    except ShortPacket:\n        self.index = start\n        return None\n
"},{"location":"binary/#preserves.binary.Encoder","title":"Encoder(encode_embedded=lambda x: x, canonicalize=False, include_annotations=None)","text":"

Bases: BinaryCodec

Implementation of an encoder for the machine-oriented binary Preserves syntax.

>>> e = Encoder()\n>>> e.append(123)\n>>> e.append('hello')\n>>> e.append(annotate([], Symbol('x')))\n>>> e.contents()\nb'\\xa0{\\xb1\\x05hello\\x85\\xb3\\x01x\\xb5\\x84'\n

Parameters:

Name Type Description Default encode_embedded

function accepting an Embedded.embeddedValue and returning a Value for serialization.

lambda x: x canonicalize bool

if True, ensures the serialized data are in canonical form. This is slightly more work than producing potentially-non-canonical output.

False include_annotations bool | None

if None, includes annotations in the output only when canonicalize is False, because canonical serialization of values demands omission of annotations. If explicitly True or False, however, annotations will be included resp. excluded no matter the canonicalize setting. This can be used to get canonical ordering (canonicalize=True) and annotations (include_annotations=True).

None

Attributes:

Name Type Description buffer bytearray

accumulator for the output of the encoder

Source code in preserves/binary.py
def __init__(self,\n             encode_embedded=lambda x: x,\n             canonicalize=False,\n             include_annotations=None):\n    super(Encoder, self).__init__()\n    self.buffer = bytearray()\n    self._encode_embedded = encode_embedded\n    self._canonicalize = canonicalize\n    if include_annotations is None:\n        self.include_annotations = not self._canonicalize\n    else:\n        self.include_annotations = include_annotations\n
"},{"location":"binary/#preserves.binary.Encoder.append","title":"append(v)","text":"

Extend self.buffer with an encoding of v.

Source code in preserves/binary.py
def append(self, v):\n\"\"\"Extend `self.buffer` with an encoding of `v`.\"\"\"\n    v = preserve(v)\n    if hasattr(v, '__preserve_write_binary__'):\n        v.__preserve_write_binary__(self)\n    elif v is False:\n        self.buffer.append(0x80)\n    elif v is True:\n        self.buffer.append(0x81)\n    elif isinstance(v, float):\n        self.buffer.append(0x83)\n        self.buffer.extend(struct.pack('>d', v))\n    elif isinstance(v, numbers.Number):\n        if v >= -3 and v <= 12:\n            self.buffer.append(0x90 + (v if v >= 0 else v + 16))\n        else:\n            self.encodeint(v)\n    elif isinstance(v, bytes):\n        self.encodebytes(2, v)\n    elif isinstance(v, basestring_):\n        self.encodebytes(1, v.encode('utf-8'))\n    elif isinstance(v, list):\n        self.encodevalues(5, v)\n    elif isinstance(v, tuple):\n        self.encodevalues(5, v)\n    elif isinstance(v, set):\n        self.encodeset(v)\n    elif isinstance(v, frozenset):\n        self.encodeset(v)\n    elif isinstance(v, dict):\n        self.encodedict(v)\n    else:\n        try:\n            i = iter(v)\n        except TypeError:\n            i = None\n        if i is None:\n            self.cannot_encode(v)\n        else:\n            self.encodevalues(5, i)\n
"},{"location":"binary/#preserves.binary.Encoder.contents","title":"contents()","text":"

Returns a bytes constructed from the contents of self.buffer.

Source code in preserves/binary.py
def contents(self):\n\"\"\"Returns a `bytes` constructed from the contents of `self.buffer`.\"\"\"\n    return bytes(self.buffer)\n
"},{"location":"binary/#preserves.binary.Encoder.reset","title":"reset()","text":"

Clears self.buffer to a fresh empty bytearray.

Source code in preserves/binary.py
def reset(self):\n\"\"\"Clears `self.buffer` to a fresh empty `bytearray`.\"\"\"\n    self.buffer = bytearray()\n
"},{"location":"binary/#preserves.binary.canonicalize","title":"canonicalize(v, **kwargs)","text":"

As encode, but sets canonicalize=True in the Encoder constructor.

Source code in preserves/binary.py
def canonicalize(v, **kwargs):\n\"\"\"As [encode][preserves.binary.encode], but sets `canonicalize=True` in the\n    [Encoder][preserves.binary.Encoder] constructor.\n\n    \"\"\"\n    return encode(v, canonicalize=True, **kwargs)\n
"},{"location":"binary/#preserves.binary.decode","title":"decode(bs, **kwargs)","text":"

Yields the first complete encoded value from bs, passing kwargs through to the Decoder constructor. Raises exceptions as per next.

Parameters:

Name Type Description Default bs bytes

encoded data to decode

required Source code in preserves/binary.py
def decode(bs, **kwargs):\n\"\"\"Yields the first complete encoded value from `bs`, passing `kwargs` through to the\n    [Decoder][preserves.binary.Decoder] constructor. Raises exceptions as per\n    [next][preserves.binary.Decoder.next].\n\n    Args:\n        bs (bytes): encoded data to decode\n\n    \"\"\"\n    return Decoder(packet=bs, **kwargs).next()\n
"},{"location":"binary/#preserves.binary.decode_with_annotations","title":"decode_with_annotations(bs, **kwargs)","text":"

Like decode, but supplying include_annotations=True to the Decoder constructor.

Source code in preserves/binary.py
def decode_with_annotations(bs, **kwargs):\n\"\"\"Like [decode][preserves.binary.decode], but supplying `include_annotations=True` to the\n    [Decoder][preserves.binary.Decoder] constructor.\"\"\"\n    return Decoder(packet=bs, include_annotations=True, **kwargs).next()\n
"},{"location":"binary/#preserves.binary.encode","title":"encode(v, **kwargs)","text":"

Encode a single Value v to a byte string. Any supplied kwargs are passed on to the underlying Encoder constructor.

Source code in preserves/binary.py
def encode(v, **kwargs):\n\"\"\"Encode a single `Value` `v` to a byte string. Any supplied `kwargs` are passed on to the\n    underlying [Encoder][preserves.binary.Encoder] constructor.\"\"\"\n    e = Encoder(**kwargs)\n    e.append(v)\n    return e.contents()\n
"},{"location":"compare/","title":"Comparing Values","text":"

Preserves specifies a total ordering and an equivalence between terms. This module implements the ordering and equivalence relations.

>>> cmp(\"bzz\", \"c\")\n-1\n>>> cmp(True, [])\n-1\n>>> lt(\"bzz\", \"c\")\nTrue\n>>> eq(\"bzz\", \"c\")\nFalse\n

Note that the ordering relates more values than Python's built-in ordering:

>>> [1, 2, 2] < [1, 2, \"3\"]\nTraceback (most recent call last):\n  ..\nTypeError: '<' not supported between instances of 'int' and 'str'\n\n>>> lt([1, 2, 2], [1, 2, \"3\"])\nTrue\n
"},{"location":"compare/#preserves.compare.cmp","title":"cmp(a, b)","text":"

Returns -1 if a < b, or 0 if a = b, or 1 if a > b according to the Preserves total order.

Source code in preserves/compare.py
def cmp(a, b):\n\"\"\"Returns `-1` if `a` < `b`, or `0` if `a` = `b`, or `1` if `a` > `b` according to the\n    [Preserves total order](https://preserves.dev/preserves.html#total-order).\"\"\"\n    return _cmp(preserve(a), preserve(b))\n
"},{"location":"compare/#preserves.compare.eq","title":"eq(a, b)","text":"

Returns True iff a = b according to the Preserves equivalence relation.

Source code in preserves/compare.py
def eq(a, b):\n\"\"\"Returns `True` iff `a` = `b` according to the [Preserves equivalence\n    relation](https://preserves.dev/preserves.html#equivalence).\"\"\"\n    return _eq(preserve(a), preserve(b))\n
"},{"location":"compare/#preserves.compare.le","title":"le(a, b)","text":"

Returns True iff a \u2264 b according to the Preserves total order.

Source code in preserves/compare.py
def le(a, b):\n\"\"\"Returns `True` iff `a` \u2264 `b` according to the [Preserves total\n    order](https://preserves.dev/preserves.html#total-order).\"\"\"\n    return cmp(a, b) <= 0\n
"},{"location":"compare/#preserves.compare.lt","title":"lt(a, b)","text":"

Returns True iff a < b according to the Preserves total order.

Source code in preserves/compare.py
def lt(a, b):\n\"\"\"Returns `True` iff `a` < `b` according to the [Preserves total\n    order](https://preserves.dev/preserves.html#total-order).\"\"\"\n    return cmp(a, b) < 0\n
"},{"location":"compare/#preserves.compare.sorted","title":"sorted(iterable, *, key=lambda x: x, reverse=False)","text":"

Returns a sorted list built from iterable, extracting a sort key using key, and ordering according to the Preserves total order. Directly analogous to the built-in Python sorted routine, except uses the Preserves order instead of Python's less-than relation.

Source code in preserves/compare.py
def sorted(iterable, *, key=lambda x: x, reverse=False):\n\"\"\"Returns a sorted list built from `iterable`, extracting a sort key using `key`, and\n    ordering according to the [Preserves total\n    order](https://preserves.dev/preserves.html#total-order). Directly analogous to the\n    [built-in Python `sorted`\n    routine](https://docs.python.org/3/library/functions.html#sorted), except uses the\n    Preserves order instead of Python's less-than relation.\n\n    \"\"\"\n    return _sorted(iterable, key=lambda x: _key(key(x)), reverse=reverse)\n
"},{"location":"compare/#preserves.compare.sorted_items","title":"sorted_items(d)","text":"

Given a dictionary d, yields a list of (key, value) tuples sorted by key.

Source code in preserves/compare.py
def sorted_items(d):\n\"\"\"Given a dictionary `d`, yields a list of `(key, value)` tuples sorted by `key`.\"\"\"\n    return sorted(d.items(), key=_item_key)\n
"},{"location":"error/","title":"Codec errors","text":"

TODO

"},{"location":"error/#preserves.error.DecodeError","title":"DecodeError","text":"

Bases: ValueError

TODO

"},{"location":"error/#preserves.error.EncodeError","title":"EncodeError","text":"

Bases: ValueError

TODO

"},{"location":"error/#preserves.error.ShortPacket","title":"ShortPacket","text":"

Bases: DecodeError

TODO

"},{"location":"fold/","title":"Traversing values","text":"

TODO

"},{"location":"fold/#preserves.fold.map_embeddeds","title":"map_embeddeds(f, v)","text":"

TODO

Source code in preserves/fold.py
def map_embeddeds(f, v):\n\"\"\"TODO\"\"\"\n    def walk(v):\n        if isinstance(v, Embedded):\n            return f(v.embeddedValue)\n        elif isinstance(v, (list, tuple)):\n            return tuple(walk(w) for w in v)\n        elif isinstance(v, (set, frozenset)):\n            return frozenset(walk(w) for w in v)\n        elif isinstance(v, dict):\n            return ImmutableDict.from_kvs(walk(w) for w in dict_kvs(v))\n        elif isinstance(v, Record):\n            return Record(walk(v.key), walk(v.fields))\n        else:\n            return v\n    return walk(v)\n
"},{"location":"merge/","title":"Merging values","text":"

TODO

"},{"location":"merge/#preserves.merge.merge","title":"merge(v0, *vs, merge_embedded=None)","text":"

TODO

Source code in preserves/merge.py
def merge(v0, *vs, merge_embedded=None):\n\"\"\"TODO\"\"\"\n    v = v0\n    for vN in vs:\n        v = merge2(v, vN, merge_embedded=merge_embedded)\n    return v\n
"},{"location":"merge/#preserves.merge.merge2","title":"merge2(a, b, merge_embedded=None)","text":"

TODO

Source code in preserves/merge.py
def merge2(a, b, merge_embedded=None):\n\"\"\"TODO\"\"\"\n    if a == b:\n        return a\n    if isinstance(a, (list, tuple)) and isinstance(b, (list, tuple)):\n        return merge_seq(a, b)\n    if isinstance(a, (set, frozenset)) and isinstance(b, (set, frozenset)):\n        _die()\n    if isinstance(a, dict) and isinstance(b, dict):\n        r = {}\n        for (ak, av) in a.items():\n            bv = b.get(ak, None)\n            r[ak] = av if bv is None else merge2(av, bv, merge_embedded=merge_embedded)\n        for (bk, bv) in b.items():\n            if bk not in r:\n                r[bk] = bv\n        return r\n    if isinstance(a, Record) and isinstance(b, Record):\n        return Record(merge2(a.key, b.key, merge_embedded=merge_embedded),\n                      merge_seq(a.fields, b.fields, merge_embedded=merge_embedded))\n    if isinstance(a, Embedded) and isinstance(b, Embedded):\n        m = (merge_embedded or merge_embedded_id)(a.embeddedValue, b.embeddedValue)\n        if m is None: _die()\n        return Embedded(m)\n    _die()\n
"},{"location":"path/","title":"Preserves Path","text":"

TODO (document main behaviour)

"},{"location":"path/#preserves.path.Predicate","title":"Predicate = syntax.Predicate module-attribute","text":"

TODO

"},{"location":"path/#preserves.path.Selector","title":"Selector = syntax.Selector module-attribute","text":"

TODO

"},{"location":"path/#preserves.path.syntax","title":"syntax = load_schema_file(pathlib.Path(__file__).parent / 'path.prb').path module-attribute","text":"

TODO

"},{"location":"path/#preserves.path.parse","title":"parse(s)","text":"

TODO

Source code in preserves/path.py
def parse(s):\n\"\"\"TODO\"\"\"\n    return parse_selector(Parser(s))\n
"},{"location":"schema/","title":"Preserves Schema","text":"

This is an implementation of Preserves Schema for Python 3.

TODO

"},{"location":"schema/#preserves.schema.meta","title":"meta = load_schema_file(__metaschema_filename).schema module-attribute","text":"

TODO

"},{"location":"schema/#preserves.schema.Compiler","title":"Compiler()","text":"

TODO

Source code in preserves/schema.py
def __init__(self):\n    self.root = Namespace(())\n
"},{"location":"schema/#preserves.schema.Definition","title":"Definition(*args, **kwargs)","text":"

Bases: SchemaObject

TODO

Source code in preserves/schema.py
def __init__(self, *args, **kwargs):\n    self._fields = args\n    if self.SIMPLE:\n        if self.EMPTY:\n            if len(args) != 0:\n                raise TypeError('%s takes no arguments' % (self._constructor_name(),))\n        else:\n            if len(args) != 1:\n                raise TypeError('%s needs exactly one argument' % (self._constructor_name(),))\n            self.value = args[0]\n    else:\n        i = 0\n        for arg in args:\n            if i >= len(self.FIELD_NAMES):\n                raise TypeError('%s given too many positional arguments' % (self._constructor_name(),))\n            setattr(self, self.SAFE_FIELD_NAMES[i], arg)\n            i = i + 1\n        for (argname, arg) in kwargs.items():\n            if hasattr(self, argname):\n                raise TypeError('%s given duplicate attribute: %r' % (self._constructor_name, argname))\n            if argname not in self.SAFE_FIELD_NAMES:\n                raise TypeError('%s given unknown attribute: %r' % (self._constructor_name, argname))\n            setattr(self, argname, arg)\n            i = i + 1\n        if i != len(self.FIELD_NAMES):\n            raise TypeError('%s needs argument(s) %r' % (self._constructor_name(), self.FIELD_NAMES))\n
"},{"location":"schema/#preserves.schema.Enumeration","title":"Enumeration()","text":"

Bases: SchemaObject

TODO

Source code in preserves/schema.py
def __init__(self):\n    raise TypeError('Cannot create instance of Enumeration')\n
"},{"location":"schema/#preserves.schema.Namespace","title":"Namespace(prefix)","text":"

TODO

Source code in preserves/schema.py
def __init__(self, prefix):\n    self._prefix = prefix\n
"},{"location":"schema/#preserves.schema.SchemaDecodeFailed","title":"SchemaDecodeFailed(cls, p, v, failures=None)","text":"

Bases: ValueError

TODO

Source code in preserves/schema.py
def __init__(self, cls, p, v, failures=None):\n    super().__init__()\n    self.cls = cls\n    self.pattern = p\n    self.value = v\n    self.failures = [] if failures is None else failures\n
"},{"location":"schema/#preserves.schema.SchemaObject","title":"SchemaObject","text":"

TODO

"},{"location":"schema/#preserves.schema.SchemaObject.__preserve__","title":"__preserve__()","text":"

TODO

Source code in preserves/schema.py
def __preserve__(self):\n\"\"\"TODO\"\"\"\n    raise NotImplementedError('Subclass responsibility')\n
"},{"location":"schema/#preserves.schema.SchemaObject.decode","title":"decode(v) classmethod","text":"

TODO

Source code in preserves/schema.py
@classmethod\ndef decode(cls, v):\n\"\"\"TODO\"\"\"\n    raise NotImplementedError('Subclass responsibility')\n
"},{"location":"schema/#preserves.schema.SchemaObject.try_decode","title":"try_decode(v) classmethod","text":"

TODO

Source code in preserves/schema.py
@classmethod\ndef try_decode(cls, v):\n\"\"\"TODO\"\"\"\n    try:\n        return cls.decode(v)\n    except SchemaDecodeFailed:\n        return None\n
"},{"location":"schema/#preserves.schema.encode","title":"encode(p, v)","text":"

TODO

Source code in preserves/schema.py
def encode(p, v):\n\"\"\"TODO\"\"\"\n    if hasattr(v, '__escape_schema__'):\n        return preserve(v.__escape_schema__())\n    if p == ANY:\n        return v\n    if p.key == NAMED:\n        return encode(p[1], safegetattr(v, p[0].name))\n    if p.key == ATOM:\n        return v\n    if p.key == EMBEDDED:\n        return Embedded(v)\n    if p.key == LIT:\n        return p[0]\n    if p.key == SEQOF:\n        return tuple(encode(p[0], w) for w in v)\n    if p.key == SETOF:\n        return set(encode(p[0], w) for w in v)\n    if p.key == DICTOF:\n        return dict((encode(p[0], k), encode(p[1], w)) for (k, w) in v.items())\n    if p.key == REF:\n        return preserve(v)\n    if p.key == REC:\n        return Record(encode(p[0], v), encode(p[1], v))\n    if p.key == TUPLE:\n        return tuple(encode(pp, v) for pp in p[0])\n    if p.key == TUPLE_PREFIX:\n        return tuple(encode(pp, v) for pp in p[0]) + encode(p[1], v)\n    if p.key == DICT:\n        return dict((k, encode(pp, v)) for (k, pp) in p[0].items())\n    if p.key == AND:\n        return merge(*[encode(pp, v) for pp in p[0]])\n    raise ValueError(f'Bad schema {p}')\n
"},{"location":"schema/#preserves.schema.extend","title":"extend(cls)","text":"

TODO

Source code in preserves/schema.py
def extend(cls):\n\"\"\"TODO\"\"\"\n    def extender(f):\n        setattr(cls, f.__name__, f)\n        return f\n    return extender\n
"},{"location":"schema/#preserves.schema.load_schema_file","title":"load_schema_file(filename)","text":"

TODO

Source code in preserves/schema.py
def load_schema_file(filename):\n\"\"\"TODO\"\"\"\n    c = Compiler()\n    c.load(filename)\n    return c.root\n
"},{"location":"text/","title":"Human-readable text syntax","text":"

TODO

"},{"location":"text/#preserves.text.Formatter","title":"Formatter(format_embedded=lambda x: x, indent=None, with_commas=False, trailing_comma=False, include_annotations=True)","text":"

Bases: TextCodec

TODO

TODO

Source code in preserves/text.py
def __init__(self,\n             format_embedded=lambda x: x,\n             indent=None,\n             with_commas=False,\n             trailing_comma=False,\n             include_annotations=True):\n\"\"\"TODO\"\"\"\n    super(Formatter, self).__init__()\n    self.indent_delta = 0 if indent is None else indent\n    self.indent_distance = 0\n    self.with_commas = with_commas\n    self.trailing_comma = trailing_comma\n    self.chunks = []\n    self._format_embedded = format_embedded\n    self.include_annotations = include_annotations\n
"},{"location":"text/#preserves.text.Formatter.append","title":"append(v)","text":"

TODO

Source code in preserves/text.py
def append(self, v):\n\"\"\"TODO\"\"\"\n    v = preserve(v)\n    if hasattr(v, '__preserve_write_text__'):\n        v.__preserve_write_text__(self)\n    elif v is False:\n        self.chunks.append('#f')\n    elif v is True:\n        self.chunks.append('#t')\n    elif isinstance(v, float):\n        if math.isnan(v) or math.isinf(v):\n            self.chunks.append('#xd\"' + struct.pack('>d', v).hex() + '\"')\n        else:\n            self.chunks.append(repr(v))\n    elif isinstance(v, numbers.Number):\n        self.chunks.append('%d' % (v,))\n    elif isinstance(v, bytes):\n        self.chunks.append('#[%s]' % (base64.b64encode(v).decode('ascii'),))\n    elif isinstance(v, basestring_):\n        self.chunks.append('\"')\n        for c in v:\n            if c == '\"': self.chunks.append('\\\\\"')\n            else: self.write_stringlike_char(c)\n        self.chunks.append('\"')\n    elif isinstance(v, list):\n        self.write_seq('[', ']', v, self.append)\n    elif isinstance(v, tuple):\n        self.write_seq('[', ']', v, self.append)\n    elif isinstance(v, set):\n        self.write_seq('#{', '}', v, self.append)\n    elif isinstance(v, frozenset):\n        self.write_seq('#{', '}', v, self.append)\n    elif isinstance(v, dict):\n        def append_kv(kv):\n            self.append(kv[0])\n            self.chunks.append(': ')\n            self.append(kv[1])\n        self.write_seq('{', '}', v.items(), append_kv)\n    else:\n        try:\n            i = iter(v)\n        except TypeError:\n            i = None\n        if i is None:\n            self.cannot_format(v)\n        else:\n            self.write_seq('[', ']', i, self.append)\n
"},{"location":"text/#preserves.text.Formatter.contents","title":"contents()","text":"

TODO

Source code in preserves/text.py
def contents(self):\n\"\"\"TODO\"\"\"\n    return u''.join(self.chunks)\n
"},{"location":"text/#preserves.text.Parser","title":"Parser(input_buffer='', include_annotations=False, parse_embedded=lambda x: x)","text":"

Bases: TextCodec

TODO

TODO

Source code in preserves/text.py
def __init__(self, input_buffer=u'', include_annotations=False, parse_embedded=lambda x: x):\n\"\"\"TODO\"\"\"\n    super(Parser, self).__init__()\n    self.input_buffer = input_buffer\n    self.index = 0\n    self.include_annotations = include_annotations\n    self.parse_embedded = parse_embedded\n
"},{"location":"text/#preserves.text.Parser.__iter__","title":"__iter__()","text":"

TODO

Source code in preserves/text.py
def __iter__(self):\n\"\"\"TODO\"\"\"\n    return self\n
"},{"location":"text/#preserves.text.Parser.extend","title":"extend(text)","text":"

TODO

Source code in preserves/text.py
def extend(self, text):\n\"\"\"TODO\"\"\"\n    self.input_buffer = self.input_buffer[self.index:] + text\n    self.index = 0\n
"},{"location":"text/#preserves.text.Parser.next","title":"next()","text":"

TODO

Source code in preserves/text.py
def next(self):\n\"\"\"TODO\"\"\"\n    self.skip_whitespace()\n    c = self.peek()\n    if c == '\"':\n        self.skip()\n        return self.wrap(self.read_string('\"'))\n    if c == '|':\n        self.skip()\n        return self.wrap(Symbol(self.read_string('|')))\n    if c in ';@':\n        annotations = self.gather_annotations()\n        v = self.next()\n        if self.include_annotations:\n            v.annotations = annotations + v.annotations\n        return v\n    if c == ':':\n        raise DecodeError('Unexpected key/value separator between items')\n    if c == '#':\n        self.skip()\n        c = self.nextchar()\n        if c == 'f': return self.wrap(False)\n        if c == 't': return self.wrap(True)\n        if c == '{': return self.wrap(frozenset(self.upto('}')))\n        if c == '\"': return self.wrap(self.read_literal_binary())\n        if c == 'x':\n            c = self.nextchar()\n            if c == '\"': return self.wrap(self.read_hex_binary())\n            if c == 'f': return self.wrap(self.read_hex_float(4))\n            if c == 'd': return self.wrap(self.read_hex_float(8))\n            raise DecodeError('Invalid #x syntax')\n        if c == '[': return self.wrap(self.read_base64_binary())\n        if c == '=':\n            old_ann = self.include_annotations\n            self.include_annotations = True\n            bs_val = self.next()\n            self.include_annotations = old_ann\n            if len(bs_val.annotations) > 0:\n                raise DecodeError('Annotations not permitted after #=')\n            bs_val = bs_val.item\n            if not isinstance(bs_val, bytes):\n                raise DecodeError('ByteString must follow #=')\n            return self.wrap(Decoder(bs_val, include_annotations = self.include_annotations).next())\n        if c == '!':\n            if self.parse_embedded is None:\n                raise DecodeError('No parse_embedded function supplied')\n            return self.wrap(Embedded(self.parse_embedded(self.next())))\n        raise DecodeError('Invalid # syntax')\n    if c == '<':\n        self.skip()\n        vs = self.upto('>')\n        if len(vs) == 0:\n            raise DecodeError('Missing record label')\n        return self.wrap(Record(vs[0], vs[1:]))\n    if c == '[':\n        self.skip()\n        return self.wrap(self.upto(']'))\n    if c == '{':\n        self.skip()\n        return self.wrap(self.read_dictionary())\n    if c in '>]}':\n        raise DecodeError('Unexpected ' + c)\n    self.skip()\n    return self.wrap(self.read_raw_symbol_or_number([c]))\n
"},{"location":"text/#preserves.text.Parser.try_next","title":"try_next()","text":"

TODO

Source code in preserves/text.py
def try_next(self):\n\"\"\"TODO\"\"\"\n    start = self.index\n    try:\n        return self.next()\n    except ShortPacket:\n        self.index = start\n        return None\n
"},{"location":"text/#preserves.text.TextCodec","title":"TextCodec","text":"

Bases: object

TODO

"},{"location":"text/#preserves.text.parse","title":"parse(bs, **kwargs)","text":"

TODO

Source code in preserves/text.py
def parse(bs, **kwargs):\n\"\"\"TODO\"\"\"\n    return Parser(input_buffer=bs, **kwargs).next()\n
"},{"location":"text/#preserves.text.parse_with_annotations","title":"parse_with_annotations(bs, **kwargs)","text":"

TODO

Source code in preserves/text.py
def parse_with_annotations(bs, **kwargs):\n\"\"\"TODO\"\"\"\n    return Parser(input_buffer=bs, include_annotations=True, **kwargs).next()\n
"},{"location":"text/#preserves.text.stringify","title":"stringify(v, **kwargs)","text":"

TODO

Source code in preserves/text.py
def stringify(v, **kwargs):\n\"\"\"TODO\"\"\"\n    e = Formatter(**kwargs)\n    e.append(v)\n    return e.contents()\n
"},{"location":"values/","title":"Representations of Values","text":"

TODO

"},{"location":"values/#preserves.values.Annotated","title":"Annotated(item)","text":"

Bases: object

TODO

TODO

Source code in preserves/values.py
def __init__(self, item):\n\"\"\"TODO\"\"\"\n    self.annotations = []\n    self.item = item\n
"},{"location":"values/#preserves.values.Annotated.peel","title":"peel()","text":"

TODO

Source code in preserves/values.py
def peel(self):\n\"\"\"TODO\"\"\"\n    return strip_annotations(self, 1)\n
"},{"location":"values/#preserves.values.Annotated.strip","title":"strip(depth=inf)","text":"

TODO

Source code in preserves/values.py
def strip(self, depth=inf):\n\"\"\"TODO\"\"\"\n    return strip_annotations(self, depth)\n
"},{"location":"values/#preserves.values.Embedded","title":"Embedded(value)","text":"

TODO

TODO

Source code in preserves/values.py
def __init__(self, value):\n\"\"\"TODO\"\"\"\n    self.embeddedValue = value\n
"},{"location":"values/#preserves.values.Float","title":"Float(value)","text":"

Bases: object

TODO

TODO

Source code in preserves/values.py
def __init__(self, value):\n\"\"\"TODO\"\"\"\n    self.value = value\n
"},{"location":"values/#preserves.values.Float.from_bytes","title":"from_bytes(bs) staticmethod","text":"

TODO

Source code in preserves/values.py
@staticmethod\ndef from_bytes(bs):\n\"\"\"TODO\"\"\"\n    vf = struct.unpack('>I', bs)[0]\n    if (vf & 0x7f800000) == 0x7f800000:\n        # NaN or inf. Preserve quiet/signalling bit by manually expanding to double-precision.\n        sign = vf >> 31\n        payload = vf & 0x007fffff\n        dbs = struct.pack('>Q', (sign << 63) | 0x7ff0000000000000 | (payload << 29))\n        return Float(struct.unpack('>d', dbs)[0])\n    else:\n        return Float(struct.unpack('>f', bs)[0])\n
"},{"location":"values/#preserves.values.ImmutableDict","title":"ImmutableDict(*args, **kwargs)","text":"

Bases: dict

TODO

TODO

Source code in preserves/values.py
def __init__(self, *args, **kwargs):\n\"\"\"TODO\"\"\"\n    if hasattr(self, '__hash'): raise TypeError('Immutable')\n    super(ImmutableDict, self).__init__(*args, **kwargs)\n    self.__hash = None\n
"},{"location":"values/#preserves.values.ImmutableDict.from_kvs","title":"from_kvs(kvs) staticmethod","text":"

TODO

Source code in preserves/values.py
@staticmethod\ndef from_kvs(kvs):\n\"\"\"TODO\"\"\"\n    i = iter(kvs)\n    result = ImmutableDict()\n    result_proxy = super(ImmutableDict, result)\n    try:\n        while True:\n            k = next(i)\n            try:\n                v = next(i)\n            except StopIteration:\n                raise DecodeError(\"Missing dictionary value\")\n            result_proxy.__setitem__(k, v)\n    except StopIteration:\n        pass\n    return result\n
"},{"location":"values/#preserves.values.Record","title":"Record(key, fields)","text":"

Bases: object

TODO

TODO

Source code in preserves/values.py
def __init__(self, key, fields):\n\"\"\"TODO\"\"\"\n    self.key = key\n    self.fields = tuple(fields)\n    self.__hash = None\n
"},{"location":"values/#preserves.values.Record.makeBasicConstructor","title":"makeBasicConstructor(label, fieldNames) staticmethod","text":"

TODO

Source code in preserves/values.py
@staticmethod\ndef makeBasicConstructor(label, fieldNames):\n\"\"\"TODO\"\"\"\n    if type(fieldNames) == str:\n        fieldNames = fieldNames.split()\n    arity = len(fieldNames)\n    def ctor(*fields):\n        if len(fields) != arity:\n            raise Exception(\"Record: cannot instantiate %r expecting %d fields with %d fields\"%(\n                label,\n                arity,\n                len(fields)))\n        return Record(label, fields)\n    ctor.constructorInfo = RecordConstructorInfo(label, arity)\n    ctor.isClassOf = lambda v: \\\n                     isinstance(v, Record) and v.key == label and len(v.fields) == arity\n    def ensureClassOf(v):\n        if not ctor.isClassOf(v):\n            raise TypeError(\"Record: expected %r/%d, got %r\" % (label, arity, v))\n        return v\n    ctor.ensureClassOf = ensureClassOf\n    for fieldIndex in range(len(fieldNames)):\n        fieldName = fieldNames[fieldIndex]\n        # Stupid python scoping bites again\n        def getter(fieldIndex):\n            return lambda v: ensureClassOf(v)[fieldIndex]\n        setattr(ctor, '_' + fieldName, getter(fieldIndex))\n    return ctor\n
"},{"location":"values/#preserves.values.Record.makeConstructor","title":"makeConstructor(labelSymbolText, fieldNames) staticmethod","text":"

TODO

Source code in preserves/values.py
@staticmethod\ndef makeConstructor(labelSymbolText, fieldNames):\n\"\"\"TODO\"\"\"\n    return Record.makeBasicConstructor(Symbol(labelSymbolText), fieldNames)\n
"},{"location":"values/#preserves.values.RecordConstructorInfo","title":"RecordConstructorInfo(key, arity)","text":"

Bases: object

TODO

TODO

Source code in preserves/values.py
def __init__(self, key, arity):\n\"\"\"TODO\"\"\"\n    self.key = key\n    self.arity = arity\n
"},{"location":"values/#preserves.values.Symbol","title":"Symbol(name)","text":"

Bases: object

TODO

TODO

Source code in preserves/values.py
def __init__(self, name):\n\"\"\"TODO\"\"\"\n    self.name = name.name if isinstance(name, Symbol) else name\n
"},{"location":"values/#preserves.values.annotate","title":"annotate(v, *anns)","text":"

TODO

Source code in preserves/values.py
def annotate(v, *anns):\n\"\"\"TODO\"\"\"\n    if not is_annotated(v):\n        v = Annotated(v)\n    for a in anns:\n        v.annotations.append(a)\n    return v\n
"},{"location":"values/#preserves.values.cmp_floats","title":"cmp_floats(a, b)","text":"

TODO

Source code in preserves/values.py
def cmp_floats(a, b):\n\"\"\"TODO\"\"\"\n    a = float_to_int(a)\n    b = float_to_int(b)\n    if a & 0x8000000000000000: a = a ^ 0x7fffffffffffffff\n    if b & 0x8000000000000000: b = b ^ 0x7fffffffffffffff\n    return a - b\n
"},{"location":"values/#preserves.values.dict_kvs","title":"dict_kvs(d)","text":"

TODO

Source code in preserves/values.py
def dict_kvs(d):\n\"\"\"TODO\"\"\"\n    for k in d:\n        yield k\n        yield d[k]\n
"},{"location":"values/#preserves.values.is_annotated","title":"is_annotated(v)","text":"

TODO

Source code in preserves/values.py
def is_annotated(v):\n\"\"\"TODO\"\"\"\n    return isinstance(v, Annotated)\n
"},{"location":"values/#preserves.values.preserve","title":"preserve(v)","text":"

TODO

Source code in preserves/values.py
def preserve(v):\n\"\"\"TODO\"\"\"\n    while hasattr(v, '__preserve__'):\n        v = v.__preserve__()\n    return v\n
"},{"location":"values/#preserves.values.strip_annotations","title":"strip_annotations(v, depth=inf)","text":"

TODO

Source code in preserves/values.py
def strip_annotations(v, depth=inf):\n\"\"\"TODO\"\"\"\n    if depth == 0: return v\n    if not is_annotated(v): return v\n\n    next_depth = depth - 1\n    def walk(v):\n        return strip_annotations(v, next_depth)\n\n    v = v.item\n    if isinstance(v, Record):\n        return Record(strip_annotations(v.key, depth), tuple(walk(f) for f in v.fields))\n    elif isinstance(v, list):\n        return tuple(walk(f) for f in v)\n    elif isinstance(v, tuple):\n        return tuple(walk(f) for f in v)\n    elif isinstance(v, set):\n        return frozenset(walk(f) for f in v)\n    elif isinstance(v, frozenset):\n        return frozenset(walk(f) for f in v)\n    elif isinstance(v, dict):\n        return ImmutableDict.from_kvs(walk(f) for f in dict_kvs(v))\n    elif is_annotated(v):\n        raise ValueError('Improper annotation structure')\n    else:\n        return v\n
"}]} \ No newline at end of file