From cca5396dd806dae2039a7099cce3bfc7e568fec2 Mon Sep 17 00:00:00 2001 From: Paolo Dina Date: Wed, 14 Feb 2018 19:57:19 +0100 Subject: [PATCH] handle reserved words used as dictionary keys --- namedtupled/namedtupled.py | 12 ++++++++++++ tests/test_map.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/namedtupled/namedtupled.py b/namedtupled/namedtupled.py index bf8d590..d09b17f 100644 --- a/namedtupled/namedtupled.py +++ b/namedtupled/namedtupled.py @@ -1,7 +1,10 @@ +import keyword +import builtins from future import standard_library standard_library.install_aliases() from collections import Mapping, namedtuple, UserDict +RESERVED = keyword.kwlist + dir(builtins) def mapper(mapping, _nt_name='NT'): """ Convert mappings to namedtuples recursively. """ @@ -15,6 +18,15 @@ def mapper(mapping, _nt_name='NT'): def namedtuple_wrapper(_nt_name, **kwargs): + for k in kwargs: + if k in RESERVED: + new_k = k + '_' + val = kwargs.pop(k) + if new_k in kwargs: + msg = "Can't rename the field {} to {}. {} already exist" + raise ValueError(msg.format(k, new_k, new_k)) + kwargs.update({new_k: val}) + wrap = namedtuple(_nt_name, kwargs) return wrap(**kwargs) diff --git a/tests/test_map.py b/tests/test_map.py index f885db1..c003e38 100644 --- a/tests/test_map.py +++ b/tests/test_map.py @@ -17,6 +17,17 @@ mapping_array = [mapping, mapping] +mapping_keywords = { + 'baz': 'bar', + 'from': 'John Doe' +} + +mapping_keywords_dup = { + 'baz': 'bar', + 'from': 'John Doe', + 'from_': 'Acme Corp' +} + def test_namedtupled_map_object(mapping=mapping): t = namedtupled.map(mapping) @@ -43,3 +54,20 @@ def test_namedtupled_map_array(mapping=mapping_array): assert t[0].alist[1].two == '2' assert t[0].baz != {'qux': 'quux'} assert t[0].alist[0] != {'one': '1', 'a': 'A'} + + +def test_namedtupled_map_object_keywords(mapping=mapping_keywords): + try: + t = namedtupled.map(mapping) + except ValueError: + # Type names and field names cannot be a keyword: 'from' + assert False + + assert t.from_ == 'John Doe' + assert len(t._fields) == len(mapping) + +def test_namedtupled_map_object_keywords_dup(mapping=mapping_keywords_dup): + try: + t = namedtupled.map(mapping) + except ValueError: + assert True \ No newline at end of file