|ukraine|
|version| |tests| |coverage| |pyversions| |pre-commit| |black| |bandit|
==========
Handpick
==========
Handpick is a tool to work with nested data structures.
Installation
============
.. code::
pip install handpick
Quick introduction
==================
The ``pick`` function
---------------------
The `pick`_ generator function performs the recursive traversal
of a nested data structure and picks all objects that meet certain
criteria provided in the form of a predicate function.
Picked objects are retrieved lazily by an iterator.
Simple predicate functions
~~~~~~~~~~~~~~~~~~~~~~~~~~
The predicate function is passed to ``pick`` as the ``predicate``
argument. For example:
.. code-block:: python
from handpick import pick
def is_non_empty_string(obj):
return isinstance(obj, str) and obj
data = [[1, ""], [-2, ["foo", 3.0]], -4, "bar"]
.. code::
>>> for s in pick(data, predicate=is_non_empty_string):
... print(s)
...
foo
bar
Handling dictionary keys
~~~~~~~~~~~~~~~~~~~~~~~~
When traversing mappings like dictionaries, you can configure
whether or not ``pick`` will examine dictionary keys by specifying
the ``dict_keys`` keyword argument. Default is False, which means
only dictionary values are examined. For example:
.. code-block:: python
from handpick import pick
data = {"foo": {"name": "foo"}, "bar": {"name": "bar"}}
.. code::
>>> for s in pick(data, predicate=lambda obj: "a" in obj):
... print(s)
...
bar
>>> for s in pick(data, predicate=lambda obj: "a" in obj, dict_keys=True):
... print(s)
...
name
bar
name
bar
Predicates
----------
The ``Predicate`` decorator
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The `Predicate`_ decorator wraps a function in an object that can be
combined with other predicates using the operators ``&`` (and) and
``|`` (or), as well as negated using the operator ``~`` (not).
Combining predicates
~~~~~~~~~~~~~~~~~~~~
For example:
.. code-block:: python
from handpick import pick, Predicate
@Predicate
def is_integer(obj):
return isinstance(obj, int)
@Predicate
def is_even(number):
return number % 2 == 0
data = [[4, [5.0, 1], 3.0], [[15, []], {17: [7, [8], 0]}]]
# compound predicate
odd_int = is_integer & ~is_even
.. code::
>>> for n in pick(data, predicate=odd_int):
... print(n)
...
1
15
7
Combining predicates with functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In addition, the ``&`` and ``|`` operations are supported between
predicates and regular undecorated functions. For example:
.. code-block:: python
from handpick import pick, Predicate
@Predicate
def is_list(obj):
return isinstance(obj, list)
data = [("1", [2]), {("x",): [(3, [4]), "5"]}, ["x", ["6"]], {7: ("x",)}]
# compound predicate
short_list = (lambda obj: len(obj) < 2) & is_list
.. code::
>>> for l in pick(data, predicate=short_list):
... print(l)
...
[2]
[4]
['6']
Suppressing errors
~~~~~~~~~~~~~~~~~~
The important thing to note is that when the predicate's underlying
function raises an exception, the exception is suppressed and the predicate
returns False. In other words, it is assumed that the object in question does
not meet the picking criteria. For example:
.. code-block:: python
from handpick import pick, Predicate
@Predicate
def above_zero(number):
return number > 0
.. code::
>>> above_zero(1)
True
>>> above_zero("a")
False
>>> for n in pick([[1, "Py", -2], [None, 3.0]], predicate=above_zero):
... print(n)
...
1
3.0
In the example above, several lists and strings were internally compared to ``0``
but no ``TypeError`` propagated up to the code that called ``above_zero``.
Predicate factories
~~~~~~~~~~~~~~~~~~~
The `is_type`_ function can be used to create predicates based on
an object's type. For example:
.. code-block:: python
from handpick import pick, is_type
data = [[1.0, [2, True]], [False, [3]], ["4"]]
strictly_int = is_type(int) & ~is_type(bool)
.. code::
>>> for n in pick(data, predicate=strictly_int):
... print(n)
...
2
3
The `no_error`_ function can be used to create predicates based on
whether a function applied to an object raises an error.
.. code-block:: python
from handpick import pick, is_type, no_error
data = {"name": "spam", "price": "15.42", "quantity": 68, "year": "2011"}
# strings that can be cast to floats
numeric_str = is_type(str) & no_error(float)
.. code::
>>> for s in pick(data, predicate=numeric_str):
... print(s)
...
15.42
2011
Useful functions
----------------
The ``values_for_key`` function
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When inspecting data structures that contain dictionaries or other
mappings, you can use `values_for_key`_ to retrieve values associated with
a specific key, regardless of the nested depth in which these values
are stored. Values are retrieved lazily by an iterator. For example:
.. code-block:: python
from handpick import values_for_key
data = {
"node_id": 4,
"child_nodes": [
{
"node_id": 8,
"child_nodes": [
{
"node_id": 16,
},
],
},
{
"id": 9,
},
],
}
.. code::
>>> for i in values_for_key(data, key="node_id"):
... print(i)
...
4
8
16
Multiple keys may be specified at a time. For example:
.. code::
>>> for i in values_for_key(data, key=["node_id", "id"]):
... print(i)
...
4
8
16
9
The ``max_depth`` function
~~~~~~~~~~~~~~~~~~~~~~~~~~
This function returns the maximum nested depth of a data structure. For
example:
.. code::
>>> from handpick import max_depth
>>> max_depth([0, [1, [2]]])
2
>>> max_depth({0: {1: {2: {3: {4: 4}}}}})
4
**Note:** Just like non-empty collections, empty collections constitute
another level of nested depth. For example:
.. code::
>>> max_depth([0, [1, []]])
2
Recipes
=======
Flattening nested data
----------------------
To flatten a list of lists, use the `pick`_ function without
the ``predicate`` argument and pass ``collections=False``. For example:
.. code-block:: python
from handpick import pick
data = [[], [0], [[[], 1], [2, [3, [4]], []], [5]]]
.. code::
>>> list(pick(data, collections=False))
[0, 1, 2, 3, 4, 5]
API reference
=============
pick
----
*handpick.pick(data, predicate=None, *, collections=True, dict_keys=False, bytes_like=False)*
Pick objects from ``data`` based on ``predicate``.
Traverse ``data`` recursively and yield all objects for which
``predicate(obj)`` is True or truthy. ``data`` should be an iterable
collection.
``predicate`` must be callable, must take one argument, and should
return a Boolean value. If ``predicate`` is omitted or None, all objects
are picked.
By default, collections of other objects are yielded just like any
other objects. To exclude collections, pass ``collections=False``.
When traversing a mapping, only its values are inspected by default.
To inspect both keys and values of mappings, pass ``dict_keys=True``.
By default, bytes-like sequences (bytes and bytearrays) are not
treated as collections of other objects and therefore not iterated
by the recursive algorithm. This can be changed by passing
``bytes_like=True``.
Strings are not treated as collections of other objects and
therefore not iterated by the recursive algorithm.
Predicate
---------
*@handpick.Predicate(func=None, *, suppressed_errors=(TypeError, ValueError, LookupError, AttributeError))*
Decorator wrapping a function in a predicate object.
The decorated function can be combined with other predicates using
the operators ``&`` (and) and ``|`` (or), as well as negated using the
operator ``~`` (not).
``suppressed_errors`` can be used to customize which exception classes
will be suppressed by the predicate.
Predicate objects are intended to be used as the ``predicate``
argument to the ``pick`` function.
is_type
-------
*handpick.is_type(type_or_types)*
Predicate factory. Return a predicate that returns True if
object is an instance of specified type(s).
``type_or_types`` must be a type or tuple of types.
no_error
--------
*handpick.no_error(func)*
Predicate factory. Return a predicate that returns True if ``func``
can be applied on object without an exception being raised,
False otherwise.
values_for_key
--------------
*handpick.values_for_key(data, key)*
Pick values associated with a specific key.
Traverse ``data`` recursively and yield a sequence of dictionary
values that are mapped to ``key``. ``key`` may be a list of multiple
keys.
max_depth
---------
*handpick.max_depth(data)*
Return maximum nested depth of ``data``.
``data`` should be an iterable collection. Depth is counted from zero,
i.e. the direct elements of ``data`` are in depth 0.
.. |version| image:: https://img.shields.io/pypi/v/handpick
:target: https://pypi.org/project/handpick
.. |ukraine| image:: https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg
:target: https://stand-with-ukraine.pp.ua
.. |tests| image:: https://github.com/mportesdev/handpick/actions/workflows/tests.yml/badge.svg
:target: https://github.com/mportesdev/handpick/actions
.. |coverage| image:: https://img.shields.io/codecov/c/gh/mportesdev/handpick
:target: https://codecov.io/gh/mportesdev/handpick
.. |pyversions| image:: https://img.shields.io/pypi/pyversions/handpick
:target: https://pypi.org/project/handpick
.. |pre-commit| image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit
:target: https://github.com/pre-commit/pre-commit
.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
.. |bandit| image:: https://img.shields.io/badge/security-bandit-yellow.svg
:target: https://github.com/PyCQA/bandit
Raw data
{
"_id": null,
"home_page": "https://github.com/mportesdev/handpick",
"name": "handpick",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8,<4.0",
"maintainer_email": "",
"keywords": "",
"author": "Michal Porte\u0161",
"author_email": "michalportes1@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/a1/ca/4910f43d800bb1ac788efaf9abb22ca1741b2521245ab5ee90207b25b3c6/handpick-0.16.0.tar.gz",
"platform": null,
"description": "|ukraine|\n\n|version| |tests| |coverage| |pyversions| |pre-commit| |black| |bandit|\n\n==========\n Handpick\n==========\n\nHandpick is a tool to work with nested data structures.\n\n\nInstallation\n============\n\n.. code::\n\n pip install handpick\n\n\nQuick introduction\n==================\n\n\nThe ``pick`` function\n---------------------\n\nThe `pick`_ generator function performs the recursive traversal\nof a nested data structure and picks all objects that meet certain\ncriteria provided in the form of a predicate function.\nPicked objects are retrieved lazily by an iterator.\n\n\nSimple predicate functions\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe predicate function is passed to ``pick`` as the ``predicate``\nargument. For example:\n\n.. code-block:: python\n\n from handpick import pick\n\n def is_non_empty_string(obj):\n return isinstance(obj, str) and obj\n\n data = [[1, \"\"], [-2, [\"foo\", 3.0]], -4, \"bar\"]\n\n.. code::\n\n >>> for s in pick(data, predicate=is_non_empty_string):\n ... print(s)\n ...\n foo\n bar\n\n\nHandling dictionary keys\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen traversing mappings like dictionaries, you can configure\nwhether or not ``pick`` will examine dictionary keys by specifying\nthe ``dict_keys`` keyword argument. Default is False, which means\nonly dictionary values are examined. For example:\n\n.. code-block:: python\n\n from handpick import pick\n\n data = {\"foo\": {\"name\": \"foo\"}, \"bar\": {\"name\": \"bar\"}}\n\n.. code::\n\n >>> for s in pick(data, predicate=lambda obj: \"a\" in obj):\n ... print(s)\n ...\n bar\n >>> for s in pick(data, predicate=lambda obj: \"a\" in obj, dict_keys=True):\n ... print(s)\n ...\n name\n bar\n name\n bar\n\n\nPredicates\n----------\n\n\nThe ``Predicate`` decorator\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe `Predicate`_ decorator wraps a function in an object that can be\ncombined with other predicates using the operators ``&`` (and) and\n``|`` (or), as well as negated using the operator ``~`` (not).\n\n\nCombining predicates\n~~~~~~~~~~~~~~~~~~~~\n\nFor example:\n\n.. code-block:: python\n\n from handpick import pick, Predicate\n\n @Predicate\n def is_integer(obj):\n return isinstance(obj, int)\n\n @Predicate\n def is_even(number):\n return number % 2 == 0\n\n data = [[4, [5.0, 1], 3.0], [[15, []], {17: [7, [8], 0]}]]\n\n # compound predicate\n odd_int = is_integer & ~is_even\n\n.. code::\n\n >>> for n in pick(data, predicate=odd_int):\n ... print(n)\n ...\n 1\n 15\n 7\n\n\nCombining predicates with functions\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn addition, the ``&`` and ``|`` operations are supported between\npredicates and regular undecorated functions. For example:\n\n.. code-block:: python\n\n from handpick import pick, Predicate\n\n @Predicate\n def is_list(obj):\n return isinstance(obj, list)\n\n data = [(\"1\", [2]), {(\"x\",): [(3, [4]), \"5\"]}, [\"x\", [\"6\"]], {7: (\"x\",)}]\n\n # compound predicate\n short_list = (lambda obj: len(obj) < 2) & is_list\n\n.. code::\n\n >>> for l in pick(data, predicate=short_list):\n ... print(l)\n ...\n [2]\n [4]\n ['6']\n\n\nSuppressing errors\n~~~~~~~~~~~~~~~~~~\n\nThe important thing to note is that when the predicate's underlying\nfunction raises an exception, the exception is suppressed and the predicate\nreturns False. In other words, it is assumed that the object in question does\nnot meet the picking criteria. For example:\n\n.. code-block:: python\n\n from handpick import pick, Predicate\n\n @Predicate\n def above_zero(number):\n return number > 0\n\n.. code::\n\n >>> above_zero(1)\n True\n >>> above_zero(\"a\")\n False\n >>> for n in pick([[1, \"Py\", -2], [None, 3.0]], predicate=above_zero):\n ... print(n)\n ...\n 1\n 3.0\n\nIn the example above, several lists and strings were internally compared to ``0``\nbut no ``TypeError`` propagated up to the code that called ``above_zero``.\n\n\nPredicate factories\n~~~~~~~~~~~~~~~~~~~\n\nThe `is_type`_ function can be used to create predicates based on\nan object's type. For example:\n\n.. code-block:: python\n\n from handpick import pick, is_type\n\n data = [[1.0, [2, True]], [False, [3]], [\"4\"]]\n\n strictly_int = is_type(int) & ~is_type(bool)\n\n.. code::\n\n >>> for n in pick(data, predicate=strictly_int):\n ... print(n)\n ...\n 2\n 3\n\n\nThe `no_error`_ function can be used to create predicates based on\nwhether a function applied to an object raises an error.\n\n.. code-block:: python\n\n from handpick import pick, is_type, no_error\n\n data = {\"name\": \"spam\", \"price\": \"15.42\", \"quantity\": 68, \"year\": \"2011\"}\n\n # strings that can be cast to floats\n numeric_str = is_type(str) & no_error(float)\n\n.. code::\n\n >>> for s in pick(data, predicate=numeric_str):\n ... print(s)\n ...\n 15.42\n 2011\n\n\nUseful functions\n----------------\n\n\nThe ``values_for_key`` function\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen inspecting data structures that contain dictionaries or other\nmappings, you can use `values_for_key`_ to retrieve values associated with\na specific key, regardless of the nested depth in which these values\nare stored. Values are retrieved lazily by an iterator. For example:\n\n.. code-block:: python\n\n from handpick import values_for_key\n\n data = {\n \"node_id\": 4,\n \"child_nodes\": [\n {\n \"node_id\": 8,\n \"child_nodes\": [\n {\n \"node_id\": 16,\n },\n ],\n },\n {\n \"id\": 9,\n },\n ],\n }\n\n.. code::\n\n >>> for i in values_for_key(data, key=\"node_id\"):\n ... print(i)\n ...\n 4\n 8\n 16\n\nMultiple keys may be specified at a time. For example:\n\n.. code::\n\n >>> for i in values_for_key(data, key=[\"node_id\", \"id\"]):\n ... print(i)\n ...\n 4\n 8\n 16\n 9\n\n\nThe ``max_depth`` function\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis function returns the maximum nested depth of a data structure. For\nexample:\n\n.. code::\n\n >>> from handpick import max_depth\n >>> max_depth([0, [1, [2]]])\n 2\n >>> max_depth({0: {1: {2: {3: {4: 4}}}}})\n 4\n\n**Note:** Just like non-empty collections, empty collections constitute\nanother level of nested depth. For example:\n\n.. code::\n\n >>> max_depth([0, [1, []]])\n 2\n\n\nRecipes\n=======\n\n\nFlattening nested data\n----------------------\n\nTo flatten a list of lists, use the `pick`_ function without\nthe ``predicate`` argument and pass ``collections=False``. For example:\n\n.. code-block:: python\n\n from handpick import pick\n\n data = [[], [0], [[[], 1], [2, [3, [4]], []], [5]]]\n\n.. code::\n\n >>> list(pick(data, collections=False))\n [0, 1, 2, 3, 4, 5]\n\n\nAPI reference\n=============\n\npick\n----\n\n*handpick.pick(data, predicate=None, *, collections=True, dict_keys=False, bytes_like=False)*\n\nPick objects from ``data`` based on ``predicate``.\n\nTraverse ``data`` recursively and yield all objects for which\n``predicate(obj)`` is True or truthy. ``data`` should be an iterable\ncollection.\n\n``predicate`` must be callable, must take one argument, and should\nreturn a Boolean value. If ``predicate`` is omitted or None, all objects\nare picked.\n\nBy default, collections of other objects are yielded just like any\nother objects. To exclude collections, pass ``collections=False``.\n\nWhen traversing a mapping, only its values are inspected by default.\nTo inspect both keys and values of mappings, pass ``dict_keys=True``.\n\nBy default, bytes-like sequences (bytes and bytearrays) are not\ntreated as collections of other objects and therefore not iterated\nby the recursive algorithm. This can be changed by passing\n``bytes_like=True``.\n\nStrings are not treated as collections of other objects and\ntherefore not iterated by the recursive algorithm.\n\nPredicate\n---------\n\n*@handpick.Predicate(func=None, *, suppressed_errors=(TypeError, ValueError, LookupError, AttributeError))*\n\nDecorator wrapping a function in a predicate object.\n\nThe decorated function can be combined with other predicates using\nthe operators ``&`` (and) and ``|`` (or), as well as negated using the\noperator ``~`` (not).\n\n``suppressed_errors`` can be used to customize which exception classes\nwill be suppressed by the predicate.\n\nPredicate objects are intended to be used as the ``predicate``\nargument to the ``pick`` function.\n\nis_type\n-------\n\n*handpick.is_type(type_or_types)*\n\nPredicate factory. Return a predicate that returns True if\nobject is an instance of specified type(s).\n\n``type_or_types`` must be a type or tuple of types.\n\nno_error\n--------\n\n*handpick.no_error(func)*\n\nPredicate factory. Return a predicate that returns True if ``func``\ncan be applied on object without an exception being raised,\nFalse otherwise.\n\nvalues_for_key\n--------------\n\n*handpick.values_for_key(data, key)*\n\nPick values associated with a specific key.\n\nTraverse ``data`` recursively and yield a sequence of dictionary\nvalues that are mapped to ``key``. ``key`` may be a list of multiple\nkeys.\n\nmax_depth\n---------\n\n*handpick.max_depth(data)*\n\nReturn maximum nested depth of ``data``.\n\n``data`` should be an iterable collection. Depth is counted from zero,\ni.e. the direct elements of ``data`` are in depth 0.\n\n\n.. |version| image:: https://img.shields.io/pypi/v/handpick\n :target: https://pypi.org/project/handpick\n.. |ukraine| image:: https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg\n :target: https://stand-with-ukraine.pp.ua\n.. |tests| image:: https://github.com/mportesdev/handpick/actions/workflows/tests.yml/badge.svg\n :target: https://github.com/mportesdev/handpick/actions\n.. |coverage| image:: https://img.shields.io/codecov/c/gh/mportesdev/handpick\n :target: https://codecov.io/gh/mportesdev/handpick\n.. |pyversions| image:: https://img.shields.io/pypi/pyversions/handpick\n :target: https://pypi.org/project/handpick\n.. |pre-commit| image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit\n :target: https://github.com/pre-commit/pre-commit\n.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg\n :target: https://github.com/psf/black\n.. |bandit| image:: https://img.shields.io/badge/security-bandit-yellow.svg\n :target: https://github.com/PyCQA/bandit\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Traverse nested data structures.",
"version": "0.16.0",
"project_urls": {
"Homepage": "https://github.com/mportesdev/handpick"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "f0d4dd484e06bbf044765ba86958c2b3c48fb4049c150f8c931a7001f7a2a28f",
"md5": "f912ad32f678852d0ea8cd11d613315f",
"sha256": "54e05420e43edb586f1dbfab89f0746d49040c5846096460a822ccaa31b93852"
},
"downloads": -1,
"filename": "handpick-0.16.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "f912ad32f678852d0ea8cd11d613315f",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8,<4.0",
"size": 7482,
"upload_time": "2023-10-04T09:32:06",
"upload_time_iso_8601": "2023-10-04T09:32:06.740229Z",
"url": "https://files.pythonhosted.org/packages/f0/d4/dd484e06bbf044765ba86958c2b3c48fb4049c150f8c931a7001f7a2a28f/handpick-0.16.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "a1ca4910f43d800bb1ac788efaf9abb22ca1741b2521245ab5ee90207b25b3c6",
"md5": "87997c9e189615009dd019ba5e1f5f62",
"sha256": "e55452d57faed507c1b28a1ac52093489e847d068dff8f48f1c6e3f6828926e4"
},
"downloads": -1,
"filename": "handpick-0.16.0.tar.gz",
"has_sig": false,
"md5_digest": "87997c9e189615009dd019ba5e1f5f62",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8,<4.0",
"size": 6263,
"upload_time": "2023-10-04T09:32:08",
"upload_time_iso_8601": "2023-10-04T09:32:08.389707Z",
"url": "https://files.pythonhosted.org/packages/a1/ca/4910f43d800bb1ac788efaf9abb22ca1741b2521245ab5ee90207b25b3c6/handpick-0.16.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-10-04 09:32:08",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "mportesdev",
"github_project": "handpick",
"travis_ci": false,
"coveralls": true,
"github_actions": true,
"tox": true,
"lcname": "handpick"
}