From 3a79ed0dd9f0dba9674f4fe1c870fc58a9a4f4c6 Mon Sep 17 00:00:00 2001 From: moomoohk Date: Mon, 28 Nov 2022 23:03:49 +0200 Subject: [PATCH 1/6] Catch all exceptions in type check --- dpath/segments.py | 4 ++-- dpath/version.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dpath/segments.py b/dpath/segments.py index d87a7b2..ccf10e7 100644 --- a/dpath/segments.py +++ b/dpath/segments.py @@ -309,7 +309,7 @@ def set( ) -> MutableMapping: """ Set the value in obj at the place indicated by segments. If creator is not - None (default __default_creator__), then call the creator function to + None (default _default_creator), then call the creator function to create any missing path components. set(obj, segments, value) -> obj @@ -326,7 +326,7 @@ def set( # Unfortunately, for our use, 'x in thing' for lists checks # values, not keys whereas dicts check keys. current[segment] - except (KeyError, IndexError): + except: if creator is not None: creator(current, segments, i, hints) else: diff --git a/dpath/version.py b/dpath/version.py index 127c148..5b0431e 100644 --- a/dpath/version.py +++ b/dpath/version.py @@ -1 +1 @@ -VERSION = "2.1.0" +VERSION = "2.1.1" From 3e6565be15f956ce2c714ed4e082cd6f61a814ac Mon Sep 17 00:00:00 2001 From: moomoohk Date: Tue, 29 Nov 2022 23:55:05 +0200 Subject: [PATCH 2/6] Cast path segment to int if it's supposed to be an index --- dpath/__init__.py | 14 ++------------ dpath/options.py | 1 - dpath/segments.py | 5 +++++ dpath/types.py | 2 +- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/dpath/__init__.py b/dpath/__init__.py index c717314..c4c5c8f 100644 --- a/dpath/__init__.py +++ b/dpath/__init__.py @@ -21,7 +21,7 @@ ] from collections.abc import MutableMapping, MutableSequence -from typing import Union, List, Any, Callable, Optional +from typing import Union, List, Any, Callable, Optional, Sequence from dpath import segments, options from dpath.exceptions import InvalidKeyName, PathNotFound @@ -30,7 +30,7 @@ _DEFAULT_SENTINEL = object() -def _split_path(path: Path, separator: Optional[str]) -> Union[List[PathSegment], PathSegment]: +def _split_path(path: Path, separator: Optional[str] = "/") -> Union[List[PathSegment], PathSegment]: """ Given a path and separator, return a tuple of segments. If path is already a non-leaf thing, return it. @@ -45,16 +45,6 @@ def _split_path(path: Path, separator: Optional[str]) -> Union[List[PathSegment] else: split_segments = path.lstrip(separator).split(separator) - if options.CONVERT_INT_LIKE_SEGMENTS: - # Attempt to convert integer segments into actual integers. - final = [] - for segment in split_segments: - try: - final.append(int(segment)) - except ValueError: - final.append(segment) - split_segments = final - return split_segments diff --git a/dpath/options.py b/dpath/options.py index 91b4290..41f35c4 100644 --- a/dpath/options.py +++ b/dpath/options.py @@ -1,2 +1 @@ ALLOW_EMPTY_STRING_KEYS = False -CONVERT_INT_LIKE_SEGMENTS = True diff --git a/dpath/segments.py b/dpath/segments.py index ccf10e7..ed368bd 100644 --- a/dpath/segments.py +++ b/dpath/segments.py @@ -320,6 +320,11 @@ def set( # For everything except the last value, walk down the path and # create if creator is set. for (i, segment) in enumerate(segments[:-1]): + + # If segment is non-int but supposed to be a sequence index + if not isinstance(segment, int) and segment.isdigit() and isinstance(current, Sequence): + segment = int(segment) + try: # Optimistically try to get the next value. This makes the # code agnostic to whether current is a list or a dict. diff --git a/dpath/types.py b/dpath/types.py index b876e6a..d54a2e6 100644 --- a/dpath/types.py +++ b/dpath/types.py @@ -42,4 +42,4 @@ def creator( segments: Sequence[PathSegment], i: int, hints: Sequence[Tuple[PathSegment, type]] = () - )""" + ) -> PathSegment""" From 056bf0de2fd06f4e4cafad40955ec5558d36c0f0 Mon Sep 17 00:00:00 2001 From: moomoohk Date: Wed, 30 Nov 2022 00:00:31 +0200 Subject: [PATCH 3/6] Remove redundant import --- dpath/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpath/__init__.py b/dpath/__init__.py index c4c5c8f..9f56e6b 100644 --- a/dpath/__init__.py +++ b/dpath/__init__.py @@ -21,7 +21,7 @@ ] from collections.abc import MutableMapping, MutableSequence -from typing import Union, List, Any, Callable, Optional, Sequence +from typing import Union, List, Any, Callable, Optional from dpath import segments, options from dpath.exceptions import InvalidKeyName, PathNotFound From 5b937f4e19e287ad8868a22cfb70c7a4dd470160 Mon Sep 17 00:00:00 2001 From: moomoohk Date: Wed, 30 Nov 2022 00:12:49 +0200 Subject: [PATCH 4/6] Remove ambiguity of last path segment --- dpath/segments.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/dpath/segments.py b/dpath/segments.py index ed368bd..faa763f 100644 --- a/dpath/segments.py +++ b/dpath/segments.py @@ -322,7 +322,7 @@ def set( for (i, segment) in enumerate(segments[:-1]): # If segment is non-int but supposed to be a sequence index - if not isinstance(segment, int) and segment.isdigit() and isinstance(current, Sequence): + if isinstance(segment, str) and isinstance(current, Sequence) and segment.isdigit(): segment = int(segment) try: @@ -341,10 +341,16 @@ def set( if i != length - 1 and leaf(current): raise PathNotFound(f"Path: {segments}[{i}]") - if isinstance(segments[-1], int): - extend(current, segments[-1]) + last_segment = segments[-1] - current[segments[-1]] = value + # Resolve ambiguity of last segment + if isinstance(last_segment, str) and isinstance(current, Sequence) and last_segment.isdigit(): + last_segment = int(last_segment) + + if isinstance(last_segment, int): + extend(current, last_segment) + + current[last_segment] = value return obj @@ -393,9 +399,11 @@ def view(obj, glob): view(obj, glob) -> obj' """ + def f(obj, pair, result): (segments, value) = pair if match(segments, glob): if not has(result, segments): set(result, segments, deepcopy(value), hints=types(obj, segments)) + return fold(obj, f, type(obj)()) From 023a0341ebb2c5d161175c14abe77dc1f46ab2ee Mon Sep 17 00:00:00 2001 From: moomoohk Date: Wed, 30 Nov 2022 00:17:46 +0200 Subject: [PATCH 5/6] Remove bad documentation --- dpath/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpath/types.py b/dpath/types.py index d54a2e6..b876e6a 100644 --- a/dpath/types.py +++ b/dpath/types.py @@ -42,4 +42,4 @@ def creator( segments: Sequence[PathSegment], i: int, hints: Sequence[Tuple[PathSegment, type]] = () - ) -> PathSegment""" + )""" From 38007dff851e54743095490689fe7546dd23b408 Mon Sep 17 00:00:00 2001 From: moomoohk Date: Wed, 30 Nov 2022 17:07:39 +0200 Subject: [PATCH 6/6] Add test for int ambiguity --- tests/test_new.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_new.py b/tests/test_new.py index 6da31e7..15b21c6 100644 --- a/tests/test_new.py +++ b/tests/test_new.py @@ -42,6 +42,16 @@ def test_set_new_list(): assert dict['a'][0] is None +def test_set_list_with_dict_int_ambiguity(): + d = {"list": [{"root": {"1": {"k": None}}}]} + + dpath.new(d, "list/0/root/1/k", "new") + + expected = {"list": [{"root": {"1": {"k": "new"}}}]} + + assert d == expected + + def test_set_new_list_path_with_separator(): # This test kills many birds with one stone, forgive me dict = {