An XML-like document with spreadsheet formulas for values and underlying persistent data structures
Project description
persistent_doc - An XML-like document with spreadsheet formulas for values and underlying persistent data structures
Installing
Clone this repository and then
pip install -r requirements.txt
Sample usage
>>> from persistent_doc.document import Document, FrozenNode as Node, Ex
>>> root = Node("foo", params={"id": "one"})
>>> doc = Document(root)
>>> doc['one'].append(Node("bar", params={"id": "two"}))
>>> doc['one.0']
bar(id=two)[]
>>> doc['one.x'] = 3
>>> doc['one']['x']
3
>>> doc['two.x'] = Ex("`one.x + 3")
>>> doc['two.x']
6
>>> doc['one.x'] += 1
>>> doc['two.x']
7
Examples of relative paths, longer reference chain and undo-redo
>>> doc['two.y'] = Ex("`two.parent.x + `two.x")
>>> doc['two.y']
11
>>> doc['one.x'] += 1
>>> doc['two.y']
13
>>> doc.undo()
>>> doc.undo()
>>> doc.undo()
>>> doc.undo()
>>> doc['one.x'], doc['two.y']
(4, 11)
>>> doc.start_group("one_step")
True
>>> doc['one.x'] += 1
>>> doc.end_group("one_step")
>>> doc['one.x'], doc['two.y']
(5, 13)
>>> doc.undo()
>>> doc['one.x'], doc['two.y']
(4, 11)
>>> doc.redo()
>>> doc['one.x'], doc['two.y']
(5, 13)
Document model
Like XML, the document is a tree where each node has a list of children and key-value pairs for parameters. Like SVG, each node has a unique id
property within the document.
Parameter values can be "formulas" treating other nodes' id as variables (and a relative path of parent, children and parameter keys).
The document is persistent to allow easy undo-redo with spending too much memory. Persistent means that mutations to an object always creates a new version but the new version shares memory with the old version. (It could potentially allow erasing history to save more on memory.)
While the underlying data structures are immutable but have the same interface as mutable objects.
Formula syntax
Syntax may change in the future. To set a formula as value, create an Ex object with a string (in this syntax) to be evaluated.
- node id:
`foo
evaluates to the node with idfoo
in the document. - parameter keys:
`foo.bar
evaluates to (the value of) parameterbar
of nodefoo
in the document.`foo.bar.baz
works as expected iffoo.bar
is a node. - children and parents:
`foo[1]
and`foo.1
both evaluates to the child of (the node with id)foo
at index 1 (i.e., the second child).`foo.1.0
isfoo
's second child's first child.`foo.parent
is the parent offoo
. - function calls and operations: Function calls
foo(`bar, 3 + `baz)
work as in Python.
If doc
is a persistent_doc.document.Document
, doc['foo.parent.3.2']
gives foo
's parent's fourth child's second child. Function calls and operations cannot be used with Document.__getitem__
(instead use them outside, in Python f(doc['foo.p1'], doc['bar.p1'])
).
Formula reevaluation
There are three reevaluation strategies
- cached (default): The formula is reevaluated when a term it depends on changes. The result is cached for reads.
- reeval: The formula is reevaluated every time it is read.
- on first read: The formula is reevaluated the first time it is read after one of the terms it depends on has changed. The result is cached for reads.
The calc
parameter is passed to Ex
to indicate which one to use. For example Ex(`foo + 3, calc="reeval")
. It is thus possible to have a document with mixed reevaluation strategies.
Expr objects
To get the object for a formula instead of its value, use doc.get_expr('foo.bar')
instead of doc['foo.bar']
.
Errors and debugging
Expressions that produce an error when evaluated will return an EvalError
object instead of raising an error.
Tests
Run python test.py
.
Pointers
Because the document is persistent, pointers to values in the document can become stale. Use doc.m
or node.L
(instead of doc
and node
) to use the latest version.
Internals
default_doc
Because parent-child relations are encoded as lookups, its not possible to create a new subtree of Nodes "in the void" and then hook it up to existing nodes. It needs at least a doc (memory) to be created. So default_doc
helps with that part.
If there's only one document, persistent_doc.document.default_doc
should be set to that.
Conventions
Parent sets the child's .parent
.
numpy
numpy
isn't really a requirement but since numpy
1.13, equality test behave differently and some of the polymorphism doesn't work otherwise. document.py
contains an alternate definition of equal
that can be used if there are no numpy arrays as values.
mutable values
Mutable values are expected not to be mutated. They should instead be replaced.
doc['foo.arr'] = numpy.array([1, 2])
doc['foo.arr'] = doc['foo.arr'] + numpy.array([1, 1])
instead of
arr = numpy.array([1, 2])
doc['foo.arr'] = arr
arr += numpy.array([1, 1])
Todo
- Find something better than all the explicit type checking with
Ex
andExpr
. - Find a way to automatically decide if a node needs to be replaced by its latest version before an operation. Maybe by comparing timestamps?
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file persistent_doc-0.5.1.tar.gz
.
File metadata
- Download URL: persistent_doc-0.5.1.tar.gz
- Upload date:
- Size: 11.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.19.1 setuptools/40.4.3 requests-toolbelt/0.8.0 tqdm/4.26.0 CPython/2.7.10
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 23151ec1c9ec1198f24be01c0af692ba92c902897275b7ec11ed03a0672b3800 |
|
MD5 | 0ce8c4af00f604817fc8c432414da1bb |
|
BLAKE2b-256 | e93f62c40690ff6785350d67b84071acd1766cd968d0cc50ab8921c186a795c7 |
File details
Details for the file persistent_doc-0.5.1-py2-none-any.whl
.
File metadata
- Download URL: persistent_doc-0.5.1-py2-none-any.whl
- Upload date:
- Size: 12.5 kB
- Tags: Python 2
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.19.1 setuptools/40.4.3 requests-toolbelt/0.8.0 tqdm/4.26.0 CPython/2.7.10
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 7bad807b34a69544366e6130d1144064464a0b11a6d6897d20c9642145723f1b |
|
MD5 | f9f9ad85509a37903cf0a8d865a0aefd |
|
BLAKE2b-256 | 2099803daa3a046425989ca0297e60f59b9cd92c4cf4e45b6522a4e2c1a2d7d6 |