Skip to content
Snippets Groups Projects
Commit a1efc279 authored by Alexandre Detiste's avatar Alexandre Detiste
Browse files

New upstream version 2.2.0

parent 3a0156e1
No related branches found
No related tags found
No related merge requests found
......@@ -30,18 +30,15 @@ jobs:
- name: Check out code
uses: actions/checkout@main
- name: Set up Python 3.11
- name: Set up Python 3.12
uses: actions/setup-python@main
with:
python-version: "3.11"
python-version: "3.12"
- name: Setup flake8 annotations
uses: rbialon/flake8-annotations@v1.1
- name: Lint with flake8
run: |
pip install flake8
flake8 setup.py dpath/ tests/
uses: TrueBrain/actions-flake8@v2.3
with:
path: setup.py dpath/ tests/
# Generate a common hashseed for all tests
generate-hashseed:
......@@ -70,7 +67,7 @@ jobs:
strategy:
matrix:
# Match versions specified in tox.ini
python-version: ['3.8', '3.9', '3.10', '3.11', 'pypy-3.7']
python-version: ['3.8', '3.9', '3.10', '3.11', 'pypy-3.7', '3.12']
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
......
# Needed for pre-3.10 versions
from __future__ import annotations
__all__ = [
"new",
"delete",
......@@ -48,7 +51,7 @@ def _split_path(path: Path, separator: Optional[str] = "/") -> Union[List[PathSe
return split_segments
def new(obj: MutableMapping, path: Path, value, separator="/", creator: Creator = None) -> MutableMapping:
def new(obj: MutableMapping, path: Path, value, separator="/", creator: Creator | None = None) -> MutableMapping:
"""
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
......@@ -68,7 +71,7 @@ def new(obj: MutableMapping, path: Path, value, separator="/", creator: Creator
return segments.set(obj, split_segments, value)
def delete(obj: MutableMapping, glob: Glob, separator="/", afilter: Filter = None) -> int:
def delete(obj: MutableMapping, glob: Glob, separator="/", afilter: Filter | None = None) -> int:
"""
Given a obj, delete all elements that match the glob.
......@@ -127,7 +130,7 @@ def delete(obj: MutableMapping, glob: Glob, separator="/", afilter: Filter = Non
return deleted
def set(obj: MutableMapping, glob: Glob, value, separator="/", afilter: Filter = None) -> int:
def set(obj: MutableMapping, glob: Glob, value, separator="/", afilter: Filter | None = None) -> int:
"""
Given a path glob, set all existing elements in the document
to the given value. Returns the number of elements changed.
......@@ -167,7 +170,7 @@ def get(
If more than one leaf matches the glob, ValueError is raised. If the glob is
not found and a default is not provided, KeyError is raised.
"""
if glob == "/":
if isinstance(glob, str) and glob == "/" or len(glob) == 0:
return obj
globlist = _split_path(glob, separator)
......@@ -193,7 +196,7 @@ def get(
return results[0]
def values(obj: MutableMapping, glob: Glob, separator="/", afilter: Filter = None, dirs=True):
def values(obj: MutableMapping, glob: Glob, separator="/", afilter: Filter | None = None, dirs=True):
"""
Given an object and a path glob, return an array of all values which match
the glob. The arguments to this function are identical to those of search().
......@@ -203,13 +206,13 @@ def values(obj: MutableMapping, glob: Glob, separator="/", afilter: Filter = Non
return [v for p, v in search(obj, glob, yielded, separator, afilter, dirs)]
def search(obj: MutableMapping, glob: Glob, yielded=False, separator="/", afilter: Filter = None, dirs=True):
def search(obj: MutableMapping, glob: Glob, yielded=False, separator="/", afilter: Filter | None = None, dirs=True):
"""
Given a path glob, return a dictionary containing all keys
that matched the given glob.
If 'yielded' is true, then a dictionary will not be returned.
Instead tuples will be yielded in the form of (path, value) for
Instead, tuples will be yielded in the form of (path, value) for
every element in the document that matched the glob.
"""
......@@ -218,7 +221,7 @@ def search(obj: MutableMapping, glob: Glob, yielded=False, separator="/", afilte
def keeper(path, found):
"""
Generalized test for use in both yielded and folded cases.
Returns True if we want this result. Otherwise returns False.
Returns True if we want this result. Otherwise, returns False.
"""
if not dirs and not segments.leaf(found):
return False
......@@ -245,7 +248,13 @@ def search(obj: MutableMapping, glob: Glob, yielded=False, separator="/", afilte
return segments.fold(obj, f, {})
def merge(dst: MutableMapping, src: MutableMapping, separator="/", afilter: Filter = None, flags=MergeType.ADDITIVE):
def merge(
dst: MutableMapping,
src: MutableMapping,
separator="/",
afilter: Filter | None = None,
flags=MergeType.ADDITIVE
):
"""
Merge source into destination. Like dict.update() but performs deep
merging.
......@@ -316,7 +325,7 @@ def merge(dst: MutableMapping, src: MutableMapping, separator="/", afilter: Filt
target = segments.get(dst, current_path)
# If the types don't match, replace it.
if type(found) != type(target) and not are_both_mutable(found, target):
if type(found) is not type(target) and not are_both_mutable(found, target):
segments.set(dst, current_path, found)
continue
......
......@@ -4,7 +4,7 @@ from typing import Sequence, Tuple, Iterator, Any, Union, Optional, MutableMappi
from dpath import options
from dpath.exceptions import InvalidGlob, InvalidKeyName, PathNotFound
from dpath.types import PathSegment, Creator, Hints, Glob, Path, SymmetricInt
from dpath.types import PathSegment, Creator, Hints, Glob, Path, ListIndex
def make_walkable(node) -> Iterator[Tuple[PathSegment, Any]]:
......@@ -22,8 +22,8 @@ def make_walkable(node) -> Iterator[Tuple[PathSegment, Any]]:
except AttributeError:
try:
indices = range(len(node))
# Convert all list indices to object so negative indexes are supported.
indices = map(lambda i: SymmetricInt(i, len(node)), indices)
# Convert all list indices to objects so negative indices are supported.
indices = map(lambda i: ListIndex(i, len(node)), indices)
return zip(indices, node)
except TypeError:
# This can happen in cases where the node isn't leaf(node) == True,
......
......@@ -2,18 +2,18 @@ from enum import IntFlag, auto
from typing import Union, Any, Callable, Sequence, Tuple, List, Optional, MutableMapping
class SymmetricInt(int):
"""Same as a normal int but mimicks the behavior of list indexes (can be compared to a negative number)."""
class ListIndex(int):
"""Same as a normal int but mimics the behavior of list indices (can be compared to a negative number)."""
def __new__(cls, value: int, max_value: int, *args, **kwargs):
if value >= max_value:
def __new__(cls, value: int, list_length: int, *args, **kwargs):
if value >= list_length:
raise TypeError(
f"Tried to initiate a {cls.__name__} with a value ({value}) "
f"greater than the provided max value ({max_value})"
f"greater than the provided max value ({list_length})"
)
obj = super().__new__(cls, value)
obj.max_value = max_value
obj.list_length = list_length
return obj
......@@ -21,13 +21,11 @@ class SymmetricInt(int):
if not isinstance(other, int):
return False
if other >= self.max_value or other <= -self.max_value:
return False
return int(self) == (self.max_value + other) % self.max_value
# Based on how Python sequences handle negative indices as described in footnote (3) of https://docs.python.org/3/library/stdtypes.html#common-sequence-operations
return other == int(self) or self.list_length + other == int(self)
def __repr__(self):
return f"<{self.__class__.__name__} {int(self)}%{self.max_value}>"
return f"<{self.__class__.__name__} {int(self)}/{self.list_length}>"
def __str__(self):
return str(int(self))
......
VERSION = "2.1.6"
VERSION = "2.2.0"
......@@ -49,6 +49,7 @@ if __name__ == "__main__":
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Topic :: Software Development :: Libraries :: Python Modules',
'Typing :: Typed',
],
......
......@@ -2,7 +2,8 @@ import datetime
import decimal
import time
import mock
from unittest import mock
from nose2.tools.such import helper
import dpath
......@@ -17,6 +18,9 @@ def test_util_get_root():
ret = dpath.get(x, '/')
assert ret == x
ret = dpath.get(x, [])
assert ret == x
def test_get_explicit_single():
ehash = {
......
......@@ -7,7 +7,7 @@
ignore = E501,E722
[tox]
envlist = pypy37, py38, py39, py310, py311
envlist = pypy37, py38, py39, py310, py311, py312
[gh-actions]
python =
......@@ -16,10 +16,10 @@ python =
3.9: py39
3.10: py310
3.11: py311
3.12: py312
[testenv]
deps =
hypothesis
mock
nose2
commands = nose2 {posargs}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment