From 7ddc7185a0ae127bb4127a5930b602268d3c0b42 Mon Sep 17 00:00:00 2001 From: Alexander Ignatev Date: Thu, 21 May 2020 21:31:52 +0300 Subject: [PATCH 1/2] Fix bug with [False] and [0] equal --- jsonschema/_utils.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/jsonschema/_utils.py b/jsonschema/_utils.py index ceb880198..db419502f 100644 --- a/jsonschema/_utils.py +++ b/jsonschema/_utils.py @@ -174,13 +174,17 @@ def equal(one, two): def unbool(element, true=object(), false=object()): """ - A hack to make True and 1 and False and 0 unique for ``uniq``. + A hack to make True and 1, False and 0, [True] and [1], [False] and [0] unique for ``uniq``. """ if element is True: return true elif element is False: return false + elif element == [True] and isinstance(*element, bool): + return [true] + elif element == [False] and isinstance(*element, bool): + return [false] return element @@ -197,9 +201,9 @@ def uniq(container): return len(set(unbool(i) for i in container)) == len(container) except TypeError: try: - sort = sorted(unbool(i) for i in container) - sliced = itertools.islice(sort, 1, None) - for i, j in zip(sort, sliced): + li = [unbool(i) for i in container] + sliced = itertools.islice(li, 1, None) + for i, j in zip(li, sliced): if i == j: return False except (NotImplementedError, TypeError): From 3c5ba95089ff0fe29d58d8b348d6387e42f56af3 Mon Sep 17 00:00:00 2001 From: Alexander Ignatev Date: Fri, 22 May 2020 23:16:17 +0300 Subject: [PATCH 2/2] Fix bug with [False] and [0] equal --- jsonschema/_utils.py | 57 ++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/jsonschema/_utils.py b/jsonschema/_utils.py index db419502f..90aa689f3 100644 --- a/jsonschema/_utils.py +++ b/jsonschema/_utils.py @@ -174,43 +174,58 @@ def equal(one, two): def unbool(element, true=object(), false=object()): """ - A hack to make True and 1, False and 0, [True] and [1], [False] and [0] unique for ``uniq``. + A hack to make True and 1 and False and 0 unique for ``uniq``. """ if element is True: return true elif element is False: return false - elif element == [True] and isinstance(*element, bool): - return [true] - elif element == [False] and isinstance(*element, bool): - return [false] return element +def unbool_nested(element): + """ + Iterative implementation of breadth-first search for nested unbooling + """ + + if not isinstance(element, (list, dict)): + return unbool(element) + + result = [] if isinstance(element, list) else {} + stack = [(element, result)] + while stack: + element, new = stack.pop() + items = enumerate(element) if isinstance(element, list) else element.items() + for key, item in items: + if not isinstance(item, (list, dict)): + x = unbool(item) + else: + sub = [] if isinstance(item, list) else {} + x = sub + stack.append((item, sub)) + + if isinstance(new, list): + new.append(x) + else: + new[key] = x + return result + + def uniq(container): """ Check if all of a container's elements are unique. - Successively tries first to rely that the elements are hashable, then - falls back on them being sortable, and finally falls back on brute - force. + Successively tries first to rely that the elements are hashable. If not, it goes to brute force. """ try: return len(set(unbool(i) for i in container)) == len(container) except TypeError: - try: - li = [unbool(i) for i in container] - sliced = itertools.islice(li, 1, None) - for i, j in zip(li, sliced): - if i == j: - return False - except (NotImplementedError, TypeError): - seen = [] - for e in container: - e = unbool(e) - if e in seen: - return False - seen.append(e) + seen = [] + for element in container: + element = unbool_nested(element) + if element in seen: + return False + seen.append(element) return True