Skip to content

Commit

Permalink
Merge pull request #9 from shakefu/fix_copy
Browse files Browse the repository at this point in the history
Fix copy
  • Loading branch information
shakefu committed Apr 10, 2018
2 parents ea9b78b + 43aac18 commit c151437
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 4 deletions.
24 changes: 22 additions & 2 deletions pytool/lang.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
that do miscelleneous things.
"""
import re
import copy
import inspect
import weakref
import functools
Expand Down Expand Up @@ -462,7 +463,17 @@ def as_dict(self, base_name=None):
:param str base_name: Base namespace (optional)
"""
return dict(self.iteritems(base_name))
space = dict(self.iteritems(base_name))
for key, value in list(space.items()):
if isinstance(value, list):
# We have to copy the list before mutating its items to avoid
# altering the original namespace
value = copy.copy(value)
space[key] = value
for i in range(len(value)):
if isinstance(value[i], Namespace):
value[i] = value[i].as_dict()
return space

def from_dict(self, obj):
""" Populate this Namespace from the given *obj* dictionary.
Expand All @@ -482,6 +493,9 @@ def _coerce_value(value):
if isinstance(value, dict):
return Namespace(value)
elif isinstance(value, list):
# We have to copy the list so we can modify in place without
# breaking things
value = copy.copy(value)
for i in range(len(value)):
value[i] = _coerce_value(value[i])
return value
Expand All @@ -494,13 +508,19 @@ def _coerce_value(value):
def __repr__(self):
return "<Namespace({})>".format(self.as_dict())

def copy(self):
def copy(self, *args, **kwargs):
""" Return a copy of a Namespace by writing it to a dict and then
writing back to a Namespace.
Arguments to this method are ignored.
"""
return Namespace(self.as_dict())

# Aliases for the stdlib copy module
__copy__ = copy
__deepcopy__ = copy


def _split_keys(obj):
"""
Expand Down
72 changes: 70 additions & 2 deletions test/test_lang.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import gc
import copy
import inspect

import pytool
Expand Down Expand Up @@ -162,6 +163,14 @@ def test_namespace_base_name():
eq_(ns.as_dict('ns'), {'ns.foo': 'bar'})


def test_namespace_with_list():
ns = pytool.lang.Namespace()
ns.foo = []
ns.foo.append(pytool.lang.Namespace())
ns.foo[0].bar = 1
eq_(ns.as_dict('ns'), {'ns.foo': [{'bar': 1}]})


def test_namespace_iterable():
ns = pytool.lang.Namespace()
ns.foo = 1
Expand Down Expand Up @@ -297,6 +306,14 @@ def test_namespaces_allow_merging_multiple_dicts():
eq_(ns.bar, 2)


def test_namespaces_will_convert_dicts_in_lists():
obj = {'foo': [{'bar': 1}]}
ns = pytool.lang.Namespace()
ns.from_dict(obj)

eq_(ns.foo[0].bar, 1)


def test_namespaces_work_with_dot_notation():
obj = {'foo.bar': 1}
ns = pytool.lang.Namespace(obj)
Expand Down Expand Up @@ -438,7 +455,7 @@ def test_unflatten():
eq_(result, expected)


def test_copy():
def test_namespace_copy():
a = pytool.lang.Namespace()
a.foo = 'one'
a.bar = [1, 2, 3]
Expand All @@ -450,11 +467,62 @@ def test_copy():
eq_(b.bar, [1, 2, 3])


def test_copy_deeper():
def test_namespace_copy_deeper():
a = pytool.lang.Namespace()
a.foo = [[1, 2], [3, 4]]
b = a.copy()
b.foo[0][0] = 100

eq_(a.foo, [[1, 2], [3, 4]])
eq_(b.foo, [[100, 2], [3, 4]])


def test_namespace_copy_mut():
ns = pytool.lang.Namespace()
ns.foo.bar = 1
ns2 = ns.copy()
ns2.foo.bar = 2

eq_(ns.foo.bar, 1)


def test_namespace_copy_mut_deep():
ns = pytool.lang.Namespace()
ns.foo.bar.baz = 1
ns2 = ns.copy()
ns2.foo.bar.baz = 2

eq_(ns.foo.bar.baz, 1)


def test_namespace_copy_mut_list():
ns = pytool.lang.Namespace()
ns.foo = []
ns.foo.append(pytool.lang.Namespace())
ns.foo[0].bar.baz = 1

ns2 = ns.copy()
ns2.foo[0].bar.baz = 2

eq_(ns.foo[0].bar.baz, 1)


def test_namespace_copy_api_deep():
ns = pytool.lang.Namespace()
ns.foo.bar.baz = 1
ns2 = copy.copy(ns)
ns2.foo.bar.baz = 2

eq_(ns.foo.bar.baz, 1)


def test_namespace_copy_api_list():
ns = pytool.lang.Namespace()
ns.foo = []
ns.foo.append(pytool.lang.Namespace())
ns.foo[0].bar.baz = 1

ns2 = copy.deepcopy(ns)
ns2.foo[0].bar.baz = 2

eq_(ns.foo[0].bar.baz, 1)

0 comments on commit c151437

Please sign in to comment.