Skip to content

Commit

Permalink
Merge pull request #61 from wwkimball/development
Browse files Browse the repository at this point in the history
Release 2.3.7
  • Loading branch information
wwkimball authored Aug 28, 2020
2 parents 68ea75c + 92c3277 commit aedfdc4
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 41 deletions.
6 changes: 6 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
2.3.7:
Bug Fixes:
* Setting negative floats could cause the leading "-" symbol to be replaced
with an unexpcted "0" when specifying a float format, or crash when using
the default format.

2.3.6:
Bug Fixes:
* When using yaml-set with --format=folded and --eyamlcrypt, the encrypted
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="yamlpath",
version="2.3.6",
version="2.3.7",
description="Read and change YAML/Compatible data using powerful, intuitive, command-line friendly syntax",
long_description=long_description,
long_description_content_type="text/markdown",
Expand Down
2 changes: 1 addition & 1 deletion tests/test_commands_eyaml_rotate_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def test_bad_recryption_key(self, script_runner, tmp_path_factory, old_eyaml_key
yaml_file
)
assert not result.success, result.stderr
assert "unable to encrypt" in result.stderr
assert "unable to encrypt" in result.stderr or "cannot be run due to exit code: 1" in result.stderr

def test_backup_file(self, script_runner, tmp_path_factory, old_eyaml_keys, new_eyaml_keys):
import os
Expand Down
7 changes: 7 additions & 0 deletions tests/test_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ def test_set_value_in_none_data(self, capsys, quiet_logger):
("aliases[&testAnchor]", "Updated Value", 1, True, YAMLValueFormats.DEFAULT, PathSeperators.AUTO),
(YAMLPath("top_scalar"), "New top-level value", 1, False, YAMLValueFormats.DEFAULT, PathSeperators.DOT),
("/top_array/2", 42, 1, False, YAMLValueFormats.INT, PathSeperators.FSLASH),
("/top_hash/positive_float", 0.009, 1, True, YAMLValueFormats.FLOAT, PathSeperators.FSLASH),
("/top_hash/negative_float", -0.009, 1, True, YAMLValueFormats.FLOAT, PathSeperators.FSLASH),
("/top_hash/positive_float", -2.71828, 1, True, YAMLValueFormats.FLOAT, PathSeperators.FSLASH),
("/top_hash/negative_float", 5283.4, 1, True, YAMLValueFormats.FLOAT, PathSeperators.FSLASH),
])
def test_set_value(self, quiet_logger, yamlpath, value, tally, mustexist, vformat, pathsep):
yamldata = """---
Expand All @@ -172,6 +176,9 @@ def test_set_value(self, quiet_logger, yamlpath, value, tally, mustexist, vforma
- 2
# Comment N
top_scalar: Top-level plain scalar string
top_hash:
positive_float: 3.14159265358
negative_float: -11.034
"""
yaml = YAML()
data = yaml.load(yamldata)
Expand Down
2 changes: 1 addition & 1 deletion yamlpath/commands/yaml_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from yamlpath.wrappers import ConsolePrinter

# Implied Constants
MY_VERSION = "1.0.7"
MY_VERSION = "1.0.8"

def processcli():
"""Process command-line arguments."""
Expand Down
2 changes: 1 addition & 1 deletion yamlpath/exceptions/yamlpathexception.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def __init__(self, user_message: str, yaml_path: str,
self.yaml_path: str = yaml_path
self.segment: Optional[str] = segment

super(YAMLPathException, self).__init__(
super().__init__(
"user_message: {}, yaml_path: {}, segment: {}"
.format(user_message, yaml_path, segment))

Expand Down
10 changes: 7 additions & 3 deletions yamlpath/eyaml/eyamlprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def decrypt_eyaml(self, value: str) -> str:
raise EYAMLCommandException(
"The {} command cannot be run due to exit code: {}"
.format(self.eyaml, ex.returncode)
)
) from ex

# Check for bad decryptions
self.logger.debug(
Expand Down Expand Up @@ -227,9 +227,13 @@ def encrypt_eyaml(self, value: str,
raise EYAMLCommandException(
"The {} command cannot be run due to exit code: {}"
.format(self.eyaml, ex.returncode)
)
) from ex

if not retval:
# While exceedingly rare and difficult to test for, it is possible
# for custom eyaml commands to produce no output. This is a critical
# error in every conceivable case but pycov will never get a test
# that works multi-platform. So, ignore covering this case.
if not retval: # pragma: no cover
raise EYAMLCommandException(
("The {} command was unable to encrypt your value. Please"
+ " verify this process can run that command and read your"
Expand Down
78 changes: 54 additions & 24 deletions yamlpath/func.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ def wrap_type(value: Any) -> Any:
elif typ is int:
wrapped_value = ScalarInt(value)
elif typ is float:
wrapped_value = ScalarFloat(value)
wrapped_value = make_float_node(ast_value)
elif typ is bool:
wrapped_value = ScalarBoolean(value)

Expand Down Expand Up @@ -264,7 +264,45 @@ def clone_node(node: Any) -> Any:
return type(node)(clone_value, anchor=node.anchor.value)
return type(node)(clone_value)

# pylint: disable=locally-disabled,too-many-branches,too-many-statements
def make_float_node(value: float, anchor: str = None):
"""
Create a new ScalarFloat data node from a bare float.
An optional anchor may be attached.
Parameters:
1. value (float) The bare float to wrap.
2. anchor (str) OPTIONAL anchor to add.
Returns: (ScalarNode) The new node
"""
minus_sign = "-" if value < 0.0 else None
strval = format(value, '.15f').rstrip('0').rstrip('.')
precision = 0
width = len(strval)
lastdot = strval.rfind(".")
if -1 < lastdot:
precision = strval.rfind(".")

if anchor is None:
new_node = ScalarFloat(
value,
m_sign=minus_sign,
prec=precision,
width=width
)
else:
new_node = ScalarFloat(
value
, anchor=anchor
, m_sign=minus_sign
, prec=precision
, width=width
)

return new_node

# pylint: disable=locally-disabled,too-many-branches,too-many-statements,too-many-locals
def make_new_node(source_node: Any, value: Any,
value_format: YAMLValueFormats) -> Any:
"""
Expand Down Expand Up @@ -297,14 +335,14 @@ def make_new_node(source_node: Any, value: Any,
strform = str(value_format)
try:
valform = YAMLValueFormats.from_str(strform)
except NameError:
except NameError as wrap_ex:
raise NameError(
"Unknown YAML Value Format: {}".format(strform)
+ ". Please specify one of: "
+ ", ".join(
[l.lower() for l in YAMLValueFormats.get_names()]
)
)
) from wrap_ex

if valform == YAMLValueFormats.BARE:
new_type = PlainScalarString
Expand All @@ -330,43 +368,35 @@ def make_new_node(source_node: Any, value: Any,
elif valform == YAMLValueFormats.FLOAT:
try:
new_value = float(value)
except ValueError:
except ValueError as wrap_ex:
raise ValueError(
("The requested value format is {}, but '{}' cannot be"
+ " cast to a floating-point number.")
.format(valform, value)
)

strval = str(value)
precision = 0
width = len(strval)
lastdot = strval.rfind(".")
if -1 < lastdot:
precision = strval.rfind(".")
) from wrap_ex

anchor_val = None
if hasattr(source_node, "anchor"):
new_node = ScalarFloat(
new_value
, anchor=source_node.anchor.value
, prec=precision
, width=width
)
else:
new_node = ScalarFloat(new_value, prec=precision, width=width)
anchor_val = source_node.anchor.value
new_node = make_float_node(new_value, anchor_val)
elif valform == YAMLValueFormats.INT:
new_type = ScalarInt

try:
new_value = int(value)
except ValueError:
except ValueError as wrap_ex:
raise ValueError(
("The requested value format is {}, but '{}' cannot be"
+ " cast to an integer number.")
.format(valform, value)
)
) from wrap_ex
else:
# Punt to whatever the best type may be
new_type = type(wrap_type(value))
wrapped_value = wrap_type(value)
new_type = type(wrapped_value)
new_format = YAMLValueFormats.from_node(wrapped_value)
if new_format is not YAMLValueFormats.DEFAULT:
new_node = make_new_node(source_node, value, new_format)

if new_node is None:
if hasattr(source_node, "anchor"):
Expand Down
31 changes: 23 additions & 8 deletions yamlpath/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,9 @@ def set_value(self, yaml_path: Union[YAMLPath, str],
self.data, yaml_path, value
):
self.logger.debug(
"Processor::set_value: Matched optional node coord, {}."
.format(node_coord)
("Processor::set_value: Matched optional node coord, {};"
+ " setting its value to {}<{}>.")
.format(node_coord, value, value_format)
)
self._update_node(
node_coord.parent, node_coord.parentref, value,
Expand Down Expand Up @@ -333,13 +334,13 @@ def _get_nodes_by_index(
try:
intmin: int = int(min_match)
intmax: int = int(max_match)
except ValueError:
except ValueError as wrap_ex:
raise YAMLPathException(
"{} is not an integer array slice"
.format(str_stripped),
str(yaml_path),
str(unstripped_attrs)
)
) from wrap_ex

if intmin == intmax and len(data) > intmin:
yield NodeCoords([data[intmin]], data, intmin)
Expand All @@ -353,13 +354,13 @@ def _get_nodes_by_index(
else:
try:
idx: int = int(str_stripped)
except ValueError:
except ValueError as wrap_ex:
raise YAMLPathException(
"{} is not an integer array index"
.format(str_stripped),
str(yaml_path),
str(unstripped_attrs)
)
) from wrap_ex

if isinstance(data, list) and len(data) > idx:
yield NodeCoords(data[idx], data, idx)
Expand Down Expand Up @@ -762,14 +763,14 @@ def _get_optional_nodes(
else:
try:
newidx = int(str(stripped_attrs))
except ValueError:
except ValueError as wrap_ex:
raise YAMLPathException(
("Cannot add non-integer {} subreference"
+ " to lists")
.format(str(segment_type)),
str(yaml_path),
except_segment
)
) from wrap_ex
for _ in range(len(data) - 1, newidx):
next_node = build_next_node(
yaml_path, depth + 1, value
Expand Down Expand Up @@ -884,4 +885,18 @@ def recurse(data, parent, parentref, reference_node, replacement_node):

change_node = parent[parentref]
new_node = make_new_node(change_node, value, value_format)

self.logger.debug(
("Processor::_update_node: Changing the following node of"
+ " type {} to {}<{}> as {}, a {} YAML element:"
).format(type(change_node), value, value_format,
new_node, type(new_node))
)
self.logger.debug(change_node)

recurse(self.data, parent, parentref, change_node, new_node)

self.logger.debug(
"Processor::_update_node: Parent after change:"
)
self.logger.debug(parent)
4 changes: 2 additions & 2 deletions yamlpath/yamlpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,12 +547,12 @@ def _parse_path(self,
):
try:
idx = int(segment_id)
except ValueError:
except ValueError as wrap_ex:
raise YAMLPathException(
"Not an integer index: {}".format(segment_id)
, yaml_path
, segment_id
)
) from wrap_ex
path_segments.append((segment_type, idx))
elif (
segment_type is PathSegmentTypes.SEARCH
Expand Down

0 comments on commit aedfdc4

Please sign in to comment.