merge Ronny's changes, add some documentation and changelog entries
--HG-- branch : trunk
This commit is contained in:
		
						commit
						6951da7da0
					
				|  | @ -4,6 +4,14 @@ Changes between 1.3.1 and 1.3.x | ||||||
| New features  | New features  | ||||||
| ++++++++++++++++++ | ++++++++++++++++++ | ||||||
| 
 | 
 | ||||||
|  | - fix issue103: introduce additional "with py.test.raises(exc):" form, example:: | ||||||
|  | 
 | ||||||
|  |     with py.test.raises(ZeroDivisionError): | ||||||
|  |         x = 0 | ||||||
|  |         1 / x | ||||||
|  | 
 | ||||||
|  |   (thanks Ronny Pfannschmidt)  | ||||||
|  | 
 | ||||||
| - Funcarg factories can now dynamically apply a marker to a  | - Funcarg factories can now dynamically apply a marker to a  | ||||||
|   test invocation.  This is particularly useful if a factory |   test invocation.  This is particularly useful if a factory | ||||||
|   provides parameters to a test which you expect-to-fail: |   provides parameters to a test which you expect-to-fail: | ||||||
|  |  | ||||||
|  | @ -130,14 +130,20 @@ first failed and then succeeded. | ||||||
| asserting expected exceptions  | asserting expected exceptions  | ||||||
| ---------------------------------------- | ---------------------------------------- | ||||||
| 
 | 
 | ||||||
| In order to write assertions about exceptions, you use | In order to write assertions about exceptions, you can use | ||||||
| one of two forms:: | ``py.test.raises`` as a context manager like this::  | ||||||
| 
 | 
 | ||||||
|     py.test.raises(Exception, func, *args, **kwargs)  |     with py.test.raises(ZeroDivisionError): | ||||||
|     py.test.raises(Exception, "func(*args, **kwargs)") |         1 / 0 | ||||||
|  | 
 | ||||||
|  | If you want to write test code that works on Python2.4 as well,  | ||||||
|  | you may also use two other ways to test for an expected exception:: | ||||||
|  | 
 | ||||||
|  |     py.test.raises(ExpectedException, func, *args, **kwargs)  | ||||||
|  |     py.test.raises(ExpectedException, "func(*args, **kwargs)") | ||||||
| 
 | 
 | ||||||
| both of which execute the specified function with args and kwargs and | both of which execute the specified function with args and kwargs and | ||||||
| asserts that the given ``Exception`` is raised.  The reporter will | asserts that the given ``ExpectedException`` is raised.  The reporter will | ||||||
| provide you with helpful output in case of failures such as *no | provide you with helpful output in case of failures such as *no | ||||||
| exception* or *wrong exception*. | exception* or *wrong exception*. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1 +0,0 @@ | ||||||
| # |  | ||||||
|  | @ -1,65 +0,0 @@ | ||||||
| import threading |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class PathServer: |  | ||||||
| 
 |  | ||||||
|     def __init__(self, channel): |  | ||||||
|         self.channel = channel |  | ||||||
|         self.C2P = {} |  | ||||||
|         self.next_id = 0 |  | ||||||
|         threading.Thread(target=self.serve).start() |  | ||||||
| 
 |  | ||||||
|     def p2c(self, path): |  | ||||||
|         id = self.next_id |  | ||||||
|         self.next_id += 1 |  | ||||||
|         self.C2P[id] = path |  | ||||||
|         return id |  | ||||||
| 
 |  | ||||||
|     def command_LIST(self, id, *args): |  | ||||||
|         path = self.C2P[id] |  | ||||||
|         answer = [(self.p2c(p), p.basename) for p in path.listdir(*args)] |  | ||||||
|         self.channel.send(answer) |  | ||||||
| 
 |  | ||||||
|     def command_DEL(self, id): |  | ||||||
|         del self.C2P[id] |  | ||||||
| 
 |  | ||||||
|     def command_GET(self, id, spec): |  | ||||||
|         path = self.C2P[id] |  | ||||||
|         self.channel.send(path._getbyspec(spec)) |  | ||||||
| 
 |  | ||||||
|     def command_READ(self, id): |  | ||||||
|         path = self.C2P[id] |  | ||||||
|         self.channel.send(path.read()) |  | ||||||
| 
 |  | ||||||
|     def command_JOIN(self, id, resultid, *args): |  | ||||||
|         path = self.C2P[id] |  | ||||||
|         assert resultid not in self.C2P |  | ||||||
|         self.C2P[resultid] = path.join(*args) |  | ||||||
| 
 |  | ||||||
|     def command_DIRPATH(self, id, resultid): |  | ||||||
|         path = self.C2P[id] |  | ||||||
|         assert resultid not in self.C2P |  | ||||||
|         self.C2P[resultid] = path.dirpath() |  | ||||||
| 
 |  | ||||||
|     def serve(self): |  | ||||||
|         try: |  | ||||||
|             while 1: |  | ||||||
|                 msg = self.channel.receive() |  | ||||||
|                 meth = getattr(self, 'command_' + msg[0]) |  | ||||||
|                 meth(*msg[1:]) |  | ||||||
|         except EOFError: |  | ||||||
|             pass |  | ||||||
| 
 |  | ||||||
| if __name__ == '__main__': |  | ||||||
|     import py |  | ||||||
|     gw = execnet.PopenGateway() |  | ||||||
|     channel = gw._channelfactory.new() |  | ||||||
|     srv = PathServer(channel) |  | ||||||
|     c = gw.remote_exec(""" |  | ||||||
|         import remotepath |  | ||||||
|         p = remotepath.RemotePath(channel.receive(), channel.receive()) |  | ||||||
|         channel.send(len(p.listdir())) |  | ||||||
|     """) |  | ||||||
|     c.send(channel) |  | ||||||
|     c.send(srv.p2c(py.path.local('/tmp'))) |  | ||||||
|     print(c.receive()) |  | ||||||
|  | @ -1,21 +0,0 @@ | ||||||
| import py |  | ||||||
| from remotepath import RemotePath |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| SRC = open('channeltest.py', 'r').read() |  | ||||||
| 
 |  | ||||||
| SRC += ''' |  | ||||||
| import py |  | ||||||
| srv = PathServer(channel.receive()) |  | ||||||
| channel.send(srv.p2c(py.path.local("/tmp"))) |  | ||||||
| ''' |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| #gw = execnet.SshGateway('codespeak.net') |  | ||||||
| gw = execnet.PopenGateway() |  | ||||||
| gw.remote_init_threads(5) |  | ||||||
| c = gw.remote_exec(SRC, stdout=py.std.sys.stdout, stderr=py.std.sys.stderr) |  | ||||||
| subchannel = gw._channelfactory.new() |  | ||||||
| c.send(subchannel) |  | ||||||
| 
 |  | ||||||
| p = RemotePath(subchannel, c.receive()) |  | ||||||
|  | @ -1,47 +0,0 @@ | ||||||
| import py, itertools |  | ||||||
| from py._path import common |  | ||||||
| 
 |  | ||||||
| COUNTER = itertools.count() |  | ||||||
| 
 |  | ||||||
| class RemotePath(common.PathBase): |  | ||||||
|     sep = '/' |  | ||||||
| 
 |  | ||||||
|     def __init__(self, channel, id, basename=None): |  | ||||||
|         self._channel = channel |  | ||||||
|         self._id = id |  | ||||||
|         self._basename = basename |  | ||||||
|         self._specs = {} |  | ||||||
| 
 |  | ||||||
|     def __del__(self): |  | ||||||
|         self._channel.send(('DEL', self._id)) |  | ||||||
| 
 |  | ||||||
|     def __repr__(self): |  | ||||||
|         return 'RemotePath(%s)' % self.basename |  | ||||||
| 
 |  | ||||||
|     def listdir(self, *args): |  | ||||||
|         self._channel.send(('LIST', self._id) + args) |  | ||||||
|         return [RemotePath(self._channel, id, basename) |  | ||||||
|                 for (id, basename) in self._channel.receive()] |  | ||||||
| 
 |  | ||||||
|     def dirpath(self): |  | ||||||
|         id = ~COUNTER.next() |  | ||||||
|         self._channel.send(('DIRPATH', self._id, id)) |  | ||||||
|         return RemotePath(self._channel, id) |  | ||||||
| 
 |  | ||||||
|     def join(self, *args): |  | ||||||
|         id = ~COUNTER.next() |  | ||||||
|         self._channel.send(('JOIN', self._id, id) + args) |  | ||||||
|         return RemotePath(self._channel, id) |  | ||||||
| 
 |  | ||||||
|     def _getbyspec(self, spec): |  | ||||||
|         parts = spec.split(',') |  | ||||||
|         ask = [x for x in parts  if x not in self._specs] |  | ||||||
|         if ask: |  | ||||||
|             self._channel.send(('GET', self._id, ",".join(ask))) |  | ||||||
|             for part, value in zip(ask, self._channel.receive()): |  | ||||||
|                 self._specs[part] = value |  | ||||||
|         return [self._specs[x] for x in parts] |  | ||||||
| 
 |  | ||||||
|     def read(self): |  | ||||||
|         self._channel.send(('READ', self._id)) |  | ||||||
|         return self._channel.receive() |  | ||||||
|  | @ -353,18 +353,30 @@ def xfail(reason=""): | ||||||
| xfail.Exception = XFailed | xfail.Exception = XFailed | ||||||
| 
 | 
 | ||||||
| def raises(ExpectedException, *args, **kwargs): | def raises(ExpectedException, *args, **kwargs): | ||||||
|     """ if args[0] is callable: raise AssertionError if calling it with  |     """ assert that a code block/function call raises an exception.  | ||||||
|  |          | ||||||
|  |         If using Python 2.5 or above, you may use this function as a  | ||||||
|  |         context manager:: | ||||||
|  | 
 | ||||||
|  |         >>> with raises(ZeroDivisionError): | ||||||
|  |         ...    1/0 | ||||||
|  | 
 | ||||||
|  |         Or you can one of two forms:  | ||||||
|  | 
 | ||||||
|  |         if args[0] is callable: raise AssertionError if calling it with  | ||||||
|         the remaining arguments does not raise the expected exception.   |         the remaining arguments does not raise the expected exception.   | ||||||
|         if args[0] is a string: raise AssertionError if executing the |         if args[0] is a string: raise AssertionError if executing the | ||||||
|         the string in the calling scope does not raise expected exception.  |         the string in the calling scope does not raise expected exception.  | ||||||
|         for examples: |         examples: | ||||||
|         x = 5 |         >>> x = 5 | ||||||
|         raises(TypeError, lambda x: x + 'hello', x=x) |         >>> raises(TypeError, lambda x: x + 'hello', x=x) | ||||||
|         raises(TypeError, "x + 'hello'") |         >>> raises(TypeError, "x + 'hello'") | ||||||
|     """ |     """ | ||||||
|     __tracebackhide__ = True  |     __tracebackhide__ = True | ||||||
|     assert args | 
 | ||||||
|     if isinstance(args[0], str): |     if not args: | ||||||
|  |         return RaisesContext(ExpectedException) | ||||||
|  |     elif isinstance(args[0], str): | ||||||
|         code, = args |         code, = args | ||||||
|         assert isinstance(code, str) |         assert isinstance(code, str) | ||||||
|         frame = sys._getframe(1) |         frame = sys._getframe(1) | ||||||
|  | @ -391,6 +403,26 @@ def raises(ExpectedException, *args, **kwargs): | ||||||
|     raise ExceptionFailure(msg="DID NOT RAISE",  |     raise ExceptionFailure(msg="DID NOT RAISE",  | ||||||
|                            expr=args, expected=ExpectedException)  |                            expr=args, expected=ExpectedException)  | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | class RaisesContext(object): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, ExpectedException): | ||||||
|  |         self.ExpectedException = ExpectedException | ||||||
|  |         self.excinfo = None | ||||||
|  | 
 | ||||||
|  |     def __enter__(self): | ||||||
|  |         return self | ||||||
|  | 
 | ||||||
|  |     def __exit__(self, *tp): | ||||||
|  |         __tracebackhide__ = True | ||||||
|  |         if tp[0] is None: | ||||||
|  |             raise ExceptionFailure(msg="DID NOT RAISE", | ||||||
|  |                                    expr=(), | ||||||
|  |                                    expected=self.ExpectedException) | ||||||
|  |         self.excinfo = py.code.ExceptionInfo(tp) | ||||||
|  |         return issubclass(self.excinfo.type, self.ExpectedException) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| raises.Exception = ExceptionFailure | raises.Exception = ExceptionFailure | ||||||
| 
 | 
 | ||||||
| def importorskip(modname, minversion=None): | def importorskip(modname, minversion=None): | ||||||
|  |  | ||||||
|  | @ -340,6 +340,37 @@ class TestRaises: | ||||||
|         except py.test.raises.Exception: |         except py.test.raises.Exception: | ||||||
|             pass |             pass | ||||||
| 
 | 
 | ||||||
|  |     @py.test.mark.skipif('sys.version < "2.5"') | ||||||
|  |     def test_raises_as_contextmanager(self, testdir): | ||||||
|  |         testdir.makepyfile(""" | ||||||
|  |             from __future__ import with_statement | ||||||
|  |             import py | ||||||
|  | 
 | ||||||
|  |             def test_simple(): | ||||||
|  |                 with py.test.raises(ZeroDivisionError) as ctx: | ||||||
|  |                     1/0 | ||||||
|  | 
 | ||||||
|  |                 print ctx.excinfo | ||||||
|  |                 assert ctx.excinfo.type is ZeroDivisionError | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             def test_noraise(): | ||||||
|  |                 with py.test.raises(py.test.raises.Exception): | ||||||
|  |                     with py.test.raises(ValueError): | ||||||
|  |                            int() | ||||||
|  | 
 | ||||||
|  |             def test_raise_wrong_exception_passes_by(): | ||||||
|  |                 with py.test.raises(ZeroDivisionError): | ||||||
|  |                     with py.test.raises(ValueError): | ||||||
|  |                            1/0 | ||||||
|  |         """) | ||||||
|  |         result = testdir.runpytest() | ||||||
|  |         result.stdout.fnmatch_lines([ | ||||||
|  |             '*3 passed*', | ||||||
|  |         ]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| def test_pytest_exit(): | def test_pytest_exit(): | ||||||
|     try: |     try: | ||||||
|         py.test.exit("hello") |         py.test.exit("hello") | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue