Skip to content

Commit 6b13379

Browse files
authored
Merge pull request #6521 from blueyed/harden-nose-raises
tests: improve test for `nose.raises`
2 parents 863bab5 + 9dcdea5 commit 6b13379

File tree

9 files changed

+77
-27
lines changed

9 files changed

+77
-27
lines changed

src/_pytest/compat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ def get_real_method(obj, holder):
307307
return obj
308308

309309

310-
def getfslineno(obj):
310+
def getfslineno(obj) -> Tuple[Union[str, py.path.local], int]:
311311
# xxx let decorators etc specify a sane ordering
312312
obj = get_real_func(obj)
313313
if hasattr(obj, "place_as"):

src/_pytest/config/argparsing.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ def parse(self, args, namespace=None):
8282

8383
self.optparser = self._getparser()
8484
try_argcomplete(self.optparser)
85-
args = [str(x) if isinstance(x, py.path.local) else x for x in args]
86-
return self.optparser.parse_args(args, namespace=namespace)
85+
strargs = [str(x) if isinstance(x, py.path.local) else x for x in args]
86+
return self.optparser.parse_args(strargs, namespace=namespace)
8787

8888
def _getparser(self) -> "MyOptionParser":
8989
from _pytest._argcomplete import filescompleter
@@ -124,8 +124,8 @@ def parse_known_and_unknown_args(
124124
the remaining arguments unknown at this point.
125125
"""
126126
optparser = self._getparser()
127-
args = [str(x) if isinstance(x, py.path.local) else x for x in args]
128-
return optparser.parse_known_args(args, namespace=namespace)
127+
strargs = [str(x) if isinstance(x, py.path.local) else x for x in args]
128+
return optparser.parse_known_args(strargs, namespace=namespace)
129129

130130
def addini(self, name, help, type=None, default=None):
131131
""" register an ini-file option.

src/_pytest/config/findpaths.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import os
2+
from typing import Any
3+
from typing import Iterable
24
from typing import List
35
from typing import Optional
6+
from typing import Tuple
47

58
import py
69

@@ -60,7 +63,7 @@ def getcfg(args, config=None):
6063
return None, None, None
6164

6265

63-
def get_common_ancestor(paths):
66+
def get_common_ancestor(paths: Iterable[py.path.local]) -> py.path.local:
6467
common_ancestor = None
6568
for path in paths:
6669
if not path.exists():
@@ -113,7 +116,7 @@ def determine_setup(
113116
args: List[str],
114117
rootdir_cmd_arg: Optional[str] = None,
115118
config: Optional["Config"] = None,
116-
):
119+
) -> Tuple[py.path.local, Optional[str], Any]:
117120
dirs = get_dirs_from_args(args)
118121
if inifile:
119122
iniconfig = py.iniconfig.IniConfig(inifile)

src/_pytest/doctest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ def repr_failure(self, excinfo):
308308
else:
309309
return super().repr_failure(excinfo)
310310

311-
def reportinfo(self) -> Tuple[str, int, str]:
311+
def reportinfo(self) -> Tuple[py.path.local, int, str]:
312312
return self.fspath, self.dtest.lineno, "[doctest] %s" % self.name
313313

314314

src/_pytest/fixtures.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ def __init__(self, pyfuncitem):
351351
self.fixturename = None
352352
#: Scope string, one of "function", "class", "module", "session"
353353
self.scope = "function"
354-
self._fixture_defs = {} # argname -> FixtureDef
354+
self._fixture_defs = {} # type: Dict[str, FixtureDef]
355355
fixtureinfo = pyfuncitem._fixtureinfo
356356
self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy()
357357
self._arg2index = {}
@@ -426,7 +426,8 @@ def module(self):
426426
@scopeproperty()
427427
def fspath(self) -> py.path.local:
428428
""" the file system path of the test module which collected this test. """
429-
return self._pyfuncitem.fspath
429+
# TODO: Remove ignore once _pyfuncitem is properly typed.
430+
return self._pyfuncitem.fspath # type: ignore
430431

431432
@property
432433
def keywords(self):
@@ -549,7 +550,9 @@ def _compute_fixture_value(self, fixturedef):
549550
source_lineno = frameinfo.lineno
550551
source_path = py.path.local(source_path)
551552
if source_path.relto(funcitem.config.rootdir):
552-
source_path = source_path.relto(funcitem.config.rootdir)
553+
source_path_str = source_path.relto(funcitem.config.rootdir)
554+
else:
555+
source_path_str = str(source_path)
553556
msg = (
554557
"The requested fixture has no parameter defined for test:\n"
555558
" {}\n\n"
@@ -558,7 +561,7 @@ def _compute_fixture_value(self, fixturedef):
558561
funcitem.nodeid,
559562
fixturedef.argname,
560563
getlocation(fixturedef.func, funcitem.config.rootdir),
561-
source_path,
564+
source_path_str,
562565
source_lineno,
563566
)
564567
)

src/_pytest/main.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -376,9 +376,9 @@ class Failed(Exception):
376376

377377
@attr.s
378378
class _bestrelpath_cache(dict):
379-
path = attr.ib()
379+
path = attr.ib(type=py.path.local)
380380

381-
def __missing__(self, path: str) -> str:
381+
def __missing__(self, path: py.path.local) -> str:
382382
r = self.path.bestrelpath(path) # type: str
383383
self[path] = r
384384
return r
@@ -412,7 +412,7 @@ def __init__(self, config: Config) -> None:
412412

413413
self._bestrelpathcache = _bestrelpath_cache(
414414
config.rootdir
415-
) # type: Dict[str, str]
415+
) # type: Dict[py.path.local, str]
416416

417417
self.config.pluginmanager.register(self, name="session")
418418

@@ -425,7 +425,7 @@ def __repr__(self):
425425
self.testscollected,
426426
)
427427

428-
def _node_location_to_relpath(self, node_path: str) -> str:
428+
def _node_location_to_relpath(self, node_path: py.path.local) -> str:
429429
# bestrelpath is a quite slow function
430430
return self._bestrelpathcache[node_path]
431431

src/_pytest/nodes.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,10 @@ def reportinfo(self) -> Tuple[Union[py.path.local, str], Optional[int], str]:
462462
@cached_property
463463
def location(self) -> Tuple[str, Optional[int], str]:
464464
location = self.reportinfo()
465-
fspath = self.session._node_location_to_relpath(location[0])
465+
if isinstance(location[0], py.path.local):
466+
fspath = location[0]
467+
else:
468+
fspath = py.path.local(location[0])
469+
relfspath = self.session._node_location_to_relpath(fspath)
466470
assert type(location[2]) is str
467-
return (fspath, location[1], location[2])
471+
return (relfspath, location[1], location[2])

src/_pytest/python.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from textwrap import dedent
1313
from typing import List
1414
from typing import Tuple
15+
from typing import Union
1516

1617
import py
1718

@@ -280,15 +281,16 @@ def getmodpath(self, stopatmodule=True, includemodule=False):
280281
parts.reverse()
281282
return ".".join(parts)
282283

283-
def reportinfo(self) -> Tuple[str, int, str]:
284+
def reportinfo(self) -> Tuple[Union[py.path.local, str], int, str]:
284285
# XXX caching?
285286
obj = self.obj
286287
compat_co_firstlineno = getattr(obj, "compat_co_firstlineno", None)
287288
if isinstance(compat_co_firstlineno, int):
288289
# nose compatibility
289-
fspath = sys.modules[obj.__module__].__file__
290-
if fspath.endswith(".pyc"):
291-
fspath = fspath[:-1]
290+
file_path = sys.modules[obj.__module__].__file__
291+
if file_path.endswith(".pyc"):
292+
file_path = file_path[:-1]
293+
fspath = file_path # type: Union[py.path.local, str]
292294
lineno = compat_co_firstlineno
293295
else:
294296
fspath, lineno = getfslineno(obj)
@@ -367,7 +369,12 @@ def collect(self):
367369
if not isinstance(res, list):
368370
res = [res]
369371
values.extend(res)
370-
values.sort(key=lambda item: item.reportinfo()[:2])
372+
373+
def sort_key(item):
374+
fspath, lineno, _ = item.reportinfo()
375+
return (str(fspath), lineno)
376+
377+
values.sort(key=sort_key)
371378
return values
372379

373380
def _makeitem(self, name, obj):

testing/test_nose.py

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -377,15 +377,48 @@ def test_io(self):
377377
result.stdout.fnmatch_lines(["* 1 skipped *"])
378378

379379

380-
def test_issue_6517(testdir):
380+
def test_raises(testdir):
381381
testdir.makepyfile(
382382
"""
383383
from nose.tools import raises
384384
385385
@raises(RuntimeError)
386-
def test_fail_without_tcp():
386+
def test_raises_runtimeerror():
387387
raise RuntimeError
388+
389+
@raises(Exception)
390+
def test_raises_baseexception_not_caught():
391+
raise BaseException
392+
393+
@raises(BaseException)
394+
def test_raises_baseexception_caught():
395+
raise BaseException
388396
"""
389397
)
390-
result = testdir.runpytest()
391-
result.stdout.fnmatch_lines(["* 1 passed *"])
398+
result = testdir.runpytest("-vv")
399+
result.stdout.fnmatch_lines(
400+
[
401+
"test_raises.py::test_raises_runtimeerror PASSED*",
402+
"test_raises.py::test_raises_baseexception_not_caught FAILED*",
403+
"test_raises.py::test_raises_baseexception_caught PASSED*",
404+
"*= FAILURES =*",
405+
"*_ test_raises_baseexception_not_caught _*",
406+
"",
407+
"arg = (), kw = {}",
408+
"",
409+
" def newfunc(*arg, **kw):",
410+
" try:",
411+
"> func(*arg, **kw)",
412+
"",
413+
"*/nose/*: ",
414+
"_ _ *",
415+
"",
416+
" @raises(Exception)",
417+
" def test_raises_baseexception_not_caught():",
418+
"> raise BaseException",
419+
"E BaseException",
420+
"",
421+
"test_raises.py:9: BaseException",
422+
"* 1 failed, 2 passed *",
423+
]
424+
)

0 commit comments

Comments
 (0)