44from __future__ import absolute_import
55
66import os .path
7+ import sys
78
89import pytest
910
@@ -141,6 +142,7 @@ def _ensure_parent(self, path, parentid, suiteids):
141142 rootdir , parent )
142143 self ._parents [(rootdir , parentid )] = funcinfo
143144 elif parent != parentid :
145+ print (parent , parentid )
144146 # TODO: What to do?
145147 raise NotImplementedError
146148 return parentid
@@ -168,10 +170,12 @@ def _ensure_file(self, rootdir, relfile):
168170 def _ensure_suites (self , fullsuite , rootdir , fileid , suiteids ):
169171 if not fullsuite :
170172 if suiteids :
173+ print (suiteids )
171174 # TODO: What to do?
172175 raise NotImplementedError
173176 return None
174177 if len (suiteids ) != fullsuite .count ('.' ) + 1 :
178+ print (suiteids )
175179 # TODO: What to do?
176180 raise NotImplementedError
177181
@@ -210,13 +214,12 @@ def _parse_item(item, _normcase, _pathsep):
210214 #_debug_item(item, showsummary=True)
211215 kind , _ = _get_item_kind (item )
212216 # Figure out the func, suites, and subs.
213- (fileid , suiteids , suites , funcid , basename , parameterized
214- ) = _parse_node_id (item .nodeid , kind )
217+ (nodeid , fileid , suiteids , suites , funcid , basename , parameterized
218+ ) = _parse_node_id (item .nodeid , kind , _pathsep , _normcase )
215219 if kind == 'function' :
216220 funcname = basename
217- if funcid and item .function .__name__ != funcname :
218- # TODO: What to do?
219- raise NotImplementedError
221+ # Note: funcname does not necessarily match item.function.__name__.
222+ # This can result from importing a test function from another module.
220223 if suites :
221224 testfunc = '.' .join (suites ) + '.' + funcname
222225 else :
@@ -226,32 +229,23 @@ def _parse_item(item, _normcase, _pathsep):
226229 funcname = None
227230
228231 # Figure out the file.
232+ relfile = _normcase (fileid )
229233 fspath = str (item .fspath )
230- if not fspath .endswith (_pathsep + fileid ):
234+ if not _normcase (fspath ).endswith (relfile [1 :]):
235+ print (fspath )
236+ print (relfile )
231237 raise NotImplementedError
232- filename = fspath [- len (fileid ):]
233- testroot = str (item .fspath )[:- len (fileid )].rstrip (_pathsep )
234- if _pathsep in filename :
235- relfile = filename
236- else :
237- relfile = '.' + _pathsep + filename
238- srcfile , lineno , fullname = item .location
239- if srcfile != fileid :
240- # pytest supports discovery of tests imported from other
241- # modules. This is reflected by a different filename
242- # in item.location.
243- if _normcase (fileid ) == _normcase (srcfile ):
244- srcfile = fileid
245- else :
246- srcfile = relfile
247- location = '{}:{}' .format (srcfile , lineno )
238+ testroot = str (item .fspath )[:- len (relfile ) + 1 ]
239+ location , fullname = _get_location (item , relfile , _normcase , _pathsep )
248240 if kind == 'function' :
249241 if testfunc and fullname != testfunc + parameterized :
250- print (fullname , testfunc )
242+ print (item .nodeid )
243+ print (fullname , suites , testfunc )
251244 # TODO: What to do?
252245 raise NotImplementedError
253246 elif kind == 'doctest' :
254247 if testfunc and fullname != testfunc + parameterized :
248+ print (item .nodeid )
255249 print (fullname , testfunc )
256250 # TODO: What to do?
257251 raise NotImplementedError
@@ -280,7 +274,7 @@ def _parse_item(item, _normcase, _pathsep):
280274 # TODO: Support other markers?
281275
282276 test = TestInfo (
283- id = item . nodeid ,
277+ id = nodeid ,
284278 name = item .name ,
285279 path = TestPath (
286280 root = testroot ,
@@ -295,11 +289,62 @@ def _parse_item(item, _normcase, _pathsep):
295289 return test , suiteids
296290
297291
298- def _parse_node_id (nodeid , kind = 'function' ):
292+ def _get_location (item , relfile , _normcase , _pathsep ):
293+ srcfile , lineno , fullname = item .location
294+ srcfile = _normcase (srcfile )
295+ if srcfile in (relfile , relfile [len (_pathsep ) + 1 :]):
296+ srcfile = relfile
297+ else :
298+ # pytest supports discovery of tests imported from other
299+ # modules. This is reflected by a different filename
300+ # in item.location.
301+ srcfile , lineno = _find_location (
302+ srcfile , lineno , relfile , item .function , _pathsep )
303+ if not srcfile .startswith ('.' + _pathsep ):
304+ srcfile = '.' + _pathsep + srcfile
305+ # from pytest, line numbers are 0-based
306+ location = '{}:{}' .format (srcfile , int (lineno ) + 1 )
307+ return location , fullname
308+
309+
310+ def _find_location (srcfile , lineno , relfile , func , _pathsep ):
311+ if sys .version_info > (3 ,):
312+ return srcfile , lineno
313+ if (_pathsep + 'unittest' + _pathsep + 'case.py' ) not in srcfile :
314+ return srcfile , lineno
315+
316+ # Unwrap the decorator (e.g. unittest.skip).
317+ srcfile = relfile
318+ lineno = - 1
319+ try :
320+ func = func .__closure__ [0 ].cell_contents
321+ except (IndexError , AttributeError ):
322+ return srcfile , lineno
323+ else :
324+ if callable (func ) and func .__code__ .co_filename .endswith (relfile [1 :]):
325+ lineno = func .__code__ .co_firstlineno - 1
326+ return srcfile , lineno
327+
328+
329+ def _parse_node_id (nodeid , kind , _pathsep , _normcase ):
330+ if not nodeid .startswith ('.' + _pathsep ):
331+ nodeid = '.' + _pathsep + nodeid
332+ while '::()::' in nodeid :
333+ nodeid = nodeid .replace ('::()::' , '::' )
334+
335+ fileid , _ , remainder = nodeid .partition ('::' )
336+ if not fileid or not remainder :
337+ print (nodeid )
338+ # TODO: Unexpected! What to do?
339+ raise NotImplementedError
340+ fileid = _normcase (fileid )
341+ nodeid = fileid + '::' + remainder
342+
299343 if kind == 'doctest' :
300344 try :
301345 parentid , name = nodeid .split ('::' )
302346 except ValueError :
347+ print (nodeid )
303348 # TODO: Unexpected! What to do?
304349 raise NotImplementedError
305350 funcid = None
@@ -309,26 +354,30 @@ def _parse_node_id(nodeid, kind='function'):
309354 if nodeid .endswith (']' ):
310355 funcid , sep , parameterized = nodeid .partition ('[' )
311356 if not sep :
357+ print (nodeid )
312358 # TODO: Unexpected! What to do?
313359 raise NotImplementedError
314360 parameterized = sep + parameterized
315361 else :
316362 funcid = nodeid
317-
318363 parentid , _ , name = funcid .rpartition ('::' )
319- if not name :
364+ if not parentid or not name :
365+ print (parentid , name )
320366 # TODO: What to do? We expect at least a filename and a function
321367 raise NotImplementedError
322368
323369 suites = []
324370 suiteids = []
325371 while '::' in parentid :
326- suiteids .insert (0 , parentid )
327- parentid , _ , suitename = parentid .rpartition ('::' )
372+ fullid = parentid
373+ parentid , _ , suitename = fullid .rpartition ('::' )
374+ suiteids .insert (0 , fullid )
328375 suites .insert (0 , suitename )
329- fileid = parentid
376+ if parentid != fileid :
377+ print (nodeid )
378+ print (parentid , fileid )
330379
331- return fileid , suiteids , suites , funcid , name , parameterized
380+ return nodeid , fileid , suiteids , suites , funcid , name , parameterized
332381
333382
334383def _get_item_kind (item ):
0 commit comments