Ai
2 Star 0 Fork 0

mirrors_seperman/deepdiff

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
test_delta.py 107.93 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934
import copy
import datetime
from typing import NamedTuple
import pytest
import os
import io
import json
import sys
from decimal import Decimal
from unittest import mock
from deepdiff import Delta, DeepDiff
from deepdiff.helper import np, number_to_string, TEXT_VIEW, DELTA_VIEW, CannotCompare, FlatDeltaRow, FlatDataAction, SetOrdered
from deepdiff.path import GETATTR, GET
from deepdiff.delta import (
ELEM_NOT_FOUND_TO_ADD_MSG,
VERIFICATION_MSG, VERIFY_BIDIRECTIONAL_MSG, not_found, DeltaNumpyOperatorOverrideError,
BINIARY_MODE_NEEDED_MSG, DELTA_AT_LEAST_ONE_ARG_NEEDED, DeltaError,
INVALID_ACTION_WHEN_CALLING_GET_ELEM, INVALID_ACTION_WHEN_CALLING_SIMPLE_SET_ELEM,
INVALID_ACTION_WHEN_CALLING_SIMPLE_DELETE_ELEM, INDEXES_NOT_FOUND_WHEN_IGNORE_ORDER,
FAIL_TO_REMOVE_ITEM_IGNORE_ORDER_MSG, UNABLE_TO_GET_PATH_MSG, NOT_VALID_NUMPY_TYPE)
from deepdiff.serialization import (
DELTA_IGNORE_ORDER_NEEDS_REPETITION_REPORT, DELTA_ERROR_WHEN_GROUP_BY,
json_dumps, json_loads,
)
from tests import PicklableClass, parameterize_cases, CustomClass, CustomClass2
class TestBasicsOfDelta:
def test_from_null_delta_json(self):
t1 = None
t2 = [1, 2, 3, 5]
diff = DeepDiff(t1, t2)
delta = Delta(diff, serializer=json_dumps)
dump = delta.dumps()
delta2 = Delta(dump, deserializer=json_loads)
assert delta2 + t1 == t2
assert t1 + delta2 == t2
with pytest.raises(ValueError) as exc_info:
t2 - delta
assert 'Please recreate the delta with bidirectional=True' == str(exc_info.value)
delta = Delta(diff, serializer=json_dumps, bidirectional=True)
assert t2 - delta == t1
def test_to_null_delta1_json(self):
t1 = 1
t2 = None
diff = DeepDiff(t1, t2)
delta = Delta(diff, serializer=json_dumps)
dump = delta.dumps()
delta2 = Delta(dump, deserializer=json_loads)
assert delta2 + t1 == t2
assert t1 + delta2 == t2
def test_to_null_delta2_json(self):
t1 = [1, 2, 3, 5]
t2 = None
diff = DeepDiff(t1, t2)
delta = Delta(diff)
assert delta + t1 == t2
assert t1 + delta == t2
def test_list_difference_add_delta(self):
t1 = [1, 2]
t2 = [1, 2, 3, 5]
diff = {'iterable_item_added': {'root[3]': 5, 'root[2]': 3}}
delta = Delta(diff)
assert delta + t1 == t2
assert t1 + delta == t2
flat_result1 = delta.to_flat_rows()
flat_expected1 = [
FlatDeltaRow(path=[3], value=5, action='iterable_item_added', type=int),
FlatDeltaRow(path=[2], value=3, action='iterable_item_added', type=int),
]
assert flat_expected1 == flat_result1
delta2 = Delta(diff=diff, bidirectional=True)
assert t1 == t2 - delta2
def test_list_difference_dump_delta(self):
t1 = [1, 2]
t2 = [1, 2, 3, 5]
diff = DeepDiff(t1, t2)
dump = Delta(diff).dumps()
delta = Delta(dump)
assert delta + t1 == t2
def test_multiple_delta(self):
t1 = [1, 2]
t2 = [1, 2, 3, 5]
t3 = [{1}, 3, 5]
dump1 = Delta(DeepDiff(t1, t2)).dumps()
dump2 = Delta(DeepDiff(t2, t3)).dumps()
delta1 = Delta(dump1)
delta2 = Delta(dump2)
assert t1 + delta1 + delta2 == t3
def test_delta_dump_and_read1(self, tmp_path):
t1 = [1, 2]
t2 = [1, 2, 3, 5]
diff = DeepDiff(t1, t2)
path = os.path.join(tmp_path, 'delta_test.delta')
with open(path, 'wb') as the_file:
Delta(diff).dump(the_file)
delta = Delta(delta_path=path)
os.remove(path)
assert delta + t1 == t2
def test_delta_dump_and_read2(self, tmp_path):
t1 = [1, 2]
t2 = [1, 2, 3, 5]
diff = DeepDiff(t1, t2)
delta_content = Delta(diff).dumps()
path = os.path.join(tmp_path, 'delta_test2.delta')
with open(path, 'wb') as the_file:
the_file.write(delta_content)
delta = Delta(delta_path=path)
os.remove(path)
assert delta + t1 == t2
def test_delta_dump_and_read3(self, tmp_path):
t1 = [1, 2]
t2 = [1, 2, 3, 5]
diff = DeepDiff(t1, t2)
delta_content = Delta(diff).dumps()
path = os.path.join(tmp_path, 'delta_test2.delta')
with open(path, 'wb') as the_file:
the_file.write(delta_content)
with pytest.raises(ValueError) as excinfo:
with open(path, 'r') as the_file:
delta = Delta(delta_file=the_file)
assert BINIARY_MODE_NEEDED_MSG[:20] == str(excinfo.value)[:20]
with open(path, 'rb') as the_file:
delta = Delta(delta_file=the_file)
os.remove(path)
assert delta + t1 == t2
def test_delta_when_no_arg_passed(self):
with pytest.raises(ValueError) as excinfo:
Delta()
assert DELTA_AT_LEAST_ONE_ARG_NEEDED == str(excinfo.value)
def test_delta_when_group_by(self):
t1 = [
{'id': 'AA', 'name': 'Joe', 'last_name': 'Nobody'},
{'id': 'BB', 'name': 'James', 'last_name': 'Blue'},
]
t2 = [
{'id': 'AA', 'name': 'Joe', 'last_name': 'Nobody'},
{'id': 'BB', 'name': 'James', 'last_name': 'Brown'},
]
diff = DeepDiff(t1, t2, group_by='id')
with pytest.raises(ValueError) as excinfo:
Delta(diff)
assert DELTA_ERROR_WHEN_GROUP_BY == str(excinfo.value)
def test_delta_repr(self):
t1 = [1, 2]
t2 = [1, 2, 3, 5]
diff = DeepDiff(t1, t2)
delta = Delta(diff)
options = {
'<Delta: {"iterable_item_added":{"root[2]":3,"root[3]":5}}>',
'<Delta: {"iterable_item_added":{"root[3]":5,"root[2]":3}}>',
}
assert repr(delta) in options
def test_get_elem_and_compare_to_old_value(self):
delta = Delta({})
with pytest.raises(DeltaError) as excinfo:
delta._get_elem_and_compare_to_old_value(
obj=None, path_for_err_reporting=None, expected_old_value=None, action='ketchup on steak')
assert INVALID_ACTION_WHEN_CALLING_GET_ELEM.format('ketchup on steak') == str(excinfo.value)
def test_simple_set_elem_value(self):
delta = Delta({}, raise_errors=True)
with pytest.raises(DeltaError) as excinfo:
delta._simple_set_elem_value(
obj=None, elem=None, value=None, action='mayo on salad', path_for_err_reporting=None)
assert INVALID_ACTION_WHEN_CALLING_SIMPLE_SET_ELEM.format('mayo on salad') == str(excinfo.value)
with pytest.raises(DeltaError) as excinfo:
delta._simple_set_elem_value(
obj={}, elem={1}, value=None, action=GET, path_for_err_reporting='mypath')
assert str(excinfo.value) in {"Failed to set mypath due to unhashable type: 'set'",
"Failed to set mypath due to 'set' objects are unhashable",
"Failed to set mypath due to cannot use 'set' as a dict key (unhashable type: 'set')"}
def test_simple_delete_elem(self):
delta = Delta({}, raise_errors=True)
with pytest.raises(DeltaError) as excinfo:
delta._simple_delete_elem(
obj=None, elem=None, action='burnt oil', path_for_err_reporting=None)
assert INVALID_ACTION_WHEN_CALLING_SIMPLE_DELETE_ELEM.format('burnt oil') == str(excinfo.value)
with pytest.raises(DeltaError) as excinfo:
delta._simple_delete_elem(
obj={}, elem=1, action=GET, path_for_err_reporting='mypath')
assert "Failed to set mypath due to 1" == str(excinfo.value)
def test_raise_error(self):
t1 = [1, 2, [3, 5, 6]]
t2 = [2, 3, [3, 6, 8]]
diff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True)
delta = Delta(diff, raise_errors=False)
t3 = [1, 2, 3, 5]
t4 = t3 + delta
assert [3, 2, 3, 5] == t4
delta2 = Delta(diff, raise_errors=True)
with pytest.raises(DeltaError) as excinfo:
t3 + delta2
assert "Unable to get the item at root[2][1]" == str(excinfo.value)
def test_identical_delta(self):
delta = Delta({})
t1 = [1, 3]
assert t1 + delta == t1
flat_result1 = delta.to_flat_rows()
flat_expected1 = []
assert flat_expected1 == flat_result1
def test_delta_mutate(self):
t1 = [1, 2]
t2 = [1, 2, 3, 5]
diff = DeepDiff(t1, t2)
delta = Delta(diff, mutate=True)
t1 + delta
assert t1 == t2
@mock.patch('deepdiff.delta.logger.error')
def test_list_difference_add_delta_when_index_not_valid(self, mock_logger):
t1 = [1, 2]
diff = {'iterable_item_added': {'root[20]': 3, 'root[3]': 5}}
delta = Delta(diff, log_errors=False)
assert delta + t1 == t1
# since we sort the keys by the path elements, root[3] is gonna be processed before root[20]
expected_msg = ELEM_NOT_FOUND_TO_ADD_MSG.format(3, 'root[3]')
delta2 = Delta(diff, bidirectional=True, raise_errors=True, log_errors=False)
with pytest.raises(ValueError) as excinfo:
delta2 + t1
assert expected_msg == str(excinfo.value)
assert not mock_logger.called
delta3 = Delta(diff, bidirectional=True, raise_errors=True, log_errors=True)
with pytest.raises(ValueError) as excinfo:
delta3 + t1
assert expected_msg == str(excinfo.value)
mock_logger.assert_called_once_with(expected_msg)
def test_list_difference3_delta(self):
t1 = {1: 1, 2: 2, 3: 3, 4: {"a": "hello", "b": [1, 2, 5]}}
t2 = {1: 1, 2: 2, 3: 3, 4: {"a": "hello", "b": [1, 3, 2, 5]}}
diff = {
'values_changed': {
"root[4]['b'][2]": {
'new_value': 2,
'old_value': 5
},
"root[4]['b'][1]": {
'new_value': 3,
'old_value': 2
}
},
'iterable_item_added': {
"root[4]['b'][3]": 5
}
}
delta = Delta(diff)
assert delta + t1 == t2
assert t1 + delta == t2
flat_result1 = delta.to_flat_rows()
flat_expected1 = [
FlatDeltaRow(path=[4, 'b', 2], action='values_changed', value=2, old_value=5, type=int, old_type=int),
FlatDeltaRow(path=[4, 'b', 1], action='values_changed', value=3, old_value=2, type=int, old_type=int),
FlatDeltaRow(path=[4, 'b', 3], value=5, action='iterable_item_added', type=int),
]
assert flat_expected1 == flat_result1
delta2 = Delta(diff=diff, bidirectional=True)
assert t1 == t2 - delta2
def test_list_difference_delta_raises_error_if_prev_value_does_not_match(self):
t1 = [1, 2, 6]
t2 = [1, 3, 2, 5]
diff = {
'values_changed': {
"root[2]": {
'new_value': 2,
'old_value': 5
},
"root[1]": {
'new_value': 3,
'old_value': 2
}
},
'iterable_item_added': {
"root[3]": 5
}
}
expected_msg = VERIFICATION_MSG.format('root[2]', 5, 6, VERIFY_BIDIRECTIONAL_MSG)
delta = Delta(diff, bidirectional=True, raise_errors=True)
with pytest.raises(ValueError) as excinfo:
delta + t1
assert expected_msg == str(excinfo.value)
delta2 = Delta(diff, bidirectional=False)
assert delta2 + t1 == t2
flat_result2 = delta2.to_flat_rows()
flat_expected2 = [
FlatDeltaRow(path=[2], action='values_changed', value=2, old_value=5, type=int, old_type=int),
FlatDeltaRow(path=[1], action='values_changed', value=3, old_value=2, type=int, old_type=int),
FlatDeltaRow(path=[3], value=5, action='iterable_item_added', type=int),
]
assert flat_expected2 == flat_result2
def test_list_difference_delta1(self):
t1 = {
1: 1,
2: 2,
3: 3,
4: {
"a": "hello",
"b": [1, 2, 'to_be_removed', 'to_be_removed2']
}
}
t2 = {1: 1, 2: 2, 3: 3, 4: {"a": "hello", "b": [1, 2]}}
diff = {
'iterable_item_removed': {
"root[4]['b'][2]": "to_be_removed",
"root[4]['b'][3]": 'to_be_removed2'
}
}
delta = Delta(diff)
assert delta + t1 == t2
flat_result = delta.to_flat_rows()
flat_expected = [
FlatDeltaRow(path=[4, 'b', 2], value='to_be_removed', action='iterable_item_removed', type=str),
FlatDeltaRow(path=[4, 'b', 3], value='to_be_removed2', action='iterable_item_removed', type=str),
]
assert flat_expected == flat_result
delta2 = Delta(diff=diff, bidirectional=True)
assert t1 == t2 - delta2
@mock.patch('deepdiff.delta.logger.error')
def test_list_difference_delta_if_item_is_already_removed(self, mock_logger):
t1 = [1, 2, 'to_be_removed']
t2 = [1, 2]
diff = {
'iterable_item_removed': {
"root[2]": "to_be_removed",
"root[3]": 'to_be_removed2'
}
}
delta = Delta(diff, bidirectional=True, raise_errors=True)
assert delta + t1 == t2, (
"We used to throw errors when the item to be removed was not found. "
"Instead, we try to look for the item to be removed even when the "
"index of it in delta is different than the index of it in the object."
)
delta2 = Delta(diff, bidirectional=False, raise_errors=False)
assert t1 + delta2 == t2
expected_msg = UNABLE_TO_GET_PATH_MSG.format('root[3]')
assert 0 == mock_logger.call_count
def test_list_difference_delta_does_not_raise_error_if_prev_value_changed(self):
t1 = {
1: 1,
2: 2,
3: 3,
4: {
"a": "hello",
"b": [1, 2, 'wrong', 'to_be_removed2']
}
}
t2 = {1: 1, 2: 2, 3: 3, 4: {"a": "hello", "b": [1, 2]}}
diff = {
'iterable_item_removed': {
"root[4]['b'][2]": "to_be_removed",
"root[4]['b'][3]": 'to_be_removed2'
}
}
# The previous behavior was to throw an error here because the original value for "root[4]['b'][2]" was not 'wrong' anymore.
# However, I decided to change that behavior to what makes more sense and is consistent with the bidirectional flag.
# No more verify_symmetry flag.
delta = Delta(diff, bidirectional=True, raise_errors=True)
assert delta + t1 != t2
expected = {1: 1, 2: 2, 3: 3, 4: {'a': 'hello', 'b': [1, 2, 'wrong']}}
assert expected == delta + t1
delta2 = Delta(diff, bidirectional=False, raise_errors=True)
assert expected == t1 + delta2
def test_delta_dict_items_added_retain_order(self):
t1 = {
6: 6
}
t2 = {
6: 6,
7: 7,
3: 3,
5: 5,
2: 2,
4: 4
}
expected_delta_dict = {
'dictionary_item_added': {
'root[7]': 7,
'root[3]': 3,
'root[5]': 5,
'root[2]': 2,
'root[4]': 4
}
}
diff = DeepDiff(t1, t2, threshold_to_diff_deeper=0)
delta_dict = diff._to_delta_dict()
assert expected_delta_dict == delta_dict
delta = Delta(diff, bidirectional=False, raise_errors=True)
result = t1 + delta
assert result == t2
assert set(result.keys()) == {6, 7, 3, 5, 2, 4}
assert set(result.keys()) == set(t2.keys())
delta2 = Delta(diff=diff, bidirectional=True)
assert t1 == t2 - delta2
delta3 = Delta(diff, always_include_values=True, bidirectional=True, raise_errors=True)
flat_rows_list = delta3.to_flat_rows()
delta4 = Delta(flat_rows_list=flat_rows_list,
always_include_values=True, bidirectional=True, raise_errors=True)
assert t1 == t2 - delta4
assert t1 + delta4 == t2
def test_delta_constr_flat_dict_list_param_preserve(self):
"""
Issue: https://github.com/seperman/deepdiff/issues/457
Scenario:
We found that when a flat_rows_list was provided as a constructor
parameter for instantiating a new delta, the provided flat_rows_list
is unexpectedly being mutated/changed, which can be troublesome for the
caller if they were expecting the flat_rows_list to be used BY COPY
rather than BY REFERENCE.
Intent:
Preserve the original value of the flat_rows_list variable within the
calling module/function after instantiating the new delta.
"""
t1 = {
"individualNames": [
{
"firstName": "Johnathan",
"lastName": "Doe",
"prefix": "COLONEL",
"middleName": "A",
"primaryIndicator": True,
"professionalDesignation": "PHD",
"suffix": "SR",
"nameIdentifier": "00001"
},
{
"firstName": "John",
"lastName": "Doe",
"prefix": "",
"middleName": "",
"primaryIndicator": False,
"professionalDesignation": "",
"suffix": "SR",
"nameIdentifier": "00002"
}
]
}
t2 = {
"individualNames": [
{
"firstName": "Johnathan",
"lastName": "Doe",
"prefix": "COLONEL",
"middleName": "A",
"primaryIndicator": True,
"professionalDesignation": "PHD",
"suffix": "SR",
"nameIdentifier": "00001"
},
{
"firstName": "Johnny",
"lastName": "Doe",
"prefix": "",
"middleName": "A",
"primaryIndicator": False,
"professionalDesignation": "",
"suffix": "SR",
"nameIdentifier": "00003"
}
]
}
def compare_func(item1, item2, level=None):
print("*** inside compare ***")
it1_keys = item1.keys()
try:
# --- individualNames ---
if 'nameIdentifier' in it1_keys and 'lastName' in it1_keys:
match_result = item1['nameIdentifier'] == item2['nameIdentifier']
print("individualNames - matching result:", match_result)
return match_result
else:
print("Unknown list item...", "matching result:", item1 == item2)
return item1 == item2
except Exception:
raise CannotCompare() from None
# ---------------------------- End of nested function
# This diff should show:
# 1 - list item (with an index on the path) being added
# 1 - list item (with an index on the path) being removed
diff = DeepDiff(t1, t2, report_repetition=True,
ignore_order=True, iterable_compare_func=compare_func, cutoff_intersection_for_pairs=1)
# Now create a flat_rows_list from a delta instantiated from the diff...
temp_delta = Delta(diff, always_include_values=True, bidirectional=True, raise_errors=True)
flat_rows_list = temp_delta.to_flat_rows()
# Note: the list index is provided on the path value...
assert flat_rows_list == [FlatDeltaRow(path=['individualNames', 1],
value={'firstName': 'Johnny',
'lastName': 'Doe',
'prefix': '',
'middleName': 'A',
'primaryIndicator': False,
'professionalDesignation': '',
'suffix': 'SR',
'nameIdentifier': '00003'},
action='unordered_iterable_item_added',
type=dict),
FlatDeltaRow(path=['individualNames', 1],
value={'firstName': 'John',
'lastName': 'Doe',
'prefix': '',
'middleName': '',
'primaryIndicator': False,
'professionalDesignation': '',
'suffix': 'SR',
'nameIdentifier': '00002'},
action='unordered_iterable_item_removed',
type=dict),
]
preserved_flat_dict_list = copy.deepcopy(flat_rows_list) # Use this later for assert comparison
# Now use the flat_rows_list to instantiate a new delta...
delta = Delta(flat_rows_list=flat_rows_list,
always_include_values=True, bidirectional=True, raise_errors=True)
flat_rows_list_again = delta.to_flat_rows()
# if the flat_rows_list is (unexpectedly) mutated, it will be missing the list index number on the path value.
old_mutated_list_missing_indexes_on_path = [FlatDeltaRow(path=['individualNames'],
value={'firstName': 'Johnny',
'lastName': 'Doe',
'prefix': '',
'middleName': 'A',
'primaryIndicator': False,
'professionalDesignation': '',
'suffix': 'SR',
'nameIdentifier': '00003'},
action='unordered_iterable_item_added'),
FlatDeltaRow(path=['individualNames'],
value={'firstName': 'John',
'lastName': 'Doe',
'prefix': '',
'middleName': '',
'primaryIndicator': False,
'professionalDesignation': '',
'suffix': 'SR',
'nameIdentifier': '00002'},
action='unordered_iterable_item_removed')]
# Verify that our fix in the delta constructor worked...
assert flat_rows_list != old_mutated_list_missing_indexes_on_path
assert flat_rows_list == preserved_flat_dict_list
assert flat_rows_list == flat_rows_list_again
def test_namedtuple_add_delta(self):
class Point(NamedTuple):
x: int
y: int
p1 = Point(1, 1)
p2 = Point(1, 2)
diff = DeepDiff(p1, p2)
delta = Delta(diff)
assert p2 == p1 + delta
def test_namedtuple_frozenset_add_delta(self):
class Article(NamedTuple):
tags: frozenset
a1 = Article(frozenset(["a" ]))
a2 = Article(frozenset(["a", "b"]))
diff = DeepDiff(a1, a2)
delta = Delta(diff)
assert a2 == a1 + delta
picklalbe_obj_without_item = PicklableClass(11)
del picklalbe_obj_without_item.item
DELTA_CASES = {
'delta_case0': {
't1': frozenset([1, 2, 'B']),
't2': frozenset([1, 2, 'B']),
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {},
},
'delta_case1': {
't1': frozenset([1, 2, 'B']),
't2': frozenset([1, 2, 3, 5]),
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {'set_item_removed': {'root': {'B'}}, 'set_item_added': {'root': {3, 5}}},
},
'delta_case2': {
't1': [1, 2, 'B'],
't2': [1, 2, 3, 5],
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {
'type_changes': {
'root[2]': {
'old_type': str,
'new_type': int,
'new_value': 3
}
},
'iterable_item_added': {
'root[3]': 5
}
},
},
'delta_case3': {
't1': [1, 2, '3'],
't2': [1, 2, 3],
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {
'type_changes': {
'root[2]': {
'old_type': str,
'new_type': int,
}
}
},
},
'delta_case4': {
't1': 3,
't2': '3',
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {
'type_changes': {
'root': {
'old_type': int,
'new_type': str,
}
}
},
},
'delta_case5': {
't1': 3.2,
't2': Decimal('3.2'),
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {
'type_changes': {
'root': {
'old_type': float,
'new_type': Decimal,
'new_value': Decimal('3.2')
}
}
},
},
'delta_case6': {
't1': (1, 2),
't2': (1, 3),
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {
'values_changed': {
'root[1]': {
'new_value': 3
}
}
},
},
'delta_case7': {
't1': (1, 2, 5),
't2': (1, ),
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {
'iterable_item_removed': {
'root[1]': 2,
'root[2]': 5
}
},
},
'delta_case8': {
't1': (1, 2, 5),
't2': (1, 3),
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {
'iterable_item_removed': {
'root[2]': 5
},
'values_changed': {
'root[1]': {
'new_value': 3
}
}
},
},
'delta_case9': {
't1': (1, ),
't2': (1, 3),
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {
'iterable_item_added': {
'root[1]': 3
},
},
},
'delta_case10': {
't1': {
2: 2
},
't2': {
2: 2,
3: 3
},
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {
'dictionary_item_added': {
'root[3]': 3
},
},
},
'delta_case11': {
't1': {
1: 1,
2: 2
},
't2': {
2: 2,
3: 3
},
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {
'dictionary_item_added': {
'root[3]': 3
},
'dictionary_item_removed': {
'root[1]': 1
},
},
},
'delta_case12': {
't1': PicklableClass(10),
't2': PicklableClass(11),
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {
'values_changed': {
'root.item': {
'new_value': 11
}
}
}
},
'delta_case13': {
't1': PicklableClass(10),
't2': picklalbe_obj_without_item,
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {
'attribute_removed': {
'root.item': 10
}
}
},
'delta_case14': {
't1': picklalbe_obj_without_item,
't2': PicklableClass(10),
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {
'attribute_added': {
'root.item': 10
}
}
},
'delta_case14b_threshold_to_diff_deeper': {
't1': picklalbe_obj_without_item,
't2': PicklableClass(11),
'deepdiff_kwargs': {'threshold_to_diff_deeper': 0.5},
'to_delta_kwargs': {},
'expected_delta_dict': {'attribute_added': {'root.item': 11}}
},
'delta_case15_diffing_simple_numbers': {
't1': 1,
't2': 2,
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {'values_changed': {'root': {'new_value': 2}}}
},
'delta_case16_diffmultiline_text': {
't1': {1: 1, 2: 2, 3: 3, 4: {'a': 'hello', 'b': 'world\n1\n2\nEnd'}},
't2': {1: 1, 2: 2, 3: 3, 4: {'a': 'hello', 'b': 'world!\nGoodbye!\n1\n2\nEnd'}},
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {'values_changed': {"root[4]['b']": {'new_value': 'world!\nGoodbye!\n1\n2\nEnd'}}}
},
'delta_case17_numbers_and_letters': {
't1': [0, 1, 2, 3, 4, 5, 6, 7, 8],
't2': [0, 1, 2, 3, 4, 5, 6, 7, 8, 'a', 'b', 'c'],
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {'iterable_item_added': {'root[9]': 'a', 'root[10]': 'b', 'root[11]': 'c'}}
},
'delta_case18_numbers_and_letters': {
't1': [0, 1, 2, 3, 4, 5, 6, 7, 8, 'a', 'b', 'c'],
't2': [0, 1, 2, 3, 4, 5, 6, 7, 8],
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {'iterable_item_removed': {'root[9]': 'a', 'root[10]': 'b', 'root[11]': 'c'}}
},
'delta_case19_value_removed_from_the_middle_of_list': {
't1': [0, 1, 2, 3, 4, 5, 6, 7, 8, 'a', 'b', 'c'],
't2': [0, 1, 2, 3, 5, 6, 7, 8, 'a', 'b', 'c'],
'deepdiff_kwargs': {},
'to_delta_kwargs': {'directed': True},
'expected_delta_dict': {'iterable_item_removed': {'root[4]': 4}}
},
'delta_case20_quotes_in_path': {
't1': {"a']['b']['c": 1},
't2': {"a']['b']['c": 2},
'deepdiff_kwargs': {},
'to_delta_kwargs': {'directed': True},
'expected_delta_dict': {'values_changed': {'root["a\'][\'b\'][\'c"]': {'new_value': 2}}}
},
'delta_case21_empty_list_add': {
't1': {'car_model': [], 'car_model_version_id': 0},
't2': {'car_model': ['Super Duty F-250'], 'car_model_version_id': 1},
'deepdiff_kwargs': {},
'to_delta_kwargs': {'directed': True},
'expected_delta_dict': {'iterable_item_added': {"root['car_model'][0]": 'Super Duty F-250'}, 'values_changed': {"root['car_model_version_id']": {'new_value': 1}}},
},
}
DELTA_CASES_PARAMS = parameterize_cases('test_name, t1, t2, deepdiff_kwargs, to_delta_kwargs, expected_delta_dict', DELTA_CASES)
class TestDelta:
@pytest.mark.parametrize(**DELTA_CASES_PARAMS)
def test_delta_cases(self, test_name, t1, t2, deepdiff_kwargs, to_delta_kwargs, expected_delta_dict):
diff = DeepDiff(t1, t2, **deepdiff_kwargs)
delta_dict = diff._to_delta_dict(**to_delta_kwargs)
assert expected_delta_dict == delta_dict, f"test_delta_cases {test_name} failed."
delta = Delta(diff, bidirectional=False, raise_errors=True)
assert t1 + delta == t2, f"test_delta_cases {test_name} failed."
delta2 = Delta(diff, bidirectional=True, raise_errors=True)
assert t2 - delta2 == t1, f"test_delta_cases {test_name} failed."
DELTA_IGNORE_ORDER_CASES = {
'delta_ignore_order_case1': {
't1': [1, 2, 'B', 3],
't2': [1, 2, 3, 5],
'deepdiff_kwargs': {
'ignore_order': True,
'report_repetition': True
},
'to_delta_kwargs': {},
'expected_delta_dict': {
'iterable_items_added_at_indexes': {
'root': {
3: 5
}
},
'iterable_items_removed_at_indexes': {
'root': {
2: 'B'
}
}
},
'expected_t1_plus_delta': 't2',
},
'delta_ignore_order_case2': {
't1': [1, 2, 'B', 3, 'B', 'B', 4],
't2': [1, 2, 3, 5],
'deepdiff_kwargs': {
'ignore_order': True,
'report_repetition': True
},
'to_delta_kwargs': {},
'expected_delta_dict': {
'values_changed': {
'root[6]': {
'new_value': 5,
'new_path': 'root[3]',
},
},
'iterable_items_removed_at_indexes': {
'root': {
2: 'B',
4: 'B',
5: 'B'
}
}
},
'expected_t1_plus_delta': 't2',
},
'delta_ignore_order_case_reverse2': {
't1': [1, 2, 3, 5],
't2': [1, 2, 'B', 3, 'B', 'B', 4],
'deepdiff_kwargs': {
'ignore_order': True,
'report_repetition': True
},
'to_delta_kwargs': {},
'expected_delta_dict': {
'values_changed': {
'root[3]': {
'new_value': 4,
'new_path': 'root[6]',
},
},
'iterable_items_added_at_indexes': {
'root': {
2: 'B',
4: 'B',
5: 'B'
}
}
},
'expected_t1_plus_delta': 't2',
},
'delta_ignore_order_case3': {
't1': [5, 1, 1, 1, 6],
't2': [7, 1, 1, 1, 8],
'deepdiff_kwargs': {
'ignore_order': True,
'report_repetition': True
},
'to_delta_kwargs': {},
'expected_delta_dict': {
'values_changed': {
'root[4]': {
'new_value': 7,
'new_path': 'root[0]'
},
'root[0]': {
'new_value': 8,
'new_path': 'root[4]'
}
}
},
'expected_t1_plus_delta': [8, 1, 1, 1, 7],
},
'delta_ignore_order_case4': {
't1': [5, 1, 3, 1, 4, 4, 6],
't2': [7, 4, 4, 1, 3, 4, 8],
'deepdiff_kwargs': {
'ignore_order': True,
'report_repetition': True
},
'to_delta_kwargs': {},
'expected_delta_dict': {
'values_changed': {
'root[6]': {
'new_value': 7,
'new_path': 'root[0]'
},
'root[0]': {
'new_value': 8,
'new_path': 'root[6]'
}
},
'iterable_items_added_at_indexes': {
'root': {
1: 4,
2: 4,
5: 4,
3: 1,
}
}
},
'expected_t1_plus_delta': [8, 4, 4, 1, 3, 4, 7],
},
'delta_ignore_order_case5': {
't1': (5, 1, 3, 1, 4, 4, 6),
't2': (7, 4, 4, 1, 3, 4, 8, 1),
'deepdiff_kwargs': {
'ignore_order': True,
'report_repetition': True
},
'to_delta_kwargs': {},
'expected_delta_dict': {
'iterable_items_added_at_indexes': {
'root': {
1: 4,
2: 4,
5: 4
}
},
'values_changed': {
'root[6]': {
'new_value': 7,
'new_path': 'root[0]',
},
'root[0]': {
'new_value': 8,
'new_path': 'root[6]',
}
}
},
'expected_t1_plus_delta': (8, 4, 4, 1, 3, 4, 1, 7),
},
'delta_ignore_order_case6': {
't1': [{1, 2, 3}, {4, 5}],
't2': [{4, 5, 6}, {1, 2, 3}],
'deepdiff_kwargs': {
'ignore_order': True,
'report_repetition': True
},
'to_delta_kwargs': {},
'expected_delta_dict': {'set_item_added': {'root[1]': {6}}},
'expected_t1_plus_delta': [{1, 2, 3}, {4, 5, 6}],
},
'delta_ignore_order_case7': {
't1': [{1, 2, 3}, {4, 5, 'hello', 'right!'}, {4, 5, (2, 4, 7)}],
't2': [{4, 5, 6, (2, )}, {1, 2, 3}, {5, 'hello', 'right!'}],
'deepdiff_kwargs': {
'ignore_order': True,
'report_repetition': True
},
'to_delta_kwargs': {},
'expected_delta_dict': {
'set_item_removed': {
'root[1]': {4}
},
'iterable_items_added_at_indexes': {
'root': {
0: {(2, ), 4, 5, 6}
}
},
'iterable_items_removed_at_indexes': {
'root': {
2: {4, 5, (2, 4, 7)}
}
}
},
'expected_t1_plus_delta': 't2',
},
'delta_ignore_order_case8_multi_dimensional_list': {
't1': [[1, 2, 3, 4], [4, 2, 2, 1]],
't2': [[4, 1, 1, 1], [1, 3, 2, 4]],
'deepdiff_kwargs': {
'ignore_order': True,
'report_repetition': True
},
'to_delta_kwargs': {},
'expected_delta_dict': {
'iterable_items_added_at_indexes': {
'root[1]': {
1: 1,
2: 1,
3: 1
}
},
'iterable_items_removed_at_indexes': {
'root[1]': {
1: 2,
2: 2
}
}
},
'expected_t1_plus_delta': [[1, 2, 3, 4], [4, 1, 1, 1]],
},
'delta_ignore_order_case9': {
't1': [{
"path": ["interface1", "ipv1"]
}, {
"path": ["interface2", "ipv2"]
}, {
"path": ["interface3", "ipv3"]
}, {
"path": [{
"test0": "interface4.0",
"test0.0": "ipv4.0"
}, {
"test1": "interface4.1",
"test1.1": "ipv4.1"
}]
}, {
"path": ["interface5", "ipv5"]
}],
't2': [{
"path": ["interface1", "ipv1"]
}, {
"path": ["interface3", "ipv3"]
}, {
"path": [{
"test0": "interface4.0",
"test0.0": "ipv4.0"
}, {
"test2": "interface4.2",
"test2.2": "ipv4.0"
}, {
"test1": "interface4.1",
"test1.1": "ipv4.1"
}]
}, {
"path": ["interface6", "ipv6"]
}, {
"path": ["interface5", "ipv5"]
}],
'deepdiff_kwargs': {
'ignore_order': True,
'report_repetition': True
},
'to_delta_kwargs': {},
'expected_delta_dict': {
'iterable_items_added_at_indexes': {
"root[3]['path']": {
1: {
'test2': 'interface4.2',
'test2.2': 'ipv4.0'
}
},
'root': {
3: {
'path': [
'interface6', 'ipv6'
]
}
}
},
'iterable_items_removed_at_indexes': {
'root': {
1: {
'path': ['interface2', 'ipv2']
}
}
}
},
'expected_t1_plus_delta':
't2',
},
}
DELTA_IGNORE_ORDER_CASES_PARAMS = parameterize_cases(
'test_name, t1, t2, deepdiff_kwargs, to_delta_kwargs, expected_delta_dict, expected_t1_plus_delta', DELTA_IGNORE_ORDER_CASES)
class TestIgnoreOrderDelta:
@pytest.mark.parametrize(**DELTA_IGNORE_ORDER_CASES_PARAMS)
def test_ignore_order_delta_cases(
self, test_name, t1, t2, deepdiff_kwargs, to_delta_kwargs, expected_delta_dict, expected_t1_plus_delta, request):
# test_name = request.node.callspec.id
diff = DeepDiff(t1, t2, **deepdiff_kwargs)
delta_dict = diff._to_delta_dict(**to_delta_kwargs)
assert expected_delta_dict == delta_dict, f"test_ignore_order_delta_cases {test_name} failed"
delta = Delta(diff, bidirectional=False, raise_errors=True)
expected_t1_plus_delta = t2 if expected_t1_plus_delta == 't2' else expected_t1_plus_delta
t1_plus_delta = t1 + delta
assert t1 + delta == t1_plus_delta, f"test_ignore_order_delta_cases {test_name} 'asserting that delta is not mutated once it is applied' failed"
# assert not DeepDiff(t1_plus_delta, expected_t1_plus_delta, ignore_order=True), f"test_ignore_order_delta_cases {test_name} failed: diff = {DeepDiff(t1_plus_delta, expected_t1_plus_delta, ignore_order=True)}"
DELTA_NUMPY_TEST_CASES = {
'delta_case15_similar_to_delta_numpy': {
't1': [1, 2, 3],
't2': [1, 2, 5],
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {'values_changed': {'root[2]': {'new_value': 5}}},
'expected_result': 't2'
},
'delta_numpy1_operator_override': {
't1': np.array([1, 2, 3], np.int8),
't2': np.array([1, 2, 5], np.int8),
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {'values_changed': {'root[2]': {'new_value': 5}}, '_numpy_paths': {'root': 'int8'}},
'expected_result': DeltaNumpyOperatorOverrideError
},
'delta_numpy2': {
't1': np.array([1, 2, 3], np.int8),
't2': np.array([1, 2, 5], np.int8),
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {'values_changed': {'root[2]': {'new_value': 5}}, '_numpy_paths': {'root': 'int8'}},
'expected_result': 't2'
},
'delta_numpy3_type_change_but_no_value_change': {
't1': np.array([1, 2, 3], np.int8),
't2': np.array([1, 2, 3], np.int16),
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {'type_changes': {'root': {'old_type': np.int8, 'new_type': np.int16}}},
'expected_result': 't2'
},
'delta_numpy4_type_change_plus_value_change': {
't1': np.array([1, 2, 3], np.int8),
't2': np.array([1, 2, 5], np.int16),
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': None, # Not easy to compare since it throws:
# ValueError: The truth value of an array with more than one element is ambiguous.
# And we don't want to use DeepDiff for testing the equality inside deepdiff tests themselves!
'expected_result': 't2'
},
'delta_numpy4_type_change_ignore_numeric_type_changes': {
't1': np.array([1, 2, 3], np.int8),
't2': np.array([1, 2, 5], np.int16),
'deepdiff_kwargs': {
'ignore_numeric_type_changes': True
},
'to_delta_kwargs': {},
'expected_delta_dict': {
'values_changed': {
'root[2]': {
'new_value': 5
}
},
'_numpy_paths': {
'root': 'int16'
}
},
'expected_result': 't2'
},
'delta_numpy5_multi_dimensional': {
't1': np.array([[1, 2, 3], [4, 2, 2]], np.int8),
't2': np.array([[1, 2, 5], [4, 1, 2]], np.int8),
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {
'values_changed': {
'root[0][2]': {
'new_value': 5
},
'root[1][1]': {
'new_value': 1
}
},
'_numpy_paths': {
'root': 'int8'
}
},
'expected_result': 't2'
},
'delta_numpy6_multi_dimensional_ignore_order': {
't1': np.array([[1, 2, 3, 4], [4, 2, 2, 1]], np.int8),
't2': np.array([[4, 1, 1, 1], [1, 3, 2, 4]], np.int8),
'deepdiff_kwargs': {
'ignore_order': True,
'report_repetition': True
},
'to_delta_kwargs': {},
'expected_delta_dict': {
'iterable_items_added_at_indexes': {
'root[1]': {
1: 1,
2: 1,
3: 1
}
},
'iterable_items_removed_at_indexes': {
'root[1]': {
1: 2,
2: 2
}
},
'_numpy_paths': {
'root': 'int8'
}
},
'expected_result': 't2_via_deepdiff'
},
'delta_numpy7_arrays_of_different_sizes': {
't1': np.array([1, 2, 3, 4]),
't2': np.array([5, 6, 7, 8, 9, 10]),
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {
'values_changed': {
'root[0]': {
'new_value': 5
},
'root[1]': {
'new_value': 6
},
'root[2]': {
'new_value': 7
},
'root[3]': {
'new_value': 8
}
},
'iterable_item_added': {
'root[4]': 9,
'root[5]': 10
},
'_numpy_paths': {
'root': np.where((sys.maxsize > 2**32), 'int64', 'int32')
}
},
'expected_result': 't2'
},
'delta_with_null_as_key': {
't1': { None: [1, 2], 'foo': [1, 2] },
't2': { None: [1], 'foo': [1] },
'deepdiff_kwargs': {},
'to_delta_kwargs': {},
'expected_delta_dict': {
},
'expected_result': 't2'
},
}
DELTA_NUMPY_TEST_PARAMS = parameterize_cases(
'test_name, t1, t2, deepdiff_kwargs, to_delta_kwargs, expected_delta_dict, expected_result', DELTA_NUMPY_TEST_CASES)
class TestNumpyDelta:
@pytest.mark.parametrize(**DELTA_NUMPY_TEST_PARAMS)
def test_numpy_delta_cases(self, test_name, t1, t2, deepdiff_kwargs, to_delta_kwargs, expected_delta_dict, expected_result):
diff = DeepDiff(t1, t2, **deepdiff_kwargs)
delta_dict = diff._to_delta_dict(**to_delta_kwargs)
if expected_delta_dict:
assert expected_delta_dict == delta_dict, f"test_numpy_delta_cases {test_name} failed."
delta = Delta(diff, bidirectional=False, raise_errors=True)
if expected_result == 't2':
result = delta + t1
assert np.array_equal(result, t2), f"test_numpy_delta_cases {test_name} failed."
elif expected_result == 't2_via_deepdiff':
result = delta + t1
diff = DeepDiff(result, t2, ignore_order=True, report_repetition=True)
assert not diff, f"test_numpy_delta_cases {test_name} failed."
elif expected_result is DeltaNumpyOperatorOverrideError:
with pytest.raises(DeltaNumpyOperatorOverrideError):
t1 + delta
else:
result = delta + t1
assert np.array_equal(result, expected_result), f"test_numpy_delta_cases {test_name} failed."
def test_invalid_numpy_type(self):
t1 = np.array([1, 2, 3], np.int8)
delta_dict = {'iterable_item_added': {'root[2]': 5}, '_numpy_paths': {'root': 'int11'}}
with pytest.raises(DeltaError) as excinfo:
Delta(delta_dict, raise_errors=True) + t1
expected_msg = NOT_VALID_NUMPY_TYPE.format("'int11'")
assert expected_msg == str(excinfo.value)
class TestDeltaOther:
def test_list_ignore_order_various_deltas1(self):
t1 = [5, 1, 3, 1, 4, 4, 6]
t2 = [7, 4, 4, 1, 3, 4, 8]
delta_dict1 = {'iterable_items_added_at_indexes': {'root': {0: 7, 6: 8, 1: 4, 2: 4, 5: 4, 3: 1}}, 'iterable_items_removed_at_indexes': {'root': {0: 5, 6: 6}}}
delta_dict2 = {'iterable_items_added_at_indexes': {'root': {1: 4, 2: 4, 5: 4, 3: 1}}, 'values_changed': {'root[6]': {'new_value': 7}, 'root[0]': {'new_value': 8}}}
delta1 = Delta(delta_dict1)
t1_plus_delta1 = t1 + delta1
assert t1_plus_delta1 == t2
delta2 = Delta(delta_dict2)
t1_plus_delta2 = t1 + delta2
assert t1_plus_delta2 == [8, 4, 4, 1, 3, 4, 7]
def test_list_ignore_order_various_deltas2(self):
t1 = (5, 1, 3, 1, 4, 4, 6)
t2 = (7, 4, 4, 1, 3, 4, 8, 1)
delta_dict1 = {'iterable_items_added_at_indexes': {'root': {0: 7, 6: 8, 1: 4, 2: 4, 5: 4}}, 'iterable_items_removed_at_indexes': {'root': {6: 6, 0: 5}}}
delta_dict2 = {'iterable_items_added_at_indexes': {'root': {1: 4, 2: 4, 5: 4}}, 'values_changed': {'root[6]': {'new_value': 7}, 'root[0]': {'new_value': 8}}}
delta1 = Delta(delta_dict1)
t1_plus_delta1 = t1 + delta1
assert t1_plus_delta1 == t2
delta2 = Delta(delta_dict2)
t1_plus_delta2 = t1 + delta2
assert t1_plus_delta2 == (8, 4, 4, 1, 3, 4, 1, 7)
flat_result1 = delta1.to_flat_rows()
flat_expected1 = [
{'path': [0], 'value': 7, 'action': 'unordered_iterable_item_added', 'type': int},
{'path': [6], 'value': 8, 'action': 'unordered_iterable_item_added', 'type': int},
{'path': [1], 'value': 4, 'action': 'unordered_iterable_item_added', 'type': int},
{'path': [2], 'value': 4, 'action': 'unordered_iterable_item_added', 'type': int},
{'path': [5], 'value': 4, 'action': 'unordered_iterable_item_added', 'type': int},
{'path': [6], 'value': 6, 'action': 'unordered_iterable_item_removed', 'type': int},
{'path': [0], 'value': 5, 'action': 'unordered_iterable_item_removed', 'type': int},
]
flat_expected1 = [FlatDeltaRow(**i) for i in flat_expected1]
assert flat_expected1 == flat_result1
delta1_again = Delta(flat_rows_list=flat_expected1)
assert t1_plus_delta1 == t1 + delta1_again
assert delta1.diff == delta1_again.diff
flat_result2 = delta2.to_flat_rows()
flat_expected2 = [
{'path': [1], 'value': 4, 'action': 'unordered_iterable_item_added', 'type': int},
{'path': [2], 'value': 4, 'action': 'unordered_iterable_item_added', 'type': int},
{'path': [5], 'value': 4, 'action': 'unordered_iterable_item_added', 'type': int},
{'path': [6], 'action': 'values_changed', 'value': 7, 'type': int},
{'path': [0], 'action': 'values_changed', 'value': 8, 'type': int},
]
flat_expected2 = [FlatDeltaRow(**i) for i in flat_expected2]
assert flat_expected2 == flat_result2
delta2_again = Delta(flat_rows_list=flat_expected2)
assert delta2.diff == delta2_again.diff
def test_delta_view_and_to_delta_dict_are_equal_when_parameteres_passed(self):
"""
This is a test that passes parameters in a dictionary instead of kwargs.
Note that when parameters are passed as a dictionary, all of them even the ones that
have default values need to be passed.
"""
t1 = [4, 2, 2, 1]
t2 = [4, 1, 1, 1]
_parameters = {
'ignore_order': True,
'ignore_numeric_type_changes': False,
'ignore_string_type_changes': False,
'ignore_type_in_groups': [],
'report_repetition': True,
'use_enum_value': False,
'exclude_paths': None,
'include_paths': None,
'exclude_regex_paths': None,
'exclude_types': None,
'exclude_types_tuple': None,
'ignore_type_subclasses': False,
'ignore_string_case': False,
'include_obj_callback': None,
'include_obj_callback_strict': None,
'exclude_obj_callback': None,
'exclude_obj_callback_strict': None,
'ignore_uuid_types': False,
'ignore_private_variables': True,
'ignore_nan_inequality': False,
'hasher': None,
'significant_digits': None,
'number_format_notation': 'f',
'verbose_level': 1,
'view': DELTA_VIEW,
'max_passes': 10000000,
'max_diffs': None,
'number_to_string': number_to_string,
'cache_tuning_sample_size': 500,
'cache_size': 500,
'cutoff_intersection_for_pairs': 0.6,
'group_by': None,
'ignore_order_func': lambda *args, **kwargs: True,
'custom_operators': [],
'encodings': None,
'ignore_encoding_errors': False,
'iterable_compare_func': None,
'default_timezone': datetime.timezone.utc,
}
expected = {'iterable_items_added_at_indexes': {'root': {1: 1, 2: 1, 3: 1}}, 'iterable_items_removed_at_indexes': {'root': {1: 2, 2: 2}}}
diff1 = DeepDiff(t1, t2, _parameters=_parameters)
assert expected == diff1
_parameters['view'] = TEXT_VIEW
diff2 = DeepDiff(t1, t2, _parameters=_parameters)
assert expected == diff2._to_delta_dict()
def test_verify_symmetry_and_get_elem_and_compare_to_old_value(self):
"""
Test a specific case where path was a list of elements (in the form of tuples)
and the item could not be found.
"""
delta = Delta({}, bidirectional=True, raise_errors=True, log_errors=False)
with pytest.raises(DeltaError) as excinfo:
delta._get_elem_and_compare_to_old_value(
obj={},
path_for_err_reporting=(('root', GETATTR),),
expected_old_value='Expected Value',
action=GET,
elem='key')
assert VERIFICATION_MSG.format('root', 'Expected Value', 'not found', "'key'") == str(excinfo.value)
def test_apply_delta_to_incompatible_object1(self):
t1 = {1: {2: [4, 5]}}
t2 = {1: {2: [4]}, 0: 'new'}
diff = DeepDiff(t1, t2)
delta = Delta(diff, raise_errors=True)
t3 = []
with pytest.raises(DeltaError) as excinfo:
delta + t3
assert "Unable to get the item at root[1][2][1]: list index out of range" == str(excinfo.value)
assert [] == t3
def test_apply_delta_to_incompatible_object3_errors_can_be_muted(self):
t1 = {1: {2: [4]}}
t2 = {1: {2: [4, 6]}}
t3 = []
diff = DeepDiff(t1, t2)
delta2 = Delta(diff, raise_errors=False)
t4 = delta2 + t3
assert [] == t4
def test_apply_delta_to_incompatible_object4_errors_can_be_muted(self):
t1 = {1: {2: [4, 5]}}
t2 = {1: {2: [4]}, 0: 'new'}
t3 = []
diff = DeepDiff(t1, t2)
# The original delta was based on a diff between 2 dictionaries.
# if we turn raise_errors=False, we can try to see what portions of the delta
delta2 = Delta(diff, raise_errors=False)
t4 = delta2 + t3
assert ['new'] == t4
def test_apply_delta_to_incompatible_object5_no_errors_detected(self):
t1 = {3: {2: [4]}}
t2 = {3: {2: [4]}, 0: 'new', 1: 'newer'}
diff = DeepDiff(t1, t2)
t3 = []
# The original delta was based on a diff between 2 dictionaries.
# if we turn raise_errors=True, and there are no errors, a delta can be applied fully to another object!
delta2 = Delta(diff, raise_errors=True)
t4 = delta2 + t3
assert ['new', 'newer'] == t4
def test_apply_delta_to_incompatible_object6_value_change(self):
t1 = {1: {2: [4]}}
t2 = {1: {2: [5]}}
t3 = []
diff = DeepDiff(t1, t2)
delta2 = Delta(diff, raise_errors=False)
t4 = delta2 + t3
assert [] == t4
flat_result2 = delta2.to_flat_rows()
flat_expected2 = [{'path': [1, 2, 0], 'action': 'values_changed', 'value': 5, 'type': int}]
flat_expected2 = [FlatDeltaRow(**i) for i in flat_expected2]
assert flat_expected2 == flat_result2
delta2_again = Delta(flat_rows_list=flat_expected2)
assert delta2.diff == delta2_again.diff
delta3 = Delta(diff, raise_errors=False, bidirectional=True)
flat_result3 = delta3.to_flat_rows()
flat_expected3 = [{'path': [1, 2, 0], 'action': 'values_changed', 'value': 5, 'old_value': 4, 'type': int, 'old_type': int}]
flat_expected3 = [FlatDeltaRow(**i) for i in flat_expected3]
assert flat_expected3 == flat_result3
delta3_again = Delta(flat_rows_list=flat_expected3)
assert delta3.diff == delta3_again.diff
def test_apply_delta_to_incompatible_object7_type_change(self):
t1 = ['1']
t2 = [1]
t3 = ['a']
diff = DeepDiff(t1, t2)
delta2 = Delta(diff, raise_errors=False)
t4 = delta2 + t3
assert ['a'] == t4
@mock.patch('deepdiff.delta.logger.error')
def test_apply_delta_to_incompatible_object7_verify_symmetry(self, mock_logger):
t1 = [1]
t2 = [2]
t3 = [3]
diff = DeepDiff(t1, t2)
delta2 = Delta(diff, raise_errors=False, bidirectional=True)
t4 = delta2 + t3
assert [2] == t4
expected_msg = VERIFICATION_MSG.format('root[0]', 1, 3, VERIFY_BIDIRECTIONAL_MSG)
mock_logger.assert_called_once_with(expected_msg)
@mock.patch('deepdiff.delta.logger.error')
def test_apply_delta_to_incompatible_object8_verify_symmetry_ignore_order(self, mock_logger):
t1 = [1, 2, 'B', 3]
t2 = [1, 2, 3, 5]
t3 = []
diff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True)
delta2 = Delta(diff, raise_errors=False, bidirectional=True)
t4 = delta2 + t3
assert [5] == t4
expected_msg = INDEXES_NOT_FOUND_WHEN_IGNORE_ORDER.format({3: 5})
mock_logger.assert_called_once_with(expected_msg)
@mock.patch('deepdiff.delta.logger.error')
def test_apply_delta_to_incompatible_object9_ignore_order_and_verify_symmetry(self, mock_logger):
t1 = [1, 2, 'B']
t2 = [1, 2]
t3 = [1, 2, 'C']
diff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True)
delta = Delta(diff, raise_errors=False, bidirectional=True)
t4 = delta + t3
assert [1, 2, 'C'] == t4
expected_msg = FAIL_TO_REMOVE_ITEM_IGNORE_ORDER_MSG.format(2, 'root', 'B', 'C')
mock_logger.assert_called_once_with(expected_msg)
@mock.patch('deepdiff.delta.logger.error')
def test_apply_delta_to_incompatible_object10_ignore_order(self, mock_logger):
t1 = [1, 2, 'B']
t2 = [1, 2]
t3 = [1, 2, 'C']
diff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True)
# when bidirectional=False, we still won't remove the item that is different
# than what we expect specifically when ignore_order=True when generating the diff.
# The reason is that when ignore_order=True, we can' rely too much on the index
# of the item alone to delete it. We need to make sure we are deleting the correct value.
# The expected behavior is exactly the same as when bidirectional=True
# specifically for when ignore_order=True AND an item is removed.
delta = Delta(diff, raise_errors=False, bidirectional=False)
t4 = delta + t3
assert [1, 2, 'C'] == t4
expected_msg = FAIL_TO_REMOVE_ITEM_IGNORE_ORDER_MSG.format(2, 'root', 'B', 'C')
mock_logger.assert_called_once_with(expected_msg)
@mock.patch('deepdiff.delta.logger.error')
def test_apply_delta_to_incompatible_object11_ignore_order(self, mock_logger):
t1 = [[1, 2, 'B']]
t2 = [[1, 2]]
t3 = {}
diff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True)
delta = Delta(diff, raise_errors=False, bidirectional=False)
t4 = delta + t3
assert {} == t4
expected_msg = UNABLE_TO_GET_PATH_MSG.format('root[0][0]')
mock_logger.assert_called_once_with(expected_msg)
def test_delta_to_dict(self):
t1 = [1, 2, 'B']
t2 = [1, 2]
diff = DeepDiff(t1, t2, ignore_order=True, report_repetition=True)
delta = Delta(diff, raise_errors=False, bidirectional=False)
result = delta.to_dict()
expected = {'iterable_items_removed_at_indexes': {'root': {2: 'B'}}}
assert expected == result
flat_result = delta.to_flat_rows()
flat_expected = [{'action': 'unordered_iterable_item_removed', 'path': [2], 'value': 'B', 'type': str}]
flat_expected = [FlatDeltaRow(**i) for i in flat_expected]
assert flat_expected == flat_result
delta_again = Delta(flat_rows_list=flat_expected)
assert delta.diff == delta_again.diff
def test_class_type_change(self):
t1 = CustomClass
t2 = CustomClass2
diff = DeepDiff(t1, t2, view=DELTA_VIEW)
expected = {'type_changes': {'root': {'new_type': CustomClass2,
'old_type': CustomClass}}}
assert expected == diff
def test_numpy_type_invalid(self):
t1 = np.array([[1, 2, 3], [4, 2, 2]], np.int8)
diff = {
'iterable_item_added': {'root[2]': [7, 8, 9]},
'values_changed': {
'root[0][2]': {
'new_value': 5
},
'root[1][1]': {
'new_value': 1
}
},
'_numpy_paths': {
'root': 'int88'
}
}
delta = Delta(diff, raise_errors=True)
with pytest.raises(DeltaError) as excinfo:
delta + t1
assert "'int88' is not a valid numpy type." == str(excinfo.value)
def test_ignore_order_but_not_report_repetition(self):
t1 = [1, 2, 'B', 3]
t2 = [1, 2, 3, 5]
with pytest.raises(ValueError) as excinfo:
Delta(DeepDiff(t1, t2, ignore_order=True))
assert DELTA_IGNORE_ORDER_NEEDS_REPETITION_REPORT == str(excinfo.value)
def test_none_in_delta_object(self):
t1 = {"a": None}
t2 = {"a": 1}
dump = Delta(DeepDiff(t1, t2)).dumps()
delta = Delta(dump)
assert t2 == delta + t1
flat_result = delta.to_flat_rows()
flat_expected = [{'path': ['a'], 'action': 'type_changes', 'value': 1, 'type': int, 'old_type': type(None)}]
flat_expected = [FlatDeltaRow(**i) for i in flat_expected]
assert flat_expected == flat_result
delta_again = Delta(flat_rows_list=flat_expected)
assert delta.diff == delta_again.diff
with pytest.raises(ValueError) as exc_info:
delta.to_flat_rows(report_type_changes=False)
assert str(exc_info.value).startswith("When converting to flat dictionaries, if report_type_changes=False and there are type")
delta2 = Delta(dump, always_include_values=True)
flat_result2 = delta2.to_flat_rows(report_type_changes=False)
flat_expected2 = [{'path': ['a'], 'action': 'values_changed', 'value': 1}]
flat_expected2 = [FlatDeltaRow(**i) for i in flat_expected2]
assert flat_expected2 == flat_result2
def test_delta_set_in_objects(self):
t1 = [[1, SetOrdered(['A', 'B'])], {1}]
t2 = [[2, SetOrdered([10, 'C', 'B'])], {1}]
delta = Delta(DeepDiff(t1, t2))
flat_result = delta.to_flat_rows()
flat_expected = [
{'path': [0, 1], 'value': 10, 'action': 'set_item_added', 'type': int},
{'path': [0, 0], 'action': 'values_changed', 'value': 2, 'type': int},
{'path': [0, 1], 'value': 'A', 'action': 'set_item_removed', 'type': str},
{'path': [0, 1], 'value': 'C', 'action': 'set_item_added', 'type': str},
]
flat_expected = [FlatDeltaRow(**i) for i in flat_expected]
# Sorting because otherwise the order is not deterministic for sets,
# even though we are using SetOrdered here. It still is converted to set at some point and loses its order.
flat_result.sort(key=lambda x: str(x.value))
assert flat_expected == flat_result
delta_again = Delta(flat_rows_list=flat_expected)
assert delta.diff == delta_again.diff
def test_delta_array_of_bytes(self):
t1 = []
t2 = [b"hello"]
diff = DeepDiff(t1, t2)
expected_diff = {'iterable_item_added': {'root[0]': b'hello'}}
assert expected_diff == diff
delta = Delta(diff)
flat_result = delta.to_flat_rows()
flat_expected = [FlatDeltaRow(path=[0], action=FlatDataAction.iterable_item_added, value=b'hello', type=bytes)]
assert flat_expected == flat_result
delta_again = Delta(flat_rows_list=flat_expected)
assert delta.diff == delta_again.diff
assert t1 + delta_again == t2
def test_delta_with_json_serializer(self):
t1 = {"a": 1}
t2 = {"a": 2}
diff = DeepDiff(t1, t2)
delta = Delta(diff, serializer=json.dumps)
dump = delta.dumps()
delta_reloaded = Delta(dump, deserializer=json.loads)
assert t2 == delta_reloaded + t1
the_file = io.StringIO()
delta.dump(the_file)
the_file.seek(0)
delta_reloaded_again = Delta(delta_file=the_file, deserializer=json.loads)
assert t2 == delta_reloaded_again + t1
def test_brackets_in_keys(self):
"""
Delta calculation not correct when bracket in Json key
https://github.com/seperman/deepdiff/issues/265
"""
t1 = "{ \
\"test\": \"test1\" \
}"
t2 = "{ \
\"test\": \"test1\", \
\"test2 [uuu]\": \"test2\" \
}"
json1 = json.loads(t1)
json2 = json.loads(t2)
ddiff = DeepDiff(json1, json2)
delta = Delta(ddiff)
original_json2 = delta + json1
assert json2 == original_json2
class TestDeltaCompareFunc:
@staticmethod
def compare_func(x, y, level):
if (not isinstance(x, dict) or not isinstance(y, dict)):
raise CannotCompare
if(level.path() == "root['path2']"):
if (x["ID"] == y["ID"]):
return True
return False
if("id" in x and "id" in y):
if (x["id"] == y["id"]):
return True
return False
raise CannotCompare
def test_compare_func1(self, compare_func_t1, compare_func_t2, compare_func_result1):
ddiff = DeepDiff(
compare_func_t1, compare_func_t2,
iterable_compare_func=self.compare_func, verbose_level=1)
assert compare_func_result1 == ddiff
delta = Delta(ddiff)
recreated_t2 = compare_func_t1 + delta
assert compare_func_t2 == recreated_t2
def test_compare_func_with_duplicates_removed(self):
t1 = [
{
'id': 1,
'val': 1,
"nested": [
{"id": 1, "val": 1},
{"id": 2, "val": 2},
]
},
{
'id': 2,
'val': 2
},
{
'id': 1,
'val': 3
},
{
'id': 3,
'val': 3
}
]
t2 = [
{
'id': 3,
'val': 3
},
{
'id': 2,
'val': 2
},
{
'id': 1,
'val': 3,
"nested":[
{
"id": 2,
"val": 3
},
]
}
]
ddiff = DeepDiff(t1, t2, iterable_compare_func=self.compare_func, verbose_level=2)
expected = {
"iterable_item_removed": {
"root[2]": {
"id": 1,
"val": 3
},
"root[2]['nested'][0]": {
"id": 1,
"val": 1
}
},
"iterable_item_moved": {
"root[0]": {
"new_path": "root[2]",
"value": {
"id": 1,
"val": 3,
"nested": [{"id": 2, "val": 3}, ]
},
},
"root[0]['nested'][1]": {
"new_path": "root[2]['nested'][0]",
"value": {
"id": 2,
"val": 3
}
},
"root[3]": {
"new_path": "root[0]",
"value": {
"id": 3,
"val": 3
}
}
},
'values_changed': {
"root[2]['nested'][0]['val']": {
'new_path': "root[0]['nested'][1]['val']",
'new_value': 3,
'old_value': 2
},
"root[2]['val']": {
'new_value': 3,
'old_value': 1,
'new_path': "root[0]['val']"
}
},
}
assert expected == ddiff
delta = Delta(ddiff)
recreated_t2 = t1 + delta
assert t2 == recreated_t2
flat_result = delta.to_flat_rows()
flat_expected = [
{'path': [2, 'val'], 'value': 3, 'action': 'values_changed', 'type': int, 'new_path': [0, 'val']},
{'path': [2, 'nested', 0, 'val'], 'value': 3, 'action': 'values_changed', 'type': int, 'new_path': [0, 'nested', 1, 'val']},
{'path': [2, 'nested', 0], 'value': {'id': 1, 'val': 1}, 'action': 'iterable_item_removed', 'type': dict},
{'path': [2], 'value': {'id': 1, 'val': 3}, 'action': 'iterable_item_removed', 'type': dict},
{'path': [0], 'value': {'id': 1, 'val': 3, 'nested': [{'id': 2, 'val': 3}]}, 'action': 'iterable_item_removed', 'type': dict},
{'path': [0, 'nested', 1], 'value': {'id': 2, 'val': 3}, 'action': 'iterable_item_removed', 'type': dict},
{'path': [3], 'value': {'id': 3, 'val': 3}, 'action': 'iterable_item_removed', 'type': dict},
{'path': [0], 'action': 'iterable_item_moved', 'value': {'id': 1, 'val': 3, 'nested': [{'id': 2, 'val': 3}]}, 'new_path': [2], 'type': dict},
{'path': [0, 'nested', 1], 'value': {'id': 2, 'val': 3}, 'action': 'iterable_item_moved', 'type': dict, 'new_path': [2, 'nested', 0]},
{'path': [3], 'action': 'iterable_item_moved', 'value': {'id': 3, 'val': 3}, 'new_path': [0], 'type': dict},
]
flat_expected = [FlatDeltaRow(**i) for i in flat_expected]
assert flat_expected == flat_result
# Delta.DEBUG = True
delta_again = Delta(flat_rows_list=flat_expected, iterable_compare_func_was_used=True)
expected_delta_dict = {
'iterable_item_removed': {
'root[2]': {
'id': 1,
'val': 3
},
'root[0]': {
'id': 1,
'val': 3,
'nested': [{'id': 2, 'val': 3}]
},
'root[3]': {
'id': 3,
'val': 3
},
"root[2]['nested'][0]": {
"id": 1,
"val": 1
},
"root[0]['nested'][1]": {
"id": 2,
"val": 3
}
},
'iterable_item_moved': {
'root[0]': {
'new_path': 'root[2]',
'value': {
'id': 1,
'val': 3,
'nested': [{'id': 2, 'val': 3}]
}
},
"root[0]['nested'][1]": {
'new_path': "root[2]['nested'][0]",
'value': {
'id': 2,
'val': 3
}
},
'root[3]': {
'new_path': 'root[0]',
'value': {
'id': 3,
'val': 3
}
}
},
'values_changed': {
"root[2]['val']": {
'new_value': 3,
'new_path': "root[0]['val']",
},
"root[2]['nested'][0]['val']": {
'new_path': "root[0]['nested'][1]['val']",
'new_value': 3,
},
}
}
assert expected_delta_dict == delta_again.diff
assert t2 == t1 + delta_again
def test_compare_func_with_duplicates_added(self):
t1 = [{'id': 3, 'val': 3}, {'id': 2, 'val': 2}, {'id': 1, 'val': 3}]
t2 = [{'id': 1, 'val': 1}, {'id': 2, 'val': 2}, {'id': 1, 'val': 3}, {'id': 3, 'val': 3}]
ddiff = DeepDiff(t1, t2, iterable_compare_func=self.compare_func, verbose_level=2)
expected = {
'iterable_item_added': {
'root[2]': {
'id': 1,
'val': 3
}
},
'iterable_item_moved': {
'root[0]': {
'new_path': 'root[3]',
'value': {
'id': 3,
'val': 3
}
},
'root[2]': {
'new_path': 'root[0]',
'value': {
'id': 1,
'val': 1
}
}
},
'values_changed': {
"root[0]['val']": {
'new_value': 1,
'old_value': 3,
'new_path': "root[2]['val']"
}
},
}
assert expected == ddiff
delta = Delta(ddiff)
recreated_t2 = t1 + delta
assert t2 == recreated_t2
def test_compare_func_swap(self):
t1 = [{'id': 1, 'val': 1}, {'id': 1, 'val': 3}]
t2 = [{'id': 1, 'val': 3}, {'id': 1, 'val': 1}]
ddiff = DeepDiff(t1, t2, iterable_compare_func=self.compare_func, verbose_level=2)
expected = {'values_changed': {"root[0]['val']": {'new_value': 3, 'old_value': 1}, "root[1]['val']": {'new_value': 1, 'old_value': 3}}}
assert expected == ddiff
delta = Delta(ddiff)
recreated_t2 = t1 + delta
assert t2 == recreated_t2
def test_compare_func_path_specific(self):
t1 = {"path1": [{'id': 1, 'val': 1}, {'id': 2, 'val': 3}], "path2": [{'ID': 4, 'val': 3}, {'ID': 3, 'val': 1}, ], "path3": [{'no_id': 5, 'val': 1}, {'no_id': 6, 'val': 3}]}
t2 = {"path1": [{'id': 1, 'val': 1}, {'id': 2, 'val': 3}], "path2": [{'ID': 3, 'val': 1}, {'ID': 4, 'val': 3}], "path3": [{'no_id': 5, 'val': 1}, {'no_id': 6, 'val': 3}]}
ddiff = DeepDiff(t1, t2, iterable_compare_func=self.compare_func, verbose_level=2)
expected = {'iterable_item_moved': {"root['path2'][0]": {'new_path': "root['path2'][1]", 'value': {'ID': 4, 'val': 3}},"root['path2'][1]": {'new_path': "root['path2'][0]", 'value': {'ID': 3, 'val': 1}}}}
assert expected == ddiff
delta = Delta(ddiff)
recreated_t2 = t1 + delta
assert t2 == recreated_t2
def test_compare_func_nested_changes(self):
t1 = {
"TestTable": [
{
"id": "022fb580-800e-11ea-a361-39b3dada34b5",
"name": "Max",
"NestedTable": [
{
"id": "022fb580-800e-11ea-a361-39b3dada34a6",
"NestedField": "Test Field"
}
]
},
{
"id": "022fb580-800e-11ea-a361-12354656532",
"name": "Bob",
"NestedTable": [
{
"id": "022fb580-800e-11ea-a361-39b3dada34c7",
"NestedField": "Test Field 2"
},
]
},
]
}
t2 = {"TestTable": [
{
"id": "022fb580-800e-11ea-a361-12354656532",
"name": "Bob (Changed Name)",
"NestedTable": [
{
"id": "022fb580-800e-11ea-a361-39b3dada34c7",
"NestedField": "Test Field 2 (Changed Nested Field)"
},
{
"id": "new id",
"NestedField": "Test Field 3"
},
{
"id": "newer id",
"NestedField": "Test Field 4"
},
]
},
{
"id": "adding_some_random_id",
"name": "New Name",
"NestedTable": [
{
"id": "random_nested_id_added",
"NestedField": "New Nested Field"
},
{
"id": "random_nested_id_added2",
"NestedField": "New Nested Field2"
},
{
"id": "random_nested_id_added3",
"NestedField": "New Nested Field43"
},
]
}
]}
ddiff = DeepDiff(t1, t2, iterable_compare_func=self.compare_func, verbose_level=2)
delta = Delta(ddiff)
recreated_t2 = t1 + delta
assert t2 == recreated_t2
def test_compare_func_deep_nested_changes(self):
t1 = {
"Locations": [
{
"id": "c4fa7b12-f365-42a9-9544-3efc11963558",
"Items": [
{
"id": "2399528f-2556-4e2c-bf9b-c8ea17bc323f"
},
{
"id": "2399528f-2556-4e2c-bf9b-c8ea17bc323f1",
},
{
"id": "2399528f-2556-4e2c-bf9b-c8ea17bc323f2"
},
{
"id": "2399528f-2556-4e2c-bf9b-c8ea17bc323f3"
}
]
},
{
"id": "d9095676-bc41-4cbf-9fd2-7148bb26bcc4",
"Items": [
{
"id": "26b78305-df71-40c0-8e98-dcd40b7f716d"
},
{
"id": "3235125d-0110-4d0e-847a-24912cf73feb"
},
{
"id": "7699552a-add9-4338-aeb9-662bec14c175"
},
{
"id": "015e74f0-2c2a-45c0-a172-21758d14bf3a"
}
]
},
{
"id": "41b38757-8984-47fd-890d-8c4ed18c3c47",
"Items": [
{
"id": "494e839e-37b1-4cac-b1dc-a44f3e6e7ada"
},
{
"id": "60547ca6-3ef0-4b67-8826-2c7b76e67011"
},
{
"id": "cee762a0-fbd8-48bb-ba92-be32cf3cf250"
},
{
"id": "7a0da2b7-c1e6-45b4-8810-fec7b4b6186d"
}
]
},
{
"id": "c0be071a-5457-497d-9a78-ff7cb561d4d3",
"Items": [
{
"id": "e54dcdff-ec99-4941-92eb-c12bb3cbeb91"
}
]
},
{
"id": "dfe4b37b-8df3-4dc6-8686-0588937fbe10",
"Items": [
{
"id": "27a574ae-08db-47f9-a9dc-18df59287f4d"
},
{
"id": "23edf031-8c4e-43d6-b5bf-4d5ee9008a36",
"Containers": [
{"id": "1", "val": 1},
{"id": "2", "val": 2},
{"id": "3", "val": 3},
]
},
{
"id": "e1e54643-23ee-496d-b7d2-de67c4bb7d68"
},
{
"id": "2f910da3-8cd0-4cf5-81c9-23668fc9477f"
},
{
"id": "5e36d258-2a82-49ee-b4fc-db0a8c28b404"
},
{
"id": "4bf2ce8d-05ed-4718-a529-8c9e4704e38f"
},
]
},
]
}
t2 = {
"Locations": [
{
"id": "41b38757-8984-47fd-890d-8c4ed18c3c47",
"Items": [
{
"id": "60547ca6-3ef0-4b67-8826-2c7b76e67011"
},
{
"id": "cee762a0-fbd8-48bb-ba92-be32cf3cf250"
},
{
"id": "7a0da2b7-c1e6-45b4-8810-fec7b4b6186d"
}
]
},
{
"id": "c0be071a-5457-497d-9a78-ff7cb561d4d3",
"Items": [
{
"id": "e54dcdff-ec99-4941-92eb-c12bb3cbeb91"
}
]
},
{
"id": "dfe4b37b-8df3-4dc6-8686-0588937fbe10",
"Items": [
{
"id": "27a574ae-08db-47f9-a9dc-18df59287f4d"
},
{
"id": "27a574ae-08db-47f9-a9dc-88df59287f4d"
},
{
"id": "23edf031-8c4e-43d6-b5bf-4d5ee9008a36",
"Containers": [
{"id": "1", "val": 1},
{"id": "3", "val": 3},
{"id": "2", "val": 2},
]
},
{
"id": "e1e54643-23ee-496d-b7d2-de67c4bb7d68"
},
{
"id": "2f910da3-8cd0-4cf5-81c9-23668fc9477f"
},
{
"id": "5e36d258-2a82-49ee-b4fc-db0a8c28b404"
},
{
"id": "4bf2ce8d-05ed-4718-a529-8c9e4704e38f"
},
]
},
]
}
ddiff = DeepDiff(t1, t2, iterable_compare_func=self.compare_func, verbose_level=2)
delta2 = Delta(ddiff)
expected_move_1 = {'new_path': "root['Locations'][2]['Items'][2]['Containers'][2]", 'value': {'id': '2', 'val': 2}}
expected_move_2 = {'new_path': "root['Locations'][2]['Items'][2]['Containers'][1]", 'value': {'id': '3', 'val': 3}}
assert ddiff["iterable_item_moved"]["root['Locations'][4]['Items'][1]['Containers'][1]"] == expected_move_1
assert ddiff["iterable_item_moved"]["root['Locations'][4]['Items'][1]['Containers'][2]"] == expected_move_2
recreated_t2 = t1 + delta2
assert t2 == recreated_t2
def test_delta_force1(self):
t1 = {
'x': {
'y': [1, 2, 3]
},
'q': {
'r': 'abc',
}
}
t2 = {
'x': {
'y': [1, 2, 3, 4]
},
'q': {
'r': 'abc',
't': 0.5,
}
}
diff = DeepDiff(t1, t2)
delta = Delta(diff=diff, force=True)
result = {} + delta
expected = {'x': {'y': {3: 4}}, 'q': {'t': 0.5}}
assert expected == result
def test_delta_force_fill(self):
t1 = {
'x': {
'y': [{"b": "c"}, {"b": "c"}, {"b": "c"}, {"b": "c"}]
},
'q': {
'r': 'abc',
}
}
t2 = {
'x': {
'y': [{"b": "c"}, {"b": "c"}, {"b": "c"}, {"b": "c"}, {"b": "c"}, {"b": "c"}, {"b": "c"}]
},
'q': {
'r': 'abc',
't': 0.5,
}
}
diff = DeepDiff(t1, t2)
delta = Delta(diff=diff, force=True)
result = {"x": {"y": [1,]}} + delta
expected = {'x': {'y': [1]}, 'q': {'t': 0.5}}
assert expected == result
delta = Delta(diff=diff, force=True, fill=None)
result = {"x": {"y": [1,]}} + delta
expected = {'x': {'y': [1, None, None, None, {"b": "c"}, {"b": "c"}, {"b": "c"}]}, 'q': {'t': 0.5}}
assert expected == result
def fill_func(obj, value, path):
return value.copy()
delta = Delta(diff=diff, force=True, fill=fill_func)
result = {"x": {"y": [1,]}} + delta
expected = {'x': {'y': [1, {"b": "c"}, {"b": "c"}, {"b": "c"}, {"b": "c"}, {"b": "c"}, {"b": "c"}]}, 'q': {'t': 0.5}}
assert expected == result
def test_flatten_dict_with_one_key_added(self):
t1 = {"field1": {"joe": "Joe"}}
t2 = {"field1": {"joe": "Joe Nobody"}, "field2": {"jimmy": "Jimmy"}}
diff = DeepDiff(t1, t2)
delta = Delta(diff=diff, always_include_values=True)
flat_result = delta.to_flat_rows(report_type_changes=False)
flat_expected = [
{'path': ['field2', 'jimmy'], 'value': 'Jimmy', 'action': 'dictionary_item_added'},
{'path': ['field1', 'joe'], 'action': 'values_changed', 'value': 'Joe Nobody'},
]
flat_expected = [FlatDeltaRow(**i) for i in flat_expected]
assert flat_expected == flat_result
delta_again = Delta(flat_rows_list=flat_expected, force=True) # We need to enable force so it creates the dictionary when added to t1
expected_data_again_diff = {'dictionary_item_added': {"root['field2']['jimmy']": 'Jimmy'}, 'values_changed': {"root['field1']['joe']": {'new_value': 'Joe Nobody'}}}
assert delta.diff != delta_again.diff, "Since a dictionary containing a single field was created, the flat dict acted like one key was added."
assert expected_data_again_diff == delta_again.diff, "Since a dictionary containing a single field was created, the flat dict acted like one key was added."
assert t2 == t1 + delta_again
def test_flatten_dict_with_multiple_keys_added(self):
t1 = {"field1": {"joe": "Joe"}}
t2 = {"field1": {"joe": "Joe Nobody"}, "field2": {"jimmy": "Jimmy", "sar": "Sarah"}}
diff = DeepDiff(t1, t2)
delta = Delta(diff=diff, always_include_values=True)
flat_result = delta.to_flat_rows(report_type_changes=False)
flat_expected = [
{'path': ['field2'], 'value': {'jimmy': 'Jimmy', 'sar': 'Sarah'}, 'action': 'dictionary_item_added'},
{'path': ['field1', 'joe'], 'action': 'values_changed', 'value': 'Joe Nobody'},
]
flat_expected = [FlatDeltaRow(**i) for i in flat_expected]
assert flat_expected == flat_result
delta_again = Delta(flat_rows_list=flat_expected)
assert delta.diff == delta_again.diff
def test_flatten_list_with_one_item_added(self):
t1 = {"field1": {"joe": "Joe"}}
t2 = {"field1": {"joe": "Joe"}, "field2": ["James"]}
t3 = {"field1": {"joe": "Joe"}, "field2": ["James", "Jack"]}
diff = DeepDiff(t1, t2)
delta = Delta(diff=diff, always_include_values=True)
flat_result = delta.to_flat_rows(report_type_changes=False)
flat_expected = [{'path': ['field2', 0], 'value': 'James', 'action': 'iterable_item_added'}]
flat_expected = [FlatDeltaRow(**i) for i in flat_expected]
assert flat_expected == flat_result
delta_again = Delta(flat_rows_list=flat_expected, force=True)
assert {'iterable_item_added': {"root['field2'][0]": 'James'}} == delta_again.diff
# delta_again.DEBUG = True
assert t2 == t1 + delta_again
diff2 = DeepDiff(t2, t3)
delta2 = Delta(diff=diff2, always_include_values=True)
flat_result2 = delta2.to_flat_rows(report_type_changes=False)
flat_expected2 = [{'path': ['field2', 1], 'value': 'Jack', 'action': 'iterable_item_added'}]
flat_expected2 = [FlatDeltaRow(**i) for i in flat_expected2]
assert flat_expected2 == flat_result2
delta_again2 = Delta(flat_rows_list=flat_expected2, force=True)
assert {'iterable_item_added': {"root['field2'][1]": 'Jack'}} == delta_again2.diff
assert t3 == t2 + delta_again2
def test_flatten_set_with_one_item_added(self):
t1 = {"field1": {"joe": "Joe"}}
t2 = {"field1": {"joe": "Joe"}, "field2": {"James"}}
t3 = {"field1": {"joe": "Joe"}, "field2": {"James", "Jack"}}
diff = DeepDiff(t1, t2)
delta = Delta(diff=diff, always_include_values=True)
assert t2 == t1 + delta
flat_result = delta.to_flat_rows(report_type_changes=False)
flat_expected = [{'path': ['field2'], 'value': 'James', 'action': 'set_item_added'}]
flat_expected = [FlatDeltaRow(**i) for i in flat_expected]
assert flat_expected == flat_result
delta_again = Delta(flat_rows_list=flat_expected, force=True)
assert {'set_item_added': {"root['field2']": {'James'}}} == delta_again.diff
assert t2 == t1 + delta_again
diff = DeepDiff(t2, t3)
delta2 = Delta(diff=diff, always_include_values=True)
flat_result2 = delta2.to_flat_rows(report_type_changes=False)
flat_expected2 = [{'path': ['field2'], 'value': 'Jack', 'action': 'set_item_added'}]
flat_expected2 = [FlatDeltaRow(**i) for i in flat_expected2]
assert flat_expected2 == flat_result2
delta_again2 = Delta(flat_rows_list=flat_expected2, force=True)
assert {'set_item_added': {"root['field2']": {'Jack'}}} == delta_again2.diff
assert t3 == t2 + delta_again2
def test_flatten_tuple_with_one_item_added(self):
t1 = {"field1": {"joe": "Joe"}}
t2 = {"field1": {"joe": "Joe"}, "field2": ("James", )}
t3 = {"field1": {"joe": "Joe"}, "field2": ("James", "Jack")}
diff = DeepDiff(t1, t2)
delta = Delta(diff=diff, always_include_values=True)
assert t2 == t1 + delta
flat_expected = delta.to_flat_rows(report_type_changes=False)
expected_result = [{'path': ['field2', 0], 'value': 'James', 'action': 'iterable_item_added'}]
expected_result = [FlatDeltaRow(**i) for i in expected_result]
assert expected_result == flat_expected
delta_again = Delta(flat_rows_list=flat_expected, force=True)
assert {'iterable_item_added': {"root['field2'][0]": 'James'}} == delta_again.diff
assert {'field1': {'joe': 'Joe'}, 'field2': ['James']} == t1 + delta_again, "We lost the information about tuple when we convert to flat dict."
diff = DeepDiff(t2, t3)
delta2 = Delta(diff=diff, always_include_values=True, force=True)
flat_result2 = delta2.to_flat_rows(report_type_changes=False)
expected_result2 = [{'path': ['field2', 1], 'value': 'Jack', 'action': 'iterable_item_added'}]
expected_result2 = [FlatDeltaRow(**i) for i in expected_result2]
assert expected_result2 == flat_result2
assert t3 == t2 + delta2
delta_again2 = Delta(flat_rows_list=flat_result2)
assert {'iterable_item_added': {"root['field2'][1]": 'Jack'}} == delta_again2.diff
assert t3 == t2 + delta_again2
def test_flatten_list_with_multiple_item_added(self):
t1 = {"field1": {"joe": "Joe"}}
t2 = {"field1": {"joe": "Joe"}, "field2": ["James", "Jack"]}
diff = DeepDiff(t1, t2)
delta = Delta(diff=diff, always_include_values=True)
flat_result = delta.to_flat_rows(report_type_changes=False)
expected_result = [{'path': ['field2'], 'value': ['James', 'Jack'], 'action': 'dictionary_item_added'}]
expected_result = [FlatDeltaRow(**i) for i in expected_result]
assert expected_result == flat_result
delta2 = Delta(diff=diff, bidirectional=True, always_include_values=True)
flat_result2 = delta2.to_flat_rows(report_type_changes=False)
assert expected_result == flat_result2
delta_again = Delta(flat_rows_list=flat_result)
assert delta.diff == delta_again.diff
def test_flatten_attribute_added(self):
t1 = picklalbe_obj_without_item
t2 = PicklableClass(10)
diff = DeepDiff(t1, t2)
delta = Delta(diff=diff, always_include_values=True)
flat_result = delta.to_flat_rows(report_type_changes=False)
expected_result = [{'path': ['item'], 'value': 10, 'action': 'attribute_added'}]
expected_result = [FlatDeltaRow(**i) for i in expected_result]
assert expected_result == flat_result
delta_again = Delta(flat_rows_list=flat_result)
assert delta.diff == delta_again.diff
def test_flatten_when_simple_type_change(self):
t1 = [1, 2, '3']
t2 = [1, 2, 3]
diff = DeepDiff(t1, t2)
expected_diff = {
'type_changes': {'root[2]': {'old_type': str, 'new_type': int, 'old_value': '3', 'new_value': 3}}
}
assert expected_diff == diff
delta = Delta(diff=diff)
with pytest.raises(ValueError) as exc_info:
delta.to_flat_rows(report_type_changes=False)
assert str(exc_info.value).startswith("When converting to flat dictionaries")
delta2 = Delta(diff=diff, always_include_values=True)
flat_result2 = delta2.to_flat_rows(report_type_changes=False)
expected_result2 = [{'path': [2], 'action': 'values_changed', 'value': 3}]
expected_result2 = [FlatDeltaRow(**i) for i in expected_result2]
assert expected_result2 == flat_result2
delta3 = Delta(diff=diff, always_include_values=True, bidirectional=True)
flat_result3 = delta3.to_flat_rows(report_type_changes=False)
expected_result3 = [{'path': [2], 'action': 'values_changed', 'value': 3, 'old_value': '3'}]
expected_result3 = [FlatDeltaRow(**i) for i in expected_result3]
assert expected_result3 == flat_result3
delta_again = Delta(flat_rows_list=flat_result3)
assert {'values_changed': {'root[2]': {'new_value': 3, 'old_value': '3'}}} == delta_again.diff
def test_subtract_delta1(self):
t1 = {'field_name1': ['yyy']}
t2 = {'field_name1': ['xxx', 'yyy']}
delta_diff = {'iterable_items_removed_at_indexes': {"root['field_name1']": {(0, 'GET'): 'xxx'}}}
expected_reverse_diff = {'iterable_items_added_at_indexes': {"root['field_name1']": {(0, 'GET'): 'xxx'}}}
delta = Delta(delta_diff=delta_diff, bidirectional=True)
reversed_diff = delta._get_reverse_diff()
assert expected_reverse_diff == reversed_diff
assert t2 != {'field_name1': ['yyy', 'xxx']} == t1 - delta, "Since iterable_items_added_at_indexes is used when ignore_order=True, the order is not necessarily the original order."
def test_subtract_delta_made_from_flat_dicts1(self):
t1 = {'field_name1': ['xxx', 'yyy']}
t2 = {'field_name1': []}
diff = DeepDiff(t1, t2)
delta = Delta(diff=diff, bidirectional=True)
flat_rows_list = delta.to_flat_rows(include_action_in_path=False, report_type_changes=True)
expected_flat_dicts = [{
'path': ['field_name1', 0],
'value': 'xxx',
'action': 'iterable_item_removed',
'type': str,
}, {
'path': ['field_name1', 1],
'value': 'yyy',
'action': 'iterable_item_removed',
'type': str,
}]
expected_flat_dicts = [FlatDeltaRow(**i) for i in expected_flat_dicts]
assert expected_flat_dicts == flat_rows_list
delta1 = Delta(flat_rows_list=flat_rows_list, bidirectional=True, force=True)
assert t1 == t2 - delta1
delta2 = Delta(flat_rows_list=[flat_rows_list[0]], bidirectional=True, force=True)
middle_t = t2 - delta2
assert {'field_name1': ['xxx']} == middle_t
delta3 = Delta(flat_rows_list=[flat_rows_list[1]], bidirectional=True, force=True)
assert t1 == middle_t - delta3
def test_subtract_delta_made_from_flat_dicts2(self):
t1 = {'field_name1': []}
t2 = {'field_name1': ['xxx', 'yyy']}
diff = DeepDiff(t1, t2)
delta = Delta(diff=diff, bidirectional=True)
flat_rows_list = delta.to_flat_rows(include_action_in_path=False, report_type_changes=True)
expected_flat_dicts = [{
'path': ['field_name1', 0],
'value': 'xxx',
'action': 'iterable_item_added',
'type': str,
}, {
'path': ['field_name1', 1],
'value': 'yyy',
'action': 'iterable_item_added',
'type': str,
}]
expected_flat_dicts = [FlatDeltaRow(**i) for i in expected_flat_dicts]
assert expected_flat_dicts == flat_rows_list
delta1 = Delta(flat_rows_list=flat_rows_list, bidirectional=True, force=True)
assert t1 == t2 - delta1
# We need to subtract the changes in the reverse order if we want to feed the flat dict rows individually to Delta
delta2 = Delta(flat_rows_list=[flat_rows_list[0]], bidirectional=True, force=True)
middle_t = t2 - delta2
assert {'field_name1': ['yyy']} == middle_t
delta3 = Delta(flat_rows_list=[flat_rows_list[1]], bidirectional=True, force=True)
delta3.DEBUG = True
assert t1 == middle_t - delta3
def test_list_of_alphabet_and_its_delta(self):
l1 = "A B C D E F G D H".split()
l2 = "B C X D H Y Z".split()
diff = DeepDiff(l1, l2)
# Problem: The index of values_changed should be either all for AFTER removals or BEFORE removals.
# What we have here is that F & G transformation to Y and Z is not compatible with A and E removal
# it is really meant for the removals to happen first, and then have indexes in L2 for values changing
# rather than indexes in L1. Here what we need to have is:
# A B C D E F G D H
# A B C-X-E
# B C D F G D H # removal
# What we really need is to report is as it is in difflib for delta specifically:
# A B C D E F G D H
# B C D E F G D H delete t1[0:1] --> t2[0:0] ['A'] --> []
# B C D E F G D H equal t1[1:3] --> t2[0:2] ['B', 'C'] --> ['B', 'C']
# B C X D H replace t1[3:7] --> t2[2:3] ['D', 'E', 'F', 'G'] --> ['X']
# B C X D H equal t1[7:9] --> t2[3:5] ['D', 'H'] --> ['D', 'H']
# B C X D H Y Z insert t1[9:9] --> t2[5:7] [] --> ['Y', 'Z']
# So in this case, it needs to also include information about what stays equal in the delta
# NOTE: the problem is that these operations need to be performed in a specific order.
# DeepDiff removes that order and just buckets all insertions vs. replace vs. delete in their own buckets.
# For times that we use Difflib, we may want to keep the information for the array_change key
# just for the sake of delta, but not for reporting in deepdiff itself.
# that way we can re-apply the changes as they were reported in delta.
delta = Delta(diff)
assert l2 == l1 + delta
with pytest.raises(ValueError) as exc_info:
l1 == l2 - delta
assert "Please recreate the delta with bidirectional=True" == str(exc_info.value)
delta2 = Delta(diff, bidirectional=True)
assert l2 == l1 + delta2
assert l1 == l2 - delta2
dump = Delta(diff, bidirectional=True).dumps()
delta3 = Delta(dump, bidirectional=True)
assert l2 == l1 + delta3
assert l1 == l2 - delta3
dump4 = Delta(diff, bidirectional=True, serializer=json_dumps).dumps()
delta4 = Delta(dump4, bidirectional=True, deserializer=json_loads)
assert l2 == l1 + delta4
assert l1 == l2 - delta4
flat_rows = delta2.to_flat_rows()
expected_flat_rows = [
FlatDeltaRow(path=[3], action='values_changed', value='X', old_value='D', type=str, old_type=str, new_path=[2]),
FlatDeltaRow(path=[6], action='values_changed', value='Z', old_value='G', type=str, old_type=str),
FlatDeltaRow(path=[5], action='values_changed', value='Y', old_value='F', type=str, old_type=str),
FlatDeltaRow(path=[], action=FlatDataAction.iterable_items_deleted, value=[], old_value=['A'], type=list, old_type=list, t1_from_index=0, t1_to_index=1, t2_from_index=0, t2_to_index=0),
FlatDeltaRow(path=[], action=FlatDataAction.iterable_items_equal, value=None, old_value=None, type=type(None), old_type=type(None), t1_from_index=1, t1_to_index=3, t2_from_index=0, t2_to_index=2),
FlatDeltaRow(path=[], action=FlatDataAction.iterable_items_replaced, value=['X'], old_value=['D', 'E', 'F', 'G'], type=list, old_type=list, t1_from_index=3, t1_to_index=7, t2_from_index=2, t2_to_index=3),
FlatDeltaRow(path=[], action=FlatDataAction.iterable_items_equal, value=None, old_value=None, type=type(None), old_type=type(None), t1_from_index=7, t1_to_index=9, t2_from_index=3, t2_to_index=5),
FlatDeltaRow(path=[], action=FlatDataAction.iterable_items_inserted, value=['Y', 'Z'], old_value=[], type=list, old_type=list, t1_from_index=9, t1_to_index=9, t2_from_index=5, t2_to_index=7)
]
# The order of the first 3 items is not deterministic
assert not DeepDiff(expected_flat_rows[:3], flat_rows[:3], ignore_order=True)
assert expected_flat_rows[3:] == flat_rows[3:]
delta5 = Delta(flat_rows_list=flat_rows, bidirectional=True, force=True)
assert l2 == l1 + delta5
assert l1 == l2 - delta5
def test_delta_flat_rows(self):
t1 = {"key1": "value1"}
t2 = {"field2": {"key2": "value2"}}
diff = DeepDiff(t1, t2, verbose_level=2)
delta = Delta(diff, bidirectional=True)
assert t1 + delta == t2
flat_rows = delta.to_flat_rows()
# we need to set force=True because when we create flat rows, if a nested
# dictionary with a single key is created, the path in the flat row will be
# the path to the leaf node.
delta2 = Delta(flat_rows_list=flat_rows, bidirectional=True, force=True)
assert t1 + delta2 == t2
def test_delta_bool(self):
flat_rows_list = [FlatDeltaRow(path=['dollar_to_cent'], action='values_changed', value=False, old_value=True, type=bool, old_type=bool)]
value = {'dollar_to_cent': False}
delta = Delta(flat_rows_list=flat_rows_list, bidirectional=True, force=True)
assert {'dollar_to_cent': True} == value - delta
def test_detla_add_to_empty_iterable_and_flatten(self):
t1 = {'models': [], 'version_id': 0}
t2 = {'models': ['Super Duty F-250'], 'version_id': 1}
t3 = {'models': ['Super Duty F-250', 'Focus'], 'version_id': 1}
diff = DeepDiff(t1, t2, verbose_level=2)
delta = Delta(diff, bidirectional=True)
assert t1 + delta == t2
flat_rows = delta.to_flat_rows()
delta2 = Delta(flat_rows_list=flat_rows, bidirectional=True) # , force=True
assert t1 + delta2 == t2
assert t2 - delta2 == t1
diff3 = DeepDiff(t2, t3)
delta3 = Delta(diff3, bidirectional=True)
flat_dicts3 = delta3.to_flat_dicts()
delta3_again = Delta(flat_dict_list=flat_dicts3, bidirectional=True)
assert t2 + delta3_again == t3
assert t3 - delta3_again == t2
def test_flat_dict_and_deeply_nested_dict(self):
beforeImage = [
{
"usage": "Mailing",
"standardization": "YES",
"primaryIndicator": True,
"addressIdentifier": "Z8PDWBG42YC",
"addressLines": ["871 PHILLIPS FERRY RD"],
},
{
"usage": "Residence",
"standardization": "YES",
"primaryIndicator": False,
"addressIdentifier": "Z8PDWBG42YC",
"addressLines": ["871 PHILLIPS FERRY RD"],
},
{
"usage": "Mailing",
"standardization": None,
"primaryIndicator": False,
"addressIdentifier": "MHPP3BY0BYC",
"addressLines": ["871 PHILLIPS FERRY RD", "APT RV92"],
},
]
allAfterImage = [
{
"usage": "Residence",
"standardization": "NO",
"primaryIndicator": False,
"addressIdentifier": "Z8PDWBG42YC",
"addressLines": ["871 PHILLIPS FERRY RD"],
},
{
"usage": "Mailing",
"standardization": None,
"primaryIndicator": False,
"addressIdentifier": "MHPP3BY0BYC",
"addressLines": ["871 PHILLIPS FERRY RD", "APT RV92"],
},
{
"usage": "Mailing",
"standardization": "NO",
"primaryIndicator": True,
"addressIdentifier": "Z8PDWBG42YC",
"addressLines": ["871 PHILLIPS FERRY RD"],
},
]
diff = DeepDiff(
beforeImage,
allAfterImage,
ignore_order=True,
report_repetition=True,
)
# reverse_diff = DeepDiff(
# allAfterImage,
# beforeImage,
# ignore_order=True,
# report_repetition=True,
# )
delta = Delta(
diff, always_include_values=True, bidirectional=True
)
# reverse_delta = Delta(
# reverse_diff, always_include_values=True, bidirectional=True
# )
allAfterImageAgain = beforeImage + delta
diff2 = DeepDiff(allAfterImage, allAfterImageAgain, ignore_order=True)
assert not diff2
# print("\ndelta.diff")
# pprint(delta.diff)
# print("\ndelta._get_reverse_diff()")
# pprint(delta._get_reverse_diff())
# print("\nreverse_delta.diff")
# pprint(reverse_delta.diff)
beforeImageAgain = allAfterImage - delta
diff3 = DeepDiff(beforeImage, beforeImageAgain, ignore_order=True)
assert not diff3
# ------ now let's recreate the delta from flat dicts -------
flat_dict_list = delta.to_flat_dicts()
delta2 = Delta(
flat_dict_list=flat_dict_list,
always_include_values=True,
bidirectional=True,
raise_errors=False,
force=True,
)
# print("\ndelta from flat dicts")
# pprint(delta2.diff)
allAfterImageAgain2 = beforeImage + delta2
diff4 = DeepDiff(allAfterImage, allAfterImageAgain2, ignore_order=True)
assert not diff4
beforeImageAgain2 = allAfterImage - delta2
diff4 = DeepDiff(beforeImage, beforeImageAgain2, ignore_order=True)
assert not diff4
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/mirrors_seperman/deepdiff.git
git@gitee.com:mirrors_seperman/deepdiff.git
mirrors_seperman
deepdiff
deepdiff
master

搜索帮助