[py-svn] r10741 - in py/branch/py-collect: . bin c-extension c-extension/greenlet code code/testing documentation execnet magic magic/testing misc misc/testing path/local path/svn path/svn/testing process tool xmlobj xmlobj/testing

hpk at codespeak.net hpk at codespeak.net
Sat Apr 16 20:36:09 CEST 2005


Author: hpk
Date: Sat Apr 16 20:36:08 2005
New Revision: 10741

Added:
   py/branch/py-collect/bin/py.countloc   (props changed)
      - copied unchanged from r10740, py/dist/py/bin/py.countloc
   py/branch/py-collect/code/testing/test_code.py   (props changed)
      - copied unchanged from r10740, py/dist/py/code/testing/test_code.py
   py/branch/py-collect/tool/
      - copied from r10740, py/dist/py/tool/
   py/branch/py-collect/tool/__init__.py
   py/branch/py-collect/tool/utestconvert.py
      - copied unchanged from r10740, py/dist/py/tool/utestconvert.py
Modified:
   py/branch/py-collect/__init__.py
   py/branch/py-collect/bin/_findpy.py
   py/branch/py-collect/c-extension/   (props changed)
   py/branch/py-collect/c-extension/greenlet/greenlet.c
   py/branch/py-collect/c-extension/greenlet/greenlet.h
   py/branch/py-collect/c-extension/greenlet/test_remote.py   (props changed)
   py/branch/py-collect/code/source.py
   py/branch/py-collect/code/testing/test_source.py
   py/branch/py-collect/documentation/execnet.txt
   py/branch/py-collect/documentation/test.txt
   py/branch/py-collect/execnet/channel.py
   py/branch/py-collect/magic/assertion.py
   py/branch/py-collect/magic/invoke.py
   py/branch/py-collect/magic/testing/test_assertion.py
   py/branch/py-collect/magic/testing/test_invoke.py
   py/branch/py-collect/misc/error.py
   py/branch/py-collect/misc/testing/test_initpkg.py
   py/branch/py-collect/path/local/local.py
   py/branch/py-collect/path/svn/svncommon.py
   py/branch/py-collect/path/svn/testing/test_urlcommand.py
   py/branch/py-collect/path/svn/testing/test_wccommand.py
   py/branch/py-collect/path/svn/wccommand.py
   py/branch/py-collect/process/cmdexec.py
   py/branch/py-collect/xmlobj/html.py
   py/branch/py-collect/xmlobj/testing/test_html.py
   py/branch/py-collect/xmlobj/visit.py
Log:
remerge the trunk into the branch ... 



Modified: py/branch/py-collect/__init__.py
==============================================================================
--- py/branch/py-collect/__init__.py	(original)
+++ py/branch/py-collect/__init__.py	Sat Apr 16 20:36:08 2005
@@ -56,7 +56,7 @@
     # generalized inspection API
     'code.compile'           : ('./code/source.py', 'compile_'),
     'code.Source'            : ('./code/source.py', 'Source'),
-    'code.Code'              : ('./code/frame.py', 'Code'),
+    'code.Code'              : ('./code/code.py', 'Code'),
     'code.Frame'             : ('./code/frame.py', 'Frame'),
     'code.ExceptionInfo'     : ('./code/excinfo.py', 'ExceptionInfo'),
     'code.Traceback'         : ('./code/traceback2.py', 'Traceback'),

Modified: py/branch/py-collect/bin/_findpy.py
==============================================================================
--- py/branch/py-collect/bin/_findpy.py	(original)
+++ py/branch/py-collect/bin/_findpy.py	Sat Apr 16 20:36:08 2005
@@ -27,6 +27,6 @@
 if not searchpy(abspath(os.curdir)):
     if not searchpy(opd(abspath(sys.argv[0]))):
         if not searchpy(opd(__file__)):
-            raise SystemExit, "Could not find 'py' package!"
+            pass # let's hope it is just on sys.path 
 
 import py

Modified: py/branch/py-collect/c-extension/greenlet/greenlet.c
==============================================================================
--- py/branch/py-collect/c-extension/greenlet/greenlet.c	(original)
+++ py/branch/py-collect/c-extension/greenlet/greenlet.c	Sat Apr 16 20:36:08 2005
@@ -259,10 +259,10 @@
 	   - ts_passaround: NULL if PyErr_Occurred(),
 	             else a tuple of args sent to ts_target (holds a reference)
 	*/
-	int err, recursion_depth;
+	int err;
 	{   /* save state */
 		PyThreadState* tstate = PyThreadState_GET();
-		recursion_depth = tstate->recursion_depth;
+		ts_current->recursion_depth = tstate->recursion_depth;
 		ts_current->top_frame = tstate->frame;
 	}
 	ts_origin = ts_current;
@@ -273,7 +273,7 @@
 	}
 	else {
 		PyThreadState* tstate = PyThreadState_GET();
-		tstate->recursion_depth = recursion_depth;
+		tstate->recursion_depth = ts_target->recursion_depth;
 		tstate->frame = ts_target->top_frame;
 		ts_target->top_frame = NULL;
 		ts_current = ts_target;
@@ -346,6 +346,8 @@
 	ts_target->stack_start = NULL;
 	ts_target->stack_stop = (char*) mark;
 	ts_target->stack_prev = ts_current;
+	ts_target->top_frame = NULL;
+	ts_target->recursion_depth = PyThreadState_GET()->recursion_depth;
 	err = _PyGreen_switchstack();
 	/* returns twice!
 	   The 1st time with err=1: we are in the new greenlet

Modified: py/branch/py-collect/c-extension/greenlet/greenlet.h
==============================================================================
--- py/branch/py-collect/c-extension/greenlet/greenlet.h	(original)
+++ py/branch/py-collect/c-extension/greenlet/greenlet.h	Sat Apr 16 20:36:08 2005
@@ -19,6 +19,7 @@
 	struct _greenlet* parent;
 	PyObject* run_info;
 	struct _frame* top_frame;
+	int recursion_depth;
 } PyGreenlet;
 
 extern PyTypeObject PyGreen_Type;

Modified: py/branch/py-collect/code/source.py
==============================================================================
--- py/branch/py-collect/code/source.py	(original)
+++ py/branch/py-collect/code/source.py	Sat Apr 16 20:36:08 2005
@@ -1,6 +1,8 @@
 from __future__ import generators
 import sys
 import inspect
+import py
+cpy_compile = compile 
 
 # DON'T IMPORT PY HERE
 
@@ -13,18 +15,31 @@
         de = kwargs.get('deindent', True)
         rstrip = kwargs.get('rstrip', True) 
         for part in parts:
+            if not part: 
+                partlines = []
             if isinstance(part, Source):
                 partlines = part.lines
-            elif isinstance(part, str):
-                if rstrip:
-                    part = part.rstrip()
+            elif isinstance(part, (unicode, str)):
                 partlines = part.split('\n')
+                if rstrip:
+                    while partlines: 
+                        if partlines[-1].strip(): 
+                            break
+                        partlines.pop()
             else:
                 partlines = getsource(part, deindent=de).lines
             if de:
                 partlines = deindent(partlines)
             lines.extend(partlines)
 
+    def __eq__(self, other): 
+        try:
+            return self.lines == other.lines 
+        except AttributeError: 
+            if isinstance(other, str): 
+                return str(self) == other 
+            return False 
+
     def __getitem__(self, key):
         if isinstance(key, int):
             return self.lines[key]
@@ -43,14 +58,13 @@
         """ return new source object with trailing
             and leading blank lines removed.
         """
-        for start in range(0, len(self)):
-            if not self.lines[start].isspace():
-                break
-        for end in range(len(self)-1, -1, -1):
-            if not self.lines[end].isspace():
-                break
+        start, end = 0, len(self)
+        while start < end and not self.lines[start].strip():
+            start += 1
+        while end > start and not self.lines[end-1].strip():
+            end -= 1
         source = Source()
-        source.lines[:] = self.lines[start:end+1]
+        source.lines[:] = self.lines[start:end]
         return source
 
     def putaround(self, before='', after='', indent=' ' * 4): 
@@ -64,6 +78,14 @@
         newsource.lines = before.lines + lines +  after.lines
         return newsource
 
+    def indent(self, indent=' ' * 4): 
+        """ return a copy of the source object with 
+            all lines indented by the given indent-string. 
+        """
+        newsource = Source()
+        newsource.lines = [(indent+line) for line in self.lines]
+        return newsource
+
     def getstatement(self, lineno):
         """ return Source statement which contains the
             given linenumber (counted from 0).
@@ -125,9 +147,6 @@
         newsource.lines[:] = deindent(self.lines, offset)
         return newsource
 
-    def __len__(self):
-        return len(self.lines)
-
     def isparseable(self, deindent=True):
         """ return True if source is parseable, heuristically
             deindenting it by default. 
@@ -148,24 +167,25 @@
         return "\n".join(self.lines)
 
     def compile(self, filename=None, mode='exec',
-                flag=generators.compiler_flag):
+                flag=generators.compiler_flag, dont_inherit=0):
         """ return compiled code object. if filename is None
             invent an artificial filename which displays
             the source/line position of the caller frame.
         """
-        if filename is None:
+        if not filename or py.path.local(filename).check(file=0): 
             frame = sys._getframe(1) # the caller
-            filename = '<%s:%d>' % (frame.f_code.co_filename,
-                                    frame.f_lineno)
-        source = str(self)+'\n'
+            filename = '%s<%s:%d>' % (filename, frame.f_code.co_filename,
+                                      frame.f_lineno)
+        source = "\n".join(self.lines) + '\n'
         try:
-            co = compile(source, filename, mode, flag)
+            co = cpy_compile(source, filename, mode, flag)
         except SyntaxError, ex:
             # re-represent syntax errors from parsing python strings
-            newex = SyntaxError('\n'.join([
-                "".join(self.lines[:ex.lineno]),
-                " " * ex.offset + '^',
-                "syntax error probably generated here: %s" % filename]))
+            msglines = self.lines[:ex.lineno]
+            if ex.offset:
+                msglines.append(" "*ex.offset + '^')
+            msglines.append("syntax error probably generated here: %s" % filename)
+            newex = SyntaxError('\n'.join(msglines))
             newex.offset = ex.offset
             newex.lineno = ex.lineno
             newex.text = ex.text
@@ -173,14 +193,15 @@
         else:
             co_filename = MyStr(filename)
             co_filename.__source__ = self
-            return newcode_withfilename(co, co_filename)
+            return py.code.Code(co).new(rec=1, co_filename=co_filename) 
+            #return newcode_withfilename(co, co_filename)
 
 #
 # public API shortcut functions
 #
 
 def compile_(source, filename=None, mode='exec', flags=
-            generators.compiler_flag):
+            generators.compiler_flag, dont_inherit=0):
     """ compile the given source to a raw code object,
         which points back to the source code through
         "co_filename.__source__".  All code objects
@@ -192,6 +213,13 @@
     co = s.compile(filename, mode, flags)
     return co
 
+
+#
+# various helper functions
+#
+class MyStr(str):
+    """ custom string which allows to add attributes. """
+
 def getsource(obj, **kwargs):
     if hasattr(obj, 'func_code'):
         obj = obj.func_code
@@ -200,7 +228,6 @@
     try:
         fullsource = obj.co_filename.__source__
     except AttributeError:
-        import inspect
         strsrc = inspect.getsource(obj)
         assert isinstance(strsrc, str)
         return Source(strsrc, **kwargs)
@@ -209,12 +236,6 @@
         end = fullsource.getblockend(lineno)
         return fullsource[lineno:end+1]
 
-#
-# various helper functions
-#
-class MyStr(str):
-    """ custom string which allows to add attributes. """
-
 def deindent(lines, offset=None):
     # XXX maybe use the tokenizer to properly handle multiline
     #     strings etc.pp?
@@ -235,36 +256,3 @@
         newlines.append(line)
     return newlines
 
-def newcode(fromcode, **kwargs):
-    names = [x for x in dir(fromcode) if x[:3] == 'co_']
-    for name in names:
-        if name not in kwargs:
-            kwargs[name] = getattr(fromcode, name)
-    import new
-    return new.code(
-             kwargs['co_argcount'],
-             kwargs['co_nlocals'],
-             kwargs['co_stacksize'],
-             kwargs['co_flags'],
-             kwargs['co_code'],
-             kwargs['co_consts'],
-             kwargs['co_names'],
-             kwargs['co_varnames'],
-             kwargs['co_filename'],
-             kwargs['co_name'],
-             kwargs['co_firstlineno'],
-             kwargs['co_lnotab'],
-             kwargs['co_freevars'],
-             kwargs['co_cellvars'],
-    )
-
-def newcode_withfilename(co, co_filename):
-    newconstlist = []
-    cotype = type(co)
-    for c in co.co_consts:
-        if isinstance(c, cotype):
-            c = newcode_withfilename(c, co_filename)
-        newconstlist.append(c)
-    return newcode(co, co_consts = tuple(newconstlist),
-                       co_filename = co_filename)
-

Modified: py/branch/py-collect/code/testing/test_source.py
==============================================================================
--- py/branch/py-collect/code/testing/test_source.py	(original)
+++ py/branch/py-collect/code/testing/test_source.py	Sat Apr 16 20:36:08 2005
@@ -19,6 +19,11 @@
     """, rstrip=True)
     assert str(x) == "\n3"
 
+def test_unicode(): 
+    x = Source(unicode("4"))
+    assert str(x) == "4"
+
+
 def test_source_from_function():
     source = py.code.Source(test_source_str_function)
     assert str(source).startswith('def test_source_str_function():')
@@ -47,6 +52,26 @@
 else:
     x = 23"""
 
+def test_source_putaround(): 
+    source = Source() 
+    source = source.putaround("""
+        if 1:
+            x=1
+    """)
+    assert str(source).strip() == "if 1:\n    x=1"
+
+def test_source_strips(): 
+    source = Source("")
+    assert source == Source()
+    assert str(source) == ''
+    assert source.strip() == source 
+
+def test_source_strip_multiline(): 
+    source = Source()
+    source.lines = ["", " hello", "  "]
+    source2 = source.strip() 
+    assert source2.lines == [" hello"]
+
 def test_syntaxerror_rerepresentation():
     ex = py.test.raises(SyntaxError, py.code.compile, 'x x')
     assert ex.value.lineno == 1
@@ -84,41 +109,6 @@
         l = [x for x in self.source] 
         assert len(l) == 4 
             
-
-class TestCodeHacks:
-    def test_newcode(self):
-        from py.__impl__.code.source import newcode
-        def f():
-            pass
-        co_filename = 'hello'
-        c = newcode(f.func_code, co_filename=co_filename)
-        assert c.co_filename is co_filename
-
-    def test_newcode_withfilename(self):
-        from py.__impl__.code.source import newcode_withfilename
-        source = py.code.Source("""
-            def f():
-                def g():
-                    pass
-        """)
-        co = compile(str(source)+'\n', 'nada', 'exec')
-        obj = 'hello'
-        newco = newcode_withfilename(co, obj)
-        def walkcode(co):
-            for x in co.co_consts:
-                if isinstance(x, type(co)):
-                    for y in walkcode(x):
-                        yield y
-            yield co
-
-        names = []
-        for code in walkcode(newco):
-            assert newco.co_filename == obj
-            assert newco.co_filename is obj
-            names.append(code.co_name)
-        assert 'f' in names
-        assert 'g' in names
-
 class TestSourceParsingAndCompiling:
     source = Source("""\
         def f(x):
@@ -132,6 +122,11 @@
         exec co
         assert x == 3
 
+    def test_compile_unicode(self): 
+        co = py.code.compile(unicode('u"\xc3\xa5"', 'utf8'), mode='eval')
+        val = eval(co) 
+        assert isinstance(val, unicode)
+
     def test_compile_and_getsource_simple(self):
         co = py.code.compile("x=3")
         exec co
@@ -179,6 +174,9 @@
         #print "block", str(block)
         assert str(stmt).strip().startswith('assert')
 
+    def test_offsetless_synerr(self):
+        py.test.raises(SyntaxError, py.code.compile, "lambda a,a: 0", mode='eval')
+
 def test_getstartingblock_singleline():
     class A:
         def __init__(self, *args):

Modified: py/branch/py-collect/documentation/execnet.txt
==============================================================================
--- py/branch/py-collect/documentation/execnet.txt	(original)
+++ py/branch/py-collect/documentation/execnet.txt	Sat Apr 16 20:36:08 2005
@@ -45,8 +45,8 @@
 apps and especially RMI systems you often have to upgrade your
 server if you upgrade your client. 
 
-What about Security? Are you completly nuts? 
---------------------------------------------
+What about Security? Are you completely nuts? 
+---------------------------------------------
 
 We'll talk about that later :-) 
 
@@ -144,8 +144,8 @@
                     f.close()
             """ 
     # open a gateway to a fresh child process 
-    contentgateway = py.execnet.SSHGateway('codespeak.net', identity)
-    channel = contentgateway.remote_exec_async(contentserverbootstrap)
+    contentgateway = py.execnet.SshGateway('codespeak.net') 
+    channel = contentgateway.remote_exec(contentserverbootstrap)
 
     for fn in somefilelist: 
         channel.send(fn) 

Modified: py/branch/py-collect/documentation/test.txt
==============================================================================
--- py/branch/py-collect/documentation/test.txt	(original)
+++ py/branch/py-collect/documentation/test.txt	Sat Apr 16 20:36:08 2005
@@ -35,9 +35,10 @@
 
   py.test
 
-This will automatically collect any Python module that starts with
-``test_`` in the whole directory hierarchy, starting with the current
-directory, and run them.
+This will automatically collect and run any Python module whose filenames 
+start with ``test_`` from the directory and any subdirectories, starting 
+with the current directory, and run them. Each Python test module is 
+inspect for test methods starting with ``test_``. 
 
 Basic Features of ``py.test`` 
 =============================
@@ -81,14 +82,15 @@
 automatic collection of tests on all levels
 -------------------------------------------
 
-The automated test collection process collects files 
-from all subdirectories with a leading ``test_`` in their
-filename and then every function with a leading ``test_`` 
-or ``Test`` in the case of classes.  The collecting process 
-can be customized at each level.  (see `collection process`_ 
+The automated test collection process walks the current
+directory (or the directory given as a command line argument)
+and all its subdirectories and collects python modules with a
+leading ``test_`` filename.  From each test module every function
+with a leading ``test_`` or class with a leading ``Test`` name
+is collected.  The collecting process can be customized at
+directory, module or class level.  (see `collection process`_ 
 for some implementation details). 
 
-
 .. _`generative tests`: 
 
 generative tests: yielding more tests
@@ -475,3 +477,61 @@
 
 .. _`getting started`: getting_started.html
 .. _`py-dev mailing list`: http://codespeak.net/mailman/listinfo/py-dev 
+
+
+Future/Planned Features of py.test 
+==================================
+
+Please note that the following descriptions of future features
+sound factual although they aren't implemented yet.  This
+allows easy migration to real documentation later.
+Nevertheless, none of the described planned features is
+set in stone, yet. In fact, they are open to discussion on
+py-dev at codespeak dot net. 
+
+Hey, if you want to suggest new features or command line options 
+for py.test it would be great if you could do it by providing 
+documentation for the feature.  Welcome to documentation driven 
+development :-) 
+
+selecting tests by queries/full text search 
+-------------------------------------------
+
+You can selectively run tests by specifiying words 
+on the command line in a google-like way. Example:: 
+
+    py.test simple 
+
+will run all tests that are found from the current directory 
+and that have the word "simple" somewhere in their `test address`_.  
+``test_simple1`` and ``TestSomething.test_whatever_simpleton`` would both 
+qualify.  If you want to exclude the latter test method you could say:: 
+
+    py.test -- simple -simpleton 
+
+Note that the doubledash "--" signals the end of option parsing so
+that "-simpleton" will not be misinterpreted as a command line option. 
+
+Interpreting positional arguments as specifying search queries 
+means that you can only restrict the set of tests. There is no way to 
+say "run all 'simple' in addition to all 'complex' tests".  If this proves
+to be a problem we can probably come up with a command line option 
+that allows to specify multiple queries which all add to the set of
+tests-to-consider. 
+
+.. _`test address`: 
+
+the concept of a test address
+----------------------------- 
+
+For specifiying tests it is convenient to define the notion
+of a *test address*, representable as a filesystem path and a 
+list of names leading to a test item.  If represented as a single 
+string the path and names are separated by a `/` character, for example: 
+
+    ``somedir/somepath.py/TestClass/test_method``
+
+Such representations can be used to memoize failing tests 
+by writing them out in a file or communicating them across
+process and computer boundaries. 
+

Modified: py/branch/py-collect/execnet/channel.py
==============================================================================
--- py/branch/py-collect/execnet/channel.py	(original)
+++ py/branch/py-collect/execnet/channel.py	Sat Apr 16 20:36:08 2005
@@ -113,11 +113,13 @@
         return x
     
     def __iter__(self):
-        while 1: 
-            try:
-                yield self.receive()
-            except EOFError: 
-                raise StopIteration 
+        return self 
+
+    def next(self): 
+        try:
+            return self.receive()
+        except EOFError: 
+            raise StopIteration 
 
 #
 # helpers

Modified: py/branch/py-collect/magic/assertion.py
==============================================================================
--- py/branch/py-collect/magic/assertion.py	(original)
+++ py/branch/py-collect/magic/assertion.py	Sat Apr 16 20:36:08 2005
@@ -7,20 +7,23 @@
 class AssertionError(BuiltinAssertionError):
     def __init__(self, *args):
         BuiltinAssertionError.__init__(self, *args)
-        f = sys._getframe(1)
-        try:
-            source = py.code.Frame(f).statement
-            source = str(source).strip()
-        except py.error.ENOENT:
-            source = None
-            # this can also occur during reinterpretation, when the
-            # co_filename is set to "<run>".
-        if source:
-            self.msg = exprinfo.interpret(source, f, should_fail=True)
-            if not self.args:
-                self.args = (self.msg,)
-        else:
-            self.msg = None
+        if args: 
+            self.msg = args[0]
+        else: 
+            f = sys._getframe(1)
+            try:
+                source = py.code.Frame(f).statement
+                source = str(source).strip()
+            except py.error.ENOENT:
+                source = None
+                # this can also occur during reinterpretation, when the
+                # co_filename is set to "<run>".
+            if source:
+                self.msg = exprinfo.interpret(source, f, should_fail=True)
+                if not self.args:
+                    self.args = (self.msg,)
+            else:
+                self.msg = None
 
 def invoke():
     py.magic.patch(__builtin__, 'AssertionError', AssertionError)

Modified: py/branch/py-collect/magic/invoke.py
==============================================================================
--- py/branch/py-collect/magic/invoke.py	(original)
+++ py/branch/py-collect/magic/invoke.py	Sat Apr 16 20:36:08 2005
@@ -1,5 +1,7 @@
+import py 
+import __builtin__ as cpy_builtin
 
-def invoke(assertion=False):
+def invoke(assertion=False, compile=False):
     """ invoke magic, currently you can specify:
 
         assertion  patches the builtin AssertionError to try to give
@@ -10,10 +12,13 @@
     if assertion:
         from py.__impl__.magic import assertion
         assertion.invoke()
+    if compile: 
+        py.magic.patch(cpy_builtin, 'compile', py.code.compile )
 
-def revoke(assertion=False):
+def revoke(assertion=False, compile=False):
     """ revoke previously invoked magic (see invoke())."""
     if assertion:
         from py.__impl__.magic import assertion
         assertion.revoke()
-
+    if compile: 
+        py.magic.revert(cpy_builtin, 'compile') 

Modified: py/branch/py-collect/magic/testing/test_assertion.py
==============================================================================
--- py/branch/py-collect/magic/testing/test_assertion.py	(original)
+++ py/branch/py-collect/magic/testing/test_assertion.py	Sat Apr 16 20:36:08 2005
@@ -16,6 +16,12 @@
         s = str(e)
         assert s.startswith('assert 2 == 3\n')
 
+def test_assert_with_explicit_message():
+    try:
+        assert f() == 3, "hello"
+    except AssertionError, e:
+        assert e.msg == 'hello'
+
 def test_assert_within_finally():
     class A:
         def f():

Modified: py/branch/py-collect/magic/testing/test_invoke.py
==============================================================================
--- py/branch/py-collect/magic/testing/test_invoke.py	(original)
+++ py/branch/py-collect/magic/testing/test_invoke.py	Sat Apr 16 20:36:08 2005
@@ -13,3 +13,17 @@
     finally:
         py.magic.revoke(assertion=True)
 
+def test_invoke_compile():
+    py.magic.invoke(compile=True)
+    try:
+        co = compile("""if 1: 
+                    def f(): 
+                        return 1
+                    \n""", '', 'exec')
+        d = {}
+        exec co in d
+        assert py.code.Source(d['f']) 
+    finally:
+        py.magic.revoke(compile=True)
+
+

Modified: py/branch/py-collect/misc/error.py
==============================================================================
--- py/branch/py-collect/misc/error.py	(original)
+++ py/branch/py-collect/misc/error.py	Sat Apr 16 20:36:08 2005
@@ -19,7 +19,8 @@
 
 _winerrnomap = {
     3: py.std.errno.ENOENT, 
-    267: py.std.errno.ENOTDIR, 
+    267: py.std.errno.ENOTDIR,
+    5: py.std.errno.EACCES,  # anything better?
 }
 
 ModuleType = type(py)

Modified: py/branch/py-collect/misc/testing/test_initpkg.py
==============================================================================
--- py/branch/py-collect/misc/testing/test_initpkg.py	(original)
+++ py/branch/py-collect/misc/testing/test_initpkg.py	Sat Apr 16 20:36:08 2005
@@ -44,6 +44,7 @@
         base.join('documentation',),
         base.join('test', 'testing', 'import_test'),
         base.join('c-extension',), 
+        base.join('magic', 'greenlet.py'), 
         base.join('bin'),
         base.join('execnet', 'bin'),
     )

Modified: py/branch/py-collect/path/local/local.py
==============================================================================
--- py/branch/py-collect/path/local/local.py	(original)
+++ py/branch/py-collect/path/local/local.py	Sat Apr 16 20:36:08 2005
@@ -59,14 +59,14 @@
             path = path.strpath
         # initialize the path
         self = object.__new__(cls)
-        if path is None:
+        if not path: 
             self.strpath = os.getcwd()
-        elif not path:
+        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")
-        else:
-            self.strpath = os.path.abspath(os.path.normpath(str(path)))
         assert isinstance(self.strpath, str)
         return self
 
@@ -422,7 +422,17 @@
                     if len(header) == 8:
                         magic, timestamp = py.std.struct.unpack('<4si', header)
                         if magic == my_magic and timestamp == my_timestamp:
-                            return py.std.marshal.load(f)
+                            co = py.std.marshal.load(f)
+                            path1 = co.co_filename
+                            path2 = str(self)
+                            if path1 == path2:
+                                return co
+                            try:
+                                if os.path.samefile(path1, path2):
+                                    return co
+                            except (OSError,          # probably path1 not found
+                                    AttributeError):  # samefile() not available
+                                pass
                 finally:
                     f.close()
             except py.error.Error:
@@ -458,7 +468,7 @@
                                            stdout, stderr,)
         return stdout
 
-    def sysfind(self, name, checker=None):
+    def sysfind(cls, name, checker=None):
         """ return a path object found by looking at the systems
             underlying PATH specification. If the checker is not None
             it will be invoked to filter matching paths.  If a binary
@@ -503,7 +513,7 @@
             dpath = cls(tempfile.mktemp())
             try:
                 dpath.mkdir()
-            except path.FileExists:
+            except (py.error.EEXIST, py.error.EPERM, py.error.EACCES): 
                 continue
             return dpath
         raise py.error.ENOENT(dpath, "could not create tempdir, %d tries" % tries)

Modified: py/branch/py-collect/path/svn/svncommon.py
==============================================================================
--- py/branch/py-collect/path/svn/svncommon.py	(original)
+++ py/branch/py-collect/path/svn/svncommon.py	Sat Apr 16 20:36:08 2005
@@ -3,7 +3,6 @@
 """
 import os, sys, time, re
 import py
-from py import path, process
 from py.__impl__.path import common
 
 #_______________________________________________________________

Modified: py/branch/py-collect/path/svn/testing/test_urlcommand.py
==============================================================================
--- py/branch/py-collect/path/svn/testing/test_urlcommand.py	(original)
+++ py/branch/py-collect/path/svn/testing/test_urlcommand.py	Sat Apr 16 20:36:08 2005
@@ -1,5 +1,5 @@
 import py
-from svntestbase import CommonCommandAndBindingTests, getrepowc
+from py.__impl__.path.svn.testing.svntestbase import CommonCommandAndBindingTests, getrepowc
 
 class TestSvnCommandPath(CommonCommandAndBindingTests):
     def setup_class(cls): 

Modified: py/branch/py-collect/path/svn/testing/test_wccommand.py
==============================================================================
--- py/branch/py-collect/path/svn/testing/test_wccommand.py	(original)
+++ py/branch/py-collect/path/svn/testing/test_wccommand.py	Sat Apr 16 20:36:08 2005
@@ -1,5 +1,5 @@
 import py
-from svntestbase import CommonSvnTests, getrepowc
+from py.__impl__.path.svn.testing.svntestbase import CommonSvnTests, getrepowc
 
 class TestWCSvnCommandPath(CommonSvnTests):
 

Modified: py/branch/py-collect/path/svn/wccommand.py
==============================================================================
--- py/branch/py-collect/path/svn/wccommand.py	(original)
+++ py/branch/py-collect/path/svn/wccommand.py	Sat Apr 16 20:36:08 2005
@@ -9,7 +9,6 @@
 """
 
 import os, sys, time, re
-from py import path
 import py
 from py.__impl__.path import common
 from py.__impl__.path.svn import cache
@@ -22,7 +21,11 @@
 
     def __new__(cls, wcpath=None):
         self = object.__new__(cls)
-        self.localpath = path.local(wcpath)
+        if isinstance(wcpath, cls): 
+            if wcpath.__class__ == cls: 
+                return wcpath 
+            wcpath = wcpath.localpath 
+        self.localpath = py.path.local(wcpath)
         return self
 
     strpath = property(lambda x: str(x.localpath), None, None, "string path")
@@ -43,7 +46,7 @@
     def svnurl(self):
         """ return current SvnPath for this WC-item. """
         info = self.info()
-        return path.svnurl(info.url)
+        return py.path.svnurl(info.url)
 
     url = property(_geturl, None, None, "url of this WC item")
 
@@ -131,6 +134,7 @@
             ignored and considered always true (because of
             underlying svn semantics.
         """
+        assert rec, "svn cannot remove non-recursively"
         flags = []
         if force:
             flags.append('--force')
@@ -357,14 +361,14 @@
     def get(self, spec):
         return self.localpath.get(spec)
 
-    class Checkers(path.local.Checkers):
+    class Checkers(py.path.local.Checkers):
         def __init__(self, path):
             self.svnwcpath = path
             self.path = path.localpath
         def versioned(self):
             try:
                 s = self.svnwcpath.info()
-            except py.error.ENOENT: 
+            except (py.error.ENOENT, py.error.EEXIST): 
                 return False 
             except py.process.cmdexec.Error, e:
                 if e.err.find('is not a working copy')!=-1:

Modified: py/branch/py-collect/process/cmdexec.py
==============================================================================
--- py/branch/py-collect/process/cmdexec.py	(original)
+++ py/branch/py-collect/process/cmdexec.py	Sat Apr 16 20:36:08 2005
@@ -79,7 +79,14 @@
 
     Note that this method can currently deadlock because
     we don't have WaitForMultipleObjects in the std-python api.
+
+    Further note that the rules for quoting are very special
+    under Windows. Do a HELP CMD in a shell, and tell me if
+    you understand this. For now, I try to do a fix.
     """
+    print "*****", cmd
+    if '"' in cmd and not cmd.startswith('""'):
+        cmd = '"%s"' % cmd
     stdin, stdout, stderr = os.popen3(cmd)
     out = stdout.read()
     err = stderr.read()

Added: py/branch/py-collect/tool/__init__.py
==============================================================================
--- (empty file)
+++ py/branch/py-collect/tool/__init__.py	Sat Apr 16 20:36:08 2005
@@ -0,0 +1 @@
+#

Modified: py/branch/py-collect/xmlobj/html.py
==============================================================================
--- py/branch/py-collect/xmlobj/html.py	(original)
+++ py/branch/py-collect/xmlobj/html.py	Sat Apr 16 20:36:08 2005
@@ -2,16 +2,34 @@
 
 
 """ 
-from py.xml import Namespace
+from py.xml import Namespace, Tag
+from py.__impl__.xmlobj.visit import SimpleUnicodeVisitor 
+
+class HtmlVisitor(SimpleUnicodeVisitor): 
+    def repr_attribute(self, attrs, name): 
+        if name == 'class_':
+            value = getattr(attrs, name) 
+            if value is None: 
+                return
+        return super(HtmlVisitor, self).repr_attribute(attrs, name) 
+
+class HtmlTag(Tag): 
+    def unicode(self, indent=2):
+        l = []
+        HtmlVisitor(l.append, indent, shortempty=False).visit(self) 
+        return u"".join(l) 
 
 # exported plain html namespace 
 class html(Namespace):
+    __tagclass__ = HtmlTag
     __stickyname__ = True 
     __tagspec__ = dict([(x,1) for x in ( 
         "h1,h2,h3,h5,h6,p,b,i,a,div,span,code,"
         "html,head,title,style,table,tr,tt,"
         "td,th,link,img,meta,body,pre,br,ul,"
-        "ol,li,em,form,input,select,option"
+        "ol,li,em,form,input,select,option,"
+        "button,script,colgroup,col,map,area,"
+        "blockquote,dl,dt,dd,strong"
     ).split(',') if x])
 
     class Style(object): 

Modified: py/branch/py-collect/xmlobj/testing/test_html.py
==============================================================================
--- py/branch/py-collect/xmlobj/testing/test_html.py	(original)
+++ py/branch/py-collect/xmlobj/testing/test_html.py	Sat Apr 16 20:36:08 2005
@@ -11,7 +11,12 @@
         class body(html.body): 
             style = html.Style(font_size = "12pt")
     u = unicode(my.body())
-    assert u == '<body style="font-size: 12pt"/>' 
+    assert u == '<body style="font-size: 12pt"></body>' 
+
+def test_class_None(): 
+    t = html.body(class_=None)
+    u = unicode(t) 
+    assert u == '<body></body>'
 
 def test_alternating_style(): 
     alternating = (

Modified: py/branch/py-collect/xmlobj/visit.py
==============================================================================
--- py/branch/py-collect/xmlobj/visit.py	(original)
+++ py/branch/py-collect/xmlobj/visit.py	Sat Apr 16 20:36:08 2005
@@ -6,13 +6,14 @@
 
 class SimpleUnicodeVisitor(object):
     """ recursive visitor to write unicode. """
-    def __init__(self, write, indent=0, curindent=0): 
+    def __init__(self, write, indent=0, curindent=0, shortempty=True): 
         self.write = write
         self.cache = {}
         self.visited = {} # for detection of recursion
         self.indent = indent 
         self.curindent = curindent
         self.parents = []
+        self.shortempty = shortempty  # short empty tags or not 
 
     def visit(self, node): 
         """ dispatcher on node's class/bases name. """
@@ -57,7 +58,11 @@
             self.write(u'</%s>' % tagname) 
             self.curindent -= self.indent 
         else:
-            self.write(u'<%s%s/>' % (tagname, self.attributes(tag)))
+            nameattr = tagname+self.attributes(tag) 
+            if self.shortempty: 
+                self.write(u'<%s/>' % (nameattr,))
+            else: 
+                self.write(u'<%s></%s>' % (nameattr, tagname))
 
     def attributes(self, tag):
         # serialize attributes
@@ -65,14 +70,19 @@
         attrlist.sort() 
         l = []
         for name in attrlist: 
-            if name[0] != '_': 
-                value = getattr(tag.attr, name) 
-                if name.endswith('_'): 
-                    name = name[:-1]
-                l.append(u' %s="%s"' % (name, escape(unicode(value))))
+            res = self.repr_attribute(tag.attr, name)
+            if res is not None: 
+                l.append(res) 
         l.extend(self.getstyle(tag))
         return u"".join(l)
 
+    def repr_attribute(self, attrs, name): 
+        if name[:2] != '__': 
+            value = getattr(attrs, name) 
+            if name.endswith('_'): 
+                name = name[:-1]
+            return u' %s="%s"' % (name, escape(unicode(value)))
+
     def getstyle(self, tag): 
         """ return attribute list suitable for styling. """ 
         try: 



More information about the pytest-commit mailing list