Skip to content

Commit

Permalink
Prep v3.6.6 (#183)
Browse files Browse the repository at this point in the history
  • Loading branch information
wwkimball authored Sep 27, 2022
1 parent 9724781 commit 6bd0188
Show file tree
Hide file tree
Showing 18 changed files with 740 additions and 54 deletions.
20 changes: 20 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
3.6.6
Enhancements:
* Support ruamel.yaml up to version 0.17.21.

Bug Fixes:
* YAML timestamp values could not be created via yamlpath tools or its library,
per http://yaml.org/type/timestamp.html.
* CAUTION 1: ruamel.yaml seems to force all timestamps to UTC while it loads
YAML/JSON/Compatible data. So, when the timestamp value contains time-zone
data, it will be stripped off after it is applied to the timestamp value.
The yamlpath library reverses this when emitting the affected values but if
you attempt to load the timestamp values directly in the DOM, you'll end up
with the UTC-converted value, stripped of any time-zone specification. If
you need the original, unmodified data as a time-zone aware
datetime.datetime value, pass it through the helper method,
Nodes.get_timestamp_with_tzinfo(data: AnchoredTimeStamp).
* CAUTION 2: In order to support timestamp parsing, this project now depends
on the python-dateutil library. Be sure to install it!
Thanks again go to https://github.com/AndydeCleyre!

3.6.5
Bug Fixes:
* When using EYAML with block formatted values on Windows, the block formatting
Expand Down
43 changes: 23 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ expressions:
5. `aliases[.%string]` (search for any elements containing "string")
6. `aliases[.$value]` (search for any elements ending with "value")
7. `aliases[.=~/^(\b[Ss][a-z]+\s){2}[a-z]+$/]` (search for any elements matching
a complex Regular Expression, which happens to match the example)
a complex Python Regular Expression, which happens to match the example)
8. `/aliases[0]` (same as 1 but in forward-slash notation)
9. `/aliases/0` (same as 2 but in forward-slash notation)
10. `/aliases[&first_anchor]` (same as 3 but in forward-slash notation)
Expand Down Expand Up @@ -188,7 +188,8 @@ YAML Path understands these segment types:
* Greater Than match: `hash[access_level>0]`
* Less Than or Equal match: `hash[access_level<=100]`
* Greater Than or Equal match: `hash[access_level>=0]`
* Regular Expression matches: `hash[access_level=~/^\D+$/]` (the `/` Regular
* [Python Regular Expression](https://docs.python.org/3/library/re.html)
matches: `hash[access_level=~/^\D+$/]` (the `/` Regular
Expression delimiter can be substituted for any character you need, except
white-space; note that `/` does not interfere with forward-slash notation
*and it does not need to be escaped* because the entire search expression is
Expand Down Expand Up @@ -272,10 +273,11 @@ all -- came pre-installed. It is generally safe to have more than one version
of Python on your system at the same time, especially when using
[virtual Python environments](https://docs.python.org/3/library/venv.html).

*yamlpath* depends on *ruamel.yaml* (derived from and greatly extending PyYAML).
When using OS-native packages or `pip`, you do not need to pre-install
*ruamel.yaml* except under extraordinary circumstances like using very old
versions of `pip` or its own dependency, *setuptools*.
*yamlpath* depends on *ruamel.yaml* (derived from and greatly extending PyYAML)
and *python-dateutil*. When using OS-native packages or `pip`, you do not need
to pre-install these libraries yourself except under extraordinary
circumstances like using very old versions of `pip` or its own dependency,
*setuptools*.

### Using pip

Expand All @@ -292,8 +294,8 @@ with Python. It is your responsibility to keep `pip` and *setuptools*
up-to-date. When `pip` or *setuptools* become outdated, _you will experience
errors_ when trying to install newer Python packages like *yamlpath* **unless
you preinstall such packages' dependencies**. In the case of *yamlpath*, this
means you'd need to preinstall *ruamel.yaml* if you cannot or choose not to
upgrade `pip` and/or *setuptools*.
means you'd need to preinstall *ruamel.yaml* and *python-dateutil* if you
cannot or choose not to upgrade `pip` and/or *setuptools*.

As long as your `pip` and *setuptools* are up-to-date, installing *yamlpath* is
as simple as a single command (the "3.7" suffix to the `pip` command is
Expand All @@ -309,9 +311,9 @@ Very old versions of Python 3 ship with seriously outdated versions of `pip` and
its *setuptools* dependency. When using versions of `pip` older than **18.1**
or *setuptools* older than version **46.4.0**, you will not be able to install
*yamlpath* with a single command. In this case, you have two options: either
pre-install *ruamel.yaml* before installing *yamlpath* or update `pip` and/or
*setuptools* to at least the minimum required versions so `pip` can
auto-determine and install dependencies. This issue is not unique to
pre-install *ruamel.yaml* and *python-dateutil* before installing *yamlpath* or
update `pip` and/or *setuptools* to at least the minimum required versions so
`pip` can auto-determine and install dependencies. This issue is not unique to
*yamlpath*.

Upgrading `pip` and *setuptools* is trivially simple as long as you have
Expand All @@ -328,21 +330,22 @@ pip3.7 install --upgrade setuptools
```

When you cannot or will not update `pip` or *setuptools*, just pre-install
*ruamel.yaml* before yamlpath. Each must be installed seperately and in order,
like this (you **cannot** combine these installations into a single command):
*ruamel.yaml* and *python-dateutil* before yamlpath. Each must be installed
seperately and in order, like this (you **cannot** combine these installations
into a single command):

```shell
pip3.7 install ruamel.yaml
pip3.7 install ruamel.yaml python-dateutil
pip3.7 install yamlpath
```

The downside to choosing this manual installation path is that you may end up
with an incompatible version of *ruamel.yaml*. This will manifest either as an
inability to install *yamlpath* at all, or only certain versions of *yamlpath*,
or *yamlpath* may experience unexpected errors caused by the incompatible code.
For the best experience, you are strongly encouraged to just keep `pip` and
*setuptools* up-to-date, particularly as a routine part of installing any new
Python packages.
with an incompatible version of *ruamel.yaml* or *python-dateutil*. This will
manifest either as an inability to install *yamlpath* at all, or only certain
versions of *yamlpath*, or *yamlpath* may experience unexpected errors caused
by the incompatible code. For the best experience, you are strongly encouraged
to just keep `pip` and *setuptools* up-to-date, particularly as a routine part
of installing any new Python packages.

### Installing EYAML (Optional)

Expand Down
6 changes: 6 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ warn_unused_configs = True

[mypy-ruamel.*]
ignore_missing_imports = True

[mypy-dateutil.*]
ignore_missing_imports = True

[mypy-yamlpath.patches.*]
ignore_missing_imports = True
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
},
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.17",
"ruamel.yaml>=0.15.96,!=0.17.0,!=0.17.1,!=0.17.2,!=0.17.5,!=0.17.18,<=0.17.21",
"python-dateutil<=3"
],
tests_require=[
"pytest",
Expand Down
4 changes: 3 additions & 1 deletion tests/test_commands_yaml_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,13 @@ def test_get_every_data_type(self, script_runner, tmp_path_factory):
nothingthing:
emptystring: ""
nullstring: "null"
datething: 2022-09-23
timestampthing: 2022-09-24T14:13:12-7:30
"""

# Note that true nulls are translated as "\x00" (hexadecimal NULL
# control-characters).
results = ["6", "6.8", "yes", "no", "True", "False", "\x00", "\x00", "", "null"]
results = ["6", "6.8", "yes", "no", "True", "False", "\x00", "\x00", "", "null", "2022-09-23", "2022-09-24T14:13:12-07:30"]

yaml_file = create_temp_yaml_file(tmp_path_factory, content)
result = script_runner.run(self.command, "--query=*", yaml_file)
Expand Down
52 changes: 44 additions & 8 deletions tests/test_common_nodes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
import pytest

import ruamel.yaml as ry
from datetime import date, datetime
from types import SimpleNamespace

from ruamel.yaml.comments import CommentedSeq, CommentedMap, TaggedScalar
from ruamel.yaml.scalarstring import PlainScalarString
from ruamel.yaml.scalarbool import ScalarBoolean
from ruamel.yaml.scalarfloat import ScalarFloat
from ruamel.yaml.scalarint import ScalarInt
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.enums import YAMLValueFormats
from yamlpath.common import Nodes
Expand All @@ -18,7 +36,7 @@ def test_list_to_str(self):
assert "[]" == Nodes.make_new_node("", "[]", YAMLValueFormats.DEFAULT)

def test_anchored_string(self):
node = ry.scalarstring.PlainScalarString("value")
node = PlainScalarString("value")
node.yaml_set_anchor("anchored")
new_node = Nodes.make_new_node(node, "new", YAMLValueFormats.DEFAULT)
assert new_node.anchor.value == node.anchor.value
Expand All @@ -29,24 +47,24 @@ def test_anchored_string(self):
###
def test_tag_map(self):
new_tag = "!something"
old_node = ry.comments.CommentedMap({"key": "value"})
old_node = CommentedMap({"key": "value"})
new_node = Nodes.apply_yaml_tag(old_node, new_tag)
assert new_node.tag.value == new_tag

def test_update_tag(self):
old_tag = "!tagged"
new_tag = "!changed"
old_node = ry.scalarstring.PlainScalarString("tagged value")
tagged_node = ry.comments.TaggedScalar(old_node, tag=old_tag)
old_node = PlainScalarString("tagged value")
tagged_node = TaggedScalar(old_node, tag=old_tag)
new_node = Nodes.apply_yaml_tag(tagged_node, new_tag)
assert new_node.tag.value == new_tag
assert new_node.value == old_node

def test_delete_tag(self):
old_tag = "!tagged"
new_tag = ""
old_node = ry.scalarstring.PlainScalarString("tagged value")
tagged_node = ry.comments.TaggedScalar(old_node, tag=old_tag)
old_node = PlainScalarString("tagged value")
tagged_node = TaggedScalar(old_node, tag=old_tag)
new_node = Nodes.apply_yaml_tag(tagged_node, new_tag)
assert not hasattr(new_node, "tag")
assert new_node == old_node
Expand All @@ -73,3 +91,21 @@ def test_aoh_is_inconsistent(self):
{"key": "value"},
None
])


###
# wrap_type
###
@pytest.mark.parametrize("value,checktype", [
([], CommentedSeq),
({}, CommentedMap),
("", PlainScalarString),
(1, ScalarInt),
(1.1, ScalarFloat),
(True, ScalarBoolean),
(date(2022, 8, 2), AnchoredDate),
(datetime(2022, 8, 2, 13, 22, 31), AnchoredTimeStamp),
(SimpleNamespace(), SimpleNamespace),
])
def test_wrap_type(self, value, checktype):
assert isinstance(Nodes.wrap_type(value), checktype)
20 changes: 18 additions & 2 deletions tests/test_common_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
import datetime as dt

import ruamel.yaml as ry
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.enums import YAMLValueFormats
from yamlpath.common import Parsers
Expand Down Expand Up @@ -94,7 +106,9 @@ def test_jsonify_complex_ruamel_data(self):
"null": null_node,
"dates": ry.comments.CommentedSeq([
dt.date(2020, 10, 31),
dt.date(2020, 11, 3)
dt.date(2020, 11, 3),
AnchoredDate(2020, 12, 1),
AnchoredTimeStamp(2021, 1, 13, 1, 2, 3)
]),
"t_bool": ry.scalarbool.ScalarBoolean(1),
"f_bool": ry.scalarbool.ScalarBoolean(0)
Expand All @@ -104,11 +118,13 @@ def test_jsonify_complex_ruamel_data(self):
assert jdata["null"] == null_value
assert jdata["dates"][0] == "2020-10-31"
assert jdata["dates"][1] == "2020-11-03"
assert jdata["dates"][2] == "2020-12-01"
assert jdata["dates"][3] == "2021-01-13T01:02:03"
assert jdata["t_bool"] == 1
assert jdata["f_bool"] == 0

jstr = json.dumps(jdata)
assert jstr == """{"tagged": "tagged value", "null": null, "dates": ["2020-10-31", "2020-11-03"], "t_bool": true, "f_bool": false}"""
assert jstr == """{"tagged": "tagged value", "null": null, "dates": ["2020-10-31", "2020-11-03", "2020-12-01", "2021-01-13T01:02:03"], "t_bool": true, "f_bool": false}"""

def test_jsonify_complex_python_data(self):
cdata = {
Expand Down
16 changes: 16 additions & 0 deletions tests/test_enums_yamlvalueformats.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
from datetime import datetime, date

from ruamel.yaml.scalarstring import (
PlainScalarString,
Expand All @@ -10,6 +11,15 @@
from ruamel.yaml.scalarbool import ScalarBoolean
from ruamel.yaml.scalarfloat import ScalarFloat
from ruamel.yaml.scalarint import ScalarInt
from ruamel.yaml import version_info as ryversion
if ryversion < (0, 17, 22): # pragma: no cover
from yamlpath.patches.timestamp import (
AnchoredTimeStamp,
) # type: ignore
else:
# Temporarily fool MYPY into resolving the future-case imports
from ruamel.yaml.timestamp import TimeStamp as AnchoredTimeStamp
#from ruamel.yaml.timestamp import AnchoredTimeStamp

from yamlpath.enums import YAMLValueFormats

Expand All @@ -20,25 +30,29 @@ def test_get_names(self):
assert YAMLValueFormats.get_names() == [
"BARE",
"BOOLEAN",
"DATE",
"DEFAULT",
"DQUOTE",
"FLOAT",
"FOLDED",
"INT",
"LITERAL",
"SQUOTE",
"TIMESTAMP",
]

@pytest.mark.parametrize("input,output", [
("BARE", YAMLValueFormats.BARE),
("BOOLEAN", YAMLValueFormats.BOOLEAN),
("DATE", YAMLValueFormats.DATE),
("DEFAULT", YAMLValueFormats.DEFAULT),
("DQUOTE", YAMLValueFormats.DQUOTE),
("FLOAT", YAMLValueFormats.FLOAT),
("FOLDED", YAMLValueFormats.FOLDED),
("INT", YAMLValueFormats.INT),
("LITERAL", YAMLValueFormats.LITERAL),
("SQUOTE", YAMLValueFormats.SQUOTE),
("TIMESTAMP", YAMLValueFormats.TIMESTAMP),
])
def test_from_str(self, input, output):
assert output == YAMLValueFormats.from_str(input)
Expand All @@ -50,12 +64,14 @@ def test_from_str_nameerror(self):
@pytest.mark.parametrize("input,output", [
(FoldedScalarString(""), YAMLValueFormats.FOLDED),
(LiteralScalarString(""), YAMLValueFormats.LITERAL),
(date(2022, 9, 24), YAMLValueFormats.DATE),
(DoubleQuotedScalarString(''), YAMLValueFormats.DQUOTE),
(SingleQuotedScalarString(""), YAMLValueFormats.SQUOTE),
(PlainScalarString(""), YAMLValueFormats.BARE),
(ScalarBoolean(False), YAMLValueFormats.BOOLEAN),
(ScalarFloat(1.01), YAMLValueFormats.FLOAT),
(ScalarInt(10), YAMLValueFormats.INT),
(AnchoredTimeStamp(2022, 9, 24, 7, 42, 38), YAMLValueFormats.TIMESTAMP),
(None, YAMLValueFormats.DEFAULT),
])
def test_from_node(self, input, output):
Expand Down
3 changes: 1 addition & 2 deletions tests/test_func.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from ruamel.yaml.scalarint import ScalarInt

from yamlpath.enums import AnchorMatches, PathSearchMethods, YAMLValueFormats
from yamlpath.types import PathAttributes
from yamlpath.path import SearchTerms
from yamlpath import YAMLPath
from yamlpath.func import (
Expand All @@ -31,7 +30,7 @@
wrap_type,
)

from tests.conftest import create_temp_yaml_file, quiet_logger
from tests.conftest import create_temp_yaml_file


@pytest.fixture
Expand Down
Loading

0 comments on commit 6bd0188

Please sign in to comment.