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:
`fooevaluates to the node with idfooin the document. - parameter keys:
`foo.barevaluates to (the value of) parameterbarof nodefooin the document.`foo.bar.bazworks as expected iffoo.baris a node. - children and parents:
`foo[1]and`foo.1both evaluates to the child of (the node with id)fooat index 1 (i.e., the second child).`foo.1.0isfoo's second child's first child.`foo.parentis 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
ExandExpr. - 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
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
|