Michael Merickel
2018-10-18 41f103af2745c336a3bcdc715e70ef3cb5d1e545
commit | author | age
6a7914 1 import copy
35c5a3 2 import os
fbbb20 3 from contextlib import contextmanager
6a7914 4
d3fe14 5 from webob.acceptparse import create_accept_header
MM 6
0c29cf 7 from zope.interface import implementer, alsoProvides
deb0dc 8
0c29cf 9 from pyramid.interfaces import IRequest, ISession
17b8bc 10
0c29cf 11 from pyramid.compat import PY3, PYPY, class_types, text_
0c1c39 12
427a02 13 from pyramid.config import Configurator
a7b1a9 14 from pyramid.decorator import reify
d24055 15 from pyramid.path import caller_package
25c64c 16 from pyramid.response import _get_response_factory
b60bdb 17 from pyramid.registry import Registry
0c1c39 18
CM 19 from pyramid.security import (
20     Authenticated,
21     Everyone,
3c2f95 22     AuthenticationAPIMixin,
MR 23     AuthorizationAPIMixin,
0c29cf 24 )
0c1c39 25
0c29cf 26 from pyramid.threadlocal import get_current_registry, manager
0c1c39 27
330164 28 from pyramid.i18n import LocalizerRequestMixin
8fe57d 29 from pyramid.request import CallbackMethodsMixin
fb90f0 30 from pyramid.url import URLMethodsMixin
303abc 31 from pyramid.util import InstancePropertyMixin
7f1174 32 from pyramid.view import ViewMethodsMixin
303abc 33
13c923 34
7ec9e7 35 _marker = object()
e8b295 36
0c29cf 37
927acd 38 class DummyRootFactory(object):
CM 39     __parent__ = None
40     __name__ = None
0c29cf 41
e0f40e 42     def __init__(self, request):
CM 43         if 'bfg.routes.matchdict' in request:
44             self.__dict__.update(request['bfg.routes.matchdict'])
927acd 45
0c29cf 46
250c02 47 class DummySecurityPolicy(object):
a1a9fb 48     """ A standin for both an IAuthentication and IAuthorization policy """
0c29cf 49
MM 50     def __init__(
51         self,
52         userid=None,
53         groupids=(),
54         permissive=True,
55         remember_result=None,
56         forget_result=None,
57     ):
deb0dc 58         self.userid = userid
CM 59         self.groupids = groupids
a1a9fb 60         self.permissive = permissive
1273d5 61         if remember_result is None:
CM 62             remember_result = []
63         if forget_result is None:
64             forget_result = []
65         self.remember_result = remember_result
66         self.forget_result = forget_result
deb0dc 67
7ec9e7 68     def authenticated_userid(self, request):
deb0dc 69         return self.userid
CM 70
2526d8 71     def unauthenticated_userid(self, request):
deb0dc 72         return self.userid
CM 73
7ec9e7 74     def effective_principals(self, request):
deb0dc 75         effective_principals = [Everyone]
CM 76         if self.userid:
77             effective_principals.append(Authenticated)
78             effective_principals.append(self.userid)
79             effective_principals.extend(self.groupids)
80         return effective_principals
81
c7afe4 82     def remember(self, request, userid, **kw):
KOP 83         self.remembered = userid
1273d5 84         return self.remember_result
a1a9fb 85
7ec9e7 86     def forget(self, request):
1273d5 87         self.forgotten = True
CM 88         return self.forget_result
a1a9fb 89
CM 90     def permits(self, context, principals, permission):
91         return self.permissive
92
93     def principals_allowed_by_permission(self, context, permission):
7ec9e7 94         return self.effective_principals(None)
deb0dc 95
0c29cf 96
250c02 97 class DummyTemplateRenderer(object):
7d32df 98     """
CM 99     An instance of this class is returned from
d533b1 100     :meth:`pyramid.config.Configurator.testing_add_renderer`.  It has a
addf99 101     helper function (``assert_``) that makes it possible to make an
CM 102     assertion which compares data passed to the renderer by the view
103     function against expected key/value pairs.
7d32df 104     """
0c29cf 105
e02ba1 106     def __init__(self, string_response=''):
7d32df 107         self._received = {}
250c02 108         self._string_response = string_response
CM 109         self._implementation = MockTemplate(string_response)
110
111     # For in-the-wild test code that doesn't create its own renderer,
112     # but mutates our internals instead.  When all you read is the
113     # source code, *everything* is an API!
114     def _get_string_response(self):
115         return self._string_response
0c29cf 116
250c02 117     def _set_string_response(self, response):
CM 118         self._string_response = response
119         self._implementation.response = response
0c29cf 120
250c02 121     string_response = property(_get_string_response, _set_string_response)
CM 122
deb0dc 123     def implementation(self):
250c02 124         return self._implementation
c5f557 125
e46105 126     def __call__(self, kw, system=None):
250c02 127         if system:
CM 128             self._received.update(system)
7d32df 129         self._received.update(kw)
e02ba1 130         return self.string_response
deb0dc 131
7d32df 132     def __getattr__(self, k):
db0467 133         """ Backwards compatibility """
7d32df 134         val = self._received.get(k, _marker)
CM 135         if val is _marker:
250c02 136             val = self._implementation._received.get(k, _marker)
CM 137             if val is _marker:
138                 raise AttributeError(k)
7d32df 139         return val
CM 140
141     def assert_(self, **kw):
142         """ Accept an arbitrary set of assertion key/value pairs.  For
143         each assertion key/value pair assert that the renderer
aaedf5 144         (eg. :func:`pyramid.renderers.render_to_response`)
addf99 145         received the key with a value that equals the asserted
CM 146         value. If the renderer did not receive the key at all, or the
147         value received by the renderer doesn't match the assertion
148         value, raise an :exc:`AssertionError`."""
7d32df 149         for k, v in kw.items():
CM 150             myval = self._received.get(k, _marker)
151             if myval is _marker:
250c02 152                 myval = self._implementation._received.get(k, _marker)
CM 153                 if myval is _marker:
154                     raise AssertionError(
155                         'A value for key "%s" was not passed to the renderer'
0c29cf 156                         % k
MM 157                     )
c5f557 158
7d32df 159             if myval != v:
CM 160                 raise AssertionError(
0c29cf 161                     '\nasserted value for %s: %r\nactual value: %r'
MM 162                     % (k, v, myval)
163                 )
7d32df 164         return True
25c64c 165
7d32df 166
3e2f12 167 class DummyResource:
CM 168     """ A dummy :app:`Pyramid` :term:`resource` object."""
0c29cf 169
MM 170     def __init__(
171         self, __name__=None, __parent__=None, __provides__=None, **kw
172     ):
3e2f12 173         """ The resource's ``__name__`` attribute will be set to the
CM 174         value of the ``__name__`` argument, and the resource's
addf99 175         ``__parent__`` attribute will be set to the value of the
CM 176         ``__parent__`` argument.  If ``__provides__`` is specified, it
177         should be an interface object or tuple of interface objects
3e2f12 178         that will be attached to the resulting resource via
addf99 179         :func:`zope.interface.alsoProvides`. Any extra keywords passed
CM 180         in the ``kw`` argumnent will be set as direct attributes of
fec457 181         the resource object.
CM 182
183         .. note:: For backwards compatibility purposes, this class can also
184                   be imported as :class:`pyramid.testing.DummyModel`.
185
186         """
6a7914 187         self.__name__ = __name__
CM 188         self.__parent__ = __parent__
ab4e7b 189         if __provides__ is not None:
CM 190             alsoProvides(self, __provides__)
6a7914 191         self.kw = kw
CM 192         self.__dict__.update(**kw)
deb0dc 193         self.subs = {}
CM 194
195     def __setitem__(self, name, val):
a9a8a2 196         """ When the ``__setitem__`` method is called, the object
6a7914 197         passed in as ``val`` will be decorated with a ``__parent__``
3e2f12 198         attribute pointing at the dummy resource and a ``__name__``
a9a8a2 199         attribute that is the value of ``name``.  The value will then
3e2f12 200         be returned when dummy resource's ``__getitem__`` is called with
a9a8a2 201         the name ``name```."""
deb0dc 202         val.__name__ = name
CM 203         val.__parent__ = self
204         self.subs[name] = val
c5f557 205
deb0dc 206     def __getitem__(self, name):
a9a8a2 207         """ Return a named subobject (see ``__setitem__``)"""
deb0dc 208         ob = self.subs[name]
CM 209         return ob
6a7914 210
ee036d 211     def __delitem__(self, name):
CM 212         del self.subs[name]
213
40232e 214     def get(self, name, default=None):
CM 215         return self.subs.get(name, default)
216
e8ebc2 217     def values(self):
CM 218         """ Return the values set by __setitem__ """
219         return self.subs.values()
220
221     def items(self):
222         """ Return the items set by __setitem__ """
223         return self.subs.items()
224
225     def keys(self):
226         """ Return the keys set by __setitem__ """
227         return self.subs.keys()
228
40232e 229     __iter__ = keys
CM 230
42b1e9 231     def __nonzero__(self):
TS 232         return True
233
050b71 234     __bool__ = __nonzero__
CM 235
42b1e9 236     def __len__(self):
TS 237         return len(self.subs)
238
e8ebc2 239     def __contains__(self, name):
CM 240         return name in self.subs
c5f557 241
6a7914 242     def clone(self, __name__=_marker, __parent__=_marker, **kw):
3e2f12 243         """ Create a clone of the resource object.  If ``__name__`` or
addf99 244         ``__parent__`` arguments are passed, use these values to
CM 245         override the existing ``__name__`` or ``__parent__`` of the
3e2f12 246         resource.  If any extra keyword args are passed in via the ``kw``
b74cd4 247         argument, use these keywords to add to or override existing
3e2f12 248         resource keywords (attributes)."""
6a7914 249         oldkw = self.kw.copy()
CM 250         oldkw.update(kw)
251         inst = self.__class__(self.__name__, self.__parent__, **oldkw)
252         inst.subs = copy.deepcopy(self.subs)
253         if __name__ is not _marker:
254             inst.__name__ = __name__
255         if __parent__ is not _marker:
256             inst.__parent__ = __parent__
257         return inst
3e2f12 258
0c29cf 259
MM 260 DummyModel = DummyResource  # b/w compat (forever)
261
6a7914 262
3b7334 263 @implementer(ISession)
51b2e8 264 class DummySession(dict):
CM 265     created = None
266     new = True
0c29cf 267
51b2e8 268     def changed(self):
CM 269         pass
270
271     def invalidate(self):
272         self.clear()
273
274     def flash(self, msg, queue='', allow_duplicate=True):
275         storage = self.setdefault('_f_' + queue, [])
276         if allow_duplicate or (msg not in storage):
277             storage.append(msg)
278
279     def pop_flash(self, queue=''):
280         storage = self.pop('_f_' + queue, [])
281         return storage
282
283     def peek_flash(self, queue=''):
284         storage = self.get('_f_' + queue, [])
285         return storage
286
287     def new_csrf_token(self):
183804 288         token = text_('0123456789012345678901234567890123456789')
51b2e8 289         self['_csrft_'] = token
CM 290         return token
291
292     def get_csrf_token(self):
4d2003 293         token = self.get('_csrft_', None)
RB 294         if token is None:
295             token = self.new_csrf_token()
296         return token
297
0c29cf 298
3b7334 299 @implementer(IRequest)
0184b5 300 class DummyRequest(
CM 301     URLMethodsMixin,
302     CallbackMethodsMixin,
303     InstancePropertyMixin,
304     LocalizerRequestMixin,
305     AuthenticationAPIMixin,
306     AuthorizationAPIMixin,
7f1174 307     ViewMethodsMixin,
0c29cf 308 ):
097d37 309     """ A DummyRequest object (incompletely) imitates a :term:`request` object.
c5f557 310
8b1f6e 311     The ``params``, ``environ``, ``headers``, ``path``, and
22d325 312     ``cookies`` arguments correspond to their :term:`WebOb`
8b1f6e 313     equivalents.
6801b5 314
TS 315     The ``post`` argument,  if passed, populates the request's
316     ``POST`` attribute, but *not* ``params``, in order to allow testing
317     that the app accepts data for a given view only from POST requests.
318     This argument also sets ``self.method`` to "POST".
319
320     Extra keyword arguments are assigned as attributes of the request
321     itself.
3e2f12 322
097d37 323     Note that DummyRequest does not have complete fidelity with a "real"
CM 324     request.  For example, by default, the DummyRequest ``GET`` and ``POST``
325     attributes are of type ``dict``, unlike a normal Request's GET and POST,
326     which are of type ``MultiDict``. If your code uses the features of
07cb8f 327     MultiDict, you should either use a real :class:`pyramid.request.Request`
097d37 328     or adapt your DummyRequest by replacing the attributes with ``MultiDict``
CM 329     instances.
330
331     Other similar incompatibilities exist.  If you need all the features of
332     a Request, use the :class:`pyramid.request.Request` class itself rather
333     than this class while writing tests.
6801b5 334     """
0c29cf 335
516729 336     method = 'GET'
CM 337     application_url = 'http://example.com'
338     host = 'example.com:80'
767e44 339     domain = 'example.com'
2dc0b5 340     content_length = 0
c5f557 341     query_string = ''
f2681a 342     charset = 'UTF-8'
0a0edf 343     script_name = ''
a7b1a9 344     _registry = None
d3fe14 345     _accept = None
849196 346     request_iface = IRequest
a7b1a9 347
0c29cf 348     def __init__(
MM 349         self,
350         params=None,
351         environ=None,
352         headers=None,
353         path='/',
354         cookies=None,
355         post=None,
d3fe14 356         accept=None,
0c29cf 357         **kw
MM 358     ):
a9a8a2 359         if environ is None:
CM 360             environ = {}
361         if params is None:
362             params = {}
363         if headers is None:
364             headers = {}
516729 365         if cookies is None:
CM 366             cookies = {}
a9a8a2 367         self.environ = environ
CM 368         self.headers = headers
168c10 369         self.params = params
516729 370         self.cookies = cookies
927acd 371         self.matchdict = {}
168c10 372         self.GET = params
6801b5 373         if post is not None:
TS 374             self.method = 'POST'
375             self.POST = post
376         else:
168c10 377             self.POST = params
a9a8a2 378         self.host_url = self.application_url
CM 379         self.path_url = self.application_url
516729 380         self.url = self.application_url
a9a8a2 381         self.path = path
CM 382         self.path_info = path
383         self.script_name = ''
384         self.path_qs = ''
385         self.body = ''
516729 386         self.view_name = ''
1cd598 387         self.subpath = ()
CM 388         self.traversed = ()
389         self.virtual_root_path = ()
516729 390         self.context = None
1cd598 391         self.root = None
CM 392         self.virtual_root = None
0c29cf 393         self.marshalled = params  # repoze.monty
51b2e8 394         self.session = DummySession()
d3fe14 395         self.accept = accept
a9a8a2 396         self.__dict__.update(kw)
844e98 397
a7b1a9 398     def _get_registry(self):
CM 399         if self._registry is None:
400             return get_current_registry()
401         return self._registry
402
403     def _set_registry(self, registry):
404         self._registry = registry
405
406     def _del_registry(self):
407         self._registry = None
408
409     registry = property(_get_registry, _set_registry, _del_registry)
410
d3fe14 411     def _set_accept(self, value):
MM 412         self._accept = create_accept_header(value)
413
414     def _get_accept(self):
415         if self._accept is None:
416             self._accept = create_accept_header(None)
417         return self._accept
418
419     def _del_accept(self):
420         self._accept = None
421
422     accept = property(_get_accept, _set_accept, _del_accept)
423
a7b1a9 424     @reify
CM 425     def response(self):
25c64c 426         f = _get_response_factory(self.registry)
32cb80 427         return f(self)
8af47b 428
0c29cf 429
bce463 430 have_zca = True
25c64c 431
bce463 432
0c29cf 433 def setUp(
MM 434     registry=None,
435     request=None,
436     hook_zca=True,
437     autocommit=True,
438     settings=None,
439     package=None,
440 ):
a7e6f2 441     """
fd5ae9 442     Set :app:`Pyramid` registry and request thread locals for the
addf99 443     duration of a single unit test.
d0b398 444
7ac5aa 445     Use this function in the ``setUp`` method of a unittest test case
fe399b 446     which directly or indirectly uses:
CM 447
aff443 448     - any method of the :class:`pyramid.config.Configurator`
addf99 449       object returned by this function.
CM 450
c81aad 451     - the :func:`pyramid.threadlocal.get_current_registry` or
CM 452       :func:`pyramid.threadlocal.get_current_request` functions.
d0b398 453
d533b1 454     If you use the ``get_current_*`` functions (or call :app:`Pyramid` code
CM 455     that uses these functions) without calling ``setUp``,
456     :func:`pyramid.threadlocal.get_current_registry` will return a *global*
457     :term:`application registry`, which may cause unit tests to not be
458     isolated with respect to registrations they perform.
d0b398 459
CM 460     If the ``registry`` argument is ``None``, a new empty
addf99 461     :term:`application registry` will be created (an instance of the
c81aad 462     :class:`pyramid.registry.Registry` class).  If the ``registry``
addf99 463     argument is not ``None``, the value passed in should be an
c81aad 464     instance of the :class:`pyramid.registry.Registry` class or a
addf99 465     suitable testing analogue.
CM 466
467     After ``setUp`` is finished, the registry returned by the
8a7d6c 468     :func:`pyramid.threadlocal.get_current_registry` function will
d0b398 469     be the passed (or constructed) registry until
c81aad 470     :func:`pyramid.testing.tearDown` is called (or
CM 471     :func:`pyramid.testing.setUp` is called again) .
d0b398 472
addf99 473     If the ``hook_zca`` argument is ``True``, ``setUp`` will attempt
CM 474     to perform the operation ``zope.component.getSiteManager.sethook(
c81aad 475     pyramid.threadlocal.get_current_registry)``, which will cause
d0b398 476     the :term:`Zope Component Architecture` global API
addf99 477     (e.g. :func:`zope.component.getSiteManager`,
CM 478     :func:`zope.component.getAdapter`, and so on) to use the registry
479     constructed by ``setUp`` as the value it returns from
480     :func:`zope.component.getSiteManager`.  If the
481     :mod:`zope.component` package cannot be imported, or if
482     ``hook_zca`` is ``False``, the hook will not be set.
483
d24055 484     If ``settings`` is not ``None``, it must be a dictionary representing the
2a13b0 485     values passed to a Configurator as its ``settings=`` argument.
d24055 486
MM 487     If ``package`` is ``None`` it will be set to the caller's package. The
488     ``package`` setting in the :class:`pyramid.config.Configurator` will
489     affect any relative imports made via
490     :meth:`pyramid.config.Configurator.include` or
491     :meth:`pyramid.config.Configurator.maybe_dotted`.
2a13b0 492
addf99 493     This function returns an instance of the
aff443 494     :class:`pyramid.config.Configurator` class, which can be
addf99 495     used for further configuration to set up an environment suitable
CM 496     for a unit or integration test.  The ``registry`` attribute
497     attached to the Configurator instance represents the 'current'
498     :term:`application registry`; the same registry will be returned
c81aad 499     by :func:`pyramid.threadlocal.get_current_registry` during the
addf99 500     execution of the test.
d322ac 501     """
cbfafb 502     manager.clear()
d0b398 503     if registry is None:
CM 504         registry = Registry('testing')
d24055 505     if package is None:
MM 506         package = caller_package()
0c29cf 507     config = Configurator(
MM 508         registry=registry, autocommit=autocommit, package=package
509     )
2a13b0 510     if settings is None:
CM 511         settings = {}
512     if getattr(registry, 'settings', None) is None:
513         config._set_settings(settings)
250c02 514     if hasattr(registry, 'registerUtility'):
CM 515         # Sometimes nose calls us with a non-registry object because
516         # it thinks this function is module test setup.  Likewise,
517         # someone may be passing us an esoteric "dummy" registry, and
518         # the below won't succeed if it doesn't have a registerUtility
519         # method.
121f45 520         config.add_default_response_adapters()
b7e92d 521         config.add_default_renderers()
121f45 522         config.add_default_accept_view_order()
a00621 523         config.add_default_view_predicates()
ceb1f2 524         config.add_default_view_derivers()
9c8ec5 525         config.add_default_route_predicates()
35b0e3 526         config.add_default_tweens()
4b3603 527         config.add_default_security()
c0d4a1 528     config.commit()
bce463 529     global have_zca
CM 530     try:
531         have_zca and hook_zca and config.hook_zca()
0c29cf 532     except ImportError:  # pragma: no cover
338cb9 533         # (dont choke on not being able to import z.component)
bce463 534         have_zca = False
bc857e 535     config.begin(request=request)
addf99 536     return config
0c29cf 537
d0b398 538
CM 539 def tearDown(unhook_zca=True):
b9b3b1 540     """Undo the effects of :func:`pyramid.testing.setUp`.  Use this
addf99 541     function in the ``tearDown`` method of a unit test that uses
c81aad 542     :func:`pyramid.testing.setUp` in its ``setUp`` method.
d0b398 543
CM 544     If the ``unhook_zca`` argument is ``True`` (the default), call
addf99 545     :func:`zope.component.getSiteManager.reset`.  This undoes the
688a93 546     action of :func:`pyramid.testing.setUp` when called with the
addf99 547     argument ``hook_zca=True``.  If :mod:`zope.component` cannot be
688a93 548     imported, ``unhook_zca`` is set to ``False``.
d0b398 549     """
bce463 550     global have_zca
CM 551     if unhook_zca and have_zca:
7696aa 552         try:
CM 553             from zope.component import getSiteManager
0c29cf 554
7696aa 555             getSiteManager.reset()
0c29cf 556         except ImportError:  # pragma: no cover
bce463 557             have_zca = False
d0b398 558     info = manager.pop()
CM 559     manager.clear()
560     if info is not None:
bc857e 561         registry = info['registry']
CM 562         if hasattr(registry, '__init__') and hasattr(registry, '__name__'):
7ac5aa 563             try:
bc857e 564                 registry.__init__(registry.__name__)
7ac5aa 565             except TypeError:
99fc64 566                 # calling __init__ is largely for the benefit of
CM 567                 # people who want to use the global ZCA registry;
568                 # however maybe somebody's using a registry we don't
569                 # understand, let's not blow up
7ac5aa 570                 pass
17b8bc 571
0c29cf 572
d0b398 573 def cleanUp(*arg, **kw):
c0d0a3 574     """ An alias for :func:`pyramid.testing.setUp`. """
d24055 575     package = kw.get('package', None)
MM 576     if package is None:
577         package = caller_package()
578         kw['package'] = package
addf99 579     return setUp(*arg, **kw)
0c29cf 580
59c5df 581
250c02 582 class DummyRendererFactory(object):
CM 583     """ Registered by
d533b1 584     :meth:`pyramid.config.Configurator.testing_add_renderer` as
250c02 585     a dummy renderer factory.  The indecision about what to use as a
CM 586     key (a spec vs. a relative name) is caused by test suites in the
587     wild believing they can register either.  The ``factory`` argument
588     passed to this constructor is usually the *real* template renderer
589     factory, found when ``testing_add_renderer`` is called."""
0c29cf 590
250c02 591     def __init__(self, name, factory):
CM 592         self.name = name
0c29cf 593         self.factory = factory  # the "real" renderer factory reg'd previously
250c02 594         self.renderers = {}
CM 595
596     def add(self, spec, renderer):
597         self.renderers[spec] = renderer
598         if ':' in spec:
599             package, relative = spec.split(':', 1)
600             self.renderers[relative] = renderer
601
3d9dd0 602     def __call__(self, info):
f5fa3f 603         spec = info.name
250c02 604         renderer = self.renderers.get(spec)
CM 605         if renderer is None:
606             if ':' in spec:
607                 package, relative = spec.split(':', 1)
608                 renderer = self.renderers.get(relative)
609             if renderer is None:
610                 if self.factory:
3d9dd0 611                     renderer = self.factory(info)
250c02 612                 else:
0c29cf 613                     raise KeyError(
MM 614                         'No testing renderer registered for %r' % spec
615                     )
250c02 616         return renderer
c5f557 617
CR 618
250c02 619 class MockTemplate(object):
CM 620     def __init__(self, response):
621         self._received = {}
622         self.response = response
0c29cf 623
250c02 624     def __getattr__(self, attrname):
CM 625         return self
0c29cf 626
250c02 627     def __getitem__(self, attrname):
CM 628         return self
0c29cf 629
250c02 630     def __call__(self, *arg, **kw):
CM 631         self._received.update(kw)
632         return self.response
35c5a3 633
0c29cf 634
MM 635 def skip_on(*platforms):  # pragma: no  cover
a84e17 636     skip = False
CM 637     for platform in platforms:
638         if skip_on.os_name.startswith(platform):
639             skip = True
160865 640         if platform == 'pypy' and PYPY:
a84e17 641             skip = True
160865 642         if platform == 'py3' and PY3:
a84e17 643             skip = True
25c64c 644
35c5a3 645     def decorator(func):
a84e17 646         if isinstance(func, class_types):
25c64c 647             if skip:
JA 648                 return None
649             else:
650                 return func
a84e17 651         else:
0c29cf 652
a84e17 653             def wrapper(*args, **kw):
CM 654                 if skip:
35c5a3 655                     return
a84e17 656                 return func(*args, **kw)
0c29cf 657
a84e17 658             wrapper.__name__ = func.__name__
CM 659             wrapper.__doc__ = func.__doc__
660             return wrapper
0c29cf 661
35c5a3 662     return decorator
0c29cf 663
MM 664
665 skip_on.os_name = os.name  # for testing
666
fbbb20 667
BS 668 @contextmanager
0c29cf 669 def testConfig(
MM 670     registry=None, request=None, hook_zca=True, autocommit=True, settings=None
671 ):
fbbb20 672     """Returns a context manager for test set up.
BS 673
aaedf5 674     This context manager calls :func:`pyramid.testing.setUp` when
fbbb20 675     entering and :func:`pyramid.testing.tearDown` when exiting.
BS 676
aaedf5 677     All arguments are passed directly to :func:`pyramid.testing.setUp`.
fbbb20 678     If the ZCA is hooked, it will always be un-hooked in tearDown.
BS 679
680     This context manager allows you to write test code like this:
681
682     .. code-block:: python
683         :linenos:
684
685         with testConfig() as config:
686             config.add_route('bar', '/bar/{id}')
687             req = DummyRequest()
b2fb29 688             resp = myview(req)
fbbb20 689     """
0c29cf 690     config = setUp(
MM 691         registry=registry,
692         request=request,
693         hook_zca=hook_zca,
694         autocommit=autocommit,
695         settings=settings,
696     )
fbbb20 697     try:
BS 698         yield config
699     finally:
700         tearDown(unhook_zca=hook_zca)