1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

"""Attribute selector plugin. 

 

Oftentimes when testing you will want to select tests based on 

criteria rather then simply by filename. For example, you might want 

to run all tests except for the slow ones. You can do this with the 

Attribute selector plugin by setting attributes on your test methods. 

Here is an example: 

 

.. code-block:: python 

 

def test_big_download(): 

import urllib 

# commence slowness... 

 

test_big_download.slow = 1 

 

Once you've assigned an attribute ``slow = 1`` you can exclude that 

test and all other tests having the slow attribute by running :: 

 

$ nosetests -a '!slow' 

 

There is also a decorator available for you that will set attributes. 

Here's how to set ``slow=1`` like above with the decorator: 

 

.. code-block:: python 

 

from nose.plugins.attrib import attr 

@attr('slow') 

def test_big_download(): 

import urllib 

# commence slowness... 

 

And here's how to set an attribute with a specific value: 

 

.. code-block:: python 

 

from nose.plugins.attrib import attr 

@attr(speed='slow') 

def test_big_download(): 

import urllib 

# commence slowness... 

 

This test could be run with :: 

 

$ nosetests -a speed=slow 

 

In Python 2.6 and higher, ``@attr`` can be used on a class to set attributes 

on all its test methods at once. For example: 

 

.. code-block:: python 

 

from nose.plugins.attrib import attr 

@attr(speed='slow') 

class MyTestCase: 

def test_long_integration(self): 

pass 

def test_end_to_end_something(self): 

pass 

 

Below is a reference to the different syntaxes available. 

 

Simple syntax 

------------- 

 

Examples of using the ``-a`` and ``--attr`` options: 

 

* ``nosetests -a status=stable`` 

Only runs tests with attribute "status" having value "stable" 

 

* ``nosetests -a priority=2,status=stable`` 

Runs tests having both attributes and values 

 

* ``nosetests -a priority=2 -a slow`` 

Runs tests that match either attribute 

 

* ``nosetests -a tags=http`` 

If a test's ``tags`` attribute was a list and it contained the value 

``http`` then it would be run 

 

* ``nosetests -a slow`` 

Runs tests with the attribute ``slow`` if its value does not equal False 

(False, [], "", etc...) 

 

* ``nosetests -a '!slow'`` 

Runs tests that do NOT have the attribute ``slow`` or have a ``slow`` 

attribute that is equal to False 

**NOTE**: 

if your shell (like bash) interprets '!' as a special character make sure to 

put single quotes around it. 

 

Expression Evaluation 

--------------------- 

 

Examples using the ``-A`` and ``--eval-attr`` options: 

 

* ``nosetests -A "not slow"`` 

Evaluates the Python expression "not slow" and runs the test if True 

 

* ``nosetests -A "(priority > 5) and not slow"`` 

Evaluates a complex Python expression and runs the test if True 

 

""" 

import inspect 

import logging 

import os 

import sys 

from inspect import isfunction 

from nose.plugins.base import Plugin 

from nose.util import tolist 

 

log = logging.getLogger('nose.plugins.attrib') 

compat_24 = sys.version_info >= (2, 4) 

 

def attr(*args, **kwargs): 

"""Decorator that adds attributes to classes or functions 

for use with the Attribute (-a) plugin. 

""" 

def wrap_ob(ob): 

for name in args: 

setattr(ob, name, True) 

for name, value in kwargs.items(): 

setattr(ob, name, value) 

return ob 

return wrap_ob 

 

def get_method_attr(method, cls, attr_name, default = False): 

"""Look up an attribute on a method/ function.  

If the attribute isn't found there, looking it up in the 

method's class, if any. 

""" 

Missing = object() 

value = getattr(method, attr_name, Missing) 

if value is Missing and cls is not None: 

value = getattr(cls, attr_name, Missing) 

if value is Missing: 

return default 

return value 

 

 

class ContextHelper: 

"""Object that can act as context dictionary for eval and looks up 

names as attributes on a method/ function and its class.  

""" 

def __init__(self, method, cls): 

self.method = method 

self.cls = cls 

 

def __getitem__(self, name): 

return get_method_attr(self.method, self.cls, name) 

 

 

class AttributeSelector(Plugin): 

"""Selects test cases to be run based on their attributes. 

""" 

 

def __init__(self): 

Plugin.__init__(self) 

self.attribs = [] 

 

def options(self, parser, env): 

"""Register command line options""" 

parser.add_option("-a", "--attr", 

dest="attr", action="append", 

default=env.get('NOSE_ATTR'), 

metavar="ATTR", 

help="Run only tests that have attributes " 

"specified by ATTR [NOSE_ATTR]") 

# disable in < 2.4: eval can't take needed args 

if compat_24: 

parser.add_option("-A", "--eval-attr", 

dest="eval_attr", metavar="EXPR", action="append", 

default=env.get('NOSE_EVAL_ATTR'), 

help="Run only tests for whose attributes " 

"the Python expression EXPR evaluates " 

"to True [NOSE_EVAL_ATTR]") 

 

def configure(self, options, config): 

"""Configure the plugin and system, based on selected options. 

 

attr and eval_attr may each be lists. 

 

self.attribs will be a list of lists of tuples. In that list, each 

list is a group of attributes, all of which must match for the rule to 

match. 

""" 

self.attribs = [] 

 

# handle python eval-expression parameter 

if compat_24 and options.eval_attr: 

eval_attr = tolist(options.eval_attr) 

for attr in eval_attr: 

# "<python expression>" 

# -> eval(expr) in attribute context must be True 

def eval_in_context(expr, obj, cls): 

return eval(expr, None, ContextHelper(obj, cls)) 

self.attribs.append([(attr, eval_in_context)]) 

 

# attribute requirements are a comma separated list of 

# 'key=value' pairs 

if options.attr: 

std_attr = tolist(options.attr) 

for attr in std_attr: 

# all attributes within an attribute group must match 

attr_group = [] 

for attrib in attr.strip().split(","): 

# don't die on trailing comma 

if not attrib: 

continue 

items = attrib.split("=", 1) 

if len(items) > 1: 

# "name=value" 

# -> 'str(obj.name) == value' must be True 

key, value = items 

else: 

key = items[0] 

if key[0] == "!": 

# "!name" 

# 'bool(obj.name)' must be False 

key = key[1:] 

value = False 

else: 

# "name" 

# -> 'bool(obj.name)' must be True 

value = True 

attr_group.append((key, value)) 

self.attribs.append(attr_group) 

if self.attribs: 

self.enabled = True 

 

def validateAttrib(self, method, cls = None): 

"""Verify whether a method has the required attributes 

The method is considered a match if it matches all attributes 

for any attribute group. 

.""" 

# TODO: is there a need for case-sensitive value comparison? 

any = False 

for group in self.attribs: 

match = True 

for key, value in group: 

attr = get_method_attr(method, cls, key) 

if callable(value): 

if not value(key, method, cls): 

match = False 

break 

elif value is True: 

# value must exist and be True 

if not bool(attr): 

match = False 

break 

elif value is False: 

# value must not exist or be False 

if bool(attr): 

match = False 

break 

elif type(attr) in (list, tuple): 

# value must be found in the list attribute 

if not str(value).lower() in [str(x).lower() 

for x in attr]: 

match = False 

break 

else: 

# value must match, convert to string and compare 

if (value != attr 

and str(value).lower() != str(attr).lower()): 

match = False 

break 

any = any or match 

if any: 

# not True because we don't want to FORCE the selection of the 

# item, only say that it is acceptable 

return None 

return False 

 

def wantFunction(self, function): 

"""Accept the function if its attributes match. 

""" 

return self.validateAttrib(function) 

 

def wantMethod(self, method): 

"""Accept the method if its attributes match. 

""" 

try: 

cls = method.__self__.__class__ 

except AttributeError: 

return False 

return self.validateAttrib(method, cls)