|
19 | 19 | import sys
|
20 | 20 |
|
21 | 21 | import nested_diff
|
| 22 | +import nested_diff.handlers |
22 | 23 |
|
23 | 24 |
|
24 | 25 | class App:
|
@@ -220,6 +221,7 @@ def load(self, file_):
|
220 | 221 | fmt = self.args.ifmt
|
221 | 222 |
|
222 | 223 | fmt_opts = self._decode_fmt_opts(self.args.ifmt_opts)
|
| 224 | + |
223 | 225 | return self.get_loader(fmt, **fmt_opts).load(file_)
|
224 | 226 |
|
225 | 227 | @staticmethod
|
@@ -550,15 +552,105 @@ def __init__(self, **kwargs):
|
550 | 552 |
|
551 | 553 | import yaml
|
552 | 554 |
|
| 555 | + from yaml.nodes import ( |
| 556 | + ScalarNode as YamlScalarNode, |
| 557 | + SequenceNode as YamlSequenceNode, |
| 558 | + MappingNode as YamlMappingNode, |
| 559 | + ) |
| 560 | + |
553 | 561 | try:
|
554 | 562 | from yaml import CSafeLoader as YamlLoader
|
555 | 563 | except ImportError:
|
556 | 564 | from yaml import SafeLoader as YamlLoader
|
557 | 565 |
|
| 566 | + self.opts = self.get_opts(kwargs) |
558 | 567 | self.yaml = yaml
|
559 | 568 | self.yaml_loader = YamlLoader
|
560 |
| - self.opts = self.get_opts(kwargs) |
| 569 | + |
| 570 | + def _default_constructor(loader, tag_suffix, node): # noqa: ARG001 |
| 571 | + tag = node.tag |
| 572 | + |
| 573 | + if isinstance(node, YamlScalarNode): |
| 574 | + value = node.value |
| 575 | + elif isinstance(node, YamlSequenceNode): |
| 576 | + node.tag = 'tag:yaml.org,2002:seq' |
| 577 | + value = loader.construct_sequence(node, deep=True) |
| 578 | + elif isinstance(node, YamlMappingNode): |
| 579 | + node.tag = 'tag:yaml.org,2002:map' |
| 580 | + value = loader.construct_mapping(node, deep=True) |
| 581 | + |
| 582 | + return YamlNode(tag, value) |
| 583 | + |
| 584 | + self.yaml_loader.add_multi_constructor(None, _default_constructor) |
561 | 585 |
|
562 | 586 | def decode(self, data):
|
563 | 587 | """Parse YAML string."""
|
564 | 588 | return self.yaml.load(data, Loader=self.yaml_loader, **self.opts)
|
| 589 | + |
| 590 | + |
| 591 | +class YamlNode: |
| 592 | + """Wrapper to represent YAML node.""" |
| 593 | + |
| 594 | + def __init__(self, tag, value): |
| 595 | + """Initialize wrapper. |
| 596 | +
|
| 597 | + Args: |
| 598 | + tag: YAML node tag. |
| 599 | + value: YAML node value. |
| 600 | +
|
| 601 | + """ |
| 602 | + self.tag = tag |
| 603 | + self.value = value |
| 604 | + |
| 605 | + def __repr__(self): |
| 606 | + """Repr for YAML node wrapper.""" |
| 607 | + return f"YamlNode(tag='{self.tag}', value={self.value!r})" |
| 608 | + |
| 609 | + |
| 610 | +class YamlNodeHandler(nested_diff.handlers.TypeHandler): |
| 611 | + """YamlNode handler.""" |
| 612 | + |
| 613 | + extension_id = 'nested_diff.YamlNode' |
| 614 | + handled_type = YamlNode |
| 615 | + |
| 616 | + def diff(self, differ, a, b): |
| 617 | + """Calculate diff for two YamlNode objects. |
| 618 | +
|
| 619 | + Args: |
| 620 | + differ: nested_diff.Differ object. |
| 621 | + a: First node to diff. |
| 622 | + b: Second node to diff. |
| 623 | +
|
| 624 | + Returns: |
| 625 | + Tuple: equality flag and nested diff. |
| 626 | +
|
| 627 | + """ |
| 628 | + equal, _ = differ.diff(a.tag, b.tag) |
| 629 | + |
| 630 | + if not equal: |
| 631 | + return equal, {'N': b, 'O': a, 'E': self.extension_id} |
| 632 | + |
| 633 | + equal, diff = differ.diff(a.value, b.value) |
| 634 | + |
| 635 | + if diff: |
| 636 | + diff = { |
| 637 | + 'D': {'value': diff}, |
| 638 | + 'E': self.extension_id, |
| 639 | + 'tag': a.tag, |
| 640 | + } |
| 641 | + |
| 642 | + return equal, diff |
| 643 | + |
| 644 | + def generate_formatted_diff(self, formatter, diff, depth): |
| 645 | + """Generate formatted YamlNode diff.""" |
| 646 | + if 'D' in diff: |
| 647 | + yield from formatter.generate_string(diff['tag'], 'D', depth) |
| 648 | + yield from formatter.generate_diff(diff['D']['value'], depth + 1) |
| 649 | + |
| 650 | + return |
| 651 | + |
| 652 | + yield from formatter.generate_string(diff['O'].tag, 'O', depth) |
| 653 | + yield from formatter.generate_value(diff['O'].value, 'O', depth + 1) |
| 654 | + |
| 655 | + yield from formatter.generate_string(diff['N'].tag, 'N', depth) |
| 656 | + yield from formatter.generate_value(diff['N'].value, 'N', depth + 1) |
0 commit comments