Dictum repository
Project description
Python redictum lib/service (redictum)
Concept
Imagine some state (a set of key value pairs; a dictionary), the state has a start time and optionally an end time (like a "valid from" and "valid to" criteria), and some of the state is just information, and some of it defines the state's uniqueness.
Imagine now that you can just throw state information at the wall, and it sticks, meaning that the state that is not unique is taken as an update, and the state which is unique, is new state.
That's what redictum does, as briefly as I can make it!
In case the idea of uniqueness isn't quite clear, imagine a type of state which represents people, and we are interested in the first name, last name, age and thirst and hunger of these people.
For example imagine these four bits of state:
{"first_name": "Fred", "last_name": "Flint", "age": 10, "thirst": 2, "hunger": 80}
{"first_name": "Jane", "last_name": "Oster", "age": 20, "thirst": 30, "hunger": 30}
{"first_name": "John", "last_name": "Medium", "age": 40, "thirst": 76, "hunger": 60}
{"first_name": "John", "last_name": "Winter", "age": 30, "thirst": 40, "hunger": 10}
Let's say, in our contrived world, that no two people have the same first and last names and that is the extent of our consideration for identifying the uniqueness of a person.
In that case all four states above are unique, but, if we add this state:
{"first_name": "John", "last_name": "Winter", "age": 31, "thirst": 55, "hunger": 10}
...John Winter has become a year older, and a bit more thirsty (that could happen in a year).
The total state of the system is now:
{"first_name": "Fred", "last_name": "Flint", "age": 10, "thirst": 2, "hunger": 80}
{"first_name": "Jane", "last_name": "Oster", "age": 20, "thirst": 30, "hunger": 30}
{"first_name": "John", "last_name": "Medium", "age": 40, "thirst": 76, "hunger": 60}
{"first_name": "John", "last_name": "Winter", "age": 31, "thirst": 55, "hunger": 10}
Had age
been considered part of what made people unique, then the state would have been
this instead:
{"first_name": "Fred", "last_name": "Flint", "age": 10, "thirst": 2, "hunger": 80}
{"first_name": "Jane", "last_name": "Oster", "age": 20, "thirst": 30, "hunger": 30}
{"first_name": "John", "last_name": "Medium", "age": 40, "thirst": 76, "hunger": 60}
{"first_name": "John", "last_name": "Winter", "age": 30, "thirst": 40, "hunger": 10}
{"first_name": "John", "last_name": "Winter", "age": 31, "thirst": 55, "hunger": 10}
You can also set the start (valid from time) and optionally end (valid to time) by way of a TTL (time to live), and get the "valid" states, or expired or future states.
This simple set of rules means you can throw state at the system that will go away if not renewed / updated regularly (as govered by the TTL), and you can throw state at the system which will become "valid" after some time, if it's not cancelled, or otherwise updated. Being able to get the "valid" states then becomes a powerful tool.
That's more or less the entire concept, it just remains to be said that there are types of state, with which code can be associated, and the back end storage can be whatever there is a storage adapter for.
In code
The above explanation and data is shown here in code now.
First import redictum, create a repository, create a class for people with a (default) time to live of 200 (seconds), then get the valid dictums, and there will of course be one item:
>>> import redictum
>>>
>>> people = redictum.Repo(redictum.store.RdbmsStore("sqlite:///:memory:"))
>>>
>>> class Person(redictum.Dictum):
... unique_by = ("first_name", "last_name")
... ttl = 200
...
>>> people.apply(Person({"first_name": "Fred", "last_name": "Flint", "age": 10, "thirst": 2, "hunger": 80}))
>>>
>>> people.valid_dictums()
[__main__.Person{'valid_from_ts': 1654451266.453832, 'valid_to_ts': 1654451466.453832, 'first_name': 'Fred', 'last_name': 'Flint', 'age': 10, 'thirst': 2, 'hunger': 80}]
>>>
Now add more people:
>>> people.apply(Person({"first_name": "Jane", "last_name": "Oster", "age": 20, "thirst": 30, "hunger": 30}))
>>> people.apply(Person({"first_name": "John", "last_name": "Medium", "age": 40, "thirst": 76, "hunger": 60}))
>>> people.apply(Person({"first_name": "John", "last_name": "Winter", "age": 30, "thirst": 40, "hunger": 10}))
>>>
The display of the dictum objects is a bit inconvenient, so something a little easier on the eye:
>>> for p in [f"{d.data['first_name']} {d.data['last_name']} {d.data['age']} {d.data['thirst']} {d.data['hunger']}" for d in people.valid_dictums()]: print(p)
...
Fred Flint 10 2 80
Jane Oster 20 30 30
John Medium 40 76 60
John Winter 30 40 10
>>>
Now add the new person, which is not a new person, because the first and last names are already in use (so it updates the existing John Winter record):
>>> people.apply(Person({"first_name": "John", "last_name": "Winter", "age": 31, "thirst": 55, "hunger": 10}))
>>>
>>> for p in [f"{d.data['first_name']} {d.data['last_name']} {d.data['age']} {d.data['thirst']} {d.data['hunger']}" for d in people.valid_dictums()]: print(p)
...
Fred Flint 10 2 80
Jane Oster 20 30 30
John Medium 40 76 60
John Winter 31 55 10
>>>
The valid from and valid to timestamps are also available:
>>> for p in [f"{d.data['first_name']} {d.data['last_name']} {d.meta_data['valid_from_ts']} {d.meta_data['valid_to_ts']}" for d in people.valid_dictums()]: print(p)
...
Fred Flint 1654451266.453832 1654451466.453832
Jane Oster 1654451279.888088 1654451479.888088
John Medium 1654451282.265007 1654451482.265007
John Winter 1654451347.873571 1654451547.873571
>>>
A short while later, the first people "applied" have expired, so people.valid_dictums()
no
longer returns them, though the later record is still valid:
>>> for p in [f"{d.data['first_name']} {d.data['last_name']} {d.meta_data['valid_from_ts']} {d.meta_data['valid_to_ts']}" for d in people.valid_dictums()]: print(p)
...
John Winter 1654451347.873571 1654451547.873571
>>>
The expired records can be retrieved:
>>> for p in [f"{d.data['first_name']} {d.data['last_name']} {d.meta_data['valid_from_ts']} {d.meta_data['valid_to_ts']}" for d in people.expired_dictums()]: print(p)
...
Fred Flint 1654451266.453832 1654451466.453832
Jane Oster 1654451279.888088 1654451479.888088
John Medium 1654451282.265007 1654451482.265007
>>>
If these records are updated, they will disappear from expired and be valid again.
dictum API
The Dictum
class API is as follows.
constructor
There is one required positional parameter, the data. Optionally a ts
kwarg may be given which
changes the valid from time from now, to whatever you set. A floating point timestamp value should
be given. For example this will create a record which will not appear in the list of valid dictums
until 20 seconds has elapsed:
Dictum({"first_name": "John", "last_name": "Winter"}, ts=datetime.now().timestamp() + 20))
The valid to time (if there is a TTL set) will be adjusted as well, so the TTL will remain whatever it was.
slide_ts_window
This slides the validity window, forwards or backwards. A single positional parameter specifying the offset to apply in seconds must be given. The dictum object it self is returned. Thus this:
Dictum({"first_name": "John", "last_name": "Winter"}).slide_ts_window(20)
...will be the same as this:
Dictum({"first_name": "John", "last_name": "Winter"}, ts=datetime.now().timestamp() + 20))
extend_ts_window
This is like slide_ts_window
but only the "to" time is shifted. Though "extend" implies
a positive offset, as with slide_ts_window
a negative number may be given to shrink the
validity window.
delete
Delete the dictum.
data
This property returns the dictum's data.
meta_data
This property returns the dictum's meta data (a dictionary with valid_from_ts
and valid_to_ts
keys).
all_data
This property returns data and meta data as one dict.
id
This property returns the store ID which can uniquely identify the dict. It will
be None
if the dictum is not committed to a store.
dictum callbacks
The Dict
class has two callback methods which do nothing by default, but which your Dict
subclass may implement. Only the instance being worked on will have it's callback called.
added
Called when a dictum is added and committed to storage.
updated
Called when a change to an existing dictum is committed.
deleted
For completeness of the API, this is called when a dictum is deleted.
the tracer dictum
The Tracer class is a Dictum subclass or mix in which automatically adds "tracer" data.
To use it, add it to the super classes of your Dictum
subclass:
class DogDictum(redictum.Tracer, redictum.Dictum):
ttl = 3600 * 24 * 365
unique_by = ("owner", "address", "name")
Now data will contain an "inst_at" key, and the value will be a structure describing the module and function where the dictum was instantiated.
It you instantiate another dog, which is the same dog, then the trace data will be updated.
Notes
You may have as many different types (Dictum
subclasses) as you like, in the same store
or different ones. If you create two or more RdbmsStore
objects with the same DSN for this
purpose, it will be the same object, which should be fine. This per DSN singleton behaviour
is specific to the RdbmsStore
class, other stores behaviour may vary.
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 redictum-0.0.2.tar.gz
.
File metadata
- Download URL: redictum-0.0.2.tar.gz
- Upload date:
- Size: 16.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/34.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.63.0 importlib-metadata/4.11.2 keyring/18.0.1 rfc3986/2.0.0 colorama/0.4.3 CPython/3.8.10
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 20fdddf69a54e75e3274538585c6a62acf06b16f425d7142001c6e76a548669c |
|
MD5 | 243aa9bb1798c3557dde1d708758ecad |
|
BLAKE2b-256 | 92c07f786dde8da5ce236a79baeb3399234cb596b3eff3b6adbf62b176b325e0 |
File details
Details for the file redictum-0.0.2-py3-none-any.whl
.
File metadata
- Download URL: redictum-0.0.2-py3-none-any.whl
- Upload date:
- Size: 16.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/34.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.63.0 importlib-metadata/4.11.2 keyring/18.0.1 rfc3986/2.0.0 colorama/0.4.3 CPython/3.8.10
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 674ac9d62017a01e9d0b24bd820e9aa24891fcaca44983a3f8fdacee77b98966 |
|
MD5 | 56e972e7c9bad61d5efe1e8e1afb6f21 |
|
BLAKE2b-256 | 276fffad2c327c12988c4ce56cc9cbd207d73d21ea7a3b000706c6044f51662d |