|  | 
| 1 | 1 | """DiffSync front-end classes and logic. | 
| 2 | 2 | 
 | 
| 3 |  | -Copyright (c) 2020 Network To Code, LLC <[email protected]> | 
|  | 3 | +Copyright (c) 2020-2021 Network To Code, LLC <[email protected]> | 
| 4 | 4 | 
 | 
| 5 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); | 
| 6 | 6 | you may not use this file except in compliance with the License. | 
|  | 
| 16 | 16 | """ | 
| 17 | 17 | from collections import defaultdict | 
| 18 | 18 | from inspect import isclass | 
| 19 |  | -from typing import ClassVar, Dict, List, Mapping, MutableMapping, Optional, Text, Tuple, Type, Union | 
|  | 19 | +from typing import Callable, ClassVar, Dict, List, Mapping, MutableMapping, Optional, Text, Tuple, Type, Union | 
| 20 | 20 | 
 | 
| 21 | 21 | from pydantic import BaseModel, PrivateAttr | 
| 22 | 22 | import structlog  # type: ignore | 
| @@ -359,7 +359,7 @@ def remove_child(self, child: "DiffSyncModel"): | 
| 359 | 359 | class DiffSync: | 
| 360 | 360 |     """Class for storing a group of DiffSyncModel instances and diffing/synchronizing to another DiffSync instance.""" | 
| 361 | 361 | 
 | 
| 362 |  | -    # Add mapping of names to specific model classes here: | 
|  | 362 | +    # In any subclass, you would add mapping of names to specific model classes here: | 
| 363 | 363 |     # modelname1 = MyModelClass1 | 
| 364 | 364 |     # modelname2 = MyModelClass2 | 
| 365 | 365 | 
 | 
| @@ -418,6 +418,10 @@ def __str__(self): | 
| 418 | 418 |     def __repr__(self): | 
| 419 | 419 |         return f"<{str(self)}>" | 
| 420 | 420 | 
 | 
|  | 421 | +    def __len__(self): | 
|  | 422 | +        """Total number of elements stored in self._data.""" | 
|  | 423 | +        return sum(len(entries) for entries in self._data.values()) | 
|  | 424 | + | 
| 421 | 425 |     def load(self): | 
| 422 | 426 |         """Load all desired data from whatever backend data source into this instance.""" | 
| 423 | 427 |         # No-op in this generic class | 
| @@ -451,29 +455,45 @@ def str(self, indent: int = 0) -> str: | 
| 451 | 455 |     # Synchronization between DiffSync instances | 
| 452 | 456 |     # ------------------------------------------------------------------------------ | 
| 453 | 457 | 
 | 
| 454 |  | -    def sync_from(self, source: "DiffSync", diff_class: Type[Diff] = Diff, flags: DiffSyncFlags = DiffSyncFlags.NONE): | 
|  | 458 | +    def sync_from( | 
|  | 459 | +        self, | 
|  | 460 | +        source: "DiffSync", | 
|  | 461 | +        diff_class: Type[Diff] = Diff, | 
|  | 462 | +        flags: DiffSyncFlags = DiffSyncFlags.NONE, | 
|  | 463 | +        callback: Optional[Callable[[Text, int, int], None]] = None, | 
|  | 464 | +    ): | 
| 455 | 465 |         """Synchronize data from the given source DiffSync object into the current DiffSync object. | 
| 456 | 466 | 
 | 
| 457 | 467 |         Args: | 
| 458 | 468 |             source (DiffSync): object to sync data from into this one | 
| 459 | 469 |             diff_class (class): Diff or subclass thereof to use to calculate the diffs to use for synchronization | 
| 460 | 470 |             flags (DiffSyncFlags): Flags influencing the behavior of this sync. | 
|  | 471 | +            callback (function): Function with parameters (stage, current, total), to be called at intervals as the | 
|  | 472 | +                calculation of the diff and subsequent sync proceed. | 
| 461 | 473 |         """ | 
| 462 |  | -        diff = self.diff_from(source, diff_class=diff_class, flags=flags) | 
| 463 |  | -        syncer = DiffSyncSyncer(diff=diff, src_diffsync=source, dst_diffsync=self, flags=flags) | 
|  | 474 | +        diff = self.diff_from(source, diff_class=diff_class, flags=flags, callback=callback) | 
|  | 475 | +        syncer = DiffSyncSyncer(diff=diff, src_diffsync=source, dst_diffsync=self, flags=flags, callback=callback) | 
| 464 | 476 |         result = syncer.perform_sync() | 
| 465 | 477 |         if result: | 
| 466 | 478 |             self.sync_complete(source, diff, flags, syncer.base_logger) | 
| 467 | 479 | 
 | 
| 468 |  | -    def sync_to(self, target: "DiffSync", diff_class: Type[Diff] = Diff, flags: DiffSyncFlags = DiffSyncFlags.NONE): | 
|  | 480 | +    def sync_to( | 
|  | 481 | +        self, | 
|  | 482 | +        target: "DiffSync", | 
|  | 483 | +        diff_class: Type[Diff] = Diff, | 
|  | 484 | +        flags: DiffSyncFlags = DiffSyncFlags.NONE, | 
|  | 485 | +        callback: Optional[Callable[[Text, int, int], None]] = None, | 
|  | 486 | +    ): | 
| 469 | 487 |         """Synchronize data from the current DiffSync object into the given target DiffSync object. | 
| 470 | 488 | 
 | 
| 471 | 489 |         Args: | 
| 472 | 490 |             target (DiffSync): object to sync data into from this one. | 
| 473 | 491 |             diff_class (class): Diff or subclass thereof to use to calculate the diffs to use for synchronization | 
| 474 | 492 |             flags (DiffSyncFlags): Flags influencing the behavior of this sync. | 
|  | 493 | +            callback (function): Function with parameters (stage, current, total), to be called at intervals as the | 
|  | 494 | +                calculation of the diff and subsequent sync proceed. | 
| 475 | 495 |         """ | 
| 476 |  | -        target.sync_from(self, diff_class=diff_class, flags=flags) | 
|  | 496 | +        target.sync_from(self, diff_class=diff_class, flags=flags, callback=callback) | 
| 477 | 497 | 
 | 
| 478 | 498 |     def sync_complete( | 
| 479 | 499 |         self, | 
| @@ -502,29 +522,43 @@ def sync_complete( | 
| 502 | 522 |     # ------------------------------------------------------------------------------ | 
| 503 | 523 | 
 | 
| 504 | 524 |     def diff_from( | 
| 505 |  | -        self, source: "DiffSync", diff_class: Type[Diff] = Diff, flags: DiffSyncFlags = DiffSyncFlags.NONE | 
|  | 525 | +        self, | 
|  | 526 | +        source: "DiffSync", | 
|  | 527 | +        diff_class: Type[Diff] = Diff, | 
|  | 528 | +        flags: DiffSyncFlags = DiffSyncFlags.NONE, | 
|  | 529 | +        callback: Optional[Callable[[Text, int, int], None]] = None, | 
| 506 | 530 |     ) -> Diff: | 
| 507 | 531 |         """Generate a Diff describing the difference from the other DiffSync to this one. | 
| 508 | 532 | 
 | 
| 509 | 533 |         Args: | 
| 510 | 534 |             source (DiffSync): Object to diff against. | 
| 511 | 535 |             diff_class (class): Diff or subclass thereof to use for diff calculation and storage. | 
| 512 | 536 |             flags (DiffSyncFlags): Flags influencing the behavior of this diff operation. | 
|  | 537 | +            callback (function): Function with parameters (stage, current, total), to be called at intervals as the | 
|  | 538 | +                calculation of the diff proceeds. | 
| 513 | 539 |         """ | 
| 514 |  | -        differ = DiffSyncDiffer(src_diffsync=source, dst_diffsync=self, flags=flags, diff_class=diff_class) | 
|  | 540 | +        differ = DiffSyncDiffer( | 
|  | 541 | +            src_diffsync=source, dst_diffsync=self, flags=flags, diff_class=diff_class, callback=callback | 
|  | 542 | +        ) | 
| 515 | 543 |         return differ.calculate_diffs() | 
| 516 | 544 | 
 | 
| 517 | 545 |     def diff_to( | 
| 518 |  | -        self, target: "DiffSync", diff_class: Type[Diff] = Diff, flags: DiffSyncFlags = DiffSyncFlags.NONE | 
|  | 546 | +        self, | 
|  | 547 | +        target: "DiffSync", | 
|  | 548 | +        diff_class: Type[Diff] = Diff, | 
|  | 549 | +        flags: DiffSyncFlags = DiffSyncFlags.NONE, | 
|  | 550 | +        callback: Optional[Callable[[Text, int, int], None]] = None, | 
| 519 | 551 |     ) -> Diff: | 
| 520 | 552 |         """Generate a Diff describing the difference from this DiffSync to another one. | 
| 521 | 553 | 
 | 
| 522 | 554 |         Args: | 
| 523 | 555 |             target (DiffSync): Object to diff against. | 
| 524 | 556 |             diff_class (class): Diff or subclass thereof to use for diff calculation and storage. | 
| 525 | 557 |             flags (DiffSyncFlags): Flags influencing the behavior of this diff operation. | 
|  | 558 | +            callback (function): Function with parameters (stage, current, total), to be called at intervals as the | 
|  | 559 | +                calculation of the diff proceeds. | 
| 526 | 560 |         """ | 
| 527 |  | -        return target.diff_from(self, diff_class=diff_class, flags=flags) | 
|  | 561 | +        return target.diff_from(self, diff_class=diff_class, flags=flags, callback=callback) | 
| 528 | 562 | 
 | 
| 529 | 563 |     # ------------------------------------------------------------------------------ | 
| 530 | 564 |     # Object Storage Management | 
| @@ -567,21 +601,21 @@ def get( | 
| 567 | 601 |             raise ObjectNotFound(f"{modelname} {uid} not present in {self.name}") | 
| 568 | 602 |         return self._data[modelname][uid] | 
| 569 | 603 | 
 | 
| 570 |  | -    def get_all(self, obj: Union[Text, DiffSyncModel, Type[DiffSyncModel]]): | 
|  | 604 | +    def get_all(self, obj: Union[Text, DiffSyncModel, Type[DiffSyncModel]]) -> List[DiffSyncModel]: | 
| 571 | 605 |         """Get all objects of a given type. | 
| 572 | 606 | 
 | 
| 573 | 607 |         Args: | 
| 574 | 608 |             obj: DiffSyncModel class or instance, or modelname string, that defines the type of the objects to retrieve | 
| 575 | 609 | 
 | 
| 576 | 610 |         Returns: | 
| 577 |  | -            ValuesList[DiffSyncModel]: List of Object | 
|  | 611 | +            List[DiffSyncModel]: List of Object | 
| 578 | 612 |         """ | 
| 579 | 613 |         if isinstance(obj, str): | 
| 580 | 614 |             modelname = obj | 
| 581 | 615 |         else: | 
| 582 | 616 |             modelname = obj.get_type() | 
| 583 | 617 | 
 | 
| 584 |  | -        return self._data[modelname].values() | 
|  | 618 | +        return list(self._data[modelname].values()) | 
| 585 | 619 | 
 | 
| 586 | 620 |     def get_by_uids( | 
| 587 | 621 |         self, uids: List[Text], obj: Union[Text, DiffSyncModel, Type[DiffSyncModel]] | 
|  | 
0 commit comments