Пример #1
0
def diff(*args, **kwargs):
    """
    Deep Diff Commandline

    Deep Difference of content in files.
    It can read csv, tsv, json, yaml, and toml files.

    T1 and T2 are the path to the files to be compared with each other.
    """
    debug = kwargs.pop('debug')
    kwargs['ignore_private_variables'] = not kwargs.pop(
        'include_private_variables')
    kwargs['progress_logger'] = logger.info if kwargs[
        'progress_logger'] == 'info' else logger.error
    create_patch = kwargs.pop('create_patch')
    t1_path = kwargs.pop("t1")
    t2_path = kwargs.pop("t2")
    t1_extension = t1_path.split('.')[-1]
    t2_extension = t2_path.split('.')[-1]

    for name, t_path, t_extension in [('t1', t1_path, t1_extension),
                                      ('t2', t2_path, t2_extension)]:
        try:
            kwargs[name] = load_path_content(t_path, file_type=t_extension)
        except Exception as e:  # pragma: no cover.
            if debug:  # pragma: no cover.
                raise  # pragma: no cover.
            else:  # pragma: no cover.
                sys.exit(str(
                    f"Error when loading {name}: {e}"))  # pragma: no cover.

    # if (t1_extension != t2_extension):
    if t1_extension in {'csv', 'tsv'}:
        kwargs['t1'] = [dict(i) for i in kwargs['t1']]
    if t2_extension in {'csv', 'tsv'}:
        kwargs['t2'] = [dict(i) for i in kwargs['t2']]

    if create_patch:
        # Disabling logging progress since it will leak into stdout
        kwargs['log_frequency_in_sec'] = 0

    try:
        diff = DeepDiff(**kwargs)
    except Exception as e:  # pragma: no cover.  No need to test this.
        sys.exit(str(e))  # pragma: no cover.  No need to test this.

    if create_patch:
        try:
            delta = Delta(diff)
        except Exception as e:  # pragma: no cover.
            if debug:  # pragma: no cover.
                raise  # pragma: no cover.
            else:  # pragma: no cover.
                sys.exit(f"Error when loading the patch (aka delta): {e}"
                         )  # pragma: no cover.

        # printing into stdout
        sys.stdout.buffer.write(delta.dumps())
    else:
        pprint(diff, indent=2)
Пример #2
0
    def test_repeated_timer(self, MockDiffLevel):
        t1 = [1, [2]]
        t2 = [1, [3]]

        progress_logger = mock.Mock()
        DeepDiff(t1, t2, log_frequency_in_sec=0.02, progress_logger=progress_logger)
        assert PROGRESS_MSG.format(0, 0, 0) == progress_logger.call_args[0][0]
Пример #3
0
    def test_truncate_datetime(self):
        d1 = {'a': datetime.datetime(2020, 5, 17, 22, 15, 34, 913070)}
        d2 = {'a': datetime.datetime(2020, 5, 17, 22, 15, 39, 296583)}
        res = DeepDiff(d1, d2, truncate_datetime='minute')
        assert res == {}

        res = DeepDiff(d1, d2, truncate_datetime='second')
        expected = datetime.datetime(2020, 5, 17, 22, 15, 39, tzinfo=datetime.timezone.utc)
        assert res['values_changed']["root['a']"]['new_value'] == expected

        d1 = {'a': datetime.time(22, 15, 34, 913070)}
        d2 = {'a': datetime.time(22, 15, 39, 296583)}

        res = DeepDiff(d1, d2, truncate_datetime='minute')
        assert res == {}

        res = DeepDiff(d1, d2, truncate_datetime='second')
        assert res['values_changed']["root['a']"]['new_value'] == 80139
Пример #4
0
 def test_bool_str(self):
     t1 = {'key1': True}
     t2 = {'key1': 'Yes'}
     diff = DeepDiff(t1, t2, ignore_type_in_groups=[(bool, str)],
                     ignore_numeric_type_changes=True)
     expected = {'values_changed':
                     {"root['key1']":
                          {'new_value': 'Yes', 'old_value': True}
                     }
                 }
     assert diff == expected
Пример #5
0
    def write_migration(schema, counter, path, previous, current):
        filename = "%s_%04i.json" % (schema, counter)
        migration = DeepDiff(previous, current, verbose_level=2).json
        if migration == "{}":
            hfoslog('Nothing changed - no new migration data.', lvl=warn)
            return

        print('Writing migration: ', os.path.join(path, filename))
        pprint(migration)

        with open(os.path.join(path, filename), 'w') as f:
            f.write(migration)
Пример #6
0
def test_dataobject_generation(source):
    ### DataObjects cannot be created itself
    with pytest.raises(NotImplementedError):
        DataObject("nosource")

    obj = MinimalObject(source)

    assert hasattr(obj, "matrix")
    assert hasattr(obj, "string")
    assert hasattr(obj, "some_meta")
    assert not hasattr(obj, "bogus")

    assert (obj.matrix == source).all()
    assert obj.string is "TestMeta"
    assert obj.some_meta is "nothing"
    assert (obj.source == source).all()

    assert not DeepDiff(obj.meta, {
        "string": "TestMeta",
        "some_meta": "nothing"
    },
                        ignore_order=True)
    assert not DeepDiff(obj.data, {"matrix": np.array(source)})
Пример #7
0
    def write_migration(schema, counter, path, previous, current):
        """Write out complete migration data"""

        filename = "%s_%04i.json" % (schema, counter)
        migration = DeepDiff(previous, current,
                             verbose_level=2).to_json_pickle()
        if migration == "{}":
            log("Nothing changed - no new migration data.", lvl=warn)
            return

        log("Writing migration: ", os.path.join(path, filename))
        log(migration, pretty=True)

        with open(os.path.join(path, filename), "w") as f:
            f.write(migration)
Пример #8
0
    def test_cache_purge_level_max(self):
        diff = DeepDiff([1], [2], cache_purge_level=1)
        assert len(diff.__dict__.keys()) > 10
        diff2 = DeepDiff([1], [2], cache_purge_level=2)
        assert not diff2.__dict__
        expected = {'values_changed': {'root[0]': {'new_value': 2, 'old_value': 1}}}
        assert expected == diff2

        diff2 = DeepDiff([1], [2], cache_purge_level=2, view='tree')
        assert not diff2.__dict__
        assert list(diff2.keys()) == ['values_changed']
Пример #9
0
    def process_analysis(self):
        logger.debug("Analyzing rule.yml file %s", self.filepath)
        logger.debug("Rule name: %s", self.rule_name)

        if self.is_added():
            msg = "Rule %s added." % self.rule_name
            self.diff_struct.add_changed_product_by_rule(self.rule_name,
                                                         msg=msg)
            self.diff_struct.add_changed_rule(self.rule_name, msg=msg)
            return self.diff_struct
        elif self.is_removed():
            msg = "Rule %s was deleted" % self.rule_name
            self.diff_struct.add_rule_log(self.rule_name, msg)
            return self.diff_struct

        before_start = re.search(r"^\s*template:", self.content_before,
                                 re.MULTILINE)
        after_start = re.search(r"^\s*template:", self.content_after,
                                re.MULTILINE)
        # No template section found, no tests selected
        if type(before_start) is type(after_start) and before_start is None:
            return self.diff_struct
        # Template section either added or removed
        elif before_start is None or after_start is None:
            msg = "Template section has been removed/added in %s" % self.rule_name
            self.diff_struct.add_changed_product_by_rule(self.rule_name,
                                                         msg=msg)
            self.diff_struct.add_changed_rule(self.rule_name, msg=msg)
            return self.diff_struct
        # Both sections with template section, do diff
        content_before = self.content_before[before_start.start():]
        content_after = self.content_after[after_start.start():]
        diff = DeepDiff(content_before, content_after)

        if diff:
            msg = "Template section has been changed in %s" % self.rule_name
            self.diff_struct.add_changed_product_by_rule(self.rule_name,
                                                         msg=msg)
            self.diff_struct.add_changed_rule(self.rule_name, msg=msg)

        return self.diff_struct
Пример #10
0
def make_migrations(schema=None):
    """Create migration data for a specified schema"""

    entrypoints = {}
    old = {}

    def _apply_migrations(migrations, new_model):
        """Apply migration data to compile an up to date model"""
        def get_path(raw_path):
            """Get local path of schema definition"""

            log("RAW PATH:", raw_path, type(raw_path))
            path = []
            for item in raw_path.split("["):
                log(item)
                item = item.rstrip("]")
                item = item.replace('"', "")
                item = item.replace("'", "")
                try:
                    item = int(item)
                except ValueError:
                    pass
                path.append(item)
            path.remove("root")
            log("PATH:", path)
            return path

        def apply_entry(changetype, change, result):
            """Upgrade with a single migration"""
            def apply_removes(removes, result):
                """Delete removed fields"""

                for remove in removes:
                    path = get_path(remove)
                    amount = dpath.util.delete(result, path)
                    if amount != 1:
                        log("Not exactly one removed!", path, remove, lvl=warn)
                return result

            def apply_additions(additions, result):
                """Add newly added fields"""

                for addition in additions:
                    path = get_path(addition)
                    entry = additions[addition]
                    log("Adding:", entry, "at", path)
                    dpath.util.new(result, path, entry)
                return result

            if changetype == "type_changes":
                log("Creating new object")
                result = change["root"]["new_value"]
                return result

            if changetype == "dictionary_item_added":
                log("Adding items")
                result = apply_additions(change, result)
            elif changetype == "dictionary_item_removed":
                log("Removing items")
                result = apply_removes(change, result)
            elif changetype == "values_changed":
                log("Changing items' types")
                for item in change:
                    path = get_path(item)
                    log(
                        "Changing",
                        path,
                        "from",
                        change[item]["old_value"],
                        " to",
                        change[item]["new_value"],
                    )
                    if dpath.util.get(result,
                                      path) != change[item]["old_value"]:
                        log("Value change did not work!", lvl=warn)
                    amount = dpath.util.set(result, path,
                                            change[item]["new_value"])
                    if amount != 1:
                        log("Not exactly one changed!", path, item, lvl=warn)

            return result

        def get_renames(migrations):
            """Check migrations for renamed fields"""

            log("Checking for rename operations:")
            # pprint(migrations)
            added = removed = None

            for entry in migrations:
                added = entry.get("dictionary_item_added", None)
                removed = entry.get("dictionary_item_removed", None)

            renames = []

            if added and removed:
                for addition in added:
                    path = get_path(addition)
                    for removal in removed:
                        removed_path = get_path(removal)
                        if path[:-1] == removed_path[:-1]:
                            log("Possible rename detected:", removal, "->",
                                addition)
                            renames.append((removed_path, path))
            return renames

        result = {}
        for no, migration in enumerate(migrations):
            log("Migrating", no)
            log("Migration:", migration, lvl=debug)
            renamed = get_renames(migrations)

            for entry in migration:
                result = apply_entry(entry, migration[entry], result)

        pprint(result)
        return result

    def write_migration(schema, counter, path, previous, current):
        """Write out complete migration data"""

        filename = "%s_%04i.json" % (schema, counter)
        migration = DeepDiff(previous, current,
                             verbose_level=2).to_json_pickle()
        if migration == "{}":
            log("Nothing changed - no new migration data.", lvl=warn)
            return

        log("Writing migration: ", os.path.join(path, filename))
        log(migration, pretty=True)

        with open(os.path.join(path, filename), "w") as f:
            f.write(migration)

    for schema_entrypoint in iter_entry_points(group="isomer.schemata",
                                               name=None):
        try:
            log("Schemata found: ", schema_entrypoint.name, lvl=debug)
            if schema is not None and schema_entrypoint.name != schema:
                continue

            entrypoints[schema_entrypoint.name] = schema_entrypoint
            pprint(schema_entrypoint.dist.location)
            schema_top = schema_entrypoint.dist.location
            schema_migrations = schema_entrypoint.module_name.replace(
                "schemata", "migrations").replace(".", "/")
            path = os.path.join(schema_top, schema_migrations)
            new_model = schema_entrypoint.load()["schema"]

            migrations = []

            try:
                for file in sorted(os.listdir(path)):
                    if not file.endswith(".json"):
                        continue
                    fullpath = os.path.join(path, file)
                    log("Importing migration", fullpath)
                    with open(fullpath, "r") as f:
                        migration = DeepDiff.from_json_pickle(f.read())
                    migrations.append(migration)
                    log("Successfully imported")

                if len(migrations) == 0:
                    raise ImportError
                pprint(migrations)
                model = _apply_migrations(migrations, new_model)
                write_migration(schema,
                                len(migrations) + 1, path, model, new_model)
            except ImportError as e:
                log("No previous migrations for", schema, e, type(e), exc=True)

            if len(migrations) == 0:
                write_migration(schema, 1, path, None, new_model)

        except (ImportError, DistributionNotFound) as e:
            log(
                "Problematic schema: ",
                e,
                type(e),
                schema_entrypoint.name,
                exc=True,
                lvl=warn,
            )

    log("Found schemata: ", sorted(entrypoints.keys()), lvl=debug)

    log("Entrypoints:", entrypoints, pretty=True, lvl=debug)

    def make_single_migration(old, new):
        pass
Пример #11
0
def make_migrations(schema=None):
    entrypoints = {}
    old = {}

    def apply_migrations(migrations, new_model):

        def get_path(raw_path):
            print("RAW PATH:", raw_path, type(raw_path))
            path = []
            for item in raw_path.split("["):
                print(item)
                item = item.rstrip("]")
                item = item.replace('"', '')
                item = item.replace("'", '')
                try:
                    item = int(item)
                except ValueError:
                    pass
                path.append(item)
            path.remove('root')
            print("PATH:", path)
            return path

        def apply_entry(changetype, change, result):

            def apply_removes(removes, result):
                for remove in removes:
                    path = get_path(remove)
                    amount = dpath.util.delete(result, path)
                    assert amount == 1
                return result

            def apply_additions(additions, result):
                for addition in additions:
                    path = get_path(addition)
                    entry = additions[addition]
                    hfoslog('Adding:', entry, 'at', path)
                    dpath.util.new(result, path, entry)
                return result

            if changetype == 'type_changes':
                hfoslog('Creating new object')
                result = change['root']['new_value']
                return result

            if changetype == 'dictionary_item_added':
                hfoslog('Adding items')
                result = apply_additions(change, result)
            elif changetype == 'dictionary_item_removed':
                hfoslog('Removing items')
                result = apply_removes(change, result)
            elif changetype == 'values_changed':
                hfoslog("Changing items' types")
                for item in change:
                    path = get_path(item)
                    hfoslog('Changing', path, 'from',
                            change[item]['old_value'], ' to',
                            change[item]['new_value'])
                    assert dpath.util.get(result, path) == change[item][
                        'old_value']
                    amount = dpath.util.set(result, path, change[item][
                        'new_value'])
                    assert amount == 1

            return result

        def get_renames(migrations):
            hfoslog('Checking for rename operations:')
            pprint(migrations)
            for entry in migrations:

                added = entry.get('dictionary_item_added', None)
                removed = entry.get('dictionary_item_removed', None)

            renames = []

            if added and removed:
                for addition in added:
                    path = get_path(addition)
                    for removal in removed:
                        removed_path = get_path(removal)
                        if path[:-1] == removed_path[:-1]:
                            hfoslog('Possible rename detected:', removal, '->',
                                    addition)
                            renames.append((removed_path, path))
            return renames

        result = {}
        for no, migration in enumerate(migrations):
            hfoslog('Migrating', no)
            hfoslog('Migration:', migration, lvl=debug)
            renamed = get_renames(migrations)

            for entry in migration:
                result = apply_entry(entry, migration[entry], result)

        pprint(result)
        return result

    def write_migration(schema, counter, path, previous, current):
        filename = "%s_%04i.json" % (schema, counter)
        migration = DeepDiff(previous, current, verbose_level=2).json
        if migration == "{}":
            hfoslog('Nothing changed - no new migration data.', lvl=warn)
            return

        print('Writing migration: ', os.path.join(path, filename))
        pprint(migration)

        with open(os.path.join(path, filename), 'w') as f:
            f.write(migration)

    for schema_entrypoint in iter_entry_points(group='hfos.schemata',
                                               name=None):
        try:
            hfoslog("Schemata found: ", schema_entrypoint.name, lvl=debug,
                    emitter='DB')
            if schema is not None and schema_entrypoint.name != schema:
                continue

            entrypoints[schema_entrypoint.name] = schema_entrypoint
            pprint(schema_entrypoint.dist.location)
            schema_top = schema_entrypoint.dist.location
            schema_migrations = schema_entrypoint.module_name.replace(
                'schemata', 'migrations').replace('.', '/')
            path = os.path.join(schema_top, schema_migrations)
            new_model = schema_entrypoint.load()['schema']

            migrations = []

            try:
                for file in sorted(os.listdir(path)):
                    if not file.endswith('.json'):
                        continue
                    fullpath = os.path.join(path, file)
                    hfoslog('Importing migration', fullpath)
                    with open(fullpath, 'r') as f:
                        migration = DeepDiff.from_json(f.read())
                    migrations.append(migration)
                    hfoslog('Successfully imported')

                if len(migrations) == 0:
                    raise ImportError
                pprint(migrations)
                model = apply_migrations(migrations, new_model)
                write_migration(schema, len(migrations) + 1, path, model,
                                new_model)
            except ImportError as e:
                hfoslog('No previous migrations for', schema, e,
                        type(e), exc=True)

            if len(migrations) == 0:
                write_migration(schema, 1, path, None, new_model)

        except (ImportError, DistributionNotFound) as e:
            hfoslog("Problematic schema: ", e, type(e),
                    schema_entrypoint.name, exc=True, lvl=warn,
                    emitter='SCHEMATA')

    hfoslog("Found schemata: ", sorted(entrypoints.keys()), lvl=debug,
            emitter='SCHEMATA')

    pprint(entrypoints)

    def make_single_migration(old, new):
        pass
Пример #12
0
 def test_invalid_cache_purge_level(self):
     with pytest.raises(ValueError) as excinfo:
         DeepDiff(1, 2, cache_purge_level=5)
     assert str(excinfo.value) == PURGE_LEVEL_RANGE_MSG
Пример #13
0
 def test_invalid_verbose_level(self):
     with pytest.raises(ValueError) as excinfo:
         DeepDiff(1, 2, verbose_level=5)
     assert str(excinfo.value) == VERBOSE_LEVEL_RANGE_MSG
Пример #14
0
 def test_invalid_view(self):
     t1 = [1]
     t2 = [2]
     with pytest.raises(ValueError) as excinfo:
         DeepDiff(t1, t2, view='blah')
     assert str(excinfo.value) == INVALID_VIEW_MSG.format('blah')
Пример #15
0
 def test_path_cache(self):
     diff = DeepDiff([1], [2], cache_purge_level=2, view='tree')
     path1 = diff['values_changed'][0].path()
     path2 = diff['values_changed'][0].path()
     assert 'root[0]' == path1 == path2
Пример #16
0
def make_migrations(schema=None):
    entrypoints = {}
    old = {}

    def apply_migrations(migrations, new_model):
        def get_path(raw_path):
            print("RAW PATH:", raw_path, type(raw_path))
            path = []
            for item in raw_path.split("["):
                print(item)
                item = item.rstrip("]")
                item = item.replace('"', '')
                item = item.replace("'", '')
                try:
                    item = int(item)
                except ValueError:
                    pass
                path.append(item)
            path.remove('root')
            print("PATH:", path)
            return path

        def apply_entry(changetype, change, result):
            def apply_removes(removes, result):
                for remove in removes:
                    path = get_path(remove)
                    amount = dpath.util.delete(result, path)
                    assert amount == 1
                return result

            def apply_additions(additions, result):
                for addition in additions:
                    path = get_path(addition)
                    entry = additions[addition]
                    hfoslog('Adding:', entry, 'at', path)
                    dpath.util.new(result, path, entry)
                return result

            if changetype == 'type_changes':
                hfoslog('Creating new object')
                result = change['root']['new_value']
                return result

            if changetype == 'dictionary_item_added':
                hfoslog('Adding items')
                result = apply_additions(change, result)
            elif changetype == 'dictionary_item_removed':
                hfoslog('Removing items')
                result = apply_removes(change, result)
            elif changetype == 'values_changed':
                hfoslog("Changing items' types")
                for item in change:
                    path = get_path(item)
                    hfoslog('Changing', path, 'from',
                            change[item]['old_value'], ' to',
                            change[item]['new_value'])
                    assert dpath.util.get(result,
                                          path) == change[item]['old_value']
                    amount = dpath.util.set(result, path,
                                            change[item]['new_value'])
                    assert amount == 1

            return result

        def get_renames(migrations):
            hfoslog('Checking for rename operations:')
            pprint(migrations)
            for entry in migrations:

                added = entry.get('dictionary_item_added', None)
                removed = entry.get('dictionary_item_removed', None)

            renames = []

            if added and removed:
                for addition in added:
                    path = get_path(addition)
                    for removal in removed:
                        removed_path = get_path(removal)
                        if path[:-1] == removed_path[:-1]:
                            hfoslog('Possible rename detected:', removal, '->',
                                    addition)
                            renames.append((removed_path, path))
            return renames

        result = {}
        for no, migration in enumerate(migrations):
            hfoslog('Migrating', no)
            hfoslog('Migration:', migration, lvl=debug)
            renamed = get_renames(migrations)

            for entry in migration:
                result = apply_entry(entry, migration[entry], result)

        pprint(result)
        return result

    def write_migration(schema, counter, path, previous, current):
        filename = "%s_%04i.json" % (schema, counter)
        migration = DeepDiff(previous, current, verbose_level=2).json
        if migration == "{}":
            hfoslog('Nothing changed - no new migration data.', lvl=warn)
            return

        print('Writing migration: ', os.path.join(path, filename))
        pprint(migration)

        with open(os.path.join(path, filename), 'w') as f:
            f.write(migration)

    for schema_entrypoint in iter_entry_points(group='hfos.schemata',
                                               name=None):
        try:
            hfoslog("Schemata found: ",
                    schema_entrypoint.name,
                    lvl=debug,
                    emitter='DB')
            if schema is not None and schema_entrypoint.name != schema:
                continue

            entrypoints[schema_entrypoint.name] = schema_entrypoint
            pprint(schema_entrypoint.dist.location)
            schema_top = schema_entrypoint.dist.location
            schema_migrations = schema_entrypoint.module_name.replace(
                'schemata', 'migrations').replace('.', '/')
            path = os.path.join(schema_top, schema_migrations)
            new_model = schema_entrypoint.load()['schema']

            migrations = []

            try:
                for file in sorted(os.listdir(path)):
                    if not file.endswith('.json'):
                        continue
                    fullpath = os.path.join(path, file)
                    hfoslog('Importing migration', fullpath)
                    with open(fullpath, 'r') as f:
                        migration = DeepDiff.from_json(f.read())
                    migrations.append(migration)
                    hfoslog('Successfully imported')

                if len(migrations) == 0:
                    raise ImportError
                pprint(migrations)
                model = apply_migrations(migrations, new_model)
                write_migration(schema,
                                len(migrations) + 1, path, model, new_model)
            except ImportError as e:
                hfoslog('No previous migrations for',
                        schema,
                        e,
                        type(e),
                        exc=True)

            if len(migrations) == 0:
                write_migration(schema, 1, path, None, new_model)

        except (ImportError, DistributionNotFound) as e:
            hfoslog("Problematic schema: ",
                    e,
                    type(e),
                    schema_entrypoint.name,
                    exc=True,
                    lvl=warn,
                    emitter='SCHEMATA')

    hfoslog("Found schemata: ",
            sorted(entrypoints.keys()),
            lvl=debug,
            emitter='SCHEMATA')

    pprint(entrypoints)

    def make_single_migration(old, new):
        pass
Пример #17
0
 def test_get_distance_cache_key(self):
     result = DeepDiff._get_distance_cache_key(added_hash=5,
                                               removed_hash=20)
     assert b'0x14--0x5dc' == result