{
"info": {
"author": "BlueDynamics Alliance",
"author_email": "dev@bluedynamics.com",
"bugtrack_url": null,
"classifiers": [
"Development Status :: 5 - Production/Stable",
"Framework :: ZODB",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content"
],
"description": ".. image:: https://travis-ci.org/bluedynamics/souper.svg?branch=master\n :target: https://travis-ci.org/bluedynamics/souper\n\nZODB Storage for lots of (light weight) data.\n\nUtilizes:\n\n- `ZODB `_ and its `BTrees `_,\n- `node `_ (and `node.ext.zodb `_).\n- `repoze.catalog `_,\n\n.. image:: https://raw.githubusercontent.com/bluedynamics/souper/master/docs/Souper-64.png\n\nSouper is a tool for programmers. It offers an integrated storage tied together with indexes in a catalog.\nThe records in the storage are generic.\nIt is possible to store any data on a record if it is persistent pickable in ZODB.\n\nSouper can be used used in any Python application, either standalone using the pure ZODB or with `Pyramid `_, `Zope `_ or `Plone `_.\n\n\nUsing Souper\n============\n\n\nProviding a Locator\n-------------------\n\nSoups are looked up by adapting ``souper.interfaces.IStorageLocator`` to some context.\nSouper does not provide any default locator.\nSo first one need to be provided. Let's assume context is some persistent dict-like instance\n\n.. code-block:: pycon\n\n >>> from zope.interface import implementer\n >>> from zope.interface import Interface\n >>> from zope.component import provideAdapter\n >>> from souper.interfaces import IStorageLocator\n >>> from souper.soup import SoupData\n >>> @implementer(IStorageLocator)\n ... class StorageLocator(object):\n ...\n ... def __init__(self, context):\n ... self.context = context\n ...\n ... def storage(self, soup_name):\n ... if soup_name not in self.context:\n ... self.context[soup_name] = SoupData()\n ... return self.context[soup_name]\n\n >>> provideAdapter(StorageLocator, adapts=[Interface])\n\nSo we have locator creating soups by name on the fly. Now its easy to get a soup by name:\n\n.. code-block:: pycon\n\n >>> from souper.soup import get_soup\n >>> soup = get_soup('mysoup', context)\n >>> soup\n \n\n\nProviding a Catalog Factory\n---------------------------\n\nDepending on your needs the catalog and its indexes may look different from use-case to use-case.\nThe catalog factory is responsible to create a catalog for a soup. The factory is a named utility implementing ``souper.interfaces.ICatalogFactory``.\nThe name of the utility has to the the same as the soup have.\n\nHere ``repoze.catalog`` is used and to let the indexes access the data on the records by key the ``NodeAttributeIndexer`` is used.\nFor special cases one may write its custom indexers, but the default one is fine most of the time:\n\n.. code-block:: pycon\n\n >>> from souper.interfaces import ICatalogFactory\n >>> from souper.soup import NodeAttributeIndexer\n >>> from souper.soup import NodeTextIndexer\n >>> from zope.component import provideUtility\n >>> from repoze.catalog.catalog import Catalog\n >>> from repoze.catalog.indexes.field import CatalogFieldIndex\n >>> from repoze.catalog.indexes.text import CatalogTextIndex\n >>> from repoze.catalog.indexes.keyword import CatalogKeywordIndex\n\n >>> @implementer(ICatalogFactory)\n ... class MySoupCatalogFactory(object):\n ...\n ... def __call__(self, context=None):\n ... catalog = Catalog()\n ... userindexer = NodeAttributeIndexer('user')\n ... catalog[u'user'] = CatalogFieldIndex(userindexer)\n ... textindexer = NodeTextIndexer(['text', 'user')\n ... catalog[u'text'] = CatalogTextIndex(textindexer)\n ... keywordindexer = NodeAttributeIndexer('keywords')\n ... catalog[u'keywords'] = CatalogKeywordIndex(keywordindexer)\n ... return catalog\n\n >>> provideUtility(MySoupCatalogFactory(), name=\"mysoup\")\n\nThe catalog factory is used soup-internal only but one may want to check if it works fine:\n\n.. code-block:: pycon\n\n >>> catalogfactory = getUtility(ICatalogFactory, name='mysoup')\n >>> catalogfactory\n \n\n >>> catalog = catalogfactory()\n >>> sorted(catalog.items())\n [(u'keywords', ),\n (u'text', ),\n (u'user', )]\n\n\nAdding records\n--------------\n\nAs mentioned above the ``souper.soup.Record`` is the one and only kind of data added to the soup.\nA record has attributes containing the data:\n\n.. code-block:: pycon\n\n >>> from souper.soup import get_soup\n >>> from souper.soup import Record\n >>> soup = get_soup('mysoup', context)\n >>> record = Record()\n >>> record.attrs['user'] = 'user1'\n >>> record.attrs['text'] = u'foo bar baz'\n >>> record.attrs['keywords'] = [u'1', u'2', u'\u00fc']\n >>> record_id = soup.add(record)\n\nA record may contains other records. But to index them one would need a custom indexer.\nSo, usually contained records are valuable for later display, not for searching:\n\n.. code-block:: pycon\n\n >>> record['subrecord'] = Record()\n >>> record['homeaddress'].attrs['zip'] = '6020'\n >>> record['homeaddress'].attrs['town'] = 'Innsbruck'\n >>> record['homeaddress'].attrs['country'] = 'Austria'\n\n\nAccess data\n-----------\n\nEven without any query a record can be fetched by id:\n\n.. code-block:: pycon\n\n >>> from souper.soup import get_soup\n >>> soup = get_soup('mysoup', context)\n >>> record = soup.get(record_id)\n\nAll records can be accessed using utilizing the container BTree:\n\n.. code-block:: pycon\n\n >>> soup.data.keys()[0] == record_id\n True\n\n\nQuery data\n----------\n\n`How to query a repoze catalog is documented well. `_\nSorting works the same too.\nQueries are passed to soups ``query`` method (which uses then repoze catalog).\nIt returns a generator:\n\n.. code-block:: pycon\n\n >>> from repoze.catalog.query import Eq\n >>> [r for r in soup.query(Eq('user', 'user1'))]\n []\n\n >>> [r for r in soup.query(Eq('user', 'nonexists'))]\n []\n\nTo also get the size of the result set pass a ``with_size=True`` to the query.\nThe first item returned by the generator is the size:\n\n.. code-block:: pycon\n\n >>> [r for r in soup.query(Eq('user', 'user1'), with_size-True)]\n [1, ]\n\n\nTo optimize handling of large result sets one may not to fetch the record but a generator returning light weight objects. Records are fetched on call:\n\n.. code-block:: pycon\n\n >>> lazy = [l for l in soup.lazy(Eq('name', 'name'))]\n >>> lazy\n [,\n\n >>> lazy[0]()\n \n\nHere the size is passed as first value of the geneartor too if ``with_size=True`` is passed.\n\n\nDelete a record\n---------------\n\nTo remove a record from the soup python ``del`` is used like one would do on\nany dict:\n\n.. code-block:: pycon\n\n >>> del soup[record]\n\n\nReindex\n-------\n\nAfter a records data changed it needs a reindex:\n\n.. code-block:: pycon\n\n >>> record.attrs['user'] = 'user1'\n >>> soup.reindex(records=[record])\n\nSometimes one may want to reindex all data. Then ``reindex`` has to be called without parameters.\nIt may take a while:\n\n.. code-block:: pycon\n\n >>> soup.reindex()\n\n\nRebuild catalog\n---------------\n\nUsally after a change of the catalog factory was made - i.e. some index was added - a rebuild of the catalog i needed.\nIt replaces the current catalog with a new one created by the catalog factory and reindexes all data.\nIt may take while:\n\n.. code-block:: pycon\n\n >>> soup.rebuild()\n\n\nReset (or clear) the soup\n-------------------------\n\nTo remove all data from the soup and empty and rebuild the catalog call ``clear``.\n\n**Attention**: *All data is lost!*\n\n.. code-block:: pycon\n\n >>> soup.clear()\n\n\nSource Code\n===========\n\nThe sources are in a GIT DVCS with its main branches at `github `_.\n\nWe'd be happy to see many forks and pull-requests to make souper even better.\n\n\nContributors\n============\n\n- Robert Niederreiter \n\n- Jens W. Klein \n\n\nChangelog\n=========\n\n1.1.1 (2019-09-16)\n------------------\n\n- Cleanup NodeTextIndexer (one loop is enough).\n [jensens]\n\n\n1.1.0 (2019-03-08)\n------------------\n\n- Code style (black, isort, utf8headers).\n [jensens]\n\n- Switched to tox for testing, builodut gone.\n [jensens]\n\n- Python 2/3 compatibility\n [agitator]\n\n\n1.0.2 (2015-02-25)\n------------------\n\n- fix: unicode with special chars in text indexer failed.\n [jensens, 2014-02-25]\n\n1.0.1\n-----\n\n- PEP-8.\n [rnix, 2012-10-16]\n\n- Python 2.7 Support.\n [rnix, 2012-10-16]\n\n- Fix documentation.\n\n1.0\n---\n\n- make it work\n [rnix, jensens, et al]",
"description_content_type": "",
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "https://pypi.org/project/souper",
"keywords": "zodb zope pyramid node plone",
"license": "BSD",
"maintainer": "",
"maintainer_email": "",
"name": "souper",
"package_url": "https://pypi.org/project/souper/",
"platform": "",
"project_url": "https://pypi.org/project/souper/",
"project_urls": {
"Homepage": "https://pypi.org/project/souper"
},
"release_url": "https://pypi.org/project/souper/1.1.1/",
"requires_dist": null,
"requires_python": "",
"summary": "Souper - Generic Indexed Storage based on ZODB",
"version": "1.1.1"
},
"last_serial": 5836702,
"releases": {
"1.0": [
{
"comment_text": "",
"digests": {
"md5": "5718f015f9b715b9a2e849695d0dacff",
"sha256": "9cd668c26725cb1b01301395287f13a61102f7ce63f01fe5cd358d35ed50ece1"
},
"downloads": -1,
"filename": "souper-1.0.tar.gz",
"has_sig": false,
"md5_digest": "5718f015f9b715b9a2e849695d0dacff",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 10836,
"upload_time": "2012-10-07T08:38:58",
"url": "https://files.pythonhosted.org/packages/de/d7/62e4b352ab064a6b4d3cb01def687995bd28c7b314ca5cc97a3d45ce0379/souper-1.0.tar.gz"
}
],
"1.0-beta1": [
{
"comment_text": "",
"digests": {
"md5": "1cf40e0713b33a2566a9f62600c27577",
"sha256": "173416722f4de319cfbee6161fc1b58c83ef410ffe88c29cd99d3397eba656cf"
},
"downloads": -1,
"filename": "souper-1.0-beta1.tar.gz",
"has_sig": false,
"md5_digest": "1cf40e0713b33a2566a9f62600c27577",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 13196,
"upload_time": "2012-08-28T17:06:34",
"url": "https://files.pythonhosted.org/packages/98/3c/43239a9419ba1445430f76504c152e1407a2519d04703ab27a273bb9fb5e/souper-1.0-beta1.tar.gz"
}
],
"1.0.1": [
{
"comment_text": "",
"digests": {
"md5": "1510d6af465249a9989159ef753255d2",
"sha256": "d96b43b0e51a8111fd8671bc1f1a15fa68be8e0fed1f90ba6a86e0f219b231d9"
},
"downloads": -1,
"filename": "souper-1.0.1.tar.gz",
"has_sig": false,
"md5_digest": "1510d6af465249a9989159ef753255d2",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 10881,
"upload_time": "2012-12-05T11:10:14",
"url": "https://files.pythonhosted.org/packages/eb/0e/5e9138d50a87c5ff26aa069b9abb814762e246b0c97adf8e8c3aac5189ca/souper-1.0.1.tar.gz"
}
],
"1.0.2": [
{
"comment_text": "",
"digests": {
"md5": "f3c64a13b6bde42c8320a0ff165d47c7",
"sha256": "7d3b6114d43b2c00be9d501abb498fcf5e48b907f85337ddc55e084ed0c1002f"
},
"downloads": -1,
"filename": "souper-1.0.2.zip",
"has_sig": false,
"md5_digest": "f3c64a13b6bde42c8320a0ff165d47c7",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 20727,
"upload_time": "2015-02-25T11:55:55",
"url": "https://files.pythonhosted.org/packages/36/6c/2baf6b285d5298f3c7640cc8e393462204baf82c8d23f0822742b86b17bb/souper-1.0.2.zip"
}
],
"1.1.0": [
{
"comment_text": "",
"digests": {
"md5": "927da1a81038394b49389fc4360fdda8",
"sha256": "0ff37aa97c782e4ea9225c3bdeba24bdf1d60a6c316e9f806d8dadb9550c4181"
},
"downloads": -1,
"filename": "souper-1.1.0.tar.gz",
"has_sig": false,
"md5_digest": "927da1a81038394b49389fc4360fdda8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 25163,
"upload_time": "2019-03-08T09:14:13",
"url": "https://files.pythonhosted.org/packages/6f/f7/a1dead8b833f162973ab0dcd8f2d12af2664abddc76be4c1a4ddcebddf64/souper-1.1.0.tar.gz"
}
],
"1.1.1": [
{
"comment_text": "",
"digests": {
"md5": "59ded489f73aead5a5db5c1f3b56784b",
"sha256": "2ff9d90f3e8437392b6140a086dbe0ea5a8c76b08604729f77bd7813e26f3ca1"
},
"downloads": -1,
"filename": "souper-1.1.1.tar.gz",
"has_sig": false,
"md5_digest": "59ded489f73aead5a5db5c1f3b56784b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 24996,
"upload_time": "2019-09-16T14:56:21",
"url": "https://files.pythonhosted.org/packages/1e/dd/c58e66c9c18269ce8e6d277db8670aabd5d074690b1830d43fc1151a5e0f/souper-1.1.1.tar.gz"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "59ded489f73aead5a5db5c1f3b56784b",
"sha256": "2ff9d90f3e8437392b6140a086dbe0ea5a8c76b08604729f77bd7813e26f3ca1"
},
"downloads": -1,
"filename": "souper-1.1.1.tar.gz",
"has_sig": false,
"md5_digest": "59ded489f73aead5a5db5c1f3b56784b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 24996,
"upload_time": "2019-09-16T14:56:21",
"url": "https://files.pythonhosted.org/packages/1e/dd/c58e66c9c18269ce8e6d277db8670aabd5d074690b1830d43fc1151a5e0f/souper-1.1.1.tar.gz"
}
]
}