diff --git a/CHANGES b/CHANGES index 6650cf0..5ec9171 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,9 @@ +3.6.7 +Bug Fixes: +* Release 3.6.6 had a broken package; it was unexpectedly missing the required + ruamel.yaml patch found in yamlpath.patches.timestamp. Thanks to + https://github.com/tsinggggg and https://github.com/kaniblu for reporting it! + 3.6.6 Enhancements: * Support ruamel.yaml up to version 0.17.21. diff --git a/mypy.ini b/mypy.ini index 2f070e5..efb6b58 100644 --- a/mypy.ini +++ b/mypy.ini @@ -7,6 +7,3 @@ ignore_missing_imports = True [mypy-dateutil.*] ignore_missing_imports = True - -[mypy-yamlpath.patches.*] -ignore_missing_imports = True diff --git a/setup.py b/setup.py index 7ad9b5d..5352bb3 100644 --- a/setup.py +++ b/setup.py @@ -43,7 +43,7 @@ }, python_requires=">3.6.0", install_requires=[ - "ruamel.yaml>=0.15.96,!=0.17.0,!=0.17.1,!=0.17.2,!=0.17.5,!=0.17.18,<=0.17.21", + "ruamel.yaml>0.17.5,!=0.17.18,<=0.17.21", "python-dateutil<=3" ], tests_require=[ diff --git a/yamlpath/__init__.py b/yamlpath/__init__.py index 4609b1c..7673109 100644 --- a/yamlpath/__init__.py +++ b/yamlpath/__init__.py @@ -1,6 +1,6 @@ """Core YAML Path classes.""" # Establish the version number common to all components -__version__ = "3.6.6" +__version__ = "3.6.7" from yamlpath.yamlpath import YAMLPath from yamlpath.processor import Processor diff --git a/yamlpath/commands/yaml_get.py b/yamlpath/commands/yaml_get.py index e510559..c899a29 100644 --- a/yamlpath/commands/yaml_get.py +++ b/yamlpath/commands/yaml_get.py @@ -15,19 +15,10 @@ from os.path import isfile from ruamel.yaml.comments import CommentedSet -# pylint: disable=wrong-import-position,ungrouped-imports -from ruamel.yaml import version_info as ryversion -if ryversion < (0, 17, 22): # pragma: no cover - from yamlpath.patches.timestamp import ( - AnchoredTimeStamp, - AnchoredDate, - ) # type: ignore -else: # pragma: no cover - # Temporarily fool MYPY into resolving the future-case imports - from ruamel.yaml.timestamp import TimeStamp as AnchoredTimeStamp - AnchoredDate = AnchoredTimeStamp - #from ruamel.yaml.timestamp import AnchoredTimeStamp - # From whence shall come AnchoredDate? +from yamlpath.patches.timestamp import ( + AnchoredTimeStamp, + AnchoredDate, +) from yamlpath import __version__ as YAMLPATH_VERSION from yamlpath.common import Parsers, Nodes diff --git a/yamlpath/common/nodes.py b/yamlpath/common/nodes.py index 89fbdaa..1586369 100644 --- a/yamlpath/common/nodes.py +++ b/yamlpath/common/nodes.py @@ -21,19 +21,10 @@ FoldedScalarString, LiteralScalarString, ) -# pylint: disable=wrong-import-position,ungrouped-imports -from ruamel.yaml import version_info as ryversion -if ryversion < (0, 17, 22): # pragma: no cover - from yamlpath.patches.timestamp import ( - AnchoredTimeStamp, - AnchoredDate, - ) # type: ignore -else: # pragma: no cover - # Temporarily fool MYPY into resolving the future-case imports - from ruamel.yaml.timestamp import TimeStamp as AnchoredTimeStamp - AnchoredDate = AnchoredTimeStamp - #from ruamel.yaml.timestamp import AnchoredTimeStamp - # From whence shall come AnchoredDate? +from yamlpath.patches.timestamp import ( + AnchoredTimeStamp, + AnchoredDate, +) from yamlpath.enums import ( PathSegmentTypes, @@ -41,7 +32,6 @@ ) from yamlpath.wrappers import NodeCoords from yamlpath import YAMLPath -# pylint: enable=wrong-import-position,ungrouped-imports class Nodes: @@ -660,7 +650,7 @@ def typed_value(value: str) -> Any: return typed_value @staticmethod - def get_timestamp_with_tzinfo(data: AnchoredTimeStamp) -> datetime: + def get_timestamp_with_tzinfo(data: AnchoredTimeStamp) -> Any: """ Get an AnchoredTimeStamp with time-zone info correctly applied. @@ -675,7 +665,9 @@ def get_timestamp_with_tzinfo(data: AnchoredTimeStamp) -> datetime: Parameters: 1. value (AnchoredTimeStamp) the value to correct - Returns: (datetime) time-zone aware non-pre-calculated value + Returns: One of: + * (datetime) time-zone aware non-pre-calculated value + * (AnchoredTimeStamp) original value when it had no time-zone data """ # As stated in the method comments, ruamel.yaml hides the time-zone # details in a private dict after forcibly normalizing the datetime; @@ -697,6 +689,6 @@ def get_timestamp_with_tzinfo(data: AnchoredTimeStamp) -> datetime: sign = -1 if sign_mark == '-' else 1 tdelta = timedelta(hours=int(hours), minutes=int(minutes)) tzinfo = timezone(sign * tdelta) - data = (data + tdelta * sign).replace( - tzinfo=tzinfo) - return data # type: ignore + return ((data + tdelta * sign).replace( + tzinfo=tzinfo)) + return data diff --git a/yamlpath/common/parsers.py b/yamlpath/common/parsers.py index c61ca9a..f6fcf46 100644 --- a/yamlpath/common/parsers.py +++ b/yamlpath/common/parsers.py @@ -19,28 +19,14 @@ from ruamel.yaml.comments import ( CommentedMap, CommentedSet, CommentedSeq, TaggedScalar ) -# pylint: disable=wrong-import-position,ungrouped-imports -from ruamel.yaml import version_info as ryversion -if ryversion < (0, 17, 22): # pragma: no cover - from yamlpath.patches.timestamp import ( - AnchoredTimeStamp, - AnchoredDate, - ) # type: ignore -else: # pragma: no cover - # Temporarily fool MYPY into resolving the future-case imports - from ruamel.yaml.timestamp import TimeStamp as AnchoredTimeStamp - AnchoredDate = AnchoredTimeStamp - #from ruamel.yaml.timestamp import AnchoredTimeStamp - # From whence shall come AnchoredDate? +from yamlpath.patches.timestamp import ( + AnchoredTimeStamp, + AnchoredDate, +) from yamlpath.wrappers import ConsolePrinter from yamlpath.common import Nodes -if ruamel.yaml.version_info < (0, 17, 5): # pragma: no cover - from yamlpath.patches.aliasstyle import MySerializer # type: ignore - from yamlpath.patches.aliasstyle import MyEmitter # type: ignore -# pylint: enable=wrong-import-position,ungrouped-imports - class Parsers: """Helper methods for common YAML/JSON/Compatible parser operations.""" @@ -76,12 +62,6 @@ def get_yaml_editor(**kwargs: Any) -> YAML: # these valid assignments cannot be type-checked. yaml = YAML() - # Import Anthon's patch for Aliased entries in Unordered Sets per - # https://sourceforge.net/p/ruamel-yaml/tickets/384/ - if ruamel.yaml.version_info < (0, 17, 5): # pragma: no cover - yaml.Serializer = MySerializer # type: ignore - yaml.Emitter = MyEmitter # type: ignore - yaml.indent(mapping=2, sequence=4, offset=2) yaml.explicit_start = explicit_start # type: ignore yaml.preserve_quotes = preserve_quotes # type: ignore diff --git a/yamlpath/enums/yamlvalueformats.py b/yamlpath/enums/yamlvalueformats.py index ef4be5a..9c9e19c 100644 --- a/yamlpath/enums/yamlvalueformats.py +++ b/yamlpath/enums/yamlvalueformats.py @@ -17,20 +17,10 @@ from ruamel.yaml.scalarbool import ScalarBoolean from ruamel.yaml.scalarfloat import ScalarFloat from ruamel.yaml.scalarint import ScalarInt -# pylint: disable=wrong-import-position,ungrouped-imports -from ruamel.yaml import version_info as ryversion -if ryversion < (0, 17, 22): # pragma: no cover - from yamlpath.patches.timestamp import ( - AnchoredTimeStamp, - AnchoredDate, - ) # type: ignore -else: # pragma: no cover - # Temporarily fool MYPY into resolving the future-case imports - from ruamel.yaml.timestamp import TimeStamp as AnchoredTimeStamp - AnchoredDate = AnchoredTimeStamp - #from ruamel.yaml.timestamp import AnchoredTimeStamp - # From whence shall come AnchoredDate? -# pylint: enable=wrong-import-position,ungrouped-imports +from yamlpath.patches.timestamp import ( + AnchoredTimeStamp, + AnchoredDate, +) class YAMLValueFormats(Enum): diff --git a/yamlpath/patches/__init__.py b/yamlpath/patches/__init__.py new file mode 100644 index 0000000..cdffc07 --- /dev/null +++ b/yamlpath/patches/__init__.py @@ -0,0 +1 @@ +"""Include ruamel.yaml patches.""" diff --git a/yamlpath/patches/aliasstyle.py b/yamlpath/patches/aliasstyle.py deleted file mode 100644 index c54b178..0000000 --- a/yamlpath/patches/aliasstyle.py +++ /dev/null @@ -1,253 +0,0 @@ -# pylint: skip-file -# type: ignore -""" -Fix writing unordered sets with aliased entries. - -Source: https://sourceforge.net/p/ruamel-yaml/tickets/384/ -Copyright 2021 Anthon van der Neut, William W. Kimball, Jr. MBA MSIS -""" - -import ruamel.yaml - - -if ruamel.yaml.version_info < (0, 17, 5): - class MyAliasEvent(ruamel.yaml.events.NodeEvent): - """Add a style slot.""" - - __slots__ = ('style') - - def __init__(self, anchor, start_mark=None, end_mark=None, style=None, comment=None, ): - """Initialize.""" - ruamel.yaml.events.NodeEvent.__init__(self, anchor, start_mark, end_mark, comment) - self.style = style - - ruamel.yaml.events.AliasEvent = MyAliasEvent - - def new_init(self, anchor, start_mark=None, end_mark=None, style=None, comment=None, ): - """Override class initializer.""" - ruamel.yaml.events.NodeEvent.__init__(self, anchor, start_mark, end_mark, comment) - self.style = style - - ruamel.yaml.events.AliasEvent.__slots__ = ('style', ) - ruamel.yaml.events.AliasEvent.__init__ = new_init - - class MyEmitter(ruamel.yaml.emitter.Emitter): - """Custom Emitter.""" - - def expect_node(self, root=False, sequence=False, mapping=False, simple_key=False): - """Expect a node.""" - self.root_context = root - self.sequence_context = sequence # not used in PyYAML - self.mapping_context = mapping - self.simple_key_context = simple_key - if isinstance(self.event, (MyAliasEvent, ruamel.yaml.events.AliasEvent)): - self.expect_alias() - elif isinstance(self.event, (ruamel.yaml.events.ScalarEvent, ruamel.yaml.events.CollectionStartEvent)): - if ( - self.process_anchor('&') - and isinstance(self.event, ruamel.yaml.events.ScalarEvent) - and self.sequence_context - ): - self.sequence_context = False - if ( - root - and isinstance(self.event, ruamel.yaml.events.ScalarEvent) - and not self.scalar_after_indicator - ): - self.write_indent() - self.process_tag() - if isinstance(self.event, ruamel.yaml.events.ScalarEvent): - # nprint('@', self.indention, self.no_newline, self.column) - self.expect_scalar() - elif isinstance(self.event, ruamel.yaml.events.SequenceStartEvent): - # nprint('@', self.indention, self.no_newline, self.column) - i2, n2 = self.indention, self.no_newline # NOQA - if self.event.comment: - if self.event.flow_style is False and self.event.comment: - if self.write_post_comment(self.event): - self.indention = False - self.no_newline = True - if self.write_pre_comment(self.event): - self.indention = i2 - self.no_newline = not self.indention - if ( - self.flow_level - or self.canonical - or self.event.flow_style - or self.check_empty_sequence() - ): - self.expect_flow_sequence() - else: - self.expect_block_sequence() - elif isinstance(self.event, ruamel.yaml.events.MappingStartEvent): - if self.event.flow_style is False and self.event.comment: - self.write_post_comment(self.event) - if self.event.comment and self.event.comment[1]: - self.write_pre_comment(self.event) - if ( - self.flow_level - or self.canonical - or self.event.flow_style - or self.check_empty_mapping() - ): - self.expect_flow_mapping(single=self.event.nr_items == 1) - else: - self.expect_block_mapping() - else: - raise ruamel.yaml.emitter.EmitterError( - "expected NodeEvent, but got {}".format(self.event) - ) - - def expect_block_mapping_key(self, first=False): - """Expect block mapping key.""" - if not first and isinstance(self.event, ruamel.yaml.events.MappingEndEvent): - if self.event.comment and self.event.comment[1]: - # final comments from a doc - self.write_pre_comment(self.event) - self.indent = self.indents.pop() - self.state = self.states.pop() - else: - if self.event.comment and self.event.comment[1]: - # final comments from a doc - self.write_pre_comment(self.event) - self.write_indent() - if self.check_simple_key(): - if not isinstance( - self.event, (ruamel.yaml.events.SequenceStartEvent, ruamel.yaml.events.MappingStartEvent) - ): # sequence keys - try: - if self.event.style == '?': - self.write_indicator('?', True, indention=True) - except AttributeError: # aliases have no style - pass - self.states.append(self.expect_block_mapping_simple_value) - self.expect_node(mapping=True, simple_key=True) - if isinstance(self.event, (MyAliasEvent, ruamel.yaml.events.AliasEvent)): - self.stream.write(' ') - else: - self.write_indicator('?', True, indention=True) - self.states.append(self.expect_block_mapping_value) - self.expect_node(mapping=True) - - def check_simple_key(self): - """Check simple keys.""" - length = 0 - if isinstance(self.event, ruamel.yaml.events.NodeEvent) and self.event.anchor is not None: - if self.prepared_anchor is None: - self.prepared_anchor = self.prepare_anchor(self.event.anchor) - length += len(self.prepared_anchor) - if ( - isinstance(self.event, (ruamel.yaml.events.ScalarEvent, ruamel.yaml.events.CollectionStartEvent)) - and self.event.tag is not None - ): - if self.prepared_tag is None: - self.prepared_tag = self.prepare_tag(self.event.tag) - length += len(self.prepared_tag) - if isinstance(self.event, ruamel.yaml.events.ScalarEvent): - if self.analysis is None: - self.analysis = self.analyze_scalar(self.event.value) - length += len(self.analysis.scalar) - return length < self.MAX_SIMPLE_KEY_LENGTH and ( - isinstance(self.event, (MyAliasEvent, ruamel.yaml.events.AliasEvent)) - or (isinstance(self.event, ruamel.yaml.events.SequenceStartEvent) and self.event.flow_style is True) - or (isinstance(self.event, ruamel.yaml.events.MappingStartEvent) and self.event.flow_style is True) - or ( - isinstance(self.event, ruamel.yaml.events.ScalarEvent) - # if there is an explicit style for an empty string, it is a simple key - and not (self.analysis.empty and self.style and self.style not in '\'"') - and not self.analysis.multiline - ) - or self.check_empty_sequence() - or self.check_empty_mapping() - ) - - - class MySerializer(ruamel.yaml.serializer.Serializer): - """Custom Serializer.""" - - def serialize_node(self, node, parent, index): - # type: (Any, Any, Any) -> None - """Serialize node.""" - alias = self.anchors[node] - if node in self.serialized_nodes: - # self.emitter.emit(ruamel.yaml.events.AliasEvent(alias, style=node.style if node.style == '?' else None)) - node_style = getattr(node, 'style', None) - if node_style != '?': - node_style = None - self.emitter.emit(MyAliasEvent(alias, style=node_style)) - else: - self.serialized_nodes[node] = True - self.resolver.descend_resolver(parent, index) - if isinstance(node, ruamel.yaml.nodes.ScalarNode): - # here check if the node.tag equals the one that would result from parsing - # if not equal quoting is necessary for strings - detected_tag = self.resolver.resolve(ruamel.yaml.nodes.ScalarNode, node.value, (True, False)) - default_tag = self.resolver.resolve(ruamel.yaml.nodes.ScalarNode, node.value, (False, True)) - implicit = ( - (node.tag == detected_tag), - (node.tag == default_tag), - node.tag.startswith('tag:yaml.org,2002:'), - ) - self.emitter.emit( - ruamel.yaml.events.ScalarEvent( - alias, - node.tag, - implicit, - node.value, - style=node.style, - comment=node.comment, - ) - ) - elif isinstance(node, ruamel.yaml.nodes.SequenceNode): - implicit = node.tag == self.resolver.resolve(ruamel.yaml.nodes.SequenceNode, node.value, True) - comment = node.comment - end_comment = None - seq_comment = None - if node.flow_style is True: - if comment: # eol comment on flow style sequence - seq_comment = comment[0] - # comment[0] = None - if comment and len(comment) > 2: - end_comment = comment[2] - else: - end_comment = None - self.emitter.emit( - ruamel.yaml.events.SequenceStartEvent( - alias, - node.tag, - implicit, - flow_style=node.flow_style, - comment=node.comment, - ) - ) - index = 0 - for item in node.value: - self.serialize_node(item, node, index) - index += 1 - self.emitter.emit(ruamel.yaml.events.SequenceEndEvent(comment=[seq_comment, end_comment])) - elif isinstance(node, ruamel.yaml.nodes.MappingNode): - implicit = node.tag == self.resolver.resolve(ruamel.yaml.nodes.MappingNode, node.value, True) - comment = node.comment - end_comment = None - map_comment = None - if node.flow_style is True: - if comment: # eol comment on flow style sequence - map_comment = comment[0] - # comment[0] = None - if comment and len(comment) > 2: - end_comment = comment[2] - self.emitter.emit( - ruamel.yaml.events.MappingStartEvent( - alias, - node.tag, - implicit, - flow_style=node.flow_style, - comment=node.comment, - nr_items=len(node.value), - ) - ) - for key, value in node.value: - self.serialize_node(key, node, None) - self.serialize_node(value, node, key) - self.emitter.emit(ruamel.yaml.events.MappingEndEvent(comment=[map_comment, end_comment])) - self.resolver.ascend_resolver() diff --git a/yamlpath/patches/timestamp.py b/yamlpath/patches/timestamp.py index fda9c4a..543b375 100644 --- a/yamlpath/patches/timestamp.py +++ b/yamlpath/patches/timestamp.py @@ -1,134 +1,135 @@ # pylint: skip-file -# type: ignore """ Fix missing anchors from timestamp and date nodes. +This must be removed once incorporated into ruamel.yaml, likely at version +0.17.22. + Source: https://sourceforge.net/p/ruamel-yaml/tickets/440/ -Copyright 2022 Anthon van der Neut +Copyright 2022 Anthon van der Neut, William W. Kimball Jr. MBA MSIS """ import ruamel.yaml -Anchor = ruamel.yaml.anchor.Anchor -TimeStamp = ruamel.yaml.timestamp.TimeStamp +from ruamel.yaml.constructor import ConstructorError +from ruamel.yaml.anchor import Anchor +from ruamel.yaml.timestamp import TimeStamp from typing import Any, Dict, Union # NOQA import datetime import copy -if not hasattr(TimeStamp, 'anchor'): - - class AnchoredTimeStamp(TimeStamp): - """Extend TimeStamp to track YAML Anchors.""" - - def __init__(self, *args: Any, **kw: Any) -> None: - """Initialize a new instance.""" - self._yaml: Dict[Any, Any] = dict(t=False, tz=None, delta=0) - - def __new__(cls, *args: Any, **kw: Any) -> Any: # datetime is immutable - """Create a new, immutable instance.""" - anchor = kw.pop('anchor', None) - ts = TimeStamp.__new__(cls, *args, **kw) - if anchor is not None: - ts.yaml_set_anchor(anchor, always_dump=True) - return ts - - def __deepcopy__(self, memo: Any) -> Any: - """Deeply copy this instance to another.""" - ts = AnchoredTimeStamp(self.year, self.month, self.day, self.hour, self.minute, self.second) - ts._yaml = copy.deepcopy(self._yaml) - return ts - - @property - def anchor(self) -> Any: - """Access the YAML Anchor.""" - if not hasattr(self, Anchor.attrib): - setattr(self, Anchor.attrib, Anchor()) - return getattr(self, Anchor.attrib) - - def yaml_anchor(self, any: bool = False) -> Any: - """Get the YAML Anchor.""" - if not hasattr(self, Anchor.attrib): - return None - if any or self.anchor.always_dump: - return self.anchor +class AnchoredTimeStamp(TimeStamp): + """Extend TimeStamp to track YAML Anchors.""" + + def __init__(self, *args: Any, **kw: Any) -> None: + """Initialize a new instance.""" + self._yaml: Dict[Any, Any] = dict(t=False, tz=None, delta=0) + + def __new__(cls, *args: Any, **kw: Any) -> Any: # datetime is immutable + """Create a new, immutable instance.""" + anchor = kw.pop('anchor', None) + ts = TimeStamp.__new__(cls, *args, **kw) + if anchor is not None: + ts.yaml_set_anchor(anchor, always_dump=True) + return ts + + def __deepcopy__(self, memo: Any) -> Any: + """Deeply copy this instance to another.""" + ts = AnchoredTimeStamp(self.year, self.month, self.day, self.hour, self.minute, self.second) + ts._yaml = copy.deepcopy(self._yaml) + return ts + + @property + def anchor(self) -> Any: + """Access the YAML Anchor.""" + if not hasattr(self, Anchor.attrib): + setattr(self, Anchor.attrib, Anchor()) + return getattr(self, Anchor.attrib) + + def yaml_anchor(self, any: bool = False) -> Any: + """Get the YAML Anchor.""" + if not hasattr(self, Anchor.attrib): return None - - def yaml_set_anchor(self, value: Any, always_dump: bool = False) -> None: - """Set the YAML Anchor.""" - self.anchor.value = value - self.anchor.always_dump = always_dump - - - class AnchoredDate(AnchoredTimeStamp): - """Define AnchoredDate.""" - - pass - - - def construct_anchored_timestamp( - self, node: Any, values: Any = None - ) -> Union[AnchoredTimeStamp, AnchoredDate]: - """Construct an AnchoredTimeStamp.""" - try: - match = self.timestamp_regexp.match(node.value) - except TypeError: - match = None - if match is None: - raise ConstructorError( - None, - None, - f'failed to construct timestamp from "{node.value}"', - node.start_mark, - ) - values = match.groupdict() - dd = ruamel.yaml.util.create_timestamp(**values) # this has delta applied - delta = None - if values['tz_sign']: - tz_hour = int(values['tz_hour']) - minutes = values['tz_minute'] - tz_minute = int(minutes) if minutes else 0 - delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute) - if values['tz_sign'] == '-': - delta = -delta - if isinstance(dd, datetime.datetime): - data = AnchoredTimeStamp( - dd.year, dd.month, dd.day, dd.hour, dd.minute, dd.second, dd.microsecond, anchor=node.anchor - ) - else: - data = AnchoredDate(dd.year, dd.month, dd.day, 0, 0, 0, 0, anchor=node.anchor) - return data - if delta: - data._yaml['delta'] = delta - tz = values['tz_sign'] + values['tz_hour'] - if values['tz_minute']: - tz += ':' + values['tz_minute'] - data._yaml['tz'] = tz - else: - if values['tz']: # no delta - data._yaml['tz'] = values['tz'] - if values['t']: - data._yaml['t'] = True + if any or self.anchor.always_dump: + return self.anchor + return None + + def yaml_set_anchor(self, value: Any, always_dump: bool = False) -> None: + """Set the YAML Anchor.""" + self.anchor.value = value + self.anchor.always_dump = always_dump + + +class AnchoredDate(AnchoredTimeStamp): + """Define AnchoredDate.""" + + pass + + +def construct_anchored_timestamp( + self, node: Any, values: Any = None +) -> Union[AnchoredTimeStamp, AnchoredDate]: + """Construct an AnchoredTimeStamp.""" + try: + match = self.timestamp_regexp.match(node.value) + except TypeError: + match = None + if match is None: + raise ConstructorError( + None, + None, + f'failed to construct timestamp from "{node.value}"', + node.start_mark, + ) + values = match.groupdict() + dd = ruamel.yaml.util.create_timestamp(**values) # this has delta applied + delta = None + if values['tz_sign']: + tz_hour = int(values['tz_hour']) + minutes = values['tz_minute'] + tz_minute = int(minutes) if minutes else 0 + delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute) + if values['tz_sign'] == '-': + delta = -delta + if isinstance(dd, datetime.datetime): + data = AnchoredTimeStamp( + dd.year, dd.month, dd.day, dd.hour, dd.minute, dd.second, dd.microsecond, anchor=node.anchor + ) + else: + data = AnchoredDate(dd.year, dd.month, dd.day, 0, 0, 0, 0, anchor=node.anchor) return data - - ruamel.yaml.constructor.RoundTripConstructor.add_constructor('tag:yaml.org,2002:timestamp', construct_anchored_timestamp) - - def represent_anchored_timestamp(self, data: Any): - """Render an AnchoredTimeStamp.""" - try: - anchor = data.yaml_anchor() - except AttributeError: - anchor = None - inter = 'T' if data._yaml['t'] else ' ' - _yaml = data._yaml - if _yaml['delta']: - data += _yaml['delta'] - if isinstance(data, AnchoredDate): - value = data.date().isoformat() - else: - value = data.isoformat(inter) - if _yaml['tz']: - value += _yaml['tz'] - return self.represent_scalar('tag:yaml.org,2002:timestamp', value, anchor=anchor) - - ruamel.yaml.representer.RoundTripRepresenter.add_representer(AnchoredTimeStamp, represent_anchored_timestamp) - ruamel.yaml.representer.RoundTripRepresenter.add_representer(AnchoredDate, represent_anchored_timestamp) + if delta: + data._yaml['delta'] = delta + tz = values['tz_sign'] + values['tz_hour'] + if values['tz_minute']: + tz += ':' + values['tz_minute'] + data._yaml['tz'] = tz + else: + if values['tz']: # no delta + data._yaml['tz'] = values['tz'] + if values['t']: + data._yaml['t'] = True + return data + +ruamel.yaml.constructor.RoundTripConstructor.add_constructor('tag:yaml.org,2002:timestamp', construct_anchored_timestamp) + +def represent_anchored_timestamp(self, data: Any): + """Render an AnchoredTimeStamp.""" + try: + anchor = data.yaml_anchor() + except AttributeError: + anchor = None + inter = 'T' if data._yaml['t'] else ' ' + _yaml = data._yaml + if _yaml['delta']: + data += _yaml['delta'] + if isinstance(data, AnchoredDate): + value = data.date().isoformat() + else: + value = data.isoformat(inter) + if _yaml['tz']: + value += _yaml['tz'] + return self.represent_scalar('tag:yaml.org,2002:timestamp', value, anchor=anchor) + +ruamel.yaml.representer.RoundTripRepresenter.add_representer(AnchoredTimeStamp, represent_anchored_timestamp) +ruamel.yaml.representer.RoundTripRepresenter.add_representer(AnchoredDate, represent_anchored_timestamp) diff --git a/yamlpath/wrappers/consoleprinter.py b/yamlpath/wrappers/consoleprinter.py index 31cd179..379a7cb 100644 --- a/yamlpath/wrappers/consoleprinter.py +++ b/yamlpath/wrappers/consoleprinter.py @@ -23,22 +23,12 @@ CommentedSet, TaggedScalar ) -# pylint: disable=wrong-import-position,ungrouped-imports -from ruamel.yaml import version_info as ryversion -if ryversion < (0, 17, 22): # pragma: no cover - from yamlpath.patches.timestamp import ( - AnchoredTimeStamp, - AnchoredDate, - ) # type: ignore -else: # pragma: no cover - # Temporarily fool MYPY into resolving the future-case imports - from ruamel.yaml.timestamp import TimeStamp as AnchoredTimeStamp - AnchoredDate = AnchoredTimeStamp - #from ruamel.yaml.timestamp import AnchoredTimeStamp - # From whence shall come AnchoredDate? +from yamlpath.patches.timestamp import ( + AnchoredTimeStamp, + AnchoredDate, +) from yamlpath.wrappers.nodecoords import NodeCoords -# pylint: enable=wrong-import-position,ungrouped-imports class ConsolePrinter: