Skip to content

Commit f530b80

Browse files
committed
Adds new backup_attributes context manager for tests
This context manager will makes it possible to rollback an object state after leaving the context manager. This is a follow up of <kivy#1867 (comment)> Also address docstring format as suggested by @opacam in <kivy#1933 (comment)>
1 parent 3dabded commit f530b80

File tree

4 files changed

+61
-9
lines changed

4 files changed

+61
-9
lines changed

pythonforandroid/toolchain.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -741,12 +741,12 @@ def _read_configuration():
741741
def recipes(self, args):
742742
"""
743743
Prints recipes basic info, e.g.
744-
```
745-
python3 3.7.1
746-
depends: ['hostpython3', 'sqlite3', 'openssl', 'libffi']
747-
conflicts: ['python2']
748-
optional depends: ['sqlite3', 'libffi', 'openssl']
749-
```
744+
.. code-block:: bash
745+
746+
python3 3.7.1
747+
depends: ['hostpython3', 'sqlite3', 'openssl', 'libffi']
748+
conflicts: ['python2']
749+
optional depends: ['sqlite3', 'libffi', 'openssl']
750750
"""
751751
ctx = self.ctx
752752
if args.compact:

tests/test_toolchain.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import sys
33
import pytest
44
import mock
5+
from util import backup_attributes
56
from pythonforandroid.recipe import Recipe
67
from pythonforandroid.toolchain import ToolchainCL
78
from pythonforandroid.util import BuildInterruptingException
@@ -121,7 +122,10 @@ def test_recipes(self):
121122
Checks the `recipes` command prints out recipes information without crashing.
122123
"""
123124
argv = ['toolchain.py', 'recipes']
124-
with patch_sys_argv(argv), patch_sys_stdout() as m_stdout:
125+
with (
126+
patch_sys_argv(argv)), (
127+
patch_sys_stdout()) as m_stdout, (
128+
backup_attributes(Recipe, {'recipes'})):
125129
ToolchainCL()
126130
# check if we have common patterns in the output
127131
expected_strings = (
@@ -134,5 +138,3 @@ def test_recipes(self):
134138
)
135139
for expected_string in expected_strings:
136140
assert expected_string in m_stdout.getvalue()
137-
# deletes static attribute to not mess with other tests
138-
del Recipe.recipes

tests/test_util.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
# `Python 2` or lower than `Python 3.3` does not
99
# have the `unittest.mock` module built-in
1010
import mock
11+
import util as test_util
1112
from pythonforandroid import util
1213

1314

@@ -17,6 +18,36 @@ class TestUtil(unittest.TestCase):
1718
:mod:`~pythonforandroid.util`.
1819
"""
1920

21+
def test_backup_attributes(self):
22+
"""
23+
Checks the helper backup_attributes context manager works as expected.
24+
"""
25+
class MyClass:
26+
def __init__(self, foo, bar):
27+
self.foo = foo
28+
self.bar = bar
29+
30+
# testing trivial flat backup
31+
foo = 'foo'
32+
bar = 'bar'
33+
my_object = MyClass(foo=foo, bar=bar)
34+
with test_util.backup_attributes(my_object, {'foo'}):
35+
my_object.foo = 'not foo'
36+
my_object.bar = 'not bar'
37+
assert my_object.foo == 'foo'
38+
assert my_object.bar == 'not bar'
39+
# testing deep backup
40+
foo = {'foo': {1, 2, 3}}
41+
bar = {'bar': {3, 2, 1}}
42+
my_object = MyClass(foo=foo, bar=bar)
43+
with test_util.backup_attributes(my_object, {'foo', 'bar'}):
44+
# changing the reference
45+
my_object.foo = {}
46+
# and mutating the object the attribute is referencing to
47+
my_object.bar['bar'] = None
48+
assert my_object.foo == {'foo': {1, 2, 3}}
49+
assert my_object.bar == {'bar': {3, 2, 1}}
50+
2051
@mock.patch("pythonforandroid.util.makedirs")
2152
def test_ensure_dir(self, mock_makedirs):
2253
"""

tests/util.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from copy import deepcopy
2+
from contextlib import contextmanager
3+
4+
5+
@contextmanager
6+
def backup_attributes(obj, attributes):
7+
"""
8+
Makes a backup of the object attributes that gets restored later.
9+
"""
10+
obj_dict = obj.__dict__
11+
# creates a subset dictionary of the attributes we're interested in
12+
attributes_backup = dict(
13+
(k, obj_dict[k]) for k in attributes if k in obj_dict)
14+
attributes_backup = deepcopy(attributes_backup)
15+
try:
16+
yield
17+
finally:
18+
for attribute in attributes:
19+
setattr(obj, attribute, attributes_backup[attribute])

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy