[py-svn] r52664 - in py/branch/bugfix-0.9.0/py: . apigen apigen/testing apigen/tracer/testing/package/submodule/pak bin builtin builtin/testing code code/testing doc doc/future execnet execnet/script execnet/testing io magic misc misc/testing path/local path/svn path/svn/testing test test/rsession test/rsession/testing test/rsession/webdata test/testing tool
guido at codespeak.net
guido at codespeak.net
Mon Mar 17 20:18:50 CET 2008
Author: guido
Date: Mon Mar 17 20:18:48 2008
New Revision: 52664
Added:
py/branch/bugfix-0.9.0/py/misc/killproc.py (contents, props changed)
py/branch/bugfix-0.9.0/py/misc/testing/test_oskill.py (contents, props changed)
py/branch/bugfix-0.9.0/py/path/svn/testing/test_auth.py (contents, props changed)
Modified:
py/branch/bugfix-0.9.0/py/__init__.py
py/branch/bugfix-0.9.0/py/apigen/apigen.py (props changed)
py/branch/bugfix-0.9.0/py/apigen/html.py (props changed)
py/branch/bugfix-0.9.0/py/apigen/testing/test_apigen_functional.py (contents, props changed)
py/branch/bugfix-0.9.0/py/apigen/testing/test_htmlgen.py (props changed)
py/branch/bugfix-0.9.0/py/apigen/todo-apigen.txt (props changed)
py/branch/bugfix-0.9.0/py/apigen/tracer/testing/package/submodule/pak/somenamespace.py (props changed)
py/branch/bugfix-0.9.0/py/bin/_docgen.py (contents, props changed)
py/branch/bugfix-0.9.0/py/bin/_update_website.py (props changed)
py/branch/bugfix-0.9.0/py/bin/py.lookup
py/branch/bugfix-0.9.0/py/builtin/set.py (props changed)
py/branch/bugfix-0.9.0/py/builtin/testing/test_set.py (props changed)
py/branch/bugfix-0.9.0/py/code/excinfo.py
py/branch/bugfix-0.9.0/py/code/source.py
py/branch/bugfix-0.9.0/py/code/testing/test_source.py
py/branch/bugfix-0.9.0/py/code/traceback2.py
py/branch/bugfix-0.9.0/py/conftest.py
py/branch/bugfix-0.9.0/py/doc/code.txt (props changed)
py/branch/bugfix-0.9.0/py/doc/conftest.py
py/branch/bugfix-0.9.0/py/doc/future/pylib_pypy.txt (props changed)
py/branch/bugfix-0.9.0/py/doc/impl-test.txt (contents, props changed)
py/branch/bugfix-0.9.0/py/doc/path.txt (contents, props changed)
py/branch/bugfix-0.9.0/py/execnet/channel.py
py/branch/bugfix-0.9.0/py/execnet/gateway.py
py/branch/bugfix-0.9.0/py/execnet/inputoutput.py
py/branch/bugfix-0.9.0/py/execnet/register.py
py/branch/bugfix-0.9.0/py/execnet/script/loop_socketserver.py (props changed)
py/branch/bugfix-0.9.0/py/execnet/testing/test_gateway.py
py/branch/bugfix-0.9.0/py/io/fdcapture.py
py/branch/bugfix-0.9.0/py/magic/greenlet.py
py/branch/bugfix-0.9.0/py/misc/conftest-socketgatewayrun.py (contents, props changed)
py/branch/bugfix-0.9.0/py/misc/testing/test_terminal.py (props changed)
py/branch/bugfix-0.9.0/py/misc/testing/test_update_website.py (props changed)
py/branch/bugfix-0.9.0/py/path/local/local.py
py/branch/bugfix-0.9.0/py/path/svn/svncommon.py
py/branch/bugfix-0.9.0/py/path/svn/testing/test_urlcommand.py
py/branch/bugfix-0.9.0/py/path/svn/testing/test_wccommand.py
py/branch/bugfix-0.9.0/py/path/svn/urlcommand.py
py/branch/bugfix-0.9.0/py/path/svn/wccommand.py
py/branch/bugfix-0.9.0/py/test/collect.py
py/branch/bugfix-0.9.0/py/test/conftesthandle.py (props changed)
py/branch/bugfix-0.9.0/py/test/item.py
py/branch/bugfix-0.9.0/py/test/outcome.py (contents, props changed)
py/branch/bugfix-0.9.0/py/test/representation.py (contents, props changed)
py/branch/bugfix-0.9.0/py/test/rsession/executor.py
py/branch/bugfix-0.9.0/py/test/rsession/reporter.py
py/branch/bugfix-0.9.0/py/test/rsession/testing/basetest.py (props changed)
py/branch/bugfix-0.9.0/py/test/rsession/testing/test_executor.py
py/branch/bugfix-0.9.0/py/test/rsession/testing/test_hostmanage.py (props changed)
py/branch/bugfix-0.9.0/py/test/rsession/testing/test_reporter.py
py/branch/bugfix-0.9.0/py/test/rsession/testing/test_rsession.py
py/branch/bugfix-0.9.0/py/test/rsession/testing/test_web.py
py/branch/bugfix-0.9.0/py/test/rsession/web.py
py/branch/bugfix-0.9.0/py/test/rsession/webdata/index.html
py/branch/bugfix-0.9.0/py/test/rsession/webdata/source.js
py/branch/bugfix-0.9.0/py/test/rsession/webjs.py
py/branch/bugfix-0.9.0/py/test/session.py
py/branch/bugfix-0.9.0/py/test/testing/setupdata.py (props changed)
py/branch/bugfix-0.9.0/py/test/testing/test_conftesthandle.py (props changed)
py/branch/bugfix-0.9.0/py/test/testing/test_repr.py (props changed)
py/branch/bugfix-0.9.0/py/test/testing/test_session.py
py/branch/bugfix-0.9.0/py/test/testing/test_setup_nested.py
py/branch/bugfix-0.9.0/py/tool/utestconvert.py
Log:
Merging all bugfixes and some small features from the trunk to the 0.9 branch,
in preparation of the upcoming 0.9.1 release.
Revisions that were merged:
38967, 38969, 39106, 39340, 39655, 39982, 39994, 40001, 40002, 40702, 40737,
40738, 40739, 40831, 40832, 40834, 40934, 41224, 41480, 43299, 43575, 44248,
44648, 44655, 45294, 45295, 45483, 45484, 45518, 45519, 45535, 45538, 45539,
45541, 45545, 45547, 45548, 45549, 45646, 45647, 45648, 45649, 45655, 45671,
45901, 45906, 45994, 46010, 46692, 47277, 49423, 49974, 50606, 50645, 50755,
51285, 51292, 52000, 52001, 52481
Modified: py/branch/bugfix-0.9.0/py/__init__.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/__init__.py (original)
+++ py/branch/bugfix-0.9.0/py/__init__.py Mon Mar 17 20:18:48 2008
@@ -1,3 +1,5 @@
+
+# -*- coding: utf-8 -*-
"""
the py lib is a development support library featuring
py.test, ad-hoc distributed execution, micro-threads
@@ -5,7 +7,7 @@
"""
from initpkg import initpkg
-version = "0.9.0"
+version = "0.9.1-alpha"
initpkg(__name__,
description = "py lib: agile development and test support library",
@@ -13,9 +15,9 @@
lastchangedate = '$LastChangedDate$',
version = version,
url = "http://codespeak.net/py",
- download_url = "http://codespeak.net/download/py/py-%s.tar.gz" %(version,),
+ download_url = "XXX", # "http://codespeak.net/download/py/py-%s.tar.gz" %(version,),
license = "MIT license",
- platforms = ['unix', 'linux', 'cygwin'],
+ platforms = ['unix', 'linux', 'cygwin', 'win32'],
author = "holger krekel, Carl Friedrich Bolz, Guido Wesdorp, Maciej Fijalkowski, Armin Rigo & others",
author_email = "py-dev at codespeak.net",
long_description = globals()['__doc__'],
@@ -29,6 +31,8 @@
'test.skip' : ('./test/item.py', 'skip'),
'test.fail' : ('./test/item.py', 'fail'),
'test.exit' : ('./test/session.py', 'exit'),
+ 'test.broken' : ('./test/item.py', 'Broken'),
+ 'test.notimplemented' : ('./test/item.py', '_NotImplemented'),
# configuration/initialization related test api
'test.config' : ('./test/config.py', 'config_per_process'),
@@ -62,6 +66,7 @@
'path.svnwc' : ('./path/svn/wccommand.py', 'SvnWCCommandPath'),
'path.svnurl' : ('./path/svn/urlcommand.py', 'SvnCommandPath'),
'path.local' : ('./path/local/local.py', 'LocalPath'),
+ 'path.SvnAuth' : ('./path/svn/svncommon.py', 'SvnAuth'),
# some nice slightly magic APIs
'magic.__doc__' : ('./magic/__init__.py', '__doc__'),
Modified: py/branch/bugfix-0.9.0/py/apigen/testing/test_apigen_functional.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/apigen/testing/test_apigen_functional.py (original)
+++ py/branch/bugfix-0.9.0/py/apigen/testing/test_apigen_functional.py Mon Mar 17 20:18:48 2008
@@ -116,7 +116,9 @@
pkgname, documentable = apigen.get_documentable_items_pkgdir(
fs_root.join(package_name))
assert pkgname == 'pak'
- assert sorted(documentable.keys()) == [
+ keys = documentable.keys()
+ keys.sort()
+ assert keys == [
'main.SomeTestClass', 'main.SomeTestSubClass', 'main.func',
'main.sub.func', 'somenamespace.baz', 'somenamespace.foo']
Modified: py/branch/bugfix-0.9.0/py/bin/_docgen.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/bin/_docgen.py (original)
+++ py/branch/bugfix-0.9.0/py/bin/_docgen.py Mon Mar 17 20:18:48 2008
@@ -28,7 +28,7 @@
def build_docs(targetpath, testargs):
docpath = pypath.join('doc')
run_tests(docpath, '',
- testargs + ' --forcegen --apigenrelpath="apigen/"')
+ testargs + ' --forcegen --apigen="%s/apigen/apigen.py"' % (pypath,))
docpath.copy(targetpath)
def build_nav(targetpath, docs=True, api=True):
Modified: py/branch/bugfix-0.9.0/py/bin/py.lookup
==============================================================================
--- py/branch/bugfix-0.9.0/py/bin/py.lookup (original)
+++ py/branch/bugfix-0.9.0/py/bin/py.lookup Mon Mar 17 20:18:48 2008
@@ -42,8 +42,11 @@
string = args[0]
if options.ignorecase:
string = string.lower()
- for x in curdir.visit('*.py', rec):
- s = x.read()
+ for x in curdir.visit('*.py', rec):
+ try:
+ s = x.read()
+ except py.error.ENOENT:
+ pass # whatever, probably broken link (ie emacs lock)
searchs = s
if options.ignorecase:
searchs = s.lower()
Modified: py/branch/bugfix-0.9.0/py/code/excinfo.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/code/excinfo.py (original)
+++ py/branch/bugfix-0.9.0/py/code/excinfo.py Mon Mar 17 20:18:48 2008
@@ -18,7 +18,7 @@
self._striptext = 'AssertionError: '
self._excinfo = tup
self.type, self.value, tb = self._excinfo
- self.typename = str(self.type)
+ self.typename = self.type.__module__ + '.' + self.type.__name__
self.traceback = py.code.Traceback(tb)
def exconly(self, tryshort=False):
Modified: py/branch/bugfix-0.9.0/py/code/source.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/code/source.py (original)
+++ py/branch/bugfix-0.9.0/py/code/source.py Mon Mar 17 20:18:48 2008
@@ -109,7 +109,7 @@
for start in range(lineno, -1, -1):
trylines = self.lines[start:lineno+1]
# quick hack to indent the source and get it as a string in one go
- trylines.insert(0, 'if 0:')
+ trylines.insert(0, 'def xxx():')
trysource = '\n '.join(trylines)
# ^ space here
try:
Modified: py/branch/bugfix-0.9.0/py/code/testing/test_source.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/code/testing/test_source.py (original)
+++ py/branch/bugfix-0.9.0/py/code/testing/test_source.py Mon Mar 17 20:18:48 2008
@@ -281,3 +281,15 @@
"""
lines = deindent(source.splitlines())
assert lines == ['', 'def f():', ' def g():', ' pass', ' ']
+
+def test_write_read():
+ py.test.skip("Failing")
+ tmpdir = py.test.ensuretemp("source_write_read")
+ source = py.code.Source('''
+ class A(object):
+ def method(self):
+ x = 1
+ ''')
+ tmpdir.ensure("a.py").write(source)
+ s2 = py.code.Source(tmpdir.join("a.py").pyimport().A)
+ assert source == s2
Modified: py/branch/bugfix-0.9.0/py/code/traceback2.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/code/traceback2.py (original)
+++ py/branch/bugfix-0.9.0/py/code/traceback2.py Mon Mar 17 20:18:48 2008
@@ -1,5 +1,6 @@
from __future__ import generators
-import py
+import py
+import sys
class TracebackEntry(object):
""" a single entry in a traceback """
@@ -9,6 +10,9 @@
def __init__(self, rawentry):
self._rawentry = rawentry
self.frame = py.code.Frame(rawentry.tb_frame)
+ # Ugh. 2.4 and 2.5 differs here when encountering
+ # multi-line statements. Not sure about the solution, but
+ # should be portable
self.lineno = rawentry.tb_lineno - 1
self.relline = self.lineno - self.frame.code.firstlineno
Modified: py/branch/bugfix-0.9.0/py/conftest.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/conftest.py (original)
+++ py/branch/bugfix-0.9.0/py/conftest.py Mon Mar 17 20:18:48 2008
@@ -33,6 +33,9 @@
action='store', dest='docpath',
default="doc", type='string',
help="relative path to doc output location (relative from py/)"),
+ Option('', '--runslowtests',
+ action="store_true", dest="runslowtests", default=False,
+ help="run slow tests)"),
)
dist_rsync_roots = ['.']
Modified: py/branch/bugfix-0.9.0/py/doc/conftest.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/doc/conftest.py (original)
+++ py/branch/bugfix-0.9.0/py/doc/conftest.py Mon Mar 17 20:18:48 2008
@@ -240,8 +240,9 @@
fn = ishtml and fn.new(ext='.txt') or fn
print "filename is", fn
if not fn.check(): # not ishtml or not fn.check():
- py.test.fail("reference error %r in %s:%d" %(
- tryfn, path.basename, lineno+1))
+ if not py.path.local(tryfn).check(): # the html could be there
+ py.test.fail("reference error %r in %s:%d" %(
+ tryfn, path.basename, lineno+1))
if anchor:
source = unicode(fn.read(), 'latin1')
source = source.lower().replace('-', ' ') # aehem
Modified: py/branch/bugfix-0.9.0/py/doc/impl-test.txt
==============================================================================
--- py/branch/bugfix-0.9.0/py/doc/impl-test.txt (original)
+++ py/branch/bugfix-0.9.0/py/doc/impl-test.txt Mon Mar 17 20:18:48 2008
@@ -180,8 +180,8 @@
custom Collectors and Items if they are found
in a local ``conftest.py`` file.
-example: perform additional ReST checs
-++++++++++++++++++++++++++++++++++++++
+example: perform additional ReST checks
++++++++++++++++++++++++++++++++++++++++
With your custom collectors or items you can completely
derive from the standard way of collecting and running
Modified: py/branch/bugfix-0.9.0/py/doc/path.txt
==============================================================================
--- py/branch/bugfix-0.9.0/py/doc/path.txt (original)
+++ py/branch/bugfix-0.9.0/py/doc/path.txt Mon Mar 17 20:18:48 2008
@@ -187,6 +187,23 @@
>>> len(wc.status().prop_modified)
0
+SVN authentication
+++++++++++++++++++++++
+
+Some uncommon functionality can also be provided as extensions, such as SVN
+authentication::
+
+ >>> auth = py.path.SvnAuth('anonymous', 'user', cache_auth=False,
+ ... interactive=False)
+ >>> wc.auth = auth
+ >>> wc.update() # this should work
+ >>> path = wc.ensure('thisshouldnotexist.txt')
+ >>> try:
+ ... path.commit('testing')
+ ... except py.process.cmdexec.Error, e:
+ ... pass
+ >>> 'authorization failed' in str(e)
+ True
Known problems / limitations
===================================
Modified: py/branch/bugfix-0.9.0/py/execnet/channel.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/execnet/channel.py (original)
+++ py/branch/bugfix-0.9.0/py/execnet/channel.py Mon Mar 17 20:18:48 2008
@@ -85,7 +85,7 @@
Msg = Message.CHANNEL_LAST_MESSAGE
else:
Msg = Message.CHANNEL_CLOSE
- self.gateway._outgoing.put(Msg(self.id))
+ self.gateway._send(Msg(self.id))
def _getremoteerror(self):
try:
@@ -117,7 +117,7 @@
# state transition "opened/sendonly" --> "closed"
# threads warning: the channel might be closed under our feet,
# but it's never damaging to send too many CHANNEL_CLOSE messages
- put = self.gateway._outgoing.put
+ put = self.gateway._send
if error is not None:
put(Message.CHANNEL_CLOSE_ERROR(self.id, str(error)))
else:
@@ -157,7 +157,7 @@
data = Message.CHANNEL_NEW(self.id, item.id)
else:
data = Message.CHANNEL_DATA(self.id, item)
- self.gateway._outgoing.put(data)
+ self.gateway._send(data)
def receive(self):
"""receives an item that was sent from the other side,
Modified: py/branch/bugfix-0.9.0/py/execnet/gateway.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/execnet/gateway.py (original)
+++ py/branch/bugfix-0.9.0/py/execnet/gateway.py Mon Mar 17 20:18:48 2008
@@ -22,33 +22,62 @@
from py.__.execnet.channel import ChannelFactory, Channel
from py.__.execnet.message import Message
ThreadOut = py._thread.ThreadOut
- WorkerPool = py._thread.WorkerPool
- NamedThreadPool = py._thread.NamedThreadPool
import os
-debug = open('/tmp/execnet-debug-%d' % os.getpid() , 'wa')
+debug = 0 # open('/tmp/execnet-debug-%d' % os.getpid() , 'wa')
sysex = (KeyboardInterrupt, SystemExit)
+# ----------------------------------------------------------
+# cleanup machinery (for exiting processes)
+# ----------------------------------------------------------
+
+class GatewayCleanup:
+ def __init__(self):
+ self._activegateways = weakref.WeakKeyDictionary()
+ atexit.register(self.cleanup_atexit)
+
+ def register(self, gateway):
+ assert gateway not in self._activegateways
+ self._activegateways[gateway] = True
+
+ def unregister(self, gateway):
+ del self._activegateways[gateway]
+
+ def cleanup_atexit(self):
+ if debug:
+ print >>debug, "="*20 + "cleaning up" + "=" * 20
+ debug.flush()
+ for gw in self._activegateways.keys():
+ gw.exit()
+ #gw.join() # should work as well
+
+# ----------------------------------------------------------
+# Base Gateway (used for both remote and local side)
+# ----------------------------------------------------------
+
class Gateway(object):
+ class _StopExecLoop(Exception): pass
_ThreadOut = ThreadOut
remoteaddress = ""
- def __init__(self, io, execthreads=None, _startcount=2):
+ _requestqueue = None
+ _cleanup = GatewayCleanup()
+
+ def __init__(self, io, _startcount=2):
""" initialize core gateway, using the given
- inputoutput object and 'execthreads' execution
- threads.
+ inputoutput object.
"""
- global registered_cleanup
- self._execpool = WorkerPool(maxthreads=execthreads)
self._io = io
- self._outgoing = Queue.Queue()
self._channelfactory = ChannelFactory(self, _startcount)
- if not registered_cleanup:
- atexit.register(cleanup_atexit)
- registered_cleanup = True
- _active_sendqueues[self._outgoing] = True
- self._pool = NamedThreadPool(receiver = self._thread_receiver,
- sender = self._thread_sender)
+ self._cleanup.register(self)
+
+ def _initreceive(self, requestqueue=False):
+ if requestqueue:
+ self._requestqueue = Queue.Queue()
+ self._receiverthread = threading.Thread(name="receiver",
+ target=self._thread_receiver)
+ self._receiverthread.setDaemon(1)
+ self._receiverthread.start()
def __repr__(self):
""" return string representing gateway type and status. """
@@ -58,10 +87,9 @@
else:
addr = ''
try:
- r = (len(self._pool.getstarted('receiver'))
- and "receiving" or "not receiving")
- s = (len(self._pool.getstarted('sender'))
- and "sending" or "not sending")
+ r = (self._receiverthread.isAlive() and "receiving" or
+ "not receiving")
+ s = "sending" # XXX
i = len(self._channelfactory.channels())
except AttributeError:
r = s = "uninitialized"
@@ -69,9 +97,6 @@
return "<%s%s %s/%s (%s active channels)>" %(
self.__class__.__name__, addr, r, s, i)
-## def _local_trystopexec(self):
-## self._execpool.shutdown()
-
def _trace(self, *args):
if debug:
try:
@@ -111,32 +136,25 @@
self._traceex(exc_info())
break
finally:
- self._outgoing.put(None)
+ self._stopexec()
+ self._stopsend()
self._channelfactory._finished_receiving()
self._trace('leaving %r' % threading.currentThread())
- def _thread_sender(self):
- """ thread to send Messages over the wire. """
- try:
- from sys import exc_info
- while 1:
- msg = self._outgoing.get()
- try:
- if msg is None:
- self._io.close_write()
- break
- msg.writeto(self._io)
- except:
- excinfo = exc_info()
- self._traceex(excinfo)
- if msg is not None:
- msg.post_sent(self, excinfo)
- break
- else:
- self._trace('sent -> %r' % msg)
- msg.post_sent(self)
- finally:
- self._trace('leaving %r' % threading.currentThread())
+ from sys import exc_info
+ def _send(self, msg):
+ if msg is None:
+ self._io.close_write()
+ else:
+ try:
+ msg.writeto(self._io)
+ except:
+ excinfo = self.exc_info()
+ self._traceex(excinfo)
+ msg.post_sent(self, excinfo)
+ else:
+ msg.post_sent(self)
+ self._trace('sent -> %r' % msg)
def _local_redirect_thread_output(self, outid, errid):
l = []
@@ -152,9 +170,58 @@
channel.close()
return close
- def _thread_executor(self, channel, (source, outid, errid)):
- """ worker thread to execute source objects from the execution queue. """
+ def _local_schedulexec(self, channel, sourcetask):
+ if self._requestqueue is not None:
+ self._requestqueue.put((channel, sourcetask))
+ else:
+ # we will not execute, let's send back an error
+ # to inform the other side
+ channel.close("execution disallowed")
+
+ def _servemain(self, joining=True):
from sys import exc_info
+ self._initreceive(requestqueue=True)
+ try:
+ while 1:
+ item = self._requestqueue.get()
+ if item is None:
+ self._stopsend()
+ break
+ try:
+ self._executetask(item)
+ except self._StopExecLoop:
+ break
+ finally:
+ self._trace("_servemain finished")
+ if joining:
+ self.join()
+
+ def remote_init_threads(self, num=None):
+ """ start up to 'num' threads for subsequent
+ remote_exec() invocations to allow concurrent
+ execution.
+ """
+ if hasattr(self, '_remotechannelthread'):
+ raise IOError("remote threads already running")
+ from py.__.thread import pool
+ source = py.code.Source(pool, """
+ execpool = WorkerPool(maxthreads=%r)
+ gw = channel.gateway
+ while 1:
+ task = gw._requestqueue.get()
+ if task is None:
+ gw._stopsend()
+ execpool.shutdown()
+ execpool.join()
+ raise gw._StopExecLoop
+ execpool.dispatch(gw._executetask, task)
+ """ % num)
+ self._remotechannelthread = self.remote_exec(source)
+
+ def _executetask(self, item):
+ """ execute channel/source items. """
+ from sys import exc_info
+ channel, (source, outid, errid) = item
try:
loc = { 'channel' : channel }
self._trace("execution starts:", repr(source)[:50])
@@ -168,6 +235,9 @@
self._trace("execution finished:", repr(source)[:50])
except (KeyboardInterrupt, SystemExit):
pass
+ except self._StopExecLoop:
+ channel.close()
+ raise
except:
excinfo = exc_info()
l = traceback.format_exception(*excinfo)
@@ -177,10 +247,6 @@
else:
channel.close()
- def _local_schedulexec(self, channel, sourcetask):
- self._trace("dispatching exec")
- self._execpool.dispatch(self._thread_executor, channel, sourcetask)
-
def _newredirectchannelid(self, callback):
if callback is None:
return
@@ -219,8 +285,8 @@
channel = self.newchannel()
outid = self._newredirectchannelid(stdout)
errid = self._newredirectchannelid(stderr)
- self._outgoing.put(Message.CHANNEL_OPEN(channel.id,
- (source, outid, errid)))
+ self._send(Message.CHANNEL_OPEN(
+ channel.id, (source, outid, errid)))
return channel
def _remote_redirect(self, stdout=None, stderr=None):
@@ -254,27 +320,26 @@
return Handle()
def exit(self):
- """ Try to stop all IO activity. """
- try:
- del _active_sendqueues[self._outgoing]
- except KeyError:
- pass
- else:
- self._outgoing.put(None)
+ """ Try to stop all exec and IO activity. """
+ self._cleanup.unregister(self)
+ self._stopexec()
+ self._stopsend()
+
+ def _stopsend(self):
+ self._send(None)
+
+ def _stopexec(self):
+ if self._requestqueue is not None:
+ self._requestqueue.put(None)
def join(self, joinexec=True):
""" Wait for all IO (and by default all execution activity)
- to stop.
+ to stop. the joinexec parameter is obsolete.
"""
current = threading.currentThread()
- for x in self._pool.getstarted():
- if x != current:
- self._trace("joining %s" % x)
- x.join()
- self._trace("joining sender/reciver threads finished, current %r" % current)
- if joinexec:
- self._execpool.join()
- self._trace("joining execution threads finished, current %r" % current)
+ if self._receiverthread.isAlive():
+ self._trace("joining receiver thread")
+ self._receiverthread.join()
def getid(gw, cache={}):
name = gw.__class__.__name__
@@ -284,15 +349,3 @@
cache[name][id(gw)] = x = "%s:%s.%d" %(os.getpid(), gw.__class__.__name__, len(cache[name]))
return x
-registered_cleanup = False
-_active_sendqueues = weakref.WeakKeyDictionary()
-def cleanup_atexit():
- if debug:
- print >>debug, "="*20 + "cleaning up" + "=" * 20
- debug.flush()
- while True:
- try:
- queue, ignored = _active_sendqueues.popitem()
- except KeyError:
- break
- queue.put(None)
Modified: py/branch/bugfix-0.9.0/py/execnet/inputoutput.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/execnet/inputoutput.py (original)
+++ py/branch/bugfix-0.9.0/py/execnet/inputoutput.py Mon Mar 17 20:18:48 2008
@@ -3,7 +3,7 @@
across process or computer barriers.
"""
-import socket, os, sys
+import socket, os, sys, thread
class SocketIO:
server_stmt = """
@@ -43,11 +43,17 @@
def close_read(self):
if self.readable:
- self.sock.shutdown(0)
+ try:
+ self.sock.shutdown(0)
+ except socket.error:
+ pass
self.readable = None
def close_write(self):
if self.writeable:
- self.sock.shutdown(1)
+ try:
+ self.sock.shutdown(1)
+ except socket.error:
+ pass
self.writeable = None
class Popen2IO:
@@ -70,6 +76,7 @@
msvcrt.setmode(outfile.fileno(), os.O_BINARY)
self.outfile, self.infile = infile, outfile
self.readable = self.writeable = True
+ self.lock = thread.allocate_lock()
def read(self, numbytes):
"""Read exactly 'bytes' bytes from the pipe. """
@@ -93,6 +100,10 @@
self.infile.close()
self.readable = None
def close_write(self):
- if self.writeable:
- self.outfile.close()
- self.writeable = None
+ self.lock.acquire()
+ try:
+ if self.writeable:
+ self.outfile.close()
+ self.writeable = None
+ finally:
+ self.lock.release()
Modified: py/branch/bugfix-0.9.0/py/execnet/register.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/execnet/register.py (original)
+++ py/branch/bugfix-0.9.0/py/execnet/register.py Mon Mar 17 20:18:48 2008
@@ -11,7 +11,6 @@
startup_modules = [
'py.__.thread.io',
- 'py.__.thread.pool',
'py.__.execnet.inputoutput',
'py.__.execnet.gateway',
'py.__.execnet.message',
@@ -29,6 +28,8 @@
def __init__(self, io):
self._remote_bootstrap_gateway(io)
super(InstallableGateway, self).__init__(io=io, _startcount=1)
+ # XXX we dissallow execution form the other side
+ self._initreceive(requestqueue=False)
def _remote_bootstrap_gateway(self, io, extra=''):
""" return Gateway with a asynchronously remotely
@@ -41,7 +42,7 @@
bootstrap = [extra]
bootstrap += [getsource(x) for x in startup_modules]
bootstrap += [io.server_stmt,
- "Gateway(io=io, _startcount=2).join(joinexec=False)",
+ "Gateway(io=io, _startcount=2)._servemain()",
]
source = "\n".join(bootstrap)
self._trace("sending gateway bootstrap code")
Modified: py/branch/bugfix-0.9.0/py/execnet/testing/test_gateway.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/execnet/testing/test_gateway.py (original)
+++ py/branch/bugfix-0.9.0/py/execnet/testing/test_gateway.py Mon Mar 17 20:18:48 2008
@@ -83,8 +83,7 @@
class BasicRemoteExecution:
def test_correct_setup(self):
- for x in 'sender', 'receiver':
- assert self.gw._pool.getstarted(x)
+ assert self.gw._receiverthread.isAlive()
def test_repr_doesnt_crash(self):
assert isinstance(repr(self), str)
@@ -373,6 +372,18 @@
res = channel.receive()
assert res == 42
+ def test_non_reverse_execution(self):
+ gw = self.gw
+ c1 = gw.remote_exec("""
+ c = channel.gateway.remote_exec("pass")
+ try:
+ c.waitclose()
+ except c.RemoteError, e:
+ channel.send(str(e))
+ """)
+ text = c1.receive()
+ assert text.find("execution disallowed") != -1
+
#class TestBlockingIssues:
# def test_join_blocked_execution_gateway(self):
# gateway = py.execnet.PopenGateway()
@@ -486,3 +497,23 @@
# now it did
py.test.raises(IOError, gw.remote_exec, "...")
+def test_threads():
+ gw = py.execnet.PopenGateway()
+ gw.remote_init_threads(3)
+ c1 = gw.remote_exec("channel.send(channel.receive())")
+ c2 = gw.remote_exec("channel.send(channel.receive())")
+ c2.send(1)
+ res = c2.receive()
+ assert res == 1
+ c1.send(42)
+ res = c1.receive()
+ assert res == 42
+ gw.exit()
+
+def test_threads_twice():
+ gw = py.execnet.PopenGateway()
+ gw.remote_init_threads(3)
+ py.test.raises(IOError, gw.remote_init_threads, 3)
+ gw.exit()
+
+
Modified: py/branch/bugfix-0.9.0/py/io/fdcapture.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/io/fdcapture.py (original)
+++ py/branch/bugfix-0.9.0/py/io/fdcapture.py Mon Mar 17 20:18:48 2008
@@ -2,6 +2,7 @@
import os
import sys
import py
+import tempfile
class FDCapture:
""" Capture IO to/from a given os-level filedescriptor. """
@@ -41,7 +42,7 @@
def maketmpfile(self):
""" create a temporary file
"""
- f = os.tmpfile()
+ f = tempfile.TemporaryFile()
newf = py.io.dupfile(f)
f.close()
return newf
@@ -49,7 +50,7 @@
def writeorg(self, str):
""" write a string to the original file descriptor
"""
- tempfp = os.tmpfile()
+ tempfp = tempfile.TemporaryFile()
try:
os.dup2(self._savefd, tempfp.fileno())
tempfp.write(str)
Modified: py/branch/bugfix-0.9.0/py/magic/greenlet.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/magic/greenlet.py (original)
+++ py/branch/bugfix-0.9.0/py/magic/greenlet.py Mon Mar 17 20:18:48 2008
@@ -2,8 +2,10 @@
if '_stackless' in sys.builtin_module_names:
# when running on top of a pypy with stackless support
from _stackless import greenlet
+elif hasattr(sys, 'pypy_objspaceclass'):
+ raise ImportError("Detected pypy without stackless support")
else:
- # regular CPython (or pypy without stackless support, and then crash :-)
+ # regular CPython
import py
gdir = py.path.local(py.__file__).dirpath()
path = gdir.join('c-extension', 'greenlet', 'greenlet.c')
Modified: py/branch/bugfix-0.9.0/py/misc/conftest-socketgatewayrun.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/misc/conftest-socketgatewayrun.py (original)
+++ py/branch/bugfix-0.9.0/py/misc/conftest-socketgatewayrun.py Mon Mar 17 20:18:48 2008
@@ -28,8 +28,8 @@
return True
class MySession(RemoteTerminalSession):
- socketserveradr = ('10.9.4.148', 8888)
socketserveradr = ('10.9.2.62', 8888)
+ socketserveradr = ('10.9.4.148', 8888)
def _initslavegateway(self):
print "MASTER: initializing remote socket gateway"
@@ -59,3 +59,5 @@
assert remotepypath.startswith(topdir), (remotepypath, topdir)
#print "remote side has rsynced pythonpath ready: %r" %(topdir,)
return gw, topdir
+
+dist_hosts = ['localhost', 'cobra', 'cobra']
Added: py/branch/bugfix-0.9.0/py/misc/killproc.py
==============================================================================
--- (empty file)
+++ py/branch/bugfix-0.9.0/py/misc/killproc.py Mon Mar 17 20:18:48 2008
@@ -0,0 +1,10 @@
+
+import py
+import os, sys
+
+def killproc(pid):
+ if sys.platform == "win32":
+ py.process.cmdexec("taskkill /F /PID %d" %(pid,))
+ else:
+ os.kill(pid, 15)
+
Added: py/branch/bugfix-0.9.0/py/misc/testing/test_oskill.py
==============================================================================
--- (empty file)
+++ py/branch/bugfix-0.9.0/py/misc/testing/test_oskill.py Mon Mar 17 20:18:48 2008
@@ -0,0 +1,19 @@
+
+import py, sys
+
+from py.__.misc.killproc import killproc
+
+def test_win_killsubprocess():
+ if sys.platform == 'win32' and not py.path.local.sysfind('taskkill'):
+ py.test.skip("you\'re using an older version of windows, which "
+ "doesn\'t support 'taskkill' - py.misc.killproc is not "
+ "available")
+ tmp = py.test.ensuretemp("test_win_killsubprocess")
+ t = tmp.join("t.py")
+ t.write("import time ; time.sleep(100)")
+ proc = py.std.subprocess.Popen([sys.executable, str(t)])
+ assert proc.poll() is None # no return value yet
+ killproc(proc.pid)
+ ret = proc.wait()
+ assert ret != 0
+
Modified: py/branch/bugfix-0.9.0/py/path/local/local.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/path/local/local.py (original)
+++ py/branch/bugfix-0.9.0/py/path/local/local.py Mon Mar 17 20:18:48 2008
@@ -68,9 +68,8 @@
elif isinstance(path, str):
self.strpath = os.path.abspath(os.path.normpath(str(path)))
else:
- raise ValueError(
- "can only pass None, Path instances "
- "or non-empty strings to LocalPath")
+ raise ValueError("can only pass None, Path instances "
+ "or non-empty strings to LocalPath")
assert isinstance(self.strpath, str)
return self
Modified: py/branch/bugfix-0.9.0/py/path/svn/svncommon.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/path/svn/svncommon.py (original)
+++ py/branch/bugfix-0.9.0/py/path/svn/svncommon.py Mon Mar 17 20:18:48 2008
@@ -5,7 +5,7 @@
import py
from py.__.path import common
-ALLOWED_CHARS = "_ -/\\=$.~" #add characters as necessary when tested
+ALLOWED_CHARS = "_ -/\\=$.~+" #add characters as necessary when tested
if sys.platform == "win32":
ALLOWED_CHARS += ":"
ALLOWED_CHARS_HOST = ALLOWED_CHARS + '@:'
@@ -65,6 +65,7 @@
"""
obj = object.__new__(self.__class__)
obj.rev = kw.get('rev', self.rev)
+ obj.auth = kw.get('auth', self.auth)
dirname, basename, purebasename, ext = self._getbyspec(
"dirname,basename,purebasename,ext")
if 'basename' in kw:
@@ -138,7 +139,7 @@
args = tuple([arg.strip(self.sep) for arg in args])
parts = (self.strpath, ) + args
- newpath = self.__class__(self.sep.join(parts), self.rev)
+ newpath = self.__class__(self.sep.join(parts), self.rev, self.auth)
return newpath
def propget(self, name):
@@ -330,3 +331,27 @@
fspath = '%s at HEAD' % (fspath,)
return 'file://%s' % (fspath,)
+class SvnAuth(object):
+ """ container for auth information for Subversion """
+ def __init__(self, username, password, cache_auth=True, interactive=True):
+ self.username = username
+ self.password = password
+ self.cache_auth = cache_auth
+ self.interactive = interactive
+
+ def makecmdoptions(self):
+ uname = self.username.replace('"', '\\"')
+ passwd = self.password.replace('"', '\\"')
+ ret = []
+ if uname:
+ ret.append('--username="%s"' % (uname,))
+ if passwd:
+ ret.append('--password="%s"' % (passwd,))
+ if not self.cache_auth:
+ ret.append('--no-auth-cache')
+ if not self.interactive:
+ ret.append('--non-interactive')
+ return ' '.join(ret)
+
+ def __str__(self):
+ return "<SvnAuth username=%s ...>" %(self.username,)
Added: py/branch/bugfix-0.9.0/py/path/svn/testing/test_auth.py
==============================================================================
--- (empty file)
+++ py/branch/bugfix-0.9.0/py/path/svn/testing/test_auth.py Mon Mar 17 20:18:48 2008
@@ -0,0 +1,479 @@
+import py
+from py.path import SvnAuth
+import svntestbase
+from threading import Thread
+import time
+from py.__.misc.killproc import killproc
+from py.__.conftest import option
+
+def make_repo_auth(repo, userdata):
+ """ write config to repo
+
+ user information in userdata is used for auth
+ userdata has user names as keys, and a tuple (password, readwrite) as
+ values, where 'readwrite' is either 'r' or 'rw'
+ """
+ confdir = py.path.local(repo).join('conf')
+ confdir.join('svnserve.conf').write('''\
+[general]
+anon-access = none
+password-db = passwd
+authz-db = authz
+realm = TestRepo
+''')
+ authzdata = '[/]\n'
+ passwddata = '[users]\n'
+ for user in userdata:
+ authzdata += '%s = %s\n' % (user, userdata[user][1])
+ passwddata += '%s = %s\n' % (user, userdata[user][0])
+ confdir.join('authz').write(authzdata)
+ confdir.join('passwd').write(passwddata)
+
+def serve_bg(repopath):
+ pidfile = py.path.local(repopath).join('pid')
+ port = 10000
+ e = None
+ while port < 10010:
+ cmd = 'svnserve -d -T --listen-port=%d --pid-file=%s -r %s' % (
+ port, pidfile, repopath)
+ try:
+ py.process.cmdexec(cmd)
+ except py.process.cmdexec.Error, e:
+ pass
+ else:
+ # XXX we assume here that the pid file gets written somewhere, I
+ # guess this should be relatively safe... (I hope, at least?)
+ while True:
+ pid = pidfile.read()
+ if pid:
+ break
+ # needs a bit more time to boot
+ time.sleep(0.1)
+ return port, int(pid)
+ port += 1
+ raise IOError('could not start svnserve: %s' % (e,))
+
+class TestSvnAuth(object):
+ def test_basic(self):
+ auth = py.path.SvnAuth('foo', 'bar')
+ assert auth.username == 'foo'
+ assert auth.password == 'bar'
+ assert str(auth)
+
+ def test_makecmdoptions_uname_pw_makestr(self):
+ auth = py.path.SvnAuth('foo', 'bar')
+ assert auth.makecmdoptions() == '--uername="foo" --password="bar"'
+
+ def test_makecmdoptions_quote_escape(self):
+ auth = py.path.SvnAuth('fo"o', '"ba\'r"')
+ assert auth.makecmdoptions() == '--username="fo\\"o" --password="\\"ba\'r\\""'
+
+ def test_makecmdoptions_no_cache_auth(self):
+ auth = py.path.SvnAuth('foo', 'bar', cache_auth=False)
+ assert auth.makecmdoptions() == ('--username="foo" --password="bar" '
+ '--no-auth-cache')
+
+ def test_makecmdoptions_no_interactive(self):
+ auth = py.path.SvnAuth('foo', 'bar', interactive=False)
+ assert auth.makecmdoptions() == ('--username="foo" --password="bar" '
+ '--non-interactive')
+
+ def test_makecmdoptions_no_interactive_no_cache_auth(self):
+ auth = py.path.SvnAuth('foo', 'bar', cache_auth=False,
+ interactive=False)
+ assert auth.makecmdoptions() == ('--username="foo" --password="bar" '
+ '--no-auth-cache --non-interactive')
+
+class svnwc_no_svn(py.path.svnwc):
+ def __init__(self, *args, **kwargs):
+ self.commands = []
+ super(svnwc_no_svn, self).__init__(*args, **kwargs)
+
+ def _svn(self, *args):
+ self.commands.append(args)
+
+class TestSvnWCAuth(object):
+ def setup_method(self, meth):
+ self.auth = SvnAuth('user', 'pass', cache_auth=False)
+
+ def test_checkout(self):
+ wc = svnwc_no_svn('foo', auth=self.auth)
+ wc.checkout('url')
+ assert wc.commands[0][-1] == ('--username="user" --password="pass" '
+ '--no-auth-cache')
+
+ def test_commit(self):
+ wc = svnwc_no_svn('foo', auth=self.auth)
+ wc.commit('msg')
+ assert wc.commands[0][-1] == ('--username="user" --password="pass" '
+ '--no-auth-cache')
+
+ def test_checkout_no_cache_auth(self):
+ wc = svnwc_no_svn('foo', auth=self.auth)
+ wc.checkout('url')
+ assert wc.commands[0][-1] == ('--username="user" --password="pass" '
+ '--no-auth-cache')
+
+ def test_checkout_auth_from_constructor(self):
+ wc = svnwc_no_svn('foo', auth=self.auth)
+ wc.checkout('url')
+ assert wc.commands[0][-1] == ('--username="user" --password="pass" '
+ '--no-auth-cache')
+
+class svnurl_no_svn(py.path.svnurl):
+ cmdexec_output = 'test'
+ popen_output = 'test'
+
+ def _cmdexec(self, cmd):
+ self.commands.append(cmd)
+ return self.cmdexec_output
+
+ def _popen(self, cmd):
+ self.commands.append(cmd)
+ return self.popen_output
+
+class TestSvnURLAuth(object):
+ def setup_method(self, meth):
+ svnurl_no_svn.commands = []
+ self.auth = SvnAuth('foo', 'bar')
+
+ def test_init(self):
+ u = svnurl_no_svn('http://foo.bar/svn')
+ assert u.auth is None
+
+ u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
+ assert u.auth is self.auth
+
+ def test_new(self):
+ u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
+ new = u.new(basename='bar')
+ assert new.auth is self.auth
+ assert new.url == 'http://foo.bar/svn/bar'
+
+ def test_join(self):
+ u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
+ new = u.join('foo')
+ assert new.auth is self.auth
+ assert new.url == 'http://foo.bar/svn/foo'
+
+ def test_listdir(self):
+ u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
+ u.cmdexec_output = '''\
+ 1717 johnny 1529 Nov 04 14:32 LICENSE.txt
+ 1716 johnny 5352 Nov 04 14:28 README.txt
+'''
+ paths = u.listdir()
+ assert paths[0].auth is self.auth
+ assert paths[1].auth is self.auth
+ assert paths[0].basename == 'LICENSE.txt'
+
+ def test_info(self):
+ u = svnurl_no_svn('http://foo.bar/svn/LICENSE.txt', auth=self.auth)
+ def dirpath(self):
+ return self
+ u.cmdexec_output = '''\
+ 1717 johnny 1529 Nov 04 14:32 LICENSE.txt
+ 1716 johnny 5352 Nov 04 14:28 README.txt
+'''
+ org_dp = u.__class__.dirpath
+ u.__class__.dirpath = dirpath
+ try:
+ info = u.info()
+ finally:
+ u.dirpath = org_dp
+ assert info.size == 1529
+
+ def test_open(self):
+ u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
+ foo = u.join('foo')
+ foo.check = lambda *args, **kwargs: True
+ ret = foo.open()
+ assert ret == 'test'
+ assert '--username="foo" --password="bar"' in foo.commands[0]
+
+ def test_dirpath(self):
+ u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
+ parent = u.dirpath()
+ assert parent.auth is self.auth
+
+ def test_mkdir(self):
+ u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
+ u.mkdir('foo', msg='created dir foo')
+ assert '--username="foo" --password="bar"' in u.commands[0]
+
+ def test_copy(self):
+ u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
+ u2 = svnurl_no_svn('http://foo.bar/svn2')
+ u.copy(u2, 'copied dir')
+ assert '--username="foo" --password="bar"' in u.commands[0]
+
+ def test_rename(self):
+ u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
+ u.rename('http://foo.bar/svn/bar', 'moved foo to bar')
+ assert '--username="foo" --password="bar"' in u.commands[0]
+
+ def test_remove(self):
+ u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
+ u.remove(msg='removing foo')
+ assert '--username="foo" --password="bar"' in u.commands[0]
+
+ def test_export(self):
+ u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
+ target = py.path.local('/foo')
+ u.export(target)
+ assert '--username="foo" --password="bar"' in u.commands[0]
+
+ def test_log(self):
+ u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
+ u.popen_output = py.std.StringIO.StringIO('''\
+<?xml version="1.0"?>
+<log>
+<logentry revision="51381">
+<author>guido</author>
+<date>2008-02-11T12:12:18.476481Z</date>
+<msg>Creating branch to work on auth support for py.path.svn*.
+</msg>
+</logentry>
+</log>
+''')
+ u.check = lambda *args, **kwargs: True
+ ret = u.log(10, 20, verbose=True)
+ assert '--username="foo" --password="bar"' in u.commands[0]
+ assert len(ret) == 1
+ assert int(ret[0].rev) == 51381
+ assert ret[0].author == 'guido'
+
+ def test_propget(self):
+ u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
+ u.propget('foo')
+ assert '--username="foo" --password="bar"' in u.commands[0]
+
+class SvnAuthFunctionalTestBase(object):
+ def setup_class(cls):
+ if not option.runslowtests:
+ py.test.skip('skipping slow functional tests - use --runslowtests '
+ 'to override')
+
+ def setup_method(self, meth):
+ func_name = meth.im_func.func_name
+ self.repo = svntestbase.make_test_repo('TestSvnAuthFunctional.%s' % (
+ func_name,))
+ repodir = str(self.repo)[7:]
+ if py.std.sys.platform == 'win32':
+ # remove trailing slash...
+ repodir = repodir[1:]
+ self.repopath = py.path.local(repodir)
+ self.temppath = py.test.ensuretemp('TestSvnAuthFunctional.%s' % (
+ func_name))
+ self.auth = py.path.SvnAuth('johnny', 'foo', cache_auth=False,
+ interactive=False)
+
+ def _start_svnserve(self):
+ make_repo_auth(self.repopath, {'johnny': ('foo', 'rw')})
+ try:
+ return serve_bg(self.repopath.dirpath())
+ except IOError, e:
+ py.test.skip(str(e))
+
+class TestSvnWCAuthFunctional(SvnAuthFunctionalTestBase):
+ def test_checkout_constructor_arg(self):
+ port, pid = self._start_svnserve()
+ try:
+ wc = py.path.svnwc(self.temppath, auth=self.auth)
+ wc.checkout(
+ 'svn://localhost:%s/%s' % (port, self.repopath.basename))
+ assert wc.join('.svn').check()
+ finally:
+ # XXX can we do this in a teardown_method too? not sure if that's
+ # guaranteed to get called...
+ killproc(pid)
+
+ def test_checkout_function_arg(self):
+ port, pid = self._start_svnserve()
+ try:
+ wc = py.path.svnwc(self.temppath, auth=self.auth)
+ wc.checkout(
+ 'svn://localhost:%s/%s' % (port, self.repopath.basename))
+ assert wc.join('.svn').check()
+ finally:
+ killproc(pid)
+
+ def test_checkout_failing_non_interactive(self):
+ port, pid = self._start_svnserve()
+ try:
+ auth = py.path.SvnAuth('johnny', 'bar', cache_auth=False,
+ interactive=False)
+ wc = py.path.svnwc(self.temppath, auth)
+ py.test.raises(Exception,
+ ("wc.checkout('svn://localhost:%s/%s' % "
+ "(port, self.repopath.basename))"))
+ finally:
+ killproc(pid)
+
+ def test_log(self):
+ port, pid = self._start_svnserve()
+ try:
+ wc = py.path.svnwc(self.temppath, self.auth)
+ wc.checkout(
+ 'svn://localhost:%s/%s' % (port, self.repopath.basename))
+ foo = wc.ensure('foo.txt')
+ wc.commit('added foo.txt')
+ log = foo.log()
+ assert len(log) == 1
+ assert log[0].msg == 'added foo.txt'
+ finally:
+ killproc(pid)
+
+ def test_switch(self):
+ port, pid = self._start_svnserve()
+ try:
+ wc = py.path.svnwc(self.temppath, auth=self.auth)
+ svnurl = 'svn://localhost:%s/%s' % (port, self.repopath.basename)
+ wc.checkout(svnurl)
+ wc.ensure('foo', dir=True).ensure('foo.txt').write('foo')
+ wc.commit('added foo dir with foo.txt file')
+ wc.ensure('bar', dir=True)
+ wc.commit('added bar dir')
+ bar = wc.join('bar')
+ bar.switch(svnurl + '/foo')
+ assert bar.join('foo.txt')
+ finally:
+ killproc(pid)
+
+ def test_update(self):
+ port, pid = self._start_svnserve()
+ try:
+ wc1 = py.path.svnwc(self.temppath.ensure('wc1', dir=True),
+ auth=self.auth)
+ wc2 = py.path.svnwc(self.temppath.ensure('wc2', dir=True),
+ auth=self.auth)
+ wc1.checkout(
+ 'svn://localhost:%s/%s' % (port, self.repopath.basename))
+ wc2.checkout(
+ 'svn://localhost:%s/%s' % (port, self.repopath.basename))
+ wc1.ensure('foo', dir=True)
+ wc1.commit('added foo dir')
+ wc2.update()
+ assert wc2.join('foo').check()
+
+ auth = py.path.SvnAuth('unknown', 'unknown', interactive=False)
+ wc2.auth = auth
+ py.test.raises(Exception, 'wc2.update()')
+ finally:
+ killproc(pid)
+
+ def test_lock_unlock_status(self):
+ port, pid = self._start_svnserve()
+ try:
+ wc = py.path.svnwc(self.temppath, auth=self.auth)
+ wc.checkout(
+ 'svn://localhost:%s/%s' % (port, self.repopath.basename,))
+ wc.ensure('foo', file=True)
+ wc.commit('added foo file')
+ foo = wc.join('foo')
+ foo.lock()
+ status = foo.status()
+ assert status.locked
+ foo.unlock()
+ status = foo.status()
+ assert not status.locked
+
+ auth = py.path.SvnAuth('unknown', 'unknown', interactive=False)
+ foo.auth = auth
+ py.test.raises(Exception, 'foo.lock()')
+ py.test.raises(Exception, 'foo.unlock()')
+ finally:
+ killproc(pid)
+
+ def test_diff(self):
+ port, pid = self._start_svnserve()
+ try:
+ wc = py.path.svnwc(self.temppath, auth=self.auth)
+ wc.checkout(
+ 'svn://localhost:%s/%s' % (port, self.repopath.basename,))
+ wc.ensure('foo', file=True)
+ wc.commit('added foo file')
+ wc.update()
+ rev = int(wc.status().rev)
+ foo = wc.join('foo')
+ foo.write('bar')
+ diff = foo.diff()
+ assert '\n+bar\n' in diff
+ foo.commit('added some content')
+ diff = foo.diff()
+ assert not diff
+ diff = foo.diff(rev=rev)
+ assert '\n+bar\n' in diff
+
+ auth = py.path.SvnAuth('unknown', 'unknown', interactive=False)
+ foo.auth = auth
+ py.test.raises(Exception, 'foo.diff(rev=rev)')
+ finally:
+ killproc(pid)
+
+class TestSvnURLAuthFunctional(SvnAuthFunctionalTestBase):
+ def test_listdir(self):
+ port, pid = self._start_svnserve()
+ try:
+ u = py.path.svnurl(
+ 'svn://localhost:%s/%s' % (port, self.repopath.basename),
+ auth=self.auth)
+ u.ensure('foo')
+ paths = u.listdir()
+ assert len(paths) == 1
+ assert paths[0].auth is self.auth
+
+ auth = SvnAuth('foo', 'bar', interactive=False)
+ u = py.path.svnurl(
+ 'svn://localhost:%s/%s' % (port, self.repopath.basename),
+ auth=auth)
+ py.test.raises(Exception, 'u.listdir()')
+ finally:
+ killproc(pid)
+
+ def test_copy(self):
+ port, pid = self._start_svnserve()
+ try:
+ u = py.path.svnurl(
+ 'svn://localhost:%s/%s' % (port, self.repopath.basename),
+ auth=self.auth)
+ foo = u.ensure('foo')
+ bar = u.join('bar')
+ foo.copy(bar)
+ assert bar.check()
+ assert bar.auth is self.auth
+
+ auth = SvnAuth('foo', 'bar', interactive=False)
+ u = py.path.svnurl(
+ 'svn://localhost:%s/%s' % (port, self.repopath.basename),
+ auth=auth)
+ foo = u.join('foo')
+ bar = u.join('bar')
+ py.test.raises(Exception, 'foo.copy(bar)')
+ finally:
+ killproc(pid)
+
+ def test_write_read(self):
+ port, pid = self._start_svnserve()
+ try:
+ u = py.path.svnurl(
+ 'svn://localhost:%s/%s' % (port, self.repopath.basename),
+ auth=self.auth)
+ foo = u.ensure('foo')
+ fp = foo.open()
+ try:
+ data = fp.read()
+ finally:
+ fp.close()
+ assert data == ''
+
+ auth = SvnAuth('foo', 'bar', interactive=False)
+ u = py.path.svnurl(
+ 'svn://localhost:%s/%s' % (port, self.repopath.basename),
+ auth=auth)
+ foo = u.join('foo')
+ py.test.raises(Exception, 'foo.open()')
+ finally:
+ killproc(pid)
+
+ # XXX rinse, repeat... :|
Modified: py/branch/bugfix-0.9.0/py/path/svn/testing/test_urlcommand.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/path/svn/testing/test_urlcommand.py (original)
+++ py/branch/bugfix-0.9.0/py/path/svn/testing/test_urlcommand.py Mon Mar 17 20:18:48 2008
@@ -59,6 +59,44 @@
py.test.skip('XXX fixme win32')
py.test.raises(ValueError, 'py.path.svnurl("http://host.com/foo:bar")')
+ def test_export(self):
+ repo, wc = getrepowc('test_export_repo', 'test_export_wc')
+ foo = wc.join('foo').ensure(dir=True)
+ bar = foo.join('bar').ensure(file=True)
+ bar.write('bar\n')
+ foo.commit('testing something')
+ exportpath = py.test.ensuretemp('test_export_exportdir')
+ url = py.path.svnurl(repo + '/foo')
+ foo = url.export(exportpath.join('foo'))
+ assert foo == exportpath.join('foo')
+ assert isinstance(foo, py.path.local)
+ assert foo.join('bar').check()
+ assert not foo.join('.svn').check()
+
+ def test_export_rev(self):
+ repo, wc = getrepowc('test_export_rev_repo', 'test_export_rev_wc')
+ foo = wc.join('foo').ensure(dir=True)
+ bar = foo.join('bar').ensure(file=True)
+ bar.write('bar\n')
+ rev1 = foo.commit('testing something')
+ print 'rev1:', rev1
+ baz = foo.join('baz').ensure(file=True)
+ baz.write('baz\n')
+ rev2 = foo.commit('testing more')
+
+ exportpath = py.test.ensuretemp('test_export_rev_exportdir')
+ url = py.path.svnurl(repo + '/foo', rev=rev1)
+ foo1 = url.export(exportpath.join('foo1'))
+ assert foo1.check()
+ assert foo1.join('bar').check()
+ assert not foo1.join('baz').check()
+
+ url = py.path.svnurl(repo + '/foo', rev=rev2)
+ foo2 = url.export(exportpath.join('foo2'))
+ assert foo2.check()
+ assert foo2.join('bar').check()
+ assert foo2.join('baz').check()
+
class TestSvnInfoCommand:
def test_svn_1_2(self):
Modified: py/branch/bugfix-0.9.0/py/path/svn/testing/test_wccommand.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/path/svn/testing/test_wccommand.py (original)
+++ py/branch/bugfix-0.9.0/py/path/svn/testing/test_wccommand.py Mon Mar 17 20:18:48 2008
@@ -1,13 +1,27 @@
import py
+import sys
from py.__.path.svn.testing.svntestbase import CommonSvnTests, getrepowc
from py.__.path.svn.wccommand import InfoSvnWCCommand
from py.__.path.svn.wccommand import parse_wcinfotime
from py.__.path.svn import svncommon
-
if py.path.local.sysfind('svn') is None:
py.test.skip("cannot test py.path.svn, 'svn' binary not found")
+if sys.platform != 'win32':
+ def normpath(p):
+ return p
+else:
+ try:
+ import win32api
+ except ImportError:
+ def normpath(p):
+ py.test.skip('this test requires win32api to run on windows')
+ else:
+ import os
+ def normpath(p):
+ p = win32api.GetShortPathName(p)
+ return os.path.normpath(os.path.normcase(p))
class TestWCSvnCommandPath(CommonSvnTests):
@@ -156,6 +170,15 @@
finally:
notexisting.remove()
+ def test_nonversioned_remove(self):
+ assert self.root.check(versioned=1)
+ somefile = self.root.join('nonversioned/somefile')
+ nonwc = py.path.local(somefile)
+ nonwc.ensure()
+ assert somefile.check()
+ assert not somefile.check(versioned=True)
+ somefile.remove() # this used to fail because it tried to 'svn rm'
+
def test_properties(self):
try:
self.root.propset('gaga', 'this')
@@ -217,6 +240,63 @@
p.remove(rec=1)
f.remove()
+ def test_lock_unlock(self):
+ root = self.root
+ somefile = root.join('somefile')
+ somefile.ensure(file=True)
+ # not yet added to repo
+ py.test.raises(py.process.cmdexec.Error, 'somefile.lock()')
+ somefile.write('foo')
+ somefile.commit('test')
+ assert somefile.check(versioned=True)
+ somefile.lock()
+ try:
+ locked = root.status().locked
+ assert len(locked) == 1
+ assert normpath(str(locked[0])) == normpath(str(somefile))
+ #assert somefile.locked()
+ py.test.raises(Exception, 'somefile.lock()')
+ finally:
+ somefile.unlock()
+ #assert not somefile.locked()
+ locked = root.status().locked
+ assert locked == []
+ py.test.raises(Exception, 'somefile,unlock()')
+ somefile.remove()
+
+ def test_commit_nonrecursive(self):
+ root = self.root
+ somedir = root.join('sampledir')
+ somefile = somedir.join('otherfile')
+ somefile.write('foo')
+ somedir.propset('foo', 'bar')
+ status = somedir.status()
+ assert len(status.prop_modified) == 1
+ assert len(status.modified) == 1
+
+ somedir.commit('non-recursive commit', rec=0)
+ status = somedir.status()
+ assert len(status.prop_modified) == 0
+ assert len(status.modified) == 1
+
+ somedir.commit('recursive commit')
+ status = somedir.status()
+ assert len(status.prop_modified) == 0
+ assert len(status.modified) == 0
+
+ def test_commit_return_value(self):
+ root = self.root
+ testfile = root.join('test.txt').ensure(file=True)
+ testfile.write('test')
+ rev = root.commit('testing')
+ assert type(rev) == int
+
+ anotherfile = root.join('another.txt').ensure(file=True)
+ anotherfile.write('test')
+ rev2 = root.commit('testing more')
+ assert type(rev2) == int
+ assert rev2 == rev + 1
+
#def test_log(self):
# l = self.root.log()
# assert len(l) == 3 # might need to be upped if more tests are added
Modified: py/branch/bugfix-0.9.0/py/path/svn/urlcommand.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/path/svn/urlcommand.py (original)
+++ py/branch/bugfix-0.9.0/py/path/svn/urlcommand.py Mon Mar 17 20:18:48 2008
@@ -21,10 +21,11 @@
_lsrevcache = BuildcostAccessCache(maxentries=128)
_lsnorevcache = AgingCache(maxentries=1000, maxseconds=60.0)
- def __new__(cls, path, rev=None):
+ def __new__(cls, path, rev=None, auth=None):
self = object.__new__(cls)
if isinstance(path, cls):
rev = path.rev
+ auth = path.auth
path = path.strpath
proto, uri = path.split("://", 1)
host, uripath = uri.split('/', 1)
@@ -36,6 +37,7 @@
path = path.rstrip('/')
self.strpath = path
self.rev = rev
+ self.auth = auth
return self
def __repr__(self):
@@ -44,7 +46,8 @@
else:
return 'svnurl(%r, %r)' % (self.strpath, self.rev)
- def _svn(self, cmd, *args):
+ def _svnwithrev(self, cmd, *args):
+ """ execute an svn command, append our own url and revision """
if self.rev is None:
return self._svnwrite(cmd, *args)
else:
@@ -52,16 +55,28 @@
return self._svnwrite(cmd, *args)
def _svnwrite(self, cmd, *args):
+ """ execute an svn command, append our own url """
l = ['svn %s' % cmd]
args = ['"%s"' % self._escape(item) for item in args]
l.extend(args)
l.append('"%s"' % self._encodedurl())
# fixing the locale because we can't otherwise parse
- string = svncommon.fixlocale() + " ".join(l)
+ string = " ".join(l)
if DEBUG:
print "execing", string
+ out = self._svncmdexecauth(string)
+ return out
+
+ def _svncmdexecauth(self, cmd):
+ """ execute an svn command 'as is' """
+ cmd = svncommon.fixlocale() + cmd
+ if self.auth is not None:
+ cmd += ' ' + self.auth.makecmdoptions()
+ return self._cmdexec(cmd)
+
+ def _cmdexec(self, cmd):
try:
- out = process.cmdexec(string)
+ out = process.cmdexec(cmd)
except py.process.cmdexec.Error, e:
if (e.err.find('File Exists') != -1 or
e.err.find('File already exists') != -1):
@@ -69,21 +84,33 @@
raise
return out
+ def _svnpopenauth(self, cmd):
+ """ execute an svn command, return a pipe for reading stdin """
+ cmd = svncommon.fixlocale() + cmd
+ if self.auth is not None:
+ cmd += ' ' + self.auth.makecmdoptions()
+ return self._popen(cmd)
+
+ def _popen(self, cmd):
+ return os.popen(cmd)
+
def _encodedurl(self):
return self._escape(self.strpath)
+ def _norev_delentry(self, path):
+ auth = self.auth and self.auth.makecmdoptions() or None
+ self._lsnorevcache.delentry((str(path), auth))
+
def open(self, mode='r'):
""" return an opened file with the given mode. """
assert 'w' not in mode and 'a' not in mode, "XXX not implemented for svn cmdline"
assert self.check(file=1) # svn cat returns an empty file otherwise
- def popen(cmd):
- return os.popen(cmd)
if self.rev is None:
- return popen(svncommon.fixlocale() +
- 'svn cat "%s"' % (self._escape(self.strpath), ))
+ return self._svnpopenauth('svn cat "%s"' % (
+ self._escape(self.strpath), ))
else:
- return popen(svncommon.fixlocale() +
- 'svn cat -r %s "%s"' % (self.rev, self._escape(self.strpath)))
+ return self._svnpopenauth('svn cat -r %s "%s"' % (
+ self.rev, self._escape(self.strpath)))
def dirpath(self, *args, **kwargs):
""" return the directory path of the current path joined
@@ -104,33 +131,47 @@
commit_msg=kwargs.get('msg', "mkdir by py lib invocation")
createpath = self.join(*args)
createpath._svnwrite('mkdir', '-m', commit_msg)
- self._lsnorevcache.delentry(createpath.dirpath().strpath)
+ self._norev_delentry(createpath.dirpath())
return createpath
def copy(self, target, msg='copied by py lib invocation'):
""" copy path to target with checkin message msg."""
if getattr(target, 'rev', None) is not None:
raise py.error.EINVAL(target, "revisions are immutable")
- process.cmdexec('svn copy -m "%s" "%s" "%s"' %(msg,
- self._escape(self), self._escape(target)))
- self._lsnorevcache.delentry(target.dirpath().strpath)
+ self._svncmdexecauth('svn copy -m "%s" "%s" "%s"' %(msg,
+ self._escape(self), self._escape(target)))
+ self._norev_delentry(target.dirpath())
def rename(self, target, msg="renamed by py lib invocation"):
""" rename this path to target with checkin message msg. """
if getattr(self, 'rev', None) is not None:
raise py.error.EINVAL(self, "revisions are immutable")
- py.process.cmdexec('svn move -m "%s" --force "%s" "%s"' %(
- msg, self._escape(self), self._escape(target)))
- self._lsnorevcache.delentry(self.dirpath().strpath)
- self._lsnorevcache.delentry(self.strpath)
+ self._svncmdexecauth('svn move -m "%s" --force "%s" "%s"' %(
+ msg, self._escape(self), self._escape(target)))
+ self._norev_delentry(self.dirpath())
+ self._norev_delentry(self)
def remove(self, rec=1, msg='removed by py lib invocation'):
""" remove a file or directory (or a directory tree if rec=1) with
checkin message msg."""
if self.rev is not None:
raise py.error.EINVAL(self, "revisions are immutable")
- process.cmdexec('svn rm -m "%s" "%s"' %(msg, self._escape(self)))
- self._lsnorevcache.delentry(self.dirpath().strpath)
+ self._svncmdexecauth('svn rm -m "%s" "%s"' %(msg, self._escape(self)))
+ self._norev_delentry(self.dirpath())
+
+ def export(self, topath):
+ """ export to a local path
+
+ topath should not exist prior to calling this, returns a
+ py.path.local instance
+ """
+ topath = py.path.local(topath)
+ args = ['"%s"' % (self._escape(self),),
+ '"%s"' % (self._escape(topath),)]
+ if self.rev is not None:
+ args = ['-r', str(self.rev)] + args
+ self._svncmdexecauth('svn export %s' % (' '.join(args),))
+ return topath
def ensure(self, *args, **kwargs):
""" ensure that an args-joined path exists (by default as
@@ -159,19 +200,19 @@
"ensure %s" % self._escape(tocreate),
self._escape(tempdir.join(basename)),
x.join(basename)._encodedurl())
- process.cmdexec(cmd)
- self._lsnorevcache.delentry(x.strpath) # !!!
+ self._svncmdexecauth(cmd)
+ self._norev_delentry(x)
finally:
tempdir.remove()
return target
# end of modifying methods
def _propget(self, name):
- res = self._svn('propget', name)
+ res = self._svnwithrev('propget', name)
return res[:-1] # strip trailing newline
def _proplist(self):
- res = self._svn('proplist')
+ res = self._svnwithrev('proplist')
lines = res.split('\n')
lines = map(str.strip, lines[1:])
return svncommon.PropListDict(self, lines)
@@ -180,7 +221,7 @@
""" return sequence of name-info directory entries of self """
def builder():
try:
- res = self._svn('ls', '-v')
+ res = self._svnwithrev('ls', '-v')
except process.cmdexec.Error, e:
if e.err.find('non-existent in that revision') != -1:
raise py.error.ENOENT(self, e.err)
@@ -200,10 +241,13 @@
info = InfoSvnCommand(lsline)
nameinfo_seq.append((info._name, info))
return nameinfo_seq
+ auth = self.auth and self.auth.makecmdoptions() or None
if self.rev is not None:
- return self._lsrevcache.getorbuild((self.strpath, self.rev), builder)
+ return self._lsrevcache.getorbuild((self.strpath, self.rev, auth),
+ builder)
else:
- return self._lsnorevcache.getorbuild(self.strpath, builder)
+ return self._lsnorevcache.getorbuild((self.strpath, auth),
+ builder)
def log(self, rev_start=None, rev_end=1, verbose=False):
""" return a list of LogEntry instances for this path.
@@ -220,9 +264,8 @@
else:
rev_opt = "-r %s:%s" % (rev_start, rev_end)
verbose_opt = verbose and "-v" or ""
- xmlpipe = os.popen(svncommon.fixlocale() +
- 'svn log --xml %s %s "%s"' %
- (rev_opt, verbose_opt, self.strpath))
+ xmlpipe = self._svnpopenauth('svn log --xml %s %s "%s"' %
+ (rev_opt, verbose_opt, self.strpath))
from xml.dom import minidom
tree = minidom.parse(xmlpipe)
result = []
@@ -240,7 +283,7 @@
# the '0?' part in the middle is an indication of whether the resource is
# locked, see 'svn help ls'
lspattern = re.compile(
- r'^ *(?P<rev>\d+) +(?P<author>\S+) +(0? *(?P<size>\d+))? '
+ r'^ *(?P<rev>\d+) +(?P<author>.+?) +(0? *(?P<size>\d+))? '
'*(?P<date>\w+ +\d{2} +[\d:]+) +(?P<file>.*)$')
def __init__(self, line):
# this is a typical line from 'svn ls http://...'
Modified: py/branch/bugfix-0.9.0/py/path/svn/wccommand.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/path/svn/wccommand.py (original)
+++ py/branch/bugfix-0.9.0/py/path/svn/wccommand.py Mon Mar 17 20:18:48 2008
@@ -25,7 +25,7 @@
"""
sep = os.sep
- def __new__(cls, wcpath=None):
+ def __new__(cls, wcpath=None, auth=None):
self = object.__new__(cls)
if isinstance(wcpath, cls):
if wcpath.__class__ == cls:
@@ -35,6 +35,7 @@
svncommon.ALLOWED_CHARS):
raise ValueError("bad char in wcpath %s" % (wcpath, ))
self.localpath = py.path.local(wcpath)
+ self.auth = auth
return self
strpath = property(lambda x: str(x.localpath), None, None, "string path")
@@ -63,13 +64,22 @@
info = self.info()
return py.path.svnurl(info.url)
-
def __repr__(self):
return "svnwc(%r)" % (self.strpath) # , self._url)
def __str__(self):
return str(self.localpath)
+ def _makeauthoptions(self):
+ if self.auth is None:
+ return ''
+ return self.auth.makecmdoptions()
+
+ def _authsvn(self, cmd, args=None):
+ args = args and list(args) or []
+ args.append(self._makeauthoptions())
+ return self._svn(cmd, *args)
+
def _svn(self, cmd, *args):
l = ['svn %s' % cmd]
args = [self._escape(item) for item in args]
@@ -101,9 +111,9 @@
raise
return out
- def switch(self, url):
+ def switch(self, url):
""" switch to given URL. """
- self._svn('switch', url)
+ self._authsvn('switch', [url])
def checkout(self, url=None, rev=None):
""" checkout from url to local wcpath. """
@@ -118,12 +128,13 @@
if svncommon._getsvnversion() == '1.3':
url += "@%d" % rev
else:
- args.append('-r', str(rev))
- self._svn('co', url, *args)
+ args.append('-r' + str(rev))
+ args.append(url)
+ self._authsvn('co', args)
def update(self, rev = 'HEAD'):
""" update working copy item to given revision. (None -> HEAD). """
- self._svn('up -r %s' % rev)
+ self._authsvn('up', ['-r', rev])
def write(self, content, mode='wb'):
""" write content into local filesystem wc. """
@@ -131,7 +142,7 @@
def dirpath(self, *args):
""" return the directory Path of the current Path. """
- return self.__class__(self.localpath.dirpath(*args))
+ return self.__class__(self.localpath.dirpath(*args), auth=self.auth)
def _ensuredirs(self):
parent = self.dirpath()
@@ -180,6 +191,10 @@
underlying svn semantics.
"""
assert rec, "svn cannot remove non-recursively"
+ if not self.check(versioned=True):
+ # not added to svn (anymore?), just remove
+ py.path.local(self).remove()
+ return
flags = []
if force:
flags.append('--force')
@@ -193,7 +208,32 @@
""" rename this path to target. """
py.process.cmdexec("svn move --force %s %s" %(str(self), str(target)))
- _rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(\S+)\s+(.*)')
+ # XXX a bit scary to assume there's always 2 spaces between username and
+ # path, however with win32 allowing spaces in user names there doesn't
+ # seem to be a more solid approach :(
+ _rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(.+?)\s{2,}(.*)')
+
+ def lock(self):
+ """ set a lock (exclusive) on the resource """
+ out = self._authsvn('lock').strip()
+ if not out:
+ # warning or error, raise exception
+ raise Exception(out[4:])
+
+ def unlock(self):
+ """ unset a previously set lock """
+ out = self._authsvn('unlock').strip()
+ if out.startswith('svn:'):
+ # warning or error, raise exception
+ raise Exception(out[4:])
+
+ def cleanup(self):
+ """ remove any locks from the resource """
+ # XXX should be fixed properly!!!
+ try:
+ self.unlock()
+ except:
+ pass
def status(self, updates=0, rec=0, externals=0):
""" return (collective) Status object for this file. """
@@ -222,7 +262,8 @@
update_rev = None
- out = self._svn('status -v %s %s %s' % (updates, rec, externals))
+ cmd = 'status -v %s %s %s' % (updates, rec, externals)
+ out = self._authsvn(cmd)
rootstatus = WCStatus(self)
for line in out.split('\n'):
if not line.strip():
@@ -230,7 +271,7 @@
#print "processing %r" % line
flags, rest = line[:8], line[8:]
# first column
- c0,c1,c2,c3,c4,x5,x6,c7 = flags
+ c0,c1,c2,c3,c4,c5,x6,c7 = flags
#if '*' in line:
# print "flags", repr(flags), "rest", repr(rest)
@@ -240,7 +281,8 @@
wcpath = self.join(fn, abs=1)
rootstatus.unknown.append(wcpath)
elif c0 == 'X':
- wcpath = self.__class__(self.localpath.join(fn, abs=1))
+ wcpath = self.__class__(self.localpath.join(fn, abs=1),
+ auth=self.auth)
rootstatus.external.append(wcpath)
elif c0 == 'I':
wcpath = self.join(fn, abs=1)
@@ -287,7 +329,8 @@
if c1 == 'M':
rootstatus.prop_modified.append(wcpath)
- if c2 == 'L':
+ # XXX do we cover all client versions here?
+ if c2 == 'L' or c5 == 'K':
rootstatus.locked.append(wcpath)
if c7 == '*':
rootstatus.update_available.append(wcpath)
@@ -305,10 +348,10 @@
""" return a diff of the current path against revision rev (defaulting
to the last one).
"""
- if rev is None:
- out = self._svn('diff')
- else:
- out = self._svn('diff -r %d' % rev)
+ args = []
+ if rev is not None:
+ args.append("-r %d" % rev)
+ out = self._authsvn('diff', args)
return out
def blame(self):
@@ -329,11 +372,14 @@
return result
_rex_commit = re.compile(r'.*Committed revision (\d+)\.$', re.DOTALL)
- def commit(self, message=""):
- """commit() returns None if there was nothing to commit
- and the revision number of the commit otherwise.
- """
- out = self._svn('commit -m "%s"' % message)
+ def commit(self, msg='', rec=1):
+ """ commit with support for non-recursive commits """
+ from py.__.path.svn import cache
+ # XXX i guess escaping should be done better here?!?
+ cmd = 'commit -m "%s" --force-log' % (msg.replace('"', '\\"'),)
+ if not rec:
+ cmd += ' -N'
+ out = self._authsvn(cmd)
try:
del cache.info[self]
except KeyError:
@@ -399,7 +445,7 @@
localpath = self.localpath.new(**kw)
else:
localpath = self.localpath
- return self.__class__(localpath)
+ return self.__class__(localpath, auth=self.auth)
def join(self, *args, **kwargs):
""" return a new Path (with the same revision) which is composed
@@ -408,7 +454,7 @@
if not args:
return self
localpath = self.localpath.join(*args, **kwargs)
- return self.__class__(localpath)
+ return self.__class__(localpath, auth=self.auth)
def info(self, usecache=1):
""" return an Info structure with svn-provided information. """
@@ -451,7 +497,7 @@
paths = []
for localpath in self.localpath.listdir(notsvn):
- p = self.__class__(localpath)
+ p = self.__class__(localpath, auth=self.auth)
paths.append(p)
if fil or sort:
@@ -502,11 +548,13 @@
else:
rev_opt = "-r %s:%s" % (rev_start, rev_end)
verbose_opt = verbose and "-v" or ""
- s = svncommon.fixlocale()
+ locale_env = svncommon.fixlocale()
# some blather on stderr
- stdin, stdout, stderr = os.popen3(s + 'svn log --xml %s %s "%s"' % (
- rev_opt, verbose_opt,
- self.strpath))
+ auth_opt = self._makeauthoptions()
+ stdin, stdout, stderr = os.popen3(locale_env +
+ 'svn log --xml %s %s %s "%s"' % (
+ rev_opt, verbose_opt, auth_opt,
+ self.strpath))
from xml.dom import minidom
from xml.parsers.expat import ExpatError
try:
@@ -530,13 +578,13 @@
return self.info().mtime
def __hash__(self):
- return hash((self.strpath, self.__class__))
+ return hash((self.strpath, self.__class__, self.auth))
class WCStatus:
attrnames = ('modified','added', 'conflict', 'unchanged', 'external',
'deleted', 'prop_modified', 'unknown', 'update_available',
- 'incomplete', 'kindmismatch', 'ignored'
+ 'incomplete', 'kindmismatch', 'ignored', 'locked'
)
def __init__(self, wcpath, rev=None, modrev=None, author=None):
Modified: py/branch/bugfix-0.9.0/py/test/collect.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/test/collect.py (original)
+++ py/branch/bugfix-0.9.0/py/test/collect.py Mon Mar 17 20:18:48 2008
@@ -46,9 +46,6 @@
self.name = name
self.parent = parent
self._config = getattr(parent, '_config', py.test.config)
- if parent is not None:
- if hasattr(parent, 'config'):
- py.test.pdb()
self.fspath = getattr(parent, 'fspath', None)
Module = configproperty('Module')
@@ -289,7 +286,7 @@
name2items[name] = res
return res
-class PyCollectorMixin(object):
+class PyCollectorMixin(Collector):
def funcnamefilter(self, name):
return name.startswith('test')
def classnamefilter(self, name):
@@ -333,7 +330,7 @@
raise
except:
self._name2items_exception = py.std.sys.exc_info()
- raise
+ raise
def run(self):
self._prepare()
@@ -350,7 +347,7 @@
def run(self):
if getattr(self.obj, 'disabled', 0):
return []
- return PyCollectorMixin.run(self)
+ return super(Module, self).run()
def join(self, name):
res = super(Module, self).join(name)
@@ -442,7 +439,40 @@
Collector.Function.__get__(self)) # XXX for python 2.2
Function = property(Function)
-class Generator(PyCollectorMixin, Collector):
+
+class FunctionMixin(object):
+ """ mixin for the code common to Function and Generator.
+ """
+ def _getpathlineno(self):
+ code = py.code.Code(self.obj)
+ return code.path, code.firstlineno
+
+ def _getsortvalue(self):
+ return self._getpathlineno()
+
+ def setup(self):
+ """ perform setup for this test function. """
+ if getattr(self.obj, 'im_self', None):
+ name = 'setup_method'
+ else:
+ name = 'setup_function'
+ obj = self.parent.obj
+ meth = getattr(obj, name, None)
+ if meth is not None:
+ return meth(self.obj)
+
+ def teardown(self):
+ """ perform teardown for this test function. """
+ if getattr(self.obj, 'im_self', None):
+ name = 'teardown_method'
+ else:
+ name = 'teardown_function'
+ obj = self.parent.obj
+ meth = getattr(obj, name, None)
+ if meth is not None:
+ return meth(self.obj)
+
+class Generator(FunctionMixin, PyCollectorMixin, Collector):
def run(self):
self._prepare()
itemlist = self._name2items
@@ -468,13 +498,6 @@
call, args = obj, ()
return call, args
- def _getpathlineno(self):
- code = py.code.Code(self.obj)
- return code.path, code.firstlineno
-
- def _getsortvalue(self):
- return self._getpathlineno()
-
class DoctestFile(PyCollectorMixin, FSCollector):
def run(self):
return [self.fspath.basename]
Modified: py/branch/bugfix-0.9.0/py/test/item.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/test/item.py (original)
+++ py/branch/bugfix-0.9.0/py/test/item.py Mon Mar 17 20:18:48 2008
@@ -2,6 +2,7 @@
from inspect import isclass, ismodule
from py.__.test.outcome import Skipped, Failed, Passed
+from py.__.test.collect import FunctionMixin
_dummy = object()
@@ -37,7 +38,7 @@
def finishcapture(self):
self._config._finishcapture(self)
-class Function(Item):
+class Function(FunctionMixin, Item):
""" a Function Item is responsible for setting up
and executing a Python callable test object.
"""
@@ -52,10 +53,6 @@
def __repr__(self):
return "<%s %r>" %(self.__class__.__name__, self.name)
- def _getpathlineno(self):
- code = py.code.Code(self.obj)
- return code.path, code.firstlineno
-
def _getsortvalue(self):
if self._sort_value is None:
return self._getpathlineno()
@@ -70,32 +67,28 @@
""" execute the given test function. """
target(*args)
- def setup(self):
- """ perform setup for this test function. """
- if getattr(self.obj, 'im_self', None):
- name = 'setup_method'
- else:
- name = 'setup_function'
- obj = self.parent.obj
- meth = getattr(obj, name, None)
- if meth is not None:
- return meth(self.obj)
-
- def teardown(self):
- """ perform teardown for this test function. """
- if getattr(self.obj, 'im_self', None):
- name = 'teardown_method'
- else:
- name = 'teardown_function'
- obj = self.parent.obj
- meth = getattr(obj, name, None)
- if meth is not None:
- return meth(self.obj)
-
#
# triggering specific outcomes while executing Items
#
-def skip(msg="unknown reason"):
+class BaseReason(object):
+ def __init__(self, msg="unknown reason", **kwds):
+ self.msg = msg
+ self.__dict__.update(kwds)
+
+ def __repr__(self):
+ return self.msg
+
+class Broken(BaseReason):
+ def __repr__(self):
+ return "Broken: %s" % (self.msg,)
+
+class _NotImplemented(BaseReason):
+ def __repr__(self):
+ return "Not implemented: %s" % (self.msg,)
+
+# whatever comes here....
+
+def skip(msg=BaseReason()):
""" skip with the given Message. """
__tracebackhide__ = True
raise Skipped(msg=msg)
Modified: py/branch/bugfix-0.9.0/py/test/outcome.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/test/outcome.py (original)
+++ py/branch/bugfix-0.9.0/py/test/outcome.py Mon Mar 17 20:18:48 2008
@@ -9,7 +9,7 @@
def __repr__(self):
if self.msg:
- return self.msg
+ return repr(self.msg)
return "<%s instance>" %(self.__class__.__name__,)
__str__ = __repr__
Modified: py/branch/bugfix-0.9.0/py/test/representation.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/test/representation.py (original)
+++ py/branch/bugfix-0.9.0/py/test/representation.py Mon Mar 17 20:18:48 2008
@@ -60,8 +60,11 @@
s = str(source.getstatement(len(source)-1))
except KeyboardInterrupt:
raise
- except:
- s = str(source[-1])
+ except:
+ try:
+ s = str(source[-1])
+ except IndexError:
+ s = "<Cannot get source>"
indent = " " * (4 + (len(s) - len(s.lstrip())))
# get the real exception information out
lines = excinfo.exconly(tryshort=True).split('\n')
Modified: py/branch/bugfix-0.9.0/py/test/rsession/executor.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/test/rsession/executor.py (original)
+++ py/branch/bugfix-0.9.0/py/test/rsession/executor.py Mon Mar 17 20:18:48 2008
@@ -40,7 +40,7 @@
raise
except:
e = sys.exc_info()[1]
- if isinstance(e, Failed):
+ if isinstance(e, Failed) and e.excinfo:
excinfo = e.excinfo
else:
excinfo = py.code.ExceptionInfo()
Modified: py/branch/bugfix-0.9.0/py/test/rsession/reporter.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/test/rsession/reporter.py (original)
+++ py/branch/bugfix-0.9.0/py/test/rsession/reporter.py Mon Mar 17 20:18:48 2008
@@ -14,6 +14,7 @@
from py.__.test.representation import Presenter
import sys
+import thread
class AbstractReporter(object):
def __init__(self, config, hosts):
@@ -280,6 +281,8 @@
def report_FailedTryiter(self, event):
self.out.line("FAILED TO LOAD MODULE: %s\n" % "/".join(event.item.listnames()))
self.failed_tests_outcome.append(event)
+ # argh! bad hack, need to fix it
+ self.failed[self.hosts[0]] += 1
def report_SkippedTryiter(self, event):
self.out.line("Skipped (%s) %s\n" % (str(event.excinfo.value), "/".
@@ -301,6 +304,7 @@
#self.show_item(event.item, False)
self.out.write("- FAILED TO LOAD MODULE")
self.failed_tests_outcome.append(event)
+ self.failed[self.hosts[0]] += 1
def report_ReceivedItemOutcome(self, event):
host = self.hosts[0]
Modified: py/branch/bugfix-0.9.0/py/test/rsession/testing/test_executor.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/test/rsession/testing/test_executor.py (original)
+++ py/branch/bugfix-0.9.0/py/test/rsession/testing/test_executor.py Mon Mar 17 20:18:48 2008
@@ -37,6 +37,10 @@
def run(self):
raise Failed(excinfo="3")
+class ItemTestFailingExplicitEmpty(Item):
+ def run(self):
+ py.test.raises(ValueError, lambda : 123)
+
class TestExecutor(BasicRsessionTest):
def test_run_executor(self):
ex = RunExecutor(ItemTestPassing("pass", self.config), config=self.config)
@@ -158,3 +162,10 @@
outcome = ex.execute()
assert not outcome.passed
assert outcome.excinfo == "3"
+
+ def test_executor_explicit_Faile_no_excinfo(self):
+ ex = RunExecutor(ItemTestFailingExplicitEmpty("failexx", self.config),
+ config=self.config)
+ outcome = ex.execute()
+ assert not outcome.passed
+
Modified: py/branch/bugfix-0.9.0/py/test/rsession/testing/test_reporter.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/test/rsession/testing/test_reporter.py (original)
+++ py/branch/bugfix-0.9.0/py/test/rsession/testing/test_reporter.py Mon Mar 17 20:18:48 2008
@@ -18,7 +18,6 @@
import py, os
-#py.test.skip("in progress")
from py.__.test.rsession.rsession import LocalReporter, AbstractSession,\
RemoteReporter
from py.__.test.rsession import repevent
@@ -135,11 +134,13 @@
r.report(repevent.RsyncFinished())
list(rootcol._tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x)))
r.report(repevent.TestFinished())
+ return r
cap = py.io.StdCaptureFD()
- boxfun()
+ r = boxfun()
out, err = cap.reset()
assert not err
+ assert out.find("1 failed in") != -1
assert out.find("NameError: name 'sadsadsa' is not defined") != -1
def _test_still_to_go(self):
@@ -171,23 +172,19 @@
reporter = LocalReporter
def test_report_received_item_outcome(self):
- #py.test.skip("XXX rewrite test to not rely on exact formatting")
assert self.report_received_item_outcome() == 'FsF.'
def test_module(self):
- #py.test.skip("XXX rewrite test to not rely on exact formatting")
output = self._test_module()
assert output.find("test_one") != -1
assert output.endswith("FsF."), output
def test_full_module(self):
- #py.test.skip("XXX rewrite test to not rely on exact formatting")
received = self._test_full_module()
- expected = """
-repmod/test_one.py[1]
-repmod/test_three.py[0] - FAILED TO LOAD MODULE
-repmod/test_two.py[0] - skipped (reason)"""
- assert received.find(expected) != -1
+ expected_lst = ["repmod/test_one.py", "FAILED TO LOAD MODULE",
+ "skipped", "reason"]
+ for i in expected_lst:
+ assert received.find(i) != -1
class TestRemoteReporter(AbstractTestReporter):
reporter = RemoteReporter
@@ -196,28 +193,24 @@
self._test_still_to_go()
def test_report_received_item_outcome(self):
- py.test.skip("XXX rewrite test to not rely on exact formatting")
val = self.report_received_item_outcome()
- expected = """ localhost: FAILED py.test.rsession.testing.test_slave.py funcpass
- localhost: SKIPPED py.test.rsession.testing.test_slave.py funcpass
- localhost: FAILED py.test.rsession.testing.test_slave.py funcpass
- localhost: PASSED py.test.rsession.testing.test_slave.py funcpass
-"""
- assert val.find(expected) != -1
+ expected_lst = ["localhost", "FAILED",
+ "funcpass", "test_one",
+ "SKIPPED",
+ "PASSED"]
+ for expected in expected_lst:
+ assert val.find(expected) != -1
def test_module(self):
- py.test.skip("XXX rewrite test to not rely on exact formatting")
val = self._test_module()
- print val
- expected = """ localhost: FAILED py.test.rsession.testing.test_slave.py funcpass
- localhost: SKIPPED py.test.rsession.testing.test_slave.py funcpass
- localhost: FAILED py.test.rsession.testing.test_slave.py funcpass
- localhost: PASSED py.test.rsession.testing.test_slave.py funcpass
-"""
- assert val.find(expected) != -1
+ expected_lst = ["localhost", "FAILED",
+ "funcpass", "test_one",
+ "SKIPPED",
+ "PASSED"]
+ for expected in expected_lst:
+ assert val.find(expected) != -1
def test_full_module(self):
- #py.test.skip("XXX rewrite test to not rely on exact formatting")
val = self._test_full_module()
- assert val.find('FAILED TO LOAD MODULE: repmod/test_three.py\n'\
- '\nSkipped (reason) repmod/test_two.py') != -1
+ assert val.find("FAILED TO LOAD MODULE: repmod/test_three.py\n"\
+ "\nSkipped ('reason') repmod/test_two.py") != -1
Modified: py/branch/bugfix-0.9.0/py/test/rsession/testing/test_rsession.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/test/rsession/testing/test_rsession.py (original)
+++ py/branch/bugfix-0.9.0/py/test/rsession/testing/test_rsession.py Mon Mar 17 20:18:48 2008
@@ -29,7 +29,7 @@
rootcol = py.test.collect.Directory(tmpdir)
data = list(rootcol._tryiter(reporterror=events.append))
assert len(events) == 2
- assert str(events[1][0].value) == "Reason"
+ assert str(events[1][0].value).find("Reason") != -1
class TestRSessionRemote(DirSetup, BasicRsessionTest):
def test_example_distribution_minus_x(self):
Modified: py/branch/bugfix-0.9.0/py/test/rsession/testing/test_web.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/test/rsession/testing/test_web.py (original)
+++ py/branch/bugfix-0.9.0/py/test/rsession/testing/test_web.py Mon Mar 17 20:18:48 2008
@@ -24,7 +24,7 @@
from py.__.test.rsession import webjs
from py.__.test.rsession.web import FUNCTION_LIST, IMPORTED_PYPY
- source = rpython2javascript(webjs, FUNCTION_LIST)
+ source = rpython2javascript(webjs, FUNCTION_LIST, use_pdb=False)
assert source
def test_parse_args():
Modified: py/branch/bugfix-0.9.0/py/test/rsession/web.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/test/rsession/web.py (original)
+++ py/branch/bugfix-0.9.0/py/test/rsession/web.py Mon Mar 17 20:18:48 2008
@@ -24,11 +24,10 @@
"show_host", "hide_host", "hide_messagebox", "opt_scroll"]
try:
- from pypy.rpython.ootypesystem.bltregistry import MethodDesc, BasicExternal,\
- described
+ from pypy.rpython.ootypesystem.bltregistry import MethodDesc, BasicExternal
from pypy.translator.js.main import rpython2javascript
from pypy.translator.js import commproxy
- from pypy.rpython.extfunc import _callable
+ from pypy.translator.js.lib.support import callback
commproxy.USE_MOCHIKIT = False
IMPORTED_PYPY = True
@@ -36,14 +35,11 @@
class BasicExternal(object):
pass
- def described(*args, **kwargs):
+ def callback(*args, **kwargs):
def decorator(func):
return func
return decorator
- def _callable(*args, **kwargs):
- pass
-
IMPORTED_PYPY = False
def add_item(event):
@@ -153,22 +149,19 @@
for host in self.hosts:
to_send[host.hostid] = host.hostname
return to_send
- show_hosts = described(retval={str:str}, args=[('callback',
- _callable([{str:str}]))])(show_hosts)
+ show_hosts = callback(retval={str:str})(show_hosts)
def show_skip(self, item_name="aa"):
return {'item_name': item_name,
'reason': self.skip_reasons[item_name]}
- show_skip = described(retval={str:str}, args=[('item_name',str),('callback',
- _callable([{str:str}]))])(show_skip)
+ show_skip = callback(retval={str:str})(show_skip)
def show_fail(self, item_name="aa"):
return {'item_name':item_name,
'traceback':str(self.fail_reasons[item_name]),
'stdout':self.stdout[item_name],
'stderr':self.stderr[item_name]}
- show_fail = described(retval={str:str}, args=[('item_name',str),('callback',
- _callable([{str:str}]))])(show_fail)
+ show_fail = callback(retval={str:str})(show_fail)
_sessids = None
_sesslock = py.std.thread.allocate_lock()
@@ -186,8 +179,7 @@
finally:
self._sesslock.release()
return sessid
- show_sessid = described(retval=str, args=[('callback',
- _callable([str]))])(show_sessid)
+ show_sessid = callback(retval=str)(show_sessid)
def failed(self, **kwargs):
if not 'sessid' in kwargs:
@@ -201,14 +193,13 @@
del self._sessids[to_del]
self.pending_events._del(kwargs['sessid'])
- def show_all_statuses(self, sessid=-1):
+ def show_all_statuses(self, sessid='xx'):
retlist = [self.show_status_change(sessid)]
while not self.pending_events.empty_queue(sessid):
retlist.append(self.show_status_change(sessid))
retval = retlist
return retval
- show_all_statuses = described(retval=[{str:str}],args=
- [('sessid',str), ('callback',_callable([[{str:str}]]))])(show_all_statuses)
+ show_all_statuses = callback(retval=[{str:str}])(show_all_statuses)
def show_status_change(self, sessid):
event = self.pending_events.get(sessid)
@@ -276,7 +267,7 @@
def repr_source(self, relline, source):
lines = []
- for num, line in enumerate(source.split("\n")):
+ for num, line in enumerate(str(source).split("\n")):
if num == relline:
lines.append(">>>>" + line)
else:
@@ -286,6 +277,14 @@
def report_ReceivedItemOutcome(self, event):
self.all += 1
self.pending_events.put(event)
+
+ def report_FailedTryiter(self, event):
+ fullitemname = "/".join(event.item.listnames())
+ self.fail_reasons[fullitemname] = self.repr_failure_tblong(
+ event.item, event.excinfo, event.excinfo.traceback)
+ self.stdout[fullitemname] = ''
+ self.stderr[fullitemname] = ''
+ self.pending_events.put(event)
def report_ItemStart(self, event):
if isinstance(event.item, py.test.collect.Module):
@@ -400,7 +399,8 @@
def run_jssource(self):
js_name = py.path.local(__file__).dirpath("webdata").join("source.js")
web_name = py.path.local(__file__).dirpath().join("webjs.py")
- if IMPORTED_PYPY and web_name.mtime() > js_name.mtime():
+ if IMPORTED_PYPY and web_name.mtime() > js_name.mtime() or \
+ (not js_name.check()) or 1:
from py.__.test.rsession import webjs
javascript_source = rpython2javascript(webjs,
Modified: py/branch/bugfix-0.9.0/py/test/rsession/webdata/index.html
==============================================================================
--- py/branch/bugfix-0.9.0/py/test/rsession/webdata/index.html (original)
+++ py/branch/bugfix-0.9.0/py/test/rsession/webdata/index.html Mon Mar 17 20:18:48 2008
@@ -20,6 +20,10 @@
z-index: 2;
}
+ div.main {
+ margin-right: 170px;
+ }
+
.error {
color: #F00;
font-weight: bold;
@@ -32,6 +36,7 @@
background-color: #ffd;
border: 1px solid #003;
z-index: 1;
+ width: 160px;
}
#navbar tr, #navbar td {
Modified: py/branch/bugfix-0.9.0/py/test/rsession/webdata/source.js
==============================================================================
Binary files. No diff available.
Modified: py/branch/bugfix-0.9.0/py/test/rsession/webjs.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/test/rsession/webjs.py (original)
+++ py/branch/bugfix-0.9.0/py/test/rsession/webjs.py Mon Mar 17 20:18:48 2008
@@ -156,7 +156,7 @@
td.appendChild(link)
exported_methods.show_fail(item_name, fail_come_back)
- if counters[msg['fullmodulename']] % MAX_COUNTER == 0:
+ if counters[msg['fullmodulename']] == 0:
tr = create_elem("tr")
module_part.appendChild(tr)
@@ -212,10 +212,16 @@
return True
tr = create_elem("tr")
td = create_elem("td")
+ a = create_elem("a")
+ a.setAttribute("href", "javascript:show_traceback('%s')" % (
+ msg['fullitemname'],))
txt = create_text_elem("- FAILED TO LOAD MODULE")
- td.appendChild(txt)
+ a.appendChild(txt)
+ td.appendChild(a)
tr.appendChild(td)
module_part.appendChild(tr)
+ item_name = msg['fullitemname']
+ exported_methods.show_fail(item_name, fail_come_back)
elif msg['type'] == 'SkippedTryiter':
module_part = get_elem(msg['fullitemname'])
if not module_part:
Modified: py/branch/bugfix-0.9.0/py/test/session.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/test/session.py (original)
+++ py/branch/bugfix-0.9.0/py/test/session.py Mon Mar 17 20:18:48 2008
@@ -67,6 +67,7 @@
self.footer(colitems)
except Exit, ex:
pass
+ return self.getitemoutcomepairs(Failed)
def runtraced(self, colitem):
if self.shouldclose():
Modified: py/branch/bugfix-0.9.0/py/test/testing/test_session.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/test/testing/test_session.py (original)
+++ py/branch/bugfix-0.9.0/py/test/testing/test_session.py Mon Mar 17 20:18:48 2008
@@ -314,3 +314,24 @@
expected_output = '\nE ' + line_to_report + '\n'
print 'Looking for:', expected_output
assert expected_output in out
+
+
+def test_skip_reasons():
+ tmp = py.test.ensuretemp("check_skip_reasons")
+ tmp.ensure("test_one.py").write(py.code.Source("""
+ import py
+ def test_1():
+ py.test.skip(py.test.broken('stuff'))
+
+ def test_2():
+ py.test.skip(py.test.notimplemented('stuff'))
+ """))
+ tmp.ensure("__init__.py")
+ config = py.test.config._reparse([tmp])
+ session = config.initsession()
+ session.main()
+ skips = session.getitemoutcomepairs(Skipped)
+ assert len(skips) == 2
+ assert repr(skips[0][1]) == 'Broken: stuff'
+ assert repr(skips[1][1]) == 'Not implemented: stuff'
+
Modified: py/branch/bugfix-0.9.0/py/test/testing/test_setup_nested.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/test/testing/test_setup_nested.py (original)
+++ py/branch/bugfix-0.9.0/py/test/testing/test_setup_nested.py Mon Mar 17 20:18:48 2008
@@ -42,14 +42,23 @@
class TestSetupTeardownOnInstance(TestSimpleClassSetup):
def setup_method(self, method):
- self.clslevel.append(17)
+ self.clslevel.append(method.__name__)
def teardown_method(self, method):
x = self.clslevel.pop()
- assert x == 17
+ assert x == method.__name__
def test_setup(self):
- assert self.clslevel[-1] == 17
+ assert self.clslevel[-1] == 'test_setup'
+
+ def test_generate(self):
+ assert self.clslevel[-1] == 'test_generate'
+ yield self.generated, 5
+ assert self.clslevel[-1] == 'test_generate'
+
+ def generated(self, value):
+ assert value == 5
+ assert self.clslevel[-1] == 'test_generate'
def test_teardown_method_worked():
assert not TestSetupTeardownOnInstance.clslevel
Modified: py/branch/bugfix-0.9.0/py/tool/utestconvert.py
==============================================================================
--- py/branch/bugfix-0.9.0/py/tool/utestconvert.py (original)
+++ py/branch/bugfix-0.9.0/py/tool/utestconvert.py Mon Mar 17 20:18:48 2008
@@ -225,7 +225,7 @@
if not args:
s = ''
- for block in blocksplitter(sys.stdin.read()):
+ for block in blocksplitter(sys.stdin):
s += rewrite_utest(block)
sys.stdout.write(s)
More information about the py-svn
mailing list