From arigo at codespeak.net Mon Nov 8 18:21:24 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 8 Nov 2004 18:21:24 +0100 (MET) Subject: [py-svn] r7193 - in py/dist/py/path: . extpy local test Message-ID: <20041108172124.3680E5A0DF@thoth.codespeak.net> Author: arigo Date: Mon Nov 8 18:21:23 2004 New Revision: 7193 Modified: py/dist/py/path/common.py py/dist/py/path/extpy/extpy.py py/dist/py/path/local/local.py py/dist/py/path/test/fscommon.py Log: Renamed getmodule() and getcodeobj() to getpymodule() and getpycodeobj(). Modified: py/dist/py/path/common.py ============================================================================== --- py/dist/py/path/common.py (original) +++ py/dist/py/path/common.py Mon Nov 8 18:21:23 2004 @@ -283,14 +283,14 @@ self.copy(target) self.remove() - def getmodule(self): + def getpymodule(self): """resolve this path to a module python object. """ modname = str(self) modname = modname.replace('.', self.sep) try: return sys.modules[modname] except KeyError: - co = self.getcodeobj() + co = self.getpycodeobj() mod = py.std.new.module(modname) mod.__file__ = str(self) #mod.__path__ = [str(self.get('dirname'))] @@ -298,7 +298,7 @@ exec co in mod.__dict__ return mod - def getcodeobj(self): + def getpycodeobj(self): s = self.read() # XXX str(self) should show up somewhere in the code's filename return py.magic.dyncode.compile2(s) Modified: py/dist/py/path/extpy/extpy.py ============================================================================== --- py/dist/py/path/extpy/extpy.py (original) +++ py/dist/py/path/extpy/extpy.py Mon Nov 8 18:21:23 2004 @@ -84,7 +84,7 @@ the root along the modpath. """ rest = filter(None, self.modpath.split('.')) - target = self.getmodule() + target = self.getpymodule() for name in rest: try: target = getattr(target, name) @@ -92,11 +92,11 @@ raise py.path.NotFound("%r in %s" %(self.modpath, self)) return target - def getmodule(self): + def getpymodule(self): if hasattr(self.root, 'resolve'): return self.root.resolve() else: - return self.root.getmodule() + return self.root.getpymodule() def relto(self, otherpath): if self.root == otherpath.root: Modified: py/dist/py/path/local/local.py ============================================================================== --- py/dist/py/path/local/local.py (original) +++ py/dist/py/path/local/local.py Mon Nov 8 18:21:23 2004 @@ -342,7 +342,7 @@ """ return string representation of the Path. """ return self.strpath - def getcodeobj(self): + def getpycodeobj(self): dotpy = self.check(ext='.py') if dotpy: my_magic = py.std.imp.get_magic() Modified: py/dist/py/path/test/fscommon.py ============================================================================== --- py/dist/py/path/test/fscommon.py (original) +++ py/dist/py/path/test/fscommon.py Mon Nov 8 18:21:23 2004 @@ -184,8 +184,8 @@ assert dest.join('moved', 'somefile').read() == '42' assert not source.check() - def test_getmodule(self): - obj = self.root.join('execfile').getmodule() + def test_getpymodule(self): + obj = self.root.join('execfile').getpymodule() assert obj.x == 42 def test_not_has_resolve(self): From hpk at codespeak.net Tue Nov 9 16:28:57 2004 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 9 Nov 2004 16:28:57 +0100 (MET) Subject: [py-svn] r7199 - py/dist/py/execnet Message-ID: <20041109152857.7CA415A5E8@thoth.codespeak.net> Author: hpk Date: Tue Nov 9 16:28:57 2004 New Revision: 7199 Modified: py/dist/py/execnet/channel.py Log: correction to the doc strings for channels Modified: py/dist/py/execnet/channel.py ============================================================================== --- py/dist/py/execnet/channel.py (original) +++ py/dist/py/execnet/channel.py Tue Nov 9 16:28:57 2004 @@ -44,7 +44,7 @@ self._closeevent.set() def newchannel(self): - """ return a new channel whose life cycle depends on this channel. """ + """ return a new channel. """ chan = self.gateway.channelfactory.new() return chan @@ -59,7 +59,7 @@ """ wait until this channel is closed. Note that a closed channel may still hold items that can be received or send. Also note that exceptions from the other side will be - reraised as gateway.ExecutionFailed exceptions containing + reraised as gateway.RemoteError exceptions containing a textual representation of the remote traceback. """ self._closeevent.wait(timeout=timeout) @@ -83,7 +83,7 @@ """receives an item that was sent from the other side, possibly blocking if there is none. Note that exceptions from the other side will be - reraised as gateway.ExecutionFailed exceptions containing + reraised as gateway.RemoteError exceptions containing a textual representation of the remote traceback. """ x = self._items.get() From arigo at codespeak.net Tue Nov 9 17:33:45 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Tue, 9 Nov 2004 17:33:45 +0100 (MET) Subject: [py-svn] r7201 - py/dist/py/magic Message-ID: <20041109163345.8EE4E5A5E8@thoth.codespeak.net> Author: arigo Date: Tue Nov 9 17:33:45 2004 New Revision: 7201 Modified: py/dist/py/magic/dyncode.py Log: After a lot of not-understanding-anything around, I've found out that py.path.local().getpyobj() suffers from a conflict with py.magic.dyncode.invoke(). Indeed, when this magic is invoked -- which apparently occurs under *some* but not all conditions when running py.test, hence my deep perplexity -- then compile() is redefined to be dyncode's own. This compile() is not working properly if the resulting code object really comes from a filesystem file and will be marshalled, because then the .pyc file contains a broken co_filename! When, the next time, the .pyc file is reloaded, its co_filename is now out of sync with dyncode's cache and you get strange errors... This is a hack to let dyncode.compile() fall back to the built-in compile() if the filename appears to name an actual file. Not sure if it's the best solution. No attempt is made to check that "source" is actually the content of this file. Modified: py/dist/py/magic/dyncode.py ============================================================================== --- py/dist/py/magic/dyncode.py (original) +++ py/dist/py/magic/dyncode.py Tue Nov 9 17:33:45 2004 @@ -137,7 +137,10 @@ _dynfileregistry[filename] = origin, lines, co return co +builtin_compile = compile def compile(source, filename, mode='exec', flag=generators.compiler_flag): + if os.path.exists(filename): + return builtin_compile(source, filename, mode, flag) frame = inspect.currentframe(1) # the caller return newcompile(frame, source, mode, flag) From arigo at codespeak.net Tue Nov 9 17:57:57 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Tue, 9 Nov 2004 17:57:57 +0100 (MET) Subject: [py-svn] r7202 - in py/dist/py: magic path path/test Message-ID: <20041109165757.749505A5E8@thoth.codespeak.net> Author: arigo Date: Tue Nov 9 17:57:56 2004 New Revision: 7202 Modified: py/dist/py/magic/autopath.py py/dist/py/path/common.py py/dist/py/path/test/fscommon.py Log: The getpymodule() logic discussed on py-dev, with tests. Modified: py/dist/py/magic/autopath.py ============================================================================== --- py/dist/py/magic/autopath.py (original) +++ py/dist/py/magic/autopath.py Tue Nov 9 17:57:56 2004 @@ -1,5 +1,6 @@ import os, sys -from py.path import local +from py.path import local +from py.__impl__.path.common import PathStr def autopath(globs=None, basefile='__init__.py'): """ return the (local) path of the "current" file pointed to by globals @@ -19,9 +20,13 @@ raise ValueError, "cannot compute autopath in interactive mode" __file__ = os.path.abspath(sys.argv[0]) - ret = local(__file__) - if ret.ext in ('.pyc', '.pyo'): - ret = ret.new(ext='.py') + custom__file__ = isinstance(__file__, PathStr) + if custom__file__: + ret = __file__.__path__ + else: + ret = local(__file__) + if ret.ext in ('.pyc', '.pyo'): + ret = ret.new(ext='.py') current = pkgdir = ret.dirpath() while 1: if basefile in current: @@ -29,7 +34,7 @@ current = current.dirpath() if pkgdir != current: continue - elif str(current) not in sys.path: + elif not custom__file__ and str(current) not in sys.path: sys.path.insert(0, str(current)) break ret.pkgdir = pkgdir Modified: py/dist/py/path/common.py ============================================================================== --- py/dist/py/path/common.py (original) +++ py/dist/py/path/common.py Tue Nov 9 17:57:56 2004 @@ -292,8 +292,7 @@ except KeyError: co = self.getpycodeobj() mod = py.std.new.module(modname) - mod.__file__ = str(self) - #mod.__path__ = [str(self.get('dirname'))] + mod.__file__ = PathStr(self) sys.modules[modname] = mod exec co in mod.__dict__ return mod @@ -303,3 +302,64 @@ # XXX str(self) should show up somewhere in the code's filename return py.magic.dyncode.compile2(s) + +class PathStr(str): + def __init__(self, path): + global old_import_hook + self.__path__ = path + if old_import_hook is None: + import __builtin__ + old_import_hook = __builtin__.__import__ + __builtin__.__import__ = custom_import_hook + +def relativeimport(p, name, parent=None): + names = name.split('.') + last_list = [False] * (len(names)-1) + [True] + modules = [] + for name, is_last in zip(names, last_list): + if hasattr(parent, name): + # shortcut if there is already the correct name + # in the parent package + submodule = getattr(parent, name) + else: + if is_last and p.new(basename=name+'.py').check(): + p = p.new(basename=name+'.py') + else: + p = p.new(basename=name).join('__init__.py') + if not p.check(): + return None # not found + submodule = p.getpymodule() + if parent is not None: + setattr(parent, name, submodule) + modules.append(submodule) + parent = submodule + return modules # success + + +old_import_hook = None + +def custom_import_hook(name, glob=None, loc=None, fromlist=None): + __file__ = glob and glob.get('__file__') + if isinstance(__file__, PathStr): + # try to perform a relative import + # for cooperation with py.magic.autopath, first look in the pkgdir + modules = None + if hasattr(__file__.__path__, 'pkgdir'): + modules = relativeimport(__file__.__path__.pkgdir, name) + if not modules: + modules = relativeimport(__file__.__path__, name) + if modules: + if fromlist: + submodule = modules[-1] # innermost submodule + # try to import submodules named in the 'fromlist' if the + # 'submodule' is a package + p = submodule.__file__.__path__ + if p.check(basename='__init__.py'): + for name in fromlist: + relativeimport(p, name, parent=submodule) + # failures are fine + return submodule + else: + return modules[0] # outermost package + # fall-back + return old_import_hook(name, glob, loc, fromlist) Modified: py/dist/py/path/test/fscommon.py ============================================================================== --- py/dist/py/path/test/fscommon.py (original) +++ py/dist/py/path/test/fscommon.py Tue Nov 9 17:57:56 2004 @@ -22,6 +22,21 @@ otherdir = path.ensure('otherdir', dir=1) otherdir.ensure('__init__.py') + module_a = otherdir.ensure('a.py') + module_a.write('from b import stuff as result\n') + module_b = otherdir.ensure('b.py') + module_b.write('stuff="got it"\n') + module_c = otherdir.ensure('c.py') + module_c.write('''import py; py.magic.autopath() +import otherdir.a +value = otherdir.a.result +''') + module_d = otherdir.ensure('d.py') + module_d.write('''import py; py.magic.autopath() +from otherdir import a +value2 = a.result +''') + class CommonFSTests(common.CommonPathTests): root = None # subclasses have to provide a current 'root' attribute @@ -193,3 +208,22 @@ # py.path.extpy assert not hasattr(self.root, 'resolve') + def test_getpymodule_a(self): + otherdir = self.root.join('otherdir') + mod = otherdir.join('a.py').getpymodule() + assert mod.result == "got it" + + def test_getpymodule_b(self): + otherdir = self.root.join('otherdir') + mod = otherdir.join('b.py').getpymodule() + assert mod.stuff == "got it" + + def test_getpymodule_c(self): + otherdir = self.root.join('otherdir') + mod = otherdir.join('c.py').getpymodule() + assert mod.value == "got it" + + def test_getpymodule_d(self): + otherdir = self.root.join('otherdir') + mod = otherdir.join('d.py').getpymodule() + assert mod.value2 == "got it" From hpk at codespeak.net Thu Nov 11 00:12:07 2004 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 11 Nov 2004 00:12:07 +0100 (MET) Subject: [py-svn] r7206 - py/dist/py/path Message-ID: <20041110231207.37FD05A652@thoth.codespeak.net> Author: hpk Date: Thu Nov 11 00:12:05 2004 New Revision: 7206 Modified: py/dist/py/path/common.py Log: set __path__ so that py itself can be getpymodule()ed (although it is questionable that this should actually happen ...) Modified: py/dist/py/path/common.py ============================================================================== --- py/dist/py/path/common.py (original) +++ py/dist/py/path/common.py Thu Nov 11 00:12:05 2004 @@ -293,6 +293,8 @@ co = self.getpycodeobj() mod = py.std.new.module(modname) mod.__file__ = PathStr(self) + if self.basename == '__init__.py': + mod.__path__ = [str(self.dirpath())] sys.modules[modname] = mod exec co in mod.__dict__ return mod From hpk at codespeak.net Sun Nov 14 14:31:20 2004 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 14 Nov 2004 14:31:20 +0100 (MET) Subject: [py-svn] r7229 - in py/dist/py/path: . local svn Message-ID: <20041114133120.A36E05AA0E@thoth.codespeak.net> Author: hpk Date: Sun Nov 14 14:31:19 2004 New Revision: 7229 Modified: py/dist/py/path/common.py py/dist/py/path/local/local.py py/dist/py/path/svn/svncommon.py Log: moved the dirpath() implementation to the FSPathPase because it is rather generic. Modified: py/dist/py/path/common.py ============================================================================== --- py/dist/py/path/common.py (original) +++ py/dist/py/path/common.py Sun Nov 14 14:31:19 2004 @@ -235,6 +235,12 @@ def __div__(self, other): return self.join(str(other)) + def dirpath(self, *args): + """ return the directory Path of the current Path joined + with any given path arguments. + """ + return self.new(basename='').join(*args) + def ext(self): return self.get('ext') ext = property(ext, None, None, 'extension part of path') Modified: py/dist/py/path/local/local.py ============================================================================== --- py/dist/py/path/local/local.py (original) +++ py/dist/py/path/local/local.py Sun Nov 14 14:31:19 2004 @@ -171,13 +171,6 @@ def __eq__(self, other): return str(self) == str(other) - def dirpath(self, *args): - x = self.new(basename='') - if args: - return x.join(*args) - else: - return x - def open(self, mode='r'): """ return an opened file with the given mode. """ return callex(open, self.strpath, mode) Modified: py/dist/py/path/svn/svncommon.py ============================================================================== --- py/dist/py/path/svn/svncommon.py (original) +++ py/dist/py/path/svn/svncommon.py Sun Nov 14 14:31:19 2004 @@ -115,10 +115,6 @@ newpath = self.__class__(self.sep.join(parts), self.rev) return newpath - def dirpath(self, *args): - """ return the directory Path of the current Path. """ - return self.new(basename='').join(*args) - def propget(self, name): """ return the content of the given property. """ value = self._propget(name) From hpk at codespeak.net Tue Nov 16 17:53:52 2004 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 16 Nov 2004 17:53:52 +0100 (MET) Subject: [py-svn] r7299 - py/dist/py/path/local Message-ID: <20041116165352.CB42A5AF43@thoth.codespeak.net> Author: hpk Date: Tue Nov 16 17:53:52 2004 New Revision: 7299 Modified: py/dist/py/path/local/test_local.py Log: fixed the choice of binaries somewhat Modified: py/dist/py/path/local/test_local.py ============================================================================== --- py/dist/py/path/local/test_local.py (original) +++ py/dist/py/path/local/test_local.py Tue Nov 16 17:53:52 2004 @@ -107,7 +107,7 @@ assert tmpdir.join('test2') == new def test_chdir(self): - tmpdir = self.tmpdir + tmpdir = self.tmpdir.realpath() old = local() try: res = tmpdir.chdir() @@ -140,7 +140,7 @@ disabled = py.std.sys.platform == 'win32' def test_sysfind(self): - x = py.path.local.sysfind('ls') + x = py.path.local.sysfind('test') assert x.check(file=1) py.test.raises(py.path.NotFound, """ py.path.local.sysfind('jaksdkasldqwe') @@ -173,7 +173,7 @@ assert out.find(x.basename) != -1 def test_sysexec_failing(self): - x = py.path.local.sysfind('ls') + x = py.path.local.sysfind('false') py.test.raises(py.process.cmdexec.Error, """ x.sysexec('aksjdkasjd') """) From hpk at codespeak.net Tue Nov 16 17:56:12 2004 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 16 Nov 2004 17:56:12 +0100 (MET) Subject: [py-svn] r7300 - py/dist/py/path/local Message-ID: <20041116165612.4D2FA5AF45@thoth.codespeak.net> Author: hpk Date: Tue Nov 16 17:56:11 2004 New Revision: 7300 Modified: py/dist/py/path/local/test_posix.py Log: use realpath() for temp dirs once more ... Modified: py/dist/py/path/local/test_posix.py ============================================================================== --- py/dist/py/path/local/test_posix.py (original) +++ py/dist/py/path/local/test_posix.py Tue Nov 16 17:56:11 2004 @@ -81,7 +81,7 @@ tmpdir.remove(rec=1) def test_symlink_remove(self): - tmpdir = local.mkdtemp() + tmpdir = local.mkdtemp().realpath() try: linkpath = tmpdir.join('test') linkpath.mksymlinkto(linkpath) # point to itself From ianb at codespeak.net Wed Nov 17 19:01:06 2004 From: ianb at codespeak.net (ianb at codespeak.net) Date: Wed, 17 Nov 2004 19:01:06 +0100 (MET) Subject: [py-svn] r7350 - in py/dist/py/test/test/import_test: . package Message-ID: <20041117180106.304C05B07A@thoth.codespeak.net> Author: ianb Date: Wed Nov 17 19:01:05 2004 New Revision: 7350 Added: py/dist/py/test/test/import_test/ py/dist/py/test/test/import_test/package/ py/dist/py/test/test/import_test/package/__init__.py py/dist/py/test/test/import_test/package/absolute_import_shared_lib.py py/dist/py/test/test/import_test/package/module_that_imports_shared_lib.py py/dist/py/test/test/import_test/package/shared_lib.py py/dist/py/test/test/import_test/package/test_import.py Log: A test case for importing directly and indirectly from a test script, as well as relative and absolute imports. per email "Import problems under py.test" Added: py/dist/py/test/test/import_test/package/__init__.py ============================================================================== --- (empty file) +++ py/dist/py/test/test/import_test/package/__init__.py Wed Nov 17 19:01:05 2004 @@ -0,0 +1 @@ +# Added: py/dist/py/test/test/import_test/package/absolute_import_shared_lib.py ============================================================================== --- (empty file) +++ py/dist/py/test/test/import_test/package/absolute_import_shared_lib.py Wed Nov 17 19:01:05 2004 @@ -0,0 +1 @@ +from package import shared_lib Added: py/dist/py/test/test/import_test/package/module_that_imports_shared_lib.py ============================================================================== --- (empty file) +++ py/dist/py/test/test/import_test/package/module_that_imports_shared_lib.py Wed Nov 17 19:01:05 2004 @@ -0,0 +1 @@ +import shared_lib Added: py/dist/py/test/test/import_test/package/shared_lib.py ============================================================================== --- (empty file) +++ py/dist/py/test/test/import_test/package/shared_lib.py Wed Nov 17 19:01:05 2004 @@ -0,0 +1,3 @@ +""" +Just a dummy module +""" Added: py/dist/py/test/test/import_test/package/test_import.py ============================================================================== --- (empty file) +++ py/dist/py/test/test/import_test/package/test_import.py Wed Nov 17 19:01:05 2004 @@ -0,0 +1,39 @@ +import sys +import os + +def setup_module(mod): + sys.path.append(os.path.dirname(os.path.dirname(mod.__file__))) + +def teardown_module(mod): + sys.path.remove(os.path.dirname(os.path.dirname(mod.__file__))) + +def test_import(): + global shared_lib, module_that_imports_shared_lib + import shared_lib + from package import shared_lib as shared_lib2 + import module_that_imports_shared_lib + import absolute_import_shared_lib + all_modules = [ + ('shared_lib', shared_lib), + ('shared_lib2', shared_lib2), + ('module_that_imports_shared_lib', + module_that_imports_shared_lib.shared_lib), + ('absolute_import_shared_lib', + absolute_import_shared_lib.shared_lib), + ] + bad_matches = [] + while all_modules: + name1, mod1 = all_modules[0] + all_modules = all_modules[1:] + for name2, mod2 in all_modules: + if mod1 is not mod2: + bad_matches.append((name1, mod1, name2, mod2)) + for name1, mod1, name2, mod2 in bad_matches: + print "These modules should be identical:" + print " %s:" % name1 + print " ", mod1 + print " %s:" % name2 + print " ", mod2 + print + if bad_matches: + assert False From ianb at codespeak.net Wed Nov 17 19:31:45 2004 From: ianb at codespeak.net (ianb at codespeak.net) Date: Wed, 17 Nov 2004 19:31:45 +0100 (MET) Subject: [py-svn] r7352 - py/dist/py/test/test/import_test/package Message-ID: <20041117183145.0F0BB5B07B@thoth.codespeak.net> Author: ianb Date: Wed Nov 17 19:31:41 2004 New Revision: 7352 Modified: py/dist/py/test/test/import_test/package/test_import.py Log: Made test executable so you can try it under normal import conditions Modified: py/dist/py/test/test/import_test/package/test_import.py ============================================================================== --- py/dist/py/test/test/import_test/package/test_import.py (original) +++ py/dist/py/test/test/import_test/package/test_import.py Wed Nov 17 19:31:41 2004 @@ -1,11 +1,19 @@ import sys import os -def setup_module(mod): - sys.path.append(os.path.dirname(os.path.dirname(mod.__file__))) +def setup_module(mod=None): + if mod is None: + f = __file__ + else: + f = mod.__file__ + sys.path.append(os.path.dirname(os.path.dirname(f))) -def teardown_module(mod): - sys.path.remove(os.path.dirname(os.path.dirname(mod.__file__))) +def teardown_module(mod=None): + if mod is None: + f = __file__ + else: + f = mod.__file__ + sys.path.remove(os.path.dirname(os.path.dirname(f))) def test_import(): global shared_lib, module_that_imports_shared_lib @@ -37,3 +45,8 @@ print if bad_matches: assert False + +if __name__ == "__main__": + setup_module() + test_import + teardown_module() From arigo at codespeak.net Mon Nov 22 14:06:41 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 22 Nov 2004 14:06:41 +0100 (MET) Subject: [py-svn] r7559 - py/dist/py/path/local Message-ID: <20041122130641.894E55AF3E@thoth.codespeak.net> Author: arigo Date: Mon Nov 22 14:06:41 2004 New Revision: 7559 Modified: py/dist/py/path/local/local.py Log: make_numbered_dirs() ignores errors (typically permission denied) when trying to remove old 'usession-%d' directories. Modified: py/dist/py/path/local/local.py ============================================================================== --- py/dist/py/path/local/local.py (original) +++ py/dist/py/path/local/local.py Mon Nov 22 14:06:41 2004 @@ -464,7 +464,10 @@ for path in rootdir.listdir(): num = parse_num(path) if num is not None and num <= (maxnum - keep): - path.remove(rec=1) + try: + path.remove(rec=1) + except error.Invalid: + pass return udir make_numbered_dir = classmethod(make_numbered_dir) From arigo at codespeak.net Mon Nov 22 14:10:46 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 22 Nov 2004 14:10:46 +0100 (MET) Subject: [py-svn] r7560 - py/dist/py/path Message-ID: <20041122131046.089115AF3E@thoth.codespeak.net> Author: arigo Date: Mon Nov 22 14:10:46 2004 New Revision: 7560 Modified: py/dist/py/path/error.py Log: Added support for the 'Operation not permitted' error, which is not to be confused with 'Access denied'. Modified: py/dist/py/path/error.py ============================================================================== --- py/dist/py/path/error.py (original) +++ py/dist/py/path/error.py Mon Nov 22 14:10:46 2004 @@ -25,12 +25,15 @@ pass class NestedLink(Invalid): pass +class NotPermitted(Invalid): + pass #__________________________________________________________ # XXX use module errno _errnoclass = {} for errno, name in { + 1 : 'NotPermitted', 2 : 'FileNotFound', 3 : 'FileExists', 13 : 'PermissionDenied', From hpk at codespeak.net Fri Nov 26 15:48:12 2004 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 26 Nov 2004 15:48:12 +0100 (MET) Subject: [py-svn] r7677 - py/dist/py/test/report/text Message-ID: <20041126144812.E6D2E5A0FE@thoth.codespeak.net> Author: hpk Date: Fri Nov 26 15:48:12 2004 New Revision: 7677 Modified: py/dist/py/test/report/text/summary.py Log: allow string exceptions (ugh :-) Modified: py/dist/py/test/report/text/summary.py ============================================================================== --- py/dist/py/test/report/text/summary.py (original) +++ py/dist/py/test/report/text/summary.py Fri Nov 26 15:48:12 2004 @@ -108,7 +108,9 @@ def repr_failure_result(self, res): cls = res.excinfo[0] - if issubclass(cls, Item.ExceptionFailure): + if isinstance(cls, str): + self.out.line("string-exception: %s" % cls) + elif issubclass(cls, Item.ExceptionFailure): if not res.innerexcinfo: self.out.line("%s <<< DID NOT RAISE" % (res.expr,)) self.out.line("expected: %r" % (res.expected,)) From faassen at codespeak.net Fri Nov 26 21:39:41 2004 From: faassen at codespeak.net (faassen at codespeak.net) Date: Fri, 26 Nov 2004 21:39:41 +0100 (MET) Subject: [py-svn] r7682 - py/dist/doc Message-ID: <20041126203941.1C6225A70E@thoth.codespeak.net> Author: faassen Date: Fri Nov 26 21:39:40 2004 New Revision: 7682 Modified: py/dist/doc/test.txt Log: Rewrote the starting point to ease the way for newbies. Modified: py/dist/doc/test.txt ============================================================================== --- py/dist/doc/test.txt (original) +++ py/dist/doc/test.txt Fri Nov 26 21:39:40 2004 @@ -8,28 +8,36 @@ starting point: ``py.test`` command line tool ============================================= -``py.test`` is the command line tool to run tests. If you -provide it a file that contains functions or methods starting -with ``test_`` then it will run those methods. Assertions -about test outcomes are done via the standard ``assert`` -statement. +First, see `getting started`_ for how to install the 'py.test' script +on your system. -The existence of a standalone ``py.test`` tool allows -to write tests without any boilerplate:: +``py.test`` is the command line tool to run tests. You can supply it +with any Python module by passing it as an argument:: - # content of test_sample.py - def test_answer(): - assert 42 == 43 + py.test test_sample.py -In contrast to ``unittest.py`` limitations you -can have test functions as well as test methods. -You can execute the test by invoking:: +``py.test`` looks for any functions and methods in the module that +start with with ``test_`` and will then run those methods. Assertions +about test outcomes are done via the standard ``assert`` statement. - py.test test_sample.py +This means you can write tests without any boilerplate:: -See `getting started`_ for how to install the 'py.test' script -on your system. + # content of test_sample.py + def test_answer(): + assert 42 == 43 +As you can see, you can have test functions as well as test +methods. This in contrast to the Python standard library's +``unittest.py``. + +You can use ``py.test`` to run all tests in a directory structure by +invoking it without any arguments:: + + 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. Basic Features of ``py.test`` ============================= From hpk at codespeak.net Fri Nov 26 21:52:07 2004 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 26 Nov 2004 21:52:07 +0100 (MET) Subject: [py-svn] r7683 - py/dist/py/test/test/import_test/package Message-ID: <20041126205207.DF5F95A70E@thoth.codespeak.net> Author: hpk Date: Fri Nov 26 21:52:07 2004 New Revision: 7683 Added: py/dist/py/test/test/import_test/package/bugtest_import.py - copied unchanged from r7676, py/dist/py/test/test/import_test/package/test_import.py Removed: py/dist/py/test/test/import_test/package/test_import.py Log: moved the failing import test to become a "bugtest_*.py". So it doesn't disturb normal testing. Of course, the real thing is to fix the problem but it's not completly clear yet how to best do it. (and i am still recovering from the pypy sprint and have some other stuff to do currently). Deleted: /py/dist/py/test/test/import_test/package/test_import.py ============================================================================== --- /py/dist/py/test/test/import_test/package/test_import.py Fri Nov 26 21:52:07 2004 +++ (empty file) @@ -1,52 +0,0 @@ -import sys -import os - -def setup_module(mod=None): - if mod is None: - f = __file__ - else: - f = mod.__file__ - sys.path.append(os.path.dirname(os.path.dirname(f))) - -def teardown_module(mod=None): - if mod is None: - f = __file__ - else: - f = mod.__file__ - sys.path.remove(os.path.dirname(os.path.dirname(f))) - -def test_import(): - global shared_lib, module_that_imports_shared_lib - import shared_lib - from package import shared_lib as shared_lib2 - import module_that_imports_shared_lib - import absolute_import_shared_lib - all_modules = [ - ('shared_lib', shared_lib), - ('shared_lib2', shared_lib2), - ('module_that_imports_shared_lib', - module_that_imports_shared_lib.shared_lib), - ('absolute_import_shared_lib', - absolute_import_shared_lib.shared_lib), - ] - bad_matches = [] - while all_modules: - name1, mod1 = all_modules[0] - all_modules = all_modules[1:] - for name2, mod2 in all_modules: - if mod1 is not mod2: - bad_matches.append((name1, mod1, name2, mod2)) - for name1, mod1, name2, mod2 in bad_matches: - print "These modules should be identical:" - print " %s:" % name1 - print " ", mod1 - print " %s:" % name2 - print " ", mod2 - print - if bad_matches: - assert False - -if __name__ == "__main__": - setup_module() - test_import - teardown_module() From faassen at codespeak.net Fri Nov 26 22:22:07 2004 From: faassen at codespeak.net (faassen at codespeak.net) Date: Fri, 26 Nov 2004 22:22:07 +0100 (MET) Subject: [py-svn] r7685 - py/dist/doc Message-ID: <20041126212207.2E1B65B1DE@thoth.codespeak.net> Author: faassen Date: Fri Nov 26 22:22:06 2004 New Revision: 7685 Modified: py/dist/doc/test.txt Log: Lots of clarifications and language editing. Modified: py/dist/doc/test.txt ============================================================================== --- py/dist/doc/test.txt (original) +++ py/dist/doc/test.txt Fri Nov 26 22:22:06 2004 @@ -45,23 +45,22 @@ assert with the ``assert`` statement ------------------------------------ -Writing assertions is very simple and this is one of py.tests +Writing assertions is very simple and this is one of py.test's most noticeable features, as you can use the ``assert`` statement with arbitrary expressions. For example you can write the following in your tests:: assert hasattr(x, 'attribute') -to state that your object has a certain ``attribute`` -and in case this assertion fails the test ``reporter`` -will provide you with a very helpful analysis and a -clean traceback. +to state that your object has a certain ``attribute``. In case this +assertion fails the test ``reporter`` will provide you with a very +helpful analysis and a clean traceback. Note that in order to display helpful analysis of a failing ``assert`` statement some magic takes place behind the scenes. For now, you only need to know that if something looks strange or you suspect a bug in that -*behind-the-scenes-magic* you may turn of the magic by +*behind-the-scenes-magic* you may turn off the magic by providing the ``--nomagic`` option. how to write assertions about execptions @@ -73,10 +72,11 @@ py.test.raises(Exception, func, *args, **kwargs) py.test.raises(Exception, "func(*args, **kwargs)") -both of which execute the given function with args and kwargs -and asserts that the given ``Exception`` is raised. Again, -in case of the possible failures (*no exception* or *wrong -exception*) the reporter provides you with helpful output. +both of which execute the given function with args and kwargs and +asserts that the given ``Exception`` is raised. The reporter will +provide you with helpful output in case of failures such as *no +exception* or *wrong exception*. + automatic collection of tests on all levels ------------------------------------------- @@ -86,15 +86,15 @@ 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`_ -for some implemenetation details). +for some implementation details). testing starts immediately -------------------------- Testing starts as soon as the first ``test item``, -usually a test function or method is collected. -There is no need to first collect all tests -before a test starts. +usually a test function or method, is collected. +There is no need to wait for a test collection phase to finish +before the actual testing process can start. no interference with cmdline utilities -------------------------------------- @@ -106,42 +106,64 @@ debug with the ``print`` statement ---------------------------------- -By default, the py lib catches stdout/stderr while executing -tests. This output is only displayed when the test fails, -otherwise you will not see it. - +By default, ``py.test`` catches text written to stdout/stderr during +the execution of each individual test. This output will only be +displayed however if the test fails; you will not see it +otherwise. This allows you to put debugging print statements in your +code without being overwhelmed by all the output that might be +generated by tests that do not fail. + +Each failing test that produced output during the running of the test +will have its output displayed in the ``recorded stdout`` section. + +The catching of stdout/stderr output can be disabled using the ``-S`` +option to the ``py.test`` runner. Any output will in this case be +displayed as soon as it is generated. order of execution is guaranteed -------------------------------- -Great care is taken that all tests run in the order in which +Great care is taken to make sure that all tests run in the order in which they appear in the files. If you run tests multiple times you will find that they execute in exactly the same order within -each file. This allows multi-stage tests where you can -rely on your test to iteratively build up a test structure. +each file. + +This allows multi-stage tests where you can rely on your test to +iteratively build up a test structure. It also makes it easier to find +failing tests. useful tracebacks, recursion detection -------------------------------------- -You will at some point notice that a lot of care is taken -to present nice tracebacks. Tracebacks usually start -with your failing test function and they will abort early -in case of recursion with a statement stating the -recursion. If you run ``example/test/failure_demo.py`` you -will see a variety of 40 tracebacks shown for different -failure situations. +A lot of care is taken to present nice tracebacks in case of test +failure. Try:: + + py.test example/test/failure_demo.py + +to see a variety of 40 tracebacks, each tailored to a different +failure situation. + +``py.test`` uses the same order for presenting tracebacks as Python +itself: the outer function is shown first, and the most recent call is +shown last. Similarly, a ``py.test`` traceback starts with your +failing test function and then works its way downwards. If the maximum +recursion depth has been exceeded during the running of a test, for +instance because of infinite recursion, ``py.test`` will indicate +where in the code the recursion was taking place. no inheritance requirement -------------------------- -Classes are recognized by their leading ``Test`` name. -You don't need to inherit some base class. This allows -to intermingle tests with an application level object. +Test classes are recognized by their leading ``Test`` name. Unlike +``unitest.py``, you don't need to inherit from some base class to make +them be found by the test runner. Besides being easier, it also allows +you to write test classes that subclass from application level +classes. -disabling a class of tests --------------------------- +disabling a test class +---------------------- -If you want to disable a complete class of tests you +If you want to disable a complete test class you can set the class-level attribute ``disabled``. For example, in order to avoid running some tests on Win32:: @@ -154,49 +176,59 @@ Managing test state across test modules, classes and methods ------------------------------------------------------------ -Often you want to create some test files, db-connections or -other state in order to run tests in a certain environment. -With ``py.test`` there are three scopes for which you can -provide hooks to manage such state. These hooks will get -called by the driver:: +Often you want to create some files, database connections or other +state in order to run tests in a certain environment. With +``py.test`` there are three scopes for which you can provide hooks to +manage such state. Again, ``py.test`` will detect these hooks in +modules on a name basis. The following module-level hooks will +automatically be called by the driver:: def setup_module(module): - """ setup up any objects specific to the execution + """ setup up any state specific to the execution of the given module. """ + def teardown_module(module): - """ teardown any objects that were previously setup + """ teardown any state that was previously setup with a setup_module method. """ +The following hooks are available for test classes:: + def setup_class(cls): - """ setup up any objects specific to the execution + """ setup up any state specific to the execution of the given class (which usually contains tests). """ + def teardown_class(cls): - """ teardown any objects that were previously setup - with a call to setup_class + """ teardown any state that was previously setup + with a call to setup_class. """ def setup_method(self, method): - """ setup up objects tied to the execution of the given + """ setup up any state tied to the execution of the given method in a class. setup_method is invoked for every test method of a class. """ + def teardown_method(self, method): - """ teardown any objects that were previously setup + """ teardown any state that was previously setup with a setup_method call. """ -While the test driver guarantees that for every ``setup`` a -corresponding ``teardown`` will be invoked (if it exists) it -does *not* guarantee that it only happens once. For example, -the driver might decide to call the ``setup_module`` / -``teardown_module`` pair more than once during the execution -of a test module. - -Note also that all setup/teardown methods are optional. You could -have a ``setup_module`` but no ``teardown_module`` and the other way round. +The last two hooks, ``setup_method`` and ``teardown_method``, are +equivalent to ``setUp`` and ``tearDown`` in the Python standard +library's ``unitest`` module. + +All setup/teardown methods are optional. You could have a +``setup_module`` but no ``teardown_module`` and the other way round. + +Note that while the test driver guarantees that for every ``setup`` a +corresponding ``teardown`` will be invoked (if it exists) it does +*not* guarantee that any ``setup`` is called only happens once. For +example, the driver might decide to call the ``setup_module`` / +``teardown_module`` pair more than once during the execution of a test +module. Working Examples ================ From hpk at codespeak.net Fri Nov 26 23:03:24 2004 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 26 Nov 2004 23:03:24 +0100 (MET) Subject: [py-svn] r7686 - py/dist/doc Message-ID: <20041126220324.835E35B1B9@thoth.codespeak.net> Author: hpk Date: Fri Nov 26 23:03:23 2004 New Revision: 7686 Modified: py/dist/doc/test.txt Log: hum, apparently the failure_demo.py contains only 17 tracebacks and not 40, Don't ask me why i said 40 earlier :-) Modified: py/dist/doc/test.txt ============================================================================== --- py/dist/doc/test.txt (original) +++ py/dist/doc/test.txt Fri Nov 26 23:03:23 2004 @@ -140,7 +140,7 @@ py.test example/test/failure_demo.py -to see a variety of 40 tracebacks, each tailored to a different +to see a variety of 17 tracebacks, each tailored to a different failure situation. ``py.test`` uses the same order for presenting tracebacks as Python From hpk at codespeak.net Sun Nov 28 22:48:53 2004 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Nov 2004 22:48:53 +0100 (MET) Subject: [py-svn] r7695 - py/dist/py/path Message-ID: <20041128214853.A2EFF5A8B2@thoth.codespeak.net> Author: hpk Date: Sun Nov 28 22:48:52 2004 New Revision: 7695 Modified: py/dist/py/path/common.py Log: ups, this is what happens when you always run py.test in stdout-capturing mode ... Modified: py/dist/py/path/common.py ============================================================================== --- py/dist/py/path/common.py (original) +++ py/dist/py/path/common.py Sun Nov 28 22:48:52 2004 @@ -132,8 +132,6 @@ """ last = None for x, y in zip(self.parts(), other.parts()): - print "x", x - print "y", y if x != y: return last last = x From hpk at codespeak.net Sun Nov 28 23:57:42 2004 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Nov 2004 23:57:42 +0100 (MET) Subject: [py-svn] r7696 - py/dist/py/path/local Message-ID: <20041128225742.16E635A8B2@thoth.codespeak.net> Author: hpk Date: Sun Nov 28 23:57:41 2004 New Revision: 7696 Modified: py/dist/py/path/local/local.py py/dist/py/path/local/test_posix.py Log: fix relto() to work when given a path with a trailing '/' or the '/' itself. Modified: py/dist/py/path/local/local.py ============================================================================== --- py/dist/py/path/local/local.py (original) +++ py/dist/py/path/local/local.py Sun Nov 28 23:57:41 2004 @@ -206,7 +206,7 @@ """ relpath = str(relpath) if self.strpath.startswith(relpath): - return self.strpath[len(relpath)+1:] + return self.strpath[len(relpath)+(relpath[-1:] != self.sep):] return "" def remove(self, rec=1): Modified: py/dist/py/path/local/test_posix.py ============================================================================== --- py/dist/py/path/local/test_posix.py (original) +++ py/dist/py/path/local/test_posix.py Sun Nov 28 23:57:41 2004 @@ -59,6 +59,10 @@ finally: tmpdir.remove(rec=1) + def test_relto_with_root(self): + y = self.root.join('x').relto(py.path.local('/')) + assert y[0] == str(self.root)[1] + def test_visit_recursive_symlink(self): tmpdir = local.mkdtemp() try: From hpk at codespeak.net Tue Nov 30 15:31:37 2004 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 30 Nov 2004 15:31:37 +0100 (MET) Subject: [py-svn] r7713 - py/dist/doc Message-ID: <20041130143137.3286B5AEDF@thoth.codespeak.net> Author: hpk Date: Tue Nov 30 15:31:36 2004 New Revision: 7713 Modified: py/dist/doc/getting_started.txt Log: hint at the possibility of using "codespeak.net:8080" ... Modified: py/dist/doc/getting_started.txt ============================================================================== --- py/dist/doc/getting_started.txt (original) +++ py/dist/doc/getting_started.txt Tue Nov 30 15:31:36 2004 @@ -22,8 +22,13 @@ svn co http://codespeak.net/svn/py/dist dist-py to checkout the code, documentation, tool and example tree -into a ``dist-py`` checkout directory. Your naming desire may vary, -of course. +into a ``dist-py`` checkout directory. Your naming desire may vary +for your local checkout directory, of course. + +If you experience problems with the subversion checkout e.g. +because you have a http-proxy in between that doesn't proxy +DAV requests you can try to use "codespeak.net:8080" instead +of just "codespeak.net". setting it up ------------- From hpk at codespeak.net Tue Nov 30 23:08:26 2004 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 30 Nov 2004 23:08:26 +0100 (MET) Subject: [py-svn] r7720 - py/dist/py/misc Message-ID: <20041130220826.5D9F35ADCC@thoth.codespeak.net> Author: hpk Date: Tue Nov 30 23:08:25 2004 New Revision: 7720 Modified: py/dist/py/misc/test_initpkg.py Log: more explicitely exclude some directories from the "import all modules" test Modified: py/dist/py/misc/test_initpkg.py ============================================================================== --- py/dist/py/misc/test_initpkg.py (original) +++ py/dist/py/misc/test_initpkg.py Tue Nov 30 23:08:25 2004 @@ -20,16 +20,22 @@ def test_importing_all_implementations(): base = py.path.local(py.__file__).dirpath() + nodirs = ( + base.join('test', 'test', 'data'), + base.join('test', 'test', 'import_test'), + base.join('bin'), + base.join('execnet', 'bin'), + ) for p in base.visit('*.py', py.path.checker(dotfile=0)): relpath = p.new(ext='').relto(base) if base.sep in relpath: # not std/*.py itself - if relpath.find('test/data') != -1: - continue - if relpath.find('bin/') != -1: - continue - relpath = relpath.replace(base.sep, '.') - modpath = 'py.__impl__.%s' % relpath - assert __import__(modpath) + for x in nodirs: + if p.relto(x): + break + else: + relpath = relpath.replace(base.sep, '.') + modpath = 'py.__impl__.%s' % relpath + assert __import__(modpath) def test_shahexdigest(): hex = py.__package__.shahexdigest()