{ "info": { "author": "Vineet Naik", "author_email": "naikvin@gmail.com", "bugtrack_url": null, "classifiers": [], "description": "Lookupy\n=======\n\nLookupy is a Python library that provides a `Django\n`_ `QuerySet\n`_ like\ninterface to query (filter and select) data (list of dictionaries).\n\nIt actually started off as a library to parse and extract useful data\nout of HAR (HTTP Archive) files but along the way I felt that a\ngeneric library can be useful since I often find myself trying to get\ndata out of JSON collections such as those obtained from facebook or\ngithub APIs. I choose to imitate the Django queryset API because of my\nfamiliarity with it.\n\nI don't use this library all the time but I do find it helpful when\nworking with deeply nested json/dicts - the kind that Facebook, Github\netc. APIs return. For everyday stuff I prefer Python's built-in\nfunctional constructs such as map, filter, list comprehensions.\n\nRequirements\n------------\n\n* Python [tested for 2.7 and 3.2]\n* `nose `_\n [optional, for running tests]\n* `coverage.py `_\n [optional, for test coverage]\n* `Tox `_\n [optional, for building and testing on different versions of Python]\n\n\nInstallation\n------------\n\nThe simplest way to install this library is to use pip\n\n.. code-block:: bash\n\n $ pip install lookupy\n\n**Tip!** Consider installing inside a\n `virtualenv `_\n\n\nQuick start\n-----------\n\nSince this library is based on Django QuerySets, it would help to\nfirst understand how they work. In Django, QuerySets are used to\nconstruct SQL queries to fetch data from the database. Using the\nfilter method of the QuerySet objects is equivalent to writing the\n*WHERE* clause in SQL.\n\nApplying the same concept to simple collections of data (lists of\ndicts), lookupy can be used to extract a subset of the data depending\nupon some criteria that is specified using what is known as the\n\"lookup parameters\".\n\nBut first, we need to construct a *Collection* object out of the\ndata set as follows,\n\n.. code-block:: pycon\n\n >>> from lookupy import Collection, Q\n >>> data = [{'framework': 'Django', 'language': 'Python', 'type': 'full-stack'},\n ... {'framework': 'Flask', 'language': 'Python', 'type': 'micro'},\n ... {'framework': 'Rails', 'language': 'Ruby', 'type': 'full-stack'},\n ... {'framework': 'Sinatra', 'language': 'Ruby', 'type': 'micro'},\n ... {'framework': 'Zend', 'language': 'PHP', 'type': 'full-stack'},\n ... {'framework': 'Slim', 'language': 'PHP', 'type': 'micro'}]\n >>> c = Collection(data)\n\nIn order to filter some data out of collection, we call the *filter*\nmethod passing our lookup parameters to it.\n\n.. code-block:: pycon\n\n >>> c.filter(framework__startswith='S')\n \n\n >>> list(c.filter(framework__startswith='S'))\n [{'framework': 'Sinatra', 'type': 'micro', 'language': 'Ruby'},\n {'framework': 'Slim', 'type': 'micro', 'language': 'PHP'}]\n\n\nA lookup parameter is basically like a conditional clause and is of\nthe form *__=* where ** is a key in the\ndict and ** is one of the predefined keywords that specify\nhow to match the ** with the actual value corresponding to the\nkey in each dict. See `list of lookup types\n<#supported-lookup-types>`_\n\nMultiple lookups passed as args are by default combined using the\n*and* logical operator (*or* and *not* are also supported as we will\nsee in a bit)\n\n.. code-block:: pycon\n\n >>> list(c.filter(framework__startswith='S', language__exact='Ruby'))\n [{'framework': 'Sinatra', 'type': 'micro', 'language': 'Ruby'}]\n\n\nFor *or* and *not*, we can compose a complex lookup using *Q* objects\nand pass them as positional arguments along with our lookup parameters\nas keyword args. Not surprisingly, the bitwise and (*&*), or (*|*) and\ninverse (*~*) are overriden to act as logical *and*, *or* and *not*\nrespectively (just the way it works in Django).\n\n.. code-block:: pycon\n\n >>> list(c.filter(Q(language__exact='Python') | Q(language__exact='Ruby')))\n [{'framework': 'Django', 'language': 'Python', 'type': 'full-stack'},\n {'framework': 'Flask', 'language': 'Python', 'type': 'micro'},\n {'framework': 'Rails', 'language': 'Ruby', 'type': 'full-stack'},\n {'framework': 'Sinatra', 'language': 'Ruby', 'type': 'micro'}]\n >>> list(c.filter(~Q(language__startswith='R'), framework__endswith='go'))\n [{'framework': 'Django', 'language': 'Python', 'type': 'full-stack'}]\n\nLookupy also supports having the result contain only selected fields\nby providing the *select* method on the QuerySet objects.\n\nCalling the filter or select methods on a QuerySet returns another\nQuerySet so these calls can be chained together. Internally, filtering\nand selecting leverage Python's generators for lazy evaluation. Also,\n*QuerySet* and *Collection* both implement the `iterator protocol\n`_ so\nnothing is evaluated until consumption.\n\n.. code-block:: pycon\n\n >>> result = c.filter(Q(language__exact='Python') | Q(language__exact='Ruby')) \\\n .filter(framework__istartswith='s')) \\\n .select('framework')\n >>> for item in result: # <-- this is where filtering will happen\n ... print(item)\n ...\n [{'framework': 'Sinatra'}]\n\nFor nested dicts, the key in the lookup parameters can be constructed\nusing double underscores as *request__status__exact=404*. Finally,\ndata can also be filtered by nested collection of key-value pairs\nusing the same *Q* object.\n\n.. code-block:: pycon\n\n >>> data = [{'a': 'python', 'b': {'p': 1, 'q': 2}, 'c': [{'name': 'version', 'value': '3.4'}, {'name': 'author', 'value': 'Guido van Rossum'}]},\n ... {'a': 'erlang', 'b': {'p': 3, 'q': 4}, 'c': [{'name': 'version', 'value': 'R16B01'}, {'name': 'author', 'y': 'Joe Armstrong'}]}]\n >>> c = Collection(data)\n >>> list(c.filter(b__q__gte=4))\n [{'a': 'erlang', 'c': [{'name': 'version', 'value': 'R16B01'}, {'y': 'Joe Armstrong', 'name': 'author'}], 'b': {'q': 4, 'p': 3}}]\n >>> list(c.filter(c__filter=Q(name='version', value__contains='.')))\n [{'a': 'python', 'c': [{'name': 'version', 'value': '3.4'}, {'name': 'author', 'value': 'Guido van Rossum'}], 'b': {'q': 2, 'p': 1}}]\n\nIn the last example, we used the *Q* object to filter the original\ndict by nested collection of key-value pairs i.e. we queried for only\nthose languages for which the version string contains a dot\n(*.*). Note that this is different from filtering the nested\ncollections themselves. To do that, we can easily construct\n*Collection* objects for the child collections.\n\nSee the *examples* subdirectory for more usage examples.\n\n\nSupported lookup types\n----------------------\n\nThese are the currently supported lookup types,\n\n* **exact** exact equality (default)\n* **neq** inequality\n* **contains** containment\n* **icontains** insensitive containment\n* **in** membership\n* **startswith** string startswith\n* **istartswith** insensitive startswith\n* **endswith** string endswith\n* **iendswith** insensitive endswith\n* **gt** greater than\n* **gte** greater than or equal to\n* **lt** less than\n* **lte** less than or equal to\n* **regex** regular expression search\n* **filter** nested filter\n\n\nGotchas!\n--------\n\n1. If a non-existent *key* is passed to *select*, then it will be\n included in the result with value *None* for all results.\n\n2. If a non-existent *key* is passed to *filter*, then the lookup will\n always fail. At first, this doesn't seem consistent with the first\n point but it's done to keep the overall behaviour predictable\n e.g. If a non-existent key is used with *lt* lookup with integer,\n say *2*, as the val, then the lookup will always fail even though\n *None < 2 == True* in Python 2. Best is to just avoid such\n situations.\n\n3. Because of the way *select* works at the moment, if chained with\n *filter* it should be called only after it and not before (unless the\n keys used for lookup are also being selected.) I plan to fix this in\n later releases.\n\n\nRunning tests\n-------------\n\n.. code-block:: bash\n\n $ make test\n\nTo conveniently test under all environments (Python 2.7 and 3.2), run,\n\n.. code-block:: bash\n\n $ tox\n\n\nTodo\n----\n\n* Measure performance for larger data sets\n* Implement CLI for JSON files\n\n\nLicense\n-------\n\nThis library is provided as-is under the\n`MIT License `_", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/naiquevin/lookupy", "keywords": null, "license": "MIT License", "maintainer": null, "maintainer_email": null, "name": "Lookupy", "package_url": "https://pypi.org/project/Lookupy/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/Lookupy/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/naiquevin/lookupy" }, "release_url": "https://pypi.org/project/Lookupy/0.2/", "requires_dist": null, "requires_python": null, "summary": "Django QuerySet inspired interface to query list of dicts", "version": "0.2" }, "last_serial": 988719, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "1a69dde4a10e26e3d97d2944e86d2cff", "sha256": "0339407df40c8d2deef3cf6fe8ff0acda9f06b4edd30ab3d314853663f15b1a5" }, "downloads": -1, "filename": "Lookupy-0.1.tar.gz", "has_sig": false, "md5_digest": "1a69dde4a10e26e3d97d2944e86d2cff", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10061, "upload_time": "2013-09-28T12:50:37", "url": "https://files.pythonhosted.org/packages/0d/71/5912bb5e31b5497a6ce388a62398cbd1230c0ff400155e006416818ae444/Lookupy-0.1.tar.gz" } ], "0.2": [ { "comment_text": "", "digests": { "md5": "2ce41b5c85f4e74bdbb48c70d14dfdda", "sha256": "04406d54bea664d04f1cc3988e7f4966469ffedc826ae0d1543cd3ac7f3a90a0" }, "downloads": -1, "filename": "Lookupy-0.2.tar.gz", "has_sig": false, "md5_digest": "2ce41b5c85f4e74bdbb48c70d14dfdda", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10006, "upload_time": "2014-02-02T18:22:23", "url": "https://files.pythonhosted.org/packages/90/bc/1fc5fdb7943c0e10300e87b71e3494b9669c9682f340231822889e4ea044/Lookupy-0.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "2ce41b5c85f4e74bdbb48c70d14dfdda", "sha256": "04406d54bea664d04f1cc3988e7f4966469ffedc826ae0d1543cd3ac7f3a90a0" }, "downloads": -1, "filename": "Lookupy-0.2.tar.gz", "has_sig": false, "md5_digest": "2ce41b5c85f4e74bdbb48c70d14dfdda", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10006, "upload_time": "2014-02-02T18:22:23", "url": "https://files.pythonhosted.org/packages/90/bc/1fc5fdb7943c0e10300e87b71e3494b9669c9682f340231822889e4ea044/Lookupy-0.2.tar.gz" } ] }