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

pytest explodes if getcwd fails #1235

Closed
bukzor opened this Issue Dec 7, 2015 · 17 comments

Comments

Projects
None yet
9 participants
@bukzor
Contributor

bukzor commented Dec 7, 2015

Demonstration below.

While in my toy example it's quite clear what the issue is, the fact that it doesn't show the original error, or even have the erroneous code anywhere on the stack means that it's extremely hard to find the source of this issue in a real project.

$ cat test.py
def test_foo(tmpdir):
    tmpdir.chdir()
    tmpdir.remove()

    assert False, 'naw'

$ py.test test.py
=========================================================================================== test session starts ===========================================================================================
platform linux2 -- Python 2.7.6, pytest-2.8.4, py-1.4.30, pluggy-0.3.1 -- /nail/home/buck/trees/yelp/pip-faster/venv-venv_update/bin/python2.7
cachedir: ../.cache
rootdir: /nail/home/buck/trees/yelp/pip-faster, inifile: pytest.ini
plugins: xdist-1.13.1, timeout-1.0.0
collected 1 items

test.py::test_foo
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/main.py", line 90, in wrap_session
INTERNALERROR>     session.exitstatus = doit(config, session) or 0
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/main.py", line 121, in _main
INTERNALERROR>     config.hook.pytest_runtestloop(session=session)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 724, in __call__
INTERNALERROR>     return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 338, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 333, in <lambda>
INTERNALERROR>     _MultiCall(methods, kwargs, hook.spec_opts).execute()
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 596, in execute
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/main.py", line 146, in pytest_runtestloop
INTERNALERROR>     item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 724, in __call__
INTERNALERROR>     return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 338, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 333, in <lambda>
INTERNALERROR>     _MultiCall(methods, kwargs, hook.spec_opts).execute()
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 595, in execute
INTERNALERROR>     return _wrapped_call(hook_impl.function(*args), self.execute)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 253, in _wrapped_call
INTERNALERROR>     return call_outcome.get_result()
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 279, in get_result
INTERNALERROR>     _reraise(*ex)  # noqa
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 264, in __init__
INTERNALERROR>     self.result = func()
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 595, in execute
INTERNALERROR>     return _wrapped_call(hook_impl.function(*args), self.execute)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 253, in _wrapped_call
INTERNALERROR>     return call_outcome.get_result()
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 279, in get_result
INTERNALERROR>     _reraise(*ex)  # noqa
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 264, in __init__
INTERNALERROR>     self.result = func()
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 596, in execute
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/runner.py", line 65, in pytest_runtest_protocol
INTERNALERROR>     runtestprotocol(item, nextitem=nextitem)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/runner.py", line 75, in runtestprotocol
INTERNALERROR>     reports.append(call_and_report(item, "call", log))
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/runner.py", line 121, in call_and_report
INTERNALERROR>     report = hook.pytest_runtest_makereport(item=item, call=call)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 724, in __call__
INTERNALERROR>     return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 338, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 333, in <lambda>
INTERNALERROR>     _MultiCall(methods, kwargs, hook.spec_opts).execute()
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 595, in execute
INTERNALERROR>     return _wrapped_call(hook_impl.function(*args), self.execute)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 249, in _wrapped_call
INTERNALERROR>     wrap_controller.send(call_outcome)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/skipping.py", line 170, in pytest_runtest_makereport
INTERNALERROR>     rep = outcome.get_result()
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 279, in get_result
INTERNALERROR>     _reraise(*ex)  # noqa
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 264, in __init__
INTERNALERROR>     self.result = func()
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py", line 596, in execute
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/runner.py", line 224, in pytest_runtest_makereport
INTERNALERROR>     longrepr = item.repr_failure(excinfo)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/python.py", line 751, in repr_failure
INTERNALERROR>     return self._repr_failure_py(excinfo, style=style)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/python.py", line 744, in _repr_failure_py
INTERNALERROR>     style=style)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/_pytest/main.py", line 404, in _repr_failure_py
INTERNALERROR>     style=style, tbfilter=tbfilter)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/py/_code/code.py", line 412, in getrepr
INTERNALERROR>     return fmt.repr_excinfo(self)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/py/_code/code.py", line 590, in repr_excinfo
INTERNALERROR>     reprtraceback = self.repr_traceback(excinfo)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/py/_code/code.py", line 582, in repr_traceback
INTERNALERROR>     reprentry = self.repr_traceback_entry(entry, einfo)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/py/_code/code.py", line 549, in repr_traceback_entry
INTERNALERROR>     path = self._makepath(entry.path)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/py/_code/code.py", line 562, in _makepath
INTERNALERROR>     np = py.path.local().bestrelpath(path)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/py/_path/local.py", line 149, in __init__
INTERNALERROR>     self.strpath = py.error.checked_call(os.getcwd)
INTERNALERROR>   File "/nail/home/buck/trees/yelp/pip-faster/venv-venv_update/lib/python2.7/site-packages/py/_error.py", line 84, in checked_call
INTERNALERROR>     raise cls("%s%r" % (func.__name__, args))
INTERNALERROR> ENOENT: [No such file or directory]: getcwd()

====================================================================================== no tests ran in 0.18 seconds =======================================================================================
@hpk42

This comment has been minimized.

Contributor

hpk42 commented Dec 8, 2015

agred, that's not robust behaviour. Also it's wrong for traceback representation to use py.path.local(). Instead we should use the "startdir" as it is stored on the terminal writer plugin (we can also put it to the config object in addition or instead) for computing relative paths. If we can merge Bruno's "move py.code to pytest" we could probably more easily fix this properly.

It's probably also possible to fix this in the pytest-2.8 series maybe by e.g. changing to "startdir" before we call the pytest_runtest_makereport hook. @bukzor if you feel like trying that in a PR you are welcome (with a test which you basically already have) .

@bukzor

This comment has been minimized.

Contributor

bukzor commented Dec 9, 2015

Full disclosure: I don't plan to work on this.

I was able to find the rogue path.chdir() and changed it to with path.as_cwd() which enabled a good traceback.

On Tue, Dec 8, 2015 at 12:54 PM holger krekel notifications@github.com
wrote:

agred, that's not robust behaviour. Also it's wrong for traceback
representation to use py.path.local(). Instead we should use the
"startdir" as it is stored on the terminal writer plugin (we can also put
it to the config object in addition or instead) for computing relative
paths. If we can merge Bruno's "move py.code to pytest" we could probably
more easily fix this properly.

It's probably also possible to fix this in the pytest-2.8 series maybe by
e.g. changing to "startdir" before we call the pytest_runtest_makereport
hook. @bukzor https://github.com/bukzor if you feel like trying that in
a PR you are welcome (with a test which you basically already have) .


Reply to this email directly or view it on GitHub
#1235 (comment).

@nicoddemus

This comment has been minimized.

Member

nicoddemus commented Dec 9, 2015

@bukzor no problem, thanks for reporting anyway!

@MagiMaster

This comment has been minimized.

MagiMaster commented Jan 25, 2016

Ran in to this bug recently.

@hangtwenty

This comment has been minimized.

hangtwenty commented Jan 26, 2016

Seems like in every case someone should just create a subdirectory OF tmpdir (easy using the sub() method) and then they can freely do whatever they want, with that subdirectory ... and no issues when it gets deleted.

@hangtwenty

This comment has been minimized.

hangtwenty commented Jan 26, 2016

For example:

@pytest.fixture
def testing_workdir(tmpdir, request):
    """ Create a workdir in a safe temporary folder; cd into dir above before test, cd out after

    :param tmpdir: py.test fixture, will be injected
    :param request: py.test fixture-related, will be injected (see pytest docs)
    """

    saved_path = os.getcwd()

    tmpdir.chdir()
    workdir = tmpdir.mkdir('mysubdir')

    def return_to_saved_path():
        os.chdir(saved_path)

    request.addfinalizer(return_to_saved_path)

    return str(workdir)

any test function using this fixture, testing_workdir will just get a subdirectory that is (a) temporary and (b) safe to delete :)

Oh, I'm not suggesting that py.test should do this, I'm just suggesting that OPs or commentors on this thread and #470, can take care of this for themselves with a fixture like this.

@MagiMaster

This comment has been minimized.

MagiMaster commented Jan 27, 2016

I agree that it's due to incorrectly written tests, but the problem is that it's hard to figure out which test caused the issue (especially when you didn't write all of them yourself).

@hangtwenty

This comment has been minimized.

hangtwenty commented Jan 27, 2016

ah, I hear you on that @MagiMaster

@RonnyPfannschmidt RonnyPfannschmidt added this to the 2.9 milestone Feb 11, 2016

@The-Compiler The-Compiler modified the milestones: 2.10, 2.9 Feb 26, 2016

dmerejkowsky added a commit to dmerejkowsky/pytest that referenced this issue Mar 1, 2016

dmerejkowsky added a commit to dmerejkowsky/pytest that referenced this issue Mar 1, 2016

@dmerejkowsky

This comment has been minimized.

dmerejkowsky commented Mar 1, 2016

I ran into the same issue (I needed to make sure my code does now blow up when cwd() gets
deleted)

The bad news is that @hangtwenty solution does not work.

See my branch for the details:
https://github.com/dmerejkowsky/pytest/commits/cwd

dmerejkowsky added a commit to dmerejkowsky/pytest that referenced this issue Mar 1, 2016

@hangtwenty

This comment has been minimized.

hangtwenty commented Mar 1, 2016

@dmerejkowsky I'm not saying this defensively, just trying to help if I can: I can tell you with confidence that my solution worked. Perhaps there is a subtle difference between the test you've added, and the real-world scenario I was in. Only mentioning in case this might reveal something in your test that is too different, and why it doesn't work. My example has no usage of monkeypatch ... I would suggest you try and get it working exactly as written, in a real test suite, before going more abstract and adding to pytest tests. If you really want to reproduce it.

In any case, it might be orthogonal to OP's request:

it's extremely hard to find the source of this issue in a real project.

That seems to be the important thing: helping people track down where the hell the problem is coming from

@dmerejkowsky

This comment has been minimized.

dmerejkowsky commented Mar 1, 2016

@hangtwenty : No problem man :) Reproducing bugs is hard ...

Anyway, in my use case pytest only crashes when the tests fails, so it's not that bad.

That seems to be the important thing: helping people track down where the hell the problem is coming from

Agreed. I may have a look at this in the future. After all, the issue is marked as "easy" :)

@dmerejkowsky

This comment has been minimized.

dmerejkowsky commented Mar 1, 2016

Hum. Interesting development. When testing with Python3, the tests I've added pass. This may be because Python2 raises OSError, and Python3 FileNotFoundError. @hangtwenty : are you running your tests with Python3 ? That maybe one of the "subtle differences" we are looking for ...

@hangtwenty

This comment has been minimized.

hangtwenty commented Mar 1, 2016

@dmerejkowsky That's very interesting. I was using Python 2.7.9 actually. The main difference I was noticing was that your example is using monkeypatch etc. and just the level of abstraction (meta-programming is always hard ... since you were using the pytest suite, you have to meta-program some tests before running)

@marscher

This comment has been minimized.

Contributor

marscher commented May 17, 2016

I also encountered this bug during the the test phase of conda build (http://conda.pydata.org/docs/building/recipe.html)
During the test phase the package built is installed in an isolated test environment. Somehow the cwd gets deleted. I dont know why. I execute py.test in a subprocess where I already tried to change cwd to my home directory (which is of course not deleted), but still I get the same stack trace as reported.

If you need further information I'll be happy to provide it.

@marscher

This comment has been minimized.

Contributor

marscher commented May 18, 2016

INTERNALERROR> File "/home/marscher/anaconda/lib/python2.7/site-packages/_pytest/_code/code.py", line 571, in _makepath
INTERNALERROR> np = py.path.local().bestrelpath(path)

The argument to path in _makepath is a file, this does not make any sense, right?

@marscher

This comment has been minimized.

Contributor

marscher commented May 18, 2016

The argument is always the name of the python file under test.

@nicoddemus

This comment has been minimized.

Member

nicoddemus commented May 18, 2016

The argument is always the name of the python file under test

I think that's OK, it should be able to guess a relative path from either a file path or directory path.

I found a trivial fix for the problem:

+++ b/_pytest/main.py
@@ -403,7 +403,7 @@ class Node(object):
             else:
                 style = "long"

-        return excinfo.getrepr(funcargs=True,
+        return excinfo.getrepr(funcargs=True, abspath=True,
                                showlocals=self.config.option.showlocals,
                                style=style, tbfilter=tbfilter)

Of course now all paths in tracebacks are full paths instead of relative to the current directory, but I personally don't think that's a problem. In fact I know Eclipse does some internal workarounds in order to make the links in pytest's traceback clickable in its IDE because they are relative instead of absolute.

I didn't work on this before because my main environment is Windows, and I can't reproduce the bug on it. 😑

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment