An extended dictionary with convenient attribute access, YAML serialization and testing utilities.
- Convenient access:
- Attribute access
x.fieldand regular key accessx["field"] - Deep attributes
x['parent.child']
- Attribute access
- YAML serialization, with those convenient differences from pure YAML specs:
- YAML mappings (
dicts) are loaded asnamespaces, instead of Pythondict. They preserve the insertion order, as they are based onodict. - YAML floats are loaded as
DecimalandDecimalobjects are stored as regular YAML floats. This avoids losing precision when succesive load/store cycles are alternated. - YAML dates are maped to an extension of
datetime.datewhich provides output formats as attributes which are convenient to call informattemplates. - Support for numpy arrays (dump only)
- YAML mappings (
- Tools to
formattemplates with complex namespace structures.- Given the attribute like access,
formattemplates result cleaner with multilevel dicts. - Function to extract an empty YAML scheletton given a template with substitutions.
- Function to fill a
formattemplate like file with a YAML file. - CLI tool to run those two functions (
nstemplate)
- Given the attribute like access,
unittestassertionsassertNsEqualto compare json like structures among them or with yaml strings and display the difference in a nice line by line diff.assertNsContainsto ensure that a json like structure is a superset of the expectation
pyunitinegrationpytestutils.assert_ns_equal: equivalent toassertNsEqualto be used in pytestpytestutils.assert_ns_contains: equivalent toassertNsContainsto be used in pytestpytestutils.yaml_snapshot: fixture to detect namespace changes between test executions in yaml format.pytestutils.text_snapshot: fixture to detect str changes between test executions in plain text format.
>>> from yamlns import namespace as ns
>>> n = ns()
>>> n.attribute1 = "value1"
>>> ns['attribute2'] = "value2"
>>> print(n.dump())
attribute1: value1
attribute2: value2
>>> n.attribute2
'value2'
>>> n['attribute1']
'value1'
>>> n.update(ns.loads("""
... attribute3: value3
... attribute4:
... attribute5: [ 4,3,2,value5 ]
... attribute6: 2015-09-23
... attribute7:
... - value7.1
... - value7.2
... """))
>>> n.attribute4.attribute5
[4, 3, 2, 'value5']
>>> n.attribute4.attribute6
datetime.date(2015,9,23)
>>> n.attribute7
['value7.1', 'value7.2']>>> template = (
... "{client.name} {client.midname[0]}. {client.surname} buys {item.name} "
... "by {item.price.amount:0.02f} {item.price.coin}."
... )
...
>>> print(ns.fromTemplate(template).dump())
client:
name: ''
midname: ''
surname: ''
item:
name: ''
price:
amount: ''
coin: ''
>>> template.format(**ns.loads("""
client:
name: 'John'
midname: 'Archivald'
surname: 'Doe'
item:
name: 'Apples'
price:
amount: 30
coin: 'dollars'
"""))
John A. Doe buys Apples by 30.00 dollars.nstemplate apply <template> <yamlfile> <output>
nstemplate extract <template> <yamlskeleton>
cat file.json | json2yaml > file.yamlclass MyTest(unittest.TestCase):
from yamlns.testutils import assertNsEqual, assertNsContains
def test(self):
data = dict(letters = dict(
(letter, i) for i,letter in enumerate('murcielago'))
)
self.assertNsEqual(data, """\
letters:
a: 7
c: 3
e: 5
g: 8
i: 4
l: 6
m: 0
o: 9
r: 2
u: 1
""")
# Data is a superset of the expectation
self.assertNsContains(data, """\
letters:
a: 7
e: 5
i: 4
o: 9
u: 1
""")The following helper tools for pytest are provided:
pytestutils.assert_ns_equal: equivalent toassertNsEqualto be used in pytestpytestutils.assert_ns_contains: equivalent toassertNsContainsto be used in pytestpytestutils.yaml_snapshot: fixture to detect changes estructure changes between test executions in yaml format.pytestutils.text_snapshot: fixture to detect changes text changes between test executions.
A custom assertion that normalizes both sides into namespaces and dumps them as yaml, which is compared side by side.
The normalization takes place, first if the data is a string, it is parsed as yaml. Then the resulting data is converted recursively into namespaces, ordering keys alfabetically. And finally the result is dumped as yaml to be compared line by line.
from yamlns.pytestutils import assert_ns_equal
def test_with_assert_ns_equal():
data = dict(hello='world')
assert_ns_equal(data, """\
hello: world
""")A custom assertion similar to assert_ns_equal but ignores any key not pressent in the expectation.
from yamlns.pytestutils import assert_ns_equal
def test_with_assert_ns_equal():
data = dict(hello='world', ignored=data)
assert_ns_equal(data, """\
hello: world
""")yaml_snapshot and text_snapshot are fixtures available whenever you install yamlns.
You can use it to make snapshots of data that can be compared to previous executions.
Snapshots are stored into testdata/snapshots/
and are given a name that depends on the fully qualified name of the test.
The ones with the .expected suffix are accepted snapshots,
while the ones ending with .result are generated
when the current execution does not match.
If you consider the .result is valid, just rename it as .expected.
For convenience, the assert message indicates the commandline to perform the renaming.
text_snapshot just dumps verbatim text while
yaml_snapshot compares the normalized dump of the data
just like assert_ns_equal does.
def test_with_yaml_snapshot(yaml_snapshot):
data = dict(hello='world')
yaml_snapshot(data)
def test_with_text_snapshot(text_snapshot):
who = 'world'
text_snapshot('hello {}'.format(who))