Merge lp:~zyga/checkbox/padme-switch into lp:checkbox

Proposed by Zygmunt Krynicki
Status: Merged
Approved by: Sylvain Pineau
Approved revision: 3972
Merged at revision: 3971
Proposed branch: lp:~zyga/checkbox/padme-switch
Merge into: lp:checkbox
Diff against target: 829 lines (+4/-780)
5 files modified
plainbox/plainbox/impl/censoREd.py (+2/-3)
plainbox/plainbox/impl/proxy.py (+0/-401)
plainbox/plainbox/impl/test_proxy.py (+0/-376)
plainbox/requirements/deb-core.txt (+1/-0)
plainbox/setup.py (+1/-0)
To merge this branch: bzr merge lp:~zyga/checkbox/padme-switch
Reviewer Review Type Date Requested Status
Sylvain Pineau Approve
Review via email: mp+269644@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

the devendorization continues, +1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'plainbox/plainbox/impl/censoREd.py'
2--- plainbox/plainbox/impl/censoREd.py 2015-07-13 09:57:16 +0000
3+++ plainbox/plainbox/impl/censoREd.py 2015-08-31 14:17:15 +0000
4@@ -56,8 +56,7 @@
5 The last resort, aka, the proxy approach. Let's use a bit of magic to work
6 around the problem. This way we won't have to subclass or override anything.
7 """
8-from plainbox.impl.proxy import proxy
9-from plainbox.impl.proxy import unproxied
10+from padme import proxy
11
12
13 __all__ = ["PatternProxy"]
14@@ -86,6 +85,6 @@
15
16 **Yes** (gets another drink).
17 """
18- @unproxied
19+ @proxy.direct
20 def __repr__(self):
21 return "re.compile({!r})".format(self.pattern)
22
23=== removed file 'plainbox/plainbox/impl/proxy.py'
24--- plainbox/plainbox/impl/proxy.py 2015-01-29 01:57:26 +0000
25+++ plainbox/plainbox/impl/proxy.py 1970-01-01 00:00:00 +0000
26@@ -1,401 +0,0 @@
27-# This file is part of Checkbox.
28-#
29-# Copyright 2012-2015 Canonical Ltd.
30-# Written by:
31-# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
32-#
33-# Checkbox is free software: you can redistribute it and/or modify
34-# it under the terms of the GNU General Public License version 3,
35-# as published by the Free Software Foundation.
36-#
37-# Checkbox is distributed in the hope that it will be useful,
38-# but WITHOUT ANY WARRANTY; without even the implied warranty of
39-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40-# GNU General Public License for more details.
41-#
42-# You should have received a copy of the GNU General Public License
43-# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
44-"""
45-:mod:`plainbox.impl.proxy ` -- mostly transparent proxy
46-=======================================================
47-
48-.. note::
49- There are a number of classes and meta-classes but the only public
50- interface is the :class:`proxy` class. See below for examples.
51-"""
52-import logging
53-import itertools
54-
55-_logger = logging.getLogger("plainbox.proxy")
56-
57-
58-__all__ = ['proxy']
59-
60-
61-class proxy_meta(type):
62- """
63- Meta-class for all proxy types
64-
65- This meta-class is responsible for gathering the __unproxied__ attribute on
66- each created class. The attribute is a frosenset of names that will not be
67- forwarded to the ``proxxie`` but instead will be looked up on the proxy
68- itself.
69- """
70-
71- def __new__(mcls, name, bases, ns):
72- _logger.debug(
73- "__new__ on proxy_meta with name: %r, bases: %r", name, bases)
74- unproxied_set = set()
75- for base in bases:
76- if hasattr(base, '__unproxied__'):
77- unproxied_set.update(base.__unproxied__)
78- for ns_attr, ns_value in ns.items():
79- if getattr(ns_value, 'unproxied', False):
80- unproxied_set.add(ns_attr)
81- if unproxied_set:
82- _logger.debug(
83- "proxy type %r will pass-thru %r", name, unproxied_set)
84- ns['__unproxied__'] = frozenset(unproxied_set)
85- return super().__new__(mcls, name, bases, ns)
86-
87-
88-cnt = itertools.count()
89-
90-
91-def make_boundproxy_meta(proxiee):
92- """
93- Make a new bound proxy meta-class for the specified object
94-
95- :param proxiee:
96- The object that will be proxied
97- :returns:
98- A new meta-class that lexically wraps ``proxiee`` and subclasses
99- :class:`proxy_meta`.
100- """
101-
102- class boundproxy_meta(proxy_meta):
103- """
104- Meta-class for all bound proxies.
105-
106- This meta-class is responsible for generating an unique name for each
107- created class and setting the setting the ``__proxiee__`` attribute to
108- the proxiee object itself.
109-
110- In addition, it implements two methods that participate in instance and
111- class checks: ``__instancecheck__`` and ``__subclasscheck__``.
112- """
113-
114- def __new__(mcls, name, bases, ns):
115- name = 'boundproxy[{!r}]'.format(next(cnt))
116- _logger.debug(
117- "__new__ on boundproxy_meta with name %r and bases %r",
118- name, bases)
119- ns['__proxiee__'] = proxiee
120- return super().__new__(mcls, name, bases, ns)
121-
122- def __instancecheck__(mcls, instance):
123- # NOTE: this is never called in practice since
124- # proxy(obj).__class__ is really obj.__class__.
125- _logger.debug("__instancecheck__ %r on %r", instance, proxiee)
126- return isinstance(instance, type(proxiee))
127-
128- def __subclasscheck__(mcls, subclass):
129- # This is still called though since type(proxy(obj)) is
130- # something else
131- _logger.debug("__subclasscheck__ %r on %r", subclass, proxiee)
132- return issubclass(type(proxiee), subclass)
133-
134- return boundproxy_meta
135-
136-
137-class proxy_base:
138- """
139- Base class for all proxies.
140-
141- This class implements the bulk of the proxy work by having a lot of dunder
142- methods that delegate their work to a ``proxiee`` object. The ``proxiee``
143- object must be available as the ``__proxiee__`` attribute on a class
144- deriving from ``base_proxy``. Apart from ``__proxiee__`, the
145- ``__unproxied__`` attribute, which should be a frozenset, must also be
146- present in all derived classes.
147-
148- In practice, the two special attributes are injected via
149- ``boundproxy_meta`` created by :func:`make_boundproxy_meta()`. This class
150- is also used as a base class for the tricky :class:`proxy` below.
151-
152- NOTE: Look at ``pydoc3 SPECIALMETHODS`` section titled ``Special method
153- lookup`` for a rationale of why we have all those dunder methods while
154- still having __getattribute__()
155- """
156- # NOTE: the order of methods below matches that of ``pydoc3
157- # SPECIALMETHODS``. The "N/A to instances" text means that it makes no
158- # sense to add proxy support to the specified method because that method
159- # makes no sense on instances. Proxy is designed to intercept access to
160- # *objects*, not construction of such objects in the first place.
161-
162- # N/A to instances: __new__
163-
164- # N/A to instances: __init__
165-
166- def __del__(self):
167- """
168- NOTE: this method is handled specially since it must be called
169- after an object becomes unreachable. As long as the proxy object
170- itself exits, it holds a strong reference to the original object.
171- """
172-
173- def __repr__(self):
174- proxiee = type(self).__proxiee__
175- _logger.debug("__repr__ on proxiee (%r)", proxiee)
176- return repr(proxiee)
177-
178- def __str__(self):
179- proxiee = type(self).__proxiee__
180- _logger.debug("__str__ on proxiee (%r)", proxiee)
181- return str(proxiee)
182-
183- def __bytes__(self):
184- proxiee = type(self).__proxiee__
185- _logger.debug("__bytes__ on proxiee (%r)", proxiee)
186- return bytes(proxiee)
187-
188- def __format__(self, format_spec):
189- proxiee = type(self).__proxiee__
190- _logger.debug("__format__ on proxiee (%r)", proxiee)
191- return format(proxiee, format_spec)
192-
193- def __lt__(self, other):
194- proxiee = type(self).__proxiee__
195- _logger.debug("__lt__ on proxiee (%r)", proxiee)
196- return proxiee < other
197-
198- def __le__(self, other):
199- proxiee = type(self).__proxiee__
200- _logger.debug("__le__ on proxiee (%r)", proxiee)
201- return proxiee <= other
202-
203- def __eq__(self, other):
204- proxiee = type(self).__proxiee__
205- _logger.debug("__eq__ on proxiee (%r)", proxiee)
206- return proxiee == other
207-
208- def __ne__(self, other):
209- proxiee = type(self).__proxiee__
210- _logger.debug("__ne__ on proxiee (%r)", proxiee)
211- return proxiee != other
212-
213- def __gt__(self, other):
214- proxiee = type(self).__proxiee__
215- _logger.debug("__gt__ on proxiee (%r)", proxiee)
216- return proxiee > other
217-
218- def __ge__(self, other):
219- proxiee = type(self).__proxiee__
220- _logger.debug("__ge__ on proxiee (%r)", proxiee)
221- return proxiee >= other
222-
223- def __hash__(self):
224- proxiee = type(self).__proxiee__
225- _logger.debug("__hash__ on proxiee (%r)", proxiee)
226- return hash(proxiee)
227-
228- def __bool__(self):
229- proxiee = type(self).__proxiee__
230- _logger.debug("__bool__ on proxiee (%r)", proxiee)
231- return bool(proxiee)
232-
233- def __getattr__(self, name):
234- proxiee = type(self).__proxiee__
235- _logger.debug("__getattr__ %r on proxiee (%r)", name, proxiee)
236- return getattr(proxiee, name)
237-
238- def __getattribute__(self, name):
239- cls = type(self)
240- if name not in cls.__unproxied__:
241- proxiee = cls.__proxiee__
242- _logger.debug("__getattribute__ %r on proxiee (%r)", name, proxiee)
243- return getattr(proxiee, name)
244- else:
245- _logger.debug("__getattribute__ %r on proxy itself", name)
246- return object.__getattribute__(self, name)
247-
248- def __setattr__(self, attr, value):
249- proxiee = type(self).__proxiee__
250- _logger.debug("__setattr__ %r on proxiee (%r)", attr, proxiee)
251- setattr(proxiee, attr, value)
252-
253- def __delattr__(self, attr):
254- proxiee = type(self).__proxiee__
255- _logger.debug("__delattr__ %r on proxiee (%r)", attr, proxiee)
256- delattr(proxiee, attr)
257-
258- def __dir__(self):
259- proxiee = type(self).__proxiee__
260- _logger.debug("__dir__ on proxiee (%r)", proxiee)
261- return dir(proxiee)
262-
263- def __get__(self, instance, owner):
264- proxiee = type(self).__proxiee__
265- _logger.debug("__get__ on proxiee (%r)", proxiee)
266- return proxiee.__get__(instance, owner)
267-
268- def __set__(self, instance, value):
269- proxiee = type(self).__proxiee__
270- _logger.debug("__set__ on proxiee (%r)", proxiee)
271- proxiee.__set__(instance, value)
272-
273- def __delete__(self, instance):
274- proxiee = type(self).__proxiee__
275- _logger.debug("__delete__ on proxiee (%r)", proxiee)
276- proxiee.__delete__(instance)
277-
278- def __call__(self, *args, **kwargs):
279- proxiee = type(self).__proxiee__
280- _logger.debug("call on proxiee (%r)", proxiee)
281- return proxiee(*args, **kwargs)
282-
283- def __len__(self):
284- proxiee = type(self).__proxiee__
285- _logger.debug("__len__ on proxiee (%r)", proxiee)
286- return len(proxiee)
287-
288- def __length_hint__(self):
289- proxiee = type(self).__proxiee__
290- _logger.debug("__length_hint__ on proxiee (%r)", proxiee)
291- return proxiee.__length_hint__()
292-
293- def __getitem__(self, item):
294- proxiee = type(self).__proxiee__
295- _logger.debug("__getitem__ on proxiee (%r)", proxiee)
296- return proxiee[item]
297-
298- def __setitem__(self, item, value):
299- proxiee = type(self).__proxiee__
300- _logger.debug("__setitem__ on proxiee (%r)", proxiee)
301- proxiee[item] = value
302-
303- def __delitem__(self, item):
304- proxiee = type(self).__proxiee__
305- _logger.debug("__delitem__ on proxiee (%r)", proxiee)
306- del proxiee[item]
307-
308- def __iter__(self):
309- proxiee = type(self).__proxiee__
310- _logger.debug("__iter__ on proxiee (%r)", proxiee)
311- return iter(proxiee)
312-
313- def __reversed__(self):
314- proxiee = type(self).__proxiee__
315- _logger.debug("__reversed__ on proxiee (%r)", proxiee)
316- return reversed(proxiee)
317-
318- def __contains__(self, item):
319- proxiee = type(self).__proxiee__
320- _logger.debug("__contains__ on proxiee (%r)", proxiee)
321- return item in proxiee
322-
323- # TODO: all numeric methods
324-
325- def __enter__(self):
326- proxiee = type(self).__proxiee__
327- _logger.debug("__enter__ on proxiee (%r)", proxiee)
328- return proxiee.__enter__()
329-
330- def __exit__(self, exc_type, exc_value, traceback):
331- proxiee = type(self).__proxiee__
332- _logger.debug("__exit__ on proxiee (%r)", proxiee)
333- return proxiee.__exit__(exc_type, exc_value, traceback)
334-
335-
336-class proxy(proxy_base, metaclass=proxy_meta):
337- """
338- A mostly transparent proxy type
339-
340- The proxy class can be used in two different ways. First, as a callable
341- ``proxy(obj)``. This simply returns a proxy for a single object.
342-
343- >>> truth = ['trust no one']
344- >>> lie = proxy(truth)
345-
346- This will return an instance of a new ``proxy`` sub-class which for all
347- intents and purposes, to the extent possible in CPython, forwards all
348- requests to the original object.
349-
350- One can still examine the proxy with some ways::
351-
352- >>> lie is truth
353- False
354- >>> type(lie) is type(truth)
355- False
356-
357- Having said that, the vast majority of stuff will make the proxy behave
358- identically to the original object.
359-
360- >>> lie[0]
361- 'trust no one'
362- >>> lie[0] = 'trust the government'
363- >>> truth[0]
364- 'trust the government'
365-
366- The second way of using the ``proxy`` class is as a base class. In this
367- way, one can actually override certain methods. To ensure that all the
368- dunder methods work correctly please use the ``@unproxied`` decorator on
369- them.
370-
371- >>> import codecs
372- >>> class crypto(proxy):
373- ...
374- ... @unproxied
375- ... def __repr__(self):
376- ... return codecs.encode(super().__repr__(), "rot_13")
377-
378- With this weird class, we can change the repr() of any object we want to be
379- ROT-13 encoded. Let's see:
380-
381- >>> orig = ['ala ma kota', 'a kot ma ale']
382- >>> prox = crypto(orig)
383-
384- We can sill access all of the data through the proxy:
385-
386- >>> prox[0]
387- 'ala ma kota'
388-
389- But the whole repr() is now a bit different than usual:
390-
391- >>> prox
392- ['nyn zn xbgn', 'n xbg zn nyr']
393- """
394-
395- def __new__(proxy_cls, proxiee):
396- """
397- Create a new instance of ``proxy()`` wrapping ``proxiee``
398-
399- :param proxiee:
400- The object to proxy
401- :returns:
402- An instance of new subclass of ``proxy``, called
403- ``boundproxy[proxiee]`` that uses a new meta-class that lexically
404- bounds the ``proxiee`` argument. The new sub-class has a different
405- implementation of ``__new__`` and can be instantiated without
406- additional arguments.
407- """
408- _logger.debug("__new__ on proxy with proxiee: %r", proxiee)
409- boundproxy_meta = make_boundproxy_meta(proxiee)
410-
411- class boundproxy(proxy_cls, metaclass=boundproxy_meta):
412-
413- def __new__(boundproxy_cls):
414- _logger.debug("__new__ on boundproxy %r", boundproxy_cls)
415- return object.__new__(boundproxy_cls)
416- return boundproxy()
417-
418-
419-def unproxied(fn):
420- """
421- Mark an object (attribute) as not-to-be-proxied.
422-
423- This decorator can be used inside :class:`proxy` sub-classes. Please
424- consult the documentation of ``proxy`` for details.
425- """
426- fn.unproxied = True
427- return fn
428
429=== removed file 'plainbox/plainbox/impl/test_proxy.py'
430--- plainbox/plainbox/impl/test_proxy.py 2015-08-31 10:05:19 +0000
431+++ plainbox/plainbox/impl/test_proxy.py 1970-01-01 00:00:00 +0000
432@@ -1,376 +0,0 @@
433-# This file is part of Checkbox.
434-#
435-# Copyright 2012-2015 Canonical Ltd.
436-# Written by:
437-# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
438-#
439-# Checkbox is free software: you can redistribute it and/or modify
440-# it under the terms of the GNU General Public License version 3,
441-# as published by the Free Software Foundation.
442-#
443-# Checkbox is distributed in the hope that it will be useful,
444-# but WITHOUT ANY WARRANTY; without even the implied warranty of
445-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
446-# GNU General Public License for more details.
447-#
448-# You should have received a copy of the GNU General Public License
449-# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
450-import doctest
451-import operator
452-import sys
453-import unittest
454-
455-from plainbox.impl.proxy import _logger
456-from plainbox.impl.proxy import proxy
457-from plainbox.impl.proxy import unproxied
458-from plainbox.vendor import mock
459-
460-
461-# XXX: Set to True for revelation
462-reality_is_broken = False
463-
464-
465-def load_tests(loader, tests, ignore):
466- tests.addTests(
467- doctest.DocTestSuite('plainbox.impl.proxy',
468- optionflags=doctest.REPORT_NDIFF))
469- return tests
470-
471-
472-def setUpModule():
473- if reality_is_broken:
474- # If you start to doubt reality
475- from plainbox.impl.logging import adjust_logging
476- from plainbox.impl.logging import setup_logging
477- setup_logging()
478- adjust_logging('DEBUG', ['plainbox.proxy'], True)
479-
480-
481-class proxy_as_function(unittest.TestCase):
482-
483- def setUp(self):
484- if reality_is_broken:
485- print()
486- _logger.debug("STARTING")
487- _logger.debug("[%s]", self._testMethodName)
488- self.obj = mock.MagicMock(name='obj')
489- self.proxy = proxy(self.obj)
490-
491- def tearDown(self):
492- if reality_is_broken:
493- _logger.debug("DONE")
494-
495- # NOTE: order of test methods matches implementation
496-
497- def test_repr(self):
498- self.assertEqual(repr(self.proxy), repr(self.obj))
499- self.assertEqual(self.proxy.__repr__(), repr(self.obj))
500-
501- def test_str(self):
502- self.assertEqual(str(self.proxy), str(self.obj))
503- self.assertEqual(self.proxy.__str__(), str(self.obj))
504-
505- def test_bytes(self):
506- # NOTE: bytes() is unlike str() or repr() in that it is not a function
507- # that converts an arbitrary object into a bytes object. We cannot
508- # just call it on a random object. What we must do is implement
509- # __bytes__() on a new class and use instances of that class.
510- class C:
511- def __bytes__(self):
512- return b'good'
513- self.obj = C()
514- self.proxy = proxy(self.obj)
515- self.assertEqual(bytes(self.proxy), bytes(self.obj))
516- self.assertEqual(self.proxy.__bytes__(), bytes(self.obj))
517-
518- def test_format(self):
519- self.assertEqual(format(self.proxy), format(self.obj))
520- self.assertEqual(self.proxy.__format__(""), format(self.obj))
521-
522- def test_lt(self):
523- # NOTE: MagicMock is not ordered so let's just use an integer
524- self.obj = 0
525- self.proxy = proxy(self.obj)
526- self.assertLess(self.proxy, 1)
527- self.assertLess(self.obj, 1)
528-
529- def test_le(self):
530- # NOTE: MagicMock is not ordered so let's just use an integer
531- self.obj = 0
532- self.proxy = proxy(self.obj)
533- self.assertLessEqual(self.proxy, 0)
534- self.assertLessEqual(self.proxy, 1)
535- self.assertLessEqual(self.obj, 0)
536- self.assertLessEqual(self.obj, 1)
537-
538- def test_eq(self):
539- self.assertEqual(self.proxy, self.obj)
540- self.obj.__eq__.assert_called_once_with(self.obj)
541- self.assertEqual(self.obj, self.obj)
542-
543- def test_ne(self):
544- other = object()
545- self.assertNotEqual(self.proxy, other)
546- self.obj.__ne__.assert_called_once_with(other)
547- self.assertNotEqual(self.obj, object())
548-
549- def test_gt(self):
550- # NOTE: MagicMock is not ordered so let's just use an integer
551- self.obj = 0
552- self.proxy = proxy(self.obj)
553- self.assertGreater(self.proxy, -1)
554- self.assertGreater(self.obj, -1)
555-
556- def test_ge(self):
557- # NOTE: MagicMock is not ordered so let's just use an integer
558- self.obj = 0
559- self.proxy = proxy(self.obj)
560- self.assertGreaterEqual(self.proxy, 0)
561- self.assertGreaterEqual(self.proxy, -1)
562- self.assertGreaterEqual(self.obj, 0)
563- self.assertGreaterEqual(self.obj, -1)
564-
565- def test_hash(self):
566- self.assertEqual(hash(self.proxy), hash(self.obj))
567- self.assertEqual(self.proxy.__hash__(), hash(self.obj))
568-
569- def test_bool(self):
570- self.assertEqual(bool(self.proxy), bool(self.obj))
571- self.assertEqual(self.proxy.__bool__(), bool(self.obj))
572-
573- def test_attr_get(self):
574- self.assertIs(self.proxy.attr, self.obj.attr)
575-
576- def test_attr_set(self):
577- value = mock.Mock(name='value')
578- self.proxy.attr = value
579- self.assertIs(self.obj.attr, value)
580-
581- def test_attr_del(self):
582- del self.proxy.attr
583- with self.assertRaises(AttributeError):
584- self.obj.attr
585-
586- def test_dir(self):
587- self.assertEqual(dir(self.proxy), dir(self.obj))
588- self.assertEqual(self.proxy.__dir__(), dir(self.obj))
589-
590- def test_descriptor_methods(self):
591- # NOTE: this tests __get__, __set__ and __delete__ in one test, for
592- # brevity
593- property_proxy = proxy(property)
594-
595- class C:
596- _ok = "default"
597-
598- @property_proxy
599- def ok(self):
600- return self._ok
601-
602- @ok.setter
603- def ok(self, value):
604- self._ok = value
605-
606- @ok.deleter
607- def ok(self):
608- del self._ok
609- obj = C()
610- self.assertEqual(obj._ok, "default")
611- # __set__ assigns the new value
612- obj.ok = True
613- self.assertTrue(obj._ok)
614- # __get__ returns the current value
615- self.assertTrue(obj.ok)
616- # __delete__ removes the current value
617- del obj.ok
618- self.assertEqual(obj._ok, "default")
619-
620- def test_call(self):
621- self.assertEqual(self.proxy(), self.obj())
622- self.assertEqual(self.proxy.__call__(), self.obj())
623-
624- def test_len(self):
625- self.assertEqual(len(self.proxy), len(self.obj))
626- self.assertEqual(self.proxy.__len__(), len(self.obj))
627-
628- @unittest.skipIf(lambda: sys.version_info[0:2] < 3, 4)
629- def test_length_hint(self):
630- # NOTE: apparently MagicMock doesn't support this method
631- class C:
632- def __length_hint__(self):
633- return 42
634- self.obj = C()
635- self.proxy = proxy(self.obj)
636- self.assertEqual(
637- operator.length_hint(self.proxy), operator.length_hint(self.obj))
638- self.assertEqual(
639- self.proxy.__length_hint__(), operator.length_hint(self.obj))
640-
641- def test_getitem(self):
642- self.assertEqual(self.proxy['item'], self.obj['item'])
643- self.assertEqual(self.proxy.__getitem__('item'), self.obj['item'])
644-
645- def test_setitem_v1(self):
646- # NOTE: MagicMock doesn't store item assignment
647- self.obj = ["old"]
648- self.proxy = proxy(self.obj)
649- self.proxy[0] = "new"
650- self.assertEqual(self.obj[0], "new")
651-
652- def test_setitem_v2(self):
653- # NOTE: MagicMock doesn't store item assignment
654- self.obj = ["old"]
655- self.proxy = proxy(self.obj)
656- self.proxy.__setitem__(0, "value")
657- self.assertEqual(self.obj[0], "value")
658-
659- def test_delitem(self):
660- obj = {'k': 'v'}
661- del proxy(obj)['k']
662- self.assertEqual(obj, {})
663- obj = {'k': 'v'}
664- proxy(obj).__delitem__('k')
665- self.assertEqual(obj, {})
666-
667- def test_iter(self):
668- # NOTE: MagicMock.__iter__ needs to return a deterministic iterator as
669- # by default a new iterator is returned each time.
670- self.obj.__iter__.return_value = iter([])
671- self.assertEqual(iter(self.proxy), iter(self.obj))
672- self.assertEqual(self.proxy.__iter__(), iter(self.obj))
673-
674- def test_reversed(self):
675- # NOTE: apparently MagicMock.doesn't support __reversed__ so we fall
676- # back to the approach with a custom class. The same comment, as above,
677- # for __iter__() applies though.
678- with self.assertRaises(AttributeError):
679- self.obj.__reversed__.return_value = reversed([])
680-
681- class C:
682- reversed_retval = iter([])
683-
684- def __reversed__(self):
685- return self.reversed_retval
686- self.obj = C()
687- self.proxy = proxy(self.obj)
688- self.assertEqual(reversed(self.proxy), reversed(self.obj))
689- self.assertEqual(self.proxy.__reversed__(), reversed(self.obj))
690-
691- def test_contains(self):
692- item = object()
693- self.assertEqual(item in self.proxy, item in self.obj)
694- self.assertEqual(self.proxy.__contains__(item), item in self.obj)
695- self.assertEqual(self.proxy.__contains__(item), False)
696- self.obj.__contains__.return_value = True
697- self.assertEqual(item in self.proxy, item in self.obj)
698- self.assertEqual(self.proxy.__contains__(item), item in self.obj)
699- self.assertEqual(self.proxy.__contains__(item), True)
700-
701- # TODO, tests and implementation for all the numeric methods
702-
703- def test_context_manager_methods_v1(self):
704- with self.proxy:
705- pass
706- self.obj.__enter__.assert_called_once_with()
707- self.obj.__exit__.assert_called_once_with(None, None, None)
708-
709- def test_context_manager_methods_v2(self):
710- exc = Exception("boom")
711- exc_info = ()
712- with self.assertRaisesRegex(Exception, "boom"):
713- try:
714- with self.proxy:
715- raise exc
716- except Exception:
717- exc_info = sys.exc_info()
718- raise
719- self.obj.__enter__.assert_called_once_with()
720- self.obj.__exit__.assert_called_once_with(*exc_info)
721-
722- def test_hasattr_parity(self):
723- class C():
724- pass
725- special_methods = '''
726- __del__
727- __repr__
728- __str__
729- __bytes__
730- __format__
731- __lt__
732- __le__
733- __eq__
734- __ne__
735- __gt__
736- __ge__
737- __hash__
738- __bool__
739- __getattr__
740- __getattribute__
741- __setattr__
742- __delattr__
743- __dir__
744- __get__
745- __set__
746- __delete__
747- __call__
748- __len__
749- __length_hint__
750- __getitem__
751- __setitem__
752- __delitem__
753- __iter__
754- __reversed__
755- __contains__
756- __enter__
757- __exit__
758- '''.split()
759- for obj in [C(), 42, property(lambda x: x), int, None]:
760- self.obj = obj
761- self.proxy = proxy(self.obj)
762- for attr in special_methods:
763- self.assertEqual(
764- hasattr(self.obj, attr),
765- hasattr(self.proxy, attr),
766- "attribute presence mismatch on attr %r and object %r" % (
767- attr, self.obj))
768-
769- def test_isinstance(self):
770- # NOTE: this method tests the metaclass
771- self.assertIsInstance(self.obj, type(self.obj))
772- self.assertIsInstance(self.proxy, type(self.obj))
773-
774- def test_issubclass(self):
775- # NOTE: this method tests the metaclass
776- # NOTE: mock doesn't support subclasscheck
777- obj = "something other than mock"
778- self.assertTrue(issubclass(str, type(obj)))
779- self.assertTrue(issubclass(str, type(proxy(obj))))
780-
781- def test_class(self):
782- self.assertEqual(self.proxy.__class__, self.obj.__class__)
783- # NOTE: The proxy cannot hide the fact, that it is a proxy
784- self.assertNotEqual(type(self.proxy), type(self.obj))
785-
786-
787-class proxy_as_class(unittest.TestCase):
788-
789- def setUp(self):
790- if reality_is_broken:
791- print()
792- _logger.debug("STARTING")
793- _logger.debug("[%s]", self._testMethodName)
794-
795- def tearDown(self):
796- if reality_is_broken:
797- _logger.debug("DONE")
798-
799- def test_proxy_subclass(self):
800- # NOTE: bring your comb, because this is the extra-hairy land
801- class censored(proxy):
802-
803- @unproxied
804- def __str__(self):
805- return "*" * len(super().__str__())
806- self.assertTrue(issubclass(censored, proxy))
807- self.assertEqual(str(censored("freedom")), "*******")
808- self.assertEqual(censored("freedom").__str__(), "*******")
809
810=== modified file 'plainbox/requirements/deb-core.txt'
811--- plainbox/requirements/deb-core.txt 2015-06-25 16:46:15 +0000
812+++ plainbox/requirements/deb-core.txt 2015-08-31 14:17:15 +0000
813@@ -1,3 +1,4 @@
814 policykit-1
815 python3-jinja2
816+python3-padme
817 python3-xlsxwriter
818
819=== modified file 'plainbox/setup.py'
820--- plainbox/setup.py 2015-08-03 14:10:44 +0000
821+++ plainbox/setup.py 2015-08-31 14:17:15 +0000
822@@ -72,6 +72,7 @@
823 ],
824 install_requires=[
825 'Jinja2 >= 2.7',
826+ 'padme >= 1.1.1',
827 'requests >= 1.0',
828 ],
829 extras_require={

Subscribers

People subscribed via source and target branches