Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit80aa405

Browse files
committed
Added auto-skip mixin metacls, some serious brainfuck, if the required module was not found. Its actually a nice mix between decorators which are class types, and a mixin as a metaclass, which applies said decorator. The InstanceDecorator wouldn't actually be needed, but it adds flexibility. Maybe it should be removed ...
1 parent690828c commit80aa405

File tree

3 files changed

+123
-14
lines changed

3 files changed

+123
-14
lines changed

‎git/test/db/dulwich/lib.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""dulwich specific utilities, as well as all the default ones"""
2+
3+
fromgit.test.libimport*
4+
5+
#{ Decoorators
6+
7+
defneeds_dulwich_or_skip(func):
8+
"""Skip this test if we have no dulwich - print warning"""
9+
returnneeds_module_or_skip('dulwich')(func)
10+
11+
#}END decorators
12+
13+
#{ MetaClasses
14+
15+
classDulwichRequiredMetaMixin(InheritedTestMethodsOverrideWrapperMetaClsAutoMixin):
16+
decorator= [needs_dulwich_or_skip]
17+
18+
#} END metaclasses

‎git/test/db/dulwich/test_base.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,22 @@
22
#
33
# This module is part of GitDB and is released under
44
# the New BSD License: http://www.opensource.org/licenses/bsd-license.php
5+
fromlibimport*
56
fromgit.test.db.baseimportRepoBase
67
fromgit.db.compleximportPureCompatibilityGitDB
78

89
try:
9-
importgit.db.dulwich# import test
10+
importdulwich
11+
exceptImportError:
12+
# om this case, all other dulwich tests will be skipped
13+
pass
1014

11-
classTestPyDBBase(RepoBase):
12-
13-
RepoCls=PureCompatibilityGitDB
15+
classTestPyDBBase(RepoBase):
16+
__metaclass__=DulwichRequiredMetaMixin
17+
RepoCls=PureCompatibilityGitDB
18+
19+
@needs_dulwich_or_skip
20+
deftest_basics(self):
21+
importdulwich
22+
pass
1423

15-
#def test_basics(self):
16-
#pass
17-
18-
exceptImportError:
19-
del(RepoBase)
20-
importwarnings
21-
warnings.warn("Skipped all dulwich tests as they are not in the path")
22-
#END handle import

‎git/test/lib/helper.py

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,19 @@
1212
importshutil
1313
importcStringIO
1414

15+
importwarnings
16+
fromnoseimportSkipTest
17+
1518
frombaseimport (
1619
maketemp,
1720
rorepo_dir
1821
)
1922

2023

2124
__all__= (
22-
'StringProcessAdapter','GlobalsItemDeletorMetaCls',
23-
'with_rw_repo','with_rw_and_rw_remote_repo','TestBase','TestCase',
25+
'StringProcessAdapter','GlobalsItemDeletorMetaCls','InheritedTestMethodsOverrideWrapperInstanceDecorator',
26+
'InheritedTestMethodsOverrideWrapperMetaClsAutoMixin',
27+
'with_rw_repo','with_rw_and_rw_remote_repo','TestBase','TestCase','needs_module_or_skip'
2428
)
2529

2630

@@ -191,6 +195,27 @@ def remote_repo_creator(self):
191195

192196
returnargument_passer
193197

198+
defneeds_module_or_skip(module):
199+
"""Decorator to be used for test cases only.
200+
Print a warning if the given module could not be imported, and skip the test.
201+
Otherwise run the test as usual
202+
:param module: the name of the module to skip"""
203+
defargpasser(func):
204+
defwrapper(self,*args,**kwargs):
205+
try:
206+
__import__(module)
207+
exceptImportError:
208+
msg="Module %r is required to run this test - skipping"%module
209+
warnings.warn(msg)
210+
raiseSkipTest(msg)
211+
#END check import
212+
returnfunc(self,*args,**kwargs)
213+
#END wrapper
214+
wrapper.__name__=func.__name__
215+
returnwrapper
216+
#END argpasser
217+
returnargpasser
218+
194219
#} END decorators
195220

196221
#{ Meta Classes
@@ -214,6 +239,71 @@ def __new__(metacls, name, bases, clsdict):
214239
#END skip case that people import our base without actually using it
215240
#END handle deletion
216241
returnnew_type
242+
243+
244+
classInheritedTestMethodsOverrideWrapperInstanceDecorator(object):
245+
"""Utility to wrap all inherited methods into a given decorator and set up new
246+
overridden methods on our actual type. This allows to adjust tests which are inherited
247+
by our parent type, automatically. The decorator set in a derived type should
248+
do what it has to do, possibly skipping the test if some prerequesites are not met.
249+
250+
To use it, instatiate it and use it as a wrapper for the __new__ function of your metacls, as in
251+
252+
__new__ = @InheritedTestMethodsOverrideWrapperInstanceDecorator(mydecorator)(MyMetaclsBase.__new__)"""
253+
254+
255+
def__init__(self,decorator):
256+
self.decorator=decorator
257+
258+
def_patch_methods_recursive(self,bases,clsdict):
259+
"""depth-first patching of methods"""
260+
forbaseinbases:
261+
self._patch_methods_recursive(base.__bases__,clsdict)
262+
forname,iteminbase.__dict__.iteritems():
263+
ifnotname.startswith('test_'):
264+
continue
265+
#END skip non-tests
266+
clsdict[name]=self.decorator(item)
267+
#END for each item
268+
#END for each base
269+
270+
def__call__(self,func):
271+
defwrapper(metacls,name,bases,clsdict):
272+
self._patch_methods_recursive(bases,clsdict)
273+
returnfunc(metacls,name,bases,clsdict)
274+
#END wrapper
275+
assertfunc.__name__=='__new__',"Can only wrap __new__ function of metaclasses"
276+
wrapper.__name__=func.__name__
277+
returnwrapper
278+
279+
280+
281+
classInheritedTestMethodsOverrideWrapperMetaClsAutoMixin(object):
282+
"""Automatically picks up the actual metaclass of the the type to be created,
283+
that is the one inherited by one of the bases, and patch up its __new__ to use
284+
the InheritedTestMethodsOverrideWrapperInstanceDecorator with our configured decorator"""
285+
286+
#{ Configuration
287+
# decorator function to use when wrapping the inherited methods. Put it into a list as first member
288+
# to hide it from being created as class method
289+
decorator= []
290+
#}END configuration
291+
292+
@classmethod
293+
def_find_metacls(metacls,bases):
294+
"""emulate pythons lookup"""
295+
mcls_attr='__metaclass__'
296+
forbaseinbases:
297+
ifhasattr(base,mcls_attr):
298+
returngetattr(base,mcls_attr)
299+
returnmetacls._find_metacls(base.__bases__)
300+
#END for each base
301+
raiseAssertionError("base class had not metaclass attached")
302+
303+
def__new__(metacls,name,bases,clsdict):
304+
assertmetacls.decorator,"'decorator' member needs to be set in subclass"
305+
base_metacls=metacls._find_metacls(bases)
306+
returnInheritedTestMethodsOverrideWrapperInstanceDecorator(metacls.decorator[0])(base_metacls.__new__)(base_metacls,name,bases,clsdict)
217307

218308
#} END meta classes
219309

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp