Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# DeepDiff v 4.0.4
# DeepDiff v 4.0.5

<!-- ![Downloads](https://img.shields.io/pypi/dm/deepdiff.svg?style=flat) -->
![Python Versions](https://img.shields.io/pypi/pyversions/deepdiff.svg?style=flat)
Expand Down
2 changes: 1 addition & 1 deletion deepdiff/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""This module offers the DeepDiff, DeepSearch, grep and DeepHash classes."""
# flake8: noqa
__version__ = '4.0.4'
__version__ = '4.0.5'
import logging

if __name__ == '__main__':
Expand Down
7 changes: 4 additions & 3 deletions deepdiff/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@
from deepdiff.helper import strings, numbers


DEFAULT_SIGNIFICANT_DIGITS_WHEN_IGNORE_NUMERIC_TYPES = 55
DEFAULT_SIGNIFICANT_DIGITS_WHEN_IGNORE_NUMERIC_TYPES = 12


class Base:
numbers = numbers
strings = strings

def get_significant_digits(self, significant_digits, ignore_numeric_type_changes):
if ignore_numeric_type_changes and not significant_digits:
significant_digits = DEFAULT_SIGNIFICANT_DIGITS_WHEN_IGNORE_NUMERIC_TYPES
if significant_digits is not None and significant_digits < 0:
raise ValueError(
"significant_digits must be None or a non-negative integer")
if significant_digits is None:
if ignore_numeric_type_changes:
significant_digits = DEFAULT_SIGNIFICANT_DIGITS_WHEN_IGNORE_NUMERIC_TYPES
return significant_digits

def get_ignore_types_in_groups(self, ignore_type_in_groups,
Expand Down
51 changes: 19 additions & 32 deletions deepdiff/deephash.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
from collections import Iterable
from collections import MutableMapping
from collections.abc import Iterable, MutableMapping
from collections import defaultdict
from decimal import Decimal
from hashlib import sha1, sha256

from deepdiff.helper import (strings, numbers, unprocessed, not_hashed, add_to_frozen_set,
convert_item_or_items_into_set_else_none, get_doc,
convert_item_or_items_into_compiled_regexes_else_none,
get_id, type_is_subclass_of_type_group, type_in_type_group)
get_id, type_is_subclass_of_type_group, type_in_type_group,
number_to_string, KEY_TO_VAL_STR)
from deepdiff.base import Base
logger = logging.getLogger(__name__)

try:
import mmh3
except ImportError:
mmh3 = False
except ImportError: # pragma: no cover
mmh3 = False # pragma: no cover

UNPROCESSED = 'unprocessed'
MURMUR_SEED = 1203
Expand All @@ -27,10 +26,6 @@

INDEX_VS_ATTRIBUTE = ('[%s]', '.%s')

KEY_TO_VAL_STR = "{}:{}"

ZERO_DECIMAL_CHARACTERS = set("-0.")


def prepare_string_for_hashing(obj, ignore_string_type_changes=False, ignore_string_case=False):
"""
Expand Down Expand Up @@ -62,20 +57,23 @@ def __init__(self,
hasher=None,
ignore_repetition=True,
significant_digits=None,
number_format_notation="f",
apply_hash=True,
ignore_type_in_groups=None,
ignore_string_type_changes=False,
ignore_numeric_type_changes=False,
ignore_type_subclasses=False,
ignore_string_case=False,
number_to_string_func=None,
**kwargs):
if kwargs:
raise ValueError(
("The following parameter(s) are not valid: %s\n"
"The valid parameters are obj, hashes, exclude_types,"
"exclude_paths, exclude_regex_paths, hasher, ignore_repetition,"
"significant_digits, apply_hash, ignore_type_in_groups, ignore_string_type_changes,"
"ignore_numeric_type_changes, ignore_type_subclasses, ignore_string_case") % ', '.join(kwargs.keys()))
"The valid parameters are obj, hashes, exclude_types, significant_digits, "
"exclude_paths, exclude_regex_paths, hasher, ignore_repetition, "
"number_format_notation, apply_hash, ignore_type_in_groups, ignore_string_type_changes, "
"ignore_numeric_type_changes, ignore_type_subclasses, ignore_string_case "
"number_to_string_func") % ', '.join(kwargs.keys()))
self.obj = obj
exclude_types = set() if exclude_types is None else set(exclude_types)
self.exclude_types_tuple = tuple(exclude_types) # we need tuple for checking isinstance
Expand All @@ -89,6 +87,7 @@ def __init__(self,
self[UNPROCESSED] = []

self.significant_digits = self.get_significant_digits(significant_digits, ignore_numeric_type_changes)
self.number_format_notation = number_format_notation
self.ignore_type_in_groups = self.get_ignore_types_in_groups(
ignore_type_in_groups=ignore_type_in_groups,
ignore_string_type_changes=ignore_string_type_changes,
Expand All @@ -102,6 +101,7 @@ def __init__(self,
# testing the individual hash functions for different types of objects.
self.apply_hash = apply_hash
self.type_check_func = type_is_subclass_of_type_group if ignore_type_subclasses else type_in_type_group
self.number_to_string = number_to_string_func or number_to_string

self._hash(obj, parent="root", parents_ids=frozenset({get_id(obj)}))

Expand Down Expand Up @@ -143,7 +143,6 @@ def murmur3_128bit(obj):
return mmh3.hash128(obj, MURMUR_SEED)

def __getitem__(self, obj):
# changed_to_id = False
key = obj
result = None

Expand Down Expand Up @@ -230,9 +229,6 @@ def _prep_dict(self, obj, parent, parents_ids=EMPTY_FROZENSET, print_as_attribut
type_str = 'dict'
return "%s:{%s}" % (type_str, result)

def _prep_set(self, obj, parent, parents_ids=EMPTY_FROZENSET):
return "set:{}".format(self._prep_iterable(obj=obj, parent=parent, parents_ids=parents_ids))

def _prep_iterable(self, obj, parent, parents_ids=EMPTY_FROZENSET):

result = defaultdict(int)
Expand Down Expand Up @@ -264,17 +260,11 @@ def _prep_iterable(self, obj, parent, parents_ids=EMPTY_FROZENSET):
return result

def _prep_number(self, obj):
if self.significant_digits is not None and (
self.ignore_numeric_type_changes or isinstance(obj, (float, complex, Decimal))):
obj_s = ("{:.%sf}" % self.significant_digits).format(obj)

# Special case for 0: "-0.00" should compare equal to "0.00"
if set(obj_s) <= ZERO_DECIMAL_CHARACTERS:
obj_s = "0.00"
result = "number:{}".format(obj_s)
else:
result = KEY_TO_VAL_STR.format(type(obj).__name__, obj)
return result
type_ = "number" if self.ignore_numeric_type_changes else obj.__class__.__name__
if self.significant_digits is not None:
obj = self.number_to_string(obj, significant_digits=self.significant_digits,
number_format_notation=self.number_format_notation)
return KEY_TO_VAL_STR.format(type_, obj)

def _prep_tuple(self, obj, parent, parents_ids):
# Checking to see if it has _fields. Which probably means it is a named
Expand Down Expand Up @@ -321,9 +311,6 @@ def _hash(self, obj, parent, parents_ids=EMPTY_FROZENSET):
elif isinstance(obj, tuple):
result = self._prep_tuple(obj=obj, parent=parent, parents_ids=parents_ids)

elif isinstance(obj, (set, frozenset)):
result = self._prep_set(obj=obj, parent=parent, parents_ids=parents_ids)

elif isinstance(obj, Iterable):
result = self._prep_iterable(obj=obj, parent=parent, parents_ids=parents_ids)

Expand Down
Loading