get py.test to run at least basically on top of jython

* allow and document calling of monkeypatch.undo() from a test
* default to 'sys' on platforms there no 'os.dup' is available
* use "compile" to perform "tryparsing" checks

--HG--
branch : trunk
This commit is contained in:
holger krekel 2009-09-05 16:54:52 +02:00
parent 7ab98c1b25
commit 783c714a2c
5 changed files with 60 additions and 22 deletions

View File

@ -11,6 +11,7 @@ syntax:glob
*.pyo *.pyo
*.swp *.swp
*.html *.html
*.class
build/ build/
py.egg-info py.egg-info

View File

@ -157,14 +157,13 @@ class Source(object):
""" return True if source is parseable, heuristically """ return True if source is parseable, heuristically
deindenting it by default. deindenting it by default.
""" """
import parser
if deindent: if deindent:
source = str(self.deindent()) source = str(self.deindent())
else: else:
source = str(self) source = str(self)
try: try:
parser.suite(source+'\n') compile(source+'\n', "x", "exec")
except (parser.ParserError, SyntaxError): except SyntaxError:
return False return False
else: else:
return True return True

View File

@ -85,6 +85,7 @@ argument which offers the same interface.
""" """
import py import py
import os
def pytest_addoption(parser): def pytest_addoption(parser):
group = parser.getgroup("general") group = parser.getgroup("general")
@ -131,11 +132,15 @@ class CaptureManager:
def _getmethod(self, config, fspath): def _getmethod(self, config, fspath):
if config.option.capture: if config.option.capture:
return config.option.capture method = config.option.capture
try: else:
return config._conftest.rget("option_capture", path=fspath) try:
except KeyError: method = config._conftest.rget("option_capture", path=fspath)
return "fd" except KeyError:
method = "fd"
if method == "fd" and not hasattr(os, 'dup'): # e.g. jython
method = "sys"
return method
def resumecapture_item(self, item): def resumecapture_item(self, item):
method = self._getmethod(item.config, item.fspath) method = self._getmethod(item.config, item.fspath)

View File

@ -4,8 +4,8 @@ safely patch object attributes, dicts and environment variables.
Usage Usage
---------------- ----------------
Use the `monkeypatch funcarg`_ to safely patch environment Use the `monkeypatch funcarg`_ to safely modify or delete environment
variables, object attributes or dictionaries. For example, if you want variables, object attributes or dictionary values. For example, if you want
to set the environment variable ``ENV1`` and patch the to set the environment variable ``ENV1`` and patch the
``os.path.abspath`` function to return a particular value during a test ``os.path.abspath`` function to return a particular value during a test
function execution you can write it down like this: function execution you can write it down like this:
@ -31,6 +31,20 @@ can use this example:
monkeypatch.setenv('PATH', 'x/y', prepend=":") monkeypatch.setenv('PATH', 'x/y', prepend=":")
# x/y will be at the beginning of $PATH # x/y will be at the beginning of $PATH
calling "undo" finalization explicitely
-----------------------------------------
Usually at the end of function execution py.test will invoke
a teardown hook which undoes the changes. If you cannot wait
that long you can also call finalization explicitely::
monkeypatch.undo()
This will undo previous changes. This call consumes the
undo stack. Calling it a second time has no effect.
Within a test you can continue to use the monkeypatch
object, however.
.. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/ .. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
""" """
@ -54,7 +68,7 @@ def pytest_funcarg__monkeypatch(request):
deletion has no target. deletion has no target.
""" """
monkeypatch = MonkeyPatch() monkeypatch = MonkeyPatch()
request.addfinalizer(monkeypatch.finalize) request.addfinalizer(monkeypatch.undo)
return monkeypatch return monkeypatch
notset = object() notset = object()
@ -97,17 +111,19 @@ class MonkeyPatch:
def delenv(self, name, raising=True): def delenv(self, name, raising=True):
self.delitem(os.environ, name, raising=raising) self.delitem(os.environ, name, raising=raising)
def finalize(self): def undo(self):
for obj, name, value in self._setattr: for obj, name, value in self._setattr:
if value is not notset: if value is not notset:
setattr(obj, name, value) setattr(obj, name, value)
else: else:
delattr(obj, name) delattr(obj, name)
self._setattr[:] = []
for dictionary, name, value in self._setitem: for dictionary, name, value in self._setitem:
if value is notset: if value is notset:
del dictionary[name] del dictionary[name]
else: else:
dictionary[name] = value dictionary[name] = value
self._setitem[:] = []
def test_setattr(): def test_setattr():
@ -118,12 +134,16 @@ def test_setattr():
assert A.x == 2 assert A.x == 2
monkeypatch.setattr(A, 'x', 3) monkeypatch.setattr(A, 'x', 3)
assert A.x == 3 assert A.x == 3
monkeypatch.finalize() monkeypatch.undo()
assert A.x == 1 assert A.x == 1
A.x = 5
monkeypatch.undo() # double-undo makes no modification
assert A.x == 5
monkeypatch.setattr(A, 'y', 3) monkeypatch.setattr(A, 'y', 3)
assert A.y == 3 assert A.y == 3
monkeypatch.finalize() monkeypatch.undo()
assert not hasattr(A, 'y') assert not hasattr(A, 'y')
def test_delattr(): def test_delattr():
@ -132,7 +152,7 @@ def test_delattr():
monkeypatch = MonkeyPatch() monkeypatch = MonkeyPatch()
monkeypatch.delattr(A, 'x') monkeypatch.delattr(A, 'x')
assert not hasattr(A, 'x') assert not hasattr(A, 'x')
monkeypatch.finalize() monkeypatch.undo()
assert A.x == 1 assert A.x == 1
monkeypatch = MonkeyPatch() monkeypatch = MonkeyPatch()
@ -141,7 +161,7 @@ def test_delattr():
monkeypatch.delattr(A, 'y', raising=False) monkeypatch.delattr(A, 'y', raising=False)
monkeypatch.setattr(A, 'x', 5) monkeypatch.setattr(A, 'x', 5)
assert A.x == 5 assert A.x == 5
monkeypatch.finalize() monkeypatch.undo()
assert A.x == 1 assert A.x == 1
def test_setitem(): def test_setitem():
@ -153,9 +173,12 @@ def test_setitem():
assert d['y'] == 1700 assert d['y'] == 1700
monkeypatch.setitem(d, 'x', 3) monkeypatch.setitem(d, 'x', 3)
assert d['x'] == 3 assert d['x'] == 3
monkeypatch.finalize() monkeypatch.undo()
assert d['x'] == 1 assert d['x'] == 1
assert 'y' not in d assert 'y' not in d
d['x'] = 5
monkeypatch.undo()
assert d['x'] == 5
def test_delitem(): def test_delitem():
d = {'x': 1} d = {'x': 1}
@ -170,7 +193,7 @@ def test_delitem():
d['hello'] = 'world' d['hello'] = 'world'
monkeypatch.setitem(d, 'x', 1500) monkeypatch.setitem(d, 'x', 1500)
assert d['x'] == 1500 assert d['x'] == 1500
monkeypatch.finalize() monkeypatch.undo()
assert d == {'hello': 'world', 'x': 1} assert d == {'hello': 'world', 'x': 1}
def test_setenv(): def test_setenv():
@ -178,7 +201,7 @@ def test_setenv():
monkeypatch.setenv('XYZ123', 2) monkeypatch.setenv('XYZ123', 2)
import os import os
assert os.environ['XYZ123'] == "2" assert os.environ['XYZ123'] == "2"
monkeypatch.finalize() monkeypatch.undo()
assert 'XYZ123' not in os.environ assert 'XYZ123' not in os.environ
def test_delenv(): def test_delenv():
@ -187,7 +210,7 @@ def test_delenv():
monkeypatch = MonkeyPatch() monkeypatch = MonkeyPatch()
py.test.raises(KeyError, "monkeypatch.delenv(%r, raising=True)" % name) py.test.raises(KeyError, "monkeypatch.delenv(%r, raising=True)" % name)
monkeypatch.delenv(name, raising=False) monkeypatch.delenv(name, raising=False)
monkeypatch.finalize() monkeypatch.undo()
os.environ[name] = "1" os.environ[name] = "1"
try: try:
monkeypatch = MonkeyPatch() monkeypatch = MonkeyPatch()
@ -195,7 +218,7 @@ def test_delenv():
assert name not in os.environ assert name not in os.environ
monkeypatch.setenv(name, "3") monkeypatch.setenv(name, "3")
assert os.environ[name] == "3" assert os.environ[name] == "3"
monkeypatch.finalize() monkeypatch.undo()
assert os.environ[name] == "1" assert os.environ[name] == "1"
finally: finally:
if name in os.environ: if name in os.environ:
@ -208,7 +231,7 @@ def test_setenv_prepend():
assert os.environ['XYZ123'] == "2" assert os.environ['XYZ123'] == "2"
monkeypatch.setenv('XYZ123', 3, prepend="-") monkeypatch.setenv('XYZ123', 3, prepend="-")
assert os.environ['XYZ123'] == "3-2" assert os.environ['XYZ123'] == "3-2"
monkeypatch.finalize() monkeypatch.undo()
assert 'XYZ123' not in os.environ assert 'XYZ123' not in os.environ
def test_monkeypatch_plugin(testdir): def test_monkeypatch_plugin(testdir):

View File

@ -2,6 +2,16 @@ import py, os, sys
from py.__.test.plugin.pytest_capture import CaptureManager from py.__.test.plugin.pytest_capture import CaptureManager
class TestCaptureManager: class TestCaptureManager:
def test_getmethod_default_no_fd(self, testdir, monkeypatch):
config = testdir.parseconfig(testdir.tmpdir)
assert config.getvalue("capture") is None
capman = CaptureManager()
monkeypatch.delattr(os, 'dup')
try:
assert capman._getmethod(config, None) == "sys"
finally:
monkeypatch.finalize()
def test_configure_per_fspath(self, testdir): def test_configure_per_fspath(self, testdir):
config = testdir.parseconfig(testdir.tmpdir) config = testdir.parseconfig(testdir.tmpdir)
assert config.getvalue("capture") is None assert config.getvalue("capture") is None