Representations of Values
Python's strings, byte strings, integers, booleans, and double-precision floats stand directly for their Preserves counterparts. Wrapper objects for Float and Symbol complete the suite of atomic types.
Python's lists and tuples correspond to Preserves Sequence
s, and dicts and sets to
Dictionary
and Set
values, respectively. Preserves Record
s are represented by
Record objects. Finally, embedded values are represented by
Embedded objects.
The preserves.values module implements the core representations of Preserves
Value
s as Python values.
Annotated(item)
Bases: object
A Preserves Value
along with a sequence of Value
s annotating it. Compares equal to
the underlying Value
, ignoring the annotations. See the specification document for more
about annotations.
>>> import preserves
>>> a = preserves.parse('''
... ; A comment
... [1 2 3]
... ''', include_annotations=True)
>>> a
@' A comment' (1, 2, 3)
>>> a.item
(1, 2, 3)
>>> a.annotations
[' A comment']
>>> a == (1, 2, 3)
True
>>> a == preserves.parse('@xyz [1 2 3]', include_annotations=True)
True
>>> a[0]
Traceback (most recent call last):
...
TypeError: 'Annotated' object is not subscriptable
>>> a.item[0]
1
>>> type(a.item[0])
<class 'preserves.values.Annotated'>
>>> a.item[0].annotations
[]
>>> print(preserves.stringify(a))
@" A comment" [1 2 3]
>>> print(preserves.stringify(a, include_annotations=False))
[1 2 3]
Attributes:
Name | Type | Description |
---|---|---|
item |
Value
|
the underlying annotated |
annotations |
list[Value]
|
the annotations attached to |
Source code in preserves/values.py
573 574 575 |
|
peel()
Calls strip_annotations on self
with depth=1
.
Source code in preserves/values.py
596 597 598 |
|
strip(depth=inf)
Calls strip_annotations on self
and depth
.
Source code in preserves/values.py
592 593 594 |
|
Embedded(embeddedValue)
Representation of a Preserves Embedded
value. For more on the meaning and use of
embedded values, see the specification.
>>> import io
>>> e = Embedded(io.StringIO('some text'))
>>> e # doctest: +ELLIPSIS
#!<_io.StringIO object at ...>
>>> e.embeddedValue # doctest: +ELLIPSIS
<_io.StringIO object at ...>
>>> import preserves
>>> print(preserves.stringify(Embedded(None)))
Traceback (most recent call last):
...
TypeError: Cannot preserves-format: None
>>> print(preserves.stringify(Embedded(None), format_embedded=lambda x: 'abcdef'))
#!"abcdef"
Attributes:
Name | Type | Description |
---|---|---|
embeddedValue |
any Python value; could be a platform object, could be a representation of a
Preserves |
Source code in preserves/values.py
718 719 |
|
Float(value)
Bases: object
Wrapper for treating a Python double-precision floating-point value as a single-precision (32-bit) float, from Preserves' perspective. (Python lacks native single-precision floating point support.)
>>> Float(3.45)
Float(3.45)
>>> import preserves
>>> preserves.stringify(Float(3.45))
'3.45f'
>>> preserves.stringify(3.45)
'3.45'
>>> preserves.parse('3.45f')
Float(3.45)
>>> preserves.parse('3.45')
3.45
>>> preserves.encode(Float(3.45))
b'\x82@\\\xcc\xcd'
>>> preserves.encode(3.45)
b'\x83@\x0b\x99\x99\x99\x99\x99\x9a'
Attributes:
Name | Type | Description |
---|---|---|
value |
float
|
the double-precision representation of intended single-precision value |
Source code in preserves/values.py
69 70 |
|
from_bytes(bs)
staticmethod
Converts a 4-byte-long byte string to a 32-bit single-precision floating point value
wrapped in a Float instance. Takes care to preserve the
quiet/signalling bit-pattern of NaN values, unlike its struct.unpack('>f', ...)
equivalent.
>>> Float.from_bytes(b'\x7f\x80\x00{')
Float(nan)
>>> Float.from_bytes(b'\x7f\x80\x00{').to_bytes()
b'\x7f\x80\x00{'
>>> struct.unpack('>f', b'\x7f\x80\x00{')[0]
nan
>>> Float(struct.unpack('>f', b'\x7f\x80\x00{')[0]).to_bytes()
b'\x7f\xc0\x00{'
>>> struct.pack('>f', struct.unpack('>f', b'\x7f\x80\x00{')[0])
b'\x7f\xc0\x00{'
(Note well the difference between 7f80007b
and 7fc0007b
!)
Source code in preserves/values.py
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
|
to_bytes()
Converts this 32-bit single-precision floating point value to its binary32 format,
taking care to preserve the quiet/signalling bit-pattern of NaN values, unlike its
struct.pack('>f', ...)
equivalent.
>>> Float.from_bytes(b'\x7f\x80\x00{')
Float(nan)
>>> Float.from_bytes(b'\x7f\x80\x00{').to_bytes()
b'\x7f\x80\x00{'
>>> struct.unpack('>f', b'\x7f\x80\x00{')[0]
nan
>>> Float(struct.unpack('>f', b'\x7f\x80\x00{')[0]).to_bytes()
b'\x7f\xc0\x00{'
>>> struct.pack('>f', struct.unpack('>f', b'\x7f\x80\x00{')[0])
b'\x7f\xc0\x00{'
(Note well the difference between 7f80007b
and 7fc0007b
!)
Source code in preserves/values.py
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
|
ImmutableDict(*args, **kwargs)
Bases: dict
A subclass of Python's built-in dict
that overrides methods that could mutate the
dictionary, causing them to raise TypeError('Immutable')
if called.
Implements the __hash__
method, allowing ImmutableDict
instances to be used whereever immutable data are permitted; in particular, as keys in
other dictionaries.
>>> d = ImmutableDict([('a', 1), ('b', 2)])
>>> d
{'a': 1, 'b': 2}
>>> d['c'] = 3
Traceback (most recent call last):
...
TypeError: Immutable
>>> del d['b']
Traceback (most recent call last):
...
TypeError: Immutable
Source code in preserves/values.py
459 460 461 462 |
|
from_kvs(kvs)
staticmethod
Constructs an ImmutableDict from a sequence of alternating keys and values; compare to the ImmutableDict constructor, which takes a sequence of key-value pairs.
>>> ImmutableDict.from_kvs(['a', 1, 'b', 2])
{'a': 1, 'b': 2}
>>> ImmutableDict.from_kvs(['a', 1, 'b', 2])['c'] = 3
Traceback (most recent call last):
...
TypeError: Immutable
Source code in preserves/values.py
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 |
|
Record(key, fields)
Bases: object
Representation of Preserves Record
s, which are a pair of a label Value
and a sequence of field Value
s.
>>> r = Record(Symbol('label'), ['field1', ['field2item1', 'field2item2']])
>>> r
#label('field1', ['field2item1', 'field2item2'])
>>> r.key
#label
>>> r.fields
('field1', ['field2item1', 'field2item2'])
>>> import preserves
>>> preserves.stringify(r)
'<label "field1" ["field2item1" "field2item2"]>'
>>> r == preserves.parse('<label "field1" ["field2item1" "field2item2"]>')
True
Parameters:
Name | Type | Description | Default |
---|---|---|---|
key |
Value
|
the |
required |
fields |
iterable[Value]
|
the fields of the |
required |
Attributes:
Name | Type | Description |
---|---|---|
key |
Value
|
the |
fields |
tuple[Value]
|
the fields of the |
Source code in preserves/values.py
271 272 273 274 |
|
makeBasicConstructor(label, fieldNames)
staticmethod
Constructs and returns a "constructor" for Record
s having a certain label
and
number of fields.
Deprecated
Use preserves.schema definitions instead.
The "constructor" is a callable function that accepts len(fields)
arguments and
returns a Record with label
as its label and the arguments
to the constructor as field values.
In addition, the "constructor" has a constructorInfo
attribute holding a
RecordConstructorInfo object, an isClassOf
attribute holding a unary function that returns True
iff its argument is a
Record with label label
and arity len(fieldNames)
, and
an ensureClassOf
attribute that raises an Exception
if isClassOf
returns false on
its argument and returns the argument otherwise.
Finally, for each field name f
in fieldNames
, the "constructor" object has an
attribute _f
that is a unary function that retrieves the f
field from the passed in
argument.
>>> c = Record.makeBasicConstructor(Symbol('date'), 'year month day')
>>> c(1969, 7, 16)
#date(1969, 7, 16)
>>> c.constructorInfo
#date/3
>>> c.isClassOf(c(1969, 7, 16))
True
>>> c.isClassOf(Record(Symbol('date'), [1969, 7, 16]))
True
>>> c.isClassOf(Record(Symbol('date'), [1969]))
False
>>> c.ensureClassOf(c(1969, 7, 16))
#date(1969, 7, 16)
>>> c.ensureClassOf(Record(Symbol('date'), [1969]))
Traceback (most recent call last):
...
TypeError: Record: expected #date/3, got #date(1969)
>>> c._year(c(1969, 7, 16))
1969
>>> c._month(c(1969, 7, 16))
7
>>> c._day(c(1969, 7, 16))
16
Parameters:
Name | Type | Description | Default |
---|---|---|---|
label |
Value
|
Label to use for constructed/matched |
required |
fieldNames |
tuple[str] | list[str] | str
|
Names of the |
required |
Source code in preserves/values.py
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 |
|
makeConstructor(labelSymbolText, fieldNames)
staticmethod
Equivalent to Record.makeBasicConstructor(Symbol(labelSymbolText), fieldNames)
.
Deprecated
Use preserves.schema definitions instead.
Source code in preserves/values.py
309 310 311 312 313 314 315 316 317 |
|
RecordConstructorInfo(key, arity)
Bases: object
Describes the shape of a Record
constructor, namely its label and its arity (field
count).
>>> RecordConstructorInfo(Symbol('label'), 3)
#label/3
Attributes:
Name | Type | Description |
---|---|---|
key |
Value
|
the label of matching |
arity |
int
|
the number of fields in matching |
Source code in preserves/values.py
414 415 416 |
|
Symbol(name)
Bases: object
Representation of Preserves Symbol
s.
>>> Symbol('xyz')
#xyz
>>> Symbol('xyz').name
'xyz'
>>> import preserves
>>> preserves.stringify(Symbol('xyz'))
'xyz'
>>> preserves.stringify(Symbol('hello world'))
'|hello world|'
>>> preserves.parse('xyz')
#xyz
>>> preserves.parse('|hello world|')
#hello world
Attributes:
Name | Type | Description |
---|---|---|
name |
str
|
the symbol's text label |
Source code in preserves/values.py
200 201 |
|
annotate(v, *anns)
Wraps v
in an Annotated object, if it isn't already
wrapped, and appends each of the anns
to the Annotated's
annotations
sequence. NOTE: Does not recursively ensure that any parts of the argument
v
are themselves wrapped in Annotated objects!
>>> import preserves
>>> print(preserves.stringify(annotate(123, "A comment", "Another comment")))
@"A comment" @"Another comment" 123
Source code in preserves/values.py
662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 |
|
cmp_floats(a, b)
Implements the totalOrder
predicate defined in section 5.10 of IEEE Std
754-2008.
Source code in preserves/values.py
31 32 33 34 35 36 37 38 39 40 |
|
dict_kvs(d)
Generator function yielding a sequence of alternating keys and values from d
. In some
sense the inverse of ImmutableDict.from_kvs.
>>> list(dict_kvs({'a': 1, 'b': 2}))
['a', 1, 'b', 2]
Source code in preserves/values.py
514 515 516 517 518 519 520 521 522 523 524 525 526 |
|
is_annotated(v)
True
iff v
is an instance of Annotated.
Source code in preserves/values.py
612 613 614 |
|
preserve(v)
Converts v
to a representation of a Preserves Value
by (repeatedly) setting
v = v.__preserve__()
while v
has a __preserve__
method. Parsed Schema
values are able to render themselves to their serialized representations this way.
Source code in preserves/values.py
13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
strip_annotations(v, depth=inf)
Exposes depth
layers of raw structure of
potentially-Annotated Value
s. If depth==0
or v
is not
Annotated, just returns v
. Otherwise, descends recursively
into the structure of v.item
.
>>> import preserves
>>> a = preserves.parse('@"A comment" [@a 1 @b 2 @c 3]', include_annotations=True)
>>> is_annotated(a)
True
>>> print(preserves.stringify(a))
@"A comment" [@a 1 @b 2 @c 3]
>>> print(preserves.stringify(strip_annotations(a)))
[1 2 3]
>>> print(preserves.stringify(strip_annotations(a, depth=1)))
[@a 1 @b 2 @c 3]
Source code in preserves/values.py
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 |
|
Created: March 16, 2023