about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/deepdiff/path.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/deepdiff/path.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/deepdiff/path.py')
-rw-r--r--.venv/lib/python3.12/site-packages/deepdiff/path.py316
1 files changed, 316 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/deepdiff/path.py b/.venv/lib/python3.12/site-packages/deepdiff/path.py
new file mode 100644
index 00000000..ee63b5b9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/deepdiff/path.py
@@ -0,0 +1,316 @@
+import logging
+from ast import literal_eval
+from functools import lru_cache
+
+logger = logging.getLogger(__name__)
+
+GETATTR = 'GETATTR'
+GET = 'GET'
+
+
+class PathExtractionError(ValueError):
+    pass
+
+
+class RootCanNotBeModified(ValueError):
+    pass
+
+
+def _add_to_elements(elements, elem, inside):
+    # Ignore private items
+    if not elem:
+        return
+    if not elem.startswith('__'):
+        remove_quotes = False
+        if '𝆺𝅥𝅯' in elem or '\\' in elem:
+            remove_quotes = True
+        else:
+            try:
+                elem = literal_eval(elem)
+                remove_quotes = False
+            except (ValueError, SyntaxError):
+                remove_quotes = True
+        if remove_quotes and elem[0] == elem[-1] and elem[0] in {'"', "'"}:
+            elem = elem[1: -1]
+        action = GETATTR if inside == '.' else GET
+        elements.append((elem, action))
+
+
+DEFAULT_FIRST_ELEMENT = ('root', GETATTR)
+
+
+@lru_cache(maxsize=1024 * 128)
+def _path_to_elements(path, root_element=DEFAULT_FIRST_ELEMENT):
+    """
+    Given a path, it extracts the elements that form the path and their relevant most likely retrieval action.
+
+        >>> from deepdiff import _path_to_elements
+        >>> path = "root[4.3].b['a3']"
+        >>> _path_to_elements(path, root_element=None)
+        [(4.3, 'GET'), ('b', 'GETATTR'), ('a3', 'GET')]
+    """
+    if isinstance(path, (tuple, list)):
+        return path
+    elements = []
+    if root_element:
+        elements.append(root_element)
+    elem = ''
+    inside = False
+    prev_char = None
+    path = path[4:]  # removing "root from the beginning"
+    brackets = []
+    inside_quotes = False
+    quote_used = ''
+    for char in path:
+        if prev_char == '𝆺𝅥𝅯':
+            elem += char
+        elif char in {'"', "'"}:
+            elem += char
+            # If we are inside and the quote is not what we expected, the quote is not closing
+            if not(inside_quotes and quote_used != char):
+                inside_quotes = not inside_quotes
+                if inside_quotes:
+                    quote_used = char
+                else:
+                    _add_to_elements(elements, elem, inside)
+                    elem = ''
+                    quote_used = ''
+        elif inside_quotes:
+            elem += char
+        elif char == '[':
+            if inside == '.':
+                _add_to_elements(elements, elem, inside)
+                inside = '['
+                elem = ''
+            # we are already inside. The bracket is a part of the word.
+            elif inside == '[':
+                elem += char
+            else:
+                inside = '['
+                brackets.append('[')
+                elem = ''
+        elif char == '.':
+            if inside == '[':
+                elem += char
+            elif inside == '.':
+                _add_to_elements(elements, elem, inside)
+                elem = ''
+            else:
+                inside = '.'
+                elem = ''
+        elif char == ']':
+            if brackets and brackets[-1] == '[':
+                brackets.pop()
+            if brackets:
+                elem += char
+            else:
+                _add_to_elements(elements, elem, inside)
+                elem = ''
+                inside = False
+        else:
+            elem += char
+        prev_char = char
+    if elem:
+        _add_to_elements(elements, elem, inside)
+    return tuple(elements)
+
+
+def _get_nested_obj(obj, elements, next_element=None):
+    for (elem, action) in elements:
+        if action == GET:
+            obj = obj[elem]
+        elif action == GETATTR:
+            obj = getattr(obj, elem)
+    return obj
+
+
+def _guess_type(elements, elem, index, next_element):
+    # If we are not at the last elements
+    if index < len(elements) - 1:
+        # We assume it is a nested dictionary not a nested list
+        return {}
+    if isinstance(next_element, int):
+        return []
+    return {}
+
+
+def _get_nested_obj_and_force(obj, elements, next_element=None):
+    prev_elem = None
+    prev_action = None
+    prev_obj = obj
+    for index, (elem, action) in enumerate(elements):
+        _prev_obj = obj
+        if action == GET:
+            try:
+                obj = obj[elem]
+                prev_obj = _prev_obj
+            except KeyError:
+                obj[elem] = _guess_type(elements, elem, index, next_element)
+                obj = obj[elem]
+                prev_obj = _prev_obj
+            except IndexError:
+                if isinstance(obj, list) and isinstance(elem, int) and elem >= len(obj):
+                    obj.extend([None] * (elem - len(obj)))
+                    obj.append(_guess_type(elements, elem, index), next_element)
+                    obj = obj[-1]
+                    prev_obj = _prev_obj
+                elif isinstance(obj, list) and len(obj) == 0 and prev_elem:
+                    # We ran into an empty list that should have been a dictionary
+                    # We need to change it from an empty list to a dictionary
+                    obj = {elem: _guess_type(elements, elem, index, next_element)}
+                    if prev_action == GET:
+                        prev_obj[prev_elem] = obj
+                    else:
+                        setattr(prev_obj, prev_elem, obj)
+                    obj = obj[elem]
+        elif action == GETATTR:
+            obj = getattr(obj, elem)
+            prev_obj = _prev_obj
+        prev_elem = elem
+        prev_action = action
+    return obj
+
+
+def extract(obj, path):
+    """
+    Get the item from obj based on path.
+
+    Example:
+
+        >>> from deepdiff import extract
+        >>> obj = {1: [{'2': 'b'}, 3], 2: [4, 5]}
+        >>> path = "root[1][0]['2']"
+        >>> extract(obj, path)
+        'b'
+
+    Note that you can use extract in conjunction with DeepDiff results
+    or even with the search and :ref:`deepsearch_label` modules. For example:
+
+        >>> from deepdiff import grep
+        >>> obj = {1: [{'2': 'b'}, 3], 2: [4, 5]}
+        >>> result = obj | grep(5)
+        >>> result
+        {'matched_values': ['root[2][1]']}
+        >>> result['matched_values'][0]
+        'root[2][1]'
+        >>> path = result['matched_values'][0]
+        >>> extract(obj, path)
+        5
+
+
+    .. note::
+        Note that even if DeepDiff tried gives you a path to an item in a set,
+        there is no such thing in Python and hence you will get an error trying
+        to extract that item from a set.
+        If you want to be able to get items from sets, use the SetOrdered module
+        to generate the sets.
+        In fact Deepdiff uses SetOrdered as a dependency.
+
+        >>> from deepdiff import grep, extract
+        >>> obj = {"a", "b"}
+        >>> obj | grep("b")
+        Set item detected in the path.'set' objects do NOT support indexing. But DeepSearch will still report a path.
+        {'matched_values': SetOrdered(['root[0]'])}
+        >>> extract(obj, 'root[0]')
+        Traceback (most recent call last):
+          File "<stdin>", line 1, in <module>
+          File "deepdiff/deepdiff/path.py", line 126, in extract
+            return _get_nested_obj(obj, elements)
+          File "deepdiff/deepdiff/path.py", line 84, in _get_nested_obj
+            obj = obj[elem]
+        TypeError: 'set' object is not subscriptable
+        >>> from orderly_set import SetOrdered
+        >>> obj = SetOrdered(["a", "b"])
+        >>> extract(obj, 'root[0]')
+        'a'
+
+    """
+    elements = _path_to_elements(path, root_element=None)
+    return _get_nested_obj(obj, elements)
+
+
+def parse_path(path, root_element=DEFAULT_FIRST_ELEMENT, include_actions=False):
+    """
+    Parse a path to a format that is machine readable
+
+    **Parameters**
+
+    path : A string
+    The path string such as "root[1][2]['age']"
+
+    root_element: string, default='root'
+        What the root is called in the path.
+
+    include_actions: boolean, default=False
+        If True, we return the action required to retrieve the item at each element of the path.  
+
+    **Examples**
+
+        >>> from deepdiff import parse_path
+        >>> parse_path("root[1][2]['age']")
+        [1, 2, 'age']
+        >>> parse_path("root[1][2]['age']", include_actions=True)
+        [{'element': 1, 'action': 'GET'}, {'element': 2, 'action': 'GET'}, {'element': 'age', 'action': 'GET'}]
+        >>>
+        >>> parse_path("root['joe'].age")
+        ['joe', 'age']
+        >>> parse_path("root['joe'].age", include_actions=True)
+        [{'element': 'joe', 'action': 'GET'}, {'element': 'age', 'action': 'GETATTR'}]
+
+    """
+
+    result = _path_to_elements(path, root_element=root_element)
+    result = iter(result)
+    if root_element:
+        next(result)  # We don't want the root item
+    if include_actions is False:
+        return [i[0] for i in result]
+    return [{'element': i[0], 'action': i[1]} for i in result]
+
+
+def stringify_element(param, quote_str=None):
+    has_quote = "'" in param
+    has_double_quote = '"' in param
+    if has_quote and has_double_quote and not quote_str:
+        new_param = []
+        for char in param:
+            if char in {'"', "'"}:
+                new_param.append('𝆺𝅥𝅯')
+            new_param.append(char)
+        result = '"' + ''.join(new_param) + '"'
+    elif has_quote:
+        result = f'"{param}"'
+    elif has_double_quote:
+        result = f"'{param}'"
+    else:
+        result = param if quote_str is None else quote_str.format(param)
+    return result
+
+
+def stringify_path(path, root_element=DEFAULT_FIRST_ELEMENT, quote_str="'{}'"):
+    """
+    Gets the path as an string.
+
+    For example [1, 2, 'age'] should become
+    root[1][2]['age']
+    """
+    if not path:
+        return root_element[0]
+    result = [root_element[0]]
+    has_actions = False
+    try:
+        if path[0][1] in {GET, GETATTR}:
+            has_actions = True
+    except (KeyError, IndexError, TypeError):
+        pass
+    if not has_actions:
+        path = [(i, GET) for i in path]
+        path[0] = (path[0][0], root_element[1])  # The action for the first element might be a GET or GETATTR. We update the action based on the root_element.
+    for element, action in path:
+        if isinstance(element, str) and action == GET:
+            element = stringify_element(element, quote_str)
+        if action == GET:
+            result.append(f"[{element}]")
+        else:
+            result.append(f".{element}")
+    return ''.join(result)