{ "info": { "author": "Shoobx, Inc.", "author_email": "dev@shoobx.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: Zope Public License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: CPython" ], "description": "=======================================\n``shoobx.immutable`` -- Immutable Types\n=======================================\n\n.. image:: https://travis-ci.org/Shoobx/shoobx.immutable.png?branch=master\n :target: https://travis-ci.org/Shoobx/shoobx.immutable\n\n.. image:: https://coveralls.io/repos/github/Shoobx/shoobx.immutable/badge.svg?branch=master\n :target: https://coveralls.io/github/Shoobx/shoobx.immutable?branch=master\n\n.. image:: https://img.shields.io/pypi/v/shoobx.immutable.svg\n :target: https://pypi.python.org/pypi/shoobx.immutable\n\n.. image:: https://img.shields.io/pypi/pyversions/shoobx.immutable.svg\n :target: https://pypi.python.org/pypi/shoobx.immutable/\n\n.. image:: https://readthedocs.org/projects/shoobximmutable/badge/?version=latest\n :target: http://shoobximmutable.readthedocs.org/en/latest/\n :alt: Documentation Status\n\nThis library provides a state-based implementation of immutable types,\nincluding lists, sets and dicts. It handles an arbitrarily deep structure of\nnested objects.\n\nIn addition, support for revisioned immutables is provided, which allows for\nfull revision histories of an immutable. A sample implementation of a\nrevisioned immutable maanger is also provided.\n\nOptional: A pjpersist-based storage mechanism for revisioned immutables is\nprovided, which allows for easy storage of versioned immutables.\n\n\n================\nUsing Immutables\n================\n\nImmutable objects can make certain complex systems more reasonable, because they\ntightly control when an object is modified and how. It also guarantees that an\nobject can never change for another accessor in a different subsystem.\n\nIntroduction\n------------\n\nLet's start with a simple dictionary:\n\n >>> import shoobx.immutable as im\n >>> answer = im.ImmutableDict({\n ... 'question': 'Answer to the ultimate question of life, ...',\n ... 'answer': 0\n ... })\n\n >>> answer['answer']\n 0\n\nBut no value can be changed anymore:\n\n >>> answer['answer'] = 42\n Traceback (most recent call last):\n ...\n AttributeError: Cannot update locked immutable object.\n\nImmutable objects are updated through a special context manager that creates a\nnew version of the object that can be modified within the context manager\nblock.\n\n >>> orig = answer\n >>> with im.update(answer) as answer:\n ... answer['answer'] = 42\n\n >>> answer['answer']\n 42\n\nNote that the `answer` dictionary is a completely new object and that the\noriginal object is still unmodified.\n\n >>> orig is not answer\n True\n >>> orig['answer']\n 0\n\nOf course we can also create complex object structures, for example by adding\na list:\n\n >>> with im.update(answer) as answer:\n ... answer['witnesses'] = ['Arthur', 'Gag']\n\n >>> answer['witnesses']\n ['Arthur', 'Gag']\n\nOf course, the list has been converted to its immutable equal, so that items\ncannot be modified.\n\n >>> isinstance(answer['witnesses'], im.ImmutableList)\n True\n >>> answer['witnesses'].append('Deep Thought')\n Traceback (most recent call last):\n ...\n AttributeError: Cannot update locked immutable object.\n\nHowever, updating from an child/sub-object is not allowed, since creating a\nnew version of a child would sematically modify its parent thus violating the\nimmutability constraint:\n\n >>> with im.update(answer['witnesses']) as witnesses:\n ... pass\n Traceback (most recent call last):\n ...\n AttributeError: update() is only available for master immutables.\n\nThe system accomplishes this by assigning \"master\" and \"slave\" modes to the\nimmutables. The root immutable is the master and any sub-objects below are\nslaves.\n\nImmutable sets are also supported as a core immutable:\n\n >>> data = im.ImmutableSet({6})\n >>> data\n {6}\n\n >>> with im.update(data) as data:\n ... data.discard(6)\n ... data.add(9)\n >>> data\n {9}\n\n\nCustom Immutables\n-----------------\n\nCreating your own immutable objects is simple:\n\n >>> class Answer(im.Immutable):\n ... def __init__(self, question=None, answer=None, witnesses=None):\n ... self.question = question\n ... self.answer = answer\n ... self.witnesses = witnesses\n\n >>> answer = Answer('The Answer', 42, ['Arthur', 'Gag'])\n >>> answer.answer\n 42\n\nNote how the list is automatically converted to its immutable equivalent:\n\n >>> isinstance(answer.witnesses, im.ImmutableList)\n True\n\nOf course you cannot modify an immutable other than the update context:\n\n >>> answer.answer = 54\n Traceback (most recent call last):\n ...\n AttributeError: Cannot update locked immutable object.\n\n >>> with im.update(answer) as answer:\n ... answer.answer = 54\n >>> answer.answer\n 54\n\n\nRevisioned Immutables\n---------------------\n\nSince mutables create a new object for every change, they are ideal for\ncreating systems that have to keep track of their entire history. This package\nprovides support for such systems by defining a revision manager API and\nrevisioned immutable that are managed within it.\n\nLet's start by creating a custom revisioned immutable:\n\n >>> class Answer(im.RevisionedImmutable):\n ...\n ... def __init__(self, question=None, answer=None):\n ... self.question = question\n ... self.answer = answer\n\nA simple implementation of the revision manager API is provided to demonstrate\na possible implementation path.\n\n >>> data = im.RevisionedMapping()\n >>> data['a'] = answer = Answer('Answer to the ultimate question')\n\nThe answer is the current revision and has been added to the\nmanager.\n\n >>> data['a'] is answer\n True\n\nIn addition to the usual immutability features, the Revisioned\nimmutable has several additional attributes that help with the management of\nthe revisions:\n\n >>> answer.__im_start_on__\n datetime.datetime(...)\n >>> answer.__im_end_on__ is None\n True\n >>> answer.__im_manager__\n \n >>> answer.__im_creator__ is None\n True\n >>> answer.__im_comment__ is None\n True\n\nThe update API is extended to support setting the creator and comment of the\nchange:\n\n >>> answer_r1 = answer\n >>> with im.update(answer, 'universe', 'Provide Answer') as answer:\n ... answer.answer = 42\n\nWe now have a second revision of the answer that has the comemnt and creator\nset:\n\n >>> answer.answer\n 42\n\n >>> answer.__im_start_on__\n datetime.datetime(...)\n >>> answer.__im_end_on__ is None\n True\n >>> answer.__im_creator__\n 'universe'\n >>> answer.__im_comment__\n 'Provide Answer'\n\nThe first revision is now retired and has an end date/time (which equals the\nstart date/time of the new revision):\n\n >>> answer_r1.__im_start_on__\n datetime.datetime(...)\n >>> answer_r1.__im_end_on__ == answer.__im_start_on__\n True\n >>> answer_r1.__im_state__ == im.interfaces.IM_STATE_RETIRED\n True\n\nThe manager has APIs to manage the various revisions.\n\n >>> revisions = data.getRevisionManager('a')\n >>> len(revisions.getRevisionHistory())\n 2\n\n >>> revisions.getCurrentRevision(answer_r1) is answer\n True\n\nWe can even roll back to a previous revision:\n\n >>> revisions.rollbackToRevision(answer_r1)\n\n >>> len(revisions.getRevisionHistory())\n 1\n >>> answer_r1.__im_end_on__ is None\n True\n >>> answer_r1.__im_state__ == im.interfaces.IM_STATE_LOCKED\n True\n\n\nOptional `pjpersist` Support\n----------------------------\n\nA more serious and production-ready implementation of the revision manager API\nis provided in `shoobx.immutable.pjpersist` which utilizes `pjpersist` to\nstore all data.\n\n\nNotes\n-----\n\nA technical discussion on the system's inner workings is located in the\ndoc strings of the corresponding interfaces. In addition, the tests covera a\nlot of special cases not dicsussed here.\n\n\n=======\nCHANGES\n=======\n\n\n1.1.1 (2019-06-11)\n------------------\n\n- Added `datetime` classes as system immutable types.\n\n\n1.1.0 (2019-05-31)\n------------------\n\n- Introduced `__im_version__` to `IRevisionedImmutable` and use it instead of\n timestamps to create a chronological order of revisions. (Timestamps might be\n slightly different accross servers and cause bad history.)\n\n- Do not duplicate implementation of `__im_update__()` in\n `RevisionedImmutableBase`. Use `__im_[before|after]_update__()` to do all\n revision-related tasks.\n\n- Tweak `copy()` implementation for `ImmutableList` and `ImmutableDict`.\n\n- Properly implement `ImmutableDict.fromkeys()`.\n\n\n1.0.5 (2019-05-31)\n------------------\n\n- Fix `ImmutableList.copy()` to just work when locked. This allows for only\n making a shallow clone, since any update will cause a deep copy and thus\n immutability is guaranteed.\n\n- Implemented `ImmutableDict.copy()`. Raise error on `ImmutableDict.fromkeys()`.\n\n- `ImmutableContainer` also needs an updated `_pj_column_fields` list.\n\n- Minor test fixes.\n\n- Minor documentation fixes and code comment enhancements.\n\n\n1.0.4 (2019-05-30)\n------------------\n\n- Add API documentation.\n\n\n1.0.3 (2019-05-30)\n------------------\n\n- Moved documentation to Read the Docs.\n\n\n1.0.2 (2019-05-30)\n------------------\n\n- Add some readable documentation.\n\n- Added high-level `shoobx.immutable.update(im, *args, **kw)` function.\n\n- Implemented `__repr__()` for `ImmutableSet` to mimic behavior of\n `ImmutableDict` and `ImmutableList`.\n\n\n1.0.1 (2019-05-30)\n------------------\n\n- Fix package description.\n\n\n1.0.0 (2019-05-30)\n------------------\n\n- Immutable Types, Immutable Dict, Immutable Set, Immutable List\n\n- Revisioned Immutable with Revision Manager sample implementation\n\n- Optional: pjpersist support for immutables. Requires pjpersist>=1.7.0.\n\n- Initial Release", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://pypi.python.org/pypi/shoobx.immutable", "keywords": "immutable revisioned pjpersist", "license": "ZPL 2.1", "maintainer": "", "maintainer_email": "", "name": "shoobx.immutable", "package_url": "https://pypi.org/project/shoobx.immutable/", "platform": "", "project_url": "https://pypi.org/project/shoobx.immutable/", "project_urls": { "Homepage": "http://pypi.python.org/pypi/shoobx.immutable" }, "release_url": "https://pypi.org/project/shoobx.immutable/1.1.1/", "requires_dist": null, "requires_python": "", "summary": "Immutable Types", "version": "1.1.1" }, "last_serial": 5385631, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "fddd9204454d199ed57951a73cbddb9b", "sha256": "1cf50ddffacd6a123d2d8160fde656ecd3a5d5a123abf58f3efca5800c386d00" }, "downloads": -1, "filename": "shoobx.immutable-1.0.0.tar.gz", "has_sig": false, "md5_digest": "fddd9204454d199ed57951a73cbddb9b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20339, "upload_time": "2019-05-30T15:04:14", "url": "https://files.pythonhosted.org/packages/24/7e/e920be04d39df95596783c3ed334c102488fbf4b6ed4fcb3cd82ebd38d8c/shoobx.immutable-1.0.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "9368129ec56774f4d3ef390cf6a5edc6", "sha256": "06077805fcea5c730c9f9603d8cc52b052cf2359233a4133f61b770bf31cc2d9" }, "downloads": -1, "filename": "shoobx.immutable-1.0.1.tar.gz", "has_sig": false, "md5_digest": "9368129ec56774f4d3ef390cf6a5edc6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20344, "upload_time": "2019-05-30T15:06:38", "url": "https://files.pythonhosted.org/packages/f7/8b/c8ff61e734ff41d31c0fdb64742c33239b1d8e8572ebbf363714b272cae0/shoobx.immutable-1.0.1.tar.gz" } ], "1.0.2": [ { "comment_text": "", "digests": { "md5": "0213846e8ea18f162e574f435b052565", "sha256": "393fdadbd1c76dd3ba3db663408501205bec1d338adcbd107c885ca08ee9976e" }, "downloads": -1, "filename": "shoobx.immutable-1.0.2.tar.gz", "has_sig": false, "md5_digest": "0213846e8ea18f162e574f435b052565", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 25536, "upload_time": "2019-05-30T17:13:35", "url": "https://files.pythonhosted.org/packages/0d/f5/5b46df70ff524e73e48378e57784ed547898365909e7ec0122e9158be109/shoobx.immutable-1.0.2.tar.gz" } ], "1.0.3": [ { "comment_text": "", "digests": { "md5": "caeceebe558faa13c85ed83c1855c17d", "sha256": "5f30fc2bd5b8bca57791a90d1b8f971289d0500afd67e3272e9f038479198e63" }, "downloads": -1, "filename": "shoobx.immutable-1.0.3.tar.gz", "has_sig": false, "md5_digest": "caeceebe558faa13c85ed83c1855c17d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 29008, "upload_time": "2019-05-30T20:14:43", "url": "https://files.pythonhosted.org/packages/d9/15/809d26c9642a633d991ad73a12f759adf81bbf5af31debbddd83babbe00e/shoobx.immutable-1.0.3.tar.gz" } ], "1.0.4": [ { "comment_text": "", "digests": { "md5": "0d8228570089687aca372fc9650ea586", "sha256": "c499660d0af694a2ccdc750764b91b4da125667d4a93883c6753b553b998c143" }, "downloads": -1, "filename": "shoobx.immutable-1.0.4.tar.gz", "has_sig": false, "md5_digest": "0d8228570089687aca372fc9650ea586", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 29698, "upload_time": "2019-05-31T00:50:01", "url": "https://files.pythonhosted.org/packages/2c/b0/0211c9db12528282b17edbe5be5fe3a17f8117daaa281c61214d4d7fcc96/shoobx.immutable-1.0.4.tar.gz" } ], "1.0.5": [ { "comment_text": "", "digests": { "md5": "04d5f2b4cb38b8a69af066a0afbcdb11", "sha256": "51ace7942435fa3c86f4f45927d687c2cd37172705283af737ac052ae5a695ad" }, "downloads": -1, "filename": "shoobx.immutable-1.0.5.tar.gz", "has_sig": false, "md5_digest": "04d5f2b4cb38b8a69af066a0afbcdb11", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30409, "upload_time": "2019-05-31T11:32:33", "url": "https://files.pythonhosted.org/packages/b0/b6/53f3c2775b787bd09b6802d7a05c86ae28bc081fc02cee0a7c51d6087c05/shoobx.immutable-1.0.5.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "1eeeeba9fbf4069ccb6b4a6370b5d007", "sha256": "06b485c6bcff542586e87552981e158ebb91700cbe057ffcbd75755830dcde3c" }, "downloads": -1, "filename": "shoobx.immutable-1.1.0.tar.gz", "has_sig": false, "md5_digest": "1eeeeba9fbf4069ccb6b4a6370b5d007", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31372, "upload_time": "2019-05-31T12:26:45", "url": "https://files.pythonhosted.org/packages/11/55/f076898b31de825214d440e5116f75b8f57a4a4e8f7d85bece4e73c2ea35/shoobx.immutable-1.1.0.tar.gz" } ], "1.1.1": [ { "comment_text": "", "digests": { "md5": "8a8afda65cfe5dc12a70667515c65794", "sha256": "5c94510f0cb8b7f52cc7ec73e18250e52764ea8a5f0cd4fb5e168de6d80dde4c" }, "downloads": -1, "filename": "shoobx.immutable-1.1.1.tar.gz", "has_sig": false, "md5_digest": "8a8afda65cfe5dc12a70667515c65794", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31226, "upload_time": "2019-06-11T09:13:01", "url": "https://files.pythonhosted.org/packages/3e/84/af2c78c45e0518d8caaf93f1982adbf41664fe07b3c7cf965c60a9186eca/shoobx.immutable-1.1.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "8a8afda65cfe5dc12a70667515c65794", "sha256": "5c94510f0cb8b7f52cc7ec73e18250e52764ea8a5f0cd4fb5e168de6d80dde4c" }, "downloads": -1, "filename": "shoobx.immutable-1.1.1.tar.gz", "has_sig": false, "md5_digest": "8a8afda65cfe5dc12a70667515c65794", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31226, "upload_time": "2019-06-11T09:13:01", "url": "https://files.pythonhosted.org/packages/3e/84/af2c78c45e0518d8caaf93f1982adbf41664fe07b3c7cf965c60a9186eca/shoobx.immutable-1.1.1.tar.gz" } ] }