[svn r57540] merge changes from release branch back

[svn merge -r 57430:HEAD ../release/0.9.x/ .]

* cmdline script organisation
* execnet windows fixes
* documentation updates
* test skips

also regen setup.py

--HG--
branch : trunk
This commit is contained in:
hpk 2008-08-21 12:18:58 +02:00
parent c0d524139e
commit a999dc8472
35 changed files with 596 additions and 508 deletions

View File

@ -1,20 +1,29 @@
$Id: CHANGELOG 57429 2008-08-18 15:08:39Z hpk $
$Id: CHANGELOG 57540 2008-08-21 10:18:58Z hpk $
Changes between 0.9.1 and 0.9.2
===============================
* refined installation and metadata, created new setup.py,
now based on setuptools/ez_setup (by flipping a bit you
can use distutils).
now based on setuptools/ez_setup (thanks to Ralf Schmitt
for his support).
* improved the way of making py.* scripts available in
windows environments, they are now added to the
Scripts directory as ".cmd" files.
* improved py.execnet bootstrapping on windows
* py.path.svnwc.status() now is more complete and
uses xml output from the 'svn' command if available
(Guido Wesdorp)
* fix for py.path.svn* to work with svn 1.5
(Chris Lamb)
* fix path.relto(otherpath) method on windows to
use normcase for checking if a path is relative.
* py.test's traceback is better parseable from editors
(follows the filenames:LINENO: MSG convention)
(thanks to Osmo Salomaa)
* fix to javascript-generation, "py.test --runbrowser"
should work more reliably now

View File

@ -61,13 +61,20 @@ py/apigen/tracer/testing/test_docgen.py
py/apigen/tracer/testing/test_model.py
py/apigen/tracer/testing/test_package.py
py/apigen/tracer/tracer.py
py/bin/_docgen.py
py/bin/_findpy.py
py/bin/_genscripts.py
py/bin/py.cleanup
py/bin/py.countloc
py/bin/py.lookup
py/bin/py.rest
py/bin/py.test
py/bin/py.which
py/bin/win32/py.cleanup.cmd
py/bin/win32/py.countloc.cmd
py/bin/win32/py.lookup.cmd
py/bin/win32/py.rest.cmd
py/bin/win32/py.test.cmd
py/builtin/__init__.py
py/builtin/enumerate.py
py/builtin/exception.py
@ -100,6 +107,14 @@ py/c-extension/greenlet/test_generator_nested.py
py/c-extension/greenlet/test_greenlet.py
py/c-extension/greenlet/test_remote.py
py/c-extension/greenlet/test_throw.py
py/cmdline/__init__.py
py/cmdline/pycleanup.py
py/cmdline/pycountloc.py
py/cmdline/pylookup.py
py/cmdline/pyrest.py
py/cmdline/pytest.py
py/cmdline/testing/__init__.py
py/cmdline/testing/test_generic.py
py/code/__init__.py
py/code/code.py
py/code/excinfo.py
@ -419,4 +434,5 @@ py/xmlobj/testing/test_html.py
py/xmlobj/testing/test_xml.py
py/xmlobj/visit.py
py/xmlobj/xml.py
setup.cfg
setup.py

View File

@ -1,37 +0,0 @@
#!/usr/bin/env python
#
# find and import a version of 'py'
#
import sys
import os
from os.path import dirname as opd, exists, join, basename, abspath
def searchpy(current):
while 1:
last = current
initpy = join(current, '__init__.py')
if not exists(initpy):
pydir = join(current, 'py')
# recognize py-package and ensure it is importable
if exists(pydir) and exists(join(pydir, '__init__.py')):
#for p in sys.path:
# if p == current:
# return True
if current != sys.path[0]: # if we are already first, then ok
print >>sys.stderr, "inserting into sys.path:", current
sys.path.insert(0, current)
return True
current = opd(current)
if last == current:
return False
if not searchpy(abspath(os.curdir)):
if not searchpy(opd(abspath(sys.argv[0]))):
if not searchpy(opd(__file__)):
pass # let's hope it is just on sys.path
import py
if __name__ == '__main__':
print "py lib is at", py.__file__

View File

@ -16,6 +16,7 @@ copyrighted by one or more of the following people and organizations:
Contributors include::
Samuele Pedroni
Chris Lamb
Harald Armin Massa
Ralf Schmitt
Ian Bicking

View File

@ -1,9 +1,24 @@
# -*- coding: utf-8 -*-
"""
the py lib is a development support library featuring
py.test, ad-hoc distributed execution, micro-threads
and svn abstractions.
The py lib is a development support library featuring these tools and APIs:
- `py.test`_: cross-project testing tool with many advanced features
- `py.execnet`_: ad-hoc code distribution to SSH, Socket and local sub processes
- `py.magic.greenlet`_: micro-threads on standard CPython ("stackless-light") and PyPy
- `py.path`_: path abstractions over local and subversion files
- `py.code`_: dynamic code compile and traceback printing support
The py lib and its tools should work well on Linux, Win32,
OSX, Python versions 2.3-2.6. For questions please go to
http://pylib.org/contact.html
.. _`py.test`: http://pylib.org/test.html
.. _`py.execnet`: http://pylib.org/execnet.html
.. _`py.magic.greenlet`: http://pylib.org/greenlet.html
.. _`py.path`: http://pylib.org/path.html
.. _`py.code`: http://pylib.org/code.html
"""
from initpkg import initpkg
@ -11,18 +26,18 @@ version = "1.0.0a1"
initpkg(__name__,
description = "pylib and py.test: agile development and test support library",
revision = int('$LastChangedRevision: 57529 $'.split(':')[1][:-1]),
lastchangedate = '$LastChangedDate: 2008-08-21 09:48:44 +0200 (Thu, 21 Aug 2008) $',
revision = int('$LastChangedRevision: 57540 $'.split(':')[1][:-1]),
lastchangedate = '$LastChangedDate: 2008-08-21 12:18:58 +0200 (Thu, 21 Aug 2008) $',
version = version,
url = "http://pylib.org",
download_url = "http://pypi.python.org/pypi?:action=display&name=py",
download_url = "http://codespeak.net/py/0.9.2/download.html",
license = "MIT license",
platforms = ['unix', 'linux', 'osx', 'cygwin', 'win32'],
author = "holger krekel, Guido Wesdorp, Carl Friedrich Bolz, Armin Rigo, Maciej Fijalkowski & others",
author_email = "holger at merlinux.eu, py-dev at codespeak.net",
long_description = globals()['__doc__'],
classifiers = [
"Development Status :: 3 - Alpha",
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: POSIX",
@ -38,6 +53,13 @@ initpkg(__name__,
# EXPORTED API
exportdefs = {
# py lib cmdline tools
'cmdline.pytest' : ('./cmdline/pytest.py', 'main',),
'cmdline.pyrest' : ('./cmdline/pyrest.py', 'main',),
'cmdline.pylookup' : ('./cmdline/pylookup.py', 'main',),
'cmdline.pycountloc' : ('./cmdline/pycountloc.py', 'main',),
'cmdline.pycleanup' : ('./cmdline/pycleanup.py', 'main',),
# helpers for use from test functions or collectors
'test.__doc__' : ('./test/__init__.py', '__doc__'),
'test.raises' : ('./test/outcome.py', 'raises'),

View File

@ -21,8 +21,21 @@ def pkg_to_dict(module):
for key, value in defs.iteritems():
chain = key.split('.')
base = module
for elem in chain:
base = getattr(base, elem)
# XXX generalize this:
# a bit of special casing for greenlets which are
# not available on all the platforms that python/py
# lib runs
try:
for elem in chain:
base = getattr(base, elem)
except RuntimeError, exc:
if elem == "greenlet":
print exc.__class__.__name__, exc
print "Greenlets not supported on this platform. Skipping apigen doc for this module"
continue
else:
raise
if value[1] == '*':
d.update(get_star_import_tree(base, key))
else:

61
py/bin/_docgen.py Executable file
View File

@ -0,0 +1,61 @@
#!/usr/bin/env python
""" quick tool to get documentation + apigen docs generated
given a certain targetpath, apigen docs will be placed in 'apigen',
user can choose to only build either docs or apigen docs: in this case,
the navigation bar will be adjusted
"""
from _findpy import py
pypath = py.__pkg__.getpath()
print "using pypath", pypath
import os
def run_tests(path, envvars='', args=''):
pytestpath = pypath.join('bin/py.test')
cmd = ('PYTHONPATH="%s" %s python "%s" %s "%s"' %
(pypath.dirpath(), envvars, pytestpath, args, path))
print cmd
errno = os.system(cmd)
assert not errno
def build_apigen_docs(targetpath, testargs=''):
run_tests(pypath,
'APIGEN_TARGET="%s/apigen" APIGEN_DOCRELPATH="../"' % (
targetpath,),
'%s --apigen="%s/apigen/apigen.py"' % (testargs, pypath))
def build_docs(targetpath, testargs):
docpath = pypath.join('doc')
run_tests(docpath, '',
testargs + ' --forcegen --apigen="%s/apigen/apigen.py"' % (pypath,))
docpath.copy(targetpath)
def build_nav(targetpath, docs=True, api=True):
pass
def build(targetpath, docs=True, api=True, testargs=''):
targetpath.ensure(dir=True)
if docs:
print 'building docs'
build_docs(targetpath, testargs)
if api:
print 'building api'
build_apigen_docs(targetpath, testargs)
if __name__ == '__main__':
import sys
if len(sys.argv) == 1:
print 'usage: %s <targetdir> [options]'
print
print ' targetdir: a path to a directory (created if it doesn\'t'
print ' exist) where the docs are put'
print ' options: options passed to py.test when running the tests'
sys.exit(1)
targetpath = py.path.local(sys.argv[1])
args = ' '.join(sys.argv[2:])
build(targetpath, True, True, args)

34
py/bin/_genscripts.py Normal file
View File

@ -0,0 +1,34 @@
from _findpy import py
mydir = py.magic.autopath().dirpath()
def getbasename(name):
assert name[:2] == "py"
return "py." + name[2:]
def genscript_unix(name):
basename = getbasename(name)
path = mydir.join(basename)
path.write(py.code.Source("""
#!/usr/bin/env python
from _findpy import py
py.cmdline.%s()
""" % name).strip())
path.chmod(0755)
def genscript_windows(name):
basename = getbasename(name)
winbasename = basename + ".cmd"
path = mydir.join("win32").join(winbasename)
path.write(py.code.Source("""
@echo off
python "%%~dp0\..\%s" %%*
""" % (basename)).strip())
if __name__ == "__main__":
for name in dir(py.cmdline):
if name[0] != "_":
genscript_unix(name)
genscript_windows(name)

View File

@ -1,24 +1,3 @@
#!/usr/bin/env python
"""\
py.cleanup [PATH]
Delete pyc file recursively, starting from PATH (which defaults to the current
working directory). Don't follow links and don't recurse into directories with
a ".".
"""
from _findpy import py
import py
parser = py.compat.optparse.OptionParser(usage=__doc__)
if __name__ == '__main__':
(options, args) = parser.parse_args()
if not args:
args = ["."]
for arg in args:
path = py.path.local(arg)
print "cleaning path", path
for x in path.visit('*.pyc', lambda x: x.check(dotfile=0, link=0)):
x.remove()
#!/usr/bin/env python
from _findpy import py
py.cmdline.pycleanup()

View File

@ -1,23 +1,3 @@
#!/usr/bin/env python
# hands on script to compute the non-empty Lines of Code
# for tests and non-test code
"""\
py.countloc [PATHS]
Count (non-empty) lines of python code and number of python files recursively
starting from a list of paths given on the command line (starting from the
current working directory). Distinguish between test files and normal ones and
report them separately.
"""
from _findpy import py
import py
from py.compat import optparse
from py.__.misc.cmdline.countloc import countloc
parser = optparse.OptionParser(usage=__doc__)
if __name__ == '__main__':
(options, args) = parser.parse_args()
countloc(args)
from _findpy import py
py.cmdline.pycountloc()

View File

@ -1,79 +1,3 @@
#!/usr/bin/env python
"""\
py.lookup SEARCH_STRING [options]
Looks recursively at Python files for a SEARCH_STRING, starting from the
present working directory. Prints the line, with the filename and line-number
prepended."""
import sys, os
sys.path.insert(0, os.path.dirname(__file__))
#!/usr/bin/env python
from _findpy import py
try:
from py.__.io.terminalwriter import ansi_print, terminal_width
except ImportError:
from py.__.misc.terminal_helper import ansi_print, terminal_width
import re
curdir = py.path.local()
def rec(p):
return p.check(dotfile=0)
optparse = py.compat.optparse
parser = optparse.OptionParser(usage=__doc__)
parser.add_option("-i", "--ignore-case", action="store_true", dest="ignorecase",
help="ignore case distinctions")
parser.add_option("-C", "--context", action="store", type="int", dest="context",
default=0, help="How many lines of output to show")
def find_indexes(search_line, string):
indexes = []
before = 0
while 1:
i = search_line.find(string, before)
if i == -1:
break
indexes.append(i)
before = i + len(string)
return indexes
if __name__ == '__main__':
(options, args) = parser.parse_args()
string = args[0]
if options.ignorecase:
string = string.lower()
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()
if s.find(string) != -1:
lines = s.splitlines()
if options.ignorecase:
searchlines = s.lower().splitlines()
else:
searchlines = lines
for i, (line, searchline) in enumerate(zip(lines, searchlines)):
indexes = find_indexes(searchline, string)
if not indexes:
continue
if not options.context:
sys.stdout.write("%s:%d: " %(x.relto(curdir), i+1))
last_index = 0
for index in indexes:
sys.stdout.write(line[last_index: index])
ansi_print(line[index: index+len(string)],
file=sys.stdout, esc=31, newline=False)
last_index = index + len(string)
sys.stdout.write(line[last_index:] + "\n")
else:
context = (options.context)/2
for count in range(max(0, i-context), min(len(lines) - 1, i+context+1)):
print "%s:%d: %s" %(x.relto(curdir), count+1, lines[count].rstrip())
print "-" * terminal_width
py.cmdline.pylookup()

View File

@ -1,79 +1,3 @@
#!/usr/bin/env python
"""
invoke
py.rest filename1.txt directory
to generate html files from ReST.
It is also possible to generate pdf files using the --topdf option.
http://docutils.sourceforge.net/docs/user/rst/quickref.html
"""
import os, sys
from _findpy import py
from py.__.misc import rest
from py.__.rest import directive
from py.__.rest.latex import process_rest_file, process_configfile
if hasattr(sys.stdout, 'fileno') and os.isatty(sys.stdout.fileno()):
def log(msg):
print msg
else:
def log(msg):
pass
optparse = py.compat.optparse
parser = optparse.OptionParser(usage=__doc__)
parser.add_option("--topdf", action="store_true", dest="topdf", default=False,
help="generate pdf files")
parser.add_option("--stylesheet", dest="stylesheet", default=None,
help="use specified latex style sheet")
parser.add_option("--debug", action="store_true", dest="debug",
default=False,
help="print debug output and don't delete files")
if __name__=='__main__':
(options, args) = parser.parse_args()
if len(args) == 0:
filenames = [py.path.svnwc()]
else:
filenames = [py.path.svnwc(x) for x in args]
if options.topdf:
directive.set_backend_and_register_directives("latex")
for p in filenames:
if not p.check():
log("path %s not found, ignoring" % p)
continue
def fil(p):
return p.check(fnmatch='*.txt', versioned=True)
def rec(p):
return p.check(dotfile=0)
if p.check(dir=1):
for x in p.visit(fil, rec):
rest.process(x)
elif p.check(file=1):
if p.ext == ".rst2pdfconfig":
directive.set_backend_and_register_directives("latex")
process_configfile(p, options.debug)
else:
if options.topdf:
cfg = p.new(ext=".rst2pdfconfig")
if cfg.check():
print "using config file %s" % (cfg, )
process_configfile(cfg, options.debug)
else:
process_rest_file(p.localpath,
options.stylesheet,
options.debug)
else:
rest.process(p)
py.cmdline.pyrest()

View File

@ -1,4 +1,3 @@
#!/usr/bin/env python
from _findpy import py
py.test.cmdline.main()
#!/usr/bin/env python
from _findpy import py
py.cmdline.pytest()

View File

@ -0,0 +1,2 @@
@echo off
python "%~dp0\..\py.cleanup" %*

View File

@ -0,0 +1,2 @@
@echo off
python "%~dp0\..\py.countloc" %*

View File

@ -0,0 +1,2 @@
@echo off
python "%~dp0\..\py.lookup" %*

2
py/bin/win32/py.rest.cmd Normal file
View File

@ -0,0 +1,2 @@
@echo off
python "%~dp0\..\py.rest" %*

2
py/bin/win32/py.test.cmd Normal file
View File

@ -0,0 +1,2 @@
@echo off
python "%~dp0\..\py.test" %*

1
py/cmdline/__init__.py Normal file
View File

@ -0,0 +1 @@
#

21
py/cmdline/pycleanup.py Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env python
"""\
py.cleanup [PATH]
Delete pyc file recursively, starting from PATH (which defaults to the current
working directory). Don't follow links and don't recurse into directories with
a ".".
"""
import py
def main():
parser = py.compat.optparse.OptionParser(usage=__doc__)
(options, args) = parser.parse_args()
if not args:
args = ["."]
for arg in args:
path = py.path.local(arg)
print "cleaning path", path
for x in path.visit('*.pyc', lambda x: x.check(dotfile=0, link=0)):
x.remove()

22
py/cmdline/pycountloc.py Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
# hands on script to compute the non-empty Lines of Code
# for tests and non-test code
"""\
py.countloc [PATHS]
Count (non-empty) lines of python code and number of python files recursively
starting from a list of paths given on the command line (starting from the
current working directory). Distinguish between test files and normal ones and
report them separately.
"""
from _findpy import py
import py
from py.compat import optparse
from py.__.misc.cmdline.countloc import countloc
def main():
parser = optparse.OptionParser(usage=__doc__)
(options, args) = parser.parse_args()
countloc(args)

74
py/cmdline/pylookup.py Executable file
View File

@ -0,0 +1,74 @@
#!/usr/bin/env python
"""\
py.lookup SEARCH_STRING [options]
Looks recursively at Python files for a SEARCH_STRING, starting from the
present working directory. Prints the line, with the filename and line-number
prepended."""
import sys, os
import py
from py.__.misc.terminal_helper import ansi_print, terminal_width
import re
curdir = py.path.local()
def rec(p):
return p.check(dotfile=0)
optparse = py.compat.optparse
parser = optparse.OptionParser(usage=__doc__)
parser.add_option("-i", "--ignore-case", action="store_true", dest="ignorecase",
help="ignore case distinctions")
parser.add_option("-C", "--context", action="store", type="int", dest="context",
default=0, help="How many lines of output to show")
def find_indexes(search_line, string):
indexes = []
before = 0
while 1:
i = search_line.find(string, before)
if i == -1:
break
indexes.append(i)
before = i + len(string)
return indexes
def main():
(options, args) = parser.parse_args()
string = args[0]
if options.ignorecase:
string = string.lower()
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()
if s.find(string) != -1:
lines = s.splitlines()
if options.ignorecase:
searchlines = s.lower().splitlines()
else:
searchlines = lines
for i, (line, searchline) in enumerate(zip(lines, searchlines)):
indexes = find_indexes(searchline, string)
if not indexes:
continue
if not options.context:
sys.stdout.write("%s:%d: " %(x.relto(curdir), i+1))
last_index = 0
for index in indexes:
sys.stdout.write(line[last_index: index])
ansi_print(line[index: index+len(string)],
file=sys.stdout, esc=31, newline=False)
last_index = index + len(string)
sys.stdout.write(line[last_index:] + "\n")
else:
context = (options.context)/2
for count in range(max(0, i-context), min(len(lines) - 1, i+context+1)):
print "%s:%d: %s" %(x.relto(curdir), count+1, lines[count].rstrip())
print "-" * terminal_width

83
py/cmdline/pyrest.py Executable file
View File

@ -0,0 +1,83 @@
#!/usr/bin/env python
"""
invoke
py.rest filename1.txt directory
to generate html files from ReST.
It is also possible to generate pdf files using the --topdf option.
http://docutils.sourceforge.net/docs/user/rst/quickref.html
"""
import os, sys
import py
if hasattr(sys.stdout, 'fileno') and os.isatty(sys.stdout.fileno()):
def log(msg):
print msg
else:
def log(msg):
pass
optparse = py.compat.optparse
parser = optparse.OptionParser(usage=__doc__)
parser.add_option("--topdf", action="store_true", dest="topdf", default=False,
help="generate pdf files")
parser.add_option("--stylesheet", dest="stylesheet", default=None,
help="use specified latex style sheet")
parser.add_option("--debug", action="store_true", dest="debug",
default=False,
help="print debug output and don't delete files")
def main():
try:
from py.__.misc import rest
from py.__.rest import directive
from py.__.rest.latex import process_rest_file, process_configfile
except ImportError, e:
print str(e)
sys.exit(1)
(options, args) = parser.parse_args()
if len(args) == 0:
filenames = [py.path.svnwc()]
else:
filenames = [py.path.svnwc(x) for x in args]
if options.topdf:
directive.set_backend_and_register_directives("latex")
for p in filenames:
if not p.check():
log("path %s not found, ignoring" % p)
continue
def fil(p):
return p.check(fnmatch='*.txt', versioned=True)
def rec(p):
return p.check(dotfile=0)
if p.check(dir=1):
for x in p.visit(fil, rec):
rest.process(x)
elif p.check(file=1):
if p.ext == ".rst2pdfconfig":
directive.set_backend_and_register_directives("latex")
process_configfile(p, options.debug)
else:
if options.topdf:
cfg = p.new(ext=".rst2pdfconfig")
if cfg.check():
print "using config file %s" % (cfg, )
process_configfile(cfg, options.debug)
else:
process_rest_file(p.localpath,
options.stylesheet,
options.debug)
else:
rest.process(p)

5
py/cmdline/pytest.py Executable file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env python
import py
def main():
py.test.cmdline.main()

View File

@ -0,0 +1 @@
#

View File

@ -0,0 +1,55 @@
import py
import sys
binpath = py.path.local(py.__file__).dirpath("bin")
binwinpath = binpath.join("win32")
def setup_module(mod):
mod.tmpdir = py.test.ensuretemp(__name__)
mod.iswin32 = sys.platform == "win32"
def checkmain(name):
main = getattr(py.cmdline, name)
assert callable(main)
assert name[:2] == "py"
scriptname = "py." + name[2:]
assert binpath.join(scriptname).check()
assert binwinpath.join(scriptname + ".cmd").check()
def checkprocess(script):
assert script.check()
old = tmpdir.ensure(script.basename, dir=1).chdir()
try:
if iswin32:
cmd = script.basename
else:
cmd = "%s" %(script, )
if script.basename.startswith("py.lookup"):
cmd += " hello"
print "executing", script
try:
py.process.cmdexec(cmd)
except py.process.cmdexec.Error, e:
if cmd.find("py.rest") != -1 and \
e.out.find("module named") != -1:
return
raise
finally:
old.chdir()
def test_cmdline_namespace():
for name in dir(py.cmdline):
if name[0] != "_":
yield checkmain, name
def test_script_invocation():
if iswin32:
for script in binwinpath.listdir("py.*"):
assert script.ext == ".cmd"
yield checkprocess, script
else:
for script in binpath.listdir("py.*"):
yield checkprocess, script

View File

@ -1,104 +1,71 @@
Download and Installation of the py lib
===============================================
.. contents::
.. sectnum::
Downloading a tar/zip file and installing it
"easy_install py"
===================================================
The latest public release:
With a working `setuptools installation`_ you can install from the command line::
`download py-0.9.0.tar.gz`_
`download py-0.9.0.zip`_
easy_install py
.. _`download py-0.9.0.tar.gz`: http://codespeak.net/download/py/py-0.9.0.tar.gz
.. _`download py-0.9.0.zip`: http://codespeak.net/download/py/py-0.9.0.zip
to get the latest release of the py lib. On non-Windows systems you will
need a working C-compiler in order to successfully complete this step.
The py lib and its tools are expected to work well on Linux, Windows
and OSX, Python versions 2.3, 2.4, 2.5 and 2.6.
The py lib can be `globally installed via setup.py`_
or `used locally`_.
**IMPORTANT NOTE**: if you are using Windows and have previous
installations of the py lib on your system, please download
and execute http://codespeak.net/svn/py/build/winpathclean.py
This will check that no previous files are getting in the way.
(Unfortunately we don't know about a way to execute this
code automatically during the above install).
WARNING: win32 there is no pre-packaged c-extension
module (greenlet) yet and thus greenlets will not work
out of the box.
.. _`setuptools installation`: http://pypi.python.org/pypi/setuptools
Getting (and updating) via subversion
--------------------------------------------
Downloading a tar/zip archive and installing that
===================================================
Use Subversion to checkout the latest 0.9.x stable release:
Go to the project pages and download a tar or zip file:
svn co http://codespeak.net/svn/py/release/0.9.x py-0.9.x
http://pypi.python.org/pypi/py/
to obtain the complete code and documentation source.
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". Alternatively, you may tweak
your local subversion installation.
If you want to follow stable snapshots
then you may use the equivalent of this invocation:
svn co http://codespeak.net/svn/py/dist py-dist
.. _`globally installed via setup.py`:
Installation via setup.py
------------------------------
Go to your unpacked/checked out directory
and issue:
and unpack it to a directory, where you then type::
python setup.py install
If you don't have a working C-compiler you can do::
.. _`used locally`:
python setup.py install_lib
Local Installation/Usage
------------------------------
You will then not be able to use greenlets but otherwise
should be fine.
You need to put the checkout-directory into your ``PYTHONPATH``
and you want to have the ``py-dist/py/bin/py.test`` script in
your (unixish) system path, which lets you execute test files
and directories.
Installing from subversion / develop mode
============================================
There is a convenient way for Bash/Shell based systems
to setup the ``PYTHONPATH`` as well as the shell ``PATH``, insert::
To follow development or help with fixing things
for the next release, checkout the complete code
and documentation source::
eval `python ~/path/to/py-dist/py/env.py`
svn co http://codespeak.net/svn/py/release/0.9.x py-0.9.x
into your ``.bash_profile``. Of course, you need to
specify your own checkout-directory.
You should then be able to issue::
python setup.py develop
.. _`svn-external scenario`:
and work with the local version.
The py lib as an svn external
-------------------------------------------------------
If this doesn't work for you then you can also
source (linux) or execute (windows) some scripts
that set environment variables for using your checkout.
You can execute:
Add the py lib as an external to your project `DIRECTORY`
which contains your svn-controlled root package::
python ~/path/to/checkout/py/env.py
svn propedit 'svn:externals' DIRECTORY
or on windows:
which will open an editor where you can add
the following line:
\\path\\to\\checkout\\py\\env.cmd
py http://codespeak.net/svn/py/dist
This will make your projcet automatically use the
most recent stable snapshot of the py lib.
Alternatively you may use this url for
integrating the development version:
http://codespeak.net/svn/py/trunk
or the next one for following the e.g. the 0.9 release branch
http://codespeak.net/svn/py/release/0.9.x
to get settings for PYTHONPATH and PATH.
py subversion directory structure

View File

@ -5,8 +5,6 @@ Implementation and Customization of ``py.test``
.. contents::
.. sectnum::
.. _`basicpicture`:
@ -71,6 +69,12 @@ By default all directories not starting with a dot are traversed,
looking for ``test_*.py`` and ``*_test.py`` files. Those files
are imported under their `package name`_.
The Module collector looks for test functions
and test classes and methods. Test functions and methods
are prefixed ``test`` by default. Test classes must
start with a capitalized ``Test`` prefix.
.. _`collector API`:
test items are collectors as well
@ -84,48 +88,28 @@ it, otherwise we consider the "test" as passed.
.. _`package name`:
constructing the package name for modules
-----------------------------------------
constructing the package name for test modules
-------------------------------------------------
Test modules are imported under their fully qualified
name. Given a module ``path`` the fully qualified package
name is constructed as follows:
name. Given a filesystem ``fspath`` it is constructed as follows:
* determine the last "upward" directory from ``path`` that
contains an ``__init__.py`` file. Going upwards
means repeatedly calling the ``dirpath()`` method
on a path object (which returns the parent directory
as a path object).
* walk the directories up to the last one that contains
an ``__init__.py`` file.
* insert this base directory into the sys.path list
as its first element
* perform ``sys.path.insert(0, basedir)``.
* import the root package
* import the root package as ``root``
* determine the fully qualified name for the module located
at ``path`` ...
* determine the fully qualified name for ``fspath`` by either:
* if the imported root package has a __package__ object
then call ``__package__.getimportname(path)``
* calling ``root.__pkg__.getimportname(fspath)`` if the
``__pkg__`` exists.` or
* otherwise use the relative path of the module path to
the base dir and turn slashes into dots and strike
the trailing ``.py``.
The Module collector will eventually trigger
``__import__(mod_fqdnname, ...)`` to finally get to
the live module object.
Side note: this whole logic is performed by local path
object's ``pyimport()`` method.
Module Collector
-----------------
The default Module collector looks for test functions
and test classes and methods. Test functions and methods
are prefixed ``test`` by default. Test classes must
start with a capitalized ``Test`` prefix.
Customizing the testing process
@ -256,32 +240,27 @@ Customizing the collection process in a module
If you have a module where you want to take responsibility for
collecting your own test Items and possibly even for executing
a test then you can provide `generative tests`_ that yield
callables and possibly arguments as a tuple. This should
serve some immediate purposes like paramtrized tests.
callables and possibly arguments as a tuple. This is especially
useful for calling application test machinery with different
parameter sets but counting each of the calls as a separate
tests.
.. _`generative tests`: test.html#generative-tests
The other extension possibility goes deeper into the machinery
and allows you to specify a custom test ``Item`` class which
The other extension possibility is about
specifying a custom test ``Item`` class which
is responsible for setting up and executing an underlying
test. [XXX not working: You can integrate your custom ``py.test.collect.Item`` subclass
by binding an ``Item`` name to a test class.] Or you can
extend the collection process for a whole directory tree
by putting Items in a ``conftest.py`` configuration file.
The collection process constantly looks at according names
in the *chain of conftest.py* modules to determine collectors
and items at ``Directory``, ``Module``, ``Class``, ``Function``
or ``Generator`` level. Note that, right now, except for ``Function``
items all classes are pure collectors, i.e. will return a list
of names (possibly empty).
XXX implement doctests as alternatives to ``Function`` items.
test. Or you can extend the collection process for a whole
directory tree by putting Items in a ``conftest.py`` configuration file.
The collection process dynamically consults the *chain of conftest.py*
modules to determine collectors and items at ``Directory``, ``Module``,
``Class``, ``Function`` or ``Generator`` level respectively.
Customizing execution of Functions
----------------------------------
- Function test items allow total control of executing their
contained test method. ``function.run()`` will get called by the
- ``py.test.collect.Function`` test items control execution
of a test function. ``function.run()`` will get called by the
session in order to actually run a test. The method is responsible
for performing proper setup/teardown ("Test Fixtures") for a
Function test.
@ -290,5 +269,4 @@ Customizing execution of Functions
the default ``Function.run()`` to actually execute a python
function with the given (usually empty set of) arguments.
.. _`py-dev mailing list`: http://codespeak.net/mailman/listinfo/py-dev

View File

@ -1,13 +1,15 @@
py lib documentation
=================================================
The py lib aims at supporting a decent development process
addressing deployment, versioning, testing and documentation
perspectives.
The py lib is a development support library featuring
py.test, ad-hoc distributed execution, micro-threads
(greenlets) and uniform local path and svn abstractions.
Works on Linux, Windows and OSX, Python versions
2.3, 2.4, 2.5 and 2.6.
`Download and Installation`_
`0.9.0 release announcement`_
`0.9.2 release announcement`_
Main tools and API
----------------------
@ -20,6 +22,8 @@ Main tools and API
`py.path`_: local and subversion Path and Filesystem access
`py.code`_: High-level access/manipulation of Python code and traceback objects.
`py lib scripts`_ describe the scripts contained in the ``py/bin`` directory.
`apigen`_: a new way to generate rich Python API documentation
@ -27,8 +31,6 @@ Main tools and API
support functionality
---------------------------------
`py.code`_: High-level access/manipulation of Python code and traceback objects.
`py.xml`_ for generating in-memory xml/html object trees
`py.io`_: Helper Classes for Capturing of Input/Output
@ -59,4 +61,4 @@ Background and Motivation information
.. _`Why What how py?`: why_py.html
.. _`future`: future.html
.. _`miscellaneous features`: misc.html
.. _`0.9.0 release announcement`: release-0.9.0.html
.. _`0.9.2 release announcement`: release-0.9.2.html

View File

@ -5,7 +5,7 @@ Welcome to the 0.9.2 py lib and py.test release -
mainly fixing Windows issues, providing better
packaging and integration with setuptools.
Summary of main feature of the py lib:
Here is a quick summary of what it provides:
* py.test: cross-project testing tool with many advanced features
* py.execnet: ad-hoc code distribution to SSH, Socket and local sub processes
@ -17,6 +17,7 @@ Summary of main feature of the py lib:
See here for more information:
Download/Install: http://codespeak.net/py/0.9.2/download.html
Documentation/API: http://codespeak.net/py/0.9.2/index.html
best and have fun,

View File

@ -59,10 +59,6 @@ import sys
class Popen2IO:
server_stmt = """
import os, sys, StringIO
if sys.platform == "win32":
import msvcrt
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
io = Popen2IO(sys.stdout, sys.stdin)
sys.stdout = sys.stderr = StringIO.StringIO()
#try:
@ -75,6 +71,11 @@ sys.stdout = sys.stderr = StringIO.StringIO()
def __init__(self, infile, outfile):
self.outfile, self.infile = infile, outfile
if sys.platform == "win32":
import msvcrt
msvcrt.setmode(infile.fileno(), os.O_BINARY)
msvcrt.setmode(outfile.fileno(), os.O_BINARY)
self.readable = self.writeable = True
self.lock = thread.allocate_lock()

View File

@ -2,8 +2,12 @@
import os, inspect, socket
import sys
from py.magic import autopath ; mypath = autopath()
import py
if sys.platform == "win32":
win32 = True
import msvcrt
else:
win32 = False
# the list of modules that must be send to the other side
# for bootstrapping gateways
@ -51,13 +55,10 @@ class InstallableGateway(gateway.Gateway):
class PopenCmdGateway(InstallableGateway):
def __init__(self, cmd):
infile, outfile = os.popen2(cmd)
if sys.platform == 'win32':
import msvcrt
msvcrt.setmode(infile.fileno(), os.O_BINARY)
msvcrt.setmode(outfile.fileno(), os.O_BINARY)
io = inputoutput.Popen2IO(infile, outfile)
super(PopenCmdGateway, self).__init__(io=io)
class PopenGateway(PopenCmdGateway):
""" This Gateway provides interaction with a newly started
python subprocess.

View File

@ -3,6 +3,9 @@ A utility to build a Python extension module from C, wrapping distutils.
"""
import py
# set to true for automatic re-compilation of extensions
AUTOREGEN = True
# XXX we should distutils in a subprocess, because it messes up the
# environment and who knows what else. Currently we just save
# and restore os.environ.
@ -32,7 +35,7 @@ def make_module_from_c(cfile):
lib = dirpath.join(modname+ext)
# XXX argl! we need better "build"-locations alltogether!
if lib.check() and lib.stat().mtime < cfile.stat().mtime:
if lib.check() and AUTOREGEN and lib.stat().mtime < cfile.stat().mtime:
try:
lib.remove()
except EnvironmentError:

View File

@ -8,6 +8,10 @@ def test_win_killsubprocess():
py.test.skip("you\'re using an older version of windows, which "
"doesn\'t support 'taskkill' - py.misc.killproc is not "
"available")
try:
import subprocess
except ImportError:
py.test.skip("no subprocess module")
tmp = py.test.ensuretemp("test_win_killsubprocess")
t = tmp.join("t.py")
t.write("import time ; time.sleep(100)")

156
setup.py
View File

@ -1,25 +1,46 @@
"""
setup file for 'py' package based on:
https://codespeak.net/svn/py/trunk, revision=57462
https://codespeak.net/svn/py/trunk, revision=57536
autogenerated by gensetup.py
"""
import os, sys
if 1: # set to zero if you want plain distutils
import ez_setup
ez_setup.use_setuptools()
from setuptools import setup, Extension
else:
from distutils.core import setup, Extension
import ez_setup
ez_setup.use_setuptools()
from setuptools import setup, Extension
long_description = """
The py lib is a development support library featuring these tools and APIs:
- `py.test`_: cross-project testing tool with many advanced features
- `py.execnet`_: ad-hoc code distribution to SSH, Socket and local sub processes
- `py.magic.greenlet`_: micro-threads on standard CPython ("stackless-light") and PyPy
- `py.path`_: path abstractions over local and subversion files
- `py.code`_: dynamic code compile and traceback printing support
The py lib and its tools should work well on Linux, Win32,
OSX, Python versions 2.3-2.6. For questions please go to
http://pylib.org/contact.html
.. _`py.test`: http://pylib.org/test.html
.. _`py.execnet`: http://pylib.org/execnet.html
.. _`py.magic.greenlet`: http://pylib.org/greenlet.html
.. _`py.path`: http://pylib.org/path.html
.. _`py.code`: http://pylib.org/code.html
"""
def main():
setup(cmdclass=cmdclass,
setup(
name='py',
description='pylib and py.test: agile development and test support library',
long_description = long_description,
version='1.0.0a1',
url='http://pylib.org',
download_url='http://codespeak.net/py/0.9.2/download.html',
license='MIT license',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
author='holger krekel, Guido Wesdorp, Carl Friedrich Bolz, Armin Rigo, Maciej Fijalkowski & others',
@ -27,9 +48,13 @@ def main():
ext_modules = [Extension("py.c-extension.greenlet.greenlet",
["py/c-extension/greenlet/greenlet.c"]),],
py_modules=['_findpy'],
long_description='the py lib is a development support library featuring py.test, ad-hoc distributed execution, micro-threads and svn abstractions.',
classifiers=['Development Status :: 3 - Alpha',
entry_points={'console_scripts': ['py.cleanup = py.cmdline:pycleanup',
'py.countloc = py.cmdline:pycountloc',
'py.lookup = py.cmdline:pylookup',
'py.rest = py.cmdline:pyrest',
'py.test = py.cmdline:pytest',
'py.which = py.cmdline:pywhich']},
classifiers=['Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: POSIX',
@ -40,12 +65,6 @@ def main():
'Topic :: System :: Distributed Computing',
'Topic :: Utilities',
'Programming Language :: Python'],
scripts=['py/bin/py.cleanup',
'py/bin/py.countloc',
'py/bin/py.lookup',
'py/bin/py.rest',
'py/bin/py.test',
'py/bin/py.which'],
packages=['py',
'py.apigen',
'py.apigen.rest',
@ -61,6 +80,8 @@ def main():
'py.builtin',
'py.builtin.testing',
'py.c-extension',
'py.cmdline',
'py.cmdline.testing',
'py.code',
'py.code.testing',
'py.compat',
@ -117,13 +138,20 @@ def main():
'apigen/style.css',
'apigen/todo-apigen.txt',
'apigen/todo.txt',
'bin/_docgen.py',
'bin/_findpy.py',
'bin/_genscripts.py',
'bin/py.cleanup',
'bin/py.countloc',
'bin/py.lookup',
'bin/py.rest',
'bin/py.test',
'bin/py.which',
'bin/win32/py.cleanup.cmd',
'bin/win32/py.countloc.cmd',
'bin/win32/py.lookup.cmd',
'bin/win32/py.rest.cmd',
'bin/win32/py.test.cmd',
'c-extension/greenlet/README.txt',
'c-extension/greenlet/dummy_greenlet.py',
'c-extension/greenlet/greenlet.c',
@ -201,100 +229,6 @@ def main():
zip_safe=False,
)
class Win32PathHandling:
""" a helper to remove things added to system PATHs in previous installations. """
_winreg = None
def __init__(self):
if sys.platform == 'win32':
try:
import _winreg
except ImportError:
print sys.stderr, "huh could not import _winreg on windows, ignoring"
else:
self._winreg = _winreg
def remove_pylib_path(self):
reg = self._winreg.ConnectRegistry(
None, self._winreg.HKEY_LOCAL_MACHINE)
key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
path = self.get_registry_value(reg, key, "Path")
newpath = self.prunepath(path)
if newpath != path:
print "PATH contains old py/bin/win32 scripts:", path
print "pruning and setting a new PATH:", newpath
self.set_registry_value(reg, key, "Path", newpath)
# Propagate changes to current command prompt
os.system("set PATH=%s" % path)
self.try_propagate_system()
def prunepath(self, path):
basename = os.path.basename
dirname = os.path.dirname
l = []
for p in path.split(';'):
if basename(p) == "win32" and basename(dirname(p)) == "bin" \
and basename(dirname(dirname(p))) == "py":
continue # prune this path
l.append(p)
return ";".join(l)
def try_propagate_system(self):
try:
import win32gui, win32con
except ImportError:
return
# Propagate changes throughout the system
win32gui.SendMessageTimeout(win32con.HWND_BROADCAST,
win32con.WM_SETTINGCHANGE, 0, "Environment",
win32con.SMTO_ABORTIFHUNG, 5000)
def get_registry_value(self, reg, key, value_name):
k = self._winreg.OpenKey(reg, key)
value = self._winreg.QueryValueEx(k, value_name)[0]
self._winreg.CloseKey(k)
return value
def set_registry_value(self, reg, key, value_name, value):
k = self._winreg.OpenKey(reg, key, 0, self._winreg.KEY_WRITE)
value_type = self._winreg.REG_SZ
# if we handle the Path value, then set its type to REG_EXPAND_SZ
# so that things like %SystemRoot% get automatically expanded by the
# command prompt
if value_name == "Path":
value_type = self._winreg.REG_EXPAND_SZ
self._winreg.SetValueEx(k, value_name, 0, value_type, value)
self._winreg.CloseKey(k)
# on windows we need to hack up the to-be-installed scripts
from distutils.command.install_scripts import install_scripts
class my_install_scripts(install_scripts):
def run(self):
install_scripts.run(self)
#print self.outfiles
for fn in self.outfiles:
basename = os.path.basename(fn)
if "." in basename:
#print "tackling", fn
newbasename = basename.replace(".", "_")
newfn = os.path.join(os.path.dirname(fn), newbasename)
if os.path.exists(newfn):
os.remove(newfn)
os.rename(fn, newfn)
newname = fn + ".cmd"
if os.path.exists(newname):
os.remove(newname)
f = open(newname, 'w')
f.write("@echo off\n")
f.write('python "%%~dp0\py_command_trampolin" %s %%*\n' % basename)
f.close()
w32path = Win32PathHandling()
w32path.remove_pylib_path()
if sys.platform == "win32":
cmdclass = {'install_scripts': my_install_scripts}
else:
cmdclass = {}
if __name__ == '__main__':
main()