Hello community,
here is the log from the commit of package python-strictyaml for
openSUSE:Factory checked in at 2019-10-10 11:53:30
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-strictyaml (Old)
and /work/SRC/openSUSE:Factory/.python-strictyaml.new.2352 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-strictyaml"
Thu Oct 10 11:53:30 2019 rev:3 rq:736798 version:1.0.5
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-strictyaml/python-strictyaml.changes
2019-09-30 15:58:44.201322373 +0200
+++
/work/SRC/openSUSE:Factory/.python-strictyaml.new.2352/python-strictyaml.changes
2019-10-10 11:53:30.847310271 +0200
@@ -1,0 +2,8 @@
+Thu Oct 10 08:20:48 UTC 2019 - Tomáš Chvátal <[email protected]>
+
+- Update to 1.0.5:
+ * BUGFIX : Fixed python 2 bug introduced when fixing #72.
+ * FEATURE : Include tests / stories in package.
+ * BUG: issue #72. Now setitem uses schema.
+
+-------------------------------------------------------------------
Old:
----
strictyaml-1.0.3.tar.gz
New:
----
strictyaml-1.0.5.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-strictyaml.spec ++++++
--- /var/tmp/diff_new_pack.CsSoUF/_old 2019-10-10 11:53:31.415308763 +0200
+++ /var/tmp/diff_new_pack.CsSoUF/_new 2019-10-10 11:53:31.419308753 +0200
@@ -18,11 +18,10 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-strictyaml
-Version: 1.0.3
+Version: 1.0.5
Release: 0
Summary: Strict, typed YAML parser
License: MIT
-Group: Development/Languages/Python
URL: https://hitchdev.com/strictyaml
Source:
https://github.com/crdoconnor/strictyaml/archive/%{version}.tar.gz#/strictyaml-%{version}.tar.gz
BuildRequires: %{python_module setuptools}
++++++ strictyaml-1.0.3.tar.gz -> strictyaml-1.0.5.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/strictyaml-1.0.3/MANIFEST.in
new/strictyaml-1.0.5/MANIFEST.in
--- old/strictyaml-1.0.3/MANIFEST.in 2019-09-12 20:21:25.000000000 +0200
+++ new/strictyaml-1.0.5/MANIFEST.in 2019-10-05 17:01:06.000000000 +0200
@@ -1,3 +1,6 @@
include VERSION
include LICENSE.txt
include README.md
+recursive-include hitch *
+prune hitch/__pycache__
+prune hitch/gen
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/strictyaml-1.0.3/VERSION new/strictyaml-1.0.5/VERSION
--- old/strictyaml-1.0.3/VERSION 2019-09-12 20:21:25.000000000 +0200
+++ new/strictyaml-1.0.5/VERSION 2019-10-05 17:01:06.000000000 +0200
@@ -1 +1 @@
-1.0.3
\ No newline at end of file
+1.0.5
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/strictyaml-1.0.3/hitch/engine.py
new/strictyaml-1.0.5/hitch/engine.py
--- old/strictyaml-1.0.3/hitch/engine.py 2019-09-12 20:21:25.000000000
+0200
+++ new/strictyaml-1.0.5/hitch/engine.py 2019-10-05 17:01:06.000000000
+0200
@@ -2,6 +2,7 @@
from hitchstory import GivenDefinition, GivenProperty, InfoDefinition,
InfoProperty
from templex import Templex
from strictyaml import Optional, Str, Map, Int, Bool, Enum, load
+from path import Path
import hitchpylibrarytoolkit
from hitchrunpy import (
ExamplePythonCode,
@@ -61,12 +62,16 @@
if not self.path.profile.exists():
self.path.profile.mkdir()
- self.python = hitchpylibrarytoolkit.project_build(
- "strictyaml",
- self.path,
- self.given["python version"],
- {"ruamel.yaml": self.given["ruamel version"]},
- ).bin.python
+ if not self.settings.get("python_path"):
+ self.python = hitchpylibrarytoolkit.project_build(
+ "strictyaml",
+ self.path,
+ self.given["python version"],
+ {"ruamel.yaml": self.given["ruamel version"]},
+ ).bin.python
+ else:
+ self.python = Path(self.settings.get("python_path"))
+ assert self.python.exists()
self.example_py_code = (
ExamplePythonCode(self.python, self.path.gen)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/strictyaml-1.0.3/hitch/key.py
new/strictyaml-1.0.5/hitch/key.py
--- old/strictyaml-1.0.3/hitch/key.py 2019-09-12 20:21:25.000000000 +0200
+++ new/strictyaml-1.0.5/hitch/key.py 2019-10-05 17:01:06.000000000 +0200
@@ -111,6 +111,16 @@
storybook.with_params(**{"python version":
"3.7.0"}).ordered_by_name().play()
+@expected(HitchStoryException)
+def regression_on_python_path(python_path, python_version):
+ """
+ Run regression tests - e.g. hk regression_on_python_path /usr/bin/python
3.7.0
+ """
+ _storybook({
+ "python_path": python_path,
+ }).with_params(**{"python version":
python_version}).only_uninherited().ordered_by_name().play()
+
+
def reformat():
"""
Reformat using black and then relint.
@@ -167,12 +177,12 @@
@expected(CommandError)
-def rerun(version="3.7.0"):
+def rerun():
"""
Rerun last example code block with specified version of python.
"""
from commandlib import Command
-
+ version = _personal_settings().data['params']['python version']
Command(DIR.gen.joinpath("py{0}".format(version), "bin", "python"))(
- DIR.gen.joinpath("state", "examplepythoncode.py")
- ).in_dir(DIR.gen.joinpath("state")).run()
+ DIR.gen.joinpath("working", "examplepythoncode.py")
+ ).in_dir(DIR.gen.joinpath("working")).run()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/strictyaml-1.0.3/hitch/story/update-with-schema.story
new/strictyaml-1.0.5/hitch/story/update-with-schema.story
--- old/strictyaml-1.0.3/hitch/story/update-with-schema.story 1970-01-01
01:00:00.000000000 +0100
+++ new/strictyaml-1.0.5/hitch/story/update-with-schema.story 2019-10-05
17:01:06.000000000 +0200
@@ -0,0 +1,93 @@
+Updating document with a schema:
+ docs: compound/update
+ based on: strictyaml
+ description: |
+ When StrictYAML loads a document with a schema, it checks that future
+ updates to that document follow the original schema.
+ given:
+ setup: |
+ import strictyaml as s
+ from ensure import Ensure
+ variations:
+ GitHub \#72:
+ steps:
+ - Run: |-
+ doc = s.load('a: 9', s.Map({
+ 'a': s.Str(),
+ s.Optional('b'): s.Int(),
+ }))
+ doc['b'] = 9
+ assert doc['b'] == 9
+
+ Works on empty mapping:
+ steps:
+ - Run: |-
+ doc = s.load('', s.EmptyDict() | s.Map({
+ 'a': s.Int(),
+ }))
+ doc['a'] = 9
+ assert doc['a'] == 9, doc.as_yaml()
+
+ Works on complex types:
+ steps:
+ - Run: |-
+ doc = s.load('a: 8', s.Map({'a': s.Int() | s.Float()}))
+ assert type(doc['a'].data) == int, repr(doc.data)
+ doc['a'] = '5.'
+ assert type(doc['a'].data) == float, repr(doc.data)
+ assert doc['a'] == 5.
+
+ Will not work on empty sequence:
+ steps:
+ - Run:
+ code: |
+ doc = s.load('', s.EmptyList() | s.Seq(s.Int()))
+ doc[0] = 9
+ raises:
+ type: strictyaml.exceptions.YAMLSerializationError
+ message: |-
+ cannot extend list via __setitem__. Instead, replace whole
list on parent node.
+
+ Works on map with setting, updating, and then setting multiple keys
(regression):
+ steps:
+ - Run:
+ code: |
+ doc = s.load('', s.EmptyDict() | s.MapPattern(
+ s.Str(),
+ s.EmptyDict() | s.Map({
+ s.Optional('b'): s.Seq(s.Int()),
+ })
+ ))
+ doc['a'] = {}
+ doc['a']['b'] = ['9']
+ assert doc.data == {'a': {'b': [9]}}, doc.data
+ assert doc.as_yaml() == 'a:\n b:\n - 9\n', doc.as_yaml()
+ # Second assignment doesn't occur...
+ doc['a']['b'] = ['9', '10']
+ assert doc.data == {'a': {'b': [9, 10]}}, doc.data
+ assert doc.as_yaml() == 'a:\n b:\n - 9\n - 10\n', doc.as_yaml()
+ # If and only if another node is overwritten. This was a bug due
+ # to mismatched _ruamelparsed objects.
+ doc['b'] = {'b': ['11']}
+ assert doc['a']['b'].data == [9, 10], doc.data
+ assert doc['b']['b'].data == [11], doc.data
+ assert doc.as_yaml() == 'a:\n b:\n - 9\n - 10\nb:\n b:\n -
11\n', doc.as_yaml()
+
+ For empty sequence, must instead assign whole sequence as key:
+ steps:
+ - Run: |-
+ doc = s.load('a:', s.Map({'a': s.EmptyList() | s.Seq(s.Int())}))
+ doc['a'] = [1, 2, 3]
+ assert doc['a'].data == [1, 2, 3], repr(doc.data)
+
+
+ Can assign from string:
+ steps:
+ - Run: |-
+ doc = s.load('a: 9', s.Map({
+ 'a': s.Str(),
+ s.Optional('b'): s.Int(),
+ }))
+ doc['b'] = '9'
+ assert doc['b'] == 9
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/strictyaml-1.0.3/strictyaml/any_validator.py
new/strictyaml-1.0.5/strictyaml/any_validator.py
--- old/strictyaml-1.0.3/strictyaml/any_validator.py 2019-09-12
20:21:25.000000000 +0200
+++ new/strictyaml-1.0.5/strictyaml/any_validator.py 2019-10-05
17:01:06.000000000 +0200
@@ -2,7 +2,7 @@
from strictyaml.compound import FixedSeq, Map
from strictyaml.validators import Validator
from strictyaml.exceptions import YAMLSerializationError
-from strictyaml.scalar import Str
+from strictyaml.scalar import Bool, EmptyDict, EmptyList, Float, Int, Str
def schema_from_document(document):
@@ -16,19 +16,29 @@
return Str()
-def schema_from_data(data):
+def schema_from_data(data, allow_empty):
if isinstance(data, dict):
if len(data) == 0:
+ if allow_empty:
+ return EmptyDict()
raise YAMLSerializationError(
"Empty dicts are not serializable to StrictYAML unless schema
is used."
)
- return Map({key: schema_from_data(value) for key, value in
data.items()})
+ return Map({key: schema_from_data(value, allow_empty) for key, value
in data.items()})
elif isinstance(data, list):
if len(data) == 0:
+ if allow_empty:
+ return EmptyList()
raise YAMLSerializationError(
"Empty lists are not serializable to StrictYAML unless schema
is used."
)
- return FixedSeq([schema_from_data(item) for item in data])
+ return FixedSeq([schema_from_data(item, allow_empty) for item in data])
+ elif isinstance(data, bool):
+ return Bool()
+ elif isinstance(data, int):
+ return Int()
+ elif isinstance(data, float):
+ return Float()
else:
return Str()
@@ -41,8 +51,13 @@
def validate(self, chunk):
return schema_from_document(chunk.contents)(chunk)
- def to_yaml(self, data):
- return schema_from_data(data).to_yaml(data)
+ def to_yaml(self, data, allow_empty=False):
+ """
+ Args:
+ allow_empty (bool): True to allow EmptyDict and EmptyList in the
+ schema generated from the data.
+ """
+ return schema_from_data(data, allow_empty=allow_empty).to_yaml(data)
@property
def key_validator(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/strictyaml-1.0.3/strictyaml/compound.py
new/strictyaml-1.0.5/strictyaml/compound.py
--- old/strictyaml-1.0.3/strictyaml/compound.py 2019-09-12 20:21:25.000000000
+0200
+++ new/strictyaml-1.0.5/strictyaml/compound.py 2019-10-05 17:01:06.000000000
+0200
@@ -1,6 +1,7 @@
from strictyaml.exceptions import YAMLSerializationError,
InvalidOptionalDefault
from strictyaml.validators import Validator, MapValidator, SeqValidator
from ruamel.yaml.comments import CommentedMap, CommentedSeq
+from strictyaml.representation import YAML
from strictyaml.scalar import ScalarValidator, Str
from strictyaml.yamllocation import YAMLChunk
import sys
@@ -176,7 +177,17 @@
# marked_up = new_value.as_marked_up()
# chunk.contents[chunk.ruamelindex(strictindex)] = marked_up
chunk.add_key_association(default_key, strictindex)
- chunk.strictparsed()[yaml_key] = updated_value
+ sp = chunk.strictparsed()
+ if isinstance(sp, YAML):
+ # Do not trigger __setitem__ validation at this point, as
+ # we just ran the validator, and
+ # representation.py:revalidate() doesn't overwrite the
+ # _validator property until after all values are checked,
+ # which leads to an exception being raised if it is
+ # re-checked.
+ sp._value[yaml_key] = updated_value
+ else:
+ sp[yaml_key] = updated_value
if not set(self._required_keys).issubset(found_keys):
chunk.while_parsing_found(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/strictyaml-1.0.3/strictyaml/representation.py
new/strictyaml-1.0.5/strictyaml/representation.py
--- old/strictyaml-1.0.3/strictyaml/representation.py 2019-09-12
20:21:25.000000000 +0200
+++ new/strictyaml-1.0.5/strictyaml/representation.py 2019-10-05
17:01:06.000000000 +0200
@@ -1,6 +1,5 @@
from ruamel.yaml.comments import CommentedSeq, CommentedMap
-from ruamel.yaml.scalarstring import PreservedScalarString
-from strictyaml.exceptions import raise_type_error
+from strictyaml.exceptions import raise_type_error, YAMLSerializationError
from strictyaml.yamllocation import YAMLChunk
from strictyaml.dumper import StrictYAMLDumper
from ruamel.yaml import dump
@@ -192,43 +191,50 @@
def __setitem__(self, index, value):
strictindex = self._strictindex(index)
- try:
- value_validator = self._value[strictindex].validator
- except KeyError:
- # TODO: What if value isn't a YAML object?
- value_validator = value.validator
-
- new_value = (
- value_validator(value._chunk)
- if isinstance(value, YAML)
- else value_validator(YAMLChunk(value_validator.to_yaml(value)))
- )
- # Fork the value
- forked_chunk = self._chunk.fork(strictindex, new_value)
-
- # Validate and attach to current structure
- if self.is_mapping():
- updated_value = value_validator(forked_chunk.val(strictindex))
- updated_value._chunk.make_child_of(self._chunk.val(strictindex))
+ # Generate nice error messages - first, copy our whole node's data
+ # and use ``to_yaml()`` to determine if the resulting data would
+ # validate our schema. Must replace whole current node to support
+ # complex types, e.g. ``EmptyList() | Seq(Str())``.
+ if isinstance(value, YAML):
+ yaml_value = self._chunk.fork(strictindex, value)
+ new_value = self._validator(yaml_value)
else:
- updated_value = value_validator(forked_chunk.index(strictindex))
- updated_value._chunk.make_child_of(self._chunk.index(strictindex))
-
- marked_up = new_value.as_marked_up()
-
- # So that the nicer x: | style of text is used instead of
- # x: "text\nacross\nlines"
- if isinstance(marked_up, (str, unicode)):
- if u"\n" in marked_up:
- marked_up = PreservedScalarString(marked_up)
-
- self._chunk.contents[self._chunk.ruamelindex(strictindex)] = marked_up
- self._value[
- YAML(forked_chunk.ruamelindex(strictindex))
- if self.is_mapping()
- else forked_chunk.ruamelindex(strictindex)
- ] = new_value
+ old_data = self.data
+ if isinstance(old_data, dict):
+ old_data[index] = value
+ elif isinstance(old_data, list):
+ if len(old_data) <= index:
+ raise YAMLSerializationError('cannot extend list via
__setitem__. '
+ 'Instead, replace whole list
on parent '
+ 'node.')
+ old_data[index] = value
+ else:
+ raise NotImplementedError(repr(old_data))
+ yaml_value = YAMLChunk(self._validator.to_yaml(old_data))
+ yaml_value_repr = self._validator(yaml_value)
+
+ # Now that the new content is properly validated, create a valid
+ # chunk with the new information.
+ forked_chunk = self._chunk.fork(strictindex,
yaml_value_repr[strictindex])
+ new_value = self._validator(forked_chunk)
+
+ # Now, overwrite our chunk and value with the new information.
+ old_chunk = self._chunk # Needed for reference to pre-fork ruamel
+ self._chunk = new_value._chunk
+ self._value = new_value._value
+ self._text = new_value._text
+ self._selected_validator = new_value._selected_validator
+ # Update any parent ruamel links to point to our new chunk.
+ self._chunk.pointer.set(old_chunk, '_ruamelparsed',
+ new_value._chunk.contents)
+ self._chunk.pointer.set(old_chunk, '_strictparsed',
+ self, strictdoc=True)
+ # forked chunk made a deep copy of the original document, but we just
+ # updated pointers in the original document. So, restore our chunk to
+ # pointing at the original document.
+ self._chunk._ruamelparsed = old_chunk._ruamelparsed
+ self._chunk._strictparsed = old_chunk._strictparsed
def __delitem__(self, index):
strictindex = self._strictindex(index)
@@ -329,9 +335,7 @@
return isinstance(self._value, CommentedSeq)
def is_scalar(self):
- return not isinstance(self._value, CommentedSeq) and not isinstance(
- self._value, CommentedMap
- )
+ return not self.is_mapping() and not self.is_sequence()
@property
def scalar(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/strictyaml-1.0.3/strictyaml/scalar.py
new/strictyaml-1.0.5/strictyaml/scalar.py
--- old/strictyaml-1.0.3/strictyaml/scalar.py 2019-09-12 20:21:25.000000000
+0200
+++ new/strictyaml-1.0.5/strictyaml/scalar.py 2019-10-05 17:01:06.000000000
+0200
@@ -8,6 +8,7 @@
import decimal
import sys
import re
+from ruamel.yaml.scalarstring import PreservedScalarString
if sys.version_info[0] == 3:
@@ -148,7 +149,9 @@
def to_yaml(self, data):
if not utils.is_string(data):
raise YAMLSerializationError("'{}' is not a string".format(data))
- return str(data)
+ if "\n" in data:
+ return PreservedScalarString(data)
+ return data
class Int(ScalarValidator):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/strictyaml-1.0.3/strictyaml/utils.py
new/strictyaml-1.0.5/strictyaml/utils.py
--- old/strictyaml-1.0.3/strictyaml/utils.py 2019-09-12 20:21:25.000000000
+0200
+++ new/strictyaml-1.0.5/strictyaml/utils.py 2019-10-05 17:01:06.000000000
+0200
@@ -77,10 +77,13 @@
>>> is_decimal("3.5")
True
+ >>> is_decimal("4.")
+ True
+
>>> is_decimal("blah")
False
"""
- return compile(r"^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$").match(value)
is not None
+ return compile(r"^[-+]?[0-9]*(\.[0-9]*)?([eE][-+]?[0-9]+)?$").match(value)
is not None
def comma_separated_positions(text):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/strictyaml-1.0.3/strictyaml/yamllocation.py
new/strictyaml-1.0.5/strictyaml/yamllocation.py
--- old/strictyaml-1.0.3/strictyaml/yamllocation.py 2019-09-12
20:21:25.000000000 +0200
+++ new/strictyaml-1.0.5/strictyaml/yamllocation.py 2019-10-05
17:01:06.000000000 +0200
@@ -149,6 +149,12 @@
label=self.label,
key_association=copy(self._key_association),
)
+ if self.is_scalar():
+ # Necessary for e.g. EmptyDict, which reports as a scalar.
+ forked_chunk.pointer.set(forked_chunk, '_ruamelparsed',
+ CommentedMap())
+ forked_chunk.pointer.set(forked_chunk, '_strictparsed',
+ CommentedMap(), strictdoc=True)
forked_chunk.contents[self.ruamelindex(strictindex)] =
new_value.as_marked_up()
forked_chunk.strictparsed()[strictindex] =
deepcopy(new_value.as_marked_up())
return forked_chunk
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/strictyaml-1.0.3/strictyaml/yamlpointer.py
new/strictyaml-1.0.5/strictyaml/yamlpointer.py
--- old/strictyaml-1.0.3/strictyaml/yamlpointer.py 2019-09-12
20:21:25.000000000 +0200
+++ new/strictyaml-1.0.5/strictyaml/yamlpointer.py 2019-10-05
17:01:06.000000000 +0200
@@ -187,5 +187,43 @@
raise RuntimeError("Invalid state")
return segment
+ def set(self, src_obj, src_attr, new_ruamel, strictdoc=False):
+ """Since set() needs to overwrite what this pointer points to, it
+ affects the parent object. Therefore, rather than taking "document"
+ as get(), it takes the object which holds the document and the name
+ of the property which is the document.
+ """
+ obj_last = src_obj
+ key_last = src_attr
+ r = getattr(src_obj, src_attr)
+ for index_type, index in self._indices:
+ obj_last = r
+ if index_type == "val":
+ key_last = index[1] if strictdoc else index[0]
+ r = r[key_last]
+ elif index_type == "index":
+ key_last = index
+ r = r[key_last]
+ elif index_type == "textslice":
+ key_last = None
+ r = r[index[0] : index[1]]
+ elif index_type == "key":
+ key_last = None
+ r = index[1] if strictdoc else index[0]
+ else:
+ raise RuntimeError("Invalid state")
+ if obj_last is src_obj:
+ # Starts with an attribute set
+ setattr(src_obj, src_attr, new_ruamel)
+ elif key_last is not None:
+ # Others are item set
+ if hasattr(obj_last, '_value'):
+ # Only want to overwrite value, do NOT re-validate schema...
+ obj_last._value[key_last] = new_ruamel
+ else:
+ obj_last[key_last] = new_ruamel
+ else:
+ raise NotImplementedError("invalid key, cannot set")
+
def __repr__(self):
return "<YAMLPointer: {0}>".format(self._indices)