Skip to content

tests: improve test for nose.raises #6521

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/_pytest/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ def get_real_method(obj, holder):
return obj


def getfslineno(obj):
def getfslineno(obj) -> Tuple[Union[str, py.path.local], int]:
# xxx let decorators etc specify a sane ordering
obj = get_real_func(obj)
if hasattr(obj, "place_as"):
Expand Down
8 changes: 4 additions & 4 deletions src/_pytest/config/argparsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ def parse(self, args, namespace=None):

self.optparser = self._getparser()
try_argcomplete(self.optparser)
args = [str(x) if isinstance(x, py.path.local) else x for x in args]
return self.optparser.parse_args(args, namespace=namespace)
strargs = [str(x) if isinstance(x, py.path.local) else x for x in args]
return self.optparser.parse_args(strargs, namespace=namespace)

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

def addini(self, name, help, type=None, default=None):
""" register an ini-file option.
Expand Down
7 changes: 5 additions & 2 deletions src/_pytest/config/findpaths.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import os
from typing import Any
from typing import Iterable
from typing import List
from typing import Optional
from typing import Tuple

import py

Expand Down Expand Up @@ -60,7 +63,7 @@ def getcfg(args, config=None):
return None, None, None


def get_common_ancestor(paths):
def get_common_ancestor(paths: Iterable[py.path.local]) -> py.path.local:
common_ancestor = None
for path in paths:
if not path.exists():
Expand Down Expand Up @@ -113,7 +116,7 @@ def determine_setup(
args: List[str],
rootdir_cmd_arg: Optional[str] = None,
config: Optional["Config"] = None,
):
) -> Tuple[py.path.local, Optional[str], Any]:
dirs = get_dirs_from_args(args)
if inifile:
iniconfig = py.iniconfig.IniConfig(inifile)
Expand Down
2 changes: 1 addition & 1 deletion src/_pytest/doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ def repr_failure(self, excinfo):
else:
return super().repr_failure(excinfo)

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


Expand Down
11 changes: 7 additions & 4 deletions src/_pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ def __init__(self, pyfuncitem):
self.fixturename = None
#: Scope string, one of "function", "class", "module", "session"
self.scope = "function"
self._fixture_defs = {} # argname -> FixtureDef
self._fixture_defs = {} # type: Dict[str, FixtureDef]
fixtureinfo = pyfuncitem._fixtureinfo
self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy()
self._arg2index = {}
Expand Down Expand Up @@ -426,7 +426,8 @@ def module(self):
@scopeproperty()
def fspath(self) -> py.path.local:
""" the file system path of the test module which collected this test. """
return self._pyfuncitem.fspath
# TODO: Remove ignore once _pyfuncitem is properly typed.
return self._pyfuncitem.fspath # type: ignore

@property
def keywords(self):
Expand Down Expand Up @@ -549,7 +550,9 @@ def _compute_fixture_value(self, fixturedef):
source_lineno = frameinfo.lineno
source_path = py.path.local(source_path)
if source_path.relto(funcitem.config.rootdir):
source_path = source_path.relto(funcitem.config.rootdir)
source_path_str = source_path.relto(funcitem.config.rootdir)
else:
source_path_str = str(source_path)
msg = (
"The requested fixture has no parameter defined for test:\n"
" {}\n\n"
Expand All @@ -558,7 +561,7 @@ def _compute_fixture_value(self, fixturedef):
funcitem.nodeid,
fixturedef.argname,
getlocation(fixturedef.func, funcitem.config.rootdir),
source_path,
source_path_str,
source_lineno,
)
)
Expand Down
8 changes: 4 additions & 4 deletions src/_pytest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,9 +376,9 @@ class Failed(Exception):

@attr.s
class _bestrelpath_cache(dict):
path = attr.ib()
path = attr.ib(type=py.path.local)

def __missing__(self, path: str) -> str:
def __missing__(self, path: py.path.local) -> str:
r = self.path.bestrelpath(path) # type: str
self[path] = r
return r
Expand Down Expand Up @@ -412,7 +412,7 @@ def __init__(self, config: Config) -> None:

self._bestrelpathcache = _bestrelpath_cache(
config.rootdir
) # type: Dict[str, str]
) # type: Dict[py.path.local, str]

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

Expand All @@ -425,7 +425,7 @@ def __repr__(self):
self.testscollected,
)

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

Expand Down
8 changes: 6 additions & 2 deletions src/_pytest/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,10 @@ def reportinfo(self) -> Tuple[Union[py.path.local, str], Optional[int], str]:
@cached_property
def location(self) -> Tuple[str, Optional[int], str]:
location = self.reportinfo()
fspath = self.session._node_location_to_relpath(location[0])
if isinstance(location[0], py.path.local):
fspath = location[0]
else:
fspath = py.path.local(location[0])
relfspath = self.session._node_location_to_relpath(fspath)
assert type(location[2]) is str
return (fspath, location[1], location[2])
return (relfspath, location[1], location[2])
17 changes: 12 additions & 5 deletions src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from textwrap import dedent
from typing import List
from typing import Tuple
from typing import Union

import py

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

def reportinfo(self) -> Tuple[str, int, str]:
def reportinfo(self) -> Tuple[Union[py.path.local, str], int, str]:
# XXX caching?
obj = self.obj
compat_co_firstlineno = getattr(obj, "compat_co_firstlineno", None)
if isinstance(compat_co_firstlineno, int):
# nose compatibility
fspath = sys.modules[obj.__module__].__file__
if fspath.endswith(".pyc"):
fspath = fspath[:-1]
file_path = sys.modules[obj.__module__].__file__
if file_path.endswith(".pyc"):
file_path = file_path[:-1]
fspath = file_path # type: Union[py.path.local, str]
lineno = compat_co_firstlineno
else:
fspath, lineno = getfslineno(obj)
Expand Down Expand Up @@ -367,7 +369,12 @@ def collect(self):
if not isinstance(res, list):
res = [res]
values.extend(res)
values.sort(key=lambda item: item.reportinfo()[:2])

def sort_key(item):
fspath, lineno, _ = item.reportinfo()
return (str(fspath), lineno)

values.sort(key=sort_key)
return values

def _makeitem(self, name, obj):
Expand Down
41 changes: 37 additions & 4 deletions testing/test_nose.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,15 +377,48 @@ def test_io(self):
result.stdout.fnmatch_lines(["* 1 skipped *"])


def test_issue_6517(testdir):
def test_raises(testdir):
testdir.makepyfile(
"""
from nose.tools import raises

@raises(RuntimeError)
def test_fail_without_tcp():
def test_raises_runtimeerror():
raise RuntimeError

@raises(Exception)
def test_raises_baseexception_not_caught():
raise BaseException

@raises(BaseException)
def test_raises_baseexception_caught():
raise BaseException
"""
)
result = testdir.runpytest()
result.stdout.fnmatch_lines(["* 1 passed *"])
result = testdir.runpytest("-vv")
result.stdout.fnmatch_lines(
[
"test_raises.py::test_raises_runtimeerror PASSED*",
"test_raises.py::test_raises_baseexception_not_caught FAILED*",
"test_raises.py::test_raises_baseexception_caught PASSED*",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without the cast here it would display "test_raises.py::test_raises_runtimeerror <- /…/test_raises0/test_raises.py PASSED` here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm sorry, what "cast" you mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"*= FAILURES =*",
"*_ test_raises_baseexception_not_caught _*",
"",
"arg = (), kw = {}",
"",
" def newfunc(*arg, **kw):",
" try:",
"> func(*arg, **kw)",
"",
"*/nose/*: ",
"_ _ *",
"",
" @raises(Exception)",
" def test_raises_baseexception_not_caught():",
"> raise BaseException",
"E BaseException",
"",
"test_raises.py:9: BaseException",
"* 1 failed, 2 passed *",
]
)