##// END OF EJS Templates
Fix error in test decorator.
Fernando Perez -
Show More
@@ -1,146 +1,147 b''
1 """Decorators for labeling test objects.
1 """Decorators for labeling test objects.
2
2
3 Decorators that merely return a modified version of the original
3 Decorators that merely return a modified version of the original
4 function object are straightforward. Decorators that return a new
4 function object are straightforward. Decorators that return a new
5 function object need to use
5 function object need to use
6 nose.tools.make_decorator(original_function)(decorator) in returning
6 nose.tools.make_decorator(original_function)(decorator) in returning
7 the decorator, in order to preserve metadata such as function name,
7 the decorator, in order to preserve metadata such as function name,
8 setup and teardown functions and so on - see nose.tools for more
8 setup and teardown functions and so on - see nose.tools for more
9 information.
9 information.
10
10
11 NOTE: This file contains IPython-specific decorators and imports the
11 NOTE: This file contains IPython-specific decorators and imports the
12 numpy.testing.decorators file, which we've copied verbatim. Any of our own
12 numpy.testing.decorators file, which we've copied verbatim. Any of our own
13 code will be added at the bottom if we end up extending this.
13 code will be added at the bottom if we end up extending this.
14 """
14 """
15
15
16 # Stdlib imports
16 # Stdlib imports
17 import inspect
17 import inspect
18
18
19 # Third-party imports
19 # Third-party imports
20
20
21 # This is Michele Simionato's decorator module, also kept verbatim.
21 # This is Michele Simionato's decorator module, also kept verbatim.
22 from decorator_msim import decorator, update_wrapper
22 from decorator_msim import decorator, update_wrapper
23
23
24 # Grab the numpy-specific decorators which we keep in a file that we
24 # Grab the numpy-specific decorators which we keep in a file that we
25 # occasionally update from upstream: decorators_numpy.py is an IDENTICAL copy
25 # occasionally update from upstream: decorators_numpy.py is an IDENTICAL copy
26 # of numpy.testing.decorators.
26 # of numpy.testing.decorators.
27 from decorators_numpy import *
27 from decorators_numpy import *
28
28
29 ##############################################################################
29 ##############################################################################
30 # Local code begins
30 # Local code begins
31
31
32 # Utility functions
32 # Utility functions
33
33
34 def apply_wrapper(wrapper,func):
34 def apply_wrapper(wrapper,func):
35 """Apply a wrapper to a function for decoration.
35 """Apply a wrapper to a function for decoration.
36
36
37 This mixes Michele Simionato's decorator tool with nose's make_decorator,
37 This mixes Michele Simionato's decorator tool with nose's make_decorator,
38 to apply a wrapper in a decorator so that all nose attributes, as well as
38 to apply a wrapper in a decorator so that all nose attributes, as well as
39 function signature and other properties, survive the decoration cleanly.
39 function signature and other properties, survive the decoration cleanly.
40 This will ensure that wrapped functions can still be well introspected via
40 This will ensure that wrapped functions can still be well introspected via
41 IPython, for example.
41 IPython, for example.
42 """
42 """
43 import nose.tools
43 import nose.tools
44
44
45 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
45 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
46
46
47
47
48 def make_label_dec(label,ds=None):
48 def make_label_dec(label,ds=None):
49 """Factory function to create a decorator that applies one or more labels.
49 """Factory function to create a decorator that applies one or more labels.
50
50
51 :Parameters:
51 :Parameters:
52 label : string or sequence
52 label : string or sequence
53 One or more labels that will be applied by the decorator to the functions
53 One or more labels that will be applied by the decorator to the functions
54 it decorates. Labels are attributes of the decorated function with their
54 it decorates. Labels are attributes of the decorated function with their
55 value set to True.
55 value set to True.
56
56
57 :Keywords:
57 :Keywords:
58 ds : string
58 ds : string
59 An optional docstring for the resulting decorator. If not given, a
59 An optional docstring for the resulting decorator. If not given, a
60 default docstring is auto-generated.
60 default docstring is auto-generated.
61
61
62 :Returns:
62 :Returns:
63 A decorator.
63 A decorator.
64
64
65 :Examples:
65 :Examples:
66
66
67 A simple labeling decorator:
67 A simple labeling decorator:
68 >>> slow = make_label_dec('slow')
68 >>> slow = make_label_dec('slow')
69 >>> print slow.__doc__
69 >>> print slow.__doc__
70 Labels a test as 'slow'.
70 Labels a test as 'slow'.
71
71
72 And one that uses multiple labels and a custom docstring:
72 And one that uses multiple labels and a custom docstring:
73 >>> rare = make_label_dec(['slow','hard'],
73 >>> rare = make_label_dec(['slow','hard'],
74 ... "Mix labels 'slow' and 'hard' for rare tests.")
74 ... "Mix labels 'slow' and 'hard' for rare tests.")
75 >>> print rare.__doc__
75 >>> print rare.__doc__
76 Mix labels 'slow' and 'hard' for rare tests.
76 Mix labels 'slow' and 'hard' for rare tests.
77
77
78 Now, let's test using this one:
78 Now, let's test using this one:
79 >>> @rare
79 >>> @rare
80 ... def f(): pass
80 ... def f(): pass
81 ...
81 ...
82 >>>
82 >>>
83 >>> f.slow
83 >>> f.slow
84 True
84 True
85 >>> f.hard
85 >>> f.hard
86 True
86 True
87 """
87 """
88
88
89 if isinstance(label,basestring):
89 if isinstance(label,basestring):
90 labels = [label]
90 labels = [label]
91 else:
91 else:
92 labels = label
92 labels = label
93
93
94 # Validate that the given label(s) are OK for use in setattr() by doing a
94 # Validate that the given label(s) are OK for use in setattr() by doing a
95 # dry run on a dummy function.
95 # dry run on a dummy function.
96 tmp = lambda : None
96 tmp = lambda : None
97 for label in labels:
97 for label in labels:
98 setattr(tmp,label,True)
98 setattr(tmp,label,True)
99
99
100 # This is the actual decorator we'll return
100 # This is the actual decorator we'll return
101 def decor(f):
101 def decor(f):
102 for label in labels:
102 for label in labels:
103 setattr(f,label,True)
103 setattr(f,label,True)
104 return f
104 return f
105
105
106 # Apply the user's docstring, or autogenerate a basic one
106 # Apply the user's docstring, or autogenerate a basic one
107 if ds is None:
107 if ds is None:
108 ds = "Labels a test as %r." % label
108 ds = "Labels a test as %r." % label
109 decor.__doc__ = ds
109 decor.__doc__ = ds
110
110
111 return decor
111 return decor
112
112
113 #-----------------------------------------------------------------------------
113 #-----------------------------------------------------------------------------
114 # Decorators for public use
114 # Decorators for public use
115
115
116 skip_doctest = make_label_dec('skip_doctest',
116 skip_doctest = make_label_dec('skip_doctest',
117 """Decorator - mark a function or method for skipping its doctest.
117 """Decorator - mark a function or method for skipping its doctest.
118
118
119 This decorator allows you to mark a function whose docstring you wish to
119 This decorator allows you to mark a function whose docstring you wish to
120 omit from testing, while preserving the docstring for introspection, help,
120 omit from testing, while preserving the docstring for introspection, help,
121 etc.""")
121 etc.""")
122
122
123
123 def skip(msg=''):
124 def skip(func,msg=''):
125 """Decorator - mark a test function for skipping from test suite.
124 """Decorator - mark a test function for skipping from test suite.
126
125
127 :Parameters:
126 :Parameters:
128
127
129 func : function
128 func : function
130 Test function to be skipped
129 Test function to be skipped
131
130
132 msg : string
131 msg : string
133 Optional message to be added.
132 Optional message to be added.
134 """
133 """
135
134
136 import nose
135 import nose
137
138 def wrapper(*a,**k):
139 if msg: out = '\n'+msg
140 else: out = ''
141 raise nose.SkipTest("Skipping test for function: %s%s" %
142 (func.__name__,out))
143
144 return apply_wrapper(wrapper,func)
145
136
137 def inner(func):
138
139 def wrapper(*a,**k):
140 if msg: out = '\n'+msg
141 else: out = ''
142 raise nose.SkipTest("Skipping test for function: %s%s" %
143 (func.__name__,out))
144
145 return apply_wrapper(wrapper,func)
146
146
147 return inner
@@ -1,180 +1,185 b''
1 # Module imports
1 # Module imports
2 # Std lib
2 # Std lib
3 import inspect
3 import inspect
4
4
5 # Third party
5 # Third party
6
6
7 # Our own
7 # Our own
8 from IPython.testing import decorators as dec
8 from IPython.testing import decorators as dec
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Utilities
11 # Utilities
12
12
13 # Note: copied from OInspect, kept here so the testing stuff doesn't create
13 # Note: copied from OInspect, kept here so the testing stuff doesn't create
14 # circular dependencies and is easier to reuse.
14 # circular dependencies and is easier to reuse.
15 def getargspec(obj):
15 def getargspec(obj):
16 """Get the names and default values of a function's arguments.
16 """Get the names and default values of a function's arguments.
17
17
18 A tuple of four things is returned: (args, varargs, varkw, defaults).
18 A tuple of four things is returned: (args, varargs, varkw, defaults).
19 'args' is a list of the argument names (it may contain nested lists).
19 'args' is a list of the argument names (it may contain nested lists).
20 'varargs' and 'varkw' are the names of the * and ** arguments or None.
20 'varargs' and 'varkw' are the names of the * and ** arguments or None.
21 'defaults' is an n-tuple of the default values of the last n arguments.
21 'defaults' is an n-tuple of the default values of the last n arguments.
22
22
23 Modified version of inspect.getargspec from the Python Standard
23 Modified version of inspect.getargspec from the Python Standard
24 Library."""
24 Library."""
25
25
26 if inspect.isfunction(obj):
26 if inspect.isfunction(obj):
27 func_obj = obj
27 func_obj = obj
28 elif inspect.ismethod(obj):
28 elif inspect.ismethod(obj):
29 func_obj = obj.im_func
29 func_obj = obj.im_func
30 else:
30 else:
31 raise TypeError, 'arg is not a Python function'
31 raise TypeError, 'arg is not a Python function'
32 args, varargs, varkw = inspect.getargs(func_obj.func_code)
32 args, varargs, varkw = inspect.getargs(func_obj.func_code)
33 return args, varargs, varkw, func_obj.func_defaults
33 return args, varargs, varkw, func_obj.func_defaults
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Testing functions
36 # Testing functions
37
37
38 def test_trivial():
38 def test_trivial():
39 """A trivial passing test."""
39 """A trivial passing test."""
40 pass
40 pass
41
41
42
42
43 @dec.skip
43 @dec.skip
44 def test_deliberately_broken():
44 def test_deliberately_broken():
45 """A deliberately broken test - we want to skip this one."""
45 """A deliberately broken test - we want to skip this one."""
46 1/0
46 1/0
47
47
48 @dec.skip('foo')
49 def test_deliberately_broken2():
50 """Another deliberately broken test - we want to skip this one."""
51 1/0
52
48
53
49 # Verify that we can correctly skip the doctest for a function at will, but
54 # Verify that we can correctly skip the doctest for a function at will, but
50 # that the docstring itself is NOT destroyed by the decorator.
55 # that the docstring itself is NOT destroyed by the decorator.
51 @dec.skip_doctest
56 @dec.skip_doctest
52 def doctest_bad(x,y=1,**k):
57 def doctest_bad(x,y=1,**k):
53 """A function whose doctest we need to skip.
58 """A function whose doctest we need to skip.
54
59
55 >>> 1+1
60 >>> 1+1
56 3
61 3
57 """
62 """
58 print 'x:',x
63 print 'x:',x
59 print 'y:',y
64 print 'y:',y
60 print 'k:',k
65 print 'k:',k
61
66
62
67
63 def call_doctest_bad():
68 def call_doctest_bad():
64 """Check that we can still call the decorated functions.
69 """Check that we can still call the decorated functions.
65
70
66 >>> doctest_bad(3,y=4)
71 >>> doctest_bad(3,y=4)
67 x: 3
72 x: 3
68 y: 4
73 y: 4
69 k: {}
74 k: {}
70 """
75 """
71 pass
76 pass
72
77
73
78
74 # Doctest skipping should work for class methods too
79 # Doctest skipping should work for class methods too
75 class foo(object):
80 class foo(object):
76 """Foo
81 """Foo
77
82
78 Example:
83 Example:
79
84
80 >>> 1+1
85 >>> 1+1
81 2
86 2
82 """
87 """
83
88
84 @dec.skip_doctest
89 @dec.skip_doctest
85 def __init__(self,x):
90 def __init__(self,x):
86 """Make a foo.
91 """Make a foo.
87
92
88 Example:
93 Example:
89
94
90 >>> f = foo(3)
95 >>> f = foo(3)
91 junk
96 junk
92 """
97 """
93 print 'Making a foo.'
98 print 'Making a foo.'
94 self.x = x
99 self.x = x
95
100
96 @dec.skip_doctest
101 @dec.skip_doctest
97 def bar(self,y):
102 def bar(self,y):
98 """Example:
103 """Example:
99
104
100 >>> f = foo(3)
105 >>> f = foo(3)
101 >>> f.bar(0)
106 >>> f.bar(0)
102 boom!
107 boom!
103 >>> 1/0
108 >>> 1/0
104 bam!
109 bam!
105 """
110 """
106 return 1/y
111 return 1/y
107
112
108 def baz(self,y):
113 def baz(self,y):
109 """Example:
114 """Example:
110
115
111 >>> f = foo(3)
116 >>> f = foo(3)
112 Making a foo.
117 Making a foo.
113 >>> f.baz(3)
118 >>> f.baz(3)
114 True
119 True
115 """
120 """
116 return self.x==y
121 return self.x==y
117
122
118
123
119 def test_skip_dt_decorator():
124 def test_skip_dt_decorator():
120 """Doctest-skipping decorator should preserve the docstring.
125 """Doctest-skipping decorator should preserve the docstring.
121 """
126 """
122 # Careful: 'check' must be a *verbatim* copy of the doctest_bad docstring!
127 # Careful: 'check' must be a *verbatim* copy of the doctest_bad docstring!
123 check = """A function whose doctest we need to skip.
128 check = """A function whose doctest we need to skip.
124
129
125 >>> 1+1
130 >>> 1+1
126 3
131 3
127 """
132 """
128 # Fetch the docstring from doctest_bad after decoration.
133 # Fetch the docstring from doctest_bad after decoration.
129 val = doctest_bad.__doc__
134 val = doctest_bad.__doc__
130
135
131 assert check==val,"doctest_bad docstrings don't match"
136 assert check==val,"doctest_bad docstrings don't match"
132
137
133
138
134 def test_skip_dt_decorator2():
139 def test_skip_dt_decorator2():
135 """Doctest-skipping decorator should preserve function signature.
140 """Doctest-skipping decorator should preserve function signature.
136 """
141 """
137 # Hardcoded correct answer
142 # Hardcoded correct answer
138 dtargs = (['x', 'y'], None, 'k', (1,))
143 dtargs = (['x', 'y'], None, 'k', (1,))
139 # Introspect out the value
144 # Introspect out the value
140 dtargsr = getargspec(doctest_bad)
145 dtargsr = getargspec(doctest_bad)
141 assert dtargsr==dtargs, \
146 assert dtargsr==dtargs, \
142 "Incorrectly reconstructed args for doctest_bad: %s" % (dtargsr,)
147 "Incorrectly reconstructed args for doctest_bad: %s" % (dtargsr,)
143
148
144
149
145 def doctest_run():
150 def doctest_run():
146 """Test running a trivial script.
151 """Test running a trivial script.
147
152
148 In [13]: run simplevars.py
153 In [13]: run simplevars.py
149 x is: 1
154 x is: 1
150 """
155 """
151
156
152 #@dec.skip_doctest
157 #@dec.skip_doctest
153 def doctest_runvars():
158 def doctest_runvars():
154 """Test that variables defined in scripts get loaded correcly via %run.
159 """Test that variables defined in scripts get loaded correcly via %run.
155
160
156 In [13]: run simplevars.py
161 In [13]: run simplevars.py
157 x is: 1
162 x is: 1
158
163
159 In [14]: x
164 In [14]: x
160 Out[14]: 1
165 Out[14]: 1
161 """
166 """
162
167
163 def doctest_ivars():
168 def doctest_ivars():
164 """Test that variables defined interactively are picked up.
169 """Test that variables defined interactively are picked up.
165 In [5]: zz=1
170 In [5]: zz=1
166
171
167 In [6]: zz
172 In [6]: zz
168 Out[6]: 1
173 Out[6]: 1
169 """
174 """
170
175
171 @dec.skip_doctest
176 @dec.skip_doctest
172 def doctest_refs():
177 def doctest_refs():
173 """DocTest reference holding issues when running scripts.
178 """DocTest reference holding issues when running scripts.
174
179
175 In [32]: run show_refs.py
180 In [32]: run show_refs.py
176 c referrers: [<type 'dict'>]
181 c referrers: [<type 'dict'>]
177
182
178 In [33]: map(type,gc.get_referrers(c))
183 In [33]: map(type,gc.get_referrers(c))
179 Out[33]: [<type 'dict'>]
184 Out[33]: [<type 'dict'>]
180 """
185 """
General Comments 0
You need to be logged in to leave comments. Login now