|
| 1 | +import copy |
| 2 | + |
1 | 3 | import pytest
|
2 | 4 | import os
|
3 | 5 | import io
|
@@ -461,6 +463,154 @@ def test_delta_dict_items_added_retain_order(self):
|
461 | 463 | delta2 = Delta(diff=diff, bidirectional=True)
|
462 | 464 | assert t1 == t2 - delta2
|
463 | 465 |
|
| 466 | + def test_delta_constr_flat_dict_list_param_preserve(self): |
| 467 | + """ |
| 468 | + Issue: https://github.com/seperman/deepdiff/issues/457 |
| 469 | +
|
| 470 | + Scenario: |
| 471 | + We found that when a flat_dict_list was provided as a constructor |
| 472 | + parameter for instantiating a new delta, the provided flat_dict_list |
| 473 | + is unexpectedly being mutated/changed, which can be troublesome for the |
| 474 | + caller if they were expecting the flat_dict_list to be used BY COPY |
| 475 | + rather than BY REFERENCE. |
| 476 | +
|
| 477 | + Intent: |
| 478 | + Preserve the original value of the flat_dict_list variable within the |
| 479 | + calling module/function after instantiating the new delta. |
| 480 | + """ |
| 481 | + |
| 482 | + t1 = { |
| 483 | + "individualNames": [ |
| 484 | + { |
| 485 | + "firstName": "Johnathan", |
| 486 | + "lastName": "Doe", |
| 487 | + "prefix": "COLONEL", |
| 488 | + "middleName": "A", |
| 489 | + "primaryIndicator": True, |
| 490 | + "professionalDesignation": "PHD", |
| 491 | + "suffix": "SR", |
| 492 | + "nameIdentifier": "00001" |
| 493 | + }, |
| 494 | + { |
| 495 | + "firstName": "John", |
| 496 | + "lastName": "Doe", |
| 497 | + "prefix": "", |
| 498 | + "middleName": "", |
| 499 | + "primaryIndicator": False, |
| 500 | + "professionalDesignation": "", |
| 501 | + "suffix": "SR", |
| 502 | + "nameIdentifier": "00002" |
| 503 | + } |
| 504 | + ] |
| 505 | + } |
| 506 | + |
| 507 | + t2 = { |
| 508 | + "individualNames": [ |
| 509 | + { |
| 510 | + "firstName": "Johnathan", |
| 511 | + "lastName": "Doe", |
| 512 | + "prefix": "COLONEL", |
| 513 | + "middleName": "A", |
| 514 | + "primaryIndicator": True, |
| 515 | + "professionalDesignation": "PHD", |
| 516 | + "suffix": "SR", |
| 517 | + "nameIdentifier": "00001" |
| 518 | + }, |
| 519 | + { |
| 520 | + "firstName": "Johnny", |
| 521 | + "lastName": "Doe", |
| 522 | + "prefix": "", |
| 523 | + "middleName": "A", |
| 524 | + "primaryIndicator": False, |
| 525 | + "professionalDesignation": "", |
| 526 | + "suffix": "SR", |
| 527 | + "nameIdentifier": "00003" |
| 528 | + } |
| 529 | + ] |
| 530 | + } |
| 531 | + |
| 532 | + def compare_func(item1, item2, level=None): |
| 533 | + print("*** inside compare ***") |
| 534 | + it1_keys = item1.keys() |
| 535 | + |
| 536 | + try: |
| 537 | + |
| 538 | + # --- individualNames --- |
| 539 | + if 'nameIdentifier' in it1_keys and 'lastName' in it1_keys: |
| 540 | + match_result = item1['nameIdentifier'] == item2['nameIdentifier'] |
| 541 | + print("individualNames - matching result:", match_result) |
| 542 | + return match_result |
| 543 | + else: |
| 544 | + print("Unknown list item...", "matching result:", item1 == item2) |
| 545 | + return item1 == item2 |
| 546 | + except Exception: |
| 547 | + raise CannotCompare() from None |
| 548 | + # ---------------------------- End of nested function |
| 549 | + |
| 550 | + # This diff should show: |
| 551 | + # 1 - list item (with an index on the path) being added |
| 552 | + # 1 - list item (with an index on the path) being removed |
| 553 | + diff = DeepDiff(t1, t2, report_repetition=True, |
| 554 | + ignore_order=True, iterable_compare_func=compare_func, cutoff_intersection_for_pairs=1) |
| 555 | + |
| 556 | + # Now create a flat_dict_list from a delta instantiated from the diff... |
| 557 | + temp_delta = Delta(diff, always_include_values=True, bidirectional=True, raise_errors=True) |
| 558 | + flat_dict_list = temp_delta.to_flat_dicts() |
| 559 | + |
| 560 | + # Note: the list index is provided on the path value... |
| 561 | + assert flat_dict_list == [{'path': ['individualNames', 1], |
| 562 | + 'value': {'firstName': 'Johnny', |
| 563 | + 'lastName': 'Doe', |
| 564 | + 'prefix': '', |
| 565 | + 'middleName': 'A', |
| 566 | + 'primaryIndicator': False, |
| 567 | + 'professionalDesignation': '', |
| 568 | + 'suffix': 'SR', |
| 569 | + 'nameIdentifier': '00003'}, |
| 570 | + 'action': 'unordered_iterable_item_added'}, |
| 571 | + {'path': ['individualNames', 1], |
| 572 | + 'value': {'firstName': 'John', |
| 573 | + 'lastName': 'Doe', |
| 574 | + 'prefix': '', |
| 575 | + 'middleName': '', |
| 576 | + 'primaryIndicator': False, |
| 577 | + 'professionalDesignation': '', |
| 578 | + 'suffix': 'SR', |
| 579 | + 'nameIdentifier': '00002'}, |
| 580 | + 'action': 'unordered_iterable_item_removed'}] |
| 581 | + |
| 582 | + preserved_flat_dict_list = copy.deepcopy(flat_dict_list) # Use this later for assert comparison |
| 583 | + |
| 584 | + # Now use the flat_dict_list to instantiate a new delta... |
| 585 | + delta = Delta(flat_dict_list=flat_dict_list, |
| 586 | + always_include_values=True, bidirectional=True, raise_errors=True) |
| 587 | + |
| 588 | + # if the flat_dict_list is (unexpectedly) mutated, it will be missing the list index number on the path value. |
| 589 | + old_mutated_list_missing_indexes_on_path = [{'path': ['individualNames'], |
| 590 | + 'value': {'firstName': 'Johnny', |
| 591 | + 'lastName': 'Doe', |
| 592 | + 'prefix': '', |
| 593 | + 'middleName': 'A', |
| 594 | + 'primaryIndicator': False, |
| 595 | + 'professionalDesignation': '', |
| 596 | + 'suffix': 'SR', |
| 597 | + 'nameIdentifier': '00003'}, |
| 598 | + 'action': 'unordered_iterable_item_added'}, |
| 599 | + {'path': ['individualNames'], |
| 600 | + 'value': {'firstName': 'John', |
| 601 | + 'lastName': 'Doe', |
| 602 | + 'prefix': '', |
| 603 | + 'middleName': '', |
| 604 | + 'primaryIndicator': False, |
| 605 | + 'professionalDesignation': '', |
| 606 | + 'suffix': 'SR', |
| 607 | + 'nameIdentifier': '00002'}, |
| 608 | + 'action': 'unordered_iterable_item_removed'}] |
| 609 | + |
| 610 | + # Verify that our fix in the delta constructor worked... |
| 611 | + assert flat_dict_list != old_mutated_list_missing_indexes_on_path |
| 612 | + assert flat_dict_list == preserved_flat_dict_list |
| 613 | + |
464 | 614 |
|
465 | 615 | picklalbe_obj_without_item = PicklableClass(11)
|
466 | 616 | del picklalbe_obj_without_item.item
|
|
0 commit comments