1212import re
1313import six
1414import pytest
15+ import sys
1516from jira .client import JIRA
1617
1718
1819class JiraHooks (object ):
19- issue_re = r"([A-Z]+-[0-9]+)"
20-
2120 def __init__ (
2221 self ,
2322 connection ,
23+ marker ,
2424 version = None ,
2525 components = None ,
2626 ):
2727 self .conn = connection
28+ self .mark = marker
2829 self .components = set (components ) if components else None
2930 self .version = version
3031
3132 # Speed up JIRA lookups for duplicate issues
3233 self .issue_cache = dict ()
3334
34- def get_jira_issues (self , item ):
35- issue_pattern = re .compile (self .issue_re )
36- jira_ids = []
37- # Was the jira marker used?
38- if 'jira' in item .keywords :
39- marker = item .keywords ['jira' ]
40- if len (marker .args ) == 0 :
41- raise TypeError ('JIRA marker requires one, or more, arguments' )
42- jira_ids .extend (item .keywords ['jira' ].args )
43-
44- # Was a jira issue referenced in the docstr?
45- if item .function .__doc__ :
46- jira_ids .extend (
47- [
48- m .group (0 )
49- for m in issue_pattern .finditer (item .function .__doc__ )
50- ]
51- )
52-
53- # Filter valid issues, and return unique issues
54- for jid in set (jira_ids ):
55- if not issue_pattern .match (jid ):
56- raise ValueError (
57- 'JIRA marker argument `%s` does not match pattern' % jid
58- )
59- return list (
60- set (jira_ids )
61- )
62-
6335 def is_issue_resolved (self , issue_id ):
6436 '''
6537 Returns whether the provided issue ID is resolved (True|False). Will
@@ -70,9 +42,12 @@ def is_issue_resolved(self, issue_id):
7042 try :
7143 self .issue_cache [issue_id ] = self .conn .get_issue (issue_id )
7244 except Exception :
73- self .issue_cache [issue_id ] = { 'status' : 'open' }
45+ self .issue_cache [issue_id ] = self . mark . get_default ( issue_id )
7446
7547 # Skip test if issue remains unresolved
48+ if self .issue_cache [issue_id ] is None :
49+ return True
50+
7651 if self .issue_cache [issue_id ]['status' ] in ['closed' , 'resolved' ]:
7752 return self .fixed_in_version (issue_id )
7853 else :
@@ -86,7 +61,7 @@ def pytest_runtest_makereport(self, item, call, __multicall__):
8661
8762 rep = __multicall__ .execute ()
8863 try :
89- jira_ids = self .get_jira_issues (item )
64+ jira_ids = self .mark . get_jira_issues (item )
9065 except Exception :
9166 jira_ids = []
9267
@@ -112,7 +87,7 @@ def pytest_runtest_setup(self, item):
11287 jira_run = True
11388 if 'jira' in item .keywords :
11489 jira_run = item .keywords ['jira' ].kwargs .get ('run' , jira_run )
115- jira_ids = self .get_jira_issues (item )
90+ jira_ids = self .mark . get_jira_issues (item )
11691
11792 # Check all linked issues
11893 for issue_id in jira_ids :
@@ -201,12 +176,68 @@ def get_url(self):
201176 return self .url
202177
203178
179+ class JiraMarkerReporter (object ):
180+ issue_re = r"([A-Z]+-[0-9]+)"
181+
182+ def __init__ (self , strategy , docs , patern ):
183+ self .issue_pattern = re .compile (patern or self .issue_re )
184+ self .docs = docs
185+ self .strategy = strategy .lower ()
186+
187+ def get_jira_issues (self , item ):
188+ jira_ids = []
189+ # Was the jira marker used?
190+ if 'jira' in item .keywords :
191+ marker = item .keywords ['jira' ]
192+ if len (marker .args ) == 0 :
193+ raise TypeError ('JIRA marker requires one, or more, arguments' )
194+ jira_ids .extend (item .keywords ['jira' ].args )
195+
196+ # Was a jira issue referenced in the docstr?
197+ if self .docs and item .function .__doc__ :
198+ jira_ids .extend (
199+ [
200+ m .group (0 )
201+ for m in self .issue_pattern .finditer (item .function .__doc__ )
202+ ]
203+ )
204+
205+ # Filter valid issues, and return unique issues
206+ for jid in set (jira_ids ):
207+ if not self .issue_pattern .match (jid ):
208+ raise ValueError (
209+ 'JIRA marker argument `%s` does not match pattern' % jid
210+ )
211+ return list (
212+ set (jira_ids )
213+ )
214+
215+ def get_default (self , jid ):
216+ if self .strategy == 'open' :
217+ return {'status' : 'open' }
218+ if self .strategy == 'strict' :
219+ raise ValueError (
220+ 'JIRA marker argument `%s` was not found' % jid
221+ )
222+ if self .strategy == 'warn' :
223+ sys .stderr .write (
224+ 'JIRA marker argument `%s` was not found' % jid
225+ )
226+ return None
227+
228+
204229def _get_value (config , section , name , default = None ):
205230 if config .has_option (section , name ):
206231 return config .get (section , name )
207232 return default
208233
209234
235+ def _get_bool (config , section , name , default = False ):
236+ if config .has_option (section , name ):
237+ return config .getboolean (section , name )
238+ return default
239+
240+
210241def pytest_addoption (parser ):
211242 """
212243 Add a options section to py.test --help for jira integration.
@@ -232,11 +263,6 @@ def pytest_addoption(parser):
232263 ]
233264 )
234265
235- try :
236- verify = config .getboolean ('DEFAULT' , 'ssl_verification' )
237- except six .moves .configparser .NoOptionError :
238- verify = True
239-
240266 group .addoption ('--jira-url' ,
241267 action = 'store' ,
242268 dest = 'jira_url' ,
@@ -258,8 +284,10 @@ def pytest_addoption(parser):
258284 group .addoption ('--jira-no-ssl-verify' ,
259285 action = 'store_false' ,
260286 dest = 'jira_verify' ,
261- default = verify ,
262- help = 'Disable SSL verification to Jira'
287+ default = _get_bool (
288+ config , 'DEFAULT' , 'ssl_verification' , True ,
289+ ),
290+ help = 'Disable SSL verification to Jira' ,
263291 )
264292 group .addoption ('--jira-components' ,
265293 action = 'store' ,
@@ -274,6 +302,32 @@ def pytest_addoption(parser):
274302 default = _get_value (config , 'DEFAULT' , 'version' ),
275303 help = 'Used version'
276304 )
305+ group .addoption ('--jira-marker-strategy' ,
306+ action = 'store' ,
307+ dest = 'jira_marker_strategy' ,
308+ default = _get_value (
309+ config , 'DEFAULT' , 'marker_strategy' , 'open'
310+ ),
311+ choices = ['open' , 'strict' , 'ignore' , 'warn' ],
312+ help = '''Action if issue ID was not found
313+ open - issue is considered as open (default)
314+ strict - raise an exception
315+ ignore - issue id is ignored
316+ warn - write error message and ignore
317+ ''' ,
318+ )
319+ group .addoption ('--jira-disable-docs-search' ,
320+ action = 'store_false' ,
321+ dest = 'jira_docs' ,
322+ default = _get_bool (config , 'DEFAULT' , 'docs_search' , True ),
323+ help = 'Issue ID in doc strings will be ignored'
324+ )
325+ group .addoption ('--jira-issue-regex' ,
326+ action = 'store' ,
327+ dest = 'jira_regex' ,
328+ default = _get_value (config , 'DEFAULT' , 'issue_regex' ),
329+ help = 'Replace default `[A-Z]+-[0-9]+` regular expression'
330+ )
277331
278332
279333def pytest_configure (config ):
@@ -302,10 +356,16 @@ def pytest_configure(config):
302356 config .getvalue ('jira_password' ),
303357 config .getvalue ('jira_verify' ),
304358 )
359+ jira_marker = JiraMarkerReporter (
360+ config .getvalue ('jira_marker_strategy' ),
361+ config .getvalue ('jira_docs' ),
362+ config .getvalue ('jira_regex' ),
363+ )
305364 if jira_connection .is_connected ():
306365 # if connection to jira fails, plugin won't be loaded
307366 jira_plugin = JiraHooks (
308367 jira_connection ,
368+ jira_marker ,
309369 config .getvalue ('jira_product_version' ),
310370 components ,
311371 )
0 commit comments