Skip to content

Commit

Permalink
Merge pull request #17 from wwkimball/development
Browse files Browse the repository at this point in the history
Release 1.2.1
  • Loading branch information
wwkimball committed May 13, 2019
2 parents 3b2b46e + 06ed54e commit 21b80eb
Show file tree
Hide file tree
Showing 13 changed files with 274 additions and 100 deletions.
23 changes: 23 additions & 0 deletions CHANGES
@@ -1,3 +1,26 @@
1.2.1
Enhancements:
* Some exception/error messages have been updated to print the entire original
-- albeit parsed -- YAML Path in addition to the present segment under
evaluation.

Bug Fixes:
* yaml-get version 1.0.2 now converts new-lines into "\n" character sequences
when writing output so that multi-line values remain one-result-per-line.
* Use of escape symbols for unusual characters (where demarcation would usually
be more intuitive) is now preserved. Thus, these two search phrases are now
identical:
array[.%" can't "]
array[.%\ can\'t\ ]
* The issue preventing some YAML Paths from being printable after parsing has
been fixed. Valid, parsed YAML Paths now correctly print into a re-parsable
form even with weird sequences and escapes. Note that superfluous whitespace
and other symbols are still removed or escaped when the YAML Path is printed,
so:
term [ key == "Superfluous spaces aren\'t kept." ]
correctly parses and prints as:
term[key=Superfluous\ spaces\ aren\'t\ kept.]

1.2.0
Enhancements:
* A new search operator, :, now enables capturing slices of Arrays (by 0-based
Expand Down
4 changes: 2 additions & 2 deletions bin/yaml-get
Expand Up @@ -31,7 +31,7 @@ from yamlpath.eyaml import EYAMLPath
from yamlpath.wrappers import ConsolePrinter

# Implied Constants
MY_VERSION = "1.0.1"
MY_VERSION = "1.0.2"

def processcli():
"""Process command-line arguments."""
Expand Down Expand Up @@ -169,7 +169,7 @@ def main():
if isinstance(node, list) or isinstance(node, dict):
print(json.dumps(node))
else:
print("{}".format(node))
print("{}".format(str(node).replace("\n", r"\n")))

if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="yamlpath",
version="1.2.0",
version="1.2.1",
description="Generally-useful YAML and EYAML tools employing a human-friendly YAML Path",
long_description=long_description,
long_description_content_type="text/markdown",
Expand Down
74 changes: 62 additions & 12 deletions tests/test_parser.py
Expand Up @@ -6,7 +6,7 @@
from yamlpath.parser import Parser
from yamlpath.exceptions import YAMLPathException
from yamlpath.wrappers import ConsolePrinter
from yamlpath.enums import PathSeperators
from yamlpath.enums import PathSeperators, PathSegmentTypes

@pytest.fixture
def parser():
Expand All @@ -19,6 +19,7 @@ def test_empty_str_path(parser):
assert parser.str_path("") == ""

@pytest.mark.parametrize("yaml_path,stringified", [
("&topArrayAnchor[0]", "&topArrayAnchor[0]"),
("aliases[&anchor]", "aliases[&anchor]"),
("a l i a s e s [ & a n c h o r ]", "aliases[&anchor]"),
("aliases[2]", "aliases[2]"),
Expand All @@ -27,6 +28,11 @@ def test_empty_str_path(parser):
("lookup::credentials.backend.database.password.hash", "lookup::credentials.backend.database.password.hash"),
("does::not[7].exist[4]", "does::not[7].exist[4]"),
("messy.messy.'dotted.sub.key'.child", r"messy.messy.dotted\.sub\.key.child"),
])
def test_happy_str_path_translations_simple(parser, yaml_path, stringified):
assert parser.str_path(yaml_path) == stringified

@pytest.mark.parametrize("yaml_path,stringified", [
('some[search="Name Here"]', r"some[search=Name\ Here]"),
('some[search=="Name Here"]', r"some[search=Name\ Here]"),
('some[search^"Name "]', r"some[search^Name\ ]"),
Expand Down Expand Up @@ -62,25 +68,32 @@ def test_empty_str_path(parser):
('some[!search < 42]', "some[search!<42]"),
('some[!search >= 5280]', "some[search!>=5280]"),
('some[!search <= 14000]', "some[search!<=14000]"),
])
def test_happy_str_path_translations_simple_searches(parser, yaml_path, stringified):
assert parser.str_path(yaml_path) == stringified

@pytest.mark.parametrize("yaml_path,stringified", [
(r'some[search =~ /^\d{5}$/]', r'some[search=~/^\d{5}$/]'),
])
def test_happy_str_path_translations_regex_searches(parser, yaml_path, stringified):
assert parser.str_path(yaml_path) == stringified

@pytest.mark.parametrize("yaml_path,stringified", [
('"aliases[&some_name]"', r'aliases\[\&some_name\]'),
('&topArrayAnchor[0]', '&topArrayAnchor[0]'),
('"&topArrayAnchor[0]"', r'\&topArrayAnchor\[0\]'),
('"&subHashAnchor.child1.attr_tst"', r'\&subHashAnchor\.child1\.attr_tst'),
("'&topArrayAnchor[!.=~/[Oo]riginal/]'", r"\&topArrayAnchor\[!\.=~/\[Oo\]riginal/\]"),
])
def test_happy_str_path_translations(parser, yaml_path, stringified):
def test_happy_str_path_translations_bad_quotes(parser, yaml_path, stringified):
assert parser.str_path(yaml_path) == stringified

@pytest.mark.parametrize("yaml_path,stringified", [
("aliases[.%' ']", r"aliases[.%\ ]"),
(r"aliases[.%\ ]", r"aliases[.%\ ]"),
])
def test_happy_str_path_translations_weird_escapes(parser, yaml_path, stringified):
assert parser.str_path(yaml_path) == stringified

# This will be a KNOWN ISSUE for this release. The fix for this may require a
# deep rethink of the Parser class. The issue here is that escaped characters
# in YAML Paths work perfectly well, but they can't be printed back to the
# screen in their pre-parsed form. So, when a user submits a YAML Path of
# "some\\escaped\\key", all printed forms of the key will become
# "someescapedkey" even though the path WILL find the requested data. This is
# only a stringification (printing) anomoly and hense, it will be LOW PRIORITY,
# tracked as a KNOWN ISSUE, for now.
@pytest.mark.xfail
@pytest.mark.parametrize("yaml_path,stringified", [
('key\\with\\slashes', 'key\\with\\slashes'),
])
Expand Down Expand Up @@ -154,3 +167,40 @@ def test_pretyped_pathsep(pathsep, compare):
def test_bad_pathsep():
with pytest.raises(YAMLPathException):
_ = Parser(None, pathsep="no such seperator!")

@pytest.mark.parametrize("yaml_path,strip_escapes", [
("some.hash.key", True),
("some.hash.key", False),
("/some/hash/key", True),
("/some/hash/key", False),
])
def test_repeat_parsings(parser, yaml_path, strip_escapes):
orig = parser._parse_path(yaml_path, strip_escapes)
comp = parser._parse_path(yaml_path, strip_escapes)
assert orig == comp

def test_no_dict_parsings(parser):
with pytest.raises(YAMLPathException):
_ = parser._parse_path({})

def test_list_to_deque_parsing(parser):
parsed_list = [
(PathSegmentTypes.KEY, 'aliases'),
(PathSegmentTypes.ANCHOR, 'secretIdentity')
]
verify_queue = deque([
(PathSegmentTypes.KEY, 'aliases'),
(PathSegmentTypes.ANCHOR, 'secretIdentity')
])
assert verify_queue == parser._parse_path(parsed_list)

def test_deque_to_deque_parsings(parser):
verify_queue = deque([
(PathSegmentTypes.KEY, 'aliases'),
(PathSegmentTypes.ANCHOR, 'secretIdentity')
])
assert verify_queue == parser._parse_path(verify_queue)

def test_none_to_empty_deque_parsings(parser):
verify_queue = deque()
assert verify_queue == parser._parse_path(None)
24 changes: 16 additions & 8 deletions tests/test_yamlpath.py
Expand Up @@ -10,7 +10,7 @@
from ruamel.yaml.scalarfloat import ScalarFloat
from ruamel.yaml.scalarint import ScalarInt

from yamlpath import YAMLPath
from yamlpath import YAMLPath, Parser
from yamlpath.exceptions import YAMLPathException
from yamlpath.enums import (
YAMLValueFormats,
Expand Down Expand Up @@ -161,11 +161,14 @@
]

@pytest.fixture
def yamlpath():
"""Returns a YAMLPath with a quiet logger."""
def quiet_logger():
args = SimpleNamespace(verbose=False, quiet=True, debug=False)
logger = ConsolePrinter(args)
return YAMLPath(logger)
return ConsolePrinter(args)

@pytest.fixture
def yamlpath(quiet_logger):
"""Returns a YAMLPath with a quiet logger."""
return YAMLPath(quiet_logger)

@pytest.fixture
def yamldata():
Expand Down Expand Up @@ -525,7 +528,7 @@ def test_nonexistant_path_segment_types(yamlpath, yamldata):
PathSegmentTypes = Enum('PathSegmentTypes', names)

with pytest.raises(NotImplementedError):
for _ in yamlpath._get_elements_by_ref(yamldata, (PathSegmentTypes.DNF, False)):
for _ in yamlpath._get_elements_by_ref(yamldata, (PathSegmentTypes.DNF, ("", False, False))):
pass

@pytest.mark.parametrize("sep,val", [
Expand All @@ -549,12 +552,12 @@ def test_append_list_element_value_error(yamlpath):

def test_get_elements_by_bad_ref(yamlpath, yamldata):
with pytest.raises(YAMLPathException):
for _ in yamlpath._get_elements_by_ref(yamldata, (PathSegmentTypes.INDEX, "4F")):
for _ in yamlpath._get_elements_by_ref(yamldata, (PathSegmentTypes.INDEX, ("bad_index[4F]", "4F", "4F"))):
pass

def test_get_elements_by_none_refs(yamlpath, yamldata):
tally = 0
for _ in yamlpath._get_elements_by_ref(None, (PathSegmentTypes.INDEX, "4F")):
for _ in yamlpath._get_elements_by_ref(None, (PathSegmentTypes.INDEX, ("bad_index[4F]", "4F", "4F"))):
tally += 1

for _ in yamlpath._get_elements_by_ref(yamldata, None):
Expand Down Expand Up @@ -582,3 +585,8 @@ def test_yamlpath_exception():
raise YAMLPathException("meh", "/some/path", "/some")
except YAMLPathException as ex:
_ = str(ex)

def test_premade_parser(quiet_logger):
premade = Parser(quiet_logger)
preload = YAMLPath(quiet_logger, parser=premade)
assert preload.parser == premade
1 change: 1 addition & 0 deletions yamlpath/__init__.py
Expand Up @@ -2,3 +2,4 @@
import yamlpath.enums
import yamlpath
from yamlpath.yamlpath import YAMLPath
from yamlpath.parser import Parser
5 changes: 4 additions & 1 deletion yamlpath/enums/pathsearchmethods.py
@@ -1,4 +1,7 @@
"""Implements the PathSearchMethods enumeration."""
"""Implements the PathSearchMethods enumeration.
Copyright 2019 William W. Kimball, Jr. MBA MSIS
"""
from enum import Enum, auto


Expand Down
5 changes: 4 additions & 1 deletion yamlpath/enums/pathsegmenttypes.py
@@ -1,4 +1,7 @@
"""Implements the PathSegmentTypes enumeration."""
"""Implements the PathSegmentTypes enumeration.
Copyright 2019 William W. Kimball, Jr. MBA MSIS
"""
from enum import Enum, auto


Expand Down
5 changes: 4 additions & 1 deletion yamlpath/enums/pathseperators.py
@@ -1,4 +1,7 @@
"""Implements the PathSeperators enumeration."""
"""Implements the PathSeperators enumeration.
Copyright 2019 William W. Kimball, Jr. MBA MSIS
"""
from enum import Enum, auto


Expand Down
5 changes: 4 additions & 1 deletion yamlpath/enums/yamlvalueformats.py
@@ -1,4 +1,7 @@
"""Implements the YAMLValueFormats enumeration."""
"""Implements the YAMLValueFormats enumeration.
Copyright 2019 William W. Kimball, Jr. MBA MSIS
"""
from enum import Enum, auto


Expand Down
6 changes: 6 additions & 0 deletions yamlpath/exceptions/eyamlcommand.py
@@ -1,2 +1,8 @@
"""Represents an exception that occurs during an EYAML command execution.
Copyright 2019 William W. Kimball, Jr. MBA MSIS
"""


class EYAMLCommandException(Exception):
pass

0 comments on commit 21b80eb

Please sign in to comment.