From 5e0bfff9817254e28655eaf7795d3babcb9e697b Mon Sep 17 00:00:00 2001 From: Martin de La Gorce Date: Wed, 14 Aug 2019 14:33:59 +0100 Subject: [PATCH 1/7] treat integers a list indices in dpath.util.new adding possibility to use integer as list indices in newly created lists (https://github.com/akesterson/dpath-python/issues/91) and fixing issue https://github.com/akesterson/dpath-python/issues/92 --- dpath/options.py | 1 + dpath/path.py | 30 +++++++++++++++++++++--------- dpath/util.py | 4 ++-- dpath/version.py | 2 +- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/dpath/options.py b/dpath/options.py index 41f35c4..d6f316f 100644 --- a/dpath/options.py +++ b/dpath/options.py @@ -1 +1,2 @@ ALLOW_EMPTY_STRING_KEYS = False +INTEGERS_AS_LIST_INDICES = False diff --git a/dpath/path.py b/dpath/path.py index 683abe6..fce64ad 100644 --- a/dpath/path.py +++ b/dpath/path.py @@ -8,30 +8,41 @@ import traceback from collections import MutableSequence, MutableMapping -def path_types(obj, path): +def path_types(obj, path, integer_as_list_index=None): """ Given a list of path name elements, return anew list of [name, type] path components, given the reference object. """ + if integer_as_list_index is None: + integer_as_list_index = dpath.options.INTEGERS_AS_LIST_INDICES + result = [] #for elem in path[:-1]: cur = obj - for elem in path[:-1]: + for nelem,elem in enumerate(path[:-1]): if ((issubclass(cur.__class__, MutableMapping) and elem in cur)): result.append([elem, cur[elem].__class__]) cur = cur[elem] - elif (issubclass(cur.__class__, MutableSequence) and int(elem) < len(cur)): + elif (issubclass(cur.__class__, MutableSequence) and int(elem) < len(cur)) and not(cur[elem] is None): elem = int(elem) result.append([elem, cur[elem].__class__]) cur = cur[elem] else: + if integer_as_list_index and (isinstance(path[nelem+1],int) or path[nelem+1].isdigit()) : + result.append([elem, list]) + cur=None + else: result.append([elem, dict]) - try: - try: - result.append([path[-1], cur[path[-1]].__class__]) - except TypeError: - result.append([path[-1], cur[int(path[-1])].__class__]) - except (KeyError, IndexError): + cur=None + if cur is None: result.append([path[-1], path[-1].__class__]) + else: + try: + try: + result.append([path[-1], cur[path[-1]].__class__]) + except TypeError: + result.append([path[-1], cur[int(path[-1])].__class__]) + except (KeyError, IndexError): + result.append([path[-1], path[-1].__class__]) return result def paths_only(path): @@ -173,6 +184,7 @@ def _create_missing_list(obj, elem): idx = int(str(elem[0])) while (len(obj)-1) < idx: obj.append(None) + obj[idx]=elem[1]() def _accessor_dict(obj, elem): return obj[elem[0]] diff --git a/dpath/util.py b/dpath/util.py index 268429e..e7a5abf 100644 --- a/dpath/util.py +++ b/dpath/util.py @@ -32,7 +32,7 @@ def __safe_path__(path, separator): validated.append(strkey) return path -def new(obj, path, value, separator="/"): +def new(obj, path, value, separator="/",integer_as_list_index=None): """ Set the element at the terminus of path to value, and create it if it does not exist (as opposed to 'set' that can only @@ -43,7 +43,7 @@ def new(obj, path, value, separator="/"): keys """ pathlist = __safe_path__(path, separator) - pathobj = dpath.path.path_types(obj, pathlist) + pathobj = dpath.path.path_types(obj, pathlist,integer_as_list_index) return dpath.path.set(obj, pathobj, value, create_missing=True) def delete(obj, glob, separator="/", afilter=None): diff --git a/dpath/version.py b/dpath/version.py index 71c4de8..f7a2759 100644 --- a/dpath/version.py +++ b/dpath/version.py @@ -1,3 +1,3 @@ import os -VERSION="1.4.2" +VERSION="1.4.3" From 4a75c3b57e89ede1f35d0cb93c79c3f2856b2a1e Mon Sep 17 00:00:00 2001 From: Martin de La Gorce Date: Wed, 14 Aug 2019 14:49:12 +0100 Subject: [PATCH 2/7] adding new integeres-as-indices feature in doc and bug fix adding new integeres-as-indices feature in doc and bug fix --- README.rst | 31 +++++++++++++++++++++++++++++++ dpath/path.py | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 182fd8c..bbea3e5 100644 --- a/README.rst +++ b/README.rst @@ -247,6 +247,37 @@ object with None entries in order to make it big enough: Handy! +Even more handy, your can also tell dpath.util.new to interpret integers a list indices so that you don't need to create empty lists. This is very convenient when using nested lists. + +.. code-block:: pycon + >>> x={} + >>> dpath.util.new(x, 'a/2/3', 5, integer_as_list_index = True) + >>> print json.dumps(x, indent=4, sort_keys=True) + { + "a": { + "2": { + "3": 5 + } + } + } + >>> x={} + >>> dpath.util.new(x, '2/3', 5, integer_as_list_index = True) + >>> print json.dumps(x, indent=4, sort_keys=True) + { + "a": [ + null, + null, + [ + null, + null, + null, + 5 + ] + ] + } + +you can setup the default value for integer_as_list_index to True using dpath.options.INTEGERS_AS_LIST_INDICES=True + Example: Merging ================ diff --git a/dpath/path.py b/dpath/path.py index fce64ad..0c7d7e4 100644 --- a/dpath/path.py +++ b/dpath/path.py @@ -31,7 +31,7 @@ def path_types(obj, path, integer_as_list_index=None): result.append([elem, list]) cur=None else: - result.append([elem, dict]) + result.append([elem, dict]) cur=None if cur is None: result.append([path[-1], path[-1].__class__]) From 4a99d869ca94176d6c2e5465dd865d8ffd1da203 Mon Sep 17 00:00:00 2001 From: Martin de La Gorce Date: Wed, 14 Aug 2019 14:57:57 +0100 Subject: [PATCH 3/7] adding test for new integer_as_list_index option adding test for new integer_as_list_index option --- tests/test_util_new.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test_util_new.py b/tests/test_util_new.py index 1afd412..62f23a5 100644 --- a/tests/test_util_new.py +++ b/tests/test_util_new.py @@ -32,6 +32,18 @@ def test_set_new_list(): dpath.util.new(dict, ['a', '1'], 1) assert(dict['a'][1] == 1) assert(dict['a'][0] == None) + +def test_set_new_list_with_integer_as_list_index(): + dict = {} + dpath.util.new(dict, 'a/2/3', 5,integer_as_list_index = True) + assert(len(dict['a']) == 3) + assert(dict['a'][0] == None) + assert(dict['a'][1] == None) + assert(len(dict['a'][2]) == 4) + assert(dict['a'][2][0] == None) + assert(dict['a'][2][1] == None) + assert(dict['a'][2][2] == None) + assert(dict['a'][2][3] == 5) def test_set_new_list_path_with_separator(): # This test kills many birds with one stone, forgive me From 27971942b25c6792d37f5b45086d548d6dc0e851 Mon Sep 17 00:00:00 2001 From: Martin de La Gorce Date: Wed, 14 Aug 2019 15:08:23 +0100 Subject: [PATCH 4/7] replace tabs by spaces replace tabs by spaces --- README.rst | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/README.rst b/README.rst index bbea3e5..f2353f2 100644 --- a/README.rst +++ b/README.rst @@ -250,31 +250,31 @@ Handy! Even more handy, your can also tell dpath.util.new to interpret integers a list indices so that you don't need to create empty lists. This is very convenient when using nested lists. .. code-block:: pycon - >>> x={} - >>> dpath.util.new(x, 'a/2/3', 5, integer_as_list_index = True) + >>> x={} + >>> dpath.util.new(x, 'a/2/3', 5, integer_as_list_index = True) >>> print json.dumps(x, indent=4, sort_keys=True) - { - "a": { - "2": { - "3": 5 - } - } - } - >>> x={} + { + "a": { + "2": { + "3": 5 + } + } + } + >>> x={} >>> dpath.util.new(x, '2/3', 5, integer_as_list_index = True) >>> print json.dumps(x, indent=4, sort_keys=True) - { - "a": [ - null, - null, - [ - null, - null, - null, - 5 - ] - ] - } + { + "a": [ + null, + null, + [ + null, + null, + null, + 5 + ] + ] + } you can setup the default value for integer_as_list_index to True using dpath.options.INTEGERS_AS_LIST_INDICES=True From e5adc2f9b3d06afd89a54e8e418491f7233180c9 Mon Sep 17 00:00:00 2001 From: Martin de La Gorce Date: Wed, 14 Aug 2019 15:09:24 +0100 Subject: [PATCH 5/7] adding empty line adding empty line --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index f2353f2..e5ae230 100644 --- a/README.rst +++ b/README.rst @@ -250,6 +250,7 @@ Handy! Even more handy, your can also tell dpath.util.new to interpret integers a list indices so that you don't need to create empty lists. This is very convenient when using nested lists. .. code-block:: pycon + >>> x={} >>> dpath.util.new(x, 'a/2/3', 5, integer_as_list_index = True) >>> print json.dumps(x, indent=4, sort_keys=True) From d46020dda35f33c7fa584dfb5797c46b68e0fe0b Mon Sep 17 00:00:00 2001 From: Martin de La Gorce Date: Wed, 14 Aug 2019 15:12:57 +0100 Subject: [PATCH 6/7] fix mistakes in readme fix mistakes in readme --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index e5ae230..6e3a0e1 100644 --- a/README.rst +++ b/README.rst @@ -247,12 +247,12 @@ object with None entries in order to make it big enough: Handy! -Even more handy, your can also tell dpath.util.new to interpret integers a list indices so that you don't need to create empty lists. This is very convenient when using nested lists. +Even more handy, you can also tell dpath.util.new to interpret integers as list indices so that new lists are automatically created. This is very convenient to create nested lists. .. code-block:: pycon >>> x={} - >>> dpath.util.new(x, 'a/2/3', 5, integer_as_list_index = True) + >>> dpath.util.new(x, 'a/2/3', 5, integer_as_list_index = False) >>> print json.dumps(x, indent=4, sort_keys=True) { "a": { From f93278b8e9311b3d5cd20a92a9f7efb2c35618da Mon Sep 17 00:00:00 2001 From: Martin de La Gorce Date: Wed, 14 Aug 2019 22:33:30 +0100 Subject: [PATCH 7/7] fix mistake in readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 6e3a0e1..07a4cb2 100644 --- a/README.rst +++ b/README.rst @@ -262,7 +262,7 @@ Even more handy, you can also tell dpath.util.new to interpret integers as list } } >>> x={} - >>> dpath.util.new(x, '2/3', 5, integer_as_list_index = True) + >>> dpath.util.new(x, 'a/2/3', 5, integer_as_list_index = True) >>> print json.dumps(x, indent=4, sort_keys=True) { "a": [