py-1.5.2/ 0000775 0001750 0001750 00000000000 13203150701 013267 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/conftest.py 0000664 0001750 0001750 00000003253 13202663755 015512 0 ustar vagrant vagrant 0000000 0000000 import py
import pytest
import sys
pytest_plugins = 'doctest', 'pytester'
collect_ignore = ['build', 'doc/_build']
def pytest_addoption(parser):
group = parser.getgroup("pylib", "py lib testing options")
group.addoption('--runslowtests',
action="store_true", dest="runslowtests", default=False,
help=("run slow tests"))
@pytest.fixture
def sshhost(request):
val = request.config.getvalue("sshhost")
if val:
return val
py.test.skip("need --sshhost option")
# XXX copied from execnet's conftest.py - needs to be merged
winpymap = {
'python2.7': r'C:\Python27\python.exe',
}
def getexecutable(name, cache={}):
try:
return cache[name]
except KeyError:
executable = py.path.local.sysfind(name)
if executable:
if name == "jython":
import subprocess
popen = subprocess.Popen(
[str(executable), "--version"],
universal_newlines=True, stderr=subprocess.PIPE)
out, err = popen.communicate()
if not err or "2.5" not in err:
executable = None
cache[name] = executable
return executable
@pytest.fixture(params=('python2.7', 'pypy-c', 'jython'))
def anypython(request):
name = request.param
executable = getexecutable(name)
if executable is None:
if sys.platform == "win32":
executable = winpymap.get(name, None)
if executable:
executable = py.path.local(executable)
if executable.check():
return executable
py.test.skip("no %s found" % (name,))
return executable
py-1.5.2/CHANGELOG 0000664 0001750 0001750 00000124577 13203150646 014531 0 ustar vagrant vagrant 0000000 0000000 1.5.2
=====
- fix #169, #170: error importing py.log on Windows: no module named ``syslog``.
1.5.1
=====
- fix #167 - prevent pip from installing py in unsupported Python versions.
1.5.0
=====
- python 2.6 and 3.3 are no longer supported
- deprecate py.std and remove all internal uses
- fix #73 turn py.error into an actual module
- path join to / no longer produces leading double slashes
- fix #82 - remove unsupportable aliases
- fix python37 compatibility of path.sysfind on windows by correctly replacing vars
- turn iniconfig and apipkg into vendored packages and ease de-vendoring for distributions
- fix #68 remove invalid py.test.ensuretemp references
- fix #25 - deprecate path.listdir(sort=callable)
- add ``TerminalWriter.chars_on_current_line`` read-only property that tracks how many characters
have been written to the current line.
1.4.34
====================================================================
- fix issue119 / pytest issue708 where tmpdir may fail to make numbered directories
when the filesystem is case-insensitive.
1.4.33
====================================================================
- avoid imports in calls to py.path.local().fnmatch(). Thanks Andreas Pelme for
the PR.
- fix issue106: Naive unicode encoding when calling fspath() in python2. Thanks Tiago Nobrega for the PR.
- fix issue110: unittest.TestCase.assertWarns fails with py imported.
1.4.32
====================================================================
- fix issue70: added ability to copy all stat info in py.path.local.copy.
- make TerminalWriter.fullwidth a property. This results in the correct
value when the terminal gets resized.
- update supported html tags to include recent additions.
Thanks Denis Afonso for the PR.
- Remove internal code in ``Source.compile`` meant to support earlier Python 3 versions that produced the side effect
of leaving ``None`` in ``sys.modules`` when called (see pytest-dev/pytest#2103).
Thanks Bruno Oliveira for the PR.
1.4.31
==================================================
- fix local().copy(dest, mode=True) to also work
with unicode.
- pass better error message with svn EEXIST paths
1.4.30
==================================================
- fix issue68 an assert with a multiline list comprehension
was not reported correctly. Thanks Henrik Heibuerger.
1.4.29
==================================================
- fix issue55: revert a change to the statement finding algorithm
which is used by pytest for generating tracebacks.
Thanks Daniel Hahler for initial analysis.
- fix pytest issue254 for when traceback rendering can't
find valid source code. Thanks Ionel Cristian Maries.
1.4.28
==================================================
- fix issue64 -- dirpath regression when "abs=True" is passed.
Thanks Gilles Dartiguelongue.
1.4.27
==================================================
- fix issue59: point to new repo site
- allow a new ensuresyspath="append" mode for py.path.local.pyimport()
so that a neccessary import path is appended instead of prepended to
sys.path
- strike undocumented, untested argument to py.path.local.pypkgpath
- speed up py.path.local.dirpath by a factor of 10
1.4.26
==================================================
- avoid calling normpath twice in py.path.local
- py.builtin._reraise properly reraises under Python3 now.
- fix issue53 - remove module index, thanks jenisys.
- allow posix path separators when "fnmatch" is called.
Thanks Christian Long for the complete PR.
1.4.25
==================================================
- fix issue52: vaguely fix py25 compat of py.path.local (it's not
officially supported), also fix docs
- fix pytest issue 589: when checking if we have a recursion error
check for the specific "maximum recursion depth" text of the exception.
1.4.24
==================================================
- Fix retrieving source when an else: line has an other statement on
the same line.
- add localpath read_text/write_text/read_bytes/write_bytes methods
as shortcuts and clearer bytes/text interfaces for read/write.
Adapted from a PR from Paul Moore.
1.4.23
==================================================
- use newer apipkg version which makes attribute access on
alias modules resolve to None rather than an ImportError.
This helps with code that uses inspect.getframeinfo()
on py34 which causes a complete walk on sys.modules
thus triggering the alias module to resolve and blowing
up with ImportError. The negative side is that something
like "py.test.X" will now result in None instead of "importerror: pytest"
if pytest is not installed. But you shouldn't import "py.test"
anyway anymore.
- adapt one svn test to only check for any exception instead
of specific ones because different svn versions cause different
errors and we don't care.
1.4.22
==================================================
- refactor class-level registry on ForkedFunc child start/finish
event to become instance based (i.e. passed into the constructor)
1.4.21
==================================================
- ForkedFunc now has class-level register_on_start/on_exit()
methods to allow adding information in the boxed process.
Thanks Marc Schlaich.
- ForkedFunc in the child opens in "auto-flush" mode for
stdout/stderr so that when a subprocess dies you can see
its output even if it didn't flush itself.
- refactor traceback generation in light of pytest issue 364
(shortening tracebacks). you can now set a new traceback style
on a per-entry basis such that a caller can force entries to be
isplayed as short or long entries.
- win32: py.path.local.sysfind(name) will preferrably return files with
extensions so that if "X" and "X.bat" or "X.exe" is on the PATH,
one of the latter two will be returned.
1.4.20
==================================================
- ignore unicode decode errors in xmlescape. Thanks Anatoly Bubenkoff.
- on python2 modify traceback.format_exception_only to match python3
behaviour, namely trying to print unicode for Exception instances
- use a safer way for serializing exception reports (helps to fix
pytest issue413)
Changes between 1.4.18 and 1.4.19
==================================================
- merge in apipkg fixes
- some micro-optimizations in py/_code/code.py for speeding
up pytest runs. Thanks Alex Gaynor for initiative.
- check PY_COLORS=1 or PY_COLORS=0 to force coloring/not-coloring
for py.io.TerminalWriter() independently from capabilities
of the output file. Thanks Marc Abramowitz for the PR.
- some fixes to unicode handling in assertion handling.
Thanks for the PR to Floris Bruynooghe. (This helps
to fix pytest issue 319).
- depend on setuptools presence, remove distribute_setup
Changes between 1.4.17 and 1.4.18
==================================================
- introduce path.ensure_dir() as a synonym for ensure(..., dir=1)
- some unicode/python3 related fixes wrt to path manipulations
(if you start passing unicode particular in py2 you might
still get problems, though)
Changes between 1.4.16 and 1.4.17
==================================================
- make py.io.TerminalWriter() prefer colorama if it is available
and avoid empty lines when separator-lines are printed by
being defensive and reducing the working terminalwidth by 1
- introduce optional "expanduser" argument to py.path.local
to that local("~", expanduser=True) gives the home
directory of "user".
Changes between 1.4.15 and 1.4.16
==================================================
- fix issue35 - define __gt__ ordering between a local path
and strings
- fix issue36 - make chdir() work even if os.getcwd() fails.
- add path.exists/isdir/isfile/islink shortcuts
- introduce local path.as_cwd() context manager.
- introduce p.write(ensure=1) and p.open(ensure=1)
where ensure triggers creation of neccessary parent
dirs.
Changes between 1.4.14 and 1.4.15
==================================================
- majorly speed up some common calling patterns with
LocalPath.listdir()/join/check/stat functions considerably.
- fix an edge case with fnmatch where a glob style pattern appeared
in an absolute path.
Changes between 1.4.13 and 1.4.14
==================================================
- fix dupfile to work with files that don't
carry a mode. Thanks Jason R. Coombs.
Changes between 1.4.12 and 1.4.13
==================================================
- fix getting statementrange/compiling a file ending
in a comment line without newline (on python2.5)
- for local paths you can pass "mode=True" to a copy()
in order to copy permission bits (underlying mechanism
is using shutil.copymode)
- add paths arguments to py.path.local.sysfind to restrict
search to the diretories in the path.
- add isdir/isfile/islink to path.stat() objects allowing to perform
multiple checks without calling out multiple times
- drop py.path.local.__new__ in favour of a simpler __init__
- iniconfig: allow "name:value" settings in config files, no space after
"name" required
- fix issue 27 - NameError in unlikely untested case of saferepr
Changes between 1.4.11 and 1.4.12
==================================================
- fix python2.4 support - for pre-AST interpreters re-introduce
old way to find statements in exceptions (closes pytest issue 209)
- add tox.ini to distribution
- fix issue23 - print *,** args information in tracebacks,
thanks Manuel Jacob
Changes between 1.4.10 and 1.4.11
==================================================
- use _ast to determine statement ranges when printing tracebacks -
avoiding multi-second delays on some large test modules
- fix an internal test to not use class-denoted pytest_funcarg__
- fix a doc link to bug tracker
- try to make terminal.write() printing more robust against
unicodeencode/decode problems, amend according test
- introduce py.builtin.text and py.builtin.bytes
to point to respective str/unicode (py2) and bytes/str (py3) types
- fix error handling on win32/py33 for ENODIR
Changes between 1.4.9 and 1.4.10
==================================================
- terminalwriter: default to encode to UTF8 if no encoding is defined
on the output stream
- issue22: improve heuristic for finding the statementrange in exceptions
Changes between 1.4.8 and 1.4.9
==================================================
- fix bug of path.visit() which would not recognize glob-style patterns
for the "rec" recursion argument
- changed iniconfig parsing to better conform, now the chars ";"
and "#" only mark a comment at the stripped start of a line
- include recent apipkg-1.2
- change internal terminalwriter.line/reline logic to more nicely
support file spinners
Changes between 1.4.7 and 1.4.8
==================================================
- fix issue 13 - correct handling of the tag name object in xmlgen
- fix issue 14 - support raw attribute values in xmlgen
- fix windows terminalwriter printing/re-line problem
- update distribute_setup.py to 0.6.27
Changes between 1.4.6 and 1.4.7
==================================================
- fix issue11 - own test failure with python3.3 / Thanks Benjamin Peterson
- help fix pytest issue 102
Changes between 1.4.5 and 1.4.6
==================================================
- help to fix pytest issue99: unify output of
ExceptionInfo.getrepr(style="native") with ...(style="long")
- fix issue7: source.getstatementrange() now raises proper error
if no valid statement can be found
- fix issue8: fix code and tests of svnurl/svnwc to work on subversion 1.7 -
note that path.status(updates=1) will not properly work svn-17's status
--xml output is broken.
- make source.getstatementrange() more resilent about non-python code frames
(as seen from jnja2)
- make trackeback recursion detection more resilent
about the eval magic of a decorator library
- iniconfig: add support for ; as comment starter
- properly handle lists in xmlgen on python3
- normalize py.code.getfslineno(obj) to always return a (string, int) tuple
defaulting to ("", -1) respectively if no source code can be found for obj.
Changes between 1.4.4 and 1.4.5
==================================================
- improve some unicode handling in terminalwriter and capturing
(used by pytest)
Changes between 1.4.3 and 1.4.4
==================================================
- a few fixes and assertion related refinements for pytest-2.1
- guard py.code.Code and getfslineno against bogus input
and make py.code.Code objects for object instance
by looking up their __call__ function.
- make exception presentation robust against invalid current cwd
Changes between 1.4.2 and 1.4.3
==================================================
- fix terminal coloring issue for skipped tests (thanks Amaury)
- fix issue4 - large calls to ansi_print (thanks Amaury)
Changes between 1.4.1 and 1.4.2
==================================================
- fix (pytest) issue23 - tmpdir argument now works on Python3.2 and WindowsXP
(which apparently starts to offer os.symlink now)
- better error message for syntax errors from compiled code
- small fix to better deal with (un-)colored terminal output on windows
Changes between 1.4.0 and 1.4.1
==================================================
- fix issue1 - py.error.* classes to be pickleable
- fix issue2 - on windows32 use PATHEXT as the list of potential
extensions to find find binaries with py.path.local.sysfind(commandname)
- fix (pytest-) issue10 and refine assertion reinterpretation
to avoid breaking if the __nonzero__ of an object fails
- fix (pytest-) issue17 where python3 does not like "import *"
leading to misrepresentation of import-errors in test modules
- fix py.error.* attribute pypy access issue
- allow path.samefile(arg) to succeed when arg is a relative filename
- fix (pytest-) issue20 path.samefile(relpath) works as expected now
- fix (pytest-) issue8 len(long_list) now shows the lenght of the list
Changes between 1.3.4 and 1.4.0
==================================================
- py.test was moved to a separate "pytest" package. What remains is
a stub hook which will proxy ``import py.test`` to ``pytest``.
- all command line tools ("py.cleanup/lookup/countloc/..." moved
to "pycmd" package)
- removed the old and deprecated "py.magic" namespace
- use apipkg-1.1 and make py.apipkg.initpkg|ApiModule available
- add py.iniconfig module for brain-dead easy ini-config file parsing
- introduce py.builtin.any()
- path objects have a .dirname attribute now (equivalent to
os.path.dirname(path))
- path.visit() accepts breadthfirst (bf) and sort options
- remove deprecated py.compat namespace
Changes between 1.3.3 and 1.3.4
==================================================
- fix issue111: improve install documentation for windows
- fix issue119: fix custom collectability of __init__.py as a module
- fix issue116: --doctestmodules work with __init__.py files as well
- fix issue115: unify internal exception passthrough/catching/GeneratorExit
- fix issue118: new --tb=native for presenting cpython-standard exceptions
Changes between 1.3.2 and 1.3.3
==================================================
- fix issue113: assertion representation problem with triple-quoted strings
(and possibly other cases)
- make conftest loading detect that a conftest file with the same
content was already loaded, avoids surprises in nested directory structures
which can be produced e.g. by Hudson. It probably removes the need to use
--confcutdir in most cases.
- fix terminal coloring for win32
(thanks Michael Foord for reporting)
- fix weirdness: make terminal width detection work on stdout instead of stdin
(thanks Armin Ronacher for reporting)
- remove trailing whitespace in all py/text distribution files
Changes between 1.3.1 and 1.3.2
==================================================
New features
++++++++++++++++++
- fix issue103: introduce py.test.raises as context manager, examples::
with py.test.raises(ZeroDivisionError):
x = 0
1 / x
with py.test.raises(RuntimeError) as excinfo:
call_something()
# you may do extra checks on excinfo.value|type|traceback here
(thanks Ronny Pfannschmidt)
- Funcarg factories can now dynamically apply a marker to a
test invocation. This is for example useful if a factory
provides parameters to a test which are expected-to-fail::
def pytest_funcarg__arg(request):
request.applymarker(py.test.mark.xfail(reason="flaky config"))
...
def test_function(arg):
...
- improved error reporting on collection and import errors. This makes
use of a more general mechanism, namely that for custom test item/collect
nodes ``node.repr_failure(excinfo)`` is now uniformly called so that you can
override it to return a string error representation of your choice
which is going to be reported as a (red) string.
- introduce '--junitprefix=STR' option to prepend a prefix
to all reports in the junitxml file.
Bug fixes / Maintenance
++++++++++++++++++++++++++
- make tests and the ``pytest_recwarn`` plugin in particular fully compatible
to Python2.7 (if you use the ``recwarn`` funcarg warnings will be enabled so that
you can properly check for their existence in a cross-python manner).
- refine --pdb: ignore xfailed tests, unify its TB-reporting and
don't display failures again at the end.
- fix assertion interpretation with the ** operator (thanks Benjamin Peterson)
- fix issue105 assignment on the same line as a failing assertion (thanks Benjamin Peterson)
- fix issue104 proper escaping for test names in junitxml plugin (thanks anonymous)
- fix issue57 -f|--looponfail to work with xpassing tests (thanks Ronny)
- fix issue92 collectonly reporter and --pastebin (thanks Benjamin Peterson)
- fix py.code.compile(source) to generate unique filenames
- fix assertion re-interp problems on PyPy, by defering code
compilation to the (overridable) Frame.eval class. (thanks Amaury Forgeot)
- fix py.path.local.pyimport() to work with directories
- streamline py.path.local.mkdtemp implementation and usage
- don't print empty lines when showing junitxml-filename
- add optional boolean ignore_errors parameter to py.path.local.remove
- fix terminal writing on win32/python2.4
- py.process.cmdexec() now tries harder to return properly encoded unicode objects
on all python versions
- install plain py.test/py.which scripts also for Jython, this helps to
get canonical script paths in virtualenv situations
- make path.bestrelpath(path) return ".", note that when calling
X.bestrelpath the assumption is that X is a directory.
- make initial conftest discovery ignore "--" prefixed arguments
- fix resultlog plugin when used in an multicpu/multihost xdist situation
(thanks Jakub Gustak)
- perform distributed testing related reporting in the xdist-plugin
rather than having dist-related code in the generic py.test
distribution
- fix homedir detection on Windows
- ship distribute_setup.py version 0.6.13
Changes between 1.3.0 and 1.3.1
==================================================
New features
++++++++++++++++++
- issue91: introduce new py.test.xfail(reason) helper
to imperatively mark a test as expected to fail. Can
be used from within setup and test functions. This is
useful especially for parametrized tests when certain
configurations are expected-to-fail. In this case the
declarative approach with the @py.test.mark.xfail cannot
be used as it would mark all configurations as xfail.
- issue102: introduce new --maxfail=NUM option to stop
test runs after NUM failures. This is a generalization
of the '-x' or '--exitfirst' option which is now equivalent
to '--maxfail=1'. Both '-x' and '--maxfail' will
now also print a line near the end indicating the Interruption.
- issue89: allow py.test.mark decorators to be used on classes
(class decorators were introduced with python2.6) and
also allow to have multiple markers applied at class/module level
by specifying a list.
- improve and refine letter reporting in the progress bar:
. pass
f failed test
s skipped tests (reminder: use for dependency/platform mismatch only)
x xfailed test (test that was expected to fail)
X xpassed test (test that was expected to fail but passed)
You can use any combination of 'fsxX' with the '-r' extended
reporting option. The xfail/xpass results will show up as
skipped tests in the junitxml output - which also fixes
issue99.
- make py.test.cmdline.main() return the exitstatus instead of raising
SystemExit and also allow it to be called multiple times. This of
course requires that your application and tests are properly teared
down and don't have global state.
Fixes / Maintenance
++++++++++++++++++++++
- improved traceback presentation:
- improved and unified reporting for "--tb=short" option
- Errors during test module imports are much shorter, (using --tb=short style)
- raises shows shorter more relevant tracebacks
- --fulltrace now more systematically makes traces longer / inhibits cutting
- improve support for raises and other dynamically compiled code by
manipulating python's linecache.cache instead of the previous
rather hacky way of creating custom code objects. This makes
it seemlessly work on Jython and PyPy where it previously didn't.
- fix issue96: make capturing more resilient against Control-C
interruptions (involved somewhat substantial refactoring
to the underlying capturing functionality to avoid race
conditions).
- fix chaining of conditional skipif/xfail decorators - so it works now
as expected to use multiple @py.test.mark.skipif(condition) decorators,
including specific reporting which of the conditions lead to skipping.
- fix issue95: late-import zlib so that it's not required
for general py.test startup.
- fix issue94: make reporting more robust against bogus source code
(and internally be more careful when presenting unexpected byte sequences)
Changes between 1.2.1 and 1.3.0
==================================================
- deprecate --report option in favour of a new shorter and easier to
remember -r option: it takes a string argument consisting of any
combination of 'xfsX' characters. They relate to the single chars
you see during the dotted progress printing and will print an extra line
per test at the end of the test run. This extra line indicates the exact
position or test ID that you directly paste to the py.test cmdline in order
to re-run a particular test.
- allow external plugins to register new hooks via the new
pytest_addhooks(pluginmanager) hook. The new release of
the pytest-xdist plugin for distributed and looponfailing
testing requires this feature.
- add a new pytest_ignore_collect(path, config) hook to allow projects and
plugins to define exclusion behaviour for their directory structure -
for example you may define in a conftest.py this method::
def pytest_ignore_collect(path):
return path.check(link=1)
to prevent even a collection try of any tests in symlinked dirs.
- new pytest_pycollect_makemodule(path, parent) hook for
allowing customization of the Module collection object for a
matching test module.
- extend and refine xfail mechanism:
``@py.test.mark.xfail(run=False)`` do not run the decorated test
``@py.test.mark.xfail(reason="...")`` prints the reason string in xfail summaries
specifiying ``--runxfail`` on command line virtually ignores xfail markers
- expose (previously internal) commonly useful methods:
py.io.get_terminal_with() -> return terminal width
py.io.ansi_print(...) -> print colored/bold text on linux/win32
py.io.saferepr(obj) -> return limited representation string
- expose test outcome related exceptions as py.test.skip.Exception,
py.test.raises.Exception etc., useful mostly for plugins
doing special outcome interpretation/tweaking
- (issue85) fix junitxml plugin to handle tests with non-ascii output
- fix/refine python3 compatibility (thanks Benjamin Peterson)
- fixes for making the jython/win32 combination work, note however:
jython2.5.1/win32 does not provide a command line launcher, see
http://bugs.jython.org/issue1491 . See pylib install documentation
for how to work around.
- fixes for handling of unicode exception values and unprintable objects
- (issue87) fix unboundlocal error in assertionold code
- (issue86) improve documentation for looponfailing
- refine IO capturing: stdin-redirect pseudo-file now has a NOP close() method
- ship distribute_setup.py version 0.6.10
- added links to the new capturelog and coverage plugins
Changes between 1.2.1 and 1.2.0
=====================================
- refined usage and options for "py.cleanup"::
py.cleanup # remove "*.pyc" and "*$py.class" (jython) files
py.cleanup -e .swp -e .cache # also remove files with these extensions
py.cleanup -s # remove "build" and "dist" directory next to setup.py files
py.cleanup -d # also remove empty directories
py.cleanup -a # synonym for "-s -d -e 'pip-log.txt'"
py.cleanup -n # dry run, only show what would be removed
- add a new option "py.test --funcargs" which shows available funcargs
and their help strings (docstrings on their respective factory function)
for a given test path
- display a short and concise traceback if a funcarg lookup fails
- early-load "conftest.py" files in non-dot first-level sub directories.
allows to conveniently keep and access test-related options in a ``test``
subdir and still add command line options.
- fix issue67: new super-short traceback-printing option: "--tb=line" will print a single line for each failing (python) test indicating its filename, lineno and the failure value
- fix issue78: always call python-level teardown functions even if the
according setup failed. This includes refinements for calling setup_module/class functions
which will now only be called once instead of the previous behaviour where they'd be called
multiple times if they raise an exception (including a Skipped exception). Any exception
will be re-corded and associated with all tests in the according module/class scope.
- fix issue63: assume <40 columns to be a bogus terminal width, default to 80
- fix pdb debugging to be in the correct frame on raises-related errors
- update apipkg.py to fix an issue where recursive imports might
unnecessarily break importing
- fix plugin links
Changes between 1.2 and 1.1.1
=====================================
- moved dist/looponfailing from py.test core into a new
separately released pytest-xdist plugin.
- new junitxml plugin: --junitxml=path will generate a junit style xml file
which is processable e.g. by the Hudson CI system.
- new option: --genscript=path will generate a standalone py.test script
which will not need any libraries installed. thanks to Ralf Schmitt.
- new option: --ignore will prevent specified path from collection.
Can be specified multiple times.
- new option: --confcutdir=dir will make py.test only consider conftest
files that are relative to the specified dir.
- new funcarg: "pytestconfig" is the pytest config object for access
to command line args and can now be easily used in a test.
- install 'py.test' and `py.which` with a ``-$VERSION`` suffix to
disambiguate between Python3, python2.X, Jython and PyPy installed versions.
- new "pytestconfig" funcarg allows access to test config object
- new "pytest_report_header" hook can return additional lines
to be displayed at the header of a test run.
- (experimental) allow "py.test path::name1::name2::..." for pointing
to a test within a test collection directly. This might eventually
evolve as a full substitute to "-k" specifications.
- streamlined plugin loading: order is now as documented in
customize.html: setuptools, ENV, commandline, conftest.
also setuptools entry point names are turned to canonical namees ("pytest_*")
- automatically skip tests that need 'capfd' but have no os.dup
- allow pytest_generate_tests to be defined in classes as well
- deprecate usage of 'disabled' attribute in favour of pytestmark
- deprecate definition of Directory, Module, Class and Function nodes
in conftest.py files. Use pytest collect hooks instead.
- collection/item node specific runtest/collect hooks are only called exactly
on matching conftest.py files, i.e. ones which are exactly below
the filesystem path of an item
- change: the first pytest_collect_directory hook to return something
will now prevent further hooks to be called.
- change: figleaf plugin now requires --figleaf to run. Also
change its long command line options to be a bit shorter (see py.test -h).
- change: pytest doctest plugin is now enabled by default and has a
new option --doctest-glob to set a pattern for file matches.
- change: remove internal py._* helper vars, only keep py._pydir
- robustify capturing to survive if custom pytest_runtest_setup
code failed and prevented the capturing setup code from running.
- make py.test.* helpers provided by default plugins visible early -
works transparently both for pydoc and for interactive sessions
which will regularly see e.g. py.test.mark and py.test.importorskip.
- simplify internal plugin manager machinery
- simplify internal collection tree by introducing a RootCollector node
- fix assert reinterpreation that sees a call containing "keyword=..."
- fix issue66: invoke pytest_sessionstart and pytest_sessionfinish
hooks on slaves during dist-testing, report module/session teardown
hooks correctly.
- fix issue65: properly handle dist-testing if no
execnet/py lib installed remotely.
- skip some install-tests if no execnet is available
- fix docs, fix internal bin/ script generation
Changes between 1.1.1 and 1.1.0
=====================================
- introduce automatic plugin registration via 'pytest11'
entrypoints via setuptools' pkg_resources.iter_entry_points
- fix py.test dist-testing to work with execnet >= 1.0.0b4
- re-introduce py.test.cmdline.main() for better backward compatibility
- svn paths: fix a bug with path.check(versioned=True) for svn paths,
allow '%' in svn paths, make svnwc.update() default to interactive mode
like in 1.0.x and add svnwc.update(interactive=False) to inhibit interaction.
- refine distributed tarball to contain test and no pyc files
- try harder to have deprecation warnings for py.compat.* accesses
report a correct location
Changes between 1.1.0 and 1.0.2
=====================================
* adjust and improve docs
* remove py.rest tool and internal namespace - it was
never really advertised and can still be used with
the old release if needed. If there is interest
it could be revived into its own tool i guess.
* fix issue48 and issue59: raise an Error if the module
from an imported test file does not seem to come from
the filepath - avoids "same-name" confusion that has
been reported repeatedly
* merged Ronny's nose-compatibility hacks: now
nose-style setup_module() and setup() functions are
supported
* introduce generalized py.test.mark function marking
* reshuffle / refine command line grouping
* deprecate parser.addgroup in favour of getgroup which creates option group
* add --report command line option that allows to control showing of skipped/xfailed sections
* generalized skipping: a new way to mark python functions with skipif or xfail
at function, class and modules level based on platform or sys-module attributes.
* extend py.test.mark decorator to allow for positional args
* introduce and test "py.cleanup -d" to remove empty directories
* fix issue #59 - robustify unittest test collection
* make bpython/help interaction work by adding an __all__ attribute
to ApiModule, cleanup initpkg
* use MIT license for pylib, add some contributors
* remove py.execnet code and substitute all usages with 'execnet' proper
* fix issue50 - cached_setup now caches more to expectations
for test functions with multiple arguments.
* merge Jarko's fixes, issue #45 and #46
* add the ability to specify a path for py.lookup to search in
* fix a funcarg cached_setup bug probably only occuring
in distributed testing and "module" scope with teardown.
* many fixes and changes for making the code base python3 compatible,
many thanks to Benjamin Peterson for helping with this.
* consolidate builtins implementation to be compatible with >=2.3,
add helpers to ease keeping 2 and 3k compatible code
* deprecate py.compat.doctest|subprocess|textwrap|optparse
* deprecate py.magic.autopath, remove py/magic directory
* move pytest assertion handling to py/code and a pytest_assertion
plugin, add "--no-assert" option, deprecate py.magic namespaces
in favour of (less) py.code ones.
* consolidate and cleanup py/code classes and files
* cleanup py/misc, move tests to bin-for-dist
* introduce delattr/delitem/delenv methods to py.test's monkeypatch funcarg
* consolidate py.log implementation, remove old approach.
* introduce py.io.TextIO and py.io.BytesIO for distinguishing between
text/unicode and byte-streams (uses underlying standard lib io.*
if available)
* make py.unittest_convert helper script available which converts "unittest.py"
style files into the simpler assert/direct-test-classes py.test/nosetests
style. The script was written by Laura Creighton.
* simplified internal localpath implementation
Changes between 1.0.1 and 1.0.2
=====================================
* fixing packaging issues, triggered by fedora redhat packaging,
also added doc, examples and contrib dirs to the tarball.
* added a documentation link to the new django plugin.
Changes between 1.0.0 and 1.0.1
=====================================
* added a 'pytest_nose' plugin which handles nose.SkipTest,
nose-style function/method/generator setup/teardown and
tries to report functions correctly.
* capturing of unicode writes or encoded strings to sys.stdout/err
work better, also terminalwriting was adapted and somewhat
unified between windows and linux.
* improved documentation layout and content a lot
* added a "--help-config" option to show conftest.py / ENV-var names for
all longopt cmdline options, and some special conftest.py variables.
renamed 'conf_capture' conftest setting to 'option_capture' accordingly.
* fix issue #27: better reporting on non-collectable items given on commandline
(e.g. pyc files)
* fix issue #33: added --version flag (thanks Benjamin Peterson)
* fix issue #32: adding support for "incomplete" paths to wcpath.status()
* "Test" prefixed classes are *not* collected by default anymore if they
have an __init__ method
* monkeypatch setenv() now accepts a "prepend" parameter
* improved reporting of collection error tracebacks
* simplified multicall mechanism and plugin architecture,
renamed some internal methods and argnames
Changes between 1.0.0b9 and 1.0.0
=====================================
* more terse reporting try to show filesystem path relatively to current dir
* improve xfail output a bit
Changes between 1.0.0b8 and 1.0.0b9
=====================================
* cleanly handle and report final teardown of test setup
* fix svn-1.6 compat issue with py.path.svnwc().versioned()
(thanks Wouter Vanden Hove)
* setup/teardown or collection problems now show as ERRORs
or with big "E"'s in the progress lines. they are reported
and counted separately.
* dist-testing: properly handle test items that get locally
collected but cannot be collected on the remote side - often
due to platform/dependency reasons
* simplified py.test.mark API - see keyword plugin documentation
* integrate better with logging: capturing now by default captures
test functions and their immediate setup/teardown in a single stream
* capsys and capfd funcargs now have a readouterr() and a close() method
(underlyingly py.io.StdCapture/FD objects are used which grew a
readouterr() method as well to return snapshots of captured out/err)
* make assert-reinterpretation work better with comparisons not
returning bools (reported with numpy from thanks maciej fijalkowski)
* reworked per-test output capturing into the pytest_iocapture.py plugin
and thus removed capturing code from config object
* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr)
Changes between 1.0.0b7 and 1.0.0b8
=====================================
* pytest_unittest-plugin is now enabled by default
* introduced pytest_keyboardinterrupt hook and
refined pytest_sessionfinish hooked, added tests.
* workaround a buggy logging module interaction ("closing already closed
files"). Thanks to Sridhar Ratnakumar for triggering.
* if plugins use "py.test.importorskip" for importing
a dependency only a warning will be issued instead
of exiting the testing process.
* many improvements to docs:
- refined funcargs doc , use the term "factory" instead of "provider"
- added a new talk/tutorial doc page
- better download page
- better plugin docstrings
- added new plugins page and automatic doc generation script
* fixed teardown problem related to partially failing funcarg setups
(thanks MrTopf for reporting), "pytest_runtest_teardown" is now
always invoked even if the "pytest_runtest_setup" failed.
* tweaked doctest output for docstrings in py modules,
thanks Radomir.
Changes between 1.0.0b3 and 1.0.0b7
=============================================
* renamed py.test.xfail back to py.test.mark.xfail to avoid
two ways to decorate for xfail
* re-added py.test.mark decorator for setting keywords on functions
(it was actually documented so removing it was not nice)
* remove scope-argument from request.addfinalizer() because
request.cached_setup has the scope arg. TOOWTDI.
* perform setup finalization before reporting failures
* apply modified patches from Andreas Kloeckner to allow
test functions to have no func_code (#22) and to make
"-k" and function keywords work (#20)
* apply patch from Daniel Peolzleithner (issue #23)
* resolve issue #18, multiprocessing.Manager() and
redirection clash
* make __name__ == "__channelexec__" for remote_exec code
Changes between 1.0.0b1 and 1.0.0b3
=============================================
* plugin classes are removed: one now defines
hooks directly in conftest.py or global pytest_*.py
files.
* added new pytest_namespace(config) hook that allows
to inject helpers directly to the py.test.* namespace.
* documented and refined many hooks
* added new style of generative tests via
pytest_generate_tests hook that integrates
well with function arguments.
Changes between 0.9.2 and 1.0.0b1
=============================================
* introduced new "funcarg" setup method,
see doc/test/funcarg.txt
* introduced plugin architecuture and many
new py.test plugins, see
doc/test/plugins.txt
* teardown_method is now guaranteed to get
called after a test method has run.
* new method: py.test.importorskip(mod,minversion)
will either import or call py.test.skip()
* completely revised internal py.test architecture
* new py.process.ForkedFunc object allowing to
fork execution of a function to a sub process
and getting a result back.
XXX lots of things missing here XXX
Changes between 0.9.1 and 0.9.2
===============================
* refined installation and metadata, created new setup.py,
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.
* 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
* removed previously accidentally added
py.test.broken and py.test.notimplemented helpers.
* there now is a py.__version__ attribute
Changes between 0.9.0 and 0.9.1
===============================
This is a fairly complete list of changes between 0.9 and 0.9.1, which can
serve as a reference for developers.
* allowing + signs in py.path.svn urls [39106]
* fixed support for Failed exceptions without excinfo in py.test [39340]
* added support for killing processes for Windows (as well as platforms that
support os.kill) in py.misc.killproc [39655]
* added setup/teardown for generative tests to py.test [40702]
* added detection of FAILED TO LOAD MODULE to py.test [40703, 40738, 40739]
* fixed problem with calling .remove() on wcpaths of non-versioned files in
py.path [44248]
* fixed some import and inheritance issues in py.test [41480, 44648, 44655]
* fail to run greenlet tests when pypy is available, but without stackless
[45294]
* small fixes in rsession tests [45295]
* fixed issue with 2.5 type representations in py.test [45483, 45484]
* made that internal reporting issues displaying is done atomically in py.test
[45518]
* made that non-existing files are igored by the py.lookup script [45519]
* improved exception name creation in py.test [45535]
* made that less threads are used in execnet [merge in 45539]
* removed lock required for atomical reporting issue displaying in py.test
[45545]
* removed globals from execnet [45541, 45547]
* refactored cleanup mechanics, made that setDaemon is set to 1 to make atexit
get called in 2.5 (py.execnet) [45548]
* fixed bug in joining threads in py.execnet's servemain [45549]
* refactored py.test.rsession tests to not rely on exact output format anymore
[45646]
* using repr() on test outcome [45647]
* added 'Reason' classes for py.test.skip() [45648, 45649]
* killed some unnecessary sanity check in py.test.collect [45655]
* avoid using os.tmpfile() in py.io.fdcapture because on Windows it's only
usable by Administrators [45901]
* added support for locking and non-recursive commits to py.path.svnwc [45994]
* locking files in py.execnet to prevent CPython from segfaulting [46010]
* added export() method to py.path.svnurl
* fixed -d -x in py.test [47277]
* fixed argument concatenation problem in py.path.svnwc [49423]
* restore py.test behaviour that it exits with code 1 when there are failures
[49974]
* don't fail on html files that don't have an accompanying .txt file [50606]
* fixed 'utestconvert.py < input' [50645]
* small fix for code indentation in py.code.source [50755]
* fix _docgen.py documentation building [51285]
* improved checks for source representation of code blocks in py.test [51292]
* added support for passing authentication to py.path.svn* objects [52000,
52001]
* removed sorted() call for py.apigen tests in favour of [].sort() to support
Python 2.3 [52481]
py-1.5.2/testing/ 0000775 0001750 0001750 00000000000 13203150701 014744 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/testing/conftest.py 0000664 0001750 0001750 00000000037 13202663617 017161 0 ustar vagrant vagrant 0000000 0000000
pytest_plugins = "pytester",
py-1.5.2/testing/root/ 0000775 0001750 0001750 00000000000 13203150701 015727 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/testing/root/test_py_imports.py 0000664 0001750 0001750 00000003671 13203150646 021564 0 ustar vagrant vagrant 0000000 0000000 import py
import sys
@py.test.mark.parametrize('name', [x for x in dir(py) if x[0] != '_'])
def test_dir(name):
obj = getattr(py, name)
if hasattr(obj, '__map__'): # isinstance(obj, Module):
keys = dir(obj)
assert len(keys) > 0
print (obj.__map__)
for name in list(obj.__map__):
assert hasattr(obj, name), (obj, name)
def test_virtual_module_identity():
from py import path as path1
from py import path as path2
assert path1 is path2
from py.path import local as local1
from py.path import local as local2
assert local1 is local2
def test_importall():
base = py._pydir
nodirs = [
]
if sys.version_info >= (3, 0):
nodirs.append(base.join('_code', '_assertionold.py'))
else:
nodirs.append(base.join('_code', '_assertionnew.py'))
def recurse(p):
return p.check(dotfile=0) and p.basename != "attic"
for p in base.visit('*.py', recurse):
if p.basename == '__init__.py':
continue
relpath = p.new(ext='').relto(base)
if base.sep in relpath: # not py/*.py itself
for x in nodirs:
if p == x or p.relto(x):
break
else:
relpath = relpath.replace(base.sep, '.')
modpath = 'py.%s' % relpath
try:
check_import(modpath)
except py.test.skip.Exception:
pass
def check_import(modpath):
py.builtin.print_("checking import", modpath)
assert __import__(modpath)
def test_star_import():
exec("from py import *")
def test_all_resolves():
seen = py.builtin.set([py])
lastlength = None
while len(seen) != lastlength:
lastlength = len(seen)
for item in py.builtin.frozenset(seen):
for value in item.__dict__.values():
if isinstance(value, type(py.test)):
seen.add(value)
py-1.5.2/testing/root/test_error.py 0000664 0001750 0001750 00000003440 13202663755 020513 0 ustar vagrant vagrant 0000000 0000000
import py
import errno
def test_error_classes():
for name in errno.errorcode.values():
x = getattr(py.error, name)
assert issubclass(x, py.error.Error)
assert issubclass(x, EnvironmentError)
def test_has_name():
assert py.error.__name__ == 'py.error'
def test_picklability_issue1():
import pickle
e1 = py.error.ENOENT()
s = pickle.dumps(e1)
e2 = pickle.loads(s)
assert isinstance(e2, py.error.ENOENT)
def test_unknown_error():
num = 3999
cls = py.error._geterrnoclass(num)
assert cls.__name__ == 'UnknownErrno%d' % (num,)
assert issubclass(cls, py.error.Error)
assert issubclass(cls, EnvironmentError)
cls2 = py.error._geterrnoclass(num)
assert cls is cls2
def test_error_conversion_ENOTDIR(testdir):
p = testdir.makepyfile("")
excinfo = py.test.raises(py.error.Error, py.error.checked_call, p.listdir)
assert isinstance(excinfo.value, EnvironmentError)
assert isinstance(excinfo.value, py.error.Error)
assert "ENOTDIR" in repr(excinfo.value)
def test_checked_call_supports_kwargs(tmpdir):
import tempfile
py.error.checked_call(tempfile.mkdtemp, dir=str(tmpdir))
try:
import unittest
unittest.TestCase.assertWarns
except (ImportError, AttributeError):
pass # required interface not available
else:
import sys
import warnings
class Case(unittest.TestCase):
def test_assertWarns(self):
# Clear everything "py.*" from sys.modules and re-import py
# as a fresh start
for mod in tuple(sys.modules.keys()):
if mod and (mod == 'py' or mod.startswith('py.')):
del sys.modules[mod]
import py
with self.assertWarns(UserWarning):
warnings.warn('this should work')
py-1.5.2/testing/root/__init__.py 0000664 0001750 0001750 00000000002 13202663617 020046 0 ustar vagrant vagrant 0000000 0000000 #
py-1.5.2/testing/root/test_std.py 0000664 0001750 0001750 00000000416 13202663617 020151 0 ustar vagrant vagrant 0000000 0000000
import py
def test_os():
import os
assert py.std.os is os
def test_import_error_converts_to_attributeerror():
py.test.raises(AttributeError, "py.std.xyzalskdj")
def test_std_gets_it():
for x in py.std.sys.modules:
assert x in py.std.__dict__
py-1.5.2/testing/root/test_xmlgen.py 0000664 0001750 0001750 00000007645 13202663755 020667 0 ustar vagrant vagrant 0000000 0000000
import py
from py._xmlgen import unicode, html, raw
import sys
class ns(py.xml.Namespace):
pass
def test_escape():
uvalue = py.builtin._totext('\xc4\x85\xc4\x87\n\xe2\x82\xac\n', 'utf-8')
class A:
def __unicode__(self):
return uvalue
def __str__(self):
x = self.__unicode__()
if sys.version_info[0] < 3:
return x.encode('utf-8')
return x
y = py.xml.escape(uvalue)
assert y == uvalue
x = py.xml.escape(A())
assert x == uvalue
if sys.version_info[0] < 3:
assert isinstance(x, unicode)
assert isinstance(y, unicode)
y = py.xml.escape(uvalue.encode('utf-8'))
assert y == uvalue
def test_tag_with_text():
x = ns.hello("world")
u = unicode(x)
assert u == "world"
def test_class_identity():
assert ns.hello is ns.hello
def test_tag_with_text_and_attributes():
x = ns.some(name="hello", value="world")
assert x.attr.name == 'hello'
assert x.attr.value == 'world'
u = unicode(x)
assert u == ''
def test_tag_with_subclassed_attr_simple():
class my(ns.hello):
class Attr(ns.hello.Attr):
hello="world"
x = my()
assert x.attr.hello == 'world'
assert unicode(x) == ''
def test_tag_with_raw_attr():
x = html.object(data=raw('&'))
assert unicode(x) == ''
def test_tag_nested():
x = ns.hello(ns.world())
unicode(x) # triggers parentifying
assert x[0].parent is x
u = unicode(x)
assert u == ''
def test_list_nested():
x = ns.hello([ns.world()]) #pass in a list here
u = unicode(x)
assert u == ''
def test_tag_xmlname():
class my(ns.hello):
xmlname = 'world'
u = unicode(my())
assert u == ''
def test_tag_with_text_entity():
x = ns.hello('world & rest')
u = unicode(x)
assert u == "world & rest"
def test_tag_with_text_and_attributes_entity():
x = ns.some(name="hello & world")
assert x.attr.name == "hello & world"
u = unicode(x)
assert u == ''
def test_raw():
x = ns.some(py.xml.raw("
literal
"))
u = unicode(x)
assert u == "
literal
"
def test_html_name_stickyness():
class my(html.p):
pass
x = my("hello")
assert unicode(x) == '
hello
'
def test_stylenames():
class my:
class body(html.body):
style = html.Style(font_size = "12pt")
u = unicode(my.body())
assert u == ''
def test_class_None():
t = html.body(class_=None)
u = unicode(t)
assert u == ''
def test_alternating_style():
alternating = (
html.Style(background="white"),
html.Style(background="grey"),
)
class my(html):
class li(html.li):
def style(self):
i = self.parent.index(self)
return alternating[i%2]
style = property(style)
x = my.ul(
my.li("hello"),
my.li("world"),
my.li("42"))
u = unicode(x)
assert u == ('
hello
'
'
world
'
'
42
'
'
')
def test_singleton():
h = html.head(html.link(href="foo"))
assert unicode(h) == ''
h = html.head(html.script(src="foo"))
assert unicode(h) == ''
def test_inline():
h = html.div(html.span('foo'), html.span('bar'))
assert (h.unicode(indent=2) ==
'
foobar
')
def test_object_tags():
o = html.object(html.object())
assert o.unicode(indent=0) == ''
py-1.5.2/testing/root/test_builtin.py 0000664 0001750 0001750 00000012071 13202663617 021025 0 ustar vagrant vagrant 0000000 0000000 import sys
import types
import py
from py.builtin import set, frozenset, reversed, sorted
def test_enumerate():
l = [0,1,2]
for i,x in enumerate(l):
assert i == x
def test_any():
assert not py.builtin.any([0,False, None])
assert py.builtin.any([0,False, None,1])
def test_all():
assert not py.builtin.all([True, 1, False])
assert py.builtin.all([True, 1, object])
def test_BaseException():
assert issubclass(IndexError, py.builtin.BaseException)
assert issubclass(Exception, py.builtin.BaseException)
assert issubclass(KeyboardInterrupt, py.builtin.BaseException)
class MyRandomClass(object):
pass
assert not issubclass(MyRandomClass, py.builtin.BaseException)
assert py.builtin.BaseException.__module__ in ('exceptions', 'builtins')
assert Exception.__name__ == 'Exception'
def test_GeneratorExit():
assert py.builtin.GeneratorExit.__module__ in ('exceptions', 'builtins')
assert issubclass(py.builtin.GeneratorExit, py.builtin.BaseException)
def test_reversed():
reversed = py.builtin.reversed
r = reversed("hello")
assert iter(r) is r
s = "".join(list(r))
assert s == "olleh"
assert list(reversed(list(reversed("hello")))) == ['h','e','l','l','o']
py.test.raises(TypeError, reversed, reversed("hello"))
def test_simple():
s = set([1, 2, 3, 4])
assert s == set([3, 4, 2, 1])
s1 = s.union(set([5, 6]))
assert 5 in s1
assert 1 in s1
def test_frozenset():
s = set([frozenset([0, 1]), frozenset([1, 0])])
assert len(s) == 1
def test_sorted():
if sorted == py.builtin.sorted:
return # don't test a real builtin
for s in [py.builtin.sorted]:
def test():
assert s([3, 2, 1]) == [1, 2, 3]
assert s([1, 2, 3], reverse=True) == [3, 2, 1]
l = s([1, 2, 3, 4, 5, 6], key=lambda x: x % 2)
assert l == [2, 4, 6, 1, 3, 5]
l = s([1, 2, 3, 4], cmp=lambda x, y: -cmp(x, y))
assert l == [4, 3, 2, 1]
l = s([1, 2, 3, 4], cmp=lambda x, y: -cmp(x, y),
key=lambda x: x % 2)
assert l == [1, 3, 2, 4]
def compare(x, y):
assert type(x) == str
assert type(y) == str
return cmp(x, y)
data = 'The quick Brown fox Jumped over The lazy Dog'.split()
s(data, cmp=compare, key=str.lower)
yield test
def test_print_simple():
from py.builtin import print_
py.test.raises(TypeError, "print_(hello=3)")
f = py.io.TextIO()
print_("hello", "world", file=f)
s = f.getvalue()
assert s == "hello world\n"
f = py.io.TextIO()
print_("hello", end="", file=f)
s = f.getvalue()
assert s == "hello"
f = py.io.TextIO()
print_("xyz", "abc", sep="", end="", file=f)
s = f.getvalue()
assert s == "xyzabc"
class X:
def __repr__(self): return "rep"
f = py.io.TextIO()
print_(X(), file=f)
assert f.getvalue() == "rep\n"
def test_execfile(tmpdir):
test_file = tmpdir.join("test.py")
test_file.write("x = y\ndef f(): pass")
ns = {"y" : 42}
py.builtin.execfile(str(test_file), ns)
assert ns["x"] == 42
assert py.code.getrawcode(ns["f"]).co_filename == str(test_file)
class A:
y = 3
x = 4
py.builtin.execfile(str(test_file))
assert A.x == 3
def test_getfuncdict():
def f():
pass
f.x = 4
assert py.builtin._getfuncdict(f)["x"] == 4
assert py.builtin._getfuncdict(2) is None
def test_callable():
class A: pass
assert py.builtin.callable(test_callable)
assert py.builtin.callable(A)
assert py.builtin.callable(list)
assert py.builtin.callable(id)
assert not py.builtin.callable(4)
assert not py.builtin.callable("hi")
def test_totext():
py.builtin._totext("hello", "UTF-8")
def test_bytes_text():
if sys.version_info[0] < 3:
assert py.builtin.text == unicode
assert py.builtin.bytes == str
else:
assert py.builtin.text == str
assert py.builtin.bytes == bytes
def test_totext_badutf8():
# this was in printouts within the pytest testsuite
# totext would fail
if sys.version_info >= (3,):
errors = 'surrogateescape'
else: # old python has crappy error handlers
errors = 'replace'
py.builtin._totext("\xa6", "UTF-8", errors)
def test_reraise():
from py.builtin import _reraise
try:
raise Exception()
except Exception:
cls, val, tb = sys.exc_info()
excinfo = py.test.raises(Exception, "_reraise(cls, val, tb)")
def test_exec():
l = []
py.builtin.exec_("l.append(1)")
assert l == [1]
d = {}
py.builtin.exec_("x=4", d)
assert d['x'] == 4
def test_tryimport():
py.test.raises(ImportError, py.builtin._tryimport, 'xqwe123')
x = py.builtin._tryimport('asldkajsdl', 'py')
assert x == py
x = py.builtin._tryimport('asldkajsdl', 'py.path')
assert x == py.path
def test_getcode():
code = py.builtin._getcode(test_getcode)
assert isinstance(code, types.CodeType)
assert py.builtin._getcode(4) is None
py-1.5.2/testing/path/ 0000775 0001750 0001750 00000000000 13203150701 015700 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/testing/path/conftest.py 0000664 0001750 0001750 00000005175 13202663755 020130 0 ustar vagrant vagrant 0000000 0000000 import py
import sys
from py._path import svnwc as svncommon
svnbin = py.path.local.sysfind('svn')
repodump = py.path.local(__file__).dirpath('repotest.dump')
from py.builtin import print_
def pytest_funcarg__repowc1(request):
if svnbin is None:
py.test.skip("svn binary not found")
tmpdir = request.getfuncargvalue("tmpdir")
repo, repourl, wc = request.cached_setup(
setup=lambda: getrepowc(tmpdir, "path1repo", "path1wc"),
scope="module",
)
for x in ('test_remove', 'test_move', 'test_status_deleted'):
if request.function.__name__.startswith(x):
#print >>sys.stderr, ("saving repo", repo, "for", request.function)
_savedrepowc = save_repowc(repo, wc)
request.addfinalizer(lambda: restore_repowc(_savedrepowc))
return repo, repourl, wc
def pytest_funcarg__repowc2(request):
tmpdir = request.getfuncargvalue("tmpdir")
name = request.function.__name__
repo, url, wc = getrepowc(tmpdir, "%s-repo-2" % name, "%s-wc-2" % name)
return repo, url, wc
def getsvnbin():
if svnbin is None:
py.test.skip("svn binary not found")
return svnbin
# make a wc directory out of a given root url
# cache previously obtained wcs!
#
def getrepowc(tmpdir, reponame='basetestrepo', wcname='wc'):
repo = tmpdir.mkdir(reponame)
wcdir = tmpdir.mkdir(wcname)
repo.ensure(dir=1)
py.process.cmdexec('svnadmin create "%s"' %
svncommon._escape_helper(repo))
py.process.cmdexec('svnadmin load -q "%s" <"%s"' %
(svncommon._escape_helper(repo), repodump))
print_("created svn repository", repo)
wcdir.ensure(dir=1)
wc = py.path.svnwc(wcdir)
if sys.platform == 'win32':
repourl = "file://" + '/' + str(repo).replace('\\', '/')
else:
repourl = "file://%s" % repo
wc.checkout(repourl)
print_("checked out new repo into", wc)
return (repo, repourl, wc)
def save_repowc(repo, wc):
assert not str(repo).startswith("file://"), repo
assert repo.check()
savedrepo = repo.dirpath(repo.basename+".1")
savedwc = wc.dirpath(wc.basename+".1")
repo.copy(savedrepo)
wc.localpath.copy(savedwc.localpath)
return savedrepo, savedwc
def restore_repowc(obj):
savedrepo, savedwc = obj
#print >>sys.stderr, ("restoring", savedrepo)
repo = savedrepo.new(basename=savedrepo.basename[:-2])
assert repo.check()
wc = savedwc.new(basename=savedwc.basename[:-2])
assert wc.check()
wc.localpath.remove()
repo.remove()
savedrepo.move(repo)
savedwc.localpath.move(wc.localpath)
py.path.svnurl._lsnorevcache.clear()
py.path.svnurl._lsrevcache.clear()
py-1.5.2/testing/path/test_cacheutil.py 0000664 0001750 0001750 00000005553 13202663755 021303 0 ustar vagrant vagrant 0000000 0000000 import pytest
from py._path import cacheutil
import time
class BasicCacheAPITest:
cache = None
def test_getorbuild(self):
val = self.cache.getorbuild(-42, lambda: 42)
assert val == 42
val = self.cache.getorbuild(-42, lambda: 23)
assert val == 42
def test_cache_get_key_error(self):
pytest.raises(KeyError, "self.cache._getentry(-23)")
def test_delentry_non_raising(self):
self.cache.getorbuild(100, lambda: 100)
self.cache.delentry(100)
pytest.raises(KeyError, "self.cache._getentry(100)")
def test_delentry_raising(self):
self.cache.getorbuild(100, lambda: 100)
self.cache.delentry(100)
pytest.raises(KeyError, self.cache.delentry, 100, raising=True)
def test_clear(self):
self.cache.clear()
class TestBuildcostAccess(BasicCacheAPITest):
cache = cacheutil.BuildcostAccessCache(maxentries=128)
def test_cache_works_somewhat_simple(self, monkeypatch):
cache = cacheutil.BuildcostAccessCache()
# the default gettime
# BuildcostAccessCache.build can
# result into time()-time() == 0 which makes the below
# test fail randomly. Let's rather use incrementing
# numbers instead.
l = [0]
def counter():
l[0] = l[0] + 1
return l[0]
monkeypatch.setattr(cacheutil, 'gettime', counter)
for x in range(cache.maxentries):
y = cache.getorbuild(x, lambda: x)
assert x == y
for x in range(cache.maxentries):
assert cache.getorbuild(x, None) == x
halfentries = int(cache.maxentries / 2)
for x in range(halfentries):
assert cache.getorbuild(x, None) == x
assert cache.getorbuild(x, None) == x
# evict one entry
val = cache.getorbuild(-1, lambda: 42)
assert val == 42
# check that recently used ones are still there
# and are not build again
for x in range(halfentries):
assert cache.getorbuild(x, None) == x
assert cache.getorbuild(-1, None) == 42
class TestAging(BasicCacheAPITest):
maxsecs = 0.10
cache = cacheutil.AgingCache(maxentries=128, maxseconds=maxsecs)
def test_cache_eviction(self):
self.cache.getorbuild(17, lambda: 17)
endtime = time.time() + self.maxsecs * 10
while time.time() < endtime:
try:
self.cache._getentry(17)
except KeyError:
break
time.sleep(self.maxsecs*0.3)
else:
pytest.fail("waiting for cache eviction failed")
def test_prune_lowestweight():
maxsecs = 0.05
cache = cacheutil.AgingCache(maxentries=10, maxseconds=maxsecs)
for x in range(cache.maxentries):
cache.getorbuild(x, lambda: x)
time.sleep(maxsecs*1.1)
cache.getorbuild(cache.maxentries+1, lambda: 42)
py-1.5.2/testing/path/test_svnwc.py 0000664 0001750 0001750 00000047152 13202663755 020503 0 ustar vagrant vagrant 0000000 0000000 import py
import os, sys
import pytest
from py._path.svnwc import InfoSvnWCCommand, XMLWCStatus, parse_wcinfotime
from py._path import svnwc as svncommon
from svntestbase import CommonSvnTests
pytestmark = pytest.mark.xfail(sys.platform.startswith('win'),
reason='#161 all tests in this file are failing on Windows',
run=False)
def test_make_repo(path1, tmpdir):
repo = tmpdir.join("repo")
py.process.cmdexec('svnadmin create %s' % repo)
if sys.platform == 'win32':
repo = '/' + str(repo).replace('\\', '/')
repo = py.path.svnurl("file://%s" % repo)
wc = py.path.svnwc(tmpdir.join("wc"))
wc.checkout(repo)
assert wc.rev == 0
assert len(wc.listdir()) == 0
p = wc.join("a_file")
p.write("test file")
p.add()
rev = wc.commit("some test")
assert p.info().rev == 1
assert rev == 1
rev = wc.commit()
assert rev is None
def pytest_funcarg__path1(request):
repo, repourl, wc = request.getfuncargvalue("repowc1")
return wc
class TestWCSvnCommandPath(CommonSvnTests):
def test_status_attributes_simple(self, path1):
def assert_nochange(p):
s = p.status()
assert not s.modified
assert not s.prop_modified
assert not s.added
assert not s.deleted
assert not s.replaced
dpath = path1.join('sampledir')
assert_nochange(path1.join('sampledir'))
assert_nochange(path1.join('samplefile'))
def test_status_added(self, path1):
nf = path1.join('newfile')
nf.write('hello')
nf.add()
try:
s = nf.status()
assert s.added
assert not s.modified
assert not s.prop_modified
assert not s.replaced
finally:
nf.revert()
def test_status_change(self, path1):
nf = path1.join('samplefile')
try:
nf.write(nf.read() + 'change')
s = nf.status()
assert not s.added
assert s.modified
assert not s.prop_modified
assert not s.replaced
finally:
nf.revert()
def test_status_added_ondirectory(self, path1):
sampledir = path1.join('sampledir')
try:
t2 = sampledir.mkdir('t2')
t1 = t2.join('t1')
t1.write('test')
t1.add()
s = sampledir.status(rec=1)
# Comparing just the file names, because paths are unpredictable
# on Windows. (long vs. 8.3 paths)
assert t1.basename in [item.basename for item in s.added]
assert t2.basename in [item.basename for item in s.added]
finally:
t2.revert(rec=1)
t2.localpath.remove(rec=1)
def test_status_unknown(self, path1):
t1 = path1.join('un1')
try:
t1.write('test')
s = path1.status()
# Comparing just the file names, because paths are unpredictable
# on Windows. (long vs. 8.3 paths)
assert t1.basename in [item.basename for item in s.unknown]
finally:
t1.localpath.remove()
def test_status_unchanged(self, path1):
r = path1
s = path1.status(rec=1)
# Comparing just the file names, because paths are unpredictable
# on Windows. (long vs. 8.3 paths)
assert r.join('samplefile').basename in [item.basename
for item in s.unchanged]
assert r.join('sampledir').basename in [item.basename
for item in s.unchanged]
assert r.join('sampledir/otherfile').basename in [item.basename
for item in s.unchanged]
def test_status_update(self, path1):
# not a mark because the global "pytestmark" will end up overwriting a mark here
pytest.xfail("svn-1.7 has buggy 'status --xml' output")
r = path1
try:
r.update(rev=1)
s = r.status(updates=1, rec=1)
# Comparing just the file names, because paths are unpredictable
# on Windows. (long vs. 8.3 paths)
import pprint
pprint.pprint(s.allpath())
assert r.join('anotherfile').basename in [item.basename for
item in s.update_available]
#assert len(s.update_available) == 1
finally:
r.update()
def test_status_replaced(self, path1):
p = path1.join("samplefile")
p.remove()
p.ensure(dir=0)
try:
s = path1.status()
assert p.basename in [item.basename for item in s.replaced]
finally:
path1.revert(rec=1)
def test_status_ignored(self, path1):
try:
d = path1.join('sampledir')
p = py.path.local(d).join('ignoredfile')
p.ensure(file=True)
s = d.status()
assert [x.basename for x in s.unknown] == ['ignoredfile']
assert [x.basename for x in s.ignored] == []
d.propset('svn:ignore', 'ignoredfile')
s = d.status()
assert [x.basename for x in s.unknown] == []
assert [x.basename for x in s.ignored] == ['ignoredfile']
finally:
path1.revert(rec=1)
def test_status_conflict(self, path1, tmpdir):
wc = path1
wccopy = py.path.svnwc(tmpdir.join("conflict_copy"))
wccopy.checkout(wc.url)
p = wc.ensure('conflictsamplefile', file=1)
p.write('foo')
wc.commit('added conflictsamplefile')
wccopy.update()
assert wccopy.join('conflictsamplefile').check()
p.write('bar')
wc.commit('wrote some data')
wccopy.join('conflictsamplefile').write('baz')
wccopy.update(interactive=False)
s = wccopy.status()
assert [x.basename for x in s.conflict] == ['conflictsamplefile']
def test_status_external(self, path1, repowc2):
otherrepo, otherrepourl, otherwc = repowc2
d = path1.ensure('sampledir', dir=1)
try:
d.update()
d.propset('svn:externals', 'otherwc %s' % (otherwc.url,))
d.update()
s = d.status()
assert [x.basename for x in s.external] == ['otherwc']
assert 'otherwc' not in [x.basename for x in s.unchanged]
s = d.status(rec=1)
assert [x.basename for x in s.external] == ['otherwc']
assert 'otherwc' in [x.basename for x in s.unchanged]
finally:
path1.revert(rec=1)
def test_status_deleted(self, path1):
d = path1.ensure('sampledir', dir=1)
d.remove()
d.ensure(dir=1)
path1.commit()
d.ensure('deletefile', dir=0)
d.commit()
s = d.status()
assert 'deletefile' in [x.basename for x in s.unchanged]
assert not s.deleted
p = d.join('deletefile')
p.remove()
s = d.status()
assert 'deletefile' not in s.unchanged
assert [x.basename for x in s.deleted] == ['deletefile']
def test_status_noauthor(self, path1):
# testing for XML without author - this used to raise an exception
xml = '''\
2008-08-19T16:50:53.400198Z
'''
XMLWCStatus.fromstring(xml, path1)
def test_status_wrong_xml(self, path1):
# testing for XML without author - this used to raise an exception
xml = '\n\n\n'
st = XMLWCStatus.fromstring(xml, path1)
assert len(st.incomplete) == 1
def test_diff(self, path1):
p = path1 / 'anotherfile'
out = p.diff(rev=2)
assert out.find('hello') != -1
def test_blame(self, path1):
p = path1.join('samplepickle')
lines = p.blame()
assert sum([l[0] for l in lines]) == len(lines)
for l1, l2 in zip(p.readlines(), [l[2] for l in lines]):
assert l1 == l2
assert [l[1] for l in lines] == ['hpk'] * len(lines)
p = path1.join('samplefile')
lines = p.blame()
assert sum([l[0] for l in lines]) == len(lines)
for l1, l2 in zip(p.readlines(), [l[2] for l in lines]):
assert l1 == l2
assert [l[1] for l in lines] == ['hpk'] * len(lines)
def test_join_abs(self, path1):
s = str(path1.localpath)
n = path1.join(s, abs=1)
assert path1 == n
def test_join_abs2(self, path1):
assert path1.join('samplefile', abs=1) == path1.join('samplefile')
def test_str_gives_localpath(self, path1):
assert str(path1) == str(path1.localpath)
def test_versioned(self, path1):
assert path1.check(versioned=1)
# TODO: Why does my copy of svn think .svn is versioned?
#assert path1.join('.svn').check(versioned=0)
assert path1.join('samplefile').check(versioned=1)
assert not path1.join('notexisting').check(versioned=1)
notexisting = path1.join('hello').localpath
try:
notexisting.write("")
assert path1.join('hello').check(versioned=0)
finally:
notexisting.remove()
def test_listdir_versioned(self, path1):
assert path1.check(versioned=1)
p = path1.localpath.ensure("not_a_versioned_file")
l = [x.localpath
for x in path1.listdir(lambda x: x.check(versioned=True))]
assert p not in l
def test_nonversioned_remove(self, path1):
assert path1.check(versioned=1)
somefile = path1.join('nonversioned/somefile')
nonwc = py.path.local(somefile)
nonwc.ensure()
assert somefile.check()
assert not somefile.check(versioned=True)
somefile.remove() # this used to fail because it tried to 'svn rm'
def test_properties(self, path1):
try:
path1.propset('gaga', 'this')
assert path1.propget('gaga') == 'this'
# Comparing just the file names, because paths are unpredictable
# on Windows. (long vs. 8.3 paths)
assert path1.basename in [item.basename for item in
path1.status().prop_modified]
assert 'gaga' in path1.proplist()
assert path1.proplist()['gaga'] == 'this'
finally:
path1.propdel('gaga')
def test_proplist_recursive(self, path1):
s = path1.join('samplefile')
s.propset('gugu', 'that')
try:
p = path1.proplist(rec=1)
# Comparing just the file names, because paths are unpredictable
# on Windows. (long vs. 8.3 paths)
assert (path1 / 'samplefile').basename in [item.basename
for item in p]
finally:
s.propdel('gugu')
def test_long_properties(self, path1):
value = """
vadm:posix : root root 0100755
Properties on 'chroot/dns/var/bind/db.net.xots':
"""
try:
path1.propset('gaga', value)
backvalue = path1.propget('gaga')
assert backvalue == value
#assert len(backvalue.split('\n')) == 1
finally:
path1.propdel('gaga')
def test_ensure(self, path1):
newpath = path1.ensure('a', 'b', 'c')
try:
assert newpath.check(exists=1, versioned=1)
newpath.write("hello")
newpath.ensure()
assert newpath.read() == "hello"
finally:
path1.join('a').remove(force=1)
def test_not_versioned(self, path1):
p = path1.localpath.mkdir('whatever')
f = path1.localpath.ensure('testcreatedfile')
try:
assert path1.join('whatever').check(versioned=0)
assert path1.join('testcreatedfile').check(versioned=0)
assert not path1.join('testcreatedfile').check(versioned=1)
finally:
p.remove(rec=1)
f.remove()
def test_lock_unlock(self, path1):
root = path1
somefile = root.join('somefile')
somefile.ensure(file=True)
# not yet added to repo
py.test.raises(Exception, 'somefile.lock()')
somefile.write('foo')
somefile.commit('test')
assert somefile.check(versioned=True)
somefile.lock()
try:
locked = root.status().locked
assert len(locked) == 1
assert locked[0].basename == somefile.basename
assert locked[0].dirpath().basename == somefile.dirpath().basename
#assert somefile.locked()
py.test.raises(Exception, 'somefile.lock()')
finally:
somefile.unlock()
#assert not somefile.locked()
locked = root.status().locked
assert locked == []
py.test.raises(Exception, 'somefile,unlock()')
somefile.remove()
def test_commit_nonrecursive(self, path1):
somedir = path1.join('sampledir')
somedir.mkdir("subsubdir")
somedir.propset('foo', 'bar')
status = somedir.status()
assert len(status.prop_modified) == 1
assert len(status.added) == 1
somedir.commit('non-recursive commit', rec=0)
status = somedir.status()
assert len(status.prop_modified) == 0
assert len(status.added) == 1
somedir.commit('recursive commit')
status = somedir.status()
assert len(status.prop_modified) == 0
assert len(status.added) == 0
def test_commit_return_value(self, path1):
testfile = path1.join('test.txt').ensure(file=True)
testfile.write('test')
rev = path1.commit('testing')
assert type(rev) == int
anotherfile = path1.join('another.txt').ensure(file=True)
anotherfile.write('test')
rev2 = path1.commit('testing more')
assert type(rev2) == int
assert rev2 == rev + 1
#def test_log(self, path1):
# l = path1.log()
# assert len(l) == 3 # might need to be upped if more tests are added
class XTestWCSvnCommandPathSpecial:
rooturl = 'http://codespeak.net/svn/py.path/trunk/dist/py.path/test/data'
#def test_update_none_rev(self, path1):
# path = tmpdir.join('checkouttest')
# wcpath = newpath(xsvnwc=str(path), url=path1url)
# try:
# wcpath.checkout(rev=2100)
# wcpath.update()
# assert wcpath.info().rev > 2100
# finally:
# wcpath.localpath.remove(rec=1)
def test_parse_wcinfotime():
assert (parse_wcinfotime('2006-05-30 20:45:26 +0200 (Tue, 30 May 2006)') ==
1149021926)
assert (parse_wcinfotime('2003-10-27 20:43:14 +0100 (Mon, 27 Oct 2003)') ==
1067287394)
class TestInfoSvnWCCommand:
def test_svn_1_2(self, path1):
output = """
Path: test_svnwc.py
Name: test_svnwc.py
URL: http://codespeak.net/svn/py/dist/py/path/svn/wccommand.py
Repository UUID: fd0d7bf2-dfb6-0310-8d31-b7ecfe96aada
Revision: 28137
Node Kind: file
Schedule: normal
Last Changed Author: jan
Last Changed Rev: 27939
Last Changed Date: 2006-05-30 20:45:26 +0200 (Tue, 30 May 2006)
Text Last Updated: 2006-06-01 00:42:53 +0200 (Thu, 01 Jun 2006)
Properties Last Updated: 2006-05-23 11:54:59 +0200 (Tue, 23 May 2006)
Checksum: 357e44880e5d80157cc5fbc3ce9822e3
"""
path = py.path.local(__file__).dirpath().chdir()
try:
info = InfoSvnWCCommand(output)
finally:
path.chdir()
assert info.last_author == 'jan'
assert info.kind == 'file'
assert info.mtime == 1149021926.0
assert info.url == 'http://codespeak.net/svn/py/dist/py/path/svn/wccommand.py'
assert info.time == 1149021926000000.0
assert info.rev == 28137
def test_svn_1_3(self, path1):
output = """
Path: test_svnwc.py
Name: test_svnwc.py
URL: http://codespeak.net/svn/py/dist/py/path/svn/wccommand.py
Repository Root: http://codespeak.net/svn
Repository UUID: fd0d7bf2-dfb6-0310-8d31-b7ecfe96aada
Revision: 28124
Node Kind: file
Schedule: normal
Last Changed Author: jan
Last Changed Rev: 27939
Last Changed Date: 2006-05-30 20:45:26 +0200 (Tue, 30 May 2006)
Text Last Updated: 2006-06-02 23:46:11 +0200 (Fri, 02 Jun 2006)
Properties Last Updated: 2006-06-02 23:45:28 +0200 (Fri, 02 Jun 2006)
Checksum: 357e44880e5d80157cc5fbc3ce9822e3
"""
path = py.path.local(__file__).dirpath().chdir()
try:
info = InfoSvnWCCommand(output)
finally:
path.chdir()
assert info.last_author == 'jan'
assert info.kind == 'file'
assert info.mtime == 1149021926.0
assert info.url == 'http://codespeak.net/svn/py/dist/py/path/svn/wccommand.py'
assert info.rev == 28124
assert info.time == 1149021926000000.0
def test_characters_at():
py.test.raises(ValueError, "py.path.svnwc('/tmp/@@@:')")
def test_characters_tilde():
py.path.svnwc('/tmp/test~')
class TestRepo:
def test_trailing_slash_is_stripped(self, path1):
# XXX we need to test more normalizing properties
url = path1.join("/")
assert path1 == url
#def test_different_revs_compare_unequal(self, path1):
# newpath = path1.new(rev=1199)
# assert newpath != path1
def test_exists_svn_root(self, path1):
assert path1.check()
#def test_not_exists_rev(self, path1):
# url = path1.__class__(path1url, rev=500)
# assert url.check(exists=0)
#def test_nonexisting_listdir_rev(self, path1):
# url = path1.__class__(path1url, rev=500)
# raises(py.error.ENOENT, url.listdir)
#def test_newrev(self, path1):
# url = path1.new(rev=None)
# assert url.rev == None
# assert url.strpath == path1.strpath
# url = path1.new(rev=10)
# assert url.rev == 10
#def test_info_rev(self, path1):
# url = path1.__class__(path1url, rev=1155)
# url = url.join("samplefile")
# res = url.info()
# assert res.size > len("samplefile") and res.created_rev == 1155
# the following tests are easier if we have a path class
def test_repocache_simple(self, path1):
repocache = svncommon.RepoCache()
repocache.put(path1.strpath, 42)
url, rev = repocache.get(path1.join('test').strpath)
assert rev == 42
assert url == path1.strpath
def test_repocache_notimeout(self, path1):
repocache = svncommon.RepoCache()
repocache.timeout = 0
repocache.put(path1.strpath, path1.rev)
url, rev = repocache.get(path1.strpath)
assert rev == -1
assert url == path1.strpath
def test_repocache_outdated(self, path1):
repocache = svncommon.RepoCache()
repocache.put(path1.strpath, 42, timestamp=0)
url, rev = repocache.get(path1.join('test').strpath)
assert rev == -1
assert url == path1.strpath
def _test_getreporev(self):
""" this test runs so slow it's usually disabled """
old = svncommon.repositories.repos
try:
_repocache.clear()
root = path1.new(rev=-1)
url, rev = cache.repocache.get(root.strpath)
assert rev>=0
assert url == svnrepourl
finally:
repositories.repos = old
py-1.5.2/testing/path/repotest.dump 0000664 0001750 0001750 00000006327 13202663617 020462 0 ustar vagrant vagrant 0000000 0000000 SVN-fs-dump-format-version: 2
UUID: 876a30f4-1eed-0310-aeb7-ae314d1e5934
Revision-number: 0
Prop-content-length: 56
Content-length: 56
K 8
svn:date
V 27
2005-01-07T23:55:31.755989Z
PROPS-END
Revision-number: 1
Prop-content-length: 118
Content-length: 118
K 7
svn:log
V 20
testrepo setup rev 1
K 10
svn:author
V 3
hpk
K 8
svn:date
V 27
2005-01-07T23:55:37.815386Z
PROPS-END
Node-path: execfile
Node-kind: file
Node-action: add
Prop-content-length: 10
Text-content-length: 4
Text-content-md5: d4b5bc61e16310f08c5d11866eba0a22
Content-length: 14
PROPS-END
x=42
Node-path: otherdir
Node-kind: dir
Node-action: add
Prop-content-length: 10
Content-length: 10
PROPS-END
Node-path: otherdir/__init__.py
Node-kind: file
Node-action: add
Prop-content-length: 10
Text-content-length: 0
Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
Content-length: 10
PROPS-END
Node-path: otherdir/a.py
Node-kind: file
Node-action: add
Prop-content-length: 10
Text-content-length: 30
Text-content-md5: 247c7daeb2ee5dcab0aba7bd12bad665
Content-length: 40
PROPS-END
from b import stuff as result
Node-path: otherdir/b.py
Node-kind: file
Node-action: add
Prop-content-length: 10
Text-content-length: 15
Text-content-md5: c1b13503469a7711306d03a4b0721bc6
Content-length: 25
PROPS-END
stuff="got it"
Node-path: otherdir/c.py
Node-kind: file
Node-action: add
Prop-content-length: 10
Text-content-length: 75
Text-content-md5: 250cdb6b5df68536152c681f48297569
Content-length: 85
PROPS-END
import py; py.magic.autopath()
import otherdir.a
value = otherdir.a.result
Node-path: otherdir/d.py
Node-kind: file
Node-action: add
Prop-content-length: 10
Text-content-length: 72
Text-content-md5: 940c9c621e7b198e081459642c37f5a7
Content-length: 82
PROPS-END
import py; py.magic.autopath()
from otherdir import a
value2 = a.result
Node-path: sampledir
Node-kind: dir
Node-action: add
Prop-content-length: 10
Content-length: 10
PROPS-END
Node-path: sampledir/otherfile
Node-kind: file
Node-action: add
Prop-content-length: 10
Text-content-length: 0
Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
Content-length: 10
PROPS-END
Node-path: samplefile
Node-kind: file
Node-action: add
Prop-content-length: 40
Text-content-length: 11
Text-content-md5: 9225ac28b32156979ab6482b8bb5fb8c
Content-length: 51
K 13
svn:eol-style
V 6
native
PROPS-END
samplefile
Node-path: samplepickle
Node-kind: file
Node-action: add
Prop-content-length: 10
Text-content-length: 56
Text-content-md5: 719d85c1329a33134bb98f56b756c545
Content-length: 66
PROPS-END
(dp1
S'answer'
p2
I42
sI1
I2
sS'hello'
p3
S'world'
p4
s.
Revision-number: 2
Prop-content-length: 108
Content-length: 108
K 7
svn:log
V 10
second rev
K 10
svn:author
V 3
hpk
K 8
svn:date
V 27
2005-01-07T23:55:39.223202Z
PROPS-END
Node-path: anotherfile
Node-kind: file
Node-action: add
Prop-content-length: 10
Text-content-length: 5
Text-content-md5: 5d41402abc4b2a76b9719d911017c592
Content-length: 15
PROPS-END
hello
Revision-number: 3
Prop-content-length: 106
Content-length: 106
K 7
svn:log
V 9
third rev
K 10
svn:author
V 3
hpk
K 8
svn:date
V 27
2005-01-07T23:55:41.556642Z
PROPS-END
Node-path: anotherfile
Node-kind: file
Node-action: change
Text-content-length: 5
Text-content-md5: 7d793037a0760186574b0282f2f435e7
Content-length: 5
world
py-1.5.2/testing/path/svntestbase.py 0000664 0001750 0001750 00000001634 13202663617 020635 0 ustar vagrant vagrant 0000000 0000000 import sys
import py
from py._path import svnwc as svncommon
from common import CommonFSTests
class CommonSvnTests(CommonFSTests):
def test_propget(self, path1):
url = path1.join("samplefile")
value = url.propget('svn:eol-style')
assert value == 'native'
def test_proplist(self, path1):
url = path1.join("samplefile")
res = url.proplist()
assert res['svn:eol-style'] == 'native'
def test_info(self, path1):
url = path1.join("samplefile")
res = url.info()
assert res.size > len("samplefile") and res.created_rev >= 0
def test_log_simple(self, path1):
url = path1.join("samplefile")
logentries = url.log()
for logentry in logentries:
assert logentry.rev == 1
assert hasattr(logentry, 'author')
assert hasattr(logentry, 'date')
#cache.repositories.put(svnrepourl, 1200, 0)
py-1.5.2/testing/path/test_svnurl.py 0000664 0001750 0001750 00000006704 13202663617 020667 0 ustar vagrant vagrant 0000000 0000000 import py
from py._path.svnurl import InfoSvnCommand
import datetime
import time
from svntestbase import CommonSvnTests
def pytest_funcarg__path1(request):
repo, repourl, wc = request.getfuncargvalue("repowc1")
return py.path.svnurl(repourl)
class TestSvnURLCommandPath(CommonSvnTests):
@py.test.mark.xfail
def test_load(self, path1):
super(TestSvnURLCommandPath, self).test_load(path1)
# the following two work on jython but not in local/svnwc
def test_listdir(self, path1):
super(TestSvnURLCommandPath, self).test_listdir(path1)
def test_visit_ignore(self, path1):
super(TestSvnURLCommandPath, self).test_visit_ignore(path1)
def test_svnurl_needs_arg(self, path1):
py.test.raises(TypeError, "py.path.svnurl()")
def test_svnurl_does_not_accept_None_either(self, path1):
py.test.raises(Exception, "py.path.svnurl(None)")
def test_svnurl_characters_simple(self, path1):
py.path.svnurl("svn+ssh://hello/world")
def test_svnurl_characters_at_user(self, path1):
py.path.svnurl("http://user@host.com/some/dir")
def test_svnurl_characters_at_path(self, path1):
py.test.raises(ValueError, 'py.path.svnurl("http://host.com/foo@bar")')
def test_svnurl_characters_colon_port(self, path1):
py.path.svnurl("http://host.com:8080/some/dir")
def test_svnurl_characters_tilde_end(self, path1):
py.path.svnurl("http://host.com/some/file~")
@py.test.mark.xfail("sys.platform == 'win32'")
def test_svnurl_characters_colon_path(self, path1):
# colons are allowed on win32, because they're part of the drive
# part of an absolute path... however, they shouldn't be allowed in
# other parts, I think
py.test.raises(ValueError, 'py.path.svnurl("http://host.com/foo:bar")')
def test_export(self, path1, tmpdir):
tmpdir = tmpdir.join("empty")
p = path1.export(tmpdir)
assert p == tmpdir # XXX should return None
n1 = [x.basename for x in tmpdir.listdir()]
n2 = [x.basename for x in path1.listdir()]
n1.sort()
n2.sort()
assert n1 == n2
assert not p.join('.svn').check()
rev = path1.mkdir("newdir")
tmpdir.remove()
assert not tmpdir.check()
path1.new(rev=1).export(tmpdir)
for p in tmpdir.listdir():
assert p.basename in n2
class TestSvnInfoCommand:
def test_svn_1_2(self):
line = " 2256 hpk 165 Nov 24 17:55 __init__.py"
info = InfoSvnCommand(line)
now = datetime.datetime.now()
assert info.last_author == 'hpk'
assert info.created_rev == 2256
assert info.kind == 'file'
# we don't check for the year (2006), because that depends
# on the clock correctly being setup
assert time.gmtime(info.mtime)[1:6] == (11, 24, 17, 55, 0)
assert info.size == 165
assert info.time == info.mtime * 1000000
def test_svn_1_3(self):
line =" 4784 hpk 2 Jun 01 2004 __init__.py"
info = InfoSvnCommand(line)
assert info.last_author == 'hpk'
assert info.kind == 'file'
def test_svn_1_3_b(self):
line =" 74 autoadmi Oct 06 23:59 plonesolutions.com/"
info = InfoSvnCommand(line)
assert info.last_author == 'autoadmi'
assert info.kind == 'dir'
def test_badchars():
py.test.raises(ValueError, "py.path.svnurl('http://host/tmp/@@@:')")
py-1.5.2/testing/path/common.py 0000664 0001750 0001750 00000040032 13202663755 017562 0 ustar vagrant vagrant 0000000 0000000 import py
import sys
import pytest
class CommonFSTests(object):
def test_constructor_equality(self, path1):
p = path1.__class__(path1)
assert p == path1
def test_eq_nonstring(self, path1):
p1 = path1.join('sampledir')
p2 = path1.join('sampledir')
assert p1 == p2
def test_new_identical(self, path1):
assert path1 == path1.new()
def test_join(self, path1):
p = path1.join('sampledir')
strp = str(p)
assert strp.endswith('sampledir')
assert strp.startswith(str(path1))
def test_join_normalized(self, path1):
newpath = path1.join(path1.sep+'sampledir')
strp = str(newpath)
assert strp.endswith('sampledir')
assert strp.startswith(str(path1))
newpath = path1.join((path1.sep*2) + 'sampledir')
strp = str(newpath)
assert strp.endswith('sampledir')
assert strp.startswith(str(path1))
def test_join_noargs(self, path1):
newpath = path1.join()
assert path1 == newpath
def test_add_something(self, path1):
p = path1.join('sample')
p = p + 'dir'
assert p.check()
assert p.exists()
assert p.isdir()
assert not p.isfile()
def test_parts(self, path1):
newpath = path1.join('sampledir', 'otherfile')
par = newpath.parts()[-3:]
assert par == [path1, path1.join('sampledir'), newpath]
revpar = newpath.parts(reverse=True)[:3]
assert revpar == [newpath, path1.join('sampledir'), path1]
def test_common(self, path1):
other = path1.join('sampledir')
x = other.common(path1)
assert x == path1
#def test_parents_nonexisting_file(self, path1):
# newpath = path1 / 'dirnoexist' / 'nonexisting file'
# par = list(newpath.parents())
# assert par[:2] == [path1 / 'dirnoexist', path1]
def test_basename_checks(self, path1):
newpath = path1.join('sampledir')
assert newpath.check(basename='sampledir')
assert newpath.check(notbasename='xyz')
assert newpath.basename == 'sampledir'
def test_basename(self, path1):
newpath = path1.join('sampledir')
assert newpath.check(basename='sampledir')
assert newpath.basename, 'sampledir'
def test_dirname(self, path1):
newpath = path1.join('sampledir')
assert newpath.dirname == str(path1)
def test_dirpath(self, path1):
newpath = path1.join('sampledir')
assert newpath.dirpath() == path1
def test_dirpath_with_args(self, path1):
newpath = path1.join('sampledir')
assert newpath.dirpath('x') == path1.join('x')
def test_newbasename(self, path1):
newpath = path1.join('samplefile')
newbase = newpath.new(basename="samplefile2")
assert newbase.basename == "samplefile2"
assert newbase.dirpath() == newpath.dirpath()
def test_not_exists(self, path1):
assert not path1.join('does_not_exist').check()
assert path1.join('does_not_exist').check(exists=0)
def test_exists(self, path1):
assert path1.join("samplefile").check()
assert path1.join("samplefile").check(exists=1)
assert path1.join("samplefile").exists()
assert path1.join("samplefile").isfile()
assert not path1.join("samplefile").isdir()
def test_dir(self, path1):
#print repr(path1.join("sampledir"))
assert path1.join("sampledir").check(dir=1)
assert path1.join('samplefile').check(notdir=1)
assert not path1.join("samplefile").check(dir=1)
assert path1.join("samplefile").exists()
assert not path1.join("samplefile").isdir()
assert path1.join("samplefile").isfile()
def test_fnmatch_file(self, path1):
assert path1.join("samplefile").check(fnmatch='s*e')
assert path1.join("samplefile").fnmatch('s*e')
assert not path1.join("samplefile").fnmatch('s*x')
assert not path1.join("samplefile").check(fnmatch='s*x')
#def test_fnmatch_dir(self, path1):
# pattern = path1.sep.join(['s*file'])
# sfile = path1.join("samplefile")
# assert sfile.check(fnmatch=pattern)
def test_relto(self, path1):
l=path1.join("sampledir", "otherfile")
assert l.relto(path1) == l.sep.join(["sampledir", "otherfile"])
assert l.check(relto=path1)
assert path1.check(notrelto=l)
assert not path1.check(relto=l)
def test_bestrelpath(self, path1):
curdir = path1
sep = curdir.sep
s = curdir.bestrelpath(curdir)
assert s == "."
s = curdir.bestrelpath(curdir.join("hello", "world"))
assert s == "hello" + sep + "world"
s = curdir.bestrelpath(curdir.dirpath().join("sister"))
assert s == ".." + sep + "sister"
assert curdir.bestrelpath(curdir.dirpath()) == ".."
assert curdir.bestrelpath("hello") == "hello"
def test_relto_not_relative(self, path1):
l1=path1.join("bcde")
l2=path1.join("b")
assert not l1.relto(l2)
assert not l2.relto(l1)
@py.test.mark.xfail("sys.platform.startswith('java')")
def test_listdir(self, path1):
l = path1.listdir()
assert path1.join('sampledir') in l
assert path1.join('samplefile') in l
py.test.raises(py.error.ENOTDIR,
"path1.join('samplefile').listdir()")
def test_listdir_fnmatchstring(self, path1):
l = path1.listdir('s*dir')
assert len(l)
assert l[0], path1.join('sampledir')
def test_listdir_filter(self, path1):
l = path1.listdir(lambda x: x.check(dir=1))
assert path1.join('sampledir') in l
assert not path1.join('samplefile') in l
def test_listdir_sorted(self, path1):
l = path1.listdir(lambda x: x.check(basestarts="sample"), sort=True)
assert path1.join('sampledir') == l[0]
assert path1.join('samplefile') == l[1]
assert path1.join('samplepickle') == l[2]
def test_visit_nofilter(self, path1):
l = []
for i in path1.visit():
l.append(i.relto(path1))
assert "sampledir" in l
assert path1.sep.join(["sampledir", "otherfile"]) in l
def test_visit_norecurse(self, path1):
l = []
for i in path1.visit(None, lambda x: x.basename != "sampledir"):
l.append(i.relto(path1))
assert "sampledir" in l
assert not path1.sep.join(["sampledir", "otherfile"]) in l
@pytest.mark.parametrize('fil', ['*dir', u'*dir',
pytest.mark.skip("sys.version_info <"
" (3,6)")(b'*dir')])
def test_visit_filterfunc_is_string(self, path1, fil):
l = []
for i in path1.visit(fil):
l.append(i.relto(path1))
assert len(l), 2
assert "sampledir" in l
assert "otherdir" in l
@py.test.mark.xfail("sys.platform.startswith('java')")
def test_visit_ignore(self, path1):
p = path1.join('nonexisting')
assert list(p.visit(ignore=py.error.ENOENT)) == []
def test_visit_endswith(self, path1):
l = []
for i in path1.visit(lambda x: x.check(endswith="file")):
l.append(i.relto(path1))
assert path1.sep.join(["sampledir", "otherfile"]) in l
assert "samplefile" in l
def test_endswith(self, path1):
assert path1.check(notendswith='.py')
x = path1.join('samplefile')
assert x.check(endswith='file')
def test_cmp(self, path1):
path1 = path1.join('samplefile')
path2 = path1.join('samplefile2')
assert (path1 < path2) == ('samplefile' < 'samplefile2')
assert not (path1 < path1)
def test_simple_read(self, path1):
x = path1.join('samplefile').read('r')
assert x == 'samplefile\n'
def test_join_div_operator(self, path1):
newpath = path1 / '/sampledir' / '/test//'
newpath2 = path1.join('sampledir', 'test')
assert newpath == newpath2
def test_ext(self, path1):
newpath = path1.join('sampledir.ext')
assert newpath.ext == '.ext'
newpath = path1.join('sampledir')
assert not newpath.ext
def test_purebasename(self, path1):
newpath = path1.join('samplefile.py')
assert newpath.purebasename == 'samplefile'
def test_multiple_parts(self, path1):
newpath = path1.join('samplefile.py')
dirname, purebasename, basename, ext = newpath._getbyspec(
'dirname,purebasename,basename,ext')
assert str(path1).endswith(dirname) # be careful with win32 'drive'
assert purebasename == 'samplefile'
assert basename == 'samplefile.py'
assert ext == '.py'
def test_dotted_name_ext(self, path1):
newpath = path1.join('a.b.c')
ext = newpath.ext
assert ext == '.c'
assert newpath.ext == '.c'
def test_newext(self, path1):
newpath = path1.join('samplefile.py')
newext = newpath.new(ext='.txt')
assert newext.basename == "samplefile.txt"
assert newext.purebasename == "samplefile"
def test_readlines(self, path1):
fn = path1.join('samplefile')
contents = fn.readlines()
assert contents == ['samplefile\n']
def test_readlines_nocr(self, path1):
fn = path1.join('samplefile')
contents = fn.readlines(cr=0)
assert contents == ['samplefile', '']
def test_file(self, path1):
assert path1.join('samplefile').check(file=1)
def test_not_file(self, path1):
assert not path1.join("sampledir").check(file=1)
assert path1.join("sampledir").check(file=0)
def test_non_existent(self, path1):
assert path1.join("sampledir.nothere").check(dir=0)
assert path1.join("sampledir.nothere").check(file=0)
assert path1.join("sampledir.nothere").check(notfile=1)
assert path1.join("sampledir.nothere").check(notdir=1)
assert path1.join("sampledir.nothere").check(notexists=1)
assert not path1.join("sampledir.nothere").check(notfile=0)
# pattern = path1.sep.join(['s*file'])
# sfile = path1.join("samplefile")
# assert sfile.check(fnmatch=pattern)
def test_size(self, path1):
url = path1.join("samplefile")
assert url.size() > len("samplefile")
def test_mtime(self, path1):
url = path1.join("samplefile")
assert url.mtime() > 0
def test_relto_wrong_type(self, path1):
py.test.raises(TypeError, "path1.relto(42)")
def test_load(self, path1):
p = path1.join('samplepickle')
obj = p.load()
assert type(obj) is dict
assert obj.get('answer',None) == 42
def test_visit_filesonly(self, path1):
l = []
for i in path1.visit(lambda x: x.check(file=1)):
l.append(i.relto(path1))
assert not "sampledir" in l
assert path1.sep.join(["sampledir", "otherfile"]) in l
def test_visit_nodotfiles(self, path1):
l = []
for i in path1.visit(lambda x: x.check(dotfile=0)):
l.append(i.relto(path1))
assert "sampledir" in l
assert path1.sep.join(["sampledir", "otherfile"]) in l
assert not ".dotfile" in l
def test_visit_breadthfirst(self, path1):
l = []
for i in path1.visit(bf=True):
l.append(i.relto(path1))
for i, p in enumerate(l):
if path1.sep in p:
for j in range(i, len(l)):
assert path1.sep in l[j]
break
else:
py.test.fail("huh")
def test_visit_sort(self, path1):
l = []
for i in path1.visit(bf=True, sort=True):
l.append(i.relto(path1))
for i, p in enumerate(l):
if path1.sep in p:
break
assert l[:i] == sorted(l[:i])
assert l[i:] == sorted(l[i:])
def test_endswith(self, path1):
def chk(p):
return p.check(endswith="pickle")
assert not chk(path1)
assert not chk(path1.join('samplefile'))
assert chk(path1.join('somepickle'))
def test_copy_file(self, path1):
otherdir = path1.join('otherdir')
initpy = otherdir.join('__init__.py')
copied = otherdir.join('copied')
initpy.copy(copied)
try:
assert copied.check()
s1 = initpy.read()
s2 = copied.read()
assert s1 == s2
finally:
if copied.check():
copied.remove()
def test_copy_dir(self, path1):
otherdir = path1.join('otherdir')
copied = path1.join('newdir')
try:
otherdir.copy(copied)
assert copied.check(dir=1)
assert copied.join('__init__.py').check(file=1)
s1 = otherdir.join('__init__.py').read()
s2 = copied.join('__init__.py').read()
assert s1 == s2
finally:
if copied.check(dir=1):
copied.remove(rec=1)
def test_remove_file(self, path1):
d = path1.ensure('todeleted')
assert d.check()
d.remove()
assert not d.check()
def test_remove_dir_recursive_by_default(self, path1):
d = path1.ensure('to', 'be', 'deleted')
assert d.check()
p = path1.join('to')
p.remove()
assert not p.check()
def test_ensure_dir(self, path1):
b = path1.ensure_dir("001", "002")
assert b.basename == "002"
assert b.isdir()
def test_mkdir_and_remove(self, path1):
tmpdir = path1
py.test.raises(py.error.EEXIST, tmpdir.mkdir, 'sampledir')
new = tmpdir.join('mktest1')
new.mkdir()
assert new.check(dir=1)
new.remove()
new = tmpdir.mkdir('mktest')
assert new.check(dir=1)
new.remove()
assert tmpdir.join('mktest') == new
def test_move_file(self, path1):
p = path1.join('samplefile')
newp = p.dirpath('moved_samplefile')
p.move(newp)
try:
assert newp.check(file=1)
assert not p.check()
finally:
dp = newp.dirpath()
if hasattr(dp, 'revert'):
dp.revert()
else:
newp.move(p)
assert p.check()
def test_move_dir(self, path1):
source = path1.join('sampledir')
dest = path1.join('moveddir')
source.move(dest)
assert dest.check(dir=1)
assert dest.join('otherfile').check(file=1)
assert not source.join('sampledir').check()
def test_fspath_protocol_match_strpath(self, path1):
assert path1.__fspath__() == path1.strpath
def test_fspath_func_match_strpath(self, path1):
try:
from os import fspath
except ImportError:
from py._path.common import fspath
assert fspath(path1) == path1.strpath
@py.test.mark.skip("sys.version_info < (3,6)")
def test_fspath_open(self, path1):
f = path1.join('opentestfile')
open(f)
@py.test.mark.skip("sys.version_info < (3,6)")
def test_fspath_fsencode(self, path1):
from os import fsencode
assert fsencode(path1) == fsencode(path1.strpath)
def setuptestfs(path):
if path.join('samplefile').check():
return
#print "setting up test fs for", repr(path)
samplefile = path.ensure('samplefile')
samplefile.write('samplefile\n')
execfile = path.ensure('execfile')
execfile.write('x=42')
execfilepy = path.ensure('execfile.py')
execfilepy.write('x=42')
d = {1:2, 'hello': 'world', 'answer': 42}
path.ensure('samplepickle').dump(d)
sampledir = path.ensure('sampledir', dir=1)
sampledir.ensure('otherfile')
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;
import otherdir.a
value = otherdir.a.result
''')
module_d = otherdir.ensure('d.py')
module_d.write('''import py;
from otherdir import a
value2 = a.result
''')
py-1.5.2/testing/path/test_local.py 0000664 0001750 0001750 00000101441 13202663755 020425 0 ustar vagrant vagrant 0000000 0000000 # -*- coding: utf-8 -*-
from __future__ import with_statement
import time
import py
import pytest
import os
import sys
import multiprocessing
from py.path import local
import common
failsonjython = py.test.mark.xfail("sys.platform.startswith('java')")
failsonjywin32 = py.test.mark.xfail(
"sys.platform.startswith('java') "
"and getattr(os, '_name', None) == 'nt'")
win32only = py.test.mark.skipif(
"not (sys.platform == 'win32' or getattr(os, '_name', None) == 'nt')")
skiponwin32 = py.test.mark.skipif(
"sys.platform == 'win32' or getattr(os, '_name', None) == 'nt'")
ATIME_RESOLUTION = 0.01
@pytest.yield_fixture(scope="session")
def path1(tmpdir_factory):
path = tmpdir_factory.mktemp('path')
common.setuptestfs(path)
yield path
assert path.join("samplefile").check()
@pytest.fixture
def fake_fspath_obj(request):
class FakeFSPathClass(object):
def __init__(self, path):
self._path = path
def __fspath__(self):
return self._path
return FakeFSPathClass(os.path.join("this", "is", "a", "fake", "path"))
def batch_make_numbered_dirs(rootdir, repeats):
try:
for i in range(repeats):
dir_ = py.path.local.make_numbered_dir(prefix='repro-', rootdir=rootdir)
file_ = dir_.join('foo')
file_.write('%s' % i)
actual = int(file_.read())
assert actual == i, 'int(file_.read()) is %s instead of %s' % (actual, i)
dir_.join('.lock').remove(ignore_errors=True)
return True
except KeyboardInterrupt:
# makes sure that interrupting test session won't hang it
os.exit(2)
class TestLocalPath(common.CommonFSTests):
def test_join_normpath(self, tmpdir):
assert tmpdir.join(".") == tmpdir
p = tmpdir.join("../%s" % tmpdir.basename)
assert p == tmpdir
p = tmpdir.join("..//%s/" % tmpdir.basename)
assert p == tmpdir
@skiponwin32
def test_dirpath_abs_no_abs(self, tmpdir):
p = tmpdir.join('foo')
assert p.dirpath('/bar') == tmpdir.join('bar')
assert tmpdir.dirpath('/bar', abs=True) == local('/bar')
def test_gethash(self, tmpdir):
md5 = py.builtin._tryimport('md5', 'hashlib').md5
lib = py.builtin._tryimport('sha', 'hashlib')
sha = getattr(lib, 'sha1', getattr(lib, 'sha', None))
fn = tmpdir.join("testhashfile")
data = 'hello'.encode('ascii')
fn.write(data, mode="wb")
assert fn.computehash("md5") == md5(data).hexdigest()
assert fn.computehash("sha1") == sha(data).hexdigest()
py.test.raises(ValueError, fn.computehash, "asdasd")
def test_remove_removes_readonly_file(self, tmpdir):
readonly_file = tmpdir.join('readonly').ensure()
readonly_file.chmod(0)
readonly_file.remove()
assert not readonly_file.check(exists=1)
def test_remove_removes_readonly_dir(self, tmpdir):
readonly_dir = tmpdir.join('readonlydir').ensure(dir=1)
readonly_dir.chmod(int("500", 8))
readonly_dir.remove()
assert not readonly_dir.check(exists=1)
def test_remove_removes_dir_and_readonly_file(self, tmpdir):
readonly_dir = tmpdir.join('readonlydir').ensure(dir=1)
readonly_file = readonly_dir.join('readonlyfile').ensure()
readonly_file.chmod(0)
readonly_dir.remove()
assert not readonly_dir.check(exists=1)
def test_remove_routes_ignore_errors(self, tmpdir, monkeypatch):
l = []
monkeypatch.setattr(
'shutil.rmtree',
lambda *args, **kwargs: l.append(kwargs))
tmpdir.remove()
assert not l[0]['ignore_errors']
for val in (True, False):
l[:] = []
tmpdir.remove(ignore_errors=val)
assert l[0]['ignore_errors'] == val
def test_initialize_curdir(self):
assert str(local()) == os.getcwd()
@skiponwin32
def test_chdir_gone(self, path1):
p = path1.ensure("dir_to_be_removed", dir=1)
p.chdir()
p.remove()
pytest.raises(py.error.ENOENT, py.path.local)
assert path1.chdir() is None
assert os.getcwd() == str(path1)
def test_as_cwd(self, path1):
dir = path1.ensure("subdir", dir=1)
old = py.path.local()
with dir.as_cwd() as x:
assert x == old
assert py.path.local() == dir
assert os.getcwd() == str(old)
def test_as_cwd_exception(self, path1):
old = py.path.local()
dir = path1.ensure("subdir", dir=1)
with pytest.raises(ValueError):
with dir.as_cwd():
raise ValueError()
assert old == py.path.local()
def test_initialize_reldir(self, path1):
with path1.as_cwd():
p = local('samplefile')
assert p.check()
def test_tilde_expansion(self, monkeypatch, tmpdir):
monkeypatch.setenv("HOME", str(tmpdir))
p = py.path.local("~", expanduser=True)
assert p == os.path.expanduser("~")
def test_eq_with_strings(self, path1):
path1 = path1.join('sampledir')
path2 = str(path1)
assert path1 == path2
assert path2 == path1
path3 = path1.join('samplefile')
assert path3 != path2
assert path2 != path3
def test_eq_with_none(self, path1):
assert path1 != None # noqa
def test_eq_non_ascii_unicode(self, path1):
path2 = path1.join(u'temp')
path3 = path1.join(u'ação')
path4 = path1.join(u'ディレクトリ')
assert path2 != path3
assert path2 != path4
assert path4 != path3
def test_gt_with_strings(self, path1):
path2 = path1.join('sampledir')
path3 = str(path1.join("ttt"))
assert path3 > path2
assert path2 < path3
assert path2 < "ttt"
assert "ttt" > path2
path4 = path1.join("aaa")
l = [path2, path4, path3]
assert sorted(l) == [path4, path2, path3]
def test_open_and_ensure(self, path1):
p = path1.join("sub1", "sub2", "file")
with p.open("w", ensure=1) as f:
f.write("hello")
assert p.read() == "hello"
def test_write_and_ensure(self, path1):
p = path1.join("sub1", "sub2", "file")
p.write("hello", ensure=1)
assert p.read() == "hello"
@py.test.mark.parametrize('bin', (False, True))
def test_dump(self, tmpdir, bin):
path = tmpdir.join("dumpfile%s" % int(bin))
try:
d = {'answer': 42}
path.dump(d, bin=bin)
f = path.open('rb+')
import pickle
dnew = pickle.load(f)
assert d == dnew
finally:
f.close()
@failsonjywin32
def test_setmtime(self):
import tempfile
import time
try:
fd, name = tempfile.mkstemp()
os.close(fd)
except AttributeError:
name = tempfile.mktemp()
open(name, 'w').close()
try:
mtime = int(time.time())-100
path = local(name)
assert path.mtime() != mtime
path.setmtime(mtime)
assert path.mtime() == mtime
path.setmtime()
assert path.mtime() != mtime
finally:
os.remove(name)
def test_normpath(self, path1):
new1 = path1.join("/otherdir")
new2 = path1.join("otherdir")
assert str(new1) == str(new2)
def test_mkdtemp_creation(self):
d = local.mkdtemp()
try:
assert d.check(dir=1)
finally:
d.remove(rec=1)
def test_tmproot(self):
d = local.mkdtemp()
tmproot = local.get_temproot()
try:
assert d.check(dir=1)
assert d.dirpath() == tmproot
finally:
d.remove(rec=1)
def test_chdir(self, tmpdir):
old = local()
try:
res = tmpdir.chdir()
assert str(res) == str(old)
assert os.getcwd() == str(tmpdir)
finally:
old.chdir()
def test_ensure_filepath_withdir(self, tmpdir):
newfile = tmpdir.join('test1', 'test')
newfile.ensure()
assert newfile.check(file=1)
newfile.write("42")
newfile.ensure()
s = newfile.read()
assert s == "42"
def test_ensure_filepath_withoutdir(self, tmpdir):
newfile = tmpdir.join('test1file')
t = newfile.ensure()
assert t == newfile
assert newfile.check(file=1)
def test_ensure_dirpath(self, tmpdir):
newfile = tmpdir.join('test1', 'testfile')
t = newfile.ensure(dir=1)
assert t == newfile
assert newfile.check(dir=1)
def test_ensure_non_ascii_unicode(self, tmpdir):
newfile = tmpdir.join(u'ação',u'ディレクトリ')
t = newfile.ensure(dir=1)
assert t == newfile
assert newfile.check(dir=1)
def test_init_from_path(self, tmpdir):
l = local()
l2 = local(l)
assert l2 == l
wc = py.path.svnwc('.')
l3 = local(wc)
assert l3 is not wc
assert l3.strpath == wc.strpath
assert not hasattr(l3, 'commit')
@py.test.mark.xfail(run=False, reason="unreliable est for long filenames")
def test_long_filenames(self, tmpdir):
if sys.platform == "win32":
py.test.skip("win32: work around needed for path length limit")
# see http://codespeak.net/pipermail/py-dev/2008q2/000922.html
# testing paths > 260 chars (which is Windows' limitation, but
# depending on how the paths are used), but > 4096 (which is the
# Linux' limitation) - the behaviour of paths with names > 4096 chars
# is undetermined
newfilename = '/test' * 60
l = tmpdir.join(newfilename)
l.ensure(file=True)
l.write('foo')
l2 = tmpdir.join(newfilename)
assert l2.read() == 'foo'
def test_visit_depth_first(self, tmpdir):
tmpdir.ensure("a", "1")
tmpdir.ensure("b", "2")
p3 = tmpdir.ensure("breadth")
l = list(tmpdir.visit(lambda x: x.check(file=1)))
assert len(l) == 3
# check that breadth comes last
assert l[2] == p3
def test_visit_rec_fnmatch(self, tmpdir):
p1 = tmpdir.ensure("a", "123")
tmpdir.ensure(".b", "345")
l = list(tmpdir.visit("???", rec="[!.]*"))
assert len(l) == 1
# check that breadth comes last
assert l[0] == p1
def test_fnmatch_file_abspath(self, tmpdir):
b = tmpdir.join("a", "b")
assert b.fnmatch(os.sep.join("ab"))
pattern = os.sep.join([str(tmpdir), "*", "b"])
assert b.fnmatch(pattern)
def test_sysfind(self):
name = sys.platform == "win32" and "cmd" or "test"
x = py.path.local.sysfind(name)
assert x.check(file=1)
assert py.path.local.sysfind('jaksdkasldqwe') is None
assert py.path.local.sysfind(name, paths=[]) is None
x2 = py.path.local.sysfind(name, paths=[x.dirpath()])
assert x2 == x
def test_fspath_protocol_other_class(self, fake_fspath_obj):
# py.path is always absolute
py_path = py.path.local(fake_fspath_obj)
str_path = fake_fspath_obj.__fspath__()
assert py_path.check(endswith=str_path)
assert py_path.join(fake_fspath_obj).strpath == os.path.join(
py_path.strpath, str_path)
def test_make_numbered_dir_multiprocess_safe(self, tmpdir):
# https://github.com/pytest-dev/py/issues/30
pool = multiprocessing.Pool()
results = [pool.apply_async(batch_make_numbered_dirs, [tmpdir, 100]) for _ in range(20)]
for r in results:
assert r.get()
class TestExecutionOnWindows:
pytestmark = win32only
def test_sysfind_bat_exe_before(self, tmpdir, monkeypatch):
monkeypatch.setenv("PATH", str(tmpdir), prepend=os.pathsep)
tmpdir.ensure("hello")
h = tmpdir.ensure("hello.bat")
x = py.path.local.sysfind("hello")
assert x == h
class TestExecution:
pytestmark = skiponwin32
def test_sysfind_no_permisson_ignored(self, monkeypatch, tmpdir):
noperm = tmpdir.ensure('noperm', dir=True)
monkeypatch.setenv("PATH", noperm, prepend=":")
noperm.chmod(0)
assert py.path.local.sysfind('jaksdkasldqwe') is None
def test_sysfind_absolute(self):
x = py.path.local.sysfind('test')
assert x.check(file=1)
y = py.path.local.sysfind(str(x))
assert y.check(file=1)
assert y == x
def test_sysfind_multiple(self, tmpdir, monkeypatch):
monkeypatch.setenv('PATH', "%s:%s" % (
tmpdir.ensure('a'),
tmpdir.join('b')),
prepend=":")
tmpdir.ensure('b', 'a')
x = py.path.local.sysfind(
'a', checker=lambda x: x.dirpath().basename == 'b')
assert x.basename == 'a'
assert x.dirpath().basename == 'b'
assert py.path.local.sysfind('a', checker=lambda x: None) is None
def test_sysexec(self):
x = py.path.local.sysfind('ls')
out = x.sysexec('-a')
for x in py.path.local().listdir():
assert out.find(x.basename) != -1
def test_sysexec_failing(self):
x = py.path.local.sysfind('false')
with pytest.raises(py.process.cmdexec.Error):
x.sysexec('aksjdkasjd')
def test_make_numbered_dir(self, tmpdir):
tmpdir.ensure('base.not_an_int', dir=1)
for i in range(10):
numdir = local.make_numbered_dir(prefix='base.', rootdir=tmpdir,
keep=2, lock_timeout=0)
assert numdir.check()
assert numdir.basename == 'base.%d' % i
if i >= 1:
assert numdir.new(ext=str(i-1)).check()
if i >= 2:
assert numdir.new(ext=str(i-2)).check()
if i >= 3:
assert not numdir.new(ext=str(i-3)).check()
def test_make_numbered_dir_case_insensitive(self, tmpdir, monkeypatch):
# https://github.com/pytest-dev/pytest/issues/708
monkeypatch.setattr(py._path.local, 'normcase',
lambda path: path.lower())
monkeypatch.setattr(tmpdir, 'listdir',
lambda: [tmpdir._fastjoin('case.0')])
numdir = local.make_numbered_dir(prefix='CAse.', rootdir=tmpdir,
keep=2, lock_timeout=0)
assert numdir.basename.endswith('.1')
def test_make_numbered_dir_case_sensitive(self, tmpdir, monkeypatch):
# https://github.com/pytest-dev/pytest/issues/708
monkeypatch.setattr(py._path.local, 'normcase', lambda path: path)
monkeypatch.setattr(tmpdir, 'listdir',
lambda: [tmpdir._fastjoin('case.0')])
numdir = local.make_numbered_dir(prefix='CAse.', rootdir=tmpdir,
keep=2, lock_timeout=0)
assert numdir.basename.endswith('.0')
def test_make_numbered_dir_NotImplemented_Error(self, tmpdir, monkeypatch):
def notimpl(x, y):
raise NotImplementedError(42)
monkeypatch.setattr(os, 'symlink', notimpl)
x = tmpdir.make_numbered_dir(rootdir=tmpdir, lock_timeout=0)
assert x.relto(tmpdir)
assert x.check()
def test_locked_make_numbered_dir(self, tmpdir):
for i in range(10):
numdir = local.make_numbered_dir(prefix='base2.', rootdir=tmpdir,
keep=2)
assert numdir.check()
assert numdir.basename == 'base2.%d' % i
for j in range(i):
assert numdir.new(ext=str(j)).check()
def test_error_preservation(self, path1):
py.test.raises(EnvironmentError, path1.join('qwoeqiwe').mtime)
py.test.raises(EnvironmentError, path1.join('qwoeqiwe').read)
# def test_parentdirmatch(self):
# local.parentdirmatch('std', startmodule=__name__)
#
class TestImport:
def test_pyimport(self, path1):
obj = path1.join('execfile.py').pyimport()
assert obj.x == 42
assert obj.__name__ == 'execfile'
def test_pyimport_renamed_dir_creates_mismatch(self, tmpdir):
p = tmpdir.ensure("a", "test_x123.py")
p.pyimport()
tmpdir.join("a").move(tmpdir.join("b"))
with pytest.raises(tmpdir.ImportMismatchError):
tmpdir.join("b", "test_x123.py").pyimport()
def test_pyimport_messy_name(self, tmpdir):
# http://bitbucket.org/hpk42/py-trunk/issue/129
path = tmpdir.ensure('foo__init__.py')
path.pyimport()
def test_pyimport_dir(self, tmpdir):
p = tmpdir.join("hello_123")
p_init = p.ensure("__init__.py")
m = p.pyimport()
assert m.__name__ == "hello_123"
m = p_init.pyimport()
assert m.__name__ == "hello_123"
def test_pyimport_execfile_different_name(self, path1):
obj = path1.join('execfile.py').pyimport(modname="0x.y.z")
assert obj.x == 42
assert obj.__name__ == '0x.y.z'
def test_pyimport_a(self, path1):
otherdir = path1.join('otherdir')
mod = otherdir.join('a.py').pyimport()
assert mod.result == "got it"
assert mod.__name__ == 'otherdir.a'
def test_pyimport_b(self, path1):
otherdir = path1.join('otherdir')
mod = otherdir.join('b.py').pyimport()
assert mod.stuff == "got it"
assert mod.__name__ == 'otherdir.b'
def test_pyimport_c(self, path1):
otherdir = path1.join('otherdir')
mod = otherdir.join('c.py').pyimport()
assert mod.value == "got it"
def test_pyimport_d(self, path1):
otherdir = path1.join('otherdir')
mod = otherdir.join('d.py').pyimport()
assert mod.value2 == "got it"
def test_pyimport_and_import(self, tmpdir):
tmpdir.ensure('xxxpackage', '__init__.py')
mod1path = tmpdir.ensure('xxxpackage', 'module1.py')
mod1 = mod1path.pyimport()
assert mod1.__name__ == 'xxxpackage.module1'
from xxxpackage import module1
assert module1 is mod1
def test_pyimport_check_filepath_consistency(self, monkeypatch, tmpdir):
name = 'pointsback123'
ModuleType = type(os)
p = tmpdir.ensure(name + '.py')
for ending in ('.pyc', '$py.class', '.pyo'):
mod = ModuleType(name)
pseudopath = tmpdir.ensure(name+ending)
mod.__file__ = str(pseudopath)
monkeypatch.setitem(sys.modules, name, mod)
newmod = p.pyimport()
assert mod == newmod
monkeypatch.undo()
mod = ModuleType(name)
pseudopath = tmpdir.ensure(name+"123.py")
mod.__file__ = str(pseudopath)
monkeypatch.setitem(sys.modules, name, mod)
excinfo = py.test.raises(pseudopath.ImportMismatchError, p.pyimport)
modname, modfile, orig = excinfo.value.args
assert modname == name
assert modfile == pseudopath
assert orig == p
assert issubclass(pseudopath.ImportMismatchError, ImportError)
def test_issue131_pyimport_on__init__(self, tmpdir):
# __init__.py files may be namespace packages, and thus the
# __file__ of an imported module may not be ourselves
# see issue
p1 = tmpdir.ensure("proja", "__init__.py")
p2 = tmpdir.ensure("sub", "proja", "__init__.py")
m1 = p1.pyimport()
m2 = p2.pyimport()
assert m1 == m2
def test_ensuresyspath_append(self, tmpdir):
root1 = tmpdir.mkdir("root1")
file1 = root1.ensure("x123.py")
assert str(root1) not in sys.path
file1.pyimport(ensuresyspath="append")
assert str(root1) == sys.path[-1]
assert str(root1) not in sys.path[:-1]
def test_pypkgdir(tmpdir):
pkg = tmpdir.ensure('pkg1', dir=1)
pkg.ensure("__init__.py")
pkg.ensure("subdir/__init__.py")
assert pkg.pypkgpath() == pkg
assert pkg.join('subdir', '__init__.py').pypkgpath() == pkg
def test_pypkgdir_unimportable(tmpdir):
pkg = tmpdir.ensure('pkg1-1', dir=1) # unimportable
pkg.ensure("__init__.py")
subdir = pkg.ensure("subdir/__init__.py").dirpath()
assert subdir.pypkgpath() == subdir
assert subdir.ensure("xyz.py").pypkgpath() == subdir
assert not pkg.pypkgpath()
def test_isimportable():
from py._path.local import isimportable
assert not isimportable("")
assert isimportable("x")
assert isimportable("x1")
assert isimportable("x_1")
assert isimportable("_")
assert isimportable("_1")
assert not isimportable("x-1")
assert not isimportable("x:1")
def test_homedir_from_HOME(monkeypatch):
path = os.getcwd()
monkeypatch.setenv("HOME", path)
assert py.path.local._gethomedir() == py.path.local(path)
def test_homedir_not_exists(monkeypatch):
monkeypatch.delenv("HOME", raising=False)
monkeypatch.delenv("HOMEDRIVE", raising=False)
homedir = py.path.local._gethomedir()
assert homedir is None
def test_samefile(tmpdir):
assert tmpdir.samefile(tmpdir)
p = tmpdir.ensure("hello")
assert p.samefile(p)
with p.dirpath().as_cwd():
assert p.samefile(p.basename)
if sys.platform == "win32":
p1 = p.__class__(str(p).lower())
p2 = p.__class__(str(p).upper())
assert p1.samefile(p2)
def test_listdir_single_arg(tmpdir):
tmpdir.ensure("hello")
assert tmpdir.listdir("hello")[0].basename == "hello"
def test_mkdtemp_rootdir(tmpdir):
dtmp = local.mkdtemp(rootdir=tmpdir)
assert tmpdir.listdir() == [dtmp]
class TestWINLocalPath:
pytestmark = win32only
def test_owner_group_not_implemented(self, path1):
py.test.raises(NotImplementedError, "path1.stat().owner")
py.test.raises(NotImplementedError, "path1.stat().group")
def test_chmod_simple_int(self, path1):
py.builtin.print_("path1 is", path1)
mode = path1.stat().mode
# Ensure that we actually change the mode to something different.
path1.chmod(mode == 0 and 1 or 0)
try:
print(path1.stat().mode)
print(mode)
assert path1.stat().mode != mode
finally:
path1.chmod(mode)
assert path1.stat().mode == mode
def test_path_comparison_lowercase_mixed(self, path1):
t1 = path1.join("a_path")
t2 = path1.join("A_path")
assert t1 == t1
assert t1 == t2
def test_relto_with_mixed_case(self, path1):
t1 = path1.join("a_path", "fiLe")
t2 = path1.join("A_path")
assert t1.relto(t2) == "fiLe"
def test_allow_unix_style_paths(self, path1):
t1 = path1.join('a_path')
assert t1 == str(path1) + '\\a_path'
t1 = path1.join('a_path/')
assert t1 == str(path1) + '\\a_path'
t1 = path1.join('dir/a_path')
assert t1 == str(path1) + '\\dir\\a_path'
def test_sysfind_in_currentdir(self, path1):
cmd = py.path.local.sysfind('cmd')
root = cmd.new(dirname='', basename='') # c:\ in most installations
with root.as_cwd():
x = py.path.local.sysfind(cmd.relto(root))
assert x.check(file=1)
def test_fnmatch_file_abspath_posix_pattern_on_win32(self, tmpdir):
# path-matching patterns might contain a posix path separator '/'
# Test that we can match that pattern on windows.
import posixpath
b = tmpdir.join("a", "b")
assert b.fnmatch(posixpath.sep.join("ab"))
pattern = posixpath.sep.join([str(tmpdir), "*", "b"])
assert b.fnmatch(pattern)
class TestPOSIXLocalPath:
pytestmark = skiponwin32
def test_hardlink(self, tmpdir):
linkpath = tmpdir.join('test')
filepath = tmpdir.join('file')
filepath.write("Hello")
nlink = filepath.stat().nlink
linkpath.mklinkto(filepath)
assert filepath.stat().nlink == nlink + 1
def test_symlink_are_identical(self, tmpdir):
filepath = tmpdir.join('file')
filepath.write("Hello")
linkpath = tmpdir.join('test')
linkpath.mksymlinkto(filepath)
assert linkpath.readlink() == str(filepath)
def test_symlink_isfile(self, tmpdir):
linkpath = tmpdir.join('test')
filepath = tmpdir.join('file')
filepath.write("")
linkpath.mksymlinkto(filepath)
assert linkpath.check(file=1)
assert not linkpath.check(link=0, file=1)
assert linkpath.islink()
def test_symlink_relative(self, tmpdir):
linkpath = tmpdir.join('test')
filepath = tmpdir.join('file')
filepath.write("Hello")
linkpath.mksymlinkto(filepath, absolute=False)
assert linkpath.readlink() == "file"
assert filepath.read() == linkpath.read()
def test_symlink_not_existing(self, tmpdir):
linkpath = tmpdir.join('testnotexisting')
assert not linkpath.check(link=1)
assert linkpath.check(link=0)
def test_relto_with_root(self, path1, tmpdir):
y = path1.join('x').relto(py.path.local('/'))
assert y[0] == str(path1)[1]
def test_visit_recursive_symlink(self, tmpdir):
linkpath = tmpdir.join('test')
linkpath.mksymlinkto(tmpdir)
visitor = tmpdir.visit(None, lambda x: x.check(link=0))
assert list(visitor) == [linkpath]
def test_symlink_isdir(self, tmpdir):
linkpath = tmpdir.join('test')
linkpath.mksymlinkto(tmpdir)
assert linkpath.check(dir=1)
assert not linkpath.check(link=0, dir=1)
def test_symlink_remove(self, tmpdir):
linkpath = tmpdir.join('test')
linkpath.mksymlinkto(linkpath) # point to itself
assert linkpath.check(link=1)
linkpath.remove()
assert not linkpath.check()
def test_realpath_file(self, tmpdir):
linkpath = tmpdir.join('test')
filepath = tmpdir.join('file')
filepath.write("")
linkpath.mksymlinkto(filepath)
realpath = linkpath.realpath()
assert realpath.basename == 'file'
def test_owner(self, path1, tmpdir):
from pwd import getpwuid
from grp import getgrgid
stat = path1.stat()
assert stat.path == path1
uid = stat.uid
gid = stat.gid
owner = getpwuid(uid)[0]
group = getgrgid(gid)[0]
assert uid == stat.uid
assert owner == stat.owner
assert gid == stat.gid
assert group == stat.group
def test_stat_helpers(self, tmpdir, monkeypatch):
path1 = tmpdir.ensure("file")
stat1 = path1.stat()
stat2 = tmpdir.stat()
assert stat1.isfile()
assert stat2.isdir()
assert not stat1.islink()
assert not stat2.islink()
def test_stat_non_raising(self, tmpdir):
path1 = tmpdir.join("file")
pytest.raises(py.error.ENOENT, lambda: path1.stat())
res = path1.stat(raising=False)
assert res is None
def test_atime(self, tmpdir):
import time
path = tmpdir.ensure('samplefile')
now = time.time()
atime1 = path.atime()
# we could wait here but timer resolution is very
# system dependent
path.read()
time.sleep(ATIME_RESOLUTION)
atime2 = path.atime()
time.sleep(ATIME_RESOLUTION)
duration = time.time() - now
assert (atime2-atime1) <= duration
def test_commondir(self, path1):
# XXX This is here in local until we find a way to implement this
# using the subversion command line api.
p1 = path1.join('something')
p2 = path1.join('otherthing')
assert p1.common(p2) == path1
assert p2.common(p1) == path1
def test_commondir_nocommon(self, path1):
# XXX This is here in local until we find a way to implement this
# using the subversion command line api.
p1 = path1.join('something')
p2 = py.path.local(path1.sep+'blabla')
assert p1.common(p2) == '/'
def test_join_to_root(self, path1):
root = path1.parts()[0]
assert len(str(root)) == 1
assert str(root.join('a')) == '/a'
def test_join_root_to_root_with_no_abs(self, path1):
nroot = path1.join('/')
assert str(path1) == str(nroot)
assert path1 == nroot
def test_chmod_simple_int(self, path1):
mode = path1.stat().mode
path1.chmod(int(mode/2))
try:
assert path1.stat().mode != mode
finally:
path1.chmod(mode)
assert path1.stat().mode == mode
def test_chmod_rec_int(self, path1):
# XXX fragile test
def recfilter(x): return x.check(dotfile=0, link=0)
oldmodes = {}
for x in path1.visit(rec=recfilter):
oldmodes[x] = x.stat().mode
path1.chmod(int("772", 8), rec=recfilter)
try:
for x in path1.visit(rec=recfilter):
assert x.stat().mode & int("777", 8) == int("772", 8)
finally:
for x, y in oldmodes.items():
x.chmod(y)
def test_copy_archiving(self, tmpdir):
unicode_fn = u"something-\342\200\223.txt"
f = tmpdir.ensure("a", unicode_fn)
a = f.dirpath()
oldmode = f.stat().mode
newmode = oldmode ^ 1
f.chmod(newmode)
b = tmpdir.join("b")
a.copy(b, mode=True)
assert b.join(f.basename).stat().mode == newmode
def test_copy_stat_file(self, tmpdir):
src = tmpdir.ensure('src')
dst = tmpdir.join('dst')
# a small delay before the copy
time.sleep(ATIME_RESOLUTION)
src.copy(dst, stat=True)
oldstat = src.stat()
newstat = dst.stat()
assert oldstat.mode == newstat.mode
assert (dst.atime() - src.atime()) < ATIME_RESOLUTION
assert (dst.mtime() - src.mtime()) < ATIME_RESOLUTION
def test_copy_stat_dir(self, tmpdir):
test_files = ['a', 'b', 'c']
src = tmpdir.join('src')
for f in test_files:
src.join(f).write(f, ensure=True)
dst = tmpdir.join('dst')
# a small delay before the copy
time.sleep(ATIME_RESOLUTION)
src.copy(dst, stat=True)
for f in test_files:
oldstat = src.join(f).stat()
newstat = dst.join(f).stat()
assert (newstat.atime - oldstat.atime) < ATIME_RESOLUTION
assert (newstat.mtime - oldstat.mtime) < ATIME_RESOLUTION
assert oldstat.mode == newstat.mode
@failsonjython
def test_chown_identity(self, path1):
owner = path1.stat().owner
group = path1.stat().group
path1.chown(owner, group)
@failsonjython
def test_chown_dangling_link(self, path1):
owner = path1.stat().owner
group = path1.stat().group
x = path1.join('hello')
x.mksymlinkto('qlwkejqwlek')
try:
path1.chown(owner, group, rec=1)
finally:
x.remove(rec=0)
@failsonjython
def test_chown_identity_rec_mayfail(self, path1):
owner = path1.stat().owner
group = path1.stat().group
path1.chown(owner, group)
class TestUnicodePy2Py3:
def test_join_ensure(self, tmpdir, monkeypatch):
if sys.version_info >= (3, 0) and "LANG" not in os.environ:
pytest.skip("cannot run test without locale")
x = py.path.local(tmpdir.strpath)
part = "hällo"
y = x.ensure(part)
assert x.join(part) == y
def test_listdir(self, tmpdir):
if sys.version_info >= (3, 0) and "LANG" not in os.environ:
pytest.skip("cannot run test without locale")
x = py.path.local(tmpdir.strpath)
part = "hällo"
y = x.ensure(part)
assert x.listdir(part)[0] == y
@pytest.mark.xfail(
reason="changing read/write might break existing usages")
def test_read_write(self, tmpdir):
x = tmpdir.join("hello")
part = py.builtin._totext("hällo", "utf8")
x.write(part)
assert x.read() == part
x.write(part.encode(sys.getdefaultencoding()))
assert x.read() == part.encode(sys.getdefaultencoding())
class TestBinaryAndTextMethods:
def test_read_binwrite(self, tmpdir):
x = tmpdir.join("hello")
part = py.builtin._totext("hällo", "utf8")
part_utf8 = part.encode("utf8")
x.write_binary(part_utf8)
assert x.read_binary() == part_utf8
s = x.read_text(encoding="utf8")
assert s == part
assert py.builtin._istext(s)
def test_read_textwrite(self, tmpdir):
x = tmpdir.join("hello")
part = py.builtin._totext("hällo", "utf8")
part_utf8 = part.encode("utf8")
x.write_text(part, encoding="utf8")
assert x.read_binary() == part_utf8
assert x.read_text(encoding="utf8") == part
def test_default_encoding(self, tmpdir):
x = tmpdir.join("hello")
# Can't use UTF8 as the default encoding (ASCII) doesn't support it
part = py.builtin._totext("hello", "ascii")
x.write_text(part, "ascii")
s = x.read_text("ascii")
assert s == part
assert type(s) == type(part)
py-1.5.2/testing/path/test_svnauth.py 0000664 0001750 0001750 00000037522 13202663755 021033 0 ustar vagrant vagrant 0000000 0000000 import py
from py.path import SvnAuth
import time
import sys
svnbin = py.path.local.sysfind('svn')
def make_repo_auth(repo, userdata):
""" write config to repo
user information in userdata is used for auth
userdata has user names as keys, and a tuple (password, readwrite) as
values, where 'readwrite' is either 'r' or 'rw'
"""
confdir = py.path.local(repo).join('conf')
confdir.join('svnserve.conf').write('''\
[general]
anon-access = none
password-db = passwd
authz-db = authz
realm = TestRepo
''')
authzdata = '[/]\n'
passwddata = '[users]\n'
for user in userdata:
authzdata += '%s = %s\n' % (user, userdata[user][1])
passwddata += '%s = %s\n' % (user, userdata[user][0])
confdir.join('authz').write(authzdata)
confdir.join('passwd').write(passwddata)
def serve_bg(repopath):
pidfile = py.path.local(repopath).join('pid')
port = 10000
e = None
while port < 10010:
cmd = 'svnserve -d -T --listen-port=%d --pid-file=%s -r %s' % (
port, pidfile, repopath)
print(cmd)
try:
py.process.cmdexec(cmd)
except py.process.cmdexec.Error:
e = sys.exc_info()[1]
else:
# XXX we assume here that the pid file gets written somewhere, I
# guess this should be relatively safe... (I hope, at least?)
counter = pid = 0
while counter < 10:
counter += 1
try:
pid = pidfile.read()
except py.error.ENOENT:
pass
if pid:
break
time.sleep(0.2)
return port, int(pid)
port += 1
raise IOError('could not start svnserve: %s' % (e,))
class TestSvnAuth(object):
def test_basic(self):
auth = SvnAuth('foo', 'bar')
assert auth.username == 'foo'
assert auth.password == 'bar'
assert str(auth)
def test_makecmdoptions_uname_pw_makestr(self):
auth = SvnAuth('foo', 'bar')
assert auth.makecmdoptions() == '--username="foo" --password="bar"'
def test_makecmdoptions_quote_escape(self):
auth = SvnAuth('fo"o', '"ba\'r"')
assert auth.makecmdoptions() == '--username="fo\\"o" --password="\\"ba\'r\\""'
def test_makecmdoptions_no_cache_auth(self):
auth = SvnAuth('foo', 'bar', cache_auth=False)
assert auth.makecmdoptions() == ('--username="foo" --password="bar" '
'--no-auth-cache')
def test_makecmdoptions_no_interactive(self):
auth = SvnAuth('foo', 'bar', interactive=False)
assert auth.makecmdoptions() == ('--username="foo" --password="bar" '
'--non-interactive')
def test_makecmdoptions_no_interactive_no_cache_auth(self):
auth = SvnAuth('foo', 'bar', cache_auth=False,
interactive=False)
assert auth.makecmdoptions() == ('--username="foo" --password="bar" '
'--no-auth-cache --non-interactive')
class svnwc_no_svn(py.path.svnwc):
def __new__(cls, *args, **kwargs):
self = super(svnwc_no_svn, cls).__new__(cls, *args, **kwargs)
self.commands = []
return self
def _svn(self, *args):
self.commands.append(args)
class TestSvnWCAuth(object):
def setup_method(self, meth):
if not svnbin:
py.test.skip("svn binary required")
self.auth = SvnAuth('user', 'pass', cache_auth=False)
def test_checkout(self):
wc = svnwc_no_svn('foo', auth=self.auth)
wc.checkout('url')
assert wc.commands[0][-1] == ('--username="user" --password="pass" '
'--no-auth-cache')
def test_commit(self):
wc = svnwc_no_svn('foo', auth=self.auth)
wc.commit('msg')
assert wc.commands[0][-1] == ('--username="user" --password="pass" '
'--no-auth-cache')
def test_checkout_no_cache_auth(self):
wc = svnwc_no_svn('foo', auth=self.auth)
wc.checkout('url')
assert wc.commands[0][-1] == ('--username="user" --password="pass" '
'--no-auth-cache')
def test_checkout_auth_from_constructor(self):
wc = svnwc_no_svn('foo', auth=self.auth)
wc.checkout('url')
assert wc.commands[0][-1] == ('--username="user" --password="pass" '
'--no-auth-cache')
class svnurl_no_svn(py.path.svnurl):
cmdexec_output = 'test'
popen_output = 'test'
def __new__(cls, *args, **kwargs):
self = super(svnurl_no_svn, cls).__new__(cls, *args, **kwargs)
self.commands = []
return self
def _cmdexec(self, cmd):
self.commands.append(cmd)
return self.cmdexec_output
def _popen(self, cmd):
self.commands.append(cmd)
return self.popen_output
class TestSvnURLAuth(object):
def setup_method(self, meth):
self.auth = SvnAuth('foo', 'bar')
def test_init(self):
u = svnurl_no_svn('http://foo.bar/svn')
assert u.auth is None
u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
assert u.auth is self.auth
def test_new(self):
u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
new = u.new(basename='bar')
assert new.auth is self.auth
assert new.url == 'http://foo.bar/svn/bar'
def test_join(self):
u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
new = u.join('foo')
assert new.auth is self.auth
assert new.url == 'http://foo.bar/svn/foo'
def test_listdir(self):
u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
u.cmdexec_output = '''\
1717 johnny 1529 Nov 04 14:32 LICENSE.txt
1716 johnny 5352 Nov 04 14:28 README.txt
'''
paths = u.listdir()
assert paths[0].auth is self.auth
assert paths[1].auth is self.auth
assert paths[0].basename == 'LICENSE.txt'
def test_info(self):
u = svnurl_no_svn('http://foo.bar/svn/LICENSE.txt', auth=self.auth)
def dirpath(self):
return self
u.cmdexec_output = '''\
1717 johnny 1529 Nov 04 14:32 LICENSE.txt
1716 johnny 5352 Nov 04 14:28 README.txt
'''
org_dp = u.__class__.dirpath
u.__class__.dirpath = dirpath
try:
info = u.info()
finally:
u.dirpath = org_dp
assert info.size == 1529
def test_open(self):
u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
foo = u.join('foo')
foo.check = lambda *args, **kwargs: True
ret = foo.open()
assert ret == 'test'
assert '--username="foo" --password="bar"' in foo.commands[0]
def test_dirpath(self):
u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
parent = u.dirpath()
assert parent.auth is self.auth
def test_mkdir(self):
u = svnurl_no_svn('http://foo.bar/svn/qweqwe', auth=self.auth)
assert not u.commands
u.mkdir(msg='created dir foo')
assert u.commands
assert '--username="foo" --password="bar"' in u.commands[0]
def test_copy(self):
u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
u2 = svnurl_no_svn('http://foo.bar/svn2')
u.copy(u2, 'copied dir')
assert '--username="foo" --password="bar"' in u.commands[0]
def test_rename(self):
u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
u.rename('http://foo.bar/svn/bar', 'moved foo to bar')
assert '--username="foo" --password="bar"' in u.commands[0]
def test_remove(self):
u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
u.remove(msg='removing foo')
assert '--username="foo" --password="bar"' in u.commands[0]
def test_export(self):
u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
target = py.path.local('/foo')
u.export(target)
assert '--username="foo" --password="bar"' in u.commands[0]
def test_log(self):
u = svnurl_no_svn('http://foo.bar/svn/foo', auth=self.auth)
u.popen_output = py.io.TextIO(py.builtin._totext('''\
guido2008-02-11T12:12:18.476481ZCreating branch to work on auth support for py.path.svn*.
''', 'ascii'))
u.check = lambda *args, **kwargs: True
ret = u.log(10, 20, verbose=True)
assert '--username="foo" --password="bar"' in u.commands[0]
assert len(ret) == 1
assert int(ret[0].rev) == 51381
assert ret[0].author == 'guido'
def test_propget(self):
u = svnurl_no_svn('http://foo.bar/svn', auth=self.auth)
u.propget('foo')
assert '--username="foo" --password="bar"' in u.commands[0]
def pytest_funcarg__setup(request):
return Setup(request)
class Setup:
def __init__(self, request):
if not svnbin:
py.test.skip("svn binary required")
if not request.config.option.runslowtests:
py.test.skip('use --runslowtests to run these tests')
tmpdir = request.getfuncargvalue("tmpdir")
repodir = tmpdir.join("repo")
py.process.cmdexec('svnadmin create %s' % repodir)
if sys.platform == 'win32':
repodir = '/' + str(repodir).replace('\\', '/')
self.repo = py.path.svnurl("file://%s" % repodir)
if sys.platform == 'win32':
# remove trailing slash...
repodir = repodir[1:]
self.repopath = py.path.local(repodir)
self.temppath = tmpdir.mkdir("temppath")
self.auth = SvnAuth('johnny', 'foo', cache_auth=False,
interactive=False)
make_repo_auth(self.repopath, {'johnny': ('foo', 'rw')})
self.port, self.pid = serve_bg(self.repopath.dirpath())
# XXX caching is too global
py.path.svnurl._lsnorevcache._dict.clear()
request.addfinalizer(lambda: py.process.kill(self.pid))
class TestSvnWCAuthFunctional:
def test_checkout_constructor_arg(self, setup):
wc = py.path.svnwc(setup.temppath, auth=setup.auth)
wc.checkout(
'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename))
assert wc.join('.svn').check()
def test_checkout_function_arg(self, setup):
wc = py.path.svnwc(setup.temppath, auth=setup.auth)
wc.checkout(
'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename))
assert wc.join('.svn').check()
def test_checkout_failing_non_interactive(self, setup):
auth = SvnAuth('johnny', 'bar', cache_auth=False,
interactive=False)
wc = py.path.svnwc(setup.temppath, auth)
py.test.raises(Exception,
("wc.checkout('svn://localhost:%(port)s/%(repopath)s')" %
setup.__dict__))
def test_log(self, setup):
wc = py.path.svnwc(setup.temppath, setup.auth)
wc.checkout(
'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename))
foo = wc.ensure('foo.txt')
wc.commit('added foo.txt')
log = foo.log()
assert len(log) == 1
assert log[0].msg == 'added foo.txt'
def test_switch(self, setup):
import pytest
try:
import xdist
pytest.skip('#160: fails under xdist')
except ImportError:
pass
wc = py.path.svnwc(setup.temppath, auth=setup.auth)
svnurl = 'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename)
wc.checkout(svnurl)
wc.ensure('foo', dir=True).ensure('foo.txt').write('foo')
wc.commit('added foo dir with foo.txt file')
wc.ensure('bar', dir=True)
wc.commit('added bar dir')
bar = wc.join('bar')
bar.switch(svnurl + '/foo')
assert bar.join('foo.txt')
def test_update(self, setup):
wc1 = py.path.svnwc(setup.temppath.ensure('wc1', dir=True),
auth=setup.auth)
wc2 = py.path.svnwc(setup.temppath.ensure('wc2', dir=True),
auth=setup.auth)
wc1.checkout(
'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename))
wc2.checkout(
'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename))
wc1.ensure('foo', dir=True)
wc1.commit('added foo dir')
wc2.update()
assert wc2.join('foo').check()
auth = SvnAuth('unknown', 'unknown', interactive=False)
wc2.auth = auth
py.test.raises(Exception, 'wc2.update()')
def test_lock_unlock_status(self, setup):
port = setup.port
wc = py.path.svnwc(setup.temppath, auth=setup.auth)
wc.checkout(
'svn://localhost:%s/%s' % (port, setup.repopath.basename,))
wc.ensure('foo', file=True)
wc.commit('added foo file')
foo = wc.join('foo')
foo.lock()
status = foo.status()
assert status.locked
foo.unlock()
status = foo.status()
assert not status.locked
auth = SvnAuth('unknown', 'unknown', interactive=False)
foo.auth = auth
py.test.raises(Exception, 'foo.lock()')
py.test.raises(Exception, 'foo.unlock()')
def test_diff(self, setup):
port = setup.port
wc = py.path.svnwc(setup.temppath, auth=setup.auth)
wc.checkout(
'svn://localhost:%s/%s' % (port, setup.repopath.basename,))
wc.ensure('foo', file=True)
wc.commit('added foo file')
wc.update()
rev = int(wc.status().rev)
foo = wc.join('foo')
foo.write('bar')
diff = foo.diff()
assert '\n+bar\n' in diff
foo.commit('added some content')
diff = foo.diff()
assert not diff
diff = foo.diff(rev=rev)
assert '\n+bar\n' in diff
auth = SvnAuth('unknown', 'unknown', interactive=False)
foo.auth = auth
py.test.raises(Exception, 'foo.diff(rev=rev)')
class TestSvnURLAuthFunctional:
def test_listdir(self, setup):
port = setup.port
u = py.path.svnurl(
'svn://localhost:%s/%s' % (port, setup.repopath.basename),
auth=setup.auth)
u.ensure('foo')
paths = u.listdir()
assert len(paths) == 1
assert paths[0].auth is setup.auth
auth = SvnAuth('foo', 'bar', interactive=False)
u = py.path.svnurl(
'svn://localhost:%s/%s' % (port, setup.repopath.basename),
auth=auth)
py.test.raises(Exception, 'u.listdir()')
def test_copy(self, setup):
port = setup.port
u = py.path.svnurl(
'svn://localhost:%s/%s' % (port, setup.repopath.basename),
auth=setup.auth)
foo = u.mkdir('foo')
assert foo.check()
bar = u.join('bar')
foo.copy(bar)
assert bar.check()
assert bar.auth is setup.auth
auth = SvnAuth('foo', 'bar', interactive=False)
u = py.path.svnurl(
'svn://localhost:%s/%s' % (port, setup.repopath.basename),
auth=auth)
foo = u.join('foo')
bar = u.join('bar')
py.test.raises(Exception, 'foo.copy(bar)')
def test_write_read(self, setup):
port = setup.port
u = py.path.svnurl(
'svn://localhost:%s/%s' % (port, setup.repopath.basename),
auth=setup.auth)
foo = u.ensure('foo')
fp = foo.open()
try:
data = fp.read()
finally:
fp.close()
assert data == ''
auth = SvnAuth('foo', 'bar', interactive=False)
u = py.path.svnurl(
'svn://localhost:%s/%s' % (port, setup.repopath.basename),
auth=auth)
foo = u.join('foo')
py.test.raises(Exception, 'foo.open()')
# XXX rinse, repeat... :|
py-1.5.2/testing/log/ 0000775 0001750 0001750 00000000000 13203150701 015525 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/testing/log/test_log.py 0000664 0001750 0001750 00000015026 13203150646 017733 0 ustar vagrant vagrant 0000000 0000000 import py
from py._log.log import default_keywordmapper
callcapture = py.io.StdCapture.call
def setup_module(mod):
mod._oldstate = default_keywordmapper.getstate()
def teardown_module(mod):
default_keywordmapper.setstate(mod._oldstate)
class TestLogProducer:
def setup_method(self, meth):
from py._log.log import default_keywordmapper
default_keywordmapper.setstate(_oldstate)
def test_getstate_setstate(self):
state = py.log._getstate()
py.log.setconsumer("hello", [].append)
state2 = py.log._getstate()
assert state2 != state
py.log._setstate(state)
state3 = py.log._getstate()
assert state3 == state
def test_producer_repr(self):
d = py.log.Producer("default")
assert repr(d).find('default') != -1
def test_produce_one_keyword(self):
l = []
py.log.setconsumer('s1', l.append)
py.log.Producer('s1')("hello world")
assert len(l) == 1
msg = l[0]
assert msg.content().startswith('hello world')
assert msg.prefix() == '[s1] '
assert str(msg) == "[s1] hello world"
def test_producer_class(self):
p = py.log.Producer('x1')
l = []
py.log.setconsumer(p._keywords, l.append)
p("hello")
assert len(l) == 1
assert len(l[0].keywords) == 1
assert 'x1' == l[0].keywords[0]
def test_producer_caching(self):
p = py.log.Producer('x1')
x2 = p.x2
assert x2 is p.x2
class TestLogConsumer:
def setup_method(self, meth):
default_keywordmapper.setstate(_oldstate)
def test_log_none(self):
log = py.log.Producer("XXX")
l = []
py.log.setconsumer('XXX', l.append)
log("1")
assert l
l[:] = []
py.log.setconsumer('XXX', None)
log("2")
assert not l
def test_log_default_stderr(self):
res, out, err = callcapture(py.log.Producer("default"), "hello")
assert err.strip() == "[default] hello"
def test_simple_consumer_match(self):
l = []
py.log.setconsumer("x1", l.append)
p = py.log.Producer("x1 x2")
p("hello")
assert l
assert l[0].content() == "hello"
def test_simple_consumer_match_2(self):
l = []
p = py.log.Producer("x1 x2")
py.log.setconsumer(p._keywords, l.append)
p("42")
assert l
assert l[0].content() == "42"
def test_no_auto_producer(self):
p = py.log.Producer('x')
py.test.raises(AttributeError, "p._x")
py.test.raises(AttributeError, "p.x_y")
def test_setconsumer_with_producer(self):
l = []
p = py.log.Producer("hello")
py.log.setconsumer(p, l.append)
p("world")
assert str(l[0]) == "[hello] world"
def test_multi_consumer(self):
l = []
py.log.setconsumer("x1", l.append)
py.log.setconsumer("x1 x2", None)
p = py.log.Producer("x1 x2")
p("hello")
assert not l
py.log.Producer("x1")("hello")
assert l
assert l[0].content() == "hello"
def test_log_stderr(self):
py.log.setconsumer("xyz", py.log.STDOUT)
res, out, err = callcapture(py.log.Producer("xyz"), "hello")
assert not err
assert out.strip() == '[xyz] hello'
def test_log_file(self, tmpdir):
customlog = tmpdir.join('log.out')
py.log.setconsumer("default", open(str(customlog), 'w', 1))
py.log.Producer("default")("hello world #1")
assert customlog.readlines() == ['[default] hello world #1\n']
py.log.setconsumer("default", py.log.Path(customlog, buffering=False))
py.log.Producer("default")("hello world #2")
res = customlog.readlines()
assert res == ['[default] hello world #2\n'] # no append by default!
def test_log_file_append_mode(self, tmpdir):
logfilefn = tmpdir.join('log_append.out')
# The append mode is on by default, so we don't need to specify it for File
py.log.setconsumer("default", py.log.Path(logfilefn, append=True,
buffering=0))
assert logfilefn.check()
py.log.Producer("default")("hello world #1")
lines = logfilefn.readlines()
assert lines == ['[default] hello world #1\n']
py.log.setconsumer("default", py.log.Path(logfilefn, append=True,
buffering=0))
py.log.Producer("default")("hello world #1")
lines = logfilefn.readlines()
assert lines == ['[default] hello world #1\n',
'[default] hello world #1\n']
def test_log_file_delayed_create(self, tmpdir):
logfilefn = tmpdir.join('log_create.out')
py.log.setconsumer("default", py.log.Path(logfilefn,
delayed_create=True, buffering=0))
assert not logfilefn.check()
py.log.Producer("default")("hello world #1")
lines = logfilefn.readlines()
assert lines == ['[default] hello world #1\n']
def test_keyword_based_log_files(self, tmpdir):
logfiles = []
keywords = 'k1 k2 k3'.split()
for key in keywords:
path = tmpdir.join(key)
py.log.setconsumer(key, py.log.Path(path, buffering=0))
py.log.Producer('k1')('1')
py.log.Producer('k2')('2')
py.log.Producer('k3')('3')
for key in keywords:
path = tmpdir.join(key)
assert path.read().strip() == '[%s] %s' % (key, key[-1])
# disabled for now; the syslog log file can usually be read only by root
# I manually inspected /var/log/messages and the entries were there
def no_test_log_syslog(self):
py.log.setconsumer("default", py.log.Syslog())
py.log.default("hello world #1")
# disabled for now until I figure out how to read entries in the
# Event Logs on Windows
# I manually inspected the Application Log and the entries were there
def no_test_log_winevent(self):
py.log.setconsumer("default", py.log.WinEvent())
py.log.default("hello world #1")
# disabled for now until I figure out how to properly pass the parameters
def no_test_log_email(self):
py.log.setconsumer("default", py.log.Email(mailhost="gheorghiu.net",
fromaddr="grig",
toaddrs="grig",
subject = "py.log email"))
py.log.default("hello world #1")
py-1.5.2/testing/log/__init__.py 0000664 0001750 0001750 00000000000 13202663617 017642 0 ustar vagrant vagrant 0000000 0000000 py-1.5.2/testing/log/test_warning.py 0000664 0001750 0001750 00000004754 13202663755 020636 0 ustar vagrant vagrant 0000000 0000000 import sys
from distutils.version import LooseVersion
import pytest
import py
mypath = py.path.local(__file__).new(ext=".py")
win = sys.platform.startswith('win')
pytestmark = pytest.mark.skipif(win and LooseVersion(pytest.__version__) >= LooseVersion('3.1'),
reason='apiwarn is not compatible with pytest >= 3.1 (#162)')
@pytest.mark.xfail
def test_forwarding_to_warnings_module():
pytest.deprecated_call(py.log._apiwarn, "1.3", "..")
def test_apiwarn_functional(recwarn):
capture = py.io.StdCapture()
py.log._apiwarn("x.y.z", "something", stacklevel=1)
out, err = capture.reset()
py.builtin.print_("out", out)
py.builtin.print_("err", err)
assert err.find("x.y.z") != -1
lno = py.code.getrawcode(test_apiwarn_functional).co_firstlineno + 2
exp = "%s:%s" % (mypath, lno)
assert err.find(exp) != -1
def test_stacklevel(recwarn):
def f():
py.log._apiwarn("x", "some", stacklevel=2)
# 3
# 4
capture = py.io.StdCapture()
f()
out, err = capture.reset()
lno = py.code.getrawcode(test_stacklevel).co_firstlineno + 6
warning = str(err)
assert warning.find(":%s" % lno) != -1
def test_stacklevel_initpkg_with_resolve(testdir, recwarn):
testdir.makepyfile(modabc="""
import py
def f():
py.log._apiwarn("x", "some", stacklevel="apipkg123")
""")
testdir.makepyfile(apipkg123="""
def __getattr__():
import modabc
modabc.f()
""")
p = testdir.makepyfile("""
import apipkg123
apipkg123.__getattr__()
""")
capture = py.io.StdCapture()
p.pyimport()
out, err = capture.reset()
warning = str(err)
loc = 'test_stacklevel_initpkg_with_resolve.py:2'
assert warning.find(loc) != -1
def test_stacklevel_initpkg_no_resolve(recwarn):
def f():
py.log._apiwarn("x", "some", stacklevel="apipkg")
capture = py.io.StdCapture()
f()
out, err = capture.reset()
lno = py.code.getrawcode(test_stacklevel_initpkg_no_resolve).co_firstlineno + 2
warning = str(err)
assert warning.find(":%s" % lno) != -1
def test_function(recwarn):
capture = py.io.StdCapture()
py.log._apiwarn("x.y.z", "something", function=test_function)
out, err = capture.reset()
py.builtin.print_("out", out)
py.builtin.print_("err", err)
assert err.find("x.y.z") != -1
lno = py.code.getrawcode(test_function).co_firstlineno
exp = "%s:%s" % (mypath, lno)
assert err.find(exp) != -1
py-1.5.2/testing/io_/ 0000775 0001750 0001750 00000000000 13203150701 015512 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/testing/io_/test_saferepr.py 0000664 0001750 0001750 00000004327 13202663755 020761 0 ustar vagrant vagrant 0000000 0000000 # -*- coding: utf-8 -*-
from __future__ import generators
import py
import sys
saferepr = py.io.saferepr
class TestSafeRepr:
def test_simple_repr(self):
assert saferepr(1) == '1'
assert saferepr(None) == 'None'
def test_maxsize(self):
s = saferepr('x'*50, maxsize=25)
assert len(s) == 25
expected = repr('x'*10 + '...' + 'x'*10)
assert s == expected
def test_maxsize_error_on_instance(self):
class A:
def __repr__(self):
raise ValueError('...')
s = saferepr(('*'*50, A()), maxsize=25)
assert len(s) == 25
assert s[0] == '(' and s[-1] == ')'
def test_exceptions(self):
class BrokenRepr:
def __init__(self, ex):
self.ex = ex
foo = 0
def __repr__(self):
raise self.ex
class BrokenReprException(Exception):
__str__ = None
__repr__ = None
assert 'Exception' in saferepr(BrokenRepr(Exception("broken")))
s = saferepr(BrokenReprException("really broken"))
assert 'TypeError' in s
assert 'TypeError' in saferepr(BrokenRepr("string"))
s2 = saferepr(BrokenRepr(BrokenReprException('omg even worse')))
assert 'NameError' not in s2
assert 'unknown' in s2
def test_big_repr(self):
from py._io.saferepr import SafeRepr
assert len(saferepr(range(1000))) <= \
len('[' + SafeRepr().maxlist * "1000" + ']')
def test_repr_on_newstyle(self):
class Function(object):
def __repr__(self):
return "<%s>" %(self.name)
try:
s = saferepr(Function())
except Exception:
py.test.fail("saferepr failed for newstyle class")
def test_unicode(self):
val = py.builtin._totext('£€', 'utf-8')
reprval = py.builtin._totext("'£€'", 'utf-8')
assert saferepr(val) == reprval
def test_unicode_handling():
value = py.builtin._totext('\xc4\x85\xc4\x87\n', 'utf-8').encode('utf8')
def f():
raise Exception(value)
excinfo = py.test.raises(Exception, f)
s = str(excinfo)
if sys.version_info[0] < 3:
u = unicode(excinfo)
py-1.5.2/testing/io_/test_terminalwriter.py 0000664 0001750 0001750 00000022475 13202663755 022226 0 ustar vagrant vagrant 0000000 0000000
import py
import os, sys
from py._io import terminalwriter
import codecs
import pytest
def test_get_terminal_width():
x = py.io.get_terminal_width
assert x == terminalwriter.get_terminal_width
def test_getdimensions(monkeypatch):
fcntl = py.test.importorskip("fcntl")
import struct
l = []
monkeypatch.setattr(fcntl, 'ioctl', lambda *args: l.append(args))
try:
terminalwriter._getdimensions()
except (TypeError, struct.error):
pass
assert len(l) == 1
assert l[0][0] == 1
def test_terminal_width_COLUMNS(monkeypatch):
""" Dummy test for get_terminal_width
"""
fcntl = py.test.importorskip("fcntl")
monkeypatch.setattr(fcntl, 'ioctl', lambda *args: int('x'))
monkeypatch.setenv('COLUMNS', '42')
assert terminalwriter.get_terminal_width() == 42
monkeypatch.delenv('COLUMNS', raising=False)
def test_terminalwriter_defaultwidth_80(monkeypatch):
monkeypatch.setattr(terminalwriter, '_getdimensions', lambda: 0/0)
monkeypatch.delenv('COLUMNS', raising=False)
tw = py.io.TerminalWriter()
assert tw.fullwidth == 80
def test_terminalwriter_getdimensions_bogus(monkeypatch):
monkeypatch.setattr(terminalwriter, '_getdimensions', lambda: (10,10))
monkeypatch.delenv('COLUMNS', raising=False)
tw = py.io.TerminalWriter()
assert tw.fullwidth == 80
def test_terminalwriter_getdimensions_emacs(monkeypatch):
# emacs terminal returns (0,0) but set COLUMNS properly
monkeypatch.setattr(terminalwriter, '_getdimensions', lambda: (0,0))
monkeypatch.setenv('COLUMNS', '42')
tw = py.io.TerminalWriter()
assert tw.fullwidth == 42
def test_terminalwriter_computes_width(monkeypatch):
monkeypatch.setattr(terminalwriter, 'get_terminal_width', lambda: 42)
tw = py.io.TerminalWriter()
assert tw.fullwidth == 42
def test_terminalwriter_default_instantiation():
tw = py.io.TerminalWriter(stringio=True)
assert hasattr(tw, 'stringio')
def test_terminalwriter_dumb_term_no_markup(monkeypatch):
monkeypatch.setattr(os, 'environ', {'TERM': 'dumb', 'PATH': ''})
class MyFile:
closed = False
def isatty(self):
return True
monkeypatch.setattr(sys, 'stdout', MyFile())
try:
assert sys.stdout.isatty()
tw = py.io.TerminalWriter()
assert not tw.hasmarkup
finally:
monkeypatch.undo()
def test_terminalwriter_file_unicode(tmpdir):
f = codecs.open(str(tmpdir.join("xyz")), "wb", "utf8")
tw = py.io.TerminalWriter(file=f)
assert tw.encoding == "utf8"
def test_unicode_encoding():
msg = py.builtin._totext('b\u00f6y', 'utf8')
for encoding in 'utf8', 'latin1':
l = []
tw = py.io.TerminalWriter(l.append, encoding=encoding)
tw.line(msg)
assert l[0].strip() == msg.encode(encoding)
@pytest.mark.parametrize("encoding", ["ascii"])
def test_unicode_on_file_with_ascii_encoding(tmpdir, monkeypatch, encoding):
msg = py.builtin._totext('hell\xf6', "latin1")
#pytest.raises(UnicodeEncodeError, lambda: bytes(msg))
f = codecs.open(str(tmpdir.join("x")), "w", encoding)
tw = py.io.TerminalWriter(f)
tw.line(msg)
f.close()
s = tmpdir.join("x").open("rb").read().strip()
assert encoding == "ascii"
assert s == msg.encode("unicode-escape")
win32 = int(sys.platform == "win32")
class TestTerminalWriter:
def pytest_generate_tests(self, metafunc):
if "tw" in metafunc.funcargnames:
metafunc.addcall(id="path", param="path")
metafunc.addcall(id="stringio", param="stringio")
metafunc.addcall(id="callable", param="callable")
def pytest_funcarg__tw(self, request):
if request.param == "path":
tmpdir = request.getfuncargvalue("tmpdir")
p = tmpdir.join("tmpfile")
f = codecs.open(str(p), 'w+', encoding='utf8')
tw = py.io.TerminalWriter(f)
def getlines():
tw._file.flush()
return codecs.open(str(p), 'r',
encoding='utf8').readlines()
elif request.param == "stringio":
tw = py.io.TerminalWriter(stringio=True)
def getlines():
tw.stringio.seek(0)
return tw.stringio.readlines()
elif request.param == "callable":
writes = []
tw = py.io.TerminalWriter(writes.append)
def getlines():
io = py.io.TextIO()
io.write("".join(writes))
io.seek(0)
return io.readlines()
tw.getlines = getlines
tw.getvalue = lambda: "".join(getlines())
return tw
def test_line(self, tw):
tw.line("hello")
l = tw.getlines()
assert len(l) == 1
assert l[0] == "hello\n"
def test_line_unicode(self, tw):
for encoding in 'utf8', 'latin1':
tw._encoding = encoding
msg = py.builtin._totext('b\u00f6y', 'utf8')
tw.line(msg)
l = tw.getlines()
assert l[0] == msg + "\n"
def test_sep_no_title(self, tw):
tw.sep("-", fullwidth=60)
l = tw.getlines()
assert len(l) == 1
assert l[0] == "-" * (60-win32) + "\n"
def test_sep_with_title(self, tw):
tw.sep("-", "hello", fullwidth=60)
l = tw.getlines()
assert len(l) == 1
assert l[0] == "-" * 26 + " hello " + "-" * (27-win32) + "\n"
@py.test.mark.skipif("sys.platform == 'win32'")
def test__escaped(self, tw):
text2 = tw._escaped("hello", (31))
assert text2.find("hello") != -1
@py.test.mark.skipif("sys.platform == 'win32'")
def test_markup(self, tw):
for bold in (True, False):
for color in ("red", "green"):
text2 = tw.markup("hello", **{color: True, 'bold': bold})
assert text2.find("hello") != -1
py.test.raises(ValueError, "tw.markup('x', wronkw=3)")
py.test.raises(ValueError, "tw.markup('x', wronkw=0)")
def test_line_write_markup(self, tw):
tw.hasmarkup = True
tw.line("x", bold=True)
tw.write("x\n", red=True)
l = tw.getlines()
if sys.platform != "win32":
assert len(l[0]) >= 2, l
assert len(l[1]) >= 2, l
def test_attr_fullwidth(self, tw):
tw.sep("-", "hello", fullwidth=70)
tw.fullwidth = 70
tw.sep("-", "hello")
l = tw.getlines()
assert len(l[0]) == len(l[1])
def test_reline(self, tw):
tw.line("hello")
tw.hasmarkup = False
pytest.raises(ValueError, lambda: tw.reline("x"))
tw.hasmarkup = True
tw.reline("0 1 2")
tw.getlines()
l = tw.getvalue().split("\n")
assert len(l) == 2
tw.reline("0 1 3")
l = tw.getvalue().split("\n")
assert len(l) == 2
assert l[1].endswith("0 1 3\r")
tw.line("so")
l = tw.getvalue().split("\n")
assert len(l) == 3
assert l[-1] == ""
assert l[1] == ("0 1 2\r0 1 3\rso ")
assert l[0] == "hello"
def test_terminal_with_callable_write_and_flush():
l = set()
class fil:
flush = lambda self: l.add("1")
write = lambda self, x: l.add("1")
__call__ = lambda self, x: l.add("2")
tw = py.io.TerminalWriter(fil())
tw.line("hello")
assert l == set(["1"])
del fil.flush
l.clear()
tw = py.io.TerminalWriter(fil())
tw.line("hello")
assert l == set(["2"])
def test_chars_on_current_line():
tw = py.io.TerminalWriter(stringio=True)
written = []
def write_and_check(s, expected):
tw.write(s, bold=True)
written.append(s)
assert tw.chars_on_current_line == expected
assert tw.stringio.getvalue() == ''.join(written)
write_and_check('foo', 3)
write_and_check('bar', 6)
write_and_check('\n', 0)
write_and_check('\n', 0)
write_and_check('\n\n\n', 0)
write_and_check('\nfoo', 3)
write_and_check('\nfbar\nhello', 5)
write_and_check('10', 7)
@pytest.mark.skipif(sys.platform == "win32", reason="win32 has no native ansi")
def test_attr_hasmarkup():
tw = py.io.TerminalWriter(stringio=True)
assert not tw.hasmarkup
tw.hasmarkup = True
tw.line("hello", bold=True)
s = tw.stringio.getvalue()
assert len(s) > len("hello\n")
assert '\x1b[1m' in s
assert '\x1b[0m' in s
@pytest.mark.skipif(sys.platform == "win32", reason="win32 has no native ansi")
def test_ansi_print():
# we have no easy way to construct a file that
# represents a terminal
f = py.io.TextIO()
f.isatty = lambda: True
py.io.ansi_print("hello", 0x32, file=f)
text2 = f.getvalue()
assert text2.find("hello") != -1
assert len(text2) >= len("hello\n")
assert '\x1b[50m' in text2
assert '\x1b[0m' in text2
def test_should_do_markup_PY_COLORS_eq_1(monkeypatch):
monkeypatch.setitem(os.environ, 'PY_COLORS', '1')
tw = py.io.TerminalWriter(stringio=True)
assert tw.hasmarkup
tw.line("hello", bold=True)
s = tw.stringio.getvalue()
assert len(s) > len("hello\n")
assert '\x1b[1m' in s
assert '\x1b[0m' in s
def test_should_do_markup_PY_COLORS_eq_0(monkeypatch):
monkeypatch.setitem(os.environ, 'PY_COLORS', '0')
f = py.io.TextIO()
f.isatty = lambda: True
tw = py.io.TerminalWriter(file=f)
assert not tw.hasmarkup
tw.line("hello", bold=True)
s = f.getvalue()
assert s == "hello\n"
py-1.5.2/testing/io_/__init__.py 0000664 0001750 0001750 00000000002 13202663617 017631 0 ustar vagrant vagrant 0000000 0000000 #
py-1.5.2/testing/io_/test_capture.py 0000664 0001750 0001750 00000034710 13202663755 020614 0 ustar vagrant vagrant 0000000 0000000 from __future__ import with_statement
import os, sys
import py
needsdup = py.test.mark.skipif("not hasattr(os, 'dup')")
from py.builtin import print_
if sys.version_info >= (3,0):
def tobytes(obj):
if isinstance(obj, str):
obj = obj.encode('UTF-8')
assert isinstance(obj, bytes)
return obj
def totext(obj):
if isinstance(obj, bytes):
obj = str(obj, 'UTF-8')
assert isinstance(obj, str)
return obj
else:
def tobytes(obj):
if isinstance(obj, unicode):
obj = obj.encode('UTF-8')
assert isinstance(obj, str)
return obj
def totext(obj):
if isinstance(obj, str):
obj = unicode(obj, 'UTF-8')
assert isinstance(obj, unicode)
return obj
def oswritebytes(fd, obj):
os.write(fd, tobytes(obj))
class TestTextIO:
def test_text(self):
f = py.io.TextIO()
f.write("hello")
s = f.getvalue()
assert s == "hello"
f.close()
def test_unicode_and_str_mixture(self):
f = py.io.TextIO()
if sys.version_info >= (3,0):
f.write("\u00f6")
py.test.raises(TypeError, "f.write(bytes('hello', 'UTF-8'))")
else:
f.write(unicode("\u00f6", 'UTF-8'))
f.write("hello") # bytes
s = f.getvalue()
f.close()
assert isinstance(s, unicode)
def test_bytes_io():
f = py.io.BytesIO()
f.write(tobytes("hello"))
py.test.raises(TypeError, "f.write(totext('hello'))")
s = f.getvalue()
assert s == tobytes("hello")
def test_dontreadfrominput():
from py._io.capture import DontReadFromInput
f = DontReadFromInput()
assert not f.isatty()
py.test.raises(IOError, f.read)
py.test.raises(IOError, f.readlines)
py.test.raises(IOError, iter, f)
py.test.raises(ValueError, f.fileno)
f.close() # just for completeness
def pytest_funcarg__tmpfile(request):
testdir = request.getfuncargvalue("testdir")
f = testdir.makepyfile("").open('wb+')
request.addfinalizer(f.close)
return f
@needsdup
def test_dupfile(tmpfile):
flist = []
for i in range(5):
nf = py.io.dupfile(tmpfile, encoding="utf-8")
assert nf != tmpfile
assert nf.fileno() != tmpfile.fileno()
assert nf not in flist
print_(i, end="", file=nf)
flist.append(nf)
for i in range(5):
f = flist[i]
f.close()
tmpfile.seek(0)
s = tmpfile.read()
assert "01234" in repr(s)
tmpfile.close()
def test_dupfile_no_mode():
"""
dupfile should trap an AttributeError and return f if no mode is supplied.
"""
class SomeFileWrapper(object):
"An object with a fileno method but no mode attribute"
def fileno(self):
return 1
tmpfile = SomeFileWrapper()
assert py.io.dupfile(tmpfile) is tmpfile
with py.test.raises(AttributeError):
py.io.dupfile(tmpfile, raising=True)
def lsof_check(func):
pid = os.getpid()
try:
out = py.process.cmdexec("lsof -p %d" % pid)
except py.process.cmdexec.Error:
py.test.skip("could not run 'lsof'")
func()
out2 = py.process.cmdexec("lsof -p %d" % pid)
len1 = len([x for x in out.split("\n") if "REG" in x])
len2 = len([x for x in out2.split("\n") if "REG" in x])
assert len2 < len1 + 3, out2
class TestFDCapture:
pytestmark = needsdup
def test_not_now(self, tmpfile):
fd = tmpfile.fileno()
cap = py.io.FDCapture(fd, now=False)
data = tobytes("hello")
os.write(fd, data)
f = cap.done()
s = f.read()
assert not s
cap = py.io.FDCapture(fd, now=False)
cap.start()
os.write(fd, data)
f = cap.done()
s = f.read()
assert s == "hello"
def test_simple(self, tmpfile):
fd = tmpfile.fileno()
cap = py.io.FDCapture(fd)
data = tobytes("hello")
os.write(fd, data)
f = cap.done()
s = f.read()
assert s == "hello"
f.close()
def test_simple_many(self, tmpfile):
for i in range(10):
self.test_simple(tmpfile)
def test_simple_many_check_open_files(self, tmpfile):
lsof_check(lambda: self.test_simple_many(tmpfile))
def test_simple_fail_second_start(self, tmpfile):
fd = tmpfile.fileno()
cap = py.io.FDCapture(fd)
f = cap.done()
py.test.raises(ValueError, cap.start)
f.close()
def test_stderr(self):
cap = py.io.FDCapture(2, patchsys=True)
print_("hello", file=sys.stderr)
f = cap.done()
s = f.read()
assert s == "hello\n"
def test_stdin(self, tmpfile):
tmpfile.write(tobytes("3"))
tmpfile.seek(0)
cap = py.io.FDCapture(0, tmpfile=tmpfile)
# check with os.read() directly instead of raw_input(), because
# sys.stdin itself may be redirected (as py.test now does by default)
x = os.read(0, 100).strip()
f = cap.done()
assert x == tobytes("3")
def test_writeorg(self, tmpfile):
data1, data2 = tobytes("foo"), tobytes("bar")
try:
cap = py.io.FDCapture(tmpfile.fileno())
tmpfile.write(data1)
cap.writeorg(data2)
finally:
tmpfile.close()
f = cap.done()
scap = f.read()
assert scap == totext(data1)
stmp = open(tmpfile.name, 'rb').read()
assert stmp == data2
class TestStdCapture:
def getcapture(self, **kw):
return py.io.StdCapture(**kw)
def test_capturing_done_simple(self):
cap = self.getcapture()
sys.stdout.write("hello")
sys.stderr.write("world")
outfile, errfile = cap.done()
s = outfile.read()
assert s == "hello"
s = errfile.read()
assert s == "world"
def test_capturing_reset_simple(self):
cap = self.getcapture()
print("hello world")
sys.stderr.write("hello error\n")
out, err = cap.reset()
assert out == "hello world\n"
assert err == "hello error\n"
def test_capturing_readouterr(self):
cap = self.getcapture()
try:
print ("hello world")
sys.stderr.write("hello error\n")
out, err = cap.readouterr()
assert out == "hello world\n"
assert err == "hello error\n"
sys.stderr.write("error2")
finally:
out, err = cap.reset()
assert err == "error2"
def test_capturing_readouterr_unicode(self):
cap = self.getcapture()
print ("hx\xc4\x85\xc4\x87")
out, err = cap.readouterr()
assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8")
@py.test.mark.skipif('sys.version_info >= (3,)',
reason='text output different for bytes on python3')
def test_capturing_readouterr_decode_error_handling(self):
cap = self.getcapture()
# triggered a internal error in pytest
print('\xa6')
out, err = cap.readouterr()
assert out == py.builtin._totext('\ufffd\n', 'unicode-escape')
def test_capturing_mixed(self):
cap = self.getcapture(mixed=True)
sys.stdout.write("hello ")
sys.stderr.write("world")
sys.stdout.write(".")
out, err = cap.reset()
assert out.strip() == "hello world."
assert not err
def test_reset_twice_error(self):
cap = self.getcapture()
print ("hello")
out, err = cap.reset()
py.test.raises(ValueError, cap.reset)
assert out == "hello\n"
assert not err
def test_capturing_modify_sysouterr_in_between(self):
oldout = sys.stdout
olderr = sys.stderr
cap = self.getcapture()
sys.stdout.write("hello")
sys.stderr.write("world")
sys.stdout = py.io.TextIO()
sys.stderr = py.io.TextIO()
print ("not seen")
sys.stderr.write("not seen\n")
out, err = cap.reset()
assert out == "hello"
assert err == "world"
assert sys.stdout == oldout
assert sys.stderr == olderr
def test_capturing_error_recursive(self):
cap1 = self.getcapture()
print ("cap1")
cap2 = self.getcapture()
print ("cap2")
out2, err2 = cap2.reset()
out1, err1 = cap1.reset()
assert out1 == "cap1\n"
assert out2 == "cap2\n"
def test_just_out_capture(self):
cap = self.getcapture(out=True, err=False)
sys.stdout.write("hello")
sys.stderr.write("world")
out, err = cap.reset()
assert out == "hello"
assert not err
def test_just_err_capture(self):
cap = self.getcapture(out=False, err=True)
sys.stdout.write("hello")
sys.stderr.write("world")
out, err = cap.reset()
assert err == "world"
assert not out
def test_stdin_restored(self):
old = sys.stdin
cap = self.getcapture(in_=True)
newstdin = sys.stdin
out, err = cap.reset()
assert newstdin != sys.stdin
assert sys.stdin is old
def test_stdin_nulled_by_default(self):
print ("XXX this test may well hang instead of crashing")
print ("XXX which indicates an error in the underlying capturing")
print ("XXX mechanisms")
cap = self.getcapture()
py.test.raises(IOError, "sys.stdin.read()")
out, err = cap.reset()
def test_suspend_resume(self):
cap = self.getcapture(out=True, err=False, in_=False)
try:
print ("hello")
sys.stderr.write("error\n")
out, err = cap.suspend()
assert out == "hello\n"
assert not err
print ("in between")
sys.stderr.write("in between\n")
cap.resume()
print ("after")
sys.stderr.write("error_after\n")
finally:
out, err = cap.reset()
assert out == "after\n"
assert not err
class TestStdCaptureNotNow(TestStdCapture):
def getcapture(self, **kw):
kw['now'] = False
cap = py.io.StdCapture(**kw)
cap.startall()
return cap
class TestStdCaptureFD(TestStdCapture):
pytestmark = needsdup
def getcapture(self, **kw):
return py.io.StdCaptureFD(**kw)
def test_intermingling(self):
cap = self.getcapture()
oswritebytes(1, "1")
sys.stdout.write(str(2))
sys.stdout.flush()
oswritebytes(1, "3")
oswritebytes(2, "a")
sys.stderr.write("b")
sys.stderr.flush()
oswritebytes(2, "c")
out, err = cap.reset()
assert out == "123"
assert err == "abc"
def test_callcapture(self):
def func(x, y):
print (x)
sys.stderr.write(str(y))
return 42
res, out, err = py.io.StdCaptureFD.call(func, 3, y=4)
assert res == 42
assert out.startswith("3")
assert err.startswith("4")
def test_many(self, capfd):
def f():
for i in range(10):
cap = py.io.StdCaptureFD()
cap.reset()
lsof_check(f)
class TestStdCaptureFDNotNow(TestStdCaptureFD):
pytestmark = needsdup
def getcapture(self, **kw):
kw['now'] = False
cap = py.io.StdCaptureFD(**kw)
cap.startall()
return cap
@needsdup
def test_stdcapture_fd_tmpfile(tmpfile):
capfd = py.io.StdCaptureFD(out=tmpfile)
os.write(1, "hello".encode("ascii"))
os.write(2, "world".encode("ascii"))
outf, errf = capfd.done()
assert outf == tmpfile
class TestStdCaptureFDinvalidFD:
pytestmark = needsdup
def test_stdcapture_fd_invalid_fd(self, testdir):
testdir.makepyfile("""
import py, os
def test_stdout():
os.close(1)
cap = py.io.StdCaptureFD(out=True, err=False, in_=False)
cap.done()
def test_stderr():
os.close(2)
cap = py.io.StdCaptureFD(out=False, err=True, in_=False)
cap.done()
def test_stdin():
os.close(0)
cap = py.io.StdCaptureFD(out=False, err=False, in_=True)
cap.done()
""")
result = testdir.runpytest("--capture=fd")
assert result.ret == 0
assert result.parseoutcomes()['passed'] == 3
def test_capture_not_started_but_reset():
capsys = py.io.StdCapture(now=False)
capsys.done()
capsys.done()
capsys.reset()
@needsdup
def test_capture_no_sys():
capsys = py.io.StdCapture()
try:
cap = py.io.StdCaptureFD(patchsys=False)
sys.stdout.write("hello")
sys.stderr.write("world")
oswritebytes(1, "1")
oswritebytes(2, "2")
out, err = cap.reset()
assert out == "1"
assert err == "2"
finally:
capsys.reset()
@needsdup
def test_callcapture_nofd():
def func(x, y):
oswritebytes(1, "hello")
oswritebytes(2, "hello")
print (x)
sys.stderr.write(str(y))
return 42
capfd = py.io.StdCaptureFD(patchsys=False)
try:
res, out, err = py.io.StdCapture.call(func, 3, y=4)
finally:
capfd.reset()
assert res == 42
assert out.startswith("3")
assert err.startswith("4")
@needsdup
@py.test.mark.parametrize('use', [True, False])
def test_fdcapture_tmpfile_remains_the_same(tmpfile, use):
if not use:
tmpfile = True
cap = py.io.StdCaptureFD(out=False, err=tmpfile, now=False)
cap.startall()
capfile = cap.err.tmpfile
cap.suspend()
cap.resume()
capfile2 = cap.err.tmpfile
assert capfile2 == capfile
@py.test.mark.parametrize('method', ['StdCapture', 'StdCaptureFD'])
def test_capturing_and_logging_fundamentals(testdir, method):
if method == "StdCaptureFD" and not hasattr(os, 'dup'):
py.test.skip("need os.dup")
# here we check a fundamental feature
p = testdir.makepyfile("""
import sys, os
import py, logging
cap = py.io.%s(out=False, in_=False)
logging.warn("hello1")
outerr = cap.suspend()
print ("suspend, captured %%s" %%(outerr,))
logging.warn("hello2")
cap.resume()
logging.warn("hello3")
outerr = cap.suspend()
print ("suspend2, captured %%s" %% (outerr,))
""" % (method,))
result = testdir.runpython(p)
result.stdout.fnmatch_lines([
"suspend, captured*hello1*",
"suspend2, captured*hello2*WARNING:root:hello3*",
])
assert "atexit" not in result.stderr.str()
py-1.5.2/testing/code/ 0000775 0001750 0001750 00000000000 13203150701 015656 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/testing/code/test_assertion.py 0000664 0001750 0001750 00000017164 13202663755 021330 0 ustar vagrant vagrant 0000000 0000000 import pytest, py
import re
def exvalue():
import sys
return sys.exc_info()[1]
def f():
return 2
def test_assert():
try:
assert f() == 3
except AssertionError:
e = exvalue()
s = str(e)
assert s.startswith('assert 2 == 3\n')
def test_assert_within_finally():
excinfo = py.test.raises(ZeroDivisionError, """
try:
1/0
finally:
i = 42
""")
s = excinfo.exconly()
assert re.search("ZeroDivisionError:.*division", s) is not None
def test_assert_multiline_1():
try:
assert (f() ==
3)
except AssertionError:
e = exvalue()
s = str(e)
assert s.startswith('assert 2 == 3\n')
def test_assert_multiline_2():
try:
assert (f() == (4,
3)[-1])
except AssertionError:
e = exvalue()
s = str(e)
assert s.startswith('assert 2 ==')
def test_in():
try:
assert "hi" in [1, 2]
except AssertionError:
e = exvalue()
s = str(e)
assert s.startswith("assert 'hi' in")
def test_is():
try:
assert 1 is 2
except AssertionError:
e = exvalue()
s = str(e)
assert s.startswith("assert 1 is 2")
def test_attrib():
class Foo(object):
b = 1
i = Foo()
try:
assert i.b == 2
except AssertionError:
e = exvalue()
s = str(e)
assert s.startswith("assert 1 == 2")
def test_attrib_inst():
class Foo(object):
b = 1
try:
assert Foo().b == 2
except AssertionError:
e = exvalue()
s = str(e)
assert s.startswith("assert 1 == 2")
def test_len():
l = list(range(42))
try:
assert len(l) == 100
except AssertionError:
e = exvalue()
s = str(e)
assert s.startswith("assert 42 == 100")
assert "where 42 = len([" in s
def test_assert_keyword_arg():
def f(x=3):
return False
try:
assert f(x=5)
except AssertionError:
e = exvalue()
assert "x=5" in str(e)
# These tests should both fail, but should fail nicely...
class WeirdRepr:
def __repr__(self):
return ''
def bug_test_assert_repr():
v = WeirdRepr()
try:
assert v == 1
except AssertionError:
e = exvalue()
assert str(e).find('WeirdRepr') != -1
assert str(e).find('second line') != -1
assert 0
def test_assert_non_string():
try:
assert 0, ['list']
except AssertionError:
e = exvalue()
assert str(e).find("list") != -1
def test_assert_implicit_multiline():
try:
x = [1,2,3]
assert x != [1,
2, 3]
except AssertionError:
e = exvalue()
assert str(e).find('assert [1, 2, 3] !=') != -1
@py.test.mark.xfail(py.test.__version__[0] != "2",
reason="broken on modern pytest",
run=False
)
def test_assert_with_brokenrepr_arg():
class BrokenRepr:
def __repr__(self): 0 / 0
e = AssertionError(BrokenRepr())
if e.msg.find("broken __repr__") == -1:
py.test.fail("broken __repr__ not handle correctly")
def test_multiple_statements_per_line():
try:
a = 1; assert a == 2
except AssertionError:
e = exvalue()
assert "assert 1 == 2" in str(e)
def test_power():
try:
assert 2**3 == 7
except AssertionError:
e = exvalue()
assert "assert (2 ** 3) == 7" in str(e)
class TestView:
def setup_class(cls):
cls.View = py.test.importorskip("py._code._assertionold").View
def test_class_dispatch(self):
### Use a custom class hierarchy with existing instances
class Picklable(self.View):
pass
class Simple(Picklable):
__view__ = object
def pickle(self):
return repr(self.__obj__)
class Seq(Picklable):
__view__ = list, tuple, dict
def pickle(self):
return ';'.join(
[Picklable(item).pickle() for item in self.__obj__])
class Dict(Seq):
__view__ = dict
def pickle(self):
return Seq.pickle(self) + '!' + Seq(self.values()).pickle()
assert Picklable(123).pickle() == '123'
assert Picklable([1,[2,3],4]).pickle() == '1;2;3;4'
assert Picklable({1:2}).pickle() == '1!2'
def test_viewtype_class_hierarchy(self):
# Use a custom class hierarchy based on attributes of existing instances
class Operation:
"Existing class that I don't want to change."
def __init__(self, opname, *args):
self.opname = opname
self.args = args
existing = [Operation('+', 4, 5),
Operation('getitem', '', 'join'),
Operation('setattr', 'x', 'y', 3),
Operation('-', 12, 1)]
class PyOp(self.View):
def __viewkey__(self):
return self.opname
def generate(self):
return '%s(%s)' % (self.opname, ', '.join(map(repr, self.args)))
class PyBinaryOp(PyOp):
__view__ = ('+', '-', '*', '/')
def generate(self):
return '%s %s %s' % (self.args[0], self.opname, self.args[1])
codelines = [PyOp(op).generate() for op in existing]
assert codelines == ["4 + 5", "getitem('', 'join')",
"setattr('x', 'y', 3)", "12 - 1"]
def test_underscore_api():
py.code._AssertionError
py.code._reinterpret_old # used by pypy
py.code._reinterpret
def test_assert_customizable_reprcompare(monkeypatch):
util = pytest.importorskip("_pytest.assertion.util")
monkeypatch.setattr(util, '_reprcompare', lambda *args: 'hello')
try:
assert 3 == 4
except AssertionError:
e = exvalue()
s = str(e)
assert "hello" in s
def test_assert_long_source_1():
try:
assert len == [
(None, ['somet text', 'more text']),
]
except AssertionError:
e = exvalue()
s = str(e)
assert 're-run' not in s
assert 'somet text' in s
def test_assert_long_source_2():
try:
assert(len == [
(None, ['somet text', 'more text']),
])
except AssertionError:
e = exvalue()
s = str(e)
assert 're-run' not in s
assert 'somet text' in s
def test_assert_raise_alias(testdir):
testdir.makepyfile("""
import sys
EX = AssertionError
def test_hello():
raise EX("hello"
"multi"
"line")
""")
result = testdir.runpytest()
result.stdout.fnmatch_lines([
"*def test_hello*",
"*raise EX*",
"*1 failed*",
])
@py.test.mark.xfail(py.test.__version__[0] != "2",
reason="broken on modern pytest",
run=False)
def test_assert_raise_subclass():
class SomeEx(AssertionError):
def __init__(self, *args):
super(SomeEx, self).__init__()
try:
raise SomeEx("hello")
except AssertionError as e:
s = str(e)
assert 're-run' not in s
assert 'could not determine' in s
def test_assert_raises_in_nonzero_of_object_pytest_issue10():
class A(object):
def __nonzero__(self):
raise ValueError(42)
def __lt__(self, other):
return A()
def __repr__(self):
return ""
def myany(x):
return True
try:
assert not(myany(A() < 0))
except AssertionError:
e = exvalue()
s = str(e)
assert " < 0" in s
py-1.5.2/testing/code/test_code.py 0000664 0001750 0001750 00000010164 13202663617 020221 0 ustar vagrant vagrant 0000000 0000000 import py
import sys
def test_ne():
code1 = py.code.Code(compile('foo = "bar"', '', 'exec'))
assert code1 == code1
code2 = py.code.Code(compile('foo = "baz"', '', 'exec'))
assert code2 != code1
def test_code_gives_back_name_for_not_existing_file():
name = 'abc-123'
co_code = compile("pass\n", name, 'exec')
assert co_code.co_filename == name
code = py.code.Code(co_code)
assert str(code.path) == name
assert code.fullsource is None
def test_code_with_class():
class A:
pass
py.test.raises(TypeError, "py.code.Code(A)")
if True:
def x():
pass
def test_code_fullsource():
code = py.code.Code(x)
full = code.fullsource
assert 'test_code_fullsource()' in str(full)
def test_code_source():
code = py.code.Code(x)
src = code.source()
expected = """def x():
pass"""
assert str(src) == expected
def test_frame_getsourcelineno_myself():
def func():
return sys._getframe(0)
f = func()
f = py.code.Frame(f)
source, lineno = f.code.fullsource, f.lineno
assert source[lineno].startswith(" return sys._getframe(0)")
def test_getstatement_empty_fullsource():
def func():
return sys._getframe(0)
f = func()
f = py.code.Frame(f)
prop = f.code.__class__.fullsource
try:
f.code.__class__.fullsource = None
assert f.statement == py.code.Source("")
finally:
f.code.__class__.fullsource = prop
def test_code_from_func():
co = py.code.Code(test_frame_getsourcelineno_myself)
assert co.firstlineno
assert co.path
def test_builtin_patch_unpatch(monkeypatch):
cpy_builtin = py.builtin.builtins
comp = cpy_builtin.compile
def mycompile(*args, **kwargs):
return comp(*args, **kwargs)
class Sub(AssertionError):
pass
monkeypatch.setattr(cpy_builtin, 'AssertionError', Sub)
monkeypatch.setattr(cpy_builtin, 'compile', mycompile)
py.code.patch_builtins()
assert cpy_builtin.AssertionError != Sub
assert cpy_builtin.compile != mycompile
py.code.unpatch_builtins()
assert cpy_builtin.AssertionError is Sub
assert cpy_builtin.compile == mycompile
def test_unicode_handling():
value = py.builtin._totext('\xc4\x85\xc4\x87\n', 'utf-8').encode('utf8')
def f():
raise Exception(value)
excinfo = py.test.raises(Exception, f)
s = str(excinfo)
if sys.version_info[0] < 3:
u = unicode(excinfo)
def test_code_getargs():
def f1(x):
pass
c1 = py.code.Code(f1)
assert c1.getargs(var=True) == ('x',)
def f2(x, *y):
pass
c2 = py.code.Code(f2)
assert c2.getargs(var=True) == ('x', 'y')
def f3(x, **z):
pass
c3 = py.code.Code(f3)
assert c3.getargs(var=True) == ('x', 'z')
def f4(x, *y, **z):
pass
c4 = py.code.Code(f4)
assert c4.getargs(var=True) == ('x', 'y', 'z')
def test_frame_getargs():
def f1(x):
return sys._getframe(0)
fr1 = py.code.Frame(f1('a'))
assert fr1.getargs(var=True) == [('x', 'a')]
def f2(x, *y):
return sys._getframe(0)
fr2 = py.code.Frame(f2('a', 'b', 'c'))
assert fr2.getargs(var=True) == [('x', 'a'), ('y', ('b', 'c'))]
def f3(x, **z):
return sys._getframe(0)
fr3 = py.code.Frame(f3('a', b='c'))
assert fr3.getargs(var=True) == [('x', 'a'), ('z', {'b': 'c'})]
def f4(x, *y, **z):
return sys._getframe(0)
fr4 = py.code.Frame(f4('a', 'b', c='d'))
assert fr4.getargs(var=True) == [('x', 'a'), ('y', ('b',)),
('z', {'c': 'd'})]
class TestExceptionInfo:
def test_bad_getsource(self):
try:
if False: pass
else: assert False
except AssertionError:
exci = py.code.ExceptionInfo()
assert exci.getrepr()
class TestTracebackEntry:
def test_getsource(self):
try:
if False: pass
else: assert False
except AssertionError:
exci = py.code.ExceptionInfo()
entry = exci.traceback[0]
source = entry.getsource()
assert len(source) == 4
assert 'else: assert False' in source[3]
py-1.5.2/testing/code/test_excinfo.py 0000664 0001750 0001750 00000075063 13202663755 020756 0 ustar vagrant vagrant 0000000 0000000 # -*- coding: utf-8 -*-
import py
import pytest
import sys
from test_source import astonly
from py._code.code import FormattedExcinfo, ReprExceptionInfo
queue = py.builtin._tryimport('queue', 'Queue')
failsonjython = py.test.mark.xfail("sys.platform.startswith('java')")
try:
import importlib
except ImportError:
invalidate_import_caches = None
else:
invalidate_import_caches = getattr(importlib, "invalidate_caches", None)
pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3]))
broken_on_modern_pytest = pytest.mark.xfail(
pytest_version_info[0] != 2,
reason="this test hasn't been fixed after moving py.code into pytest",
run=False
)
class TWMock:
def __init__(self):
self.lines = []
def sep(self, sep, line=None):
self.lines.append((sep, line))
def line(self, line, **kw):
self.lines.append(line)
def markup(self, text, **kw):
return text
fullwidth = 80
def test_excinfo_simple():
try:
raise ValueError
except ValueError:
info = py.code.ExceptionInfo()
assert info.type == ValueError
def test_excinfo_getstatement():
def g():
raise ValueError
def f():
g()
try:
f()
except ValueError:
excinfo = py.code.ExceptionInfo()
linenumbers = [
py.code.getrawcode(f).co_firstlineno-1+3,
py.code.getrawcode(f).co_firstlineno-1+1,
py.code.getrawcode(g).co_firstlineno-1+1,
]
l = list(excinfo.traceback)
foundlinenumbers = [x.lineno for x in l]
assert foundlinenumbers == linenumbers
#for x in info:
# print "%s:%d %s" %(x.path.relto(root), x.lineno, x.statement)
#xxx
# testchain for getentries test below
def f():
#
raise ValueError
#
def g():
#
__tracebackhide__ = True
f()
#
def h():
#
g()
#
class TestTraceback_f_g_h:
def setup_method(self, method):
try:
h()
except ValueError:
self.excinfo = py.code.ExceptionInfo()
def test_traceback_entries(self):
tb = self.excinfo.traceback
entries = list(tb)
assert len(tb) == 4 # maybe fragile test
assert len(entries) == 4 # maybe fragile test
names = ['f', 'g', 'h']
for entry in entries:
try:
names.remove(entry.frame.code.name)
except ValueError:
pass
assert not names
def test_traceback_entry_getsource(self):
tb = self.excinfo.traceback
s = str(tb[-1].getsource())
assert s.startswith("def f():")
assert s.endswith("raise ValueError")
@astonly
@failsonjython
def test_traceback_entry_getsource_in_construct(self):
source = py.code.Source("""\
def xyz():
try:
raise ValueError
except somenoname:
pass
xyz()
""")
try:
exec (source.compile())
except NameError:
tb = py.code.ExceptionInfo().traceback
print (tb[-1].getsource())
s = str(tb[-1].getsource())
assert s.startswith("def xyz():\n try:")
assert s.strip().endswith("except somenoname:")
def test_traceback_cut(self):
co = py.code.Code(f)
path, firstlineno = co.path, co.firstlineno
traceback = self.excinfo.traceback
newtraceback = traceback.cut(path=path, firstlineno=firstlineno)
assert len(newtraceback) == 1
newtraceback = traceback.cut(path=path, lineno=firstlineno+2)
assert len(newtraceback) == 1
def test_traceback_cut_excludepath(self, testdir):
p = testdir.makepyfile("def f(): raise ValueError")
excinfo = py.test.raises(ValueError, "p.pyimport().f()")
basedir = py.path.local(py.test.__file__).dirpath()
newtraceback = excinfo.traceback.cut(excludepath=basedir)
for x in newtraceback:
if hasattr(x, 'path'):
assert not py.path.local(x.path).relto(basedir)
assert newtraceback[-1].frame.code.path == p
def test_traceback_filter(self):
traceback = self.excinfo.traceback
ntraceback = traceback.filter()
assert len(ntraceback) == len(traceback) - 1
def test_traceback_recursion_index(self):
def f(n):
if n < 10:
n += 1
f(n)
excinfo = py.test.raises(RuntimeError, f, 8)
traceback = excinfo.traceback
recindex = traceback.recursionindex()
assert recindex == 3
def test_traceback_only_specific_recursion_errors(self, monkeypatch):
def f(n):
if n == 0:
raise RuntimeError("hello")
f(n-1)
excinfo = pytest.raises(RuntimeError, f, 100)
monkeypatch.delattr(excinfo.traceback.__class__, "recursionindex")
repr = excinfo.getrepr()
assert "RuntimeError: hello" in str(repr.reprcrash)
def test_traceback_no_recursion_index(self):
def do_stuff():
raise RuntimeError
def reraise_me():
import sys
exc, val, tb = sys.exc_info()
py.builtin._reraise(exc, val, tb)
def f(n):
try:
do_stuff()
except:
reraise_me()
excinfo = py.test.raises(RuntimeError, f, 8)
traceback = excinfo.traceback
recindex = traceback.recursionindex()
assert recindex is None
def test_traceback_messy_recursion(self):
# XXX: simplified locally testable version
decorator = py.test.importorskip('decorator').decorator
def log(f, *k, **kw):
print('%s %s' % (k, kw))
f(*k, **kw)
log = decorator(log)
def fail():
raise ValueError('')
fail = log(log(fail))
excinfo = py.test.raises(ValueError, fail)
assert excinfo.traceback.recursionindex() is None
def test_traceback_getcrashentry(self):
def i():
__tracebackhide__ = True
raise ValueError
def h():
i()
def g():
__tracebackhide__ = True
h()
def f():
g()
excinfo = py.test.raises(ValueError, f)
tb = excinfo.traceback
entry = tb.getcrashentry()
co = py.code.Code(h)
assert entry.frame.code.path == co.path
assert entry.lineno == co.firstlineno + 1
assert entry.frame.code.name == 'h'
def test_traceback_getcrashentry_empty(self):
def g():
__tracebackhide__ = True
raise ValueError
def f():
__tracebackhide__ = True
g()
excinfo = py.test.raises(ValueError, f)
tb = excinfo.traceback
entry = tb.getcrashentry()
co = py.code.Code(g)
assert entry.frame.code.path == co.path
assert entry.lineno == co.firstlineno + 2
assert entry.frame.code.name == 'g'
def hello(x):
x + 5
def test_tbentry_reinterpret():
try:
hello("hello")
except TypeError:
excinfo = py.code.ExceptionInfo()
tbentry = excinfo.traceback[-1]
msg = tbentry.reinterpret()
assert msg.startswith("TypeError: ('hello' + 5)")
def test_excinfo_exconly():
excinfo = py.test.raises(ValueError, h)
assert excinfo.exconly().startswith('ValueError')
excinfo = py.test.raises(ValueError,
"raise ValueError('hello\\nworld')")
msg = excinfo.exconly(tryshort=True)
assert msg.startswith('ValueError')
assert msg.endswith("world")
def test_excinfo_repr():
excinfo = py.test.raises(ValueError, h)
s = repr(excinfo)
assert s == ""
def test_excinfo_str():
excinfo = py.test.raises(ValueError, h)
s = str(excinfo)
assert s.startswith(__file__[:-9]) # pyc file and $py.class
assert s.endswith("ValueError")
assert len(s.split(":")) >= 3 # on windows it's 4
def test_excinfo_errisinstance():
excinfo = py.test.raises(ValueError, h)
assert excinfo.errisinstance(ValueError)
def test_excinfo_no_sourcecode():
try:
exec ("raise ValueError()")
except ValueError:
excinfo = py.code.ExceptionInfo()
s = str(excinfo.traceback[-1])
assert s == " File '':1 in \n ???\n"
def test_excinfo_no_python_sourcecode(tmpdir):
#XXX: simplified locally testable version
tmpdir.join('test.txt').write("{{ h()}}:")
jinja2 = py.test.importorskip('jinja2')
loader = jinja2.FileSystemLoader(str(tmpdir))
env = jinja2.Environment(loader=loader)
template = env.get_template('test.txt')
excinfo = py.test.raises(ValueError,
template.render, h=h)
for item in excinfo.traceback:
print(item) # XXX: for some reason jinja.Template.render is printed in full
item.source # shouldnt fail
if item.path.basename == 'test.txt':
assert str(item.source) == '{{ h()}}:'
def test_entrysource_Queue_example():
try:
queue.Queue().get(timeout=0.001)
except queue.Empty:
excinfo = py.code.ExceptionInfo()
entry = excinfo.traceback[-1]
source = entry.getsource()
assert source is not None
s = str(source).strip()
assert s.startswith("def get")
def test_codepath_Queue_example():
try:
queue.Queue().get(timeout=0.001)
except queue.Empty:
excinfo = py.code.ExceptionInfo()
entry = excinfo.traceback[-1]
path = entry.path
assert isinstance(path, py.path.local)
assert path.basename.lower() == "queue.py"
assert path.check()
class TestFormattedExcinfo:
def pytest_funcarg__importasmod(self, request):
def importasmod(source):
source = py.code.Source(source)
tmpdir = request.getfuncargvalue("tmpdir")
modpath = tmpdir.join("mod.py")
tmpdir.ensure("__init__.py")
modpath.write(source)
if invalidate_import_caches is not None:
invalidate_import_caches()
return modpath.pyimport()
return importasmod
def excinfo_from_exec(self, source):
source = py.code.Source(source).strip()
try:
exec (source.compile())
except KeyboardInterrupt:
raise
except:
return py.code.ExceptionInfo()
assert 0, "did not raise"
def test_repr_source(self):
pr = FormattedExcinfo()
source = py.code.Source("""
def f(x):
pass
""").strip()
pr.flow_marker = "|"
lines = pr.get_source(source, 0)
assert len(lines) == 2
assert lines[0] == "| def f(x):"
assert lines[1] == " pass"
@broken_on_modern_pytest
def test_repr_source_excinfo(self):
""" check if indentation is right """
pr = FormattedExcinfo()
excinfo = self.excinfo_from_exec("""
def f():
assert 0
f()
""")
pr = FormattedExcinfo()
source = pr._getentrysource(excinfo.traceback[-1])
lines = pr.get_source(source, 1, excinfo)
assert lines == [
' def f():',
'> assert 0',
'E assert 0'
]
def test_repr_source_not_existing(self):
pr = FormattedExcinfo()
co = compile("raise ValueError()", "", "exec")
try:
exec (co)
except ValueError:
excinfo = py.code.ExceptionInfo()
repr = pr.repr_excinfo(excinfo)
assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
def test_repr_many_line_source_not_existing(self):
pr = FormattedExcinfo()
co = compile("""
a = 1
raise ValueError()
""", "", "exec")
try:
exec (co)
except ValueError:
excinfo = py.code.ExceptionInfo()
repr = pr.repr_excinfo(excinfo)
assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
def test_repr_source_failing_fullsource(self):
pr = FormattedExcinfo()
class FakeCode(object):
class raw:
co_filename = '?'
path = '?'
firstlineno = 5
def fullsource(self):
return None
fullsource = property(fullsource)
class FakeFrame(object):
code = FakeCode()
f_locals = {}
f_globals = {}
class FakeTracebackEntry(py.code.Traceback.Entry):
def __init__(self, tb):
self.lineno = 5+3
@property
def frame(self):
return FakeFrame()
class Traceback(py.code.Traceback):
Entry = FakeTracebackEntry
class FakeExcinfo(py.code.ExceptionInfo):
typename = "Foo"
def __init__(self):
pass
def exconly(self, tryshort):
return "EXC"
def errisinstance(self, cls):
return False
excinfo = FakeExcinfo()
class FakeRawTB(object):
tb_next = None
tb = FakeRawTB()
excinfo.traceback = Traceback(tb)
fail = IOError()
repr = pr.repr_excinfo(excinfo)
assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
fail = py.error.ENOENT
repr = pr.repr_excinfo(excinfo)
assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
def test_repr_local(self):
p = FormattedExcinfo(showlocals=True)
loc = {'y': 5, 'z': 7, 'x': 3, '@x': 2, '__builtins__': {}}
reprlocals = p.repr_locals(loc)
assert reprlocals.lines
assert reprlocals.lines[0] == '__builtins__ = '
assert reprlocals.lines[1] == 'x = 3'
assert reprlocals.lines[2] == 'y = 5'
assert reprlocals.lines[3] == 'z = 7'
def test_repr_tracebackentry_lines(self, importasmod):
mod = importasmod("""
def func1():
raise ValueError("hello\\nworld")
""")
excinfo = py.test.raises(ValueError, mod.func1)
excinfo.traceback = excinfo.traceback.filter()
p = FormattedExcinfo()
reprtb = p.repr_traceback_entry(excinfo.traceback[-1])
# test as intermittent entry
lines = reprtb.lines
assert lines[0] == ' def func1():'
assert lines[1] == '> raise ValueError("hello\\nworld")'
# test as last entry
p = FormattedExcinfo(showlocals=True)
repr_entry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
lines = repr_entry.lines
assert lines[0] == ' def func1():'
assert lines[1] == '> raise ValueError("hello\\nworld")'
assert lines[2] == 'E ValueError: hello'
assert lines[3] == 'E world'
assert not lines[4:]
loc = repr_entry.reprlocals is not None
loc = repr_entry.reprfileloc
assert loc.path == mod.__file__
assert loc.lineno == 3
#assert loc.message == "ValueError: hello"
def test_repr_tracebackentry_lines(self, importasmod):
mod = importasmod("""
def func1(m, x, y, z):
raise ValueError("hello\\nworld")
""")
excinfo = py.test.raises(ValueError, mod.func1, "m"*90, 5, 13, "z"*120)
excinfo.traceback = excinfo.traceback.filter()
entry = excinfo.traceback[-1]
p = FormattedExcinfo(funcargs=True)
reprfuncargs = p.repr_args(entry)
assert reprfuncargs.args[0] == ('m', repr("m"*90))
assert reprfuncargs.args[1] == ('x', '5')
assert reprfuncargs.args[2] == ('y', '13')
assert reprfuncargs.args[3] == ('z', repr("z" * 120))
p = FormattedExcinfo(funcargs=True)
repr_entry = p.repr_traceback_entry(entry)
assert repr_entry.reprfuncargs.args == reprfuncargs.args
tw = TWMock()
repr_entry.toterminal(tw)
assert tw.lines[0] == "m = " + repr('m' * 90)
assert tw.lines[1] == "x = 5, y = 13"
assert tw.lines[2] == "z = " + repr('z' * 120)
def test_repr_tracebackentry_lines_var_kw_args(self, importasmod):
mod = importasmod("""
def func1(x, *y, **z):
raise ValueError("hello\\nworld")
""")
excinfo = py.test.raises(ValueError, mod.func1, 'a', 'b', c='d')
excinfo.traceback = excinfo.traceback.filter()
entry = excinfo.traceback[-1]
p = FormattedExcinfo(funcargs=True)
reprfuncargs = p.repr_args(entry)
assert reprfuncargs.args[0] == ('x', repr('a'))
assert reprfuncargs.args[1] == ('y', repr(('b',)))
assert reprfuncargs.args[2] == ('z', repr({'c': 'd'}))
p = FormattedExcinfo(funcargs=True)
repr_entry = p.repr_traceback_entry(entry)
assert repr_entry.reprfuncargs.args == reprfuncargs.args
tw = TWMock()
repr_entry.toterminal(tw)
assert tw.lines[0] == "x = 'a', y = ('b',), z = {'c': 'd'}"
def test_repr_tracebackentry_short(self, importasmod):
mod = importasmod("""
def func1():
raise ValueError("hello")
def entry():
func1()
""")
excinfo = py.test.raises(ValueError, mod.entry)
p = FormattedExcinfo(style="short")
reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
lines = reprtb.lines
basename = py.path.local(mod.__file__).basename
assert lines[0] == ' func1()'
assert basename in str(reprtb.reprfileloc.path)
assert reprtb.reprfileloc.lineno == 5
# test last entry
p = FormattedExcinfo(style="short")
reprtb = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
lines = reprtb.lines
assert lines[0] == ' raise ValueError("hello")'
assert lines[1] == 'E ValueError: hello'
assert basename in str(reprtb.reprfileloc.path)
assert reprtb.reprfileloc.lineno == 3
def test_repr_tracebackentry_no(self, importasmod):
mod = importasmod("""
def func1():
raise ValueError("hello")
def entry():
func1()
""")
excinfo = py.test.raises(ValueError, mod.entry)
p = FormattedExcinfo(style="no")
p.repr_traceback_entry(excinfo.traceback[-2])
p = FormattedExcinfo(style="no")
reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
lines = reprentry.lines
assert lines[0] == 'E ValueError: hello'
assert not lines[1:]
def test_repr_traceback_tbfilter(self, importasmod):
mod = importasmod("""
def f(x):
raise ValueError(x)
def entry():
f(0)
""")
excinfo = py.test.raises(ValueError, mod.entry)
p = FormattedExcinfo(tbfilter=True)
reprtb = p.repr_traceback(excinfo)
assert len(reprtb.reprentries) == 2
p = FormattedExcinfo(tbfilter=False)
reprtb = p.repr_traceback(excinfo)
assert len(reprtb.reprentries) == 3
def test_traceback_short_no_source(self, importasmod, monkeypatch):
mod = importasmod("""
def func1():
raise ValueError("hello")
def entry():
func1()
""")
try:
mod.entry()
except ValueError:
excinfo = py.code.ExceptionInfo()
from py._code.code import Code
monkeypatch.setattr(Code, 'path', 'bogus')
excinfo.traceback[0].frame.code.path = "bogus"
p = FormattedExcinfo(style="short")
reprtb = p.repr_traceback_entry(excinfo.traceback[-2])
lines = reprtb.lines
last_p = FormattedExcinfo(style="short")
last_reprtb = last_p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
last_lines = last_reprtb.lines
monkeypatch.undo()
basename = py.path.local(mod.__file__).basename
assert lines[0] == ' func1()'
assert last_lines[0] == ' raise ValueError("hello")'
assert last_lines[1] == 'E ValueError: hello'
def test_repr_traceback_and_excinfo(self, importasmod):
mod = importasmod("""
def f(x):
raise ValueError(x)
def entry():
f(0)
""")
excinfo = py.test.raises(ValueError, mod.entry)
for style in ("long", "short"):
p = FormattedExcinfo(style=style)
reprtb = p.repr_traceback(excinfo)
assert len(reprtb.reprentries) == 2
assert reprtb.style == style
assert not reprtb.extraline
repr = p.repr_excinfo(excinfo)
assert repr.reprtraceback
assert len(repr.reprtraceback.reprentries) == len(reprtb.reprentries)
assert repr.reprcrash.path.endswith("mod.py")
assert repr.reprcrash.message == "ValueError: 0"
def test_repr_traceback_with_invalid_cwd(self, importasmod, monkeypatch):
mod = importasmod("""
def f(x):
raise ValueError(x)
def entry():
f(0)
""")
excinfo = py.test.raises(ValueError, mod.entry)
p = FormattedExcinfo()
def raiseos():
raise OSError(2)
monkeypatch.setattr('os.getcwd', raiseos)
assert p._makepath(__file__) == __file__
reprtb = p.repr_traceback(excinfo)
@broken_on_modern_pytest
def test_repr_excinfo_addouterr(self, importasmod):
mod = importasmod("""
def entry():
raise ValueError()
""")
excinfo = py.test.raises(ValueError, mod.entry)
repr = excinfo.getrepr()
repr.addsection("title", "content")
twmock = TWMock()
repr.toterminal(twmock)
assert twmock.lines[-1] == "content"
assert twmock.lines[-2] == ("-", "title")
def test_repr_excinfo_reprcrash(self, importasmod):
mod = importasmod("""
def entry():
raise ValueError()
""")
excinfo = py.test.raises(ValueError, mod.entry)
repr = excinfo.getrepr()
assert repr.reprcrash.path.endswith("mod.py")
assert repr.reprcrash.lineno == 3
assert repr.reprcrash.message == "ValueError"
assert str(repr.reprcrash).endswith("mod.py:3: ValueError")
def test_repr_traceback_recursion(self, importasmod):
mod = importasmod("""
def rec2(x):
return rec1(x+1)
def rec1(x):
return rec2(x-1)
def entry():
rec1(42)
""")
excinfo = py.test.raises(RuntimeError, mod.entry)
for style in ("short", "long", "no"):
p = FormattedExcinfo(style="short")
reprtb = p.repr_traceback(excinfo)
assert reprtb.extraline == "!!! Recursion detected (same locals & position)"
assert str(reprtb)
@broken_on_modern_pytest
def test_tb_entry_AssertionError(self, importasmod):
# probably this test is a bit redundant
# as py/magic/testing/test_assertion.py
# already tests correctness of
# assertion-reinterpretation logic
mod = importasmod("""
def somefunc():
x = 1
assert x == 2
""")
excinfo = py.test.raises(AssertionError, mod.somefunc)
p = FormattedExcinfo()
reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
lines = reprentry.lines
assert lines[-1] == "E assert 1 == 2"
def test_reprexcinfo_getrepr(self, importasmod):
mod = importasmod("""
def f(x):
raise ValueError(x)
def entry():
f(0)
""")
try:
mod.entry()
except ValueError:
excinfo = py.code.ExceptionInfo()
for style in ("short", "long", "no"):
for showlocals in (True, False):
repr = excinfo.getrepr(style=style, showlocals=showlocals)
assert isinstance(repr, ReprExceptionInfo)
assert repr.reprtraceback.style == style
def test_reprexcinfo_unicode(self):
from py._code.code import TerminalRepr
class MyRepr(TerminalRepr):
def toterminal(self, tw):
tw.line(py.builtin._totext("Ñ", "utf-8"))
x = py.builtin._totext(MyRepr())
assert x == py.builtin._totext("Ñ", "utf-8")
@broken_on_modern_pytest
def test_toterminal_long(self, importasmod):
mod = importasmod("""
def g(x):
raise ValueError(x)
def f():
g(3)
""")
excinfo = py.test.raises(ValueError, mod.f)
excinfo.traceback = excinfo.traceback.filter()
repr = excinfo.getrepr()
tw = TWMock()
repr.toterminal(tw)
assert tw.lines[0] == ""
tw.lines.pop(0)
assert tw.lines[0] == " def f():"
assert tw.lines[1] == "> g(3)"
assert tw.lines[2] == ""
assert tw.lines[3].endswith("mod.py:5: ")
assert tw.lines[4] == ("_ ", None)
assert tw.lines[5] == ""
assert tw.lines[6] == " def g(x):"
assert tw.lines[7] == "> raise ValueError(x)"
assert tw.lines[8] == "E ValueError: 3"
assert tw.lines[9] == ""
assert tw.lines[10].endswith("mod.py:3: ValueError")
@broken_on_modern_pytest
def test_toterminal_long_missing_source(self, importasmod, tmpdir):
mod = importasmod("""
def g(x):
raise ValueError(x)
def f():
g(3)
""")
excinfo = py.test.raises(ValueError, mod.f)
tmpdir.join('mod.py').remove()
excinfo.traceback = excinfo.traceback.filter()
repr = excinfo.getrepr()
tw = TWMock()
repr.toterminal(tw)
assert tw.lines[0] == ""
tw.lines.pop(0)
assert tw.lines[0] == "> ???"
assert tw.lines[1] == ""
assert tw.lines[2].endswith("mod.py:5: ")
assert tw.lines[3] == ("_ ", None)
assert tw.lines[4] == ""
assert tw.lines[5] == "> ???"
assert tw.lines[6] == "E ValueError: 3"
assert tw.lines[7] == ""
assert tw.lines[8].endswith("mod.py:3: ValueError")
@broken_on_modern_pytest
def test_toterminal_long_incomplete_source(self, importasmod, tmpdir):
mod = importasmod("""
def g(x):
raise ValueError(x)
def f():
g(3)
""")
excinfo = py.test.raises(ValueError, mod.f)
tmpdir.join('mod.py').write('asdf')
excinfo.traceback = excinfo.traceback.filter()
repr = excinfo.getrepr()
tw = TWMock()
repr.toterminal(tw)
assert tw.lines[0] == ""
tw.lines.pop(0)
assert tw.lines[0] == "> ???"
assert tw.lines[1] == ""
assert tw.lines[2].endswith("mod.py:5: ")
assert tw.lines[3] == ("_ ", None)
assert tw.lines[4] == ""
assert tw.lines[5] == "> ???"
assert tw.lines[6] == "E ValueError: 3"
assert tw.lines[7] == ""
assert tw.lines[8].endswith("mod.py:3: ValueError")
@broken_on_modern_pytest
def test_toterminal_long_filenames(self, importasmod):
mod = importasmod("""
def f():
raise ValueError()
""")
excinfo = py.test.raises(ValueError, mod.f)
tw = TWMock()
path = py.path.local(mod.__file__)
old = path.dirpath().chdir()
try:
repr = excinfo.getrepr(abspath=False)
repr.toterminal(tw)
line = tw.lines[-1]
x = py.path.local().bestrelpath(path)
if len(x) < len(str(path)):
assert line == "mod.py:3: ValueError"
repr = excinfo.getrepr(abspath=True)
repr.toterminal(tw)
line = tw.lines[-1]
assert line == "%s:3: ValueError" %(path,)
finally:
old.chdir()
@pytest.mark.parametrize('style', ("long", "short", "no"))
@pytest.mark.parametrize('showlocals', (True, False),
ids=['locals', 'nolocals'])
@pytest.mark.parametrize('tbfilter', (True, False),
ids=['tbfilter', 'nofilter'])
@pytest.mark.parametrize('funcargs', (True, False),
ids=['funcargs', 'nofuncargs'])
def test_format_excinfo(self, importasmod,
style, showlocals, tbfilter, funcargs):
mod = importasmod("""
def g(x):
raise ValueError(x)
def f():
g(3)
""")
excinfo = py.test.raises(ValueError, mod.f)
tw = py.io.TerminalWriter(stringio=True)
repr = excinfo.getrepr(
style=style,
showlocals=showlocals,
funcargs=funcargs,
tbfilter=tbfilter
)
repr.toterminal(tw)
assert tw.stringio.getvalue()
@broken_on_modern_pytest
def test_native_style(self):
excinfo = self.excinfo_from_exec("""
assert 0
""")
repr = excinfo.getrepr(style='native')
assert "assert 0" in str(repr.reprcrash)
s = str(repr)
assert s.startswith('Traceback (most recent call last):\n File')
assert s.endswith('\nAssertionError: assert 0')
assert 'exec (source.compile())' in s
assert s.count('assert 0') == 2
@broken_on_modern_pytest
def test_traceback_repr_style(self, importasmod):
mod = importasmod("""
def f():
g()
def g():
h()
def h():
i()
def i():
raise ValueError()
""")
excinfo = py.test.raises(ValueError, mod.f)
excinfo.traceback = excinfo.traceback.filter()
excinfo.traceback[1].set_repr_style("short")
excinfo.traceback[2].set_repr_style("short")
r = excinfo.getrepr(style="long")
tw = TWMock()
r.toterminal(tw)
for line in tw.lines: print (line)
assert tw.lines[0] == ""
assert tw.lines[1] == " def f():"
assert tw.lines[2] == "> g()"
assert tw.lines[3] == ""
assert tw.lines[4].endswith("mod.py:3: ")
assert tw.lines[5] == ("_ ", None)
assert tw.lines[6].endswith("in g")
assert tw.lines[7] == " h()"
assert tw.lines[8].endswith("in h")
assert tw.lines[9] == " i()"
assert tw.lines[10] == ("_ ", None)
assert tw.lines[11] == ""
assert tw.lines[12] == " def i():"
assert tw.lines[13] == "> raise ValueError()"
assert tw.lines[14] == "E ValueError"
assert tw.lines[15] == ""
assert tw.lines[16].endswith("mod.py:9: ValueError")
py-1.5.2/testing/code/test_source.py 0000664 0001750 0001750 00000041276 13202663755 020622 0 ustar vagrant vagrant 0000000 0000000 from py.code import Source
import py
import sys
import inspect
from py._code.source import _ast
if _ast is not None:
astonly = py.test.mark.nothing
else:
astonly = py.test.mark.xfail("True", reason="only works with AST-compile")
failsonjython = py.test.mark.xfail("sys.platform.startswith('java')")
def test_source_str_function():
x = Source("3")
assert str(x) == "3"
x = Source(" 3")
assert str(x) == "3"
x = Source("""
3
""", rstrip=False)
assert str(x) == "\n3\n "
x = Source("""
3
""", rstrip=True)
assert str(x) == "\n3"
def test_unicode():
try:
unicode
except NameError:
return
x = Source(unicode("4"))
assert str(x) == "4"
co = py.code.compile(unicode('u"\xc3\xa5"', 'utf8'), mode='eval')
val = eval(co)
assert isinstance(val, unicode)
def test_source_from_function():
source = py.code.Source(test_source_str_function)
assert str(source).startswith('def test_source_str_function():')
def test_source_from_method():
class TestClass:
def test_method(self):
pass
source = py.code.Source(TestClass().test_method)
assert source.lines == ["def test_method(self):",
" pass"]
def test_source_from_lines():
lines = ["a \n", "b\n", "c"]
source = py.code.Source(lines)
assert source.lines == ['a ', 'b', 'c']
def test_source_from_inner_function():
def f():
pass
source = py.code.Source(f, deindent=False)
assert str(source).startswith(' def f():')
source = py.code.Source(f)
assert str(source).startswith('def f():')
def test_source_putaround_simple():
source = Source("raise ValueError")
source = source.putaround(
"try:", """\
except ValueError:
x = 42
else:
x = 23""")
assert str(source)=="""\
try:
raise ValueError
except ValueError:
x = 42
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, 'xyz xyz')
assert ex.value.lineno == 1
assert ex.value.offset in (4,7) # XXX pypy/jython versus cpython?
assert ex.value.text.strip(), 'x x'
def test_isparseable():
assert Source("hello").isparseable()
assert Source("if 1:\n pass").isparseable()
assert Source(" \nif 1:\n pass").isparseable()
assert not Source("if 1:\n").isparseable()
assert not Source(" \nif 1:\npass").isparseable()
assert not Source(chr(0)).isparseable()
class TestAccesses:
source = Source("""\
def f(x):
pass
def g(x):
pass
""")
def test_getrange(self):
x = self.source[0:2]
assert x.isparseable()
assert len(x.lines) == 2
assert str(x) == "def f(x):\n pass"
def test_getline(self):
x = self.source[0]
assert x == "def f(x):"
def test_len(self):
assert len(self.source) == 4
def test_iter(self):
l = [x for x in self.source]
assert len(l) == 4
class TestSourceParsingAndCompiling:
source = Source("""\
def f(x):
assert (x ==
3 +
4)
""").strip()
def test_compile(self):
co = py.code.compile("x=3")
d = {}
exec (co, d)
assert d['x'] == 3
def test_compile_and_getsource_simple(self):
co = py.code.compile("x=3")
exec (co)
source = py.code.Source(co)
assert str(source) == "x=3"
def test_compile_and_getsource_through_same_function(self):
def gensource(source):
return py.code.compile(source)
co1 = gensource("""
def f():
raise KeyError()
""")
co2 = gensource("""
def f():
raise ValueError()
""")
source1 = inspect.getsource(co1)
assert 'KeyError' in source1
source2 = inspect.getsource(co2)
assert 'ValueError' in source2
def test_getstatement(self):
#print str(self.source)
ass = str(self.source[1:])
for i in range(1, 4):
#print "trying start in line %r" % self.source[i]
s = self.source.getstatement(i)
#x = s.deindent()
assert str(s) == ass
def test_getstatementrange_triple_quoted(self):
#print str(self.source)
source = Source("""hello('''
''')""")
s = source.getstatement(0)
assert s == str(source)
s = source.getstatement(1)
assert s == str(source)
@astonly
def test_getstatementrange_within_constructs(self):
source = Source("""\
try:
try:
raise ValueError
except SomeThing:
pass
finally:
42
""")
assert len(source) == 7
# check all lineno's that could occur in a traceback
#assert source.getstatementrange(0) == (0, 7)
#assert source.getstatementrange(1) == (1, 5)
assert source.getstatementrange(2) == (2, 3)
assert source.getstatementrange(3) == (3, 4)
assert source.getstatementrange(4) == (4, 5)
#assert source.getstatementrange(5) == (0, 7)
assert source.getstatementrange(6) == (6, 7)
def test_getstatementrange_bug(self):
source = Source("""\
try:
x = (
y +
z)
except:
pass
""")
assert len(source) == 6
assert source.getstatementrange(2) == (1, 4)
def test_getstatementrange_bug2(self):
source = Source("""\
assert (
33
==
[
X(3,
b=1, c=2
),
]
)
""")
assert len(source) == 9
assert source.getstatementrange(5) == (0, 9)
def test_getstatementrange_ast_issue58(self):
source = Source("""\
def test_some():
for a in [a for a in
CAUSE_ERROR]: pass
x = 3
""")
assert getstatement(2, source).lines == source.lines[2:3]
assert getstatement(3, source).lines == source.lines[3:4]
def test_getstatementrange_out_of_bounds_py3(self):
source = Source("if xxx:\n from .collections import something")
r = source.getstatementrange(1)
assert r == (1,2)
def test_getstatementrange_with_syntaxerror_issue7(self):
source = Source(":")
py.test.raises(SyntaxError, lambda: source.getstatementrange(0))
def test_compile_to_ast(self):
import ast
source = Source("x = 4")
mod = source.compile(flag=ast.PyCF_ONLY_AST)
assert isinstance(mod, ast.Module)
compile(mod, "", "exec")
def test_compile_and_getsource(self):
co = self.source.compile()
py.builtin.exec_(co, globals())
f(7)
excinfo = py.test.raises(AssertionError, "f(6)")
frame = excinfo.traceback[-1].frame
stmt = frame.code.fullsource.getstatement(frame.lineno)
#print "block", str(block)
assert str(stmt).strip().startswith('assert')
def test_compilefuncs_and_path_sanity(self):
def check(comp, name):
co = comp(self.source, name)
if not name:
expected = "codegen %s:%d>" %(mypath, mylineno+2+1)
else:
expected = "codegen %r %s:%d>" % (name, mypath, mylineno+2+1)
fn = co.co_filename
assert fn.endswith(expected)
mycode = py.code.Code(self.test_compilefuncs_and_path_sanity)
mylineno = mycode.firstlineno
mypath = mycode.path
for comp in py.code.compile, py.code.Source.compile:
for name in '', None, 'my':
yield check, comp, name
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):
frame = sys._getframe(1)
self.source = py.code.Frame(frame).statement
x = A('x', 'y')
l = [i for i in x.source.lines if i.strip()]
assert len(l) == 1
def test_getstartingblock_multiline():
class A:
def __init__(self, *args):
frame = sys._getframe(1)
self.source = py.code.Frame(frame).statement
x = A('x',
'y' \
,
'z')
l = [i for i in x.source.lines if i.strip()]
assert len(l) == 4
def test_getline_finally():
def c(): pass
excinfo = py.test.raises(TypeError, """
teardown = None
try:
c(1)
finally:
if teardown:
teardown()
""")
source = excinfo.traceback[-1].statement
assert str(source).strip() == 'c(1)'
def test_getfuncsource_dynamic():
source = """
def f():
raise ValueError
def g(): pass
"""
co = py.code.compile(source)
py.builtin.exec_(co, globals())
assert str(py.code.Source(f)).strip() == 'def f():\n raise ValueError'
assert str(py.code.Source(g)).strip() == 'def g(): pass'
def test_getfuncsource_with_multine_string():
def f():
c = '''while True:
pass
'''
assert str(py.code.Source(f)).strip() == "def f():\n c = '''while True:\n pass\n'''"
def test_deindent():
from py._code.source import deindent as deindent
assert deindent(['\tfoo', '\tbar', ]) == ['foo', 'bar']
def f():
c = '''while True:
pass
'''
import inspect
lines = deindent(inspect.getsource(f).splitlines())
assert lines == ["def f():", " c = '''while True:", " pass", "'''"]
source = """
def f():
def g():
pass
"""
lines = deindent(source.splitlines())
assert lines == ['', 'def f():', ' def g():', ' pass', ' ']
def test_source_of_class_at_eof_without_newline(tmpdir):
# this test fails because the implicit inspect.getsource(A) below
# does not return the "x = 1" last line.
source = py.code.Source('''
class A(object):
def method(self):
x = 1
''')
path = tmpdir.join("a.py")
path.write(source)
s2 = py.code.Source(tmpdir.join("a.py").pyimport().A)
assert str(source).strip() == str(s2).strip()
if True:
def x():
pass
def test_getsource_fallback():
from py._code.source import getsource
expected = """def x():
pass"""
src = getsource(x)
assert src == expected
def test_idem_compile_and_getsource():
from py._code.source import getsource
expected = "def x(): pass"
co = py.code.compile(expected)
src = getsource(co)
assert src == expected
def test_findsource_fallback():
from py._code.source import findsource
src, lineno = findsource(x)
assert 'test_findsource_simple' in str(src)
assert src[lineno] == ' def x():'
def test_findsource():
from py._code.source import findsource
co = py.code.compile("""if 1:
def x():
pass
""")
src, lineno = findsource(co)
assert 'if 1:' in str(src)
d = {}
eval(co, d)
src, lineno = findsource(d['x'])
assert 'if 1:' in str(src)
assert src[lineno] == " def x():"
def test_getfslineno():
from py.code import getfslineno
def f(x):
pass
fspath, lineno = getfslineno(f)
assert fspath.basename == "test_source.py"
assert lineno == py.code.getrawcode(f).co_firstlineno-1 # see findsource
class A(object):
pass
fspath, lineno = getfslineno(A)
_, A_lineno = inspect.findsource(A)
assert fspath.basename == "test_source.py"
assert lineno == A_lineno
assert getfslineno(3) == ("", -1)
class B:
pass
B.__name__ = "B2"
assert getfslineno(B)[1] == -1
def test_code_of_object_instance_with_call():
class A:
pass
py.test.raises(TypeError, lambda: py.code.Source(A()))
class WithCall:
def __call__(self):
pass
code = py.code.Code(WithCall())
assert 'pass' in str(code.source())
class Hello(object):
def __call__(self):
pass
py.test.raises(TypeError, lambda: py.code.Code(Hello))
def getstatement(lineno, source):
from py._code.source import getstatementrange_ast
source = py.code.Source(source, deindent=False)
ast, start, end = getstatementrange_ast(lineno, source)
return source[start:end]
def test_oneline():
source = getstatement(0, "raise ValueError")
assert str(source) == "raise ValueError"
def test_comment_and_no_newline_at_end():
from py._code.source import getstatementrange_ast
source = Source(['def test_basic_complex():',
' assert 1 == 2',
'# vim: filetype=pyopencl:fdm=marker'])
ast, start, end = getstatementrange_ast(1, source)
assert end == 2
def test_oneline_and_comment():
source = getstatement(0, "raise ValueError\n#hello")
assert str(source) == "raise ValueError"
def test_comments():
source = '''def test():
"comment 1"
x = 1
# comment 2
# comment 3
assert False
"""
comment 4
"""
'''
for line in range(2,6):
assert str(getstatement(line, source)) == ' x = 1'
for line in range(6,10):
assert str(getstatement(line, source)) == ' assert False'
assert str(getstatement(10, source)) == '"""'
def test_comment_in_statement():
source = '''test(foo=1,
# comment 1
bar=2)
'''
for line in range(1,3):
assert str(getstatement(line, source)) == \
'test(foo=1,\n # comment 1\n bar=2)'
def test_single_line_else():
source = getstatement(1, "if False: 2\nelse: 3")
assert str(source) == "else: 3"
def test_single_line_finally():
source = getstatement(1, "try: 1\nfinally: 3")
assert str(source) == "finally: 3"
def test_issue55():
source = ('def round_trip(dinp):\n assert 1 == dinp\n'
'def test_rt():\n round_trip("""\n""")\n')
s = getstatement(3, source)
assert str(s) == ' round_trip("""\n""")'
def XXXtest_multiline():
source = getstatement(0, """\
raise ValueError(
23
)
x = 3
""")
assert str(source) == "raise ValueError(\n 23\n)"
class TestTry:
pytestmark = astonly
source = """\
try:
raise ValueError
except Something:
raise IndexError(1)
else:
raise KeyError()
"""
def test_body(self):
source = getstatement(1, self.source)
assert str(source) == " raise ValueError"
def test_except_line(self):
source = getstatement(2, self.source)
assert str(source) == "except Something:"
def test_except_body(self):
source = getstatement(3, self.source)
assert str(source) == " raise IndexError(1)"
def test_else(self):
source = getstatement(5, self.source)
assert str(source) == " raise KeyError()"
class TestTryFinally:
source = """\
try:
raise ValueError
finally:
raise IndexError(1)
"""
def test_body(self):
source = getstatement(1, self.source)
assert str(source) == " raise ValueError"
def test_finally(self):
source = getstatement(3, self.source)
assert str(source) == " raise IndexError(1)"
class TestIf:
pytestmark = astonly
source = """\
if 1:
y = 3
elif False:
y = 5
else:
y = 7
"""
def test_body(self):
source = getstatement(1, self.source)
assert str(source) == " y = 3"
def test_elif_clause(self):
source = getstatement(2, self.source)
assert str(source) == "elif False:"
def test_elif(self):
source = getstatement(3, self.source)
assert str(source) == " y = 5"
def test_else(self):
source = getstatement(5, self.source)
assert str(source) == " y = 7"
def test_semicolon():
s = """\
hello ; pytest.skip()
"""
source = getstatement(0, s)
assert str(source) == s.strip()
def test_def_online():
s = """\
def func(): raise ValueError(42)
def something():
pass
"""
source = getstatement(0, s)
assert str(source) == "def func(): raise ValueError(42)"
def XXX_test_expression_multiline():
source = """\
something
'''
'''"""
result = getstatement(1, source)
assert str(result) == "'''\n'''"
py-1.5.2/testing/process/ 0000775 0001750 0001750 00000000000 13203150701 016422 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/testing/process/test_killproc.py 0000664 0001750 0001750 00000001133 13202663755 021671 0 ustar vagrant vagrant 0000000 0000000 import pytest
import sys
import py
@pytest.mark.skipif("sys.platform.startswith('java')")
def test_kill(tmpdir):
subprocess = pytest.importorskip("subprocess")
t = tmpdir.join("t.py")
t.write("import time ; time.sleep(100)")
proc = subprocess.Popen([sys.executable, str(t)])
assert proc.poll() is None # no return value yet
py.process.kill(proc.pid)
ret = proc.wait()
if sys.platform == "win32" and ret == 0:
pytest.skip("XXX on win32, subprocess.Popen().wait() on a killed "
"process does not yield return value != 0")
assert ret != 0
py-1.5.2/testing/process/test_forkedfunc.py 0000664 0001750 0001750 00000011004 13202663755 022176 0 ustar vagrant vagrant 0000000 0000000 import pytest
import py, sys, os
pytestmark = py.test.mark.skipif("not hasattr(os, 'fork')")
def test_waitfinish_removes_tempdir():
ff = py.process.ForkedFunc(boxf1)
assert ff.tempdir.check()
ff.waitfinish()
assert not ff.tempdir.check()
def test_tempdir_gets_gc_collected(monkeypatch):
monkeypatch.setattr(os, 'fork', lambda: os.getpid())
ff = py.process.ForkedFunc(boxf1)
assert ff.tempdir.check()
ff.__del__()
assert not ff.tempdir.check()
def test_basic_forkedfunc():
result = py.process.ForkedFunc(boxf1).waitfinish()
assert result.out == "some out\n"
assert result.err == "some err\n"
assert result.exitstatus == 0
assert result.signal == 0
assert result.retval == 1
def test_exitstatus():
def func():
os._exit(4)
result = py.process.ForkedFunc(func).waitfinish()
assert result.exitstatus == 4
assert result.signal == 0
assert not result.out
assert not result.err
def test_execption_in_func():
def fun():
raise ValueError(42)
ff = py.process.ForkedFunc(fun)
result = ff.waitfinish()
assert result.exitstatus == ff.EXITSTATUS_EXCEPTION
assert result.err.find("ValueError: 42") != -1
assert result.signal == 0
assert not result.retval
def test_forkedfunc_on_fds():
result = py.process.ForkedFunc(boxf2).waitfinish()
assert result.out == "someout"
assert result.err == "someerr"
assert result.exitstatus == 0
assert result.signal == 0
assert result.retval == 2
def test_forkedfunc_on_fds_output():
result = py.process.ForkedFunc(boxf3).waitfinish()
assert result.signal == 11
assert result.out == "s"
def test_forkedfunc_on_stdout():
def boxf3():
import sys
sys.stdout.write("hello\n")
os.kill(os.getpid(), 11)
result = py.process.ForkedFunc(boxf3).waitfinish()
assert result.signal == 11
assert result.out == "hello\n"
def test_forkedfunc_signal():
result = py.process.ForkedFunc(boxseg).waitfinish()
assert result.retval is None
assert result.signal == 11
def test_forkedfunc_huge_data():
result = py.process.ForkedFunc(boxhuge).waitfinish()
assert result.out
assert result.exitstatus == 0
assert result.signal == 0
assert result.retval == 3
def test_box_seq():
# we run many boxes with huge data, just one after another
for i in range(50):
result = py.process.ForkedFunc(boxhuge).waitfinish()
assert result.out
assert result.exitstatus == 0
assert result.signal == 0
assert result.retval == 3
def test_box_in_a_box():
def boxfun():
result = py.process.ForkedFunc(boxf2).waitfinish()
print (result.out)
sys.stderr.write(result.err + "\n")
return result.retval
result = py.process.ForkedFunc(boxfun).waitfinish()
assert result.out == "someout\n"
assert result.err == "someerr\n"
assert result.exitstatus == 0
assert result.signal == 0
assert result.retval == 2
def test_kill_func_forked():
class A:
pass
info = A()
import time
def box_fun():
time.sleep(10) # we don't want to last forever here
ff = py.process.ForkedFunc(box_fun)
os.kill(ff.pid, 15)
result = ff.waitfinish()
assert result.signal == 15
def test_hooks(monkeypatch):
def _boxed():
return 1
def _on_start():
sys.stdout.write("some out\n")
sys.stdout.flush()
def _on_exit():
sys.stderr.write("some err\n")
sys.stderr.flush()
result = py.process.ForkedFunc(_boxed, child_on_start=_on_start,
child_on_exit=_on_exit).waitfinish()
assert result.out == "some out\n"
assert result.err == "some err\n"
assert result.exitstatus == 0
assert result.signal == 0
assert result.retval == 1
# ======================================================================
# examples
# ======================================================================
#
def boxf1():
sys.stdout.write("some out\n")
sys.stderr.write("some err\n")
return 1
def boxf2():
os.write(1, "someout".encode('ascii'))
os.write(2, "someerr".encode('ascii'))
return 2
def boxf3():
os.write(1, "s".encode('ascii'))
os.kill(os.getpid(), 11)
def boxseg():
os.kill(os.getpid(), 11)
def boxhuge():
s = " ".encode('ascii')
os.write(1, s * 10000)
os.write(2, s * 10000)
os.write(1, s * 10000)
os.write(1, s * 10000)
os.write(2, s * 10000)
os.write(2, s * 10000)
os.write(1, s * 10000)
return 3
py-1.5.2/testing/process/__init__.py 0000664 0001750 0001750 00000000002 13202663617 020541 0 ustar vagrant vagrant 0000000 0000000 #
py-1.5.2/testing/process/test_cmdexec.py 0000664 0001750 0001750 00000002157 13202663755 021471 0 ustar vagrant vagrant 0000000 0000000 import py
from py.process import cmdexec
def exvalue():
import sys
return sys.exc_info()[1]
class Test_exec_cmd:
def test_simple(self):
out = cmdexec('echo hallo')
assert out.strip() == 'hallo'
assert py.builtin._istext(out)
def test_simple_newline(self):
import sys
out = cmdexec(r"""%s -c "print ('hello')" """ % sys.executable)
assert out == 'hello\n'
assert py.builtin._istext(out)
def test_simple_error(self):
py.test.raises(cmdexec.Error, cmdexec, 'exit 1')
def test_simple_error_exact_status(self):
try:
cmdexec('exit 1')
except cmdexec.Error:
e = exvalue()
assert e.status == 1
assert py.builtin._istext(e.out)
assert py.builtin._istext(e.err)
def test_err(self):
try:
cmdexec('echoqweqwe123 hallo')
raise AssertionError("command succeeded but shouldn't")
except cmdexec.Error:
e = exvalue()
assert hasattr(e, 'err')
assert hasattr(e, 'out')
assert e.err or e.out
py-1.5.2/AUTHORS 0000664 0001750 0001750 00000001150 13202663617 014352 0 ustar vagrant vagrant 0000000 0000000 Holger Krekel, holger at merlinux eu
Benjamin Peterson, benjamin at python org
Ronny Pfannschmidt, Ronny.Pfannschmidt at gmx de
Guido Wesdorp, johnny at johnnydebris net
Samuele Pedroni, pedronis at openend se
Carl Friedrich Bolz, cfbolz at gmx de
Armin Rigo, arigo at tunes org
Maciek Fijalkowski, fijal at genesilico pl
Brian Dorsey, briandorsey at gmail com
Floris Bruynooghe, flub at devork be
merlinux GmbH, Germany, office at merlinux eu
Contributors include::
Ross Lawley
Ralf Schmitt
Chris Lamb
Harald Armin Massa
Martijn Faassen
Ian Bicking
Jan Balster
Grig Gheorghiu
Bob Ippolito
Christian Tismer
py-1.5.2/setup.py 0000664 0001750 0001750 00000004256 13203051015 015006 0 ustar vagrant vagrant 0000000 0000000 import os
import sys
from setuptools import setup, find_packages
def get_version():
p = os.path.join(os.path.dirname(
os.path.abspath(__file__)), "py", "__init__.py")
with open(p) as f:
for line in f.readlines():
if "__version__" in line:
return line.strip().split("=")[-1].strip(" '")
raise ValueError("could not read version")
def main():
setup(
name='py',
description='library with cross-python path, ini-parsing, io, code, log facilities',
long_description=open('README.rst').read(),
version=get_version(),
url='http://py.readthedocs.io/',
license='MIT license',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
author='holger krekel, Ronny Pfannschmidt, Benjamin Peterson and others',
author_email='pytest-dev@python.org',
classifiers=['Development Status :: 6 - Mature',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: POSIX',
'Operating System :: Microsoft :: Windows',
'Operating System :: MacOS :: MacOS X',
'Topic :: Software Development :: Testing',
'Topic :: Software Development :: Libraries',
'Topic :: Utilities',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
],
packages=find_packages(exclude=['tasks', 'testing']),
zip_safe=False,
)
if __name__ == '__main__':
main()
py-1.5.2/MANIFEST.in 0000664 0001750 0001750 00000000247 13202663617 015046 0 ustar vagrant vagrant 0000000 0000000 include CHANGELOG
include AUTHORS
include README.rst
include setup.py
include LICENSE
include conftest.py
include tox.ini
graft doc
graft testing
global-exclude *.pyc
py-1.5.2/README.rst 0000664 0001750 0001750 00000002426 13202663755 015003 0 ustar vagrant vagrant 0000000 0000000 .. image:: https://img.shields.io/pypi/v/py.svg
:target: https://pypi.org/project/py
.. image:: https://anaconda.org/conda-forge/py/badges/version.svg
:target: https://anaconda.org/conda-forge/py
.. image:: https://img.shields.io/pypi/pyversions/pytest.svg
:target: https://pypi.org/project/py
.. image:: https://img.shields.io/travis/pytest-dev/py.svg
:target: https://travis-ci.org/pytest-dev/py
.. image:: https://ci.appveyor.com/api/projects/status/10keglan6uqwj5al/branch/master?svg=true
:target: https://ci.appveyor.com/project/pytestbot/py
**NOTE**: this library is in **maintenance mode** and should not be used in new code.
The py lib is a Python development support library featuring
the following tools and modules:
* ``py.path``: uniform local and svn path objects
* ``py.apipkg``: explicit API control and lazy-importing
* ``py.iniconfig``: easy parsing of .ini files
* ``py.code``: dynamic code generation and introspection (deprecated, moved to ``pytest``).
**NOTE**: prior to the 1.4 release this distribution used to
contain py.test which is now its own package, see http://pytest.org
For questions and more information please visit http://py.readthedocs.org
Bugs and issues: https://github.com/pytest-dev/py
Authors: Holger Krekel and others, 2004-2017
py-1.5.2/LICENSE 0000664 0001750 0001750 00000002045 13202663617 014313 0 ustar vagrant vagrant 0000000 0000000
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
py-1.5.2/tox.ini 0000664 0001750 0001750 00000001657 13202663755 014634 0 ustar vagrant vagrant 0000000 0000000 [tox]
envlist=py{27,34,35,36}-pytest{29,30,31}
[testenv]
changedir=testing
commands=
pip install -U .. # hande the install order fallout since pytest depends on pip
py.test --confcutdir=.. -rfsxX --junitxml={envlogdir}/junit-{envname}.xml []
deps=
pytest29: pytest~=2.9.0
pytest30: pytest~=3.0.0
pytest31: pytest~=3.1.0
[testenv:py27-xdist]
basepython=python2.7
deps=
pytest~=2.9.0
pytest-xdist<=1.16.0
commands=
pip install -U .. # hande the install order fallout since pytest depends on pip
py.test -n3 -rfsxX --confcutdir=.. --runslowtests \
--junitxml={envlogdir}/junit-{envname}.xml []
[testenv:jython]
changedir=testing
commands=
{envpython} -m pip install -U .. # hande the install order fallout since pytest depends on pip
{envpython} -m pytest --confcutdir=.. -rfsxX --junitxml={envlogdir}/junit-{envname}0.xml {posargs:io_ code}
[pytest]
rsyncdirs = conftest.py py doc testing
addopts = -ra
py-1.5.2/PKG-INFO 0000664 0001750 0001750 00000005605 13203150701 014372 0 ustar vagrant vagrant 0000000 0000000 Metadata-Version: 1.2
Name: py
Version: 1.5.2
Summary: library with cross-python path, ini-parsing, io, code, log facilities
Home-page: http://py.readthedocs.io/
Author: holger krekel, Ronny Pfannschmidt, Benjamin Peterson and others
Author-email: pytest-dev@python.org
License: MIT license
Description-Content-Type: UNKNOWN
Description: .. image:: https://img.shields.io/pypi/v/py.svg
:target: https://pypi.org/project/py
.. image:: https://anaconda.org/conda-forge/py/badges/version.svg
:target: https://anaconda.org/conda-forge/py
.. image:: https://img.shields.io/pypi/pyversions/pytest.svg
:target: https://pypi.org/project/py
.. image:: https://img.shields.io/travis/pytest-dev/py.svg
:target: https://travis-ci.org/pytest-dev/py
.. image:: https://ci.appveyor.com/api/projects/status/10keglan6uqwj5al/branch/master?svg=true
:target: https://ci.appveyor.com/project/pytestbot/py
**NOTE**: this library is in **maintenance mode** and should not be used in new code.
The py lib is a Python development support library featuring
the following tools and modules:
* ``py.path``: uniform local and svn path objects
* ``py.apipkg``: explicit API control and lazy-importing
* ``py.iniconfig``: easy parsing of .ini files
* ``py.code``: dynamic code generation and introspection (deprecated, moved to ``pytest``).
**NOTE**: prior to the 1.4 release this distribution used to
contain py.test which is now its own package, see http://pytest.org
For questions and more information please visit http://py.readthedocs.org
Bugs and issues: https://github.com/pytest-dev/py
Authors: Holger Krekel and others, 2004-2017
Platform: unix
Platform: linux
Platform: osx
Platform: cygwin
Platform: win32
Classifier: Development Status :: 6 - Mature
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Utilities
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
py-1.5.2/py.egg-info/ 0000775 0001750 0001750 00000000000 13203150701 015411 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/py.egg-info/top_level.txt 0000664 0001750 0001750 00000000003 13203150701 020134 0 ustar vagrant vagrant 0000000 0000000 py
py-1.5.2/py.egg-info/SOURCES.txt 0000664 0001750 0001750 00000005103 13203150701 017274 0 ustar vagrant vagrant 0000000 0000000 AUTHORS
CHANGELOG
LICENSE
MANIFEST.in
README.rst
conftest.py
setup.cfg
setup.py
tox.ini
doc/Makefile
doc/changelog.txt
doc/code.txt
doc/conf.py
doc/download.html
doc/faq.txt
doc/index.txt
doc/install.txt
doc/io.txt
doc/links.inc
doc/log.txt
doc/misc.txt
doc/path.txt
doc/style.css
doc/xml.txt
doc/_templates/layout.html
doc/announce/release-0.9.0.txt
doc/announce/release-0.9.2.txt
doc/announce/release-1.0.0.txt
doc/announce/release-1.0.1.txt
doc/announce/release-1.0.2.txt
doc/announce/release-1.1.0.txt
doc/announce/release-1.1.1.txt
doc/announce/release-1.2.0.txt
doc/announce/release-1.2.1.txt
doc/announce/release-1.3.0.txt
doc/announce/release-1.3.1.txt
doc/announce/release-1.3.2.txt
doc/announce/release-1.3.3.txt
doc/announce/release-1.3.4.txt
doc/announce/release-1.4.0.txt
doc/announce/release-1.4.1.txt
doc/announce/releases.txt
doc/example/genhtml.py
doc/example/genhtmlcss.py
doc/example/genxml.py
doc/img/pylib.png
py/__init__.py
py/__metainfo.py
py/_builtin.py
py/_error.py
py/_std.py
py/_xmlgen.py
py/test.py
py.egg-info/PKG-INFO
py.egg-info/SOURCES.txt
py.egg-info/dependency_links.txt
py.egg-info/not-zip-safe
py.egg-info/top_level.txt
py/_code/__init__.py
py/_code/_assertionnew.py
py/_code/_assertionold.py
py/_code/_py2traceback.py
py/_code/assertion.py
py/_code/code.py
py/_code/source.py
py/_io/__init__.py
py/_io/capture.py
py/_io/saferepr.py
py/_io/terminalwriter.py
py/_log/__init__.py
py/_log/log.py
py/_log/warning.py
py/_path/__init__.py
py/_path/cacheutil.py
py/_path/common.py
py/_path/local.py
py/_path/svnurl.py
py/_path/svnwc.py
py/_process/__init__.py
py/_process/cmdexec.py
py/_process/forkedfunc.py
py/_process/killproc.py
py/_vendored_packages/__init__.py
py/_vendored_packages/apipkg.py
py/_vendored_packages/iniconfig.py
testing/conftest.py
testing/code/test_assertion.py
testing/code/test_code.py
testing/code/test_excinfo.py
testing/code/test_source.py
testing/io_/__init__.py
testing/io_/test_capture.py
testing/io_/test_saferepr.py
testing/io_/test_terminalwriter.py
testing/log/__init__.py
testing/log/test_log.py
testing/log/test_warning.py
testing/path/common.py
testing/path/conftest.py
testing/path/repotest.dump
testing/path/svntestbase.py
testing/path/test_cacheutil.py
testing/path/test_local.py
testing/path/test_svnauth.py
testing/path/test_svnurl.py
testing/path/test_svnwc.py
testing/process/__init__.py
testing/process/test_cmdexec.py
testing/process/test_forkedfunc.py
testing/process/test_killproc.py
testing/root/__init__.py
testing/root/test_builtin.py
testing/root/test_error.py
testing/root/test_py_imports.py
testing/root/test_std.py
testing/root/test_xmlgen.py py-1.5.2/py.egg-info/not-zip-safe 0000664 0001750 0001750 00000000001 13202663736 017657 0 ustar vagrant vagrant 0000000 0000000
py-1.5.2/py.egg-info/dependency_links.txt 0000664 0001750 0001750 00000000001 13203150701 021457 0 ustar vagrant vagrant 0000000 0000000
py-1.5.2/py.egg-info/PKG-INFO 0000664 0001750 0001750 00000005605 13203150701 016514 0 ustar vagrant vagrant 0000000 0000000 Metadata-Version: 1.2
Name: py
Version: 1.5.2
Summary: library with cross-python path, ini-parsing, io, code, log facilities
Home-page: http://py.readthedocs.io/
Author: holger krekel, Ronny Pfannschmidt, Benjamin Peterson and others
Author-email: pytest-dev@python.org
License: MIT license
Description-Content-Type: UNKNOWN
Description: .. image:: https://img.shields.io/pypi/v/py.svg
:target: https://pypi.org/project/py
.. image:: https://anaconda.org/conda-forge/py/badges/version.svg
:target: https://anaconda.org/conda-forge/py
.. image:: https://img.shields.io/pypi/pyversions/pytest.svg
:target: https://pypi.org/project/py
.. image:: https://img.shields.io/travis/pytest-dev/py.svg
:target: https://travis-ci.org/pytest-dev/py
.. image:: https://ci.appveyor.com/api/projects/status/10keglan6uqwj5al/branch/master?svg=true
:target: https://ci.appveyor.com/project/pytestbot/py
**NOTE**: this library is in **maintenance mode** and should not be used in new code.
The py lib is a Python development support library featuring
the following tools and modules:
* ``py.path``: uniform local and svn path objects
* ``py.apipkg``: explicit API control and lazy-importing
* ``py.iniconfig``: easy parsing of .ini files
* ``py.code``: dynamic code generation and introspection (deprecated, moved to ``pytest``).
**NOTE**: prior to the 1.4 release this distribution used to
contain py.test which is now its own package, see http://pytest.org
For questions and more information please visit http://py.readthedocs.org
Bugs and issues: https://github.com/pytest-dev/py
Authors: Holger Krekel and others, 2004-2017
Platform: unix
Platform: linux
Platform: osx
Platform: cygwin
Platform: win32
Classifier: Development Status :: 6 - Mature
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Utilities
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
py-1.5.2/setup.cfg 0000664 0001750 0001750 00000000220 13203150701 015102 0 ustar vagrant vagrant 0000000 0000000 [wheel]
universal = 1
[metadata]
license_file = LICENSE
[devpi:upload]
formats = sdist.tgz,bdist_wheel
[egg_info]
tag_build =
tag_date = 0
py-1.5.2/py/ 0000775 0001750 0001750 00000000000 13203150701 013717 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/py/_code/ 0000775 0001750 0001750 00000000000 13203150701 014770 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/py/_code/source.py 0000664 0001750 0001750 00000033342 13202663755 016670 0 ustar vagrant vagrant 0000000 0000000 from __future__ import generators
from bisect import bisect_right
import sys
import inspect, tokenize
import py
from types import ModuleType
cpy_compile = compile
try:
import _ast
from _ast import PyCF_ONLY_AST as _AST_FLAG
except ImportError:
_AST_FLAG = 0
_ast = None
class Source(object):
""" a immutable object holding a source code fragment,
possibly deindenting it.
"""
_compilecounter = 0
def __init__(self, *parts, **kwargs):
self.lines = lines = []
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, (tuple, list)):
partlines = [x.rstrip("\n") for x in part]
elif isinstance(part, py.builtin._basestring):
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]
else:
if key.step not in (None, 1):
raise IndexError("cannot slice a Source with a step")
return self.__getslice__(key.start, key.stop)
def __len__(self):
return len(self.lines)
def __getslice__(self, start, end):
newsource = Source()
newsource.lines = self.lines[start:end]
return newsource
def strip(self):
""" return new source object with trailing
and leading blank lines removed.
"""
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]
return source
def putaround(self, before='', after='', indent=' ' * 4):
""" return a copy of the source object with
'before' and 'after' wrapped around it.
"""
before = Source(before)
after = Source(after)
newsource = Source()
lines = [ (indent + line) for line in self.lines]
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, assertion=False):
""" return Source statement which contains the
given linenumber (counted from 0).
"""
start, end = self.getstatementrange(lineno, assertion)
return self[start:end]
def getstatementrange(self, lineno, assertion=False):
""" return (start, end) tuple which spans the minimal
statement region which containing the given lineno.
"""
if not (0 <= lineno < len(self)):
raise IndexError("lineno out of range")
ast, start, end = getstatementrange_ast(lineno, self)
return start, end
def deindent(self, offset=None):
""" return a new source object deindented by offset.
If offset is None then guess an indentation offset from
the first non-blank line. Subsequent lines which have a
lower indentation offset will be copied verbatim as
they are assumed to be part of multilines.
"""
# XXX maybe use the tokenizer to properly handle multiline
# strings etc.pp?
newsource = Source()
newsource.lines[:] = deindent(self.lines, offset)
return newsource
def isparseable(self, deindent=True):
""" return True if source is parseable, heuristically
deindenting it by default.
"""
try:
import parser
except ImportError:
syntax_checker = lambda x: compile(x, 'asd', 'exec')
else:
syntax_checker = parser.suite
if deindent:
source = str(self.deindent())
else:
source = str(self)
try:
#compile(source+'\n', "x", "exec")
syntax_checker(source+'\n')
except KeyboardInterrupt:
raise
except Exception:
return False
else:
return True
def __str__(self):
return "\n".join(self.lines)
def compile(self, filename=None, mode='exec',
flag=generators.compiler_flag,
dont_inherit=0, _genframe=None):
""" return compiled code object. if filename is None
invent an artificial filename which displays
the source/line position of the caller frame.
"""
if not filename or py.path.local(filename).check(file=0):
if _genframe is None:
_genframe = sys._getframe(1) # the caller
fn,lineno = _genframe.f_code.co_filename, _genframe.f_lineno
base = "<%d-codegen " % self._compilecounter
self.__class__._compilecounter += 1
if not filename:
filename = base + '%s:%d>' % (fn, lineno)
else:
filename = base + '%r %s:%d>' % (filename, fn, lineno)
source = "\n".join(self.lines) + '\n'
try:
co = cpy_compile(source, filename, mode, flag)
except SyntaxError:
ex = sys.exc_info()[1]
# re-represent syntax errors from parsing python strings
msglines = self.lines[:ex.lineno]
if ex.offset:
msglines.append(" "*ex.offset + '^')
msglines.append("(code was compiled probably from here: %s)" % filename)
newex = SyntaxError('\n'.join(msglines))
newex.offset = ex.offset
newex.lineno = ex.lineno
newex.text = ex.text
raise newex
else:
if flag & _AST_FLAG:
return co
lines = [(x + "\n") for x in self.lines]
import linecache
linecache.cache[filename] = (1, None, lines, filename)
return co
#
# public API shortcut functions
#
def compile_(source, filename=None, mode='exec', flags=
generators.compiler_flag, dont_inherit=0):
""" compile the given source to a raw code object,
and maintain an internal cache which allows later
retrieval of the source code for the code object
and any recursively created code objects.
"""
if _ast is not None and isinstance(source, _ast.AST):
# XXX should Source support having AST?
return cpy_compile(source, filename, mode, flags, dont_inherit)
_genframe = sys._getframe(1) # the caller
s = Source(source)
co = s.compile(filename, mode, flags, _genframe=_genframe)
return co
def getfslineno(obj):
""" Return source location (path, lineno) for the given object.
If the source cannot be determined return ("", -1)
"""
try:
code = py.code.Code(obj)
except TypeError:
try:
fn = (inspect.getsourcefile(obj) or
inspect.getfile(obj))
except TypeError:
return "", -1
fspath = fn and py.path.local(fn) or None
lineno = -1
if fspath:
try:
_, lineno = findsource(obj)
except IOError:
pass
else:
fspath = code.path
lineno = code.firstlineno
assert isinstance(lineno, int)
return fspath, lineno
#
# helper functions
#
def findsource(obj):
try:
sourcelines, lineno = inspect.findsource(obj)
except py.builtin._sysex:
raise
except:
return None, -1
source = Source()
source.lines = [line.rstrip() for line in sourcelines]
return source, lineno
def getsource(obj, **kwargs):
obj = py.code.getrawcode(obj)
try:
strsrc = inspect.getsource(obj)
except IndentationError:
strsrc = "\"Buggy python version consider upgrading, cannot get source\""
assert isinstance(strsrc, str)
return Source(strsrc, **kwargs)
def deindent(lines, offset=None):
if offset is None:
for line in lines:
line = line.expandtabs()
s = line.lstrip()
if s:
offset = len(line)-len(s)
break
else:
offset = 0
if offset == 0:
return list(lines)
newlines = []
def readline_generator(lines):
for line in lines:
yield line + '\n'
while True:
yield ''
it = readline_generator(lines)
try:
for _, _, (sline, _), (eline, _), _ in tokenize.generate_tokens(lambda: next(it)):
if sline > len(lines):
break # End of input reached
if sline > len(newlines):
line = lines[sline - 1].expandtabs()
if line.lstrip() and line[:offset].isspace():
line = line[offset:] # Deindent
newlines.append(line)
for i in range(sline, eline):
# Don't deindent continuing lines of
# multiline tokens (i.e. multiline strings)
newlines.append(lines[i])
except (IndentationError, tokenize.TokenError):
pass
# Add any lines we didn't see. E.g. if an exception was raised.
newlines.extend(lines[len(newlines):])
return newlines
def get_statement_startend2(lineno, node):
import ast
# flatten all statements and except handlers into one lineno-list
# AST's line numbers start indexing at 1
l = []
for x in ast.walk(node):
if isinstance(x, _ast.stmt) or isinstance(x, _ast.ExceptHandler):
l.append(x.lineno - 1)
for name in "finalbody", "orelse":
val = getattr(x, name, None)
if val:
# treat the finally/orelse part as its own statement
l.append(val[0].lineno - 1 - 1)
l.sort()
insert_index = bisect_right(l, lineno)
start = l[insert_index - 1]
if insert_index >= len(l):
end = None
else:
end = l[insert_index]
return start, end
def getstatementrange_ast(lineno, source, assertion=False, astnode=None):
if astnode is None:
content = str(source)
try:
astnode = compile(content, "source", "exec", 1024) # 1024 for AST
except ValueError:
start, end = getstatementrange_old(lineno, source, assertion)
return None, start, end
start, end = get_statement_startend2(lineno, astnode)
# we need to correct the end:
# - ast-parsing strips comments
# - there might be empty lines
# - we might have lesser indented code blocks at the end
if end is None:
end = len(source.lines)
if end > start + 1:
# make sure we don't span differently indented code blocks
# by using the BlockFinder helper used which inspect.getsource() uses itself
block_finder = inspect.BlockFinder()
# if we start with an indented line, put blockfinder to "started" mode
block_finder.started = source.lines[start][0].isspace()
it = ((x + "\n") for x in source.lines[start:end])
try:
for tok in tokenize.generate_tokens(lambda: next(it)):
block_finder.tokeneater(*tok)
except (inspect.EndOfBlock, IndentationError):
end = block_finder.last + start
except Exception:
pass
# the end might still point to a comment or empty line, correct it
while end:
line = source.lines[end - 1].lstrip()
if line.startswith("#") or not line:
end -= 1
else:
break
return astnode, start, end
def getstatementrange_old(lineno, source, assertion=False):
""" return (start, end) tuple which spans the minimal
statement region which containing the given lineno.
raise an IndexError if no such statementrange can be found.
"""
# XXX this logic is only used on python2.4 and below
# 1. find the start of the statement
from codeop import compile_command
for start in range(lineno, -1, -1):
if assertion:
line = source.lines[start]
# the following lines are not fully tested, change with care
if 'super' in line and 'self' in line and '__init__' in line:
raise IndexError("likely a subclass")
if "assert" not in line and "raise" not in line:
continue
trylines = source.lines[start:lineno+1]
# quick hack to prepare parsing an indented line with
# compile_command() (which errors on "return" outside defs)
trylines.insert(0, 'def xxx():')
trysource = '\n '.join(trylines)
# ^ space here
try:
compile_command(trysource)
except (SyntaxError, OverflowError, ValueError):
continue
# 2. find the end of the statement
for end in range(lineno+1, len(source)+1):
trysource = source[start:end]
if trysource.isparseable():
return start, end
raise SyntaxError("no valid source range around line %d " % (lineno,))
py-1.5.2/py/_code/code.py 0000664 0001750 0001750 00000065544 13202663755 016313 0 ustar vagrant vagrant 0000000 0000000 import py
import sys
from inspect import CO_VARARGS, CO_VARKEYWORDS, isclass
builtin_repr = repr
reprlib = py.builtin._tryimport('repr', 'reprlib')
if sys.version_info[0] >= 3:
from traceback import format_exception_only
else:
from py._code._py2traceback import format_exception_only
import traceback
class Code(object):
""" wrapper around Python code objects """
def __init__(self, rawcode):
if not hasattr(rawcode, "co_filename"):
rawcode = py.code.getrawcode(rawcode)
try:
self.filename = rawcode.co_filename
self.firstlineno = rawcode.co_firstlineno - 1
self.name = rawcode.co_name
except AttributeError:
raise TypeError("not a code object: %r" % (rawcode,))
self.raw = rawcode
def __eq__(self, other):
return self.raw == other.raw
def __ne__(self, other):
return not self == other
@property
def path(self):
""" return a path object pointing to source code (note that it
might not point to an actually existing file). """
p = py.path.local(self.raw.co_filename)
# maybe don't try this checking
if not p.check():
# XXX maybe try harder like the weird logic
# in the standard lib [linecache.updatecache] does?
p = self.raw.co_filename
return p
@property
def fullsource(self):
""" return a py.code.Source object for the full source file of the code
"""
from py._code import source
full, _ = source.findsource(self.raw)
return full
def source(self):
""" return a py.code.Source object for the code object's source only
"""
# return source only for that part of code
return py.code.Source(self.raw)
def getargs(self, var=False):
""" return a tuple with the argument names for the code object
if 'var' is set True also return the names of the variable and
keyword arguments when present
"""
# handfull shortcut for getting args
raw = self.raw
argcount = raw.co_argcount
if var:
argcount += raw.co_flags & CO_VARARGS
argcount += raw.co_flags & CO_VARKEYWORDS
return raw.co_varnames[:argcount]
class Frame(object):
"""Wrapper around a Python frame holding f_locals and f_globals
in which expressions can be evaluated."""
def __init__(self, frame):
self.lineno = frame.f_lineno - 1
self.f_globals = frame.f_globals
self.f_locals = frame.f_locals
self.raw = frame
self.code = py.code.Code(frame.f_code)
@property
def statement(self):
""" statement this frame is at """
if self.code.fullsource is None:
return py.code.Source("")
return self.code.fullsource.getstatement(self.lineno)
def eval(self, code, **vars):
""" evaluate 'code' in the frame
'vars' are optional additional local variables
returns the result of the evaluation
"""
f_locals = self.f_locals.copy()
f_locals.update(vars)
return eval(code, self.f_globals, f_locals)
def exec_(self, code, **vars):
""" exec 'code' in the frame
'vars' are optiona; additional local variables
"""
f_locals = self.f_locals.copy()
f_locals.update(vars)
py.builtin.exec_(code, self.f_globals, f_locals)
def repr(self, object):
""" return a 'safe' (non-recursive, one-line) string repr for 'object'
"""
return py.io.saferepr(object)
def is_true(self, object):
return object
def getargs(self, var=False):
""" return a list of tuples (name, value) for all arguments
if 'var' is set True also include the variable and keyword
arguments when present
"""
retval = []
for arg in self.code.getargs(var):
try:
retval.append((arg, self.f_locals[arg]))
except KeyError:
pass # this can occur when using Psyco
return retval
class TracebackEntry(object):
""" a single entry in a traceback """
_repr_style = None
exprinfo = None
def __init__(self, rawentry):
self._rawentry = rawentry
self.lineno = rawentry.tb_lineno - 1
def set_repr_style(self, mode):
assert mode in ("short", "long")
self._repr_style = mode
@property
def frame(self):
return py.code.Frame(self._rawentry.tb_frame)
@property
def relline(self):
return self.lineno - self.frame.code.firstlineno
def __repr__(self):
return "" % (self.frame.code.path, self.lineno+1)
@property
def statement(self):
""" py.code.Source object for the current statement """
source = self.frame.code.fullsource
return source.getstatement(self.lineno)
@property
def path(self):
""" path to the source code """
return self.frame.code.path
def getlocals(self):
return self.frame.f_locals
locals = property(getlocals, None, None, "locals of underlaying frame")
def reinterpret(self):
"""Reinterpret the failing statement and returns a detailed information
about what operations are performed."""
if self.exprinfo is None:
source = str(self.statement).strip()
x = py.code._reinterpret(source, self.frame, should_fail=True)
if not isinstance(x, str):
raise TypeError("interpret returned non-string %r" % (x,))
self.exprinfo = x
return self.exprinfo
def getfirstlinesource(self):
# on Jython this firstlineno can be -1 apparently
return max(self.frame.code.firstlineno, 0)
def getsource(self, astcache=None):
""" return failing source code. """
# we use the passed in astcache to not reparse asttrees
# within exception info printing
from py._code.source import getstatementrange_ast
source = self.frame.code.fullsource
if source is None:
return None
key = astnode = None
if astcache is not None:
key = self.frame.code.path
if key is not None:
astnode = astcache.get(key, None)
start = self.getfirstlinesource()
try:
astnode, _, end = getstatementrange_ast(self.lineno, source,
astnode=astnode)
except SyntaxError:
end = self.lineno + 1
else:
if key is not None:
astcache[key] = astnode
return source[start:end]
source = property(getsource)
def ishidden(self):
""" return True if the current frame has a var __tracebackhide__
resolving to True
mostly for internal use
"""
try:
return self.frame.f_locals['__tracebackhide__']
except KeyError:
try:
return self.frame.f_globals['__tracebackhide__']
except KeyError:
return False
def __str__(self):
try:
fn = str(self.path)
except py.error.Error:
fn = '???'
name = self.frame.code.name
try:
line = str(self.statement).lstrip()
except KeyboardInterrupt:
raise
except:
line = "???"
return " File %r:%d in %s\n %s\n" % (fn, self.lineno+1, name, line)
def name(self):
return self.frame.code.raw.co_name
name = property(name, None, None, "co_name of underlaying code")
class Traceback(list):
""" Traceback objects encapsulate and offer higher level
access to Traceback entries.
"""
Entry = TracebackEntry
def __init__(self, tb):
""" initialize from given python traceback object. """
if hasattr(tb, 'tb_next'):
def f(cur):
while cur is not None:
yield self.Entry(cur)
cur = cur.tb_next
list.__init__(self, f(tb))
else:
list.__init__(self, tb)
def cut(self, path=None, lineno=None, firstlineno=None, excludepath=None):
""" return a Traceback instance wrapping part of this Traceback
by provding any combination of path, lineno and firstlineno, the
first frame to start the to-be-returned traceback is determined
this allows cutting the first part of a Traceback instance e.g.
for formatting reasons (removing some uninteresting bits that deal
with handling of the exception/traceback)
"""
for x in self:
code = x.frame.code
codepath = code.path
if ((path is None or codepath == path) and
(excludepath is None or not hasattr(codepath, 'relto') or
not codepath.relto(excludepath)) and
(lineno is None or x.lineno == lineno) and
(firstlineno is None or x.frame.code.firstlineno == firstlineno)):
return Traceback(x._rawentry)
return self
def __getitem__(self, key):
val = super(Traceback, self).__getitem__(key)
if isinstance(key, type(slice(0))):
val = self.__class__(val)
return val
def filter(self, fn=lambda x: not x.ishidden()):
""" return a Traceback instance with certain items removed
fn is a function that gets a single argument, a TracebackItem
instance, and should return True when the item should be added
to the Traceback, False when not
by default this removes all the TracebackItems which are hidden
(see ishidden() above)
"""
return Traceback(filter(fn, self))
def getcrashentry(self):
""" return last non-hidden traceback entry that lead
to the exception of a traceback.
"""
for i in range(-1, -len(self)-1, -1):
entry = self[i]
if not entry.ishidden():
return entry
return self[-1]
def recursionindex(self):
""" return the index of the frame/TracebackItem where recursion
originates if appropriate, None if no recursion occurred
"""
cache = {}
for i, entry in enumerate(self):
# id for the code.raw is needed to work around
# the strange metaprogramming in the decorator lib from pypi
# which generates code objects that have hash/value equality
#XXX needs a test
key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno
#print "checking for recursion at", key
l = cache.setdefault(key, [])
if l:
f = entry.frame
loc = f.f_locals
for otherloc in l:
if f.is_true(f.eval(co_equal,
__recursioncache_locals_1=loc,
__recursioncache_locals_2=otherloc)):
return i
l.append(entry.frame.f_locals)
return None
co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2',
'?', 'eval')
class ExceptionInfo(object):
""" wraps sys.exc_info() objects and offers
help for navigating the traceback.
"""
_striptext = ''
def __init__(self, tup=None, exprinfo=None):
if tup is None:
tup = sys.exc_info()
if exprinfo is None and isinstance(tup[1], AssertionError):
exprinfo = getattr(tup[1], 'msg', None)
if exprinfo is None:
exprinfo = str(tup[1])
if exprinfo and exprinfo.startswith('assert '):
self._striptext = 'AssertionError: '
self._excinfo = tup
#: the exception class
self.type = tup[0]
#: the exception instance
self.value = tup[1]
#: the exception raw traceback
self.tb = tup[2]
#: the exception type name
self.typename = self.type.__name__
#: the exception traceback (py.code.Traceback instance)
self.traceback = py.code.Traceback(self.tb)
def __repr__(self):
return "" % (
self.typename, len(self.traceback))
def exconly(self, tryshort=False):
""" return the exception as a string
when 'tryshort' resolves to True, and the exception is a
py.code._AssertionError, only the actual exception part of
the exception representation is returned (so 'AssertionError: ' is
removed from the beginning)
"""
lines = format_exception_only(self.type, self.value)
text = ''.join(lines)
text = text.rstrip()
if tryshort:
if text.startswith(self._striptext):
text = text[len(self._striptext):]
return text
def errisinstance(self, exc):
""" return True if the exception is an instance of exc """
return isinstance(self.value, exc)
def _getreprcrash(self):
exconly = self.exconly(tryshort=True)
entry = self.traceback.getcrashentry()
path, lineno = entry.frame.code.raw.co_filename, entry.lineno
return ReprFileLocation(path, lineno+1, exconly)
def getrepr(self, showlocals=False, style="long",
abspath=False, tbfilter=True, funcargs=False):
""" return str()able representation of this exception info.
showlocals: show locals per traceback entry
style: long|short|no|native traceback style
tbfilter: hide entries (where __tracebackhide__ is true)
in case of style==native, tbfilter and showlocals is ignored.
"""
if style == 'native':
return ReprExceptionInfo(ReprTracebackNative(
traceback.format_exception(
self.type,
self.value,
self.traceback[0]._rawentry,
)), self._getreprcrash())
fmt = FormattedExcinfo(
showlocals=showlocals, style=style,
abspath=abspath, tbfilter=tbfilter, funcargs=funcargs)
return fmt.repr_excinfo(self)
def __str__(self):
entry = self.traceback[-1]
loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
return str(loc)
def __unicode__(self):
entry = self.traceback[-1]
loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
return loc.__unicode__()
class FormattedExcinfo(object):
""" presenting information about failing Functions and Generators. """
# for traceback entries
flow_marker = ">"
fail_marker = "E"
def __init__(self, showlocals=False, style="long",
abspath=True, tbfilter=True, funcargs=False):
self.showlocals = showlocals
self.style = style
self.tbfilter = tbfilter
self.funcargs = funcargs
self.abspath = abspath
self.astcache = {}
def _getindent(self, source):
# figure out indent for given source
try:
s = str(source.getstatement(len(source)-1))
except KeyboardInterrupt:
raise
except:
try:
s = str(source[-1])
except KeyboardInterrupt:
raise
except:
return 0
return 4 + (len(s) - len(s.lstrip()))
def _getentrysource(self, entry):
source = entry.getsource(self.astcache)
if source is not None:
source = source.deindent()
return source
def _saferepr(self, obj):
return py.io.saferepr(obj)
def repr_args(self, entry):
if self.funcargs:
args = []
for argname, argvalue in entry.frame.getargs(var=True):
args.append((argname, self._saferepr(argvalue)))
return ReprFuncArgs(args)
def get_source(self, source, line_index=-1, excinfo=None, short=False):
""" return formatted and marked up source lines. """
lines = []
if source is None or line_index >= len(source.lines):
source = py.code.Source("???")
line_index = 0
if line_index < 0:
line_index += len(source)
space_prefix = " "
if short:
lines.append(space_prefix + source.lines[line_index].strip())
else:
for line in source.lines[:line_index]:
lines.append(space_prefix + line)
lines.append(self.flow_marker + " " + source.lines[line_index])
for line in source.lines[line_index+1:]:
lines.append(space_prefix + line)
if excinfo is not None:
indent = 4 if short else self._getindent(source)
lines.extend(self.get_exconly(excinfo, indent=indent, markall=True))
return lines
def get_exconly(self, excinfo, indent=4, markall=False):
lines = []
indent = " " * indent
# get the real exception information out
exlines = excinfo.exconly(tryshort=True).split('\n')
failindent = self.fail_marker + indent[1:]
for line in exlines:
lines.append(failindent + line)
if not markall:
failindent = indent
return lines
def repr_locals(self, locals):
if self.showlocals:
lines = []
keys = [loc for loc in locals if loc[0] != "@"]
keys.sort()
for name in keys:
value = locals[name]
if name == '__builtins__':
lines.append("__builtins__ = ")
else:
# This formatting could all be handled by the
# _repr() function, which is only reprlib.Repr in
# disguise, so is very configurable.
str_repr = self._saferepr(value)
#if len(str_repr) < 70 or not isinstance(value,
# (list, tuple, dict)):
lines.append("%-10s = %s" %(name, str_repr))
#else:
# self._line("%-10s =\\" % (name,))
# # XXX
# pprint.pprint(value, stream=self.excinfowriter)
return ReprLocals(lines)
def repr_traceback_entry(self, entry, excinfo=None):
source = self._getentrysource(entry)
if source is None:
source = py.code.Source("???")
line_index = 0
else:
# entry.getfirstlinesource() can be -1, should be 0 on jython
line_index = entry.lineno - max(entry.getfirstlinesource(), 0)
lines = []
style = entry._repr_style
if style is None:
style = self.style
if style in ("short", "long"):
short = style == "short"
reprargs = self.repr_args(entry) if not short else None
s = self.get_source(source, line_index, excinfo, short=short)
lines.extend(s)
if short:
message = "in %s" %(entry.name)
else:
message = excinfo and excinfo.typename or ""
path = self._makepath(entry.path)
filelocrepr = ReprFileLocation(path, entry.lineno+1, message)
localsrepr = None
if not short:
localsrepr = self.repr_locals(entry.locals)
return ReprEntry(lines, reprargs, localsrepr, filelocrepr, style)
if excinfo:
lines.extend(self.get_exconly(excinfo, indent=4))
return ReprEntry(lines, None, None, None, style)
def _makepath(self, path):
if not self.abspath:
try:
np = py.path.local().bestrelpath(path)
except OSError:
return path
if len(np) < len(str(path)):
path = np
return path
def repr_traceback(self, excinfo):
traceback = excinfo.traceback
if self.tbfilter:
traceback = traceback.filter()
recursionindex = None
if excinfo.errisinstance(RuntimeError):
if "maximum recursion depth exceeded" in str(excinfo.value):
recursionindex = traceback.recursionindex()
last = traceback[-1]
entries = []
extraline = None
for index, entry in enumerate(traceback):
einfo = (last == entry) and excinfo or None
reprentry = self.repr_traceback_entry(entry, einfo)
entries.append(reprentry)
if index == recursionindex:
extraline = "!!! Recursion detected (same locals & position)"
break
return ReprTraceback(entries, extraline, style=self.style)
def repr_excinfo(self, excinfo):
reprtraceback = self.repr_traceback(excinfo)
reprcrash = excinfo._getreprcrash()
return ReprExceptionInfo(reprtraceback, reprcrash)
class TerminalRepr:
def __str__(self):
s = self.__unicode__()
if sys.version_info[0] < 3:
s = s.encode('utf-8')
return s
def __unicode__(self):
# FYI this is called from pytest-xdist's serialization of exception
# information.
io = py.io.TextIO()
tw = py.io.TerminalWriter(file=io)
self.toterminal(tw)
return io.getvalue().strip()
def __repr__(self):
return "<%s instance at %0x>" %(self.__class__, id(self))
class ReprExceptionInfo(TerminalRepr):
def __init__(self, reprtraceback, reprcrash):
self.reprtraceback = reprtraceback
self.reprcrash = reprcrash
self.sections = []
def addsection(self, name, content, sep="-"):
self.sections.append((name, content, sep))
def toterminal(self, tw):
self.reprtraceback.toterminal(tw)
for name, content, sep in self.sections:
tw.sep(sep, name)
tw.line(content)
class ReprTraceback(TerminalRepr):
entrysep = "_ "
def __init__(self, reprentries, extraline, style):
self.reprentries = reprentries
self.extraline = extraline
self.style = style
def toterminal(self, tw):
# the entries might have different styles
last_style = None
for i, entry in enumerate(self.reprentries):
if entry.style == "long":
tw.line("")
entry.toterminal(tw)
if i < len(self.reprentries) - 1:
next_entry = self.reprentries[i+1]
if entry.style == "long" or \
entry.style == "short" and next_entry.style == "long":
tw.sep(self.entrysep)
if self.extraline:
tw.line(self.extraline)
class ReprTracebackNative(ReprTraceback):
def __init__(self, tblines):
self.style = "native"
self.reprentries = [ReprEntryNative(tblines)]
self.extraline = None
class ReprEntryNative(TerminalRepr):
style = "native"
def __init__(self, tblines):
self.lines = tblines
def toterminal(self, tw):
tw.write("".join(self.lines))
class ReprEntry(TerminalRepr):
localssep = "_ "
def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr, style):
self.lines = lines
self.reprfuncargs = reprfuncargs
self.reprlocals = reprlocals
self.reprfileloc = filelocrepr
self.style = style
def toterminal(self, tw):
if self.style == "short":
self.reprfileloc.toterminal(tw)
for line in self.lines:
red = line.startswith("E ")
tw.line(line, bold=True, red=red)
#tw.line("")
return
if self.reprfuncargs:
self.reprfuncargs.toterminal(tw)
for line in self.lines:
red = line.startswith("E ")
tw.line(line, bold=True, red=red)
if self.reprlocals:
#tw.sep(self.localssep, "Locals")
tw.line("")
self.reprlocals.toterminal(tw)
if self.reprfileloc:
if self.lines:
tw.line("")
self.reprfileloc.toterminal(tw)
def __str__(self):
return "%s\n%s\n%s" % ("\n".join(self.lines),
self.reprlocals,
self.reprfileloc)
class ReprFileLocation(TerminalRepr):
def __init__(self, path, lineno, message):
self.path = str(path)
self.lineno = lineno
self.message = message
def toterminal(self, tw):
# filename and lineno output for each entry,
# using an output format that most editors unterstand
msg = self.message
i = msg.find("\n")
if i != -1:
msg = msg[:i]
tw.line("%s:%s: %s" %(self.path, self.lineno, msg))
class ReprLocals(TerminalRepr):
def __init__(self, lines):
self.lines = lines
def toterminal(self, tw):
for line in self.lines:
tw.line(line)
class ReprFuncArgs(TerminalRepr):
def __init__(self, args):
self.args = args
def toterminal(self, tw):
if self.args:
linesofar = ""
for name, value in self.args:
ns = "%s = %s" %(name, value)
if len(ns) + len(linesofar) + 2 > tw.fullwidth:
if linesofar:
tw.line(linesofar)
linesofar = ns
else:
if linesofar:
linesofar += ", " + ns
else:
linesofar = ns
if linesofar:
tw.line(linesofar)
tw.line("")
oldbuiltins = {}
def patch_builtins(assertion=True, compile=True):
""" put compile and AssertionError builtins to Python's builtins. """
if assertion:
from py._code import assertion
l = oldbuiltins.setdefault('AssertionError', [])
l.append(py.builtin.builtins.AssertionError)
py.builtin.builtins.AssertionError = assertion.AssertionError
if compile:
l = oldbuiltins.setdefault('compile', [])
l.append(py.builtin.builtins.compile)
py.builtin.builtins.compile = py.code.compile
def unpatch_builtins(assertion=True, compile=True):
""" remove compile and AssertionError builtins from Python builtins. """
if assertion:
py.builtin.builtins.AssertionError = oldbuiltins['AssertionError'].pop()
if compile:
py.builtin.builtins.compile = oldbuiltins['compile'].pop()
def getrawcode(obj, trycall=True):
""" return code object for given function. """
try:
return obj.__code__
except AttributeError:
obj = getattr(obj, 'im_func', obj)
obj = getattr(obj, 'func_code', obj)
obj = getattr(obj, 'f_code', obj)
obj = getattr(obj, '__code__', obj)
if trycall and not hasattr(obj, 'co_firstlineno'):
if hasattr(obj, '__call__') and not isclass(obj):
x = getrawcode(obj.__call__, trycall=False)
if hasattr(x, 'co_firstlineno'):
return x
return obj
py-1.5.2/py/_code/_assertionold.py 0000664 0001750 0001750 00000042715 13202663755 020241 0 ustar vagrant vagrant 0000000 0000000 import py
import sys, inspect
from compiler import parse, ast, pycodegen
from py._code.assertion import BuiltinAssertionError, _format_explanation
import types
passthroughex = py.builtin._sysex
class Failure:
def __init__(self, node):
self.exc, self.value, self.tb = sys.exc_info()
self.node = node
class View(object):
"""View base class.
If C is a subclass of View, then C(x) creates a proxy object around
the object x. The actual class of the proxy is not C in general,
but a *subclass* of C determined by the rules below. To avoid confusion
we call view class the class of the proxy (a subclass of C, so of View)
and object class the class of x.
Attributes and methods not found in the proxy are automatically read on x.
Other operations like setting attributes are performed on the proxy, as
determined by its view class. The object x is available from the proxy
as its __obj__ attribute.
The view class selection is determined by the __view__ tuples and the
optional __viewkey__ method. By default, the selected view class is the
most specific subclass of C whose __view__ mentions the class of x.
If no such subclass is found, the search proceeds with the parent
object classes. For example, C(True) will first look for a subclass
of C with __view__ = (..., bool, ...) and only if it doesn't find any
look for one with __view__ = (..., int, ...), and then ..., object,...
If everything fails the class C itself is considered to be the default.
Alternatively, the view class selection can be driven by another aspect
of the object x, instead of the class of x, by overriding __viewkey__.
See last example at the end of this module.
"""
_viewcache = {}
__view__ = ()
def __new__(rootclass, obj, *args, **kwds):
self = object.__new__(rootclass)
self.__obj__ = obj
self.__rootclass__ = rootclass
key = self.__viewkey__()
try:
self.__class__ = self._viewcache[key]
except KeyError:
self.__class__ = self._selectsubclass(key)
return self
def __getattr__(self, attr):
# attributes not found in the normal hierarchy rooted on View
# are looked up in the object's real class
return getattr(self.__obj__, attr)
def __viewkey__(self):
return self.__obj__.__class__
def __matchkey__(self, key, subclasses):
if inspect.isclass(key):
keys = inspect.getmro(key)
else:
keys = [key]
for key in keys:
result = [C for C in subclasses if key in C.__view__]
if result:
return result
return []
def _selectsubclass(self, key):
subclasses = list(enumsubclasses(self.__rootclass__))
for C in subclasses:
if not isinstance(C.__view__, tuple):
C.__view__ = (C.__view__,)
choices = self.__matchkey__(key, subclasses)
if not choices:
return self.__rootclass__
elif len(choices) == 1:
return choices[0]
else:
# combine the multiple choices
return type('?', tuple(choices), {})
def __repr__(self):
return '%s(%r)' % (self.__rootclass__.__name__, self.__obj__)
def enumsubclasses(cls):
for subcls in cls.__subclasses__():
for subsubclass in enumsubclasses(subcls):
yield subsubclass
yield cls
class Interpretable(View):
"""A parse tree node with a few extra methods."""
explanation = None
def is_builtin(self, frame):
return False
def eval(self, frame):
# fall-back for unknown expression nodes
try:
expr = ast.Expression(self.__obj__)
expr.filename = ''
self.__obj__.filename = ''
co = pycodegen.ExpressionCodeGenerator(expr).getCode()
result = frame.eval(co)
except passthroughex:
raise
except:
raise Failure(self)
self.result = result
self.explanation = self.explanation or frame.repr(self.result)
def run(self, frame):
# fall-back for unknown statement nodes
try:
expr = ast.Module(None, ast.Stmt([self.__obj__]))
expr.filename = ''
co = pycodegen.ModuleCodeGenerator(expr).getCode()
frame.exec_(co)
except passthroughex:
raise
except:
raise Failure(self)
def nice_explanation(self):
return _format_explanation(self.explanation)
class Name(Interpretable):
__view__ = ast.Name
def is_local(self, frame):
source = '%r in locals() is not globals()' % self.name
try:
return frame.is_true(frame.eval(source))
except passthroughex:
raise
except:
return False
def is_global(self, frame):
source = '%r in globals()' % self.name
try:
return frame.is_true(frame.eval(source))
except passthroughex:
raise
except:
return False
def is_builtin(self, frame):
source = '%r not in locals() and %r not in globals()' % (
self.name, self.name)
try:
return frame.is_true(frame.eval(source))
except passthroughex:
raise
except:
return False
def eval(self, frame):
super(Name, self).eval(frame)
if not self.is_local(frame):
self.explanation = self.name
class Compare(Interpretable):
__view__ = ast.Compare
def eval(self, frame):
expr = Interpretable(self.expr)
expr.eval(frame)
for operation, expr2 in self.ops:
if hasattr(self, 'result'):
# shortcutting in chained expressions
if not frame.is_true(self.result):
break
expr2 = Interpretable(expr2)
expr2.eval(frame)
self.explanation = "%s %s %s" % (
expr.explanation, operation, expr2.explanation)
source = "__exprinfo_left %s __exprinfo_right" % operation
try:
self.result = frame.eval(source,
__exprinfo_left=expr.result,
__exprinfo_right=expr2.result)
except passthroughex:
raise
except:
raise Failure(self)
expr = expr2
class And(Interpretable):
__view__ = ast.And
def eval(self, frame):
explanations = []
for expr in self.nodes:
expr = Interpretable(expr)
expr.eval(frame)
explanations.append(expr.explanation)
self.result = expr.result
if not frame.is_true(expr.result):
break
self.explanation = '(' + ' and '.join(explanations) + ')'
class Or(Interpretable):
__view__ = ast.Or
def eval(self, frame):
explanations = []
for expr in self.nodes:
expr = Interpretable(expr)
expr.eval(frame)
explanations.append(expr.explanation)
self.result = expr.result
if frame.is_true(expr.result):
break
self.explanation = '(' + ' or '.join(explanations) + ')'
# == Unary operations ==
keepalive = []
for astclass, astpattern in {
ast.Not : 'not __exprinfo_expr',
ast.Invert : '(~__exprinfo_expr)',
}.items():
class UnaryArith(Interpretable):
__view__ = astclass
def eval(self, frame, astpattern=astpattern):
expr = Interpretable(self.expr)
expr.eval(frame)
self.explanation = astpattern.replace('__exprinfo_expr',
expr.explanation)
try:
self.result = frame.eval(astpattern,
__exprinfo_expr=expr.result)
except passthroughex:
raise
except:
raise Failure(self)
keepalive.append(UnaryArith)
# == Binary operations ==
for astclass, astpattern in {
ast.Add : '(__exprinfo_left + __exprinfo_right)',
ast.Sub : '(__exprinfo_left - __exprinfo_right)',
ast.Mul : '(__exprinfo_left * __exprinfo_right)',
ast.Div : '(__exprinfo_left / __exprinfo_right)',
ast.Mod : '(__exprinfo_left % __exprinfo_right)',
ast.Power : '(__exprinfo_left ** __exprinfo_right)',
}.items():
class BinaryArith(Interpretable):
__view__ = astclass
def eval(self, frame, astpattern=astpattern):
left = Interpretable(self.left)
left.eval(frame)
right = Interpretable(self.right)
right.eval(frame)
self.explanation = (astpattern
.replace('__exprinfo_left', left .explanation)
.replace('__exprinfo_right', right.explanation))
try:
self.result = frame.eval(astpattern,
__exprinfo_left=left.result,
__exprinfo_right=right.result)
except passthroughex:
raise
except:
raise Failure(self)
keepalive.append(BinaryArith)
class CallFunc(Interpretable):
__view__ = ast.CallFunc
def is_bool(self, frame):
source = 'isinstance(__exprinfo_value, bool)'
try:
return frame.is_true(frame.eval(source,
__exprinfo_value=self.result))
except passthroughex:
raise
except:
return False
def eval(self, frame):
node = Interpretable(self.node)
node.eval(frame)
explanations = []
vars = {'__exprinfo_fn': node.result}
source = '__exprinfo_fn('
for a in self.args:
if isinstance(a, ast.Keyword):
keyword = a.name
a = a.expr
else:
keyword = None
a = Interpretable(a)
a.eval(frame)
argname = '__exprinfo_%d' % len(vars)
vars[argname] = a.result
if keyword is None:
source += argname + ','
explanations.append(a.explanation)
else:
source += '%s=%s,' % (keyword, argname)
explanations.append('%s=%s' % (keyword, a.explanation))
if self.star_args:
star_args = Interpretable(self.star_args)
star_args.eval(frame)
argname = '__exprinfo_star'
vars[argname] = star_args.result
source += '*' + argname + ','
explanations.append('*' + star_args.explanation)
if self.dstar_args:
dstar_args = Interpretable(self.dstar_args)
dstar_args.eval(frame)
argname = '__exprinfo_kwds'
vars[argname] = dstar_args.result
source += '**' + argname + ','
explanations.append('**' + dstar_args.explanation)
self.explanation = "%s(%s)" % (
node.explanation, ', '.join(explanations))
if source.endswith(','):
source = source[:-1]
source += ')'
try:
self.result = frame.eval(source, **vars)
except passthroughex:
raise
except:
raise Failure(self)
if not node.is_builtin(frame) or not self.is_bool(frame):
r = frame.repr(self.result)
self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation)
class Getattr(Interpretable):
__view__ = ast.Getattr
def eval(self, frame):
expr = Interpretable(self.expr)
expr.eval(frame)
source = '__exprinfo_expr.%s' % self.attrname
try:
self.result = frame.eval(source, __exprinfo_expr=expr.result)
except passthroughex:
raise
except:
raise Failure(self)
self.explanation = '%s.%s' % (expr.explanation, self.attrname)
# if the attribute comes from the instance, its value is interesting
source = ('hasattr(__exprinfo_expr, "__dict__") and '
'%r in __exprinfo_expr.__dict__' % self.attrname)
try:
from_instance = frame.is_true(
frame.eval(source, __exprinfo_expr=expr.result))
except passthroughex:
raise
except:
from_instance = True
if from_instance:
r = frame.repr(self.result)
self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation)
# == Re-interpretation of full statements ==
class Assert(Interpretable):
__view__ = ast.Assert
def run(self, frame):
test = Interpretable(self.test)
test.eval(frame)
# simplify 'assert False where False = ...'
if (test.explanation.startswith('False\n{False = ') and
test.explanation.endswith('\n}')):
test.explanation = test.explanation[15:-2]
# print the result as 'assert '
self.result = test.result
self.explanation = 'assert ' + test.explanation
if not frame.is_true(test.result):
try:
raise BuiltinAssertionError
except passthroughex:
raise
except:
raise Failure(self)
class Assign(Interpretable):
__view__ = ast.Assign
def run(self, frame):
expr = Interpretable(self.expr)
expr.eval(frame)
self.result = expr.result
self.explanation = '... = ' + expr.explanation
# fall-back-run the rest of the assignment
ass = ast.Assign(self.nodes, ast.Name('__exprinfo_expr'))
mod = ast.Module(None, ast.Stmt([ass]))
mod.filename = ''
co = pycodegen.ModuleCodeGenerator(mod).getCode()
try:
frame.exec_(co, __exprinfo_expr=expr.result)
except passthroughex:
raise
except:
raise Failure(self)
class Discard(Interpretable):
__view__ = ast.Discard
def run(self, frame):
expr = Interpretable(self.expr)
expr.eval(frame)
self.result = expr.result
self.explanation = expr.explanation
class Stmt(Interpretable):
__view__ = ast.Stmt
def run(self, frame):
for stmt in self.nodes:
stmt = Interpretable(stmt)
stmt.run(frame)
def report_failure(e):
explanation = e.node.nice_explanation()
if explanation:
explanation = ", in: " + explanation
else:
explanation = ""
sys.stdout.write("%s: %s%s\n" % (e.exc.__name__, e.value, explanation))
def check(s, frame=None):
if frame is None:
frame = sys._getframe(1)
frame = py.code.Frame(frame)
expr = parse(s, 'eval')
assert isinstance(expr, ast.Expression)
node = Interpretable(expr.node)
try:
node.eval(frame)
except passthroughex:
raise
except Failure:
e = sys.exc_info()[1]
report_failure(e)
else:
if not frame.is_true(node.result):
sys.stderr.write("assertion failed: %s\n" % node.nice_explanation())
###########################################################
# API / Entry points
# #########################################################
def interpret(source, frame, should_fail=False):
module = Interpretable(parse(source, 'exec').node)
#print "got module", module
if isinstance(frame, types.FrameType):
frame = py.code.Frame(frame)
try:
module.run(frame)
except Failure:
e = sys.exc_info()[1]
return getfailure(e)
except passthroughex:
raise
except:
import traceback
traceback.print_exc()
if should_fail:
return ("(assertion failed, but when it was re-run for "
"printing intermediate values, it did not fail. Suggestions: "
"compute assert expression before the assert or use --nomagic)")
else:
return None
def getmsg(excinfo):
if isinstance(excinfo, tuple):
excinfo = py.code.ExceptionInfo(excinfo)
#frame, line = gettbline(tb)
#frame = py.code.Frame(frame)
#return interpret(line, frame)
tb = excinfo.traceback[-1]
source = str(tb.statement).strip()
x = interpret(source, tb.frame, should_fail=True)
if not isinstance(x, str):
raise TypeError("interpret returned non-string %r" % (x,))
return x
def getfailure(e):
explanation = e.node.nice_explanation()
if str(e.value):
lines = explanation.split('\n')
lines[0] += " << %s" % (e.value,)
explanation = '\n'.join(lines)
text = "%s: %s" % (e.exc.__name__, explanation)
if text.startswith('AssertionError: assert '):
text = text[16:]
return text
def run(s, frame=None):
if frame is None:
frame = sys._getframe(1)
frame = py.code.Frame(frame)
module = Interpretable(parse(s, 'exec').node)
try:
module.run(frame)
except Failure:
e = sys.exc_info()[1]
report_failure(e)
if __name__ == '__main__':
# example:
def f():
return 5
def g():
return 3
def h(x):
return 'never'
check("f() * g() == 5")
check("not f()")
check("not (f() and g() or 0)")
check("f() == g()")
i = 4
check("i == f()")
check("len(f()) == 0")
check("isinstance(2+3+4, float)")
run("x = i")
check("x == 5")
run("assert not f(), 'oops'")
run("a, b, c = 1, 2")
run("a, b, c = f()")
check("max([f(),g()]) == 4")
check("'hello'[g()] == 'h'")
run("'guk%d' % h(f())")
py-1.5.2/py/_code/_assertionnew.py 0000664 0001750 0001750 00000026272 13202663755 020254 0 ustar vagrant vagrant 0000000 0000000 """
Find intermediate evalutation results in assert statements through builtin AST.
This should replace _assertionold.py eventually.
"""
import sys
import ast
import py
from py._code.assertion import _format_explanation, BuiltinAssertionError
def _is_ast_expr(node):
return isinstance(node, ast.expr)
def _is_ast_stmt(node):
return isinstance(node, ast.stmt)
class Failure(Exception):
"""Error found while interpreting AST."""
def __init__(self, explanation=""):
self.cause = sys.exc_info()
self.explanation = explanation
def interpret(source, frame, should_fail=False):
mod = ast.parse(source)
visitor = DebugInterpreter(frame)
try:
visitor.visit(mod)
except Failure:
failure = sys.exc_info()[1]
return getfailure(failure)
if should_fail:
return ("(assertion failed, but when it was re-run for "
"printing intermediate values, it did not fail. Suggestions: "
"compute assert expression before the assert or use --no-assert)")
def run(offending_line, frame=None):
if frame is None:
frame = py.code.Frame(sys._getframe(1))
return interpret(offending_line, frame)
def getfailure(failure):
explanation = _format_explanation(failure.explanation)
value = failure.cause[1]
if str(value):
lines = explanation.splitlines()
if not lines:
lines.append("")
lines[0] += " << %s" % (value,)
explanation = "\n".join(lines)
text = "%s: %s" % (failure.cause[0].__name__, explanation)
if text.startswith("AssertionError: assert "):
text = text[16:]
return text
operator_map = {
ast.BitOr : "|",
ast.BitXor : "^",
ast.BitAnd : "&",
ast.LShift : "<<",
ast.RShift : ">>",
ast.Add : "+",
ast.Sub : "-",
ast.Mult : "*",
ast.Div : "/",
ast.FloorDiv : "//",
ast.Mod : "%",
ast.Eq : "==",
ast.NotEq : "!=",
ast.Lt : "<",
ast.LtE : "<=",
ast.Gt : ">",
ast.GtE : ">=",
ast.Pow : "**",
ast.Is : "is",
ast.IsNot : "is not",
ast.In : "in",
ast.NotIn : "not in"
}
unary_map = {
ast.Not : "not %s",
ast.Invert : "~%s",
ast.USub : "-%s",
ast.UAdd : "+%s"
}
class DebugInterpreter(ast.NodeVisitor):
"""Interpret AST nodes to gleam useful debugging information. """
def __init__(self, frame):
self.frame = frame
def generic_visit(self, node):
# Fallback when we don't have a special implementation.
if _is_ast_expr(node):
mod = ast.Expression(node)
co = self._compile(mod)
try:
result = self.frame.eval(co)
except Exception:
raise Failure()
explanation = self.frame.repr(result)
return explanation, result
elif _is_ast_stmt(node):
mod = ast.Module([node])
co = self._compile(mod, "exec")
try:
self.frame.exec_(co)
except Exception:
raise Failure()
return None, None
else:
raise AssertionError("can't handle %s" %(node,))
def _compile(self, source, mode="eval"):
return compile(source, "", mode)
def visit_Expr(self, expr):
return self.visit(expr.value)
def visit_Module(self, mod):
for stmt in mod.body:
self.visit(stmt)
def visit_Name(self, name):
explanation, result = self.generic_visit(name)
# See if the name is local.
source = "%r in locals() is not globals()" % (name.id,)
co = self._compile(source)
try:
local = self.frame.eval(co)
except Exception:
# have to assume it isn't
local = False
if not local:
return name.id, result
return explanation, result
def visit_Compare(self, comp):
left = comp.left
left_explanation, left_result = self.visit(left)
for op, next_op in zip(comp.ops, comp.comparators):
next_explanation, next_result = self.visit(next_op)
op_symbol = operator_map[op.__class__]
explanation = "%s %s %s" % (left_explanation, op_symbol,
next_explanation)
source = "__exprinfo_left %s __exprinfo_right" % (op_symbol,)
co = self._compile(source)
try:
result = self.frame.eval(co, __exprinfo_left=left_result,
__exprinfo_right=next_result)
except Exception:
raise Failure(explanation)
try:
if not result:
break
except KeyboardInterrupt:
raise
except:
break
left_explanation, left_result = next_explanation, next_result
rcomp = py.code._reprcompare
if rcomp:
res = rcomp(op_symbol, left_result, next_result)
if res:
explanation = res
return explanation, result
def visit_BoolOp(self, boolop):
is_or = isinstance(boolop.op, ast.Or)
explanations = []
for operand in boolop.values:
explanation, result = self.visit(operand)
explanations.append(explanation)
if result == is_or:
break
name = is_or and " or " or " and "
explanation = "(" + name.join(explanations) + ")"
return explanation, result
def visit_UnaryOp(self, unary):
pattern = unary_map[unary.op.__class__]
operand_explanation, operand_result = self.visit(unary.operand)
explanation = pattern % (operand_explanation,)
co = self._compile(pattern % ("__exprinfo_expr",))
try:
result = self.frame.eval(co, __exprinfo_expr=operand_result)
except Exception:
raise Failure(explanation)
return explanation, result
def visit_BinOp(self, binop):
left_explanation, left_result = self.visit(binop.left)
right_explanation, right_result = self.visit(binop.right)
symbol = operator_map[binop.op.__class__]
explanation = "(%s %s %s)" % (left_explanation, symbol,
right_explanation)
source = "__exprinfo_left %s __exprinfo_right" % (symbol,)
co = self._compile(source)
try:
result = self.frame.eval(co, __exprinfo_left=left_result,
__exprinfo_right=right_result)
except Exception:
raise Failure(explanation)
return explanation, result
def visit_Call(self, call):
func_explanation, func = self.visit(call.func)
arg_explanations = []
ns = {"__exprinfo_func" : func}
arguments = []
for arg in call.args:
arg_explanation, arg_result = self.visit(arg)
arg_name = "__exprinfo_%s" % (len(ns),)
ns[arg_name] = arg_result
arguments.append(arg_name)
arg_explanations.append(arg_explanation)
for keyword in call.keywords:
arg_explanation, arg_result = self.visit(keyword.value)
arg_name = "__exprinfo_%s" % (len(ns),)
ns[arg_name] = arg_result
keyword_source = "%s=%%s" % (keyword.arg)
arguments.append(keyword_source % (arg_name,))
arg_explanations.append(keyword_source % (arg_explanation,))
if call.starargs:
arg_explanation, arg_result = self.visit(call.starargs)
arg_name = "__exprinfo_star"
ns[arg_name] = arg_result
arguments.append("*%s" % (arg_name,))
arg_explanations.append("*%s" % (arg_explanation,))
if call.kwargs:
arg_explanation, arg_result = self.visit(call.kwargs)
arg_name = "__exprinfo_kwds"
ns[arg_name] = arg_result
arguments.append("**%s" % (arg_name,))
arg_explanations.append("**%s" % (arg_explanation,))
args_explained = ", ".join(arg_explanations)
explanation = "%s(%s)" % (func_explanation, args_explained)
args = ", ".join(arguments)
source = "__exprinfo_func(%s)" % (args,)
co = self._compile(source)
try:
result = self.frame.eval(co, **ns)
except Exception:
raise Failure(explanation)
pattern = "%s\n{%s = %s\n}"
rep = self.frame.repr(result)
explanation = pattern % (rep, rep, explanation)
return explanation, result
def _is_builtin_name(self, name):
pattern = "%r not in globals() and %r not in locals()"
source = pattern % (name.id, name.id)
co = self._compile(source)
try:
return self.frame.eval(co)
except Exception:
return False
def visit_Attribute(self, attr):
if not isinstance(attr.ctx, ast.Load):
return self.generic_visit(attr)
source_explanation, source_result = self.visit(attr.value)
explanation = "%s.%s" % (source_explanation, attr.attr)
source = "__exprinfo_expr.%s" % (attr.attr,)
co = self._compile(source)
try:
result = self.frame.eval(co, __exprinfo_expr=source_result)
except Exception:
raise Failure(explanation)
explanation = "%s\n{%s = %s.%s\n}" % (self.frame.repr(result),
self.frame.repr(result),
source_explanation, attr.attr)
# Check if the attr is from an instance.
source = "%r in getattr(__exprinfo_expr, '__dict__', {})"
source = source % (attr.attr,)
co = self._compile(source)
try:
from_instance = self.frame.eval(co, __exprinfo_expr=source_result)
except Exception:
from_instance = True
if from_instance:
rep = self.frame.repr(result)
pattern = "%s\n{%s = %s\n}"
explanation = pattern % (rep, rep, explanation)
return explanation, result
def visit_Assert(self, assrt):
test_explanation, test_result = self.visit(assrt.test)
if test_explanation.startswith("False\n{False =") and \
test_explanation.endswith("\n"):
test_explanation = test_explanation[15:-2]
explanation = "assert %s" % (test_explanation,)
if not test_result:
try:
raise BuiltinAssertionError
except Exception:
raise Failure(explanation)
return explanation, test_result
def visit_Assign(self, assign):
value_explanation, value_result = self.visit(assign.value)
explanation = "... = %s" % (value_explanation,)
name = ast.Name("__exprinfo_expr", ast.Load(),
lineno=assign.value.lineno,
col_offset=assign.value.col_offset)
new_assign = ast.Assign(assign.targets, name, lineno=assign.lineno,
col_offset=assign.col_offset)
mod = ast.Module([new_assign])
co = self._compile(mod, "exec")
try:
self.frame.exec_(co, __exprinfo_expr=value_result)
except Exception:
raise Failure(explanation)
return explanation, value_result
py-1.5.2/py/_code/__init__.py 0000664 0001750 0001750 00000000056 13202663617 017120 0 ustar vagrant vagrant 0000000 0000000 """ python inspection/code generation API """
py-1.5.2/py/_code/_py2traceback.py 0000664 0001750 0001750 00000005315 13202663617 020075 0 ustar vagrant vagrant 0000000 0000000 # copied from python-2.7.3's traceback.py
# CHANGES:
# - some_str is replaced, trying to create unicode strings
#
import types
def format_exception_only(etype, value):
"""Format the exception part of a traceback.
The arguments are the exception type and value such as given by
sys.last_type and sys.last_value. The return value is a list of
strings, each ending in a newline.
Normally, the list contains a single string; however, for
SyntaxError exceptions, it contains several lines that (when
printed) display detailed information about where the syntax
error occurred.
The message indicating which exception occurred is always the last
string in the list.
"""
# An instance should not have a meaningful value parameter, but
# sometimes does, particularly for string exceptions, such as
# >>> raise string1, string2 # deprecated
#
# Clear these out first because issubtype(string1, SyntaxError)
# would throw another exception and mask the original problem.
if (isinstance(etype, BaseException) or
isinstance(etype, types.InstanceType) or
etype is None or type(etype) is str):
return [_format_final_exc_line(etype, value)]
stype = etype.__name__
if not issubclass(etype, SyntaxError):
return [_format_final_exc_line(stype, value)]
# It was a syntax error; show exactly where the problem was found.
lines = []
try:
msg, (filename, lineno, offset, badline) = value.args
except Exception:
pass
else:
filename = filename or ""
lines.append(' File "%s", line %d\n' % (filename, lineno))
if badline is not None:
lines.append(' %s\n' % badline.strip())
if offset is not None:
caretspace = badline.rstrip('\n')[:offset].lstrip()
# non-space whitespace (likes tabs) must be kept for alignment
caretspace = ((c.isspace() and c or ' ') for c in caretspace)
# only three spaces to account for offset1 == pos 0
lines.append(' %s^\n' % ''.join(caretspace))
value = msg
lines.append(_format_final_exc_line(stype, value))
return lines
def _format_final_exc_line(etype, value):
"""Return a list of a single line -- normal case for format_exception_only"""
valuestr = _some_str(value)
if value is None or not valuestr:
line = "%s\n" % etype
else:
line = "%s: %s\n" % (etype, valuestr)
return line
def _some_str(value):
try:
return unicode(value)
except Exception:
try:
return str(value)
except Exception:
pass
return '' % type(value).__name__
py-1.5.2/py/_code/assertion.py 0000664 0001750 0001750 00000006146 13202663755 017401 0 ustar vagrant vagrant 0000000 0000000 import sys
import py
BuiltinAssertionError = py.builtin.builtins.AssertionError
_reprcompare = None # if set, will be called by assert reinterp for comparison ops
def _format_explanation(explanation):
"""This formats an explanation
Normally all embedded newlines are escaped, however there are
three exceptions: \n{, \n} and \n~. The first two are intended
cover nested explanations, see function and attribute explanations
for examples (.visit_Call(), visit_Attribute()). The last one is
for when one explanation needs to span multiple lines, e.g. when
displaying diffs.
"""
raw_lines = (explanation or '').split('\n')
# escape newlines not followed by {, } and ~
lines = [raw_lines[0]]
for l in raw_lines[1:]:
if l.startswith('{') or l.startswith('}') or l.startswith('~'):
lines.append(l)
else:
lines[-1] += '\\n' + l
result = lines[:1]
stack = [0]
stackcnt = [0]
for line in lines[1:]:
if line.startswith('{'):
if stackcnt[-1]:
s = 'and '
else:
s = 'where '
stack.append(len(result))
stackcnt[-1] += 1
stackcnt.append(0)
result.append(' +' + ' '*(len(stack)-1) + s + line[1:])
elif line.startswith('}'):
assert line.startswith('}')
stack.pop()
stackcnt.pop()
result[stack[-1]] += line[1:]
else:
assert line.startswith('~')
result.append(' '*len(stack) + line[1:])
assert len(stack) == 1
return '\n'.join(result)
class AssertionError(BuiltinAssertionError):
def __init__(self, *args):
BuiltinAssertionError.__init__(self, *args)
if args:
try:
self.msg = str(args[0])
except py.builtin._sysex:
raise
except:
self.msg = "<[broken __repr__] %s at %0xd>" %(
args[0].__class__, id(args[0]))
else:
f = py.code.Frame(sys._getframe(1))
try:
source = f.code.fullsource
if source is not None:
try:
source = source.getstatement(f.lineno, assertion=True)
except IndexError:
source = None
else:
source = str(source.deindent()).strip()
except py.error.ENOENT:
source = None
# this can also occur during reinterpretation, when the
# co_filename is set to "".
if source:
self.msg = reinterpret(source, f, should_fail=True)
else:
self.msg = ""
if not self.args:
self.args = (self.msg,)
if sys.version_info > (3, 0):
AssertionError.__module__ = "builtins"
reinterpret_old = "old reinterpretation not available for py3"
else:
from py._code._assertionold import interpret as reinterpret_old
from py._code._assertionnew import interpret as reinterpret
py-1.5.2/py/_std.py 0000664 0001750 0001750 00000001167 13202663755 015250 0 ustar vagrant vagrant 0000000 0000000 import sys
import warnings
class PyStdIsDeprecatedWarning(DeprecationWarning):
pass
class Std(object):
""" makes top-level python modules available as an attribute,
importing them on first access.
"""
def __init__(self):
self.__dict__ = sys.modules
def __getattr__(self, name):
warnings.warn("py.std is deprecated, plase import %s directly" % name,
category=PyStdIsDeprecatedWarning)
try:
m = __import__(name)
except ImportError:
raise AttributeError("py.std: could not import %s" % name)
return m
std = Std()
py-1.5.2/py/_xmlgen.py 0000664 0001750 0001750 00000020254 13202663617 015743 0 ustar vagrant vagrant 0000000 0000000 """
module for generating and serializing xml and html structures
by using simple python objects.
(c) holger krekel, holger at merlinux eu. 2009
"""
import sys, re
if sys.version_info >= (3,0):
def u(s):
return s
def unicode(x, errors=None):
if hasattr(x, '__unicode__'):
return x.__unicode__()
return str(x)
else:
def u(s):
return unicode(s)
unicode = unicode
class NamespaceMetaclass(type):
def __getattr__(self, name):
if name[:1] == '_':
raise AttributeError(name)
if self == Namespace:
raise ValueError("Namespace class is abstract")
tagspec = self.__tagspec__
if tagspec is not None and name not in tagspec:
raise AttributeError(name)
classattr = {}
if self.__stickyname__:
classattr['xmlname'] = name
cls = type(name, (self.__tagclass__,), classattr)
setattr(self, name, cls)
return cls
class Tag(list):
class Attr(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __init__(self, *args, **kwargs):
super(Tag, self).__init__(args)
self.attr = self.Attr(**kwargs)
def __unicode__(self):
return self.unicode(indent=0)
__str__ = __unicode__
def unicode(self, indent=2):
l = []
SimpleUnicodeVisitor(l.append, indent).visit(self)
return u("").join(l)
def __repr__(self):
name = self.__class__.__name__
return "<%r tag object %d>" % (name, id(self))
Namespace = NamespaceMetaclass('Namespace', (object, ), {
'__tagspec__': None,
'__tagclass__': Tag,
'__stickyname__': False,
})
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 (
'a,abbr,acronym,address,applet,area,article,aside,audio,b,'
'base,basefont,bdi,bdo,big,blink,blockquote,body,br,button,'
'canvas,caption,center,cite,code,col,colgroup,command,comment,'
'datalist,dd,del,details,dfn,dir,div,dl,dt,em,embed,'
'fieldset,figcaption,figure,footer,font,form,frame,frameset,h1,'
'h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,img,input,'
'ins,isindex,kbd,keygen,label,legend,li,link,listing,map,mark,'
'marquee,menu,meta,meter,multicol,nav,nobr,noembed,noframes,'
'noscript,object,ol,optgroup,option,output,p,param,pre,progress,'
'q,rp,rt,ruby,s,samp,script,section,select,small,source,span,'
'strike,strong,style,sub,summary,sup,table,tbody,td,textarea,'
'tfoot,th,thead,time,title,tr,track,tt,u,ul,xmp,var,video,wbr'
).split(',') if x])
class Style(object):
def __init__(self, **kw):
for x, y in kw.items():
x = x.replace('_', '-')
setattr(self, x, y)
class raw(object):
"""just a box that can contain a unicode string that will be
included directly in the output"""
def __init__(self, uniobj):
self.uniobj = uniobj
class SimpleUnicodeVisitor(object):
""" recursive visitor to write unicode. """
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. """
cls = node.__class__
try:
visitmethod = self.cache[cls]
except KeyError:
for subclass in cls.__mro__:
visitmethod = getattr(self, subclass.__name__, None)
if visitmethod is not None:
break
else:
visitmethod = self.__object
self.cache[cls] = visitmethod
visitmethod(node)
# the default fallback handler is marked private
# to avoid clashes with the tag name object
def __object(self, obj):
#self.write(obj)
self.write(escape(unicode(obj)))
def raw(self, obj):
self.write(obj.uniobj)
def list(self, obj):
assert id(obj) not in self.visited
self.visited[id(obj)] = 1
for elem in obj:
self.visit(elem)
def Tag(self, tag):
assert id(tag) not in self.visited
try:
tag.parent = self.parents[-1]
except IndexError:
tag.parent = None
self.visited[id(tag)] = 1
tagname = getattr(tag, 'xmlname', tag.__class__.__name__)
if self.curindent and not self._isinline(tagname):
self.write("\n" + u(' ') * self.curindent)
if tag:
self.curindent += self.indent
self.write(u('<%s%s>') % (tagname, self.attributes(tag)))
self.parents.append(tag)
for x in tag:
self.visit(x)
self.parents.pop()
self.write(u('%s>') % tagname)
self.curindent -= self.indent
else:
nameattr = tagname+self.attributes(tag)
if self._issingleton(tagname):
self.write(u('<%s/>') % (nameattr,))
else:
self.write(u('<%s>%s>') % (nameattr, tagname))
def attributes(self, tag):
# serialize attributes
attrlist = dir(tag.attr)
attrlist.sort()
l = []
for name in attrlist:
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]
if isinstance(value, raw):
insert = value.uniobj
else:
insert = escape(unicode(value))
return ' %s="%s"' % (name, insert)
def getstyle(self, tag):
""" return attribute list suitable for styling. """
try:
styledict = tag.style.__dict__
except AttributeError:
return []
else:
stylelist = [x+': ' + y for x,y in styledict.items()]
return [u(' style="%s"') % u('; ').join(stylelist)]
def _issingleton(self, tagname):
"""can (and will) be overridden in subclasses"""
return self.shortempty
def _isinline(self, tagname):
"""can (and will) be overridden in subclasses"""
return False
class HtmlVisitor(SimpleUnicodeVisitor):
single = dict([(x, 1) for x in
('br,img,area,param,col,hr,meta,link,base,'
'input,frame').split(',')])
inline = dict([(x, 1) for x in
('a abbr acronym b basefont bdo big br cite code dfn em font '
'i img input kbd label q s samp select small span strike '
'strong sub sup textarea tt u var'.split(' '))])
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)
def _issingleton(self, tagname):
return tagname in self.single
def _isinline(self, tagname):
return tagname in self.inline
class _escape:
def __init__(self):
self.escape = {
u('"') : u('"'), u('<') : u('<'), u('>') : u('>'),
u('&') : u('&'), u("'") : u('''),
}
self.charef_rex = re.compile(u("|").join(self.escape.keys()))
def _replacer(self, match):
return self.escape[match.group(0)]
def __call__(self, ustring):
""" xml-escape the given unicode string. """
try:
ustring = unicode(ustring)
except UnicodeDecodeError:
ustring = unicode(ustring, 'utf-8', errors='replace')
return self.charef_rex.sub(self._replacer, ustring)
escape = _escape()
py-1.5.2/py/test.py 0000664 0001750 0001750 00000000336 13202663617 015270 0 ustar vagrant vagrant 0000000 0000000 import sys
if __name__ == '__main__':
import pytest
sys.exit(pytest.main())
else:
import sys, pytest
sys.modules['py.test'] = pytest
# for more API entry points see the 'tests' definition
# in __init__.py
py-1.5.2/py/_error.py 0000664 0001750 0001750 00000005545 13202663755 015613 0 ustar vagrant vagrant 0000000 0000000 """
create errno-specific classes for IO or os calls.
"""
from types import ModuleType
import sys, os, errno
class Error(EnvironmentError):
def __repr__(self):
return "%s.%s %r: %s " %(self.__class__.__module__,
self.__class__.__name__,
self.__class__.__doc__,
" ".join(map(str, self.args)),
#repr(self.args)
)
def __str__(self):
s = "[%s]: %s" %(self.__class__.__doc__,
" ".join(map(str, self.args)),
)
return s
_winerrnomap = {
2: errno.ENOENT,
3: errno.ENOENT,
17: errno.EEXIST,
18: errno.EXDEV,
13: errno.EBUSY, # empty cd drive, but ENOMEDIUM seems unavailiable
22: errno.ENOTDIR,
20: errno.ENOTDIR,
267: errno.ENOTDIR,
5: errno.EACCES, # anything better?
}
class ErrorMaker(ModuleType):
""" lazily provides Exception classes for each possible POSIX errno
(as defined per the 'errno' module). All such instances
subclass EnvironmentError.
"""
Error = Error
_errno2class = {}
def __getattr__(self, name):
if name[0] == "_":
raise AttributeError(name)
eno = getattr(errno, name)
cls = self._geterrnoclass(eno)
setattr(self, name, cls)
return cls
def _geterrnoclass(self, eno):
try:
return self._errno2class[eno]
except KeyError:
clsname = errno.errorcode.get(eno, "UnknownErrno%d" %(eno,))
errorcls = type(Error)(clsname, (Error,),
{'__module__':'py.error',
'__doc__': os.strerror(eno)})
self._errno2class[eno] = errorcls
return errorcls
def checked_call(self, func, *args, **kwargs):
""" call a function and raise an errno-exception if applicable. """
__tracebackhide__ = True
try:
return func(*args, **kwargs)
except self.Error:
raise
except (OSError, EnvironmentError):
cls, value, tb = sys.exc_info()
if not hasattr(value, 'errno'):
raise
__tracebackhide__ = False
errno = value.errno
try:
if not isinstance(value, WindowsError):
raise NameError
except NameError:
# we are not on Windows, or we got a proper OSError
cls = self._geterrnoclass(errno)
else:
try:
cls = self._geterrnoclass(_winerrnomap[errno])
except KeyError:
raise value
raise cls("%s%r" % (func.__name__, args))
__tracebackhide__ = True
error = ErrorMaker('py.error')
sys.modules[error.__name__] = error py-1.5.2/py/__metainfo.py 0000664 0001750 0001750 00000000067 13202663617 016412 0 ustar vagrant vagrant 0000000 0000000 import py
pydir = py.path.local(py.__file__).dirpath()
py-1.5.2/py/_builtin.py 0000664 0001750 0001750 00000014571 13202663617 016124 0 ustar vagrant vagrant 0000000 0000000 import sys
try:
reversed = reversed
except NameError:
def reversed(sequence):
"""reversed(sequence) -> reverse iterator over values of the sequence
Return a reverse iterator
"""
if hasattr(sequence, '__reversed__'):
return sequence.__reversed__()
if not hasattr(sequence, '__getitem__'):
raise TypeError("argument to reversed() must be a sequence")
return reversed_iterator(sequence)
class reversed_iterator(object):
def __init__(self, seq):
self.seq = seq
self.remaining = len(seq)
def __iter__(self):
return self
def next(self):
i = self.remaining
if i > 0:
i -= 1
item = self.seq[i]
self.remaining = i
return item
raise StopIteration
def __length_hint__(self):
return self.remaining
try:
any = any
except NameError:
def any(iterable):
for x in iterable:
if x:
return True
return False
try:
all = all
except NameError:
def all(iterable):
for x in iterable:
if not x:
return False
return True
try:
sorted = sorted
except NameError:
builtin_cmp = cmp # need to use cmp as keyword arg
def sorted(iterable, cmp=None, key=None, reverse=0):
use_cmp = None
if key is not None:
if cmp is None:
def use_cmp(x, y):
return builtin_cmp(x[0], y[0])
else:
def use_cmp(x, y):
return cmp(x[0], y[0])
l = [(key(element), element) for element in iterable]
else:
if cmp is not None:
use_cmp = cmp
l = list(iterable)
if use_cmp is not None:
l.sort(use_cmp)
else:
l.sort()
if reverse:
l.reverse()
if key is not None:
return [element for (_, element) in l]
return l
try:
set, frozenset = set, frozenset
except NameError:
from sets import set, frozenset
# pass through
enumerate = enumerate
try:
BaseException = BaseException
except NameError:
BaseException = Exception
try:
GeneratorExit = GeneratorExit
except NameError:
class GeneratorExit(Exception):
""" This exception is never raised, it is there to make it possible to
write code compatible with CPython 2.5 even in lower CPython
versions."""
pass
GeneratorExit.__module__ = 'exceptions'
_sysex = (KeyboardInterrupt, SystemExit, MemoryError, GeneratorExit)
try:
callable = callable
except NameError:
def callable(obj):
return hasattr(obj, "__call__")
if sys.version_info >= (3, 0):
exec ("print_ = print ; exec_=exec")
import builtins
# some backward compatibility helpers
_basestring = str
def _totext(obj, encoding=None, errors=None):
if isinstance(obj, bytes):
if errors is None:
obj = obj.decode(encoding)
else:
obj = obj.decode(encoding, errors)
elif not isinstance(obj, str):
obj = str(obj)
return obj
def _isbytes(x):
return isinstance(x, bytes)
def _istext(x):
return isinstance(x, str)
text = str
bytes = bytes
def _getimself(function):
return getattr(function, '__self__', None)
def _getfuncdict(function):
return getattr(function, "__dict__", None)
def _getcode(function):
return getattr(function, "__code__", None)
def execfile(fn, globs=None, locs=None):
if globs is None:
back = sys._getframe(1)
globs = back.f_globals
locs = back.f_locals
del back
elif locs is None:
locs = globs
fp = open(fn, "r")
try:
source = fp.read()
finally:
fp.close()
co = compile(source, fn, "exec", dont_inherit=True)
exec_(co, globs, locs)
else:
import __builtin__ as builtins
_totext = unicode
_basestring = basestring
text = unicode
bytes = str
execfile = execfile
callable = callable
def _isbytes(x):
return isinstance(x, str)
def _istext(x):
return isinstance(x, unicode)
def _getimself(function):
return getattr(function, 'im_self', None)
def _getfuncdict(function):
return getattr(function, "__dict__", None)
def _getcode(function):
try:
return getattr(function, "__code__")
except AttributeError:
return getattr(function, "func_code", None)
def print_(*args, **kwargs):
""" minimal backport of py3k print statement. """
sep = ' '
if 'sep' in kwargs:
sep = kwargs.pop('sep')
end = '\n'
if 'end' in kwargs:
end = kwargs.pop('end')
file = 'file' in kwargs and kwargs.pop('file') or sys.stdout
if kwargs:
args = ", ".join([str(x) for x in kwargs])
raise TypeError("invalid keyword arguments: %s" % args)
at_start = True
for x in args:
if not at_start:
file.write(sep)
file.write(str(x))
at_start = False
file.write(end)
def exec_(obj, globals=None, locals=None):
""" minimal backport of py3k exec statement. """
__tracebackhide__ = True
if globals is None:
frame = sys._getframe(1)
globals = frame.f_globals
if locals is None:
locals = frame.f_locals
elif locals is None:
locals = globals
exec2(obj, globals, locals)
if sys.version_info >= (3, 0):
def _reraise(cls, val, tb):
__tracebackhide__ = True
assert hasattr(val, '__traceback__')
raise cls.with_traceback(val, tb)
else:
exec ("""
def _reraise(cls, val, tb):
__tracebackhide__ = True
raise cls, val, tb
def exec2(obj, globals, locals):
__tracebackhide__ = True
exec obj in globals, locals
""")
def _tryimport(*names):
""" return the first successfully imported module. """
assert names
for name in names:
try:
__import__(name)
except ImportError:
excinfo = sys.exc_info()
else:
return sys.modules[name]
_reraise(*excinfo)
py-1.5.2/py/__init__.py 0000664 0001750 0001750 00000013537 13203150646 016051 0 ustar vagrant vagrant 0000000 0000000 """
pylib: rapid testing and development utils
this module uses apipkg.py for lazy-loading sub modules
and classes. The initpkg-dictionary below specifies
name->value mappings where value can be another namespace
dictionary or an import path.
(c) Holger Krekel and others, 2004-2014
"""
__version__ = '1.5.2'
try:
from py._vendored_packages import apipkg
lib_not_mangled_by_packagers = True
vendor_prefix = '._vendored_packages.'
except ImportError:
import apipkg
lib_not_mangled_by_packagers = False
vendor_prefix = ''
# so that py.error.* instances are picklable
import sys
apipkg.initpkg(__name__, attr={'_apipkg': apipkg}, exportdefs={
# access to all standard lib modules
'std': '._std:std',
# access to all posix errno's as classes
'error': '._error:error',
'_pydir' : '.__metainfo:pydir',
'version': 'py:__version__', # backward compatibility
# pytest-2.0 has a flat namespace, we use alias modules
# to keep old references compatible
'test' : 'pytest',
# hook into the top-level standard library
'process' : {
'__doc__' : '._process:__doc__',
'cmdexec' : '._process.cmdexec:cmdexec',
'kill' : '._process.killproc:kill',
'ForkedFunc' : '._process.forkedfunc:ForkedFunc',
},
'apipkg' : {
'initpkg' : vendor_prefix + 'apipkg:initpkg',
'ApiModule' : vendor_prefix + 'apipkg:ApiModule',
},
'iniconfig' : {
'IniConfig' : vendor_prefix + 'iniconfig:IniConfig',
'ParseError' : vendor_prefix + 'iniconfig:ParseError',
},
'path' : {
'__doc__' : '._path:__doc__',
'svnwc' : '._path.svnwc:SvnWCCommandPath',
'svnurl' : '._path.svnurl:SvnCommandPath',
'local' : '._path.local:LocalPath',
'SvnAuth' : '._path.svnwc:SvnAuth',
},
# python inspection/code-generation API
'code' : {
'__doc__' : '._code:__doc__',
'compile' : '._code.source:compile_',
'Source' : '._code.source:Source',
'Code' : '._code.code:Code',
'Frame' : '._code.code:Frame',
'ExceptionInfo' : '._code.code:ExceptionInfo',
'Traceback' : '._code.code:Traceback',
'getfslineno' : '._code.source:getfslineno',
'getrawcode' : '._code.code:getrawcode',
'patch_builtins' : '._code.code:patch_builtins',
'unpatch_builtins' : '._code.code:unpatch_builtins',
'_AssertionError' : '._code.assertion:AssertionError',
'_reinterpret_old' : '._code.assertion:reinterpret_old',
'_reinterpret' : '._code.assertion:reinterpret',
'_reprcompare' : '._code.assertion:_reprcompare',
'_format_explanation' : '._code.assertion:_format_explanation',
},
# backports and additions of builtins
'builtin' : {
'__doc__' : '._builtin:__doc__',
'enumerate' : '._builtin:enumerate',
'reversed' : '._builtin:reversed',
'sorted' : '._builtin:sorted',
'any' : '._builtin:any',
'all' : '._builtin:all',
'set' : '._builtin:set',
'frozenset' : '._builtin:frozenset',
'BaseException' : '._builtin:BaseException',
'GeneratorExit' : '._builtin:GeneratorExit',
'_sysex' : '._builtin:_sysex',
'print_' : '._builtin:print_',
'_reraise' : '._builtin:_reraise',
'_tryimport' : '._builtin:_tryimport',
'exec_' : '._builtin:exec_',
'_basestring' : '._builtin:_basestring',
'_totext' : '._builtin:_totext',
'_isbytes' : '._builtin:_isbytes',
'_istext' : '._builtin:_istext',
'_getimself' : '._builtin:_getimself',
'_getfuncdict' : '._builtin:_getfuncdict',
'_getcode' : '._builtin:_getcode',
'builtins' : '._builtin:builtins',
'execfile' : '._builtin:execfile',
'callable' : '._builtin:callable',
'bytes' : '._builtin:bytes',
'text' : '._builtin:text',
},
# input-output helping
'io' : {
'__doc__' : '._io:__doc__',
'dupfile' : '._io.capture:dupfile',
'TextIO' : '._io.capture:TextIO',
'BytesIO' : '._io.capture:BytesIO',
'FDCapture' : '._io.capture:FDCapture',
'StdCapture' : '._io.capture:StdCapture',
'StdCaptureFD' : '._io.capture:StdCaptureFD',
'TerminalWriter' : '._io.terminalwriter:TerminalWriter',
'ansi_print' : '._io.terminalwriter:ansi_print',
'get_terminal_width' : '._io.terminalwriter:get_terminal_width',
'saferepr' : '._io.saferepr:saferepr',
},
# small and mean xml/html generation
'xml' : {
'__doc__' : '._xmlgen:__doc__',
'html' : '._xmlgen:html',
'Tag' : '._xmlgen:Tag',
'raw' : '._xmlgen:raw',
'Namespace' : '._xmlgen:Namespace',
'escape' : '._xmlgen:escape',
},
'log' : {
# logging API ('producers' and 'consumers' connected via keywords)
'__doc__' : '._log:__doc__',
'_apiwarn' : '._log.warning:_apiwarn',
'Producer' : '._log.log:Producer',
'setconsumer' : '._log.log:setconsumer',
'_setstate' : '._log.log:setstate',
'_getstate' : '._log.log:getstate',
'Path' : '._log.log:Path',
'STDOUT' : '._log.log:STDOUT',
'STDERR' : '._log.log:STDERR',
'Syslog' : '._log.log:Syslog',
},
})
py-1.5.2/py/_io/ 0000775 0001750 0001750 00000000000 13203150701 014465 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/py/_io/capture.py 0000664 0001750 0001750 00000026570 13202663617 016532 0 ustar vagrant vagrant 0000000 0000000 import os
import sys
import py
import tempfile
try:
from io import StringIO
except ImportError:
from StringIO import StringIO
if sys.version_info < (3,0):
class TextIO(StringIO):
def write(self, data):
if not isinstance(data, unicode):
data = unicode(data, getattr(self, '_encoding', 'UTF-8'), 'replace')
StringIO.write(self, data)
else:
TextIO = StringIO
try:
from io import BytesIO
except ImportError:
class BytesIO(StringIO):
def write(self, data):
if isinstance(data, unicode):
raise TypeError("not a byte value: %r" %(data,))
StringIO.write(self, data)
patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'}
class FDCapture:
""" Capture IO to/from a given os-level filedescriptor. """
def __init__(self, targetfd, tmpfile=None, now=True, patchsys=False):
""" save targetfd descriptor, and open a new
temporary file there. If no tmpfile is
specified a tempfile.Tempfile() will be opened
in text mode.
"""
self.targetfd = targetfd
if tmpfile is None and targetfd != 0:
f = tempfile.TemporaryFile('wb+')
tmpfile = dupfile(f, encoding="UTF-8")
f.close()
self.tmpfile = tmpfile
self._savefd = os.dup(self.targetfd)
if patchsys:
self._oldsys = getattr(sys, patchsysdict[targetfd])
if now:
self.start()
def start(self):
try:
os.fstat(self._savefd)
except OSError:
raise ValueError("saved filedescriptor not valid, "
"did you call start() twice?")
if self.targetfd == 0 and not self.tmpfile:
fd = os.open(devnullpath, os.O_RDONLY)
os.dup2(fd, 0)
os.close(fd)
if hasattr(self, '_oldsys'):
setattr(sys, patchsysdict[self.targetfd], DontReadFromInput())
else:
os.dup2(self.tmpfile.fileno(), self.targetfd)
if hasattr(self, '_oldsys'):
setattr(sys, patchsysdict[self.targetfd], self.tmpfile)
def done(self):
""" unpatch and clean up, returns the self.tmpfile (file object)
"""
os.dup2(self._savefd, self.targetfd)
os.close(self._savefd)
if self.targetfd != 0:
self.tmpfile.seek(0)
if hasattr(self, '_oldsys'):
setattr(sys, patchsysdict[self.targetfd], self._oldsys)
return self.tmpfile
def writeorg(self, data):
""" write a string to the original file descriptor
"""
tempfp = tempfile.TemporaryFile()
try:
os.dup2(self._savefd, tempfp.fileno())
tempfp.write(data)
finally:
tempfp.close()
def dupfile(f, mode=None, buffering=0, raising=False, encoding=None):
""" return a new open file object that's a duplicate of f
mode is duplicated if not given, 'buffering' controls
buffer size (defaulting to no buffering) and 'raising'
defines whether an exception is raised when an incompatible
file object is passed in (if raising is False, the file
object itself will be returned)
"""
try:
fd = f.fileno()
mode = mode or f.mode
except AttributeError:
if raising:
raise
return f
newfd = os.dup(fd)
if sys.version_info >= (3,0):
if encoding is not None:
mode = mode.replace("b", "")
buffering = True
return os.fdopen(newfd, mode, buffering, encoding, closefd=True)
else:
f = os.fdopen(newfd, mode, buffering)
if encoding is not None:
return EncodedFile(f, encoding)
return f
class EncodedFile(object):
def __init__(self, _stream, encoding):
self._stream = _stream
self.encoding = encoding
def write(self, obj):
if isinstance(obj, unicode):
obj = obj.encode(self.encoding)
elif isinstance(obj, str):
pass
else:
obj = str(obj)
self._stream.write(obj)
def writelines(self, linelist):
data = ''.join(linelist)
self.write(data)
def __getattr__(self, name):
return getattr(self._stream, name)
class Capture(object):
def call(cls, func, *args, **kwargs):
""" return a (res, out, err) tuple where
out and err represent the output/error output
during function execution.
call the given function with args/kwargs
and capture output/error during its execution.
"""
so = cls()
try:
res = func(*args, **kwargs)
finally:
out, err = so.reset()
return res, out, err
call = classmethod(call)
def reset(self):
""" reset sys.stdout/stderr and return captured output as strings. """
if hasattr(self, '_reset'):
raise ValueError("was already reset")
self._reset = True
outfile, errfile = self.done(save=False)
out, err = "", ""
if outfile and not outfile.closed:
out = outfile.read()
outfile.close()
if errfile and errfile != outfile and not errfile.closed:
err = errfile.read()
errfile.close()
return out, err
def suspend(self):
""" return current snapshot captures, memorize tempfiles. """
outerr = self.readouterr()
outfile, errfile = self.done()
return outerr
class StdCaptureFD(Capture):
""" This class allows to capture writes to FD1 and FD2
and may connect a NULL file to FD0 (and prevent
reads from sys.stdin). If any of the 0,1,2 file descriptors
is invalid it will not be captured.
"""
def __init__(self, out=True, err=True, mixed=False,
in_=True, patchsys=True, now=True):
self._options = {
"out": out,
"err": err,
"mixed": mixed,
"in_": in_,
"patchsys": patchsys,
"now": now,
}
self._save()
if now:
self.startall()
def _save(self):
in_ = self._options['in_']
out = self._options['out']
err = self._options['err']
mixed = self._options['mixed']
patchsys = self._options['patchsys']
if in_:
try:
self.in_ = FDCapture(0, tmpfile=None, now=False,
patchsys=patchsys)
except OSError:
pass
if out:
tmpfile = None
if hasattr(out, 'write'):
tmpfile = out
try:
self.out = FDCapture(1, tmpfile=tmpfile,
now=False, patchsys=patchsys)
self._options['out'] = self.out.tmpfile
except OSError:
pass
if err:
if out and mixed:
tmpfile = self.out.tmpfile
elif hasattr(err, 'write'):
tmpfile = err
else:
tmpfile = None
try:
self.err = FDCapture(2, tmpfile=tmpfile,
now=False, patchsys=patchsys)
self._options['err'] = self.err.tmpfile
except OSError:
pass
def startall(self):
if hasattr(self, 'in_'):
self.in_.start()
if hasattr(self, 'out'):
self.out.start()
if hasattr(self, 'err'):
self.err.start()
def resume(self):
""" resume capturing with original temp files. """
self.startall()
def done(self, save=True):
""" return (outfile, errfile) and stop capturing. """
outfile = errfile = None
if hasattr(self, 'out') and not self.out.tmpfile.closed:
outfile = self.out.done()
if hasattr(self, 'err') and not self.err.tmpfile.closed:
errfile = self.err.done()
if hasattr(self, 'in_'):
tmpfile = self.in_.done()
if save:
self._save()
return outfile, errfile
def readouterr(self):
""" return snapshot value of stdout/stderr capturings. """
if hasattr(self, "out"):
out = self._readsnapshot(self.out.tmpfile)
else:
out = ""
if hasattr(self, "err"):
err = self._readsnapshot(self.err.tmpfile)
else:
err = ""
return [out, err]
def _readsnapshot(self, f):
f.seek(0)
res = f.read()
enc = getattr(f, "encoding", None)
if enc:
res = py.builtin._totext(res, enc, "replace")
f.truncate(0)
f.seek(0)
return res
class StdCapture(Capture):
""" This class allows to capture writes to sys.stdout|stderr "in-memory"
and will raise errors on tries to read from sys.stdin. It only
modifies sys.stdout|stderr|stdin attributes and does not
touch underlying File Descriptors (use StdCaptureFD for that).
"""
def __init__(self, out=True, err=True, in_=True, mixed=False, now=True):
self._oldout = sys.stdout
self._olderr = sys.stderr
self._oldin = sys.stdin
if out and not hasattr(out, 'file'):
out = TextIO()
self.out = out
if err:
if mixed:
err = out
elif not hasattr(err, 'write'):
err = TextIO()
self.err = err
self.in_ = in_
if now:
self.startall()
def startall(self):
if self.out:
sys.stdout = self.out
if self.err:
sys.stderr = self.err
if self.in_:
sys.stdin = self.in_ = DontReadFromInput()
def done(self, save=True):
""" return (outfile, errfile) and stop capturing. """
outfile = errfile = None
if self.out and not self.out.closed:
sys.stdout = self._oldout
outfile = self.out
outfile.seek(0)
if self.err and not self.err.closed:
sys.stderr = self._olderr
errfile = self.err
errfile.seek(0)
if self.in_:
sys.stdin = self._oldin
return outfile, errfile
def resume(self):
""" resume capturing with original temp files. """
self.startall()
def readouterr(self):
""" return snapshot value of stdout/stderr capturings. """
out = err = ""
if self.out:
out = self.out.getvalue()
self.out.truncate(0)
self.out.seek(0)
if self.err:
err = self.err.getvalue()
self.err.truncate(0)
self.err.seek(0)
return out, err
class DontReadFromInput:
"""Temporary stub class. Ideally when stdin is accessed, the
capturing should be turned off, with possibly all data captured
so far sent to the screen. This should be configurable, though,
because in automated test runs it is better to crash than
hang indefinitely.
"""
def read(self, *args):
raise IOError("reading from stdin while output is captured")
readline = read
readlines = read
__iter__ = read
def fileno(self):
raise ValueError("redirected Stdin is pseudofile, has no fileno()")
def isatty(self):
return False
def close(self):
pass
try:
devnullpath = os.devnull
except AttributeError:
if os.name == 'nt':
devnullpath = 'NUL'
else:
devnullpath = '/dev/null'
py-1.5.2/py/_io/terminalwriter.py 0000664 0001750 0001750 00000032256 13202663755 020140 0 ustar vagrant vagrant 0000000 0000000 """
Helper functions for writing to terminals and files.
"""
import sys, os
import py
py3k = sys.version_info[0] >= 3
from py.builtin import text, bytes
win32_and_ctypes = False
colorama = None
if sys.platform == "win32":
try:
import colorama
except ImportError:
try:
import ctypes
win32_and_ctypes = True
except ImportError:
pass
def _getdimensions():
import termios,fcntl,struct
call = fcntl.ioctl(1,termios.TIOCGWINSZ,"\000"*8)
height,width = struct.unpack( "hhhh", call ) [:2]
return height, width
def get_terminal_width():
width = 0
try:
_, width = _getdimensions()
except py.builtin._sysex:
raise
except:
# pass to fallback below
pass
if width == 0:
# FALLBACK:
# * some exception happened
# * or this is emacs terminal which reports (0,0)
width = int(os.environ.get('COLUMNS', 80))
# XXX the windows getdimensions may be bogus, let's sanify a bit
if width < 40:
width = 80
return width
terminal_width = get_terminal_width()
# XXX unify with _escaped func below
def ansi_print(text, esc, file=None, newline=True, flush=False):
if file is None:
file = sys.stderr
text = text.rstrip()
if esc and not isinstance(esc, tuple):
esc = (esc,)
if esc and sys.platform != "win32" and file.isatty():
text = (''.join(['\x1b[%sm' % cod for cod in esc]) +
text +
'\x1b[0m') # ANSI color code "reset"
if newline:
text += '\n'
if esc and win32_and_ctypes and file.isatty():
if 1 in esc:
bold = True
esc = tuple([x for x in esc if x != 1])
else:
bold = False
esctable = {() : FOREGROUND_WHITE, # normal
(31,): FOREGROUND_RED, # red
(32,): FOREGROUND_GREEN, # green
(33,): FOREGROUND_GREEN|FOREGROUND_RED, # yellow
(34,): FOREGROUND_BLUE, # blue
(35,): FOREGROUND_BLUE|FOREGROUND_RED, # purple
(36,): FOREGROUND_BLUE|FOREGROUND_GREEN, # cyan
(37,): FOREGROUND_WHITE, # white
(39,): FOREGROUND_WHITE, # reset
}
attr = esctable.get(esc, FOREGROUND_WHITE)
if bold:
attr |= FOREGROUND_INTENSITY
STD_OUTPUT_HANDLE = -11
STD_ERROR_HANDLE = -12
if file is sys.stderr:
handle = GetStdHandle(STD_ERROR_HANDLE)
else:
handle = GetStdHandle(STD_OUTPUT_HANDLE)
oldcolors = GetConsoleInfo(handle).wAttributes
attr |= (oldcolors & 0x0f0)
SetConsoleTextAttribute(handle, attr)
while len(text) > 32768:
file.write(text[:32768])
text = text[32768:]
if text:
file.write(text)
SetConsoleTextAttribute(handle, oldcolors)
else:
file.write(text)
if flush:
file.flush()
def should_do_markup(file):
if os.environ.get('PY_COLORS') == '1':
return True
if os.environ.get('PY_COLORS') == '0':
return False
return hasattr(file, 'isatty') and file.isatty() \
and os.environ.get('TERM') != 'dumb' \
and not (sys.platform.startswith('java') and os._name == 'nt')
class TerminalWriter(object):
_esctable = dict(black=30, red=31, green=32, yellow=33,
blue=34, purple=35, cyan=36, white=37,
Black=40, Red=41, Green=42, Yellow=43,
Blue=44, Purple=45, Cyan=46, White=47,
bold=1, light=2, blink=5, invert=7)
# XXX deprecate stringio argument
def __init__(self, file=None, stringio=False, encoding=None):
if file is None:
if stringio:
self.stringio = file = py.io.TextIO()
else:
from sys import stdout as file
elif py.builtin.callable(file) and not (
hasattr(file, "write") and hasattr(file, "flush")):
file = WriteFile(file, encoding=encoding)
if hasattr(file, "isatty") and file.isatty() and colorama:
file = colorama.AnsiToWin32(file).stream
self.encoding = encoding or getattr(file, 'encoding', "utf-8")
self._file = file
self.hasmarkup = should_do_markup(file)
self._lastlen = 0
self._chars_on_current_line = 0
@property
def fullwidth(self):
if hasattr(self, '_terminal_width'):
return self._terminal_width
return get_terminal_width()
@fullwidth.setter
def fullwidth(self, value):
self._terminal_width = value
@property
def chars_on_current_line(self):
"""Return the number of characters written so far in the current line.
Please note that this count does not produce correct results after a reline() call,
see #164.
.. versionadded:: 1.5.0
:rtype: int
"""
return self._chars_on_current_line
def _escaped(self, text, esc):
if esc and self.hasmarkup:
text = (''.join(['\x1b[%sm' % cod for cod in esc]) +
text +'\x1b[0m')
return text
def markup(self, text, **kw):
esc = []
for name in kw:
if name not in self._esctable:
raise ValueError("unknown markup: %r" %(name,))
if kw[name]:
esc.append(self._esctable[name])
return self._escaped(text, tuple(esc))
def sep(self, sepchar, title=None, fullwidth=None, **kw):
if fullwidth is None:
fullwidth = self.fullwidth
# the goal is to have the line be as long as possible
# under the condition that len(line) <= fullwidth
if sys.platform == "win32":
# if we print in the last column on windows we are on a
# new line but there is no way to verify/neutralize this
# (we may not know the exact line width)
# so let's be defensive to avoid empty lines in the output
fullwidth -= 1
if title is not None:
# we want 2 + 2*len(fill) + len(title) <= fullwidth
# i.e. 2 + 2*len(sepchar)*N + len(title) <= fullwidth
# 2*len(sepchar)*N <= fullwidth - len(title) - 2
# N <= (fullwidth - len(title) - 2) // (2*len(sepchar))
N = (fullwidth - len(title) - 2) // (2*len(sepchar))
fill = sepchar * N
line = "%s %s %s" % (fill, title, fill)
else:
# we want len(sepchar)*N <= fullwidth
# i.e. N <= fullwidth // len(sepchar)
line = sepchar * (fullwidth // len(sepchar))
# in some situations there is room for an extra sepchar at the right,
# in particular if we consider that with a sepchar like "_ " the
# trailing space is not important at the end of the line
if len(line) + len(sepchar.rstrip()) <= fullwidth:
line += sepchar.rstrip()
self.line(line, **kw)
def write(self, msg, **kw):
if msg:
if not isinstance(msg, (bytes, text)):
msg = text(msg)
self._update_chars_on_current_line(msg)
if self.hasmarkup and kw:
markupmsg = self.markup(msg, **kw)
else:
markupmsg = msg
write_out(self._file, markupmsg)
def _update_chars_on_current_line(self, text):
fields = text.rsplit('\n', 1)
if '\n' in text:
self._chars_on_current_line = len(fields[-1])
else:
self._chars_on_current_line += len(fields[-1])
def line(self, s='', **kw):
self.write(s, **kw)
self._checkfill(s)
self.write('\n')
def reline(self, line, **kw):
if not self.hasmarkup:
raise ValueError("cannot use rewrite-line without terminal")
self.write(line, **kw)
self._checkfill(line)
self.write('\r')
self._lastlen = len(line)
def _checkfill(self, line):
diff2last = self._lastlen - len(line)
if diff2last > 0:
self.write(" " * diff2last)
class Win32ConsoleWriter(TerminalWriter):
def write(self, msg, **kw):
if msg:
if not isinstance(msg, (bytes, text)):
msg = text(msg)
self._update_chars_on_current_line(msg)
oldcolors = None
if self.hasmarkup and kw:
handle = GetStdHandle(STD_OUTPUT_HANDLE)
oldcolors = GetConsoleInfo(handle).wAttributes
default_bg = oldcolors & 0x00F0
attr = default_bg
if kw.pop('bold', False):
attr |= FOREGROUND_INTENSITY
if kw.pop('red', False):
attr |= FOREGROUND_RED
elif kw.pop('blue', False):
attr |= FOREGROUND_BLUE
elif kw.pop('green', False):
attr |= FOREGROUND_GREEN
elif kw.pop('yellow', False):
attr |= FOREGROUND_GREEN|FOREGROUND_RED
else:
attr |= oldcolors & 0x0007
SetConsoleTextAttribute(handle, attr)
write_out(self._file, msg)
if oldcolors:
SetConsoleTextAttribute(handle, oldcolors)
class WriteFile(object):
def __init__(self, writemethod, encoding=None):
self.encoding = encoding
self._writemethod = writemethod
def write(self, data):
if self.encoding:
data = data.encode(self.encoding, "replace")
self._writemethod(data)
def flush(self):
return
if win32_and_ctypes:
TerminalWriter = Win32ConsoleWriter
import ctypes
from ctypes import wintypes
# ctypes access to the Windows console
STD_OUTPUT_HANDLE = -11
STD_ERROR_HANDLE = -12
FOREGROUND_BLACK = 0x0000 # black text
FOREGROUND_BLUE = 0x0001 # text color contains blue.
FOREGROUND_GREEN = 0x0002 # text color contains green.
FOREGROUND_RED = 0x0004 # text color contains red.
FOREGROUND_WHITE = 0x0007
FOREGROUND_INTENSITY = 0x0008 # text color is intensified.
BACKGROUND_BLACK = 0x0000 # background color black
BACKGROUND_BLUE = 0x0010 # background color contains blue.
BACKGROUND_GREEN = 0x0020 # background color contains green.
BACKGROUND_RED = 0x0040 # background color contains red.
BACKGROUND_WHITE = 0x0070
BACKGROUND_INTENSITY = 0x0080 # background color is intensified.
SHORT = ctypes.c_short
class COORD(ctypes.Structure):
_fields_ = [('X', SHORT),
('Y', SHORT)]
class SMALL_RECT(ctypes.Structure):
_fields_ = [('Left', SHORT),
('Top', SHORT),
('Right', SHORT),
('Bottom', SHORT)]
class CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
_fields_ = [('dwSize', COORD),
('dwCursorPosition', COORD),
('wAttributes', wintypes.WORD),
('srWindow', SMALL_RECT),
('dwMaximumWindowSize', COORD)]
_GetStdHandle = ctypes.windll.kernel32.GetStdHandle
_GetStdHandle.argtypes = [wintypes.DWORD]
_GetStdHandle.restype = wintypes.HANDLE
def GetStdHandle(kind):
return _GetStdHandle(kind)
SetConsoleTextAttribute = ctypes.windll.kernel32.SetConsoleTextAttribute
SetConsoleTextAttribute.argtypes = [wintypes.HANDLE, wintypes.WORD]
SetConsoleTextAttribute.restype = wintypes.BOOL
_GetConsoleScreenBufferInfo = \
ctypes.windll.kernel32.GetConsoleScreenBufferInfo
_GetConsoleScreenBufferInfo.argtypes = [wintypes.HANDLE,
ctypes.POINTER(CONSOLE_SCREEN_BUFFER_INFO)]
_GetConsoleScreenBufferInfo.restype = wintypes.BOOL
def GetConsoleInfo(handle):
info = CONSOLE_SCREEN_BUFFER_INFO()
_GetConsoleScreenBufferInfo(handle, ctypes.byref(info))
return info
def _getdimensions():
handle = GetStdHandle(STD_OUTPUT_HANDLE)
info = GetConsoleInfo(handle)
# Substract one from the width, otherwise the cursor wraps
# and the ending \n causes an empty line to display.
return info.dwSize.Y, info.dwSize.X - 1
def write_out(fil, msg):
# XXX sometimes "msg" is of type bytes, sometimes text which
# complicates the situation. Should we try to enforce unicode?
try:
# on py27 and above writing out to sys.stdout with an encoding
# should usually work for unicode messages (if the encoding is
# capable of it)
fil.write(msg)
except UnicodeEncodeError:
# on py26 it might not work because stdout expects bytes
if fil.encoding:
try:
fil.write(msg.encode(fil.encoding))
except UnicodeEncodeError:
# it might still fail if the encoding is not capable
pass
else:
fil.flush()
return
# fallback: escape all unicode characters
msg = msg.encode("unicode-escape").decode("ascii")
fil.write(msg)
fil.flush()
py-1.5.2/py/_io/saferepr.py 0000664 0001750 0001750 00000004663 13202663617 016675 0 ustar vagrant vagrant 0000000 0000000 import py
import sys
builtin_repr = repr
reprlib = py.builtin._tryimport('repr', 'reprlib')
class SafeRepr(reprlib.Repr):
""" subclass of repr.Repr that limits the resulting size of repr()
and includes information on exceptions raised during the call.
"""
def repr(self, x):
return self._callhelper(reprlib.Repr.repr, self, x)
def repr_unicode(self, x, level):
# Strictly speaking wrong on narrow builds
def repr(u):
if "'" not in u:
return py.builtin._totext("'%s'") % u
elif '"' not in u:
return py.builtin._totext('"%s"') % u
else:
return py.builtin._totext("'%s'") % u.replace("'", r"\'")
s = repr(x[:self.maxstring])
if len(s) > self.maxstring:
i = max(0, (self.maxstring-3)//2)
j = max(0, self.maxstring-3-i)
s = repr(x[:i] + x[len(x)-j:])
s = s[:i] + '...' + s[len(s)-j:]
return s
def repr_instance(self, x, level):
return self._callhelper(builtin_repr, x)
def _callhelper(self, call, x, *args):
try:
# Try the vanilla repr and make sure that the result is a string
s = call(x, *args)
except py.builtin._sysex:
raise
except:
cls, e, tb = sys.exc_info()
exc_name = getattr(cls, '__name__', 'unknown')
try:
exc_info = str(e)
except py.builtin._sysex:
raise
except:
exc_info = 'unknown'
return '<[%s("%s") raised in repr()] %s object at 0x%x>' % (
exc_name, exc_info, x.__class__.__name__, id(x))
else:
if len(s) > self.maxsize:
i = max(0, (self.maxsize-3)//2)
j = max(0, self.maxsize-3-i)
s = s[:i] + '...' + s[len(s)-j:]
return s
def saferepr(obj, maxsize=240):
""" return a size-limited safe repr-string for the given object.
Failing __repr__ functions of user instances will be represented
with a short exception info and 'saferepr' generally takes
care to never raise exceptions itself. This function is a wrapper
around the Repr/reprlib functionality of the standard 2.6 lib.
"""
# review exception handling
srepr = SafeRepr()
srepr.maxstring = maxsize
srepr.maxsize = maxsize
srepr.maxother = 160
return srepr.repr(obj)
py-1.5.2/py/_io/__init__.py 0000664 0001750 0001750 00000000035 13202663617 016612 0 ustar vagrant vagrant 0000000 0000000 """ input/output helping """
py-1.5.2/py/_log/ 0000775 0001750 0001750 00000000000 13203150701 014637 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/py/_log/log.py 0000664 0001750 0001750 00000013563 13203150646 016012 0 ustar vagrant vagrant 0000000 0000000 """
basic logging functionality based on a producer/consumer scheme.
XXX implement this API: (maybe put it into slogger.py?)
log = Logger(
info=py.log.STDOUT,
debug=py.log.STDOUT,
command=None)
log.info("hello", "world")
log.command("hello", "world")
log = Logger(info=Logger(something=...),
debug=py.log.STDOUT,
command=None)
"""
import py
import sys
class Message(object):
def __init__(self, keywords, args):
self.keywords = keywords
self.args = args
def content(self):
return " ".join(map(str, self.args))
def prefix(self):
return "[%s] " % (":".join(self.keywords))
def __str__(self):
return self.prefix() + self.content()
class Producer(object):
""" (deprecated) Log producer API which sends messages to be logged
to a 'consumer' object, which then prints them to stdout,
stderr, files, etc. Used extensively by PyPy-1.1.
"""
Message = Message # to allow later customization
keywords2consumer = {}
def __init__(self, keywords, keywordmapper=None, **kw):
if hasattr(keywords, 'split'):
keywords = tuple(keywords.split())
self._keywords = keywords
if keywordmapper is None:
keywordmapper = default_keywordmapper
self._keywordmapper = keywordmapper
def __repr__(self):
return "" % ":".join(self._keywords)
def __getattr__(self, name):
if '_' in name:
raise AttributeError(name)
producer = self.__class__(self._keywords + (name,))
setattr(self, name, producer)
return producer
def __call__(self, *args):
""" write a message to the appropriate consumer(s) """
func = self._keywordmapper.getconsumer(self._keywords)
if func is not None:
func(self.Message(self._keywords, args))
class KeywordMapper:
def __init__(self):
self.keywords2consumer = {}
def getstate(self):
return self.keywords2consumer.copy()
def setstate(self, state):
self.keywords2consumer.clear()
self.keywords2consumer.update(state)
def getconsumer(self, keywords):
""" return a consumer matching the given keywords.
tries to find the most suitable consumer by walking, starting from
the back, the list of keywords, the first consumer matching a
keyword is returned (falling back to py.log.default)
"""
for i in range(len(keywords), 0, -1):
try:
return self.keywords2consumer[keywords[:i]]
except KeyError:
continue
return self.keywords2consumer.get('default', default_consumer)
def setconsumer(self, keywords, consumer):
""" set a consumer for a set of keywords. """
# normalize to tuples
if isinstance(keywords, str):
keywords = tuple(filter(None, keywords.split()))
elif hasattr(keywords, '_keywords'):
keywords = keywords._keywords
elif not isinstance(keywords, tuple):
raise TypeError("key %r is not a string or tuple" % (keywords,))
if consumer is not None and not py.builtin.callable(consumer):
if not hasattr(consumer, 'write'):
raise TypeError(
"%r should be None, callable or file-like" % (consumer,))
consumer = File(consumer)
self.keywords2consumer[keywords] = consumer
def default_consumer(msg):
""" the default consumer, prints the message to stdout (using 'print') """
sys.stderr.write(str(msg)+"\n")
default_keywordmapper = KeywordMapper()
def setconsumer(keywords, consumer):
default_keywordmapper.setconsumer(keywords, consumer)
def setstate(state):
default_keywordmapper.setstate(state)
def getstate():
return default_keywordmapper.getstate()
#
# Consumers
#
class File(object):
""" log consumer wrapping a file(-like) object """
def __init__(self, f):
assert hasattr(f, 'write')
# assert isinstance(f, file) or not hasattr(f, 'open')
self._file = f
def __call__(self, msg):
""" write a message to the log """
self._file.write(str(msg) + "\n")
if hasattr(self._file, 'flush'):
self._file.flush()
class Path(object):
""" log consumer that opens and writes to a Path """
def __init__(self, filename, append=False,
delayed_create=False, buffering=False):
self._append = append
self._filename = str(filename)
self._buffering = buffering
if not delayed_create:
self._openfile()
def _openfile(self):
mode = self._append and 'a' or 'w'
f = open(self._filename, mode)
self._file = f
def __call__(self, msg):
""" write a message to the log """
if not hasattr(self, "_file"):
self._openfile()
self._file.write(str(msg) + "\n")
if not self._buffering:
self._file.flush()
def STDOUT(msg):
""" consumer that writes to sys.stdout """
sys.stdout.write(str(msg)+"\n")
def STDERR(msg):
""" consumer that writes to sys.stderr """
sys.stderr.write(str(msg)+"\n")
class Syslog:
""" consumer that writes to the syslog daemon """
def __init__(self, priority=None):
if priority is None:
priority = self.LOG_INFO
self.priority = priority
def __call__(self, msg):
""" write a message to the log """
import syslog
syslog.syslog(self.priority, str(msg))
try:
import syslog
except ImportError:
pass
else:
for _prio in "EMERG ALERT CRIT ERR WARNING NOTICE INFO DEBUG".split():
_prio = "LOG_" + _prio
try:
setattr(Syslog, _prio, getattr(syslog, _prio))
except AttributeError:
pass
py-1.5.2/py/_log/__init__.py 0000664 0001750 0001750 00000000112 13202663617 016760 0 ustar vagrant vagrant 0000000 0000000 """ logging API ('producers' and 'consumers' connected via keywords) """
py-1.5.2/py/_log/warning.py 0000664 0001750 0001750 00000005005 13202663755 016677 0 ustar vagrant vagrant 0000000 0000000 import py, sys
class DeprecationWarning(DeprecationWarning):
def __init__(self, msg, path, lineno):
self.msg = msg
self.path = path
self.lineno = lineno
def __repr__(self):
return "%s:%d: %s" %(self.path, self.lineno+1, self.msg)
def __str__(self):
return self.msg
def _apiwarn(startversion, msg, stacklevel=2, function=None):
# below is mostly COPIED from python2.4/warnings.py's def warn()
# Get context information
if isinstance(stacklevel, str):
frame = sys._getframe(1)
level = 1
found = frame.f_code.co_filename.find(stacklevel) != -1
while frame:
co = frame.f_code
if co.co_filename.find(stacklevel) == -1:
if found:
stacklevel = level
break
else:
found = True
level += 1
frame = frame.f_back
else:
stacklevel = 1
msg = "%s (since version %s)" %(msg, startversion)
warn(msg, stacklevel=stacklevel+1, function=function)
def warn(msg, stacklevel=1, function=None):
if function is not None:
import inspect
filename = inspect.getfile(function)
lineno = py.code.getrawcode(function).co_firstlineno
else:
try:
caller = sys._getframe(stacklevel)
except ValueError:
globals = sys.__dict__
lineno = 1
else:
globals = caller.f_globals
lineno = caller.f_lineno
if '__name__' in globals:
module = globals['__name__']
else:
module = ""
filename = globals.get('__file__')
if filename:
fnl = filename.lower()
if fnl.endswith(".pyc") or fnl.endswith(".pyo"):
filename = filename[:-1]
elif fnl.endswith("$py.class"):
filename = filename.replace('$py.class', '.py')
else:
if module == "__main__":
try:
filename = sys.argv[0]
except AttributeError:
# embedded interpreters don't have sys.argv, see bug #839151
filename = '__main__'
if not filename:
filename = module
path = py.path.local(filename)
warning = DeprecationWarning(msg, path, lineno)
import warnings
warnings.warn_explicit(warning, category=Warning,
filename=str(warning.path),
lineno=warning.lineno,
registry=warnings.__dict__.setdefault(
"__warningsregistry__", {})
)
py-1.5.2/py/_vendored_packages/ 0000775 0001750 0001750 00000000000 13203150701 017522 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/py/_vendored_packages/iniconfig.py 0000664 0001750 0001750 00000012130 13202663755 022057 0 ustar vagrant vagrant 0000000 0000000 """ brain-dead simple parser for ini-style files.
(C) Ronny Pfannschmidt, Holger Krekel -- MIT licensed
"""
__all__ = ['IniConfig', 'ParseError']
COMMENTCHARS = "#;"
class ParseError(Exception):
def __init__(self, path, lineno, msg):
Exception.__init__(self, path, lineno, msg)
self.path = path
self.lineno = lineno
self.msg = msg
def __str__(self):
return "%s:%s: %s" % (self.path, self.lineno+1, self.msg)
class SectionWrapper(object):
def __init__(self, config, name):
self.config = config
self.name = name
def lineof(self, name):
return self.config.lineof(self.name, name)
def get(self, key, default=None, convert=str):
return self.config.get(self.name, key,
convert=convert, default=default)
def __getitem__(self, key):
return self.config.sections[self.name][key]
def __iter__(self):
section = self.config.sections.get(self.name, [])
def lineof(key):
return self.config.lineof(self.name, key)
for name in sorted(section, key=lineof):
yield name
def items(self):
for name in self:
yield name, self[name]
class IniConfig(object):
def __init__(self, path, data=None):
self.path = str(path) # convenience
if data is None:
f = open(self.path)
try:
tokens = self._parse(iter(f))
finally:
f.close()
else:
tokens = self._parse(data.splitlines(True))
self._sources = {}
self.sections = {}
for lineno, section, name, value in tokens:
if section is None:
self._raise(lineno, 'no section header defined')
self._sources[section, name] = lineno
if name is None:
if section in self.sections:
self._raise(lineno, 'duplicate section %r' % (section, ))
self.sections[section] = {}
else:
if name in self.sections[section]:
self._raise(lineno, 'duplicate name %r' % (name, ))
self.sections[section][name] = value
def _raise(self, lineno, msg):
raise ParseError(self.path, lineno, msg)
def _parse(self, line_iter):
result = []
section = None
for lineno, line in enumerate(line_iter):
name, data = self._parseline(line, lineno)
# new value
if name is not None and data is not None:
result.append((lineno, section, name, data))
# new section
elif name is not None and data is None:
if not name:
self._raise(lineno, 'empty section name')
section = name
result.append((lineno, section, None, None))
# continuation
elif name is None and data is not None:
if not result:
self._raise(lineno, 'unexpected value continuation')
last = result.pop()
last_name, last_data = last[-2:]
if last_name is None:
self._raise(lineno, 'unexpected value continuation')
if last_data:
data = '%s\n%s' % (last_data, data)
result.append(last[:-1] + (data,))
return result
def _parseline(self, line, lineno):
# blank lines
if iscommentline(line):
line = ""
else:
line = line.rstrip()
if not line:
return None, None
# section
if line[0] == '[':
realline = line
for c in COMMENTCHARS:
line = line.split(c)[0].rstrip()
if line[-1] == "]":
return line[1:-1], None
return None, realline.strip()
# value
elif not line[0].isspace():
try:
name, value = line.split('=', 1)
if ":" in name:
raise ValueError()
except ValueError:
try:
name, value = line.split(":", 1)
except ValueError:
self._raise(lineno, 'unexpected line: %r' % line)
return name.strip(), value.strip()
# continuation
else:
return None, line.strip()
def lineof(self, section, name=None):
lineno = self._sources.get((section, name))
if lineno is not None:
return lineno + 1
def get(self, section, name, default=None, convert=str):
try:
return convert(self.sections[section][name])
except KeyError:
return default
def __getitem__(self, name):
if name not in self.sections:
raise KeyError(name)
return SectionWrapper(self, name)
def __iter__(self):
for name in sorted(self.sections, key=self.lineof):
yield SectionWrapper(self, name)
def __contains__(self, arg):
return arg in self.sections
def iscommentline(line):
c = line.lstrip()[:1]
return c in COMMENTCHARS
py-1.5.2/py/_vendored_packages/apipkg.py 0000664 0001750 0001750 00000014424 13202663755 021375 0 ustar vagrant vagrant 0000000 0000000 """
apipkg: control the exported namespace of a python package.
see http://pypi.python.org/pypi/apipkg
(c) holger krekel, 2009 - MIT license
"""
import os
import sys
from types import ModuleType
__version__ = '1.4'
def _py_abspath(path):
"""
special version of abspath
that will leave paths from jython jars alone
"""
if path.startswith('__pyclasspath__'):
return path
else:
return os.path.abspath(path)
def distribution_version(name):
"""try to get the version of the named distribution,
returs None on failure"""
from pkg_resources import get_distribution, DistributionNotFound
try:
dist = get_distribution(name)
except DistributionNotFound:
pass
else:
return dist.version
def initpkg(pkgname, exportdefs, attr=dict(), eager=False):
""" initialize given package from the export definitions. """
oldmod = sys.modules.get(pkgname)
d = {}
f = getattr(oldmod, '__file__', None)
if f:
f = _py_abspath(f)
d['__file__'] = f
if hasattr(oldmod, '__version__'):
d['__version__'] = oldmod.__version__
if hasattr(oldmod, '__loader__'):
d['__loader__'] = oldmod.__loader__
if hasattr(oldmod, '__path__'):
d['__path__'] = [_py_abspath(p) for p in oldmod.__path__]
if '__doc__' not in exportdefs and getattr(oldmod, '__doc__', None):
d['__doc__'] = oldmod.__doc__
d.update(attr)
if hasattr(oldmod, "__dict__"):
oldmod.__dict__.update(d)
mod = ApiModule(pkgname, exportdefs, implprefix=pkgname, attr=d)
sys.modules[pkgname] = mod
# eagerload in bypthon to avoid their monkeypatching breaking packages
if 'bpython' in sys.modules or eager:
for module in sys.modules.values():
if isinstance(module, ApiModule):
module.__dict__
def importobj(modpath, attrname):
module = __import__(modpath, None, None, ['__doc__'])
if not attrname:
return module
retval = module
names = attrname.split(".")
for x in names:
retval = getattr(retval, x)
return retval
class ApiModule(ModuleType):
def __docget(self):
try:
return self.__doc
except AttributeError:
if '__doc__' in self.__map__:
return self.__makeattr('__doc__')
def __docset(self, value):
self.__doc = value
__doc__ = property(__docget, __docset)
def __init__(self, name, importspec, implprefix=None, attr=None):
self.__name__ = name
self.__all__ = [x for x in importspec if x != '__onfirstaccess__']
self.__map__ = {}
self.__implprefix__ = implprefix or name
if attr:
for name, val in attr.items():
# print "setting", self.__name__, name, val
setattr(self, name, val)
for name, importspec in importspec.items():
if isinstance(importspec, dict):
subname = '%s.%s' % (self.__name__, name)
apimod = ApiModule(subname, importspec, implprefix)
sys.modules[subname] = apimod
setattr(self, name, apimod)
else:
parts = importspec.split(':')
modpath = parts.pop(0)
attrname = parts and parts[0] or ""
if modpath[0] == '.':
modpath = implprefix + modpath
if not attrname:
subname = '%s.%s' % (self.__name__, name)
apimod = AliasModule(subname, modpath)
sys.modules[subname] = apimod
if '.' not in name:
setattr(self, name, apimod)
else:
self.__map__[name] = (modpath, attrname)
def __repr__(self):
l = []
if hasattr(self, '__version__'):
l.append("version=" + repr(self.__version__))
if hasattr(self, '__file__'):
l.append('from ' + repr(self.__file__))
if l:
return '' % (self.__name__, " ".join(l))
return '' % (self.__name__,)
def __makeattr(self, name):
"""lazily compute value for name or raise AttributeError if unknown."""
# print "makeattr", self.__name__, name
target = None
if '__onfirstaccess__' in self.__map__:
target = self.__map__.pop('__onfirstaccess__')
importobj(*target)()
try:
modpath, attrname = self.__map__[name]
except KeyError:
if target is not None and name != '__onfirstaccess__':
# retry, onfirstaccess might have set attrs
return getattr(self, name)
raise AttributeError(name)
else:
result = importobj(modpath, attrname)
setattr(self, name, result)
try:
del self.__map__[name]
except KeyError:
pass # in a recursive-import situation a double-del can happen
return result
__getattr__ = __makeattr
@property
def __dict__(self):
# force all the content of the module
# to be loaded when __dict__ is read
dictdescr = ModuleType.__dict__['__dict__']
dict = dictdescr.__get__(self)
if dict is not None:
hasattr(self, 'some')
for name in self.__all__:
try:
self.__makeattr(name)
except AttributeError:
pass
return dict
def AliasModule(modname, modpath, attrname=None):
mod = []
def getmod():
if not mod:
x = importobj(modpath, None)
if attrname is not None:
x = getattr(x, attrname)
mod.append(x)
return mod[0]
class AliasModule(ModuleType):
def __repr__(self):
x = modpath
if attrname:
x += "." + attrname
return '' % (modname, x)
def __getattribute__(self, name):
try:
return getattr(getmod(), name)
except ImportError:
return None
def __setattr__(self, name, value):
setattr(getmod(), name, value)
def __delattr__(self, name):
delattr(getmod(), name)
return AliasModule(str(modname))
py-1.5.2/py/_vendored_packages/__init__.py 0000664 0001750 0001750 00000000000 13202663755 021642 0 ustar vagrant vagrant 0000000 0000000 py-1.5.2/py/_process/ 0000775 0001750 0001750 00000000000 13203150701 015534 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/py/_process/forkedfunc.py 0000664 0001750 0001750 00000007154 13202663617 020261 0 ustar vagrant vagrant 0000000 0000000
"""
ForkedFunc provides a way to run a function in a forked process
and get at its return value, stdout and stderr output as well
as signals and exitstatusus.
"""
import py
import os
import sys
import marshal
def get_unbuffered_io(fd, filename):
f = open(str(filename), "w")
if fd != f.fileno():
os.dup2(f.fileno(), fd)
class AutoFlush:
def write(self, data):
f.write(data)
f.flush()
def __getattr__(self, name):
return getattr(f, name)
return AutoFlush()
class ForkedFunc:
EXITSTATUS_EXCEPTION = 3
def __init__(self, fun, args=None, kwargs=None, nice_level=0,
child_on_start=None, child_on_exit=None):
if args is None:
args = []
if kwargs is None:
kwargs = {}
self.fun = fun
self.args = args
self.kwargs = kwargs
self.tempdir = tempdir = py.path.local.mkdtemp()
self.RETVAL = tempdir.ensure('retval')
self.STDOUT = tempdir.ensure('stdout')
self.STDERR = tempdir.ensure('stderr')
pid = os.fork()
if pid: # in parent process
self.pid = pid
else: # in child process
self.pid = None
self._child(nice_level, child_on_start, child_on_exit)
def _child(self, nice_level, child_on_start, child_on_exit):
# right now we need to call a function, but first we need to
# map all IO that might happen
sys.stdout = stdout = get_unbuffered_io(1, self.STDOUT)
sys.stderr = stderr = get_unbuffered_io(2, self.STDERR)
retvalf = self.RETVAL.open("wb")
EXITSTATUS = 0
try:
if nice_level:
os.nice(nice_level)
try:
if child_on_start is not None:
child_on_start()
retval = self.fun(*self.args, **self.kwargs)
retvalf.write(marshal.dumps(retval))
if child_on_exit is not None:
child_on_exit()
except:
excinfo = py.code.ExceptionInfo()
stderr.write(str(excinfo._getreprcrash()))
EXITSTATUS = self.EXITSTATUS_EXCEPTION
finally:
stdout.close()
stderr.close()
retvalf.close()
os.close(1)
os.close(2)
os._exit(EXITSTATUS)
def waitfinish(self, waiter=os.waitpid):
pid, systemstatus = waiter(self.pid, 0)
if systemstatus:
if os.WIFSIGNALED(systemstatus):
exitstatus = os.WTERMSIG(systemstatus) + 128
else:
exitstatus = os.WEXITSTATUS(systemstatus)
else:
exitstatus = 0
signal = systemstatus & 0x7f
if not exitstatus and not signal:
retval = self.RETVAL.open('rb')
try:
retval_data = retval.read()
finally:
retval.close()
retval = marshal.loads(retval_data)
else:
retval = None
stdout = self.STDOUT.read()
stderr = self.STDERR.read()
self._removetemp()
return Result(exitstatus, signal, retval, stdout, stderr)
def _removetemp(self):
if self.tempdir.check():
self.tempdir.remove()
def __del__(self):
if self.pid is not None: # only clean up in main process
self._removetemp()
class Result(object):
def __init__(self, exitstatus, signal, retval, stdout, stderr):
self.exitstatus = exitstatus
self.signal = signal
self.retval = retval
self.out = stdout
self.err = stderr
py-1.5.2/py/_process/cmdexec.py 0000664 0001750 0001750 00000003426 13202663617 017541 0 ustar vagrant vagrant 0000000 0000000 import sys
import subprocess
import py
from subprocess import Popen, PIPE
def cmdexec(cmd):
""" return unicode output of executing 'cmd' in a separate process.
raise cmdexec.Error exeception if the command failed.
the exception will provide an 'err' attribute containing
the error-output from the command.
if the subprocess module does not provide a proper encoding/unicode strings
sys.getdefaultencoding() will be used, if that does not exist, 'UTF-8'.
"""
process = subprocess.Popen(cmd, shell=True,
universal_newlines=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = process.communicate()
if sys.version_info[0] < 3: # on py3 we get unicode strings, on py2 not
try:
default_encoding = sys.getdefaultencoding() # jython may not have it
except AttributeError:
default_encoding = sys.stdout.encoding or 'UTF-8'
out = unicode(out, process.stdout.encoding or default_encoding)
err = unicode(err, process.stderr.encoding or default_encoding)
status = process.poll()
if status:
raise ExecutionFailed(status, status, cmd, out, err)
return out
class ExecutionFailed(py.error.Error):
def __init__(self, status, systemstatus, cmd, out, err):
Exception.__init__(self)
self.status = status
self.systemstatus = systemstatus
self.cmd = cmd
self.err = err
self.out = out
def __str__(self):
return "ExecutionFailed: %d %s\n%s" %(self.status, self.cmd, self.err)
# export the exception under the name 'py.process.cmdexec.Error'
cmdexec.Error = ExecutionFailed
try:
ExecutionFailed.__module__ = 'py.process.cmdexec'
ExecutionFailed.__name__ = 'Error'
except (AttributeError, TypeError):
pass
py-1.5.2/py/_process/killproc.py 0000664 0001750 0001750 00000001210 13202663617 017735 0 ustar vagrant vagrant 0000000 0000000 import py
import os, sys
if sys.platform == "win32" or getattr(os, '_name', '') == 'nt':
try:
import ctypes
except ImportError:
def dokill(pid):
py.process.cmdexec("taskkill /F /PID %d" %(pid,))
else:
def dokill(pid):
PROCESS_TERMINATE = 1
handle = ctypes.windll.kernel32.OpenProcess(
PROCESS_TERMINATE, False, pid)
ctypes.windll.kernel32.TerminateProcess(handle, -1)
ctypes.windll.kernel32.CloseHandle(handle)
else:
def dokill(pid):
os.kill(pid, 15)
def kill(pid):
""" kill process by id. """
dokill(pid)
py-1.5.2/py/_process/__init__.py 0000664 0001750 0001750 00000000050 13202663617 017656 0 ustar vagrant vagrant 0000000 0000000 """ high-level sub-process handling """
py-1.5.2/py/_path/ 0000775 0001750 0001750 00000000000 13203150701 015012 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/py/_path/svnwc.py 0000664 0001750 0001750 00000125461 13202663755 016556 0 ustar vagrant vagrant 0000000 0000000 """
svn-Command based Implementation of a Subversion WorkingCopy Path.
SvnWCCommandPath is the main class.
"""
import os, sys, time, re, calendar
import py
import subprocess
from py._path import common
#-----------------------------------------------------------
# Caching latest repository revision and repo-paths
# (getting them is slow with the current implementations)
#
# XXX make mt-safe
#-----------------------------------------------------------
class cache:
proplist = {}
info = {}
entries = {}
prop = {}
class RepoEntry:
def __init__(self, url, rev, timestamp):
self.url = url
self.rev = rev
self.timestamp = timestamp
def __str__(self):
return "repo: %s;%s %s" %(self.url, self.rev, self.timestamp)
class RepoCache:
""" The Repocache manages discovered repository paths
and their revisions. If inside a timeout the cache
will even return the revision of the root.
"""
timeout = 20 # seconds after which we forget that we know the last revision
def __init__(self):
self.repos = []
def clear(self):
self.repos = []
def put(self, url, rev, timestamp=None):
if rev is None:
return
if timestamp is None:
timestamp = time.time()
for entry in self.repos:
if url == entry.url:
entry.timestamp = timestamp
entry.rev = rev
#print "set repo", entry
break
else:
entry = RepoEntry(url, rev, timestamp)
self.repos.append(entry)
#print "appended repo", entry
def get(self, url):
now = time.time()
for entry in self.repos:
if url.startswith(entry.url):
if now < entry.timestamp + self.timeout:
#print "returning immediate Etrny", entry
return entry.url, entry.rev
return entry.url, -1
return url, -1
repositories = RepoCache()
# svn support code
ALLOWED_CHARS = "_ -/\\=$.~+%" #add characters as necessary when tested
if sys.platform == "win32":
ALLOWED_CHARS += ":"
ALLOWED_CHARS_HOST = ALLOWED_CHARS + '@:'
def _getsvnversion(ver=[]):
try:
return ver[0]
except IndexError:
v = py.process.cmdexec("svn -q --version")
v.strip()
v = '.'.join(v.split('.')[:2])
ver.append(v)
return v
def _escape_helper(text):
text = str(text)
if sys.platform != 'win32':
text = str(text).replace('$', '\\$')
return text
def _check_for_bad_chars(text, allowed_chars=ALLOWED_CHARS):
for c in str(text):
if c.isalnum():
continue
if c in allowed_chars:
continue
return True
return False
def checkbadchars(url):
# (hpk) not quite sure about the exact purpose, guido w.?
proto, uri = url.split("://", 1)
if proto != "file":
host, uripath = uri.split('/', 1)
# only check for bad chars in the non-protocol parts
if (_check_for_bad_chars(host, ALLOWED_CHARS_HOST) \
or _check_for_bad_chars(uripath, ALLOWED_CHARS)):
raise ValueError("bad char in %r" % (url, ))
#_______________________________________________________________
class SvnPathBase(common.PathBase):
""" Base implementation for SvnPath implementations. """
sep = '/'
def _geturl(self):
return self.strpath
url = property(_geturl, None, None, "url of this svn-path.")
def __str__(self):
""" return a string representation (including rev-number) """
return self.strpath
def __hash__(self):
return hash(self.strpath)
def new(self, **kw):
""" create a modified version of this path. A 'rev' argument
indicates a new revision.
the following keyword arguments modify various path parts::
http://host.com/repo/path/file.ext
|-----------------------| dirname
|------| basename
|--| purebasename
|--| ext
"""
obj = object.__new__(self.__class__)
obj.rev = kw.get('rev', self.rev)
obj.auth = kw.get('auth', self.auth)
dirname, basename, purebasename, ext = self._getbyspec(
"dirname,basename,purebasename,ext")
if 'basename' in kw:
if 'purebasename' in kw or 'ext' in kw:
raise ValueError("invalid specification %r" % kw)
else:
pb = kw.setdefault('purebasename', purebasename)
ext = kw.setdefault('ext', ext)
if ext and not ext.startswith('.'):
ext = '.' + ext
kw['basename'] = pb + ext
kw.setdefault('dirname', dirname)
kw.setdefault('sep', self.sep)
if kw['basename']:
obj.strpath = "%(dirname)s%(sep)s%(basename)s" % kw
else:
obj.strpath = "%(dirname)s" % kw
return obj
def _getbyspec(self, spec):
""" get specified parts of the path. 'arg' is a string
with comma separated path parts. The parts are returned
in exactly the order of the specification.
you may specify the following parts:
http://host.com/repo/path/file.ext
|-----------------------| dirname
|------| basename
|--| purebasename
|--| ext
"""
res = []
parts = self.strpath.split(self.sep)
for name in spec.split(','):
name = name.strip()
if name == 'dirname':
res.append(self.sep.join(parts[:-1]))
elif name == 'basename':
res.append(parts[-1])
else:
basename = parts[-1]
i = basename.rfind('.')
if i == -1:
purebasename, ext = basename, ''
else:
purebasename, ext = basename[:i], basename[i:]
if name == 'purebasename':
res.append(purebasename)
elif name == 'ext':
res.append(ext)
else:
raise NameError("Don't know part %r" % name)
return res
def __eq__(self, other):
""" return true if path and rev attributes each match """
return (str(self) == str(other) and
(self.rev == other.rev or self.rev == other.rev))
def __ne__(self, other):
return not self == other
def join(self, *args):
""" return a new Path (with the same revision) which is composed
of the self Path followed by 'args' path components.
"""
if not args:
return self
args = tuple([arg.strip(self.sep) for arg in args])
parts = (self.strpath, ) + args
newpath = self.__class__(self.sep.join(parts), self.rev, self.auth)
return newpath
def propget(self, name):
""" return the content of the given property. """
value = self._propget(name)
return value
def proplist(self):
""" list all property names. """
content = self._proplist()
return content
def size(self):
""" Return the size of the file content of the Path. """
return self.info().size
def mtime(self):
""" Return the last modification time of the file. """
return self.info().mtime
# shared help methods
def _escape(self, cmd):
return _escape_helper(cmd)
#def _childmaxrev(self):
# """ return maximum revision number of childs (or self.rev if no childs) """
# rev = self.rev
# for name, info in self._listdir_nameinfo():
# rev = max(rev, info.created_rev)
# return rev
#def _getlatestrevision(self):
# """ return latest repo-revision for this path. """
# url = self.strpath
# path = self.__class__(url, None)
#
# # we need a long walk to find the root-repo and revision
# while 1:
# try:
# rev = max(rev, path._childmaxrev())
# previous = path
# path = path.dirpath()
# except (IOError, process.cmdexec.Error):
# break
# if rev is None:
# raise IOError, "could not determine newest repo revision for %s" % self
# return rev
class Checkers(common.Checkers):
def dir(self):
try:
return self.path.info().kind == 'dir'
except py.error.Error:
return self._listdirworks()
def _listdirworks(self):
try:
self.path.listdir()
except py.error.ENOENT:
return False
else:
return True
def file(self):
try:
return self.path.info().kind == 'file'
except py.error.ENOENT:
return False
def exists(self):
try:
return self.path.info()
except py.error.ENOENT:
return self._listdirworks()
def parse_apr_time(timestr):
i = timestr.rfind('.')
if i == -1:
raise ValueError("could not parse %s" % timestr)
timestr = timestr[:i]
parsedtime = time.strptime(timestr, "%Y-%m-%dT%H:%M:%S")
return time.mktime(parsedtime)
class PropListDict(dict):
""" a Dictionary which fetches values (InfoSvnCommand instances) lazily"""
def __init__(self, path, keynames):
dict.__init__(self, [(x, None) for x in keynames])
self.path = path
def __getitem__(self, key):
value = dict.__getitem__(self, key)
if value is None:
value = self.path.propget(key)
dict.__setitem__(self, key, value)
return value
def fixlocale():
if sys.platform != 'win32':
return 'LC_ALL=C '
return ''
# some nasty chunk of code to solve path and url conversion and quoting issues
ILLEGAL_CHARS = '* | \\ / : < > ? \t \n \x0b \x0c \r'.split(' ')
if os.sep in ILLEGAL_CHARS:
ILLEGAL_CHARS.remove(os.sep)
ISWINDOWS = sys.platform == 'win32'
_reg_allow_disk = re.compile(r'^([a-z]\:\\)?[^:]+$', re.I)
def _check_path(path):
illegal = ILLEGAL_CHARS[:]
sp = path.strpath
if ISWINDOWS:
illegal.remove(':')
if not _reg_allow_disk.match(sp):
raise ValueError('path may not contain a colon (:)')
for char in sp:
if char not in string.printable or char in illegal:
raise ValueError('illegal character %r in path' % (char,))
def path_to_fspath(path, addat=True):
_check_path(path)
sp = path.strpath
if addat and path.rev != -1:
sp = '%s@%s' % (sp, path.rev)
elif addat:
sp = '%s@HEAD' % (sp,)
return sp
def url_from_path(path):
fspath = path_to_fspath(path, False)
from urllib import quote
if ISWINDOWS:
match = _reg_allow_disk.match(fspath)
fspath = fspath.replace('\\', '/')
if match.group(1):
fspath = '/%s%s' % (match.group(1).replace('\\', '/'),
quote(fspath[len(match.group(1)):]))
else:
fspath = quote(fspath)
else:
fspath = quote(fspath)
if path.rev != -1:
fspath = '%s@%s' % (fspath, path.rev)
else:
fspath = '%s@HEAD' % (fspath,)
return 'file://%s' % (fspath,)
class SvnAuth(object):
""" container for auth information for Subversion """
def __init__(self, username, password, cache_auth=True, interactive=True):
self.username = username
self.password = password
self.cache_auth = cache_auth
self.interactive = interactive
def makecmdoptions(self):
uname = self.username.replace('"', '\\"')
passwd = self.password.replace('"', '\\"')
ret = []
if uname:
ret.append('--username="%s"' % (uname,))
if passwd:
ret.append('--password="%s"' % (passwd,))
if not self.cache_auth:
ret.append('--no-auth-cache')
if not self.interactive:
ret.append('--non-interactive')
return ' '.join(ret)
def __str__(self):
return "" %(self.username,)
rex_blame = re.compile(r'\s*(\d+)\s*(\S+) (.*)')
class SvnWCCommandPath(common.PathBase):
""" path implementation offering access/modification to svn working copies.
It has methods similar to the functions in os.path and similar to the
commands of the svn client.
"""
sep = os.sep
def __new__(cls, wcpath=None, auth=None):
self = object.__new__(cls)
if isinstance(wcpath, cls):
if wcpath.__class__ == cls:
return wcpath
wcpath = wcpath.localpath
if _check_for_bad_chars(str(wcpath),
ALLOWED_CHARS):
raise ValueError("bad char in wcpath %s" % (wcpath, ))
self.localpath = py.path.local(wcpath)
self.auth = auth
return self
strpath = property(lambda x: str(x.localpath), None, None, "string path")
rev = property(lambda x: x.info(usecache=0).rev, None, None, "revision")
def __eq__(self, other):
return self.localpath == getattr(other, 'localpath', None)
def _geturl(self):
if getattr(self, '_url', None) is None:
info = self.info()
self._url = info.url #SvnPath(info.url, info.rev)
assert isinstance(self._url, py.builtin._basestring)
return self._url
url = property(_geturl, None, None, "url of this WC item")
def _escape(self, cmd):
return _escape_helper(cmd)
def dump(self, obj):
""" pickle object into path location"""
return self.localpath.dump(obj)
def svnurl(self):
""" return current SvnPath for this WC-item. """
info = self.info()
return py.path.svnurl(info.url)
def __repr__(self):
return "svnwc(%r)" % (self.strpath) # , self._url)
def __str__(self):
return str(self.localpath)
def _makeauthoptions(self):
if self.auth is None:
return ''
return self.auth.makecmdoptions()
def _authsvn(self, cmd, args=None):
args = args and list(args) or []
args.append(self._makeauthoptions())
return self._svn(cmd, *args)
def _svn(self, cmd, *args):
l = ['svn %s' % cmd]
args = [self._escape(item) for item in args]
l.extend(args)
l.append('"%s"' % self._escape(self.strpath))
# try fixing the locale because we can't otherwise parse
string = fixlocale() + " ".join(l)
try:
try:
key = 'LC_MESSAGES'
hold = os.environ.get(key)
os.environ[key] = 'C'
out = py.process.cmdexec(string)
finally:
if hold:
os.environ[key] = hold
else:
del os.environ[key]
except py.process.cmdexec.Error:
e = sys.exc_info()[1]
strerr = e.err.lower()
if strerr.find('not found') != -1:
raise py.error.ENOENT(self)
elif strerr.find("E200009:") != -1:
raise py.error.ENOENT(self)
if (strerr.find('file exists') != -1 or
strerr.find('file already exists') != -1 or
strerr.find('w150002:') != -1 or
strerr.find("can't create directory") != -1):
raise py.error.EEXIST(strerr) #self)
raise
return out
def switch(self, url):
""" switch to given URL. """
self._authsvn('switch', [url])
def checkout(self, url=None, rev=None):
""" checkout from url to local wcpath. """
args = []
if url is None:
url = self.url
if rev is None or rev == -1:
if (sys.platform != 'win32' and
_getsvnversion() == '1.3'):
url += "@HEAD"
else:
if _getsvnversion() == '1.3':
url += "@%d" % rev
else:
args.append('-r' + str(rev))
args.append(url)
self._authsvn('co', args)
def update(self, rev='HEAD', interactive=True):
""" update working copy item to given revision. (None -> HEAD). """
opts = ['-r', rev]
if not interactive:
opts.append("--non-interactive")
self._authsvn('up', opts)
def write(self, content, mode='w'):
""" write content into local filesystem wc. """
self.localpath.write(content, mode)
def dirpath(self, *args):
""" return the directory Path of the current Path. """
return self.__class__(self.localpath.dirpath(*args), auth=self.auth)
def _ensuredirs(self):
parent = self.dirpath()
if parent.check(dir=0):
parent._ensuredirs()
if self.check(dir=0):
self.mkdir()
return self
def ensure(self, *args, **kwargs):
""" ensure that an args-joined path exists (by default as
a file). if you specify a keyword argument 'directory=True'
then the path is forced to be a directory path.
"""
p = self.join(*args)
if p.check():
if p.check(versioned=False):
p.add()
return p
if kwargs.get('dir', 0):
return p._ensuredirs()
parent = p.dirpath()
parent._ensuredirs()
p.write("")
p.add()
return p
def mkdir(self, *args):
""" create & return the directory joined with args. """
if args:
return self.join(*args).mkdir()
else:
self._svn('mkdir')
return self
def add(self):
""" add ourself to svn """
self._svn('add')
def remove(self, rec=1, force=1):
""" remove a file or a directory tree. 'rec'ursive is
ignored and considered always true (because of
underlying svn semantics.
"""
assert rec, "svn cannot remove non-recursively"
if not self.check(versioned=True):
# not added to svn (anymore?), just remove
py.path.local(self).remove()
return
flags = []
if force:
flags.append('--force')
self._svn('remove', *flags)
def copy(self, target):
""" copy path to target."""
py.process.cmdexec("svn copy %s %s" %(str(self), str(target)))
def rename(self, target):
""" rename this path to target. """
py.process.cmdexec("svn move --force %s %s" %(str(self), str(target)))
def lock(self):
""" set a lock (exclusive) on the resource """
out = self._authsvn('lock').strip()
if not out:
# warning or error, raise exception
raise ValueError("unknown error in svn lock command")
def unlock(self):
""" unset a previously set lock """
out = self._authsvn('unlock').strip()
if out.startswith('svn:'):
# warning or error, raise exception
raise Exception(out[4:])
def cleanup(self):
""" remove any locks from the resource """
# XXX should be fixed properly!!!
try:
self.unlock()
except:
pass
def status(self, updates=0, rec=0, externals=0):
""" return (collective) Status object for this file. """
# http://svnbook.red-bean.com/book.html#svn-ch-3-sect-4.3.1
# 2201 2192 jum test
# XXX
if externals:
raise ValueError("XXX cannot perform status() "
"on external items yet")
else:
#1.2 supports: externals = '--ignore-externals'
externals = ''
if rec:
rec= ''
else:
rec = '--non-recursive'
# XXX does not work on all subversion versions
#if not externals:
# externals = '--ignore-externals'
if updates:
updates = '-u'
else:
updates = ''
try:
cmd = 'status -v --xml --no-ignore %s %s %s' % (
updates, rec, externals)
out = self._authsvn(cmd)
except py.process.cmdexec.Error:
cmd = 'status -v --no-ignore %s %s %s' % (
updates, rec, externals)
out = self._authsvn(cmd)
rootstatus = WCStatus(self).fromstring(out, self)
else:
rootstatus = XMLWCStatus(self).fromstring(out, self)
return rootstatus
def diff(self, rev=None):
""" return a diff of the current path against revision rev (defaulting
to the last one).
"""
args = []
if rev is not None:
args.append("-r %d" % rev)
out = self._authsvn('diff', args)
return out
def blame(self):
""" return a list of tuples of three elements:
(revision, commiter, line)
"""
out = self._svn('blame')
result = []
blamelines = out.splitlines()
reallines = py.path.svnurl(self.url).readlines()
for i, (blameline, line) in enumerate(
zip(blamelines, reallines)):
m = rex_blame.match(blameline)
if not m:
raise ValueError("output line %r of svn blame does not match "
"expected format" % (line, ))
rev, name, _ = m.groups()
result.append((int(rev), name, line))
return result
_rex_commit = re.compile(r'.*Committed revision (\d+)\.$', re.DOTALL)
def commit(self, msg='', rec=1):
""" commit with support for non-recursive commits """
# XXX i guess escaping should be done better here?!?
cmd = 'commit -m "%s" --force-log' % (msg.replace('"', '\\"'),)
if not rec:
cmd += ' -N'
out = self._authsvn(cmd)
try:
del cache.info[self]
except KeyError:
pass
if out:
m = self._rex_commit.match(out)
return int(m.group(1))
def propset(self, name, value, *args):
""" set property name to value on this path. """
d = py.path.local.mkdtemp()
try:
p = d.join('value')
p.write(value)
self._svn('propset', name, '--file', str(p), *args)
finally:
d.remove()
def propget(self, name):
""" get property name on this path. """
res = self._svn('propget', name)
return res[:-1] # strip trailing newline
def propdel(self, name):
""" delete property name on this path. """
res = self._svn('propdel', name)
return res[:-1] # strip trailing newline
def proplist(self, rec=0):
""" return a mapping of property names to property values.
If rec is True, then return a dictionary mapping sub-paths to such mappings.
"""
if rec:
res = self._svn('proplist -R')
return make_recursive_propdict(self, res)
else:
res = self._svn('proplist')
lines = res.split('\n')
lines = [x.strip() for x in lines[1:]]
return PropListDict(self, lines)
def revert(self, rec=0):
""" revert the local changes of this path. if rec is True, do so
recursively. """
if rec:
result = self._svn('revert -R')
else:
result = self._svn('revert')
return result
def new(self, **kw):
""" create a modified version of this path. A 'rev' argument
indicates a new revision.
the following keyword arguments modify various path parts:
http://host.com/repo/path/file.ext
|-----------------------| dirname
|------| basename
|--| purebasename
|--| ext
"""
if kw:
localpath = self.localpath.new(**kw)
else:
localpath = self.localpath
return self.__class__(localpath, auth=self.auth)
def join(self, *args, **kwargs):
""" return a new Path (with the same revision) which is composed
of the self Path followed by 'args' path components.
"""
if not args:
return self
localpath = self.localpath.join(*args, **kwargs)
return self.__class__(localpath, auth=self.auth)
def info(self, usecache=1):
""" return an Info structure with svn-provided information. """
info = usecache and cache.info.get(self)
if not info:
try:
output = self._svn('info')
except py.process.cmdexec.Error:
e = sys.exc_info()[1]
if e.err.find('Path is not a working copy directory') != -1:
raise py.error.ENOENT(self, e.err)
elif e.err.find("is not under version control") != -1:
raise py.error.ENOENT(self, e.err)
raise
# XXX SVN 1.3 has output on stderr instead of stdout (while it does
# return 0!), so a bit nasty, but we assume no output is output
# to stderr...
if (output.strip() == '' or
output.lower().find('not a versioned resource') != -1):
raise py.error.ENOENT(self, output)
info = InfoSvnWCCommand(output)
# Can't reliably compare on Windows without access to win32api
if sys.platform != 'win32':
if info.path != self.localpath:
raise py.error.ENOENT(self, "not a versioned resource:" +
" %s != %s" % (info.path, self.localpath))
cache.info[self] = info
return info
def listdir(self, fil=None, sort=None):
""" return a sequence of Paths.
listdir will return either a tuple or a list of paths
depending on implementation choices.
"""
if isinstance(fil, str):
fil = common.FNMatcher(fil)
# XXX unify argument naming with LocalPath.listdir
def notsvn(path):
return path.basename != '.svn'
paths = []
for localpath in self.localpath.listdir(notsvn):
p = self.__class__(localpath, auth=self.auth)
if notsvn(p) and (not fil or fil(p)):
paths.append(p)
self._sortlist(paths, sort)
return paths
def open(self, mode='r'):
""" return an opened file with the given mode. """
return open(self.strpath, mode)
def _getbyspec(self, spec):
return self.localpath._getbyspec(spec)
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, py.error.EEXIST):
return False
except py.process.cmdexec.Error:
e = sys.exc_info()[1]
if e.err.find('is not a working copy')!=-1:
return False
if e.err.lower().find('not a versioned resource') != -1:
return False
raise
else:
return True
def log(self, rev_start=None, rev_end=1, verbose=False):
""" return a list of LogEntry instances for this path.
rev_start is the starting revision (defaulting to the first one).
rev_end is the last revision (defaulting to HEAD).
if verbose is True, then the LogEntry instances also know which files changed.
"""
assert self.check() # make it simpler for the pipe
rev_start = rev_start is None and "HEAD" or rev_start
rev_end = rev_end is None and "HEAD" or rev_end
if rev_start == "HEAD" and rev_end == 1:
rev_opt = ""
else:
rev_opt = "-r %s:%s" % (rev_start, rev_end)
verbose_opt = verbose and "-v" or ""
locale_env = fixlocale()
# some blather on stderr
auth_opt = self._makeauthoptions()
#stdin, stdout, stderr = os.popen3(locale_env +
# 'svn log --xml %s %s %s "%s"' % (
# rev_opt, verbose_opt, auth_opt,
# self.strpath))
cmd = locale_env + 'svn log --xml %s %s %s "%s"' % (
rev_opt, verbose_opt, auth_opt, self.strpath)
popen = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True,
)
stdout, stderr = popen.communicate()
stdout = py.builtin._totext(stdout, sys.getdefaultencoding())
minidom,ExpatError = importxml()
try:
tree = minidom.parseString(stdout)
except ExpatError:
raise ValueError('no such revision')
result = []
for logentry in filter(None, tree.firstChild.childNodes):
if logentry.nodeType == logentry.ELEMENT_NODE:
result.append(LogEntry(logentry))
return result
def size(self):
""" Return the size of the file content of the Path. """
return self.info().size
def mtime(self):
""" Return the last modification time of the file. """
return self.info().mtime
def __hash__(self):
return hash((self.strpath, self.__class__, self.auth))
class WCStatus:
attrnames = ('modified','added', 'conflict', 'unchanged', 'external',
'deleted', 'prop_modified', 'unknown', 'update_available',
'incomplete', 'kindmismatch', 'ignored', 'locked', 'replaced'
)
def __init__(self, wcpath, rev=None, modrev=None, author=None):
self.wcpath = wcpath
self.rev = rev
self.modrev = modrev
self.author = author
for name in self.attrnames:
setattr(self, name, [])
def allpath(self, sort=True, **kw):
d = {}
for name in self.attrnames:
if name not in kw or kw[name]:
for path in getattr(self, name):
d[path] = 1
l = d.keys()
if sort:
l.sort()
return l
# XXX a bit scary to assume there's always 2 spaces between username and
# path, however with win32 allowing spaces in user names there doesn't
# seem to be a more solid approach :(
_rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(.+?)\s{2,}(.*)')
def fromstring(data, rootwcpath, rev=None, modrev=None, author=None):
""" return a new WCStatus object from data 's'
"""
rootstatus = WCStatus(rootwcpath, rev, modrev, author)
update_rev = None
for line in data.split('\n'):
if not line.strip():
continue
#print "processing %r" % line
flags, rest = line[:8], line[8:]
# first column
c0,c1,c2,c3,c4,c5,x6,c7 = flags
#if '*' in line:
# print "flags", repr(flags), "rest", repr(rest)
if c0 in '?XI':
fn = line.split(None, 1)[1]
if c0 == '?':
wcpath = rootwcpath.join(fn, abs=1)
rootstatus.unknown.append(wcpath)
elif c0 == 'X':
wcpath = rootwcpath.__class__(
rootwcpath.localpath.join(fn, abs=1),
auth=rootwcpath.auth)
rootstatus.external.append(wcpath)
elif c0 == 'I':
wcpath = rootwcpath.join(fn, abs=1)
rootstatus.ignored.append(wcpath)
continue
#elif c0 in '~!' or c4 == 'S':
# raise NotImplementedError("received flag %r" % c0)
m = WCStatus._rex_status.match(rest)
if not m:
if c7 == '*':
fn = rest.strip()
wcpath = rootwcpath.join(fn, abs=1)
rootstatus.update_available.append(wcpath)
continue
if line.lower().find('against revision:')!=-1:
update_rev = int(rest.split(':')[1].strip())
continue
if line.lower().find('status on external') > -1:
# XXX not sure what to do here... perhaps we want to
# store some state instead of just continuing, as right
# now it makes the top-level external get added twice
# (once as external, once as 'normal' unchanged item)
# because of the way SVN presents external items
continue
# keep trying
raise ValueError("could not parse line %r" % line)
else:
rev, modrev, author, fn = m.groups()
wcpath = rootwcpath.join(fn, abs=1)
#assert wcpath.check()
if c0 == 'M':
assert wcpath.check(file=1), "didn't expect a directory with changed content here"
rootstatus.modified.append(wcpath)
elif c0 == 'A' or c3 == '+' :
rootstatus.added.append(wcpath)
elif c0 == 'D':
rootstatus.deleted.append(wcpath)
elif c0 == 'C':
rootstatus.conflict.append(wcpath)
elif c0 == '~':
rootstatus.kindmismatch.append(wcpath)
elif c0 == '!':
rootstatus.incomplete.append(wcpath)
elif c0 == 'R':
rootstatus.replaced.append(wcpath)
elif not c0.strip():
rootstatus.unchanged.append(wcpath)
else:
raise NotImplementedError("received flag %r" % c0)
if c1 == 'M':
rootstatus.prop_modified.append(wcpath)
# XXX do we cover all client versions here?
if c2 == 'L' or c5 == 'K':
rootstatus.locked.append(wcpath)
if c7 == '*':
rootstatus.update_available.append(wcpath)
if wcpath == rootwcpath:
rootstatus.rev = rev
rootstatus.modrev = modrev
rootstatus.author = author
if update_rev:
rootstatus.update_rev = update_rev
continue
return rootstatus
fromstring = staticmethod(fromstring)
class XMLWCStatus(WCStatus):
def fromstring(data, rootwcpath, rev=None, modrev=None, author=None):
""" parse 'data' (XML string as outputted by svn st) into a status obj
"""
# XXX for externals, the path is shown twice: once
# with external information, and once with full info as if
# the item was a normal non-external... the current way of
# dealing with this issue is by ignoring it - this does make
# externals appear as external items as well as 'normal',
# unchanged ones in the status object so this is far from ideal
rootstatus = WCStatus(rootwcpath, rev, modrev, author)
update_rev = None
minidom, ExpatError = importxml()
try:
doc = minidom.parseString(data)
except ExpatError:
e = sys.exc_info()[1]
raise ValueError(str(e))
urevels = doc.getElementsByTagName('against')
if urevels:
rootstatus.update_rev = urevels[-1].getAttribute('revision')
for entryel in doc.getElementsByTagName('entry'):
path = entryel.getAttribute('path')
statusel = entryel.getElementsByTagName('wc-status')[0]
itemstatus = statusel.getAttribute('item')
if itemstatus == 'unversioned':
wcpath = rootwcpath.join(path, abs=1)
rootstatus.unknown.append(wcpath)
continue
elif itemstatus == 'external':
wcpath = rootwcpath.__class__(
rootwcpath.localpath.join(path, abs=1),
auth=rootwcpath.auth)
rootstatus.external.append(wcpath)
continue
elif itemstatus == 'ignored':
wcpath = rootwcpath.join(path, abs=1)
rootstatus.ignored.append(wcpath)
continue
elif itemstatus == 'incomplete':
wcpath = rootwcpath.join(path, abs=1)
rootstatus.incomplete.append(wcpath)
continue
rev = statusel.getAttribute('revision')
if itemstatus == 'added' or itemstatus == 'none':
rev = '0'
modrev = '?'
author = '?'
date = ''
elif itemstatus == "replaced":
pass
else:
#print entryel.toxml()
commitel = entryel.getElementsByTagName('commit')[0]
if commitel:
modrev = commitel.getAttribute('revision')
author = ''
author_els = commitel.getElementsByTagName('author')
if author_els:
for c in author_els[0].childNodes:
author += c.nodeValue
date = ''
for c in commitel.getElementsByTagName('date')[0]\
.childNodes:
date += c.nodeValue
wcpath = rootwcpath.join(path, abs=1)
assert itemstatus != 'modified' or wcpath.check(file=1), (
'did\'t expect a directory with changed content here')
itemattrname = {
'normal': 'unchanged',
'unversioned': 'unknown',
'conflicted': 'conflict',
'none': 'added',
}.get(itemstatus, itemstatus)
attr = getattr(rootstatus, itemattrname)
attr.append(wcpath)
propsstatus = statusel.getAttribute('props')
if propsstatus not in ('none', 'normal'):
rootstatus.prop_modified.append(wcpath)
if wcpath == rootwcpath:
rootstatus.rev = rev
rootstatus.modrev = modrev
rootstatus.author = author
rootstatus.date = date
# handle repos-status element (remote info)
rstatusels = entryel.getElementsByTagName('repos-status')
if rstatusels:
rstatusel = rstatusels[0]
ritemstatus = rstatusel.getAttribute('item')
if ritemstatus in ('added', 'modified'):
rootstatus.update_available.append(wcpath)
lockels = entryel.getElementsByTagName('lock')
if len(lockels):
rootstatus.locked.append(wcpath)
return rootstatus
fromstring = staticmethod(fromstring)
class InfoSvnWCCommand:
def __init__(self, output):
# Path: test
# URL: http://codespeak.net/svn/std.path/trunk/dist/std.path/test
# Repository UUID: fd0d7bf2-dfb6-0310-8d31-b7ecfe96aada
# Revision: 2151
# Node Kind: directory
# Schedule: normal
# Last Changed Author: hpk
# Last Changed Rev: 2100
# Last Changed Date: 2003-10-27 20:43:14 +0100 (Mon, 27 Oct 2003)
# Properties Last Updated: 2003-11-03 14:47:48 +0100 (Mon, 03 Nov 2003)
d = {}
for line in output.split('\n'):
if not line.strip():
continue
key, value = line.split(':', 1)
key = key.lower().replace(' ', '')
value = value.strip()
d[key] = value
try:
self.url = d['url']
except KeyError:
raise ValueError("Not a versioned resource")
#raise ValueError, "Not a versioned resource %r" % path
self.kind = d['nodekind'] == 'directory' and 'dir' or d['nodekind']
try:
self.rev = int(d['revision'])
except KeyError:
self.rev = None
self.path = py.path.local(d['path'])
self.size = self.path.size()
if 'lastchangedrev' in d:
self.created_rev = int(d['lastchangedrev'])
if 'lastchangedauthor' in d:
self.last_author = d['lastchangedauthor']
if 'lastchangeddate' in d:
self.mtime = parse_wcinfotime(d['lastchangeddate'])
self.time = self.mtime * 1000000
def __eq__(self, other):
return self.__dict__ == other.__dict__
def parse_wcinfotime(timestr):
""" Returns seconds since epoch, UTC. """
# example: 2003-10-27 20:43:14 +0100 (Mon, 27 Oct 2003)
m = re.match(r'(\d+-\d+-\d+ \d+:\d+:\d+) ([+-]\d+) .*', timestr)
if not m:
raise ValueError("timestring %r does not match" % timestr)
timestr, timezone = m.groups()
# do not handle timezone specially, return value should be UTC
parsedtime = time.strptime(timestr, "%Y-%m-%d %H:%M:%S")
return calendar.timegm(parsedtime)
def make_recursive_propdict(wcroot,
output,
rex = re.compile("Properties on '(.*)':")):
""" Return a dictionary of path->PropListDict mappings. """
lines = [x for x in output.split('\n') if x]
pdict = {}
while lines:
line = lines.pop(0)
m = rex.match(line)
if not m:
raise ValueError("could not parse propget-line: %r" % line)
path = m.groups()[0]
wcpath = wcroot.join(path, abs=1)
propnames = []
while lines and lines[0].startswith(' '):
propname = lines.pop(0).strip()
propnames.append(propname)
assert propnames, "must have found properties!"
pdict[wcpath] = PropListDict(wcpath, propnames)
return pdict
def importxml(cache=[]):
if cache:
return cache
from xml.dom import minidom
from xml.parsers.expat import ExpatError
cache.extend([minidom, ExpatError])
return cache
class LogEntry:
def __init__(self, logentry):
self.rev = int(logentry.getAttribute('revision'))
for lpart in filter(None, logentry.childNodes):
if lpart.nodeType == lpart.ELEMENT_NODE:
if lpart.nodeName == 'author':
self.author = lpart.firstChild.nodeValue
elif lpart.nodeName == 'msg':
if lpart.firstChild:
self.msg = lpart.firstChild.nodeValue
else:
self.msg = ''
elif lpart.nodeName == 'date':
#2003-07-29T20:05:11.598637Z
timestr = lpart.firstChild.nodeValue
self.date = parse_apr_time(timestr)
elif lpart.nodeName == 'paths':
self.strpaths = []
for ppart in filter(None, lpart.childNodes):
if ppart.nodeType == ppart.ELEMENT_NODE:
self.strpaths.append(PathEntry(ppart))
def __repr__(self):
return '' % (
self.rev, self.author, self.date)
py-1.5.2/py/_path/cacheutil.py 0000664 0001750 0001750 00000006405 13202663617 017350 0 ustar vagrant vagrant 0000000 0000000 """
This module contains multithread-safe cache implementations.
All Caches have
getorbuild(key, builder)
delentry(key)
methods and allow configuration when instantiating the cache class.
"""
from time import time as gettime
class BasicCache(object):
def __init__(self, maxentries=128):
self.maxentries = maxentries
self.prunenum = int(maxentries - maxentries/8)
self._dict = {}
def clear(self):
self._dict.clear()
def _getentry(self, key):
return self._dict[key]
def _putentry(self, key, entry):
self._prunelowestweight()
self._dict[key] = entry
def delentry(self, key, raising=False):
try:
del self._dict[key]
except KeyError:
if raising:
raise
def getorbuild(self, key, builder):
try:
entry = self._getentry(key)
except KeyError:
entry = self._build(key, builder)
self._putentry(key, entry)
return entry.value
def _prunelowestweight(self):
""" prune out entries with lowest weight. """
numentries = len(self._dict)
if numentries >= self.maxentries:
# evict according to entry's weight
items = [(entry.weight, key)
for key, entry in self._dict.items()]
items.sort()
index = numentries - self.prunenum
if index > 0:
for weight, key in items[:index]:
# in MT situations the element might be gone
self.delentry(key, raising=False)
class BuildcostAccessCache(BasicCache):
""" A BuildTime/Access-counting cache implementation.
the weight of a value is computed as the product of
num-accesses-of-a-value * time-to-build-the-value
The values with the least such weights are evicted
if the cache maxentries threshold is superceded.
For implementation flexibility more than one object
might be evicted at a time.
"""
# time function to use for measuring build-times
def _build(self, key, builder):
start = gettime()
val = builder()
end = gettime()
return WeightedCountingEntry(val, end-start)
class WeightedCountingEntry(object):
def __init__(self, value, oneweight):
self._value = value
self.weight = self._oneweight = oneweight
def value(self):
self.weight += self._oneweight
return self._value
value = property(value)
class AgingCache(BasicCache):
""" This cache prunes out cache entries that are too old.
"""
def __init__(self, maxentries=128, maxseconds=10.0):
super(AgingCache, self).__init__(maxentries)
self.maxseconds = maxseconds
def _getentry(self, key):
entry = self._dict[key]
if entry.isexpired():
self.delentry(key)
raise KeyError(key)
return entry
def _build(self, key, builder):
val = builder()
entry = AgingEntry(val, gettime() + self.maxseconds)
return entry
class AgingEntry(object):
def __init__(self, value, expirationtime):
self.value = value
self.weight = expirationtime
def isexpired(self):
t = gettime()
return t >= self.weight
py-1.5.2/py/_path/svnurl.py 0000664 0001750 0001750 00000034573 13202663617 016747 0 ustar vagrant vagrant 0000000 0000000 """
module defining a subversion path object based on the external
command 'svn'. This modules aims to work with svn 1.3 and higher
but might also interact well with earlier versions.
"""
import os, sys, time, re
import py
from py import path, process
from py._path import common
from py._path import svnwc as svncommon
from py._path.cacheutil import BuildcostAccessCache, AgingCache
DEBUG=False
class SvnCommandPath(svncommon.SvnPathBase):
""" path implementation that offers access to (possibly remote) subversion
repositories. """
_lsrevcache = BuildcostAccessCache(maxentries=128)
_lsnorevcache = AgingCache(maxentries=1000, maxseconds=60.0)
def __new__(cls, path, rev=None, auth=None):
self = object.__new__(cls)
if isinstance(path, cls):
rev = path.rev
auth = path.auth
path = path.strpath
svncommon.checkbadchars(path)
path = path.rstrip('/')
self.strpath = path
self.rev = rev
self.auth = auth
return self
def __repr__(self):
if self.rev == -1:
return 'svnurl(%r)' % self.strpath
else:
return 'svnurl(%r, %r)' % (self.strpath, self.rev)
def _svnwithrev(self, cmd, *args):
""" execute an svn command, append our own url and revision """
if self.rev is None:
return self._svnwrite(cmd, *args)
else:
args = ['-r', self.rev] + list(args)
return self._svnwrite(cmd, *args)
def _svnwrite(self, cmd, *args):
""" execute an svn command, append our own url """
l = ['svn %s' % cmd]
args = ['"%s"' % self._escape(item) for item in args]
l.extend(args)
l.append('"%s"' % self._encodedurl())
# fixing the locale because we can't otherwise parse
string = " ".join(l)
if DEBUG:
print("execing %s" % string)
out = self._svncmdexecauth(string)
return out
def _svncmdexecauth(self, cmd):
""" execute an svn command 'as is' """
cmd = svncommon.fixlocale() + cmd
if self.auth is not None:
cmd += ' ' + self.auth.makecmdoptions()
return self._cmdexec(cmd)
def _cmdexec(self, cmd):
try:
out = process.cmdexec(cmd)
except py.process.cmdexec.Error:
e = sys.exc_info()[1]
if (e.err.find('File Exists') != -1 or
e.err.find('File already exists') != -1):
raise py.error.EEXIST(self)
raise
return out
def _svnpopenauth(self, cmd):
""" execute an svn command, return a pipe for reading stdin """
cmd = svncommon.fixlocale() + cmd
if self.auth is not None:
cmd += ' ' + self.auth.makecmdoptions()
return self._popen(cmd)
def _popen(self, cmd):
return os.popen(cmd)
def _encodedurl(self):
return self._escape(self.strpath)
def _norev_delentry(self, path):
auth = self.auth and self.auth.makecmdoptions() or None
self._lsnorevcache.delentry((str(path), auth))
def open(self, mode='r'):
""" return an opened file with the given mode. """
if mode not in ("r", "rU",):
raise ValueError("mode %r not supported" % (mode,))
assert self.check(file=1) # svn cat returns an empty file otherwise
if self.rev is None:
return self._svnpopenauth('svn cat "%s"' % (
self._escape(self.strpath), ))
else:
return self._svnpopenauth('svn cat -r %s "%s"' % (
self.rev, self._escape(self.strpath)))
def dirpath(self, *args, **kwargs):
""" return the directory path of the current path joined
with any given path arguments.
"""
l = self.strpath.split(self.sep)
if len(l) < 4:
raise py.error.EINVAL(self, "base is not valid")
elif len(l) == 4:
return self.join(*args, **kwargs)
else:
return self.new(basename='').join(*args, **kwargs)
# modifying methods (cache must be invalidated)
def mkdir(self, *args, **kwargs):
""" create & return the directory joined with args.
pass a 'msg' keyword argument to set the commit message.
"""
commit_msg = kwargs.get('msg', "mkdir by py lib invocation")
createpath = self.join(*args)
createpath._svnwrite('mkdir', '-m', commit_msg)
self._norev_delentry(createpath.dirpath())
return createpath
def copy(self, target, msg='copied by py lib invocation'):
""" copy path to target with checkin message msg."""
if getattr(target, 'rev', None) is not None:
raise py.error.EINVAL(target, "revisions are immutable")
self._svncmdexecauth('svn copy -m "%s" "%s" "%s"' %(msg,
self._escape(self), self._escape(target)))
self._norev_delentry(target.dirpath())
def rename(self, target, msg="renamed by py lib invocation"):
""" rename this path to target with checkin message msg. """
if getattr(self, 'rev', None) is not None:
raise py.error.EINVAL(self, "revisions are immutable")
self._svncmdexecauth('svn move -m "%s" --force "%s" "%s"' %(
msg, self._escape(self), self._escape(target)))
self._norev_delentry(self.dirpath())
self._norev_delentry(self)
def remove(self, rec=1, msg='removed by py lib invocation'):
""" remove a file or directory (or a directory tree if rec=1) with
checkin message msg."""
if self.rev is not None:
raise py.error.EINVAL(self, "revisions are immutable")
self._svncmdexecauth('svn rm -m "%s" "%s"' %(msg, self._escape(self)))
self._norev_delentry(self.dirpath())
def export(self, topath):
""" export to a local path
topath should not exist prior to calling this, returns a
py.path.local instance
"""
topath = py.path.local(topath)
args = ['"%s"' % (self._escape(self),),
'"%s"' % (self._escape(topath),)]
if self.rev is not None:
args = ['-r', str(self.rev)] + args
self._svncmdexecauth('svn export %s' % (' '.join(args),))
return topath
def ensure(self, *args, **kwargs):
""" ensure that an args-joined path exists (by default as
a file). If you specify a keyword argument 'dir=True'
then the path is forced to be a directory path.
"""
if getattr(self, 'rev', None) is not None:
raise py.error.EINVAL(self, "revisions are immutable")
target = self.join(*args)
dir = kwargs.get('dir', 0)
for x in target.parts(reverse=True):
if x.check():
break
else:
raise py.error.ENOENT(target, "has not any valid base!")
if x == target:
if not x.check(dir=dir):
raise dir and py.error.ENOTDIR(x) or py.error.EISDIR(x)
return x
tocreate = target.relto(x)
basename = tocreate.split(self.sep, 1)[0]
tempdir = py.path.local.mkdtemp()
try:
tempdir.ensure(tocreate, dir=dir)
cmd = 'svn import -m "%s" "%s" "%s"' % (
"ensure %s" % self._escape(tocreate),
self._escape(tempdir.join(basename)),
x.join(basename)._encodedurl())
self._svncmdexecauth(cmd)
self._norev_delentry(x)
finally:
tempdir.remove()
return target
# end of modifying methods
def _propget(self, name):
res = self._svnwithrev('propget', name)
return res[:-1] # strip trailing newline
def _proplist(self):
res = self._svnwithrev('proplist')
lines = res.split('\n')
lines = [x.strip() for x in lines[1:]]
return svncommon.PropListDict(self, lines)
def info(self):
""" return an Info structure with svn-provided information. """
parent = self.dirpath()
nameinfo_seq = parent._listdir_nameinfo()
bn = self.basename
for name, info in nameinfo_seq:
if name == bn:
return info
raise py.error.ENOENT(self)
def _listdir_nameinfo(self):
""" return sequence of name-info directory entries of self """
def builder():
try:
res = self._svnwithrev('ls', '-v')
except process.cmdexec.Error:
e = sys.exc_info()[1]
if e.err.find('non-existent in that revision') != -1:
raise py.error.ENOENT(self, e.err)
elif e.err.find("E200009:") != -1:
raise py.error.ENOENT(self, e.err)
elif e.err.find('File not found') != -1:
raise py.error.ENOENT(self, e.err)
elif e.err.find('not part of a repository')!=-1:
raise py.error.ENOENT(self, e.err)
elif e.err.find('Unable to open')!=-1:
raise py.error.ENOENT(self, e.err)
elif e.err.lower().find('method not allowed')!=-1:
raise py.error.EACCES(self, e.err)
raise py.error.Error(e.err)
lines = res.split('\n')
nameinfo_seq = []
for lsline in lines:
if lsline:
info = InfoSvnCommand(lsline)
if info._name != '.': # svn 1.5 produces '.' dirs,
nameinfo_seq.append((info._name, info))
nameinfo_seq.sort()
return nameinfo_seq
auth = self.auth and self.auth.makecmdoptions() or None
if self.rev is not None:
return self._lsrevcache.getorbuild((self.strpath, self.rev, auth),
builder)
else:
return self._lsnorevcache.getorbuild((self.strpath, auth),
builder)
def listdir(self, fil=None, sort=None):
""" list directory contents, possibly filter by the given fil func
and possibly sorted.
"""
if isinstance(fil, str):
fil = common.FNMatcher(fil)
nameinfo_seq = self._listdir_nameinfo()
if len(nameinfo_seq) == 1:
name, info = nameinfo_seq[0]
if name == self.basename and info.kind == 'file':
#if not self.check(dir=1):
raise py.error.ENOTDIR(self)
paths = [self.join(name) for (name, info) in nameinfo_seq]
if fil:
paths = [x for x in paths if fil(x)]
self._sortlist(paths, sort)
return paths
def log(self, rev_start=None, rev_end=1, verbose=False):
""" return a list of LogEntry instances for this path.
rev_start is the starting revision (defaulting to the first one).
rev_end is the last revision (defaulting to HEAD).
if verbose is True, then the LogEntry instances also know which files changed.
"""
assert self.check() #make it simpler for the pipe
rev_start = rev_start is None and "HEAD" or rev_start
rev_end = rev_end is None and "HEAD" or rev_end
if rev_start == "HEAD" and rev_end == 1:
rev_opt = ""
else:
rev_opt = "-r %s:%s" % (rev_start, rev_end)
verbose_opt = verbose and "-v" or ""
xmlpipe = self._svnpopenauth('svn log --xml %s %s "%s"' %
(rev_opt, verbose_opt, self.strpath))
from xml.dom import minidom
tree = minidom.parse(xmlpipe)
result = []
for logentry in filter(None, tree.firstChild.childNodes):
if logentry.nodeType == logentry.ELEMENT_NODE:
result.append(svncommon.LogEntry(logentry))
return result
#01234567890123456789012345678901234567890123467
# 2256 hpk 165 Nov 24 17:55 __init__.py
# XXX spotted by Guido, SVN 1.3.0 has different aligning, breaks the code!!!
# 1312 johnny 1627 May 05 14:32 test_decorators.py
#
class InfoSvnCommand:
# the '0?' part in the middle is an indication of whether the resource is
# locked, see 'svn help ls'
lspattern = re.compile(
r'^ *(?P\d+) +(?P.+?) +(0? *(?P\d+))? '
r'*(?P\w+ +\d{2} +[\d:]+) +(?P.*)$')
def __init__(self, line):
# this is a typical line from 'svn ls http://...'
#_ 1127 jum 0 Jul 13 15:28 branch/
match = self.lspattern.match(line)
data = match.groupdict()
self._name = data['file']
if self._name[-1] == '/':
self._name = self._name[:-1]
self.kind = 'dir'
else:
self.kind = 'file'
#self.has_props = l.pop(0) == 'P'
self.created_rev = int(data['rev'])
self.last_author = data['author']
self.size = data['size'] and int(data['size']) or 0
self.mtime = parse_time_with_missing_year(data['date'])
self.time = self.mtime * 1000000
def __eq__(self, other):
return self.__dict__ == other.__dict__
#____________________________________________________
#
# helper functions
#____________________________________________________
def parse_time_with_missing_year(timestr):
""" analyze the time part from a single line of "svn ls -v"
the svn output doesn't show the year makes the 'timestr'
ambigous.
"""
import calendar
t_now = time.gmtime()
tparts = timestr.split()
month = time.strptime(tparts.pop(0), '%b')[1]
day = time.strptime(tparts.pop(0), '%d')[2]
last = tparts.pop(0) # year or hour:minute
try:
if ":" in last:
raise ValueError()
year = time.strptime(last, '%Y')[0]
hour = minute = 0
except ValueError:
hour, minute = time.strptime(last, '%H:%M')[3:5]
year = t_now[0]
t_result = (year, month, day, hour, minute, 0,0,0,0)
if t_result > t_now:
year -= 1
t_result = (year, month, day, hour, minute, 0,0,0,0)
return calendar.timegm(t_result)
class PathEntry:
def __init__(self, ppart):
self.strpath = ppart.firstChild.nodeValue.encode('UTF-8')
self.action = ppart.getAttribute('action').encode('UTF-8')
if self.action == 'A':
self.copyfrom_path = ppart.getAttribute('copyfrom-path').encode('UTF-8')
if self.copyfrom_path:
self.copyfrom_rev = int(ppart.getAttribute('copyfrom-rev'))
py-1.5.2/py/_path/local.py 0000664 0001750 0001750 00000105022 13202663755 016477 0 ustar vagrant vagrant 0000000 0000000 """
local path implementation.
"""
from __future__ import with_statement
from contextlib import contextmanager
import sys, os, re, atexit, io, uuid
import py
from py._path import common
from py._path.common import iswin32, fspath
from stat import S_ISLNK, S_ISDIR, S_ISREG
from os.path import abspath, normcase, normpath, isabs, exists, isdir, isfile, islink, dirname
if sys.version_info > (3,0):
def map_as_list(func, iter):
return list(map(func, iter))
else:
map_as_list = map
class Stat(object):
def __getattr__(self, name):
return getattr(self._osstatresult, "st_" + name)
def __init__(self, path, osstatresult):
self.path = path
self._osstatresult = osstatresult
@property
def owner(self):
if iswin32:
raise NotImplementedError("XXX win32")
import pwd
entry = py.error.checked_call(pwd.getpwuid, self.uid)
return entry[0]
@property
def group(self):
""" return group name of file. """
if iswin32:
raise NotImplementedError("XXX win32")
import grp
entry = py.error.checked_call(grp.getgrgid, self.gid)
return entry[0]
def isdir(self):
return S_ISDIR(self._osstatresult.st_mode)
def isfile(self):
return S_ISREG(self._osstatresult.st_mode)
def islink(self):
st = self.path.lstat()
return S_ISLNK(self._osstatresult.st_mode)
class PosixPath(common.PathBase):
def chown(self, user, group, rec=0):
""" change ownership to the given user and group.
user and group may be specified by a number or
by a name. if rec is True change ownership
recursively.
"""
uid = getuserid(user)
gid = getgroupid(group)
if rec:
for x in self.visit(rec=lambda x: x.check(link=0)):
if x.check(link=0):
py.error.checked_call(os.chown, str(x), uid, gid)
py.error.checked_call(os.chown, str(self), uid, gid)
def readlink(self):
""" return value of a symbolic link. """
return py.error.checked_call(os.readlink, self.strpath)
def mklinkto(self, oldname):
""" posix style hard link to another name. """
py.error.checked_call(os.link, str(oldname), str(self))
def mksymlinkto(self, value, absolute=1):
""" create a symbolic link with the given value (pointing to another name). """
if absolute:
py.error.checked_call(os.symlink, str(value), self.strpath)
else:
base = self.common(value)
# with posix local paths '/' is always a common base
relsource = self.__class__(value).relto(base)
reldest = self.relto(base)
n = reldest.count(self.sep)
target = self.sep.join(('..', )*n + (relsource, ))
py.error.checked_call(os.symlink, target, self.strpath)
def getuserid(user):
import pwd
if not isinstance(user, int):
user = pwd.getpwnam(user)[2]
return user
def getgroupid(group):
import grp
if not isinstance(group, int):
group = grp.getgrnam(group)[2]
return group
FSBase = not iswin32 and PosixPath or common.PathBase
class LocalPath(FSBase):
""" object oriented interface to os.path and other local filesystem
related information.
"""
class ImportMismatchError(ImportError):
""" raised on pyimport() if there is a mismatch of __file__'s"""
sep = os.sep
class Checkers(common.Checkers):
def _stat(self):
try:
return self._statcache
except AttributeError:
try:
self._statcache = self.path.stat()
except py.error.ELOOP:
self._statcache = self.path.lstat()
return self._statcache
def dir(self):
return S_ISDIR(self._stat().mode)
def file(self):
return S_ISREG(self._stat().mode)
def exists(self):
return self._stat()
def link(self):
st = self.path.lstat()
return S_ISLNK(st.mode)
def __init__(self, path=None, expanduser=False):
""" Initialize and return a local Path instance.
Path can be relative to the current directory.
If path is None it defaults to the current working directory.
If expanduser is True, tilde-expansion is performed.
Note that Path instances always carry an absolute path.
Note also that passing in a local path object will simply return
the exact same path object. Use new() to get a new copy.
"""
if path is None:
self.strpath = py.error.checked_call(os.getcwd)
else:
try:
path = fspath(path)
except TypeError:
raise ValueError("can only pass None, Path instances "
"or non-empty strings to LocalPath")
if expanduser:
path = os.path.expanduser(path)
self.strpath = abspath(path)
def __hash__(self):
return hash(self.strpath)
def __eq__(self, other):
s1 = fspath(self)
try:
s2 = fspath(other)
except TypeError:
return False
if iswin32:
s1 = s1.lower()
try:
s2 = s2.lower()
except AttributeError:
return False
return s1 == s2
def __ne__(self, other):
return not (self == other)
def __lt__(self, other):
return fspath(self) < fspath(other)
def __gt__(self, other):
return fspath(self) > fspath(other)
def samefile(self, other):
""" return True if 'other' references the same file as 'self'.
"""
other = fspath(other)
if not isabs(other):
other = abspath(other)
if self == other:
return True
if iswin32:
return False # there is no samefile
return py.error.checked_call(
os.path.samefile, self.strpath, other)
def remove(self, rec=1, ignore_errors=False):
""" remove a file or directory (or a directory tree if rec=1).
if ignore_errors is True, errors while removing directories will
be ignored.
"""
if self.check(dir=1, link=0):
if rec:
# force remove of readonly files on windows
if iswin32:
self.chmod(0o700, rec=1)
import shutil
py.error.checked_call(
shutil.rmtree, self.strpath,
ignore_errors=ignore_errors)
else:
py.error.checked_call(os.rmdir, self.strpath)
else:
if iswin32:
self.chmod(0o700)
py.error.checked_call(os.remove, self.strpath)
def computehash(self, hashtype="md5", chunksize=524288):
""" return hexdigest of hashvalue for this file. """
try:
try:
import hashlib as mod
except ImportError:
if hashtype == "sha1":
hashtype = "sha"
mod = __import__(hashtype)
hash = getattr(mod, hashtype)()
except (AttributeError, ImportError):
raise ValueError("Don't know how to compute %r hash" %(hashtype,))
f = self.open('rb')
try:
while 1:
buf = f.read(chunksize)
if not buf:
return hash.hexdigest()
hash.update(buf)
finally:
f.close()
def new(self, **kw):
""" create a modified version of this path.
the following keyword arguments modify various path parts::
a:/some/path/to/a/file.ext
xx drive
xxxxxxxxxxxxxxxxx dirname
xxxxxxxx basename
xxxx purebasename
xxx ext
"""
obj = object.__new__(self.__class__)
if not kw:
obj.strpath = self.strpath
return obj
drive, dirname, basename, purebasename,ext = self._getbyspec(
"drive,dirname,basename,purebasename,ext")
if 'basename' in kw:
if 'purebasename' in kw or 'ext' in kw:
raise ValueError("invalid specification %r" % kw)
else:
pb = kw.setdefault('purebasename', purebasename)
try:
ext = kw['ext']
except KeyError:
pass
else:
if ext and not ext.startswith('.'):
ext = '.' + ext
kw['basename'] = pb + ext
if ('dirname' in kw and not kw['dirname']):
kw['dirname'] = drive
else:
kw.setdefault('dirname', dirname)
kw.setdefault('sep', self.sep)
obj.strpath = normpath(
"%(dirname)s%(sep)s%(basename)s" % kw)
return obj
def _getbyspec(self, spec):
""" see new for what 'spec' can be. """
res = []
parts = self.strpath.split(self.sep)
args = filter(None, spec.split(',') )
append = res.append
for name in args:
if name == 'drive':
append(parts[0])
elif name == 'dirname':
append(self.sep.join(parts[:-1]))
else:
basename = parts[-1]
if name == 'basename':
append(basename)
else:
i = basename.rfind('.')
if i == -1:
purebasename, ext = basename, ''
else:
purebasename, ext = basename[:i], basename[i:]
if name == 'purebasename':
append(purebasename)
elif name == 'ext':
append(ext)
else:
raise ValueError("invalid part specification %r" % name)
return res
def dirpath(self, *args, **kwargs):
""" return the directory path joined with any given path arguments. """
if not kwargs:
path = object.__new__(self.__class__)
path.strpath = dirname(self.strpath)
if args:
path = path.join(*args)
return path
return super(LocalPath, self).dirpath(*args, **kwargs)
def join(self, *args, **kwargs):
""" return a new path by appending all 'args' as path
components. if abs=1 is used restart from root if any
of the args is an absolute path.
"""
sep = self.sep
strargs = [fspath(arg) for arg in args]
strpath = self.strpath
if kwargs.get('abs'):
newargs = []
for arg in reversed(strargs):
if isabs(arg):
strpath = arg
strargs = newargs
break
newargs.insert(0, arg)
# special case for when we have e.g. strpath == "/"
actual_sep = "" if strpath.endswith(sep) else sep
for arg in strargs:
arg = arg.strip(sep)
if iswin32:
# allow unix style paths even on windows.
arg = arg.strip('/')
arg = arg.replace('/', sep)
strpath = strpath + actual_sep + arg
actual_sep = sep
obj = object.__new__(self.__class__)
obj.strpath = normpath(strpath)
return obj
def open(self, mode='r', ensure=False, encoding=None):
""" return an opened file with the given mode.
If ensure is True, create parent directories if needed.
"""
if ensure:
self.dirpath().ensure(dir=1)
if encoding:
return py.error.checked_call(io.open, self.strpath, mode, encoding=encoding)
return py.error.checked_call(open, self.strpath, mode)
def _fastjoin(self, name):
child = object.__new__(self.__class__)
child.strpath = self.strpath + self.sep + name
return child
def islink(self):
return islink(self.strpath)
def check(self, **kw):
if not kw:
return exists(self.strpath)
if len(kw) == 1:
if "dir" in kw:
return not kw["dir"] ^ isdir(self.strpath)
if "file" in kw:
return not kw["file"] ^ isfile(self.strpath)
return super(LocalPath, self).check(**kw)
_patternchars = set("*?[" + os.path.sep)
def listdir(self, fil=None, sort=None):
""" list directory contents, possibly filter by the given fil func
and possibly sorted.
"""
if fil is None and sort is None:
names = py.error.checked_call(os.listdir, self.strpath)
return map_as_list(self._fastjoin, names)
if isinstance(fil, py.builtin._basestring):
if not self._patternchars.intersection(fil):
child = self._fastjoin(fil)
if exists(child.strpath):
return [child]
return []
fil = common.FNMatcher(fil)
names = py.error.checked_call(os.listdir, self.strpath)
res = []
for name in names:
child = self._fastjoin(name)
if fil is None or fil(child):
res.append(child)
self._sortlist(res, sort)
return res
def size(self):
""" return size of the underlying file object """
return self.stat().size
def mtime(self):
""" return last modification time of the path. """
return self.stat().mtime
def copy(self, target, mode=False, stat=False):
""" copy path to target.
If mode is True, will copy copy permission from path to target.
If stat is True, copy permission, last modification
time, last access time, and flags from path to target.
"""
if self.check(file=1):
if target.check(dir=1):
target = target.join(self.basename)
assert self!=target
copychunked(self, target)
if mode:
copymode(self.strpath, target.strpath)
if stat:
copystat(self, target)
else:
def rec(p):
return p.check(link=0)
for x in self.visit(rec=rec):
relpath = x.relto(self)
newx = target.join(relpath)
newx.dirpath().ensure(dir=1)
if x.check(link=1):
newx.mksymlinkto(x.readlink())
continue
elif x.check(file=1):
copychunked(x, newx)
elif x.check(dir=1):
newx.ensure(dir=1)
if mode:
copymode(x.strpath, newx.strpath)
if stat:
copystat(x, newx)
def rename(self, target):
""" rename this path to target. """
target = fspath(target)
return py.error.checked_call(os.rename, self.strpath, target)
def dump(self, obj, bin=1):
""" pickle object into path location"""
f = self.open('wb')
import pickle
try:
py.error.checked_call(pickle.dump, obj, f, bin)
finally:
f.close()
def mkdir(self, *args):
""" create & return the directory joined with args. """
p = self.join(*args)
py.error.checked_call(os.mkdir, fspath(p))
return p
def write_binary(self, data, ensure=False):
""" write binary data into path. If ensure is True create
missing parent directories.
"""
if ensure:
self.dirpath().ensure(dir=1)
with self.open('wb') as f:
f.write(data)
def write_text(self, data, encoding, ensure=False):
""" write text data into path using the specified encoding.
If ensure is True create missing parent directories.
"""
if ensure:
self.dirpath().ensure(dir=1)
with self.open('w', encoding=encoding) as f:
f.write(data)
def write(self, data, mode='w', ensure=False):
""" write data into path. If ensure is True create
missing parent directories.
"""
if ensure:
self.dirpath().ensure(dir=1)
if 'b' in mode:
if not py.builtin._isbytes(data):
raise ValueError("can only process bytes")
else:
if not py.builtin._istext(data):
if not py.builtin._isbytes(data):
data = str(data)
else:
data = py.builtin._totext(data, sys.getdefaultencoding())
f = self.open(mode)
try:
f.write(data)
finally:
f.close()
def _ensuredirs(self):
parent = self.dirpath()
if parent == self:
return self
if parent.check(dir=0):
parent._ensuredirs()
if self.check(dir=0):
try:
self.mkdir()
except py.error.EEXIST:
# race condition: file/dir created by another thread/process.
# complain if it is not a dir
if self.check(dir=0):
raise
return self
def ensure(self, *args, **kwargs):
""" ensure that an args-joined path exists (by default as
a file). if you specify a keyword argument 'dir=True'
then the path is forced to be a directory path.
"""
p = self.join(*args)
if kwargs.get('dir', 0):
return p._ensuredirs()
else:
p.dirpath()._ensuredirs()
if not p.check(file=1):
p.open('w').close()
return p
def stat(self, raising=True):
""" Return an os.stat() tuple. """
if raising == True:
return Stat(self, py.error.checked_call(os.stat, self.strpath))
try:
return Stat(self, os.stat(self.strpath))
except KeyboardInterrupt:
raise
except Exception:
return None
def lstat(self):
""" Return an os.lstat() tuple. """
return Stat(self, py.error.checked_call(os.lstat, self.strpath))
def setmtime(self, mtime=None):
""" set modification time for the given path. if 'mtime' is None
(the default) then the file's mtime is set to current time.
Note that the resolution for 'mtime' is platform dependent.
"""
if mtime is None:
return py.error.checked_call(os.utime, self.strpath, mtime)
try:
return py.error.checked_call(os.utime, self.strpath, (-1, mtime))
except py.error.EINVAL:
return py.error.checked_call(os.utime, self.strpath, (self.atime(), mtime))
def chdir(self):
""" change directory to self and return old current directory """
try:
old = self.__class__()
except py.error.ENOENT:
old = None
py.error.checked_call(os.chdir, self.strpath)
return old
@contextmanager
def as_cwd(self):
""" return context manager which changes to current dir during the
managed "with" context. On __enter__ it returns the old dir.
"""
old = self.chdir()
try:
yield old
finally:
old.chdir()
def realpath(self):
""" return a new path which contains no symbolic links."""
return self.__class__(os.path.realpath(self.strpath))
def atime(self):
""" return last access time of the path. """
return self.stat().atime
def __repr__(self):
return 'local(%r)' % self.strpath
def __str__(self):
""" return string representation of the Path. """
return self.strpath
def chmod(self, mode, rec=0):
""" change permissions to the given mode. If mode is an
integer it directly encodes the os-specific modes.
if rec is True perform recursively.
"""
if not isinstance(mode, int):
raise TypeError("mode %r must be an integer" % (mode,))
if rec:
for x in self.visit(rec=rec):
py.error.checked_call(os.chmod, str(x), mode)
py.error.checked_call(os.chmod, self.strpath, mode)
def pypkgpath(self):
""" return the Python package path by looking for the last
directory upwards which still contains an __init__.py.
Return None if a pkgpath can not be determined.
"""
pkgpath = None
for parent in self.parts(reverse=True):
if parent.isdir():
if not parent.join('__init__.py').exists():
break
if not isimportable(parent.basename):
break
pkgpath = parent
return pkgpath
def _ensuresyspath(self, ensuremode, path):
if ensuremode:
s = str(path)
if ensuremode == "append":
if s not in sys.path:
sys.path.append(s)
else:
if s != sys.path[0]:
sys.path.insert(0, s)
def pyimport(self, modname=None, ensuresyspath=True):
""" return path as an imported python module.
If modname is None, look for the containing package
and construct an according module name.
The module will be put/looked up in sys.modules.
if ensuresyspath is True then the root dir for importing
the file (taking __init__.py files into account) will
be prepended to sys.path if it isn't there already.
If ensuresyspath=="append" the root dir will be appended
if it isn't already contained in sys.path.
if ensuresyspath is False no modification of syspath happens.
"""
if not self.check():
raise py.error.ENOENT(self)
pkgpath = None
if modname is None:
pkgpath = self.pypkgpath()
if pkgpath is not None:
pkgroot = pkgpath.dirpath()
names = self.new(ext="").relto(pkgroot).split(self.sep)
if names[-1] == "__init__":
names.pop()
modname = ".".join(names)
else:
pkgroot = self.dirpath()
modname = self.purebasename
self._ensuresyspath(ensuresyspath, pkgroot)
__import__(modname)
mod = sys.modules[modname]
if self.basename == "__init__.py":
return mod # we don't check anything as we might
# we in a namespace package ... too icky to check
modfile = mod.__file__
if modfile[-4:] in ('.pyc', '.pyo'):
modfile = modfile[:-1]
elif modfile.endswith('$py.class'):
modfile = modfile[:-9] + '.py'
if modfile.endswith(os.path.sep + "__init__.py"):
if self.basename != "__init__.py":
modfile = modfile[:-12]
try:
issame = self.samefile(modfile)
except py.error.ENOENT:
issame = False
if not issame:
raise self.ImportMismatchError(modname, modfile, self)
return mod
else:
try:
return sys.modules[modname]
except KeyError:
# we have a custom modname, do a pseudo-import
import types
mod = types.ModuleType(modname)
mod.__file__ = str(self)
sys.modules[modname] = mod
try:
py.builtin.execfile(str(self), mod.__dict__)
except:
del sys.modules[modname]
raise
return mod
def sysexec(self, *argv, **popen_opts):
""" return stdout text from executing a system child process,
where the 'self' path points to executable.
The process is directly invoked and not through a system shell.
"""
from subprocess import Popen, PIPE
argv = map_as_list(str, argv)
popen_opts['stdout'] = popen_opts['stderr'] = PIPE
proc = Popen([str(self)] + argv, **popen_opts)
stdout, stderr = proc.communicate()
ret = proc.wait()
if py.builtin._isbytes(stdout):
stdout = py.builtin._totext(stdout, sys.getdefaultencoding())
if ret != 0:
if py.builtin._isbytes(stderr):
stderr = py.builtin._totext(stderr, sys.getdefaultencoding())
raise py.process.cmdexec.Error(ret, ret, str(self),
stdout, stderr,)
return stdout
def sysfind(cls, name, checker=None, paths=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
cannot be found, None is returned
Note: This is probably not working on plain win32 systems
but may work on cygwin.
"""
if isabs(name):
p = py.path.local(name)
if p.check(file=1):
return p
else:
if paths is None:
if iswin32:
paths = os.environ['Path'].split(';')
if '' not in paths and '.' not in paths:
paths.append('.')
try:
systemroot = os.environ['SYSTEMROOT']
except KeyError:
pass
else:
paths = [path.replace('%SystemRoot%', systemroot)
for path in paths]
else:
paths = os.environ['PATH'].split(':')
tryadd = []
if iswin32:
tryadd += os.environ['PATHEXT'].split(os.pathsep)
tryadd.append("")
for x in paths:
for addext in tryadd:
p = py.path.local(x).join(name, abs=True) + addext
try:
if p.check(file=1):
if checker:
if not checker(p):
continue
return p
except py.error.EACCES:
pass
return None
sysfind = classmethod(sysfind)
def _gethomedir(cls):
try:
x = os.environ['HOME']
except KeyError:
try:
x = os.environ["HOMEDRIVE"] + os.environ['HOMEPATH']
except KeyError:
return None
return cls(x)
_gethomedir = classmethod(_gethomedir)
# """
# special class constructors for local filesystem paths
# """
@classmethod
def get_temproot(cls):
""" return the system's temporary directory
(where tempfiles are usually created in)
"""
import tempfile
return py.path.local(tempfile.gettempdir())
@classmethod
def mkdtemp(cls, rootdir=None):
""" return a Path object pointing to a fresh new temporary directory
(which we created ourself).
"""
import tempfile
if rootdir is None:
rootdir = cls.get_temproot()
return cls(py.error.checked_call(tempfile.mkdtemp, dir=str(rootdir)))
def make_numbered_dir(cls, prefix='session-', rootdir=None, keep=3,
lock_timeout = 172800): # two days
""" return unique directory with a number greater than the current
maximum one. The number is assumed to start directly after prefix.
if keep is true directories with a number less than (maxnum-keep)
will be removed. If .lock files are used (lock_timeout non-zero),
algorithm is multi-process safe.
"""
if rootdir is None:
rootdir = cls.get_temproot()
nprefix = normcase(prefix)
def parse_num(path):
""" parse the number out of a path (if it matches the prefix) """
nbasename = normcase(path.basename)
if nbasename.startswith(nprefix):
try:
return int(nbasename[len(nprefix):])
except ValueError:
pass
def create_lockfile(path):
""" exclusively create lockfile. Throws when failed """
mypid = os.getpid()
lockfile = path.join('.lock')
if hasattr(lockfile, 'mksymlinkto'):
lockfile.mksymlinkto(str(mypid))
else:
fd = py.error.checked_call(os.open, str(lockfile), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644)
with os.fdopen(fd, 'w') as f:
f.write(str(mypid))
return lockfile
def atexit_remove_lockfile(lockfile):
""" ensure lockfile is removed at process exit """
mypid = os.getpid()
def try_remove_lockfile():
# in a fork() situation, only the last process should
# remove the .lock, otherwise the other processes run the
# risk of seeing their temporary dir disappear. For now
# we remove the .lock in the parent only (i.e. we assume
# that the children finish before the parent).
if os.getpid() != mypid:
return
try:
lockfile.remove()
except py.error.Error:
pass
atexit.register(try_remove_lockfile)
# compute the maximum number currently in use with the prefix
lastmax = None
while True:
maxnum = -1
for path in rootdir.listdir():
num = parse_num(path)
if num is not None:
maxnum = max(maxnum, num)
# make the new directory
try:
udir = rootdir.mkdir(prefix + str(maxnum+1))
if lock_timeout:
lockfile = create_lockfile(udir)
atexit_remove_lockfile(lockfile)
except (py.error.EEXIST, py.error.ENOENT, py.error.EBUSY):
# race condition (1): another thread/process created the dir
# in the meantime - try again
# race condition (2): another thread/process spuriously acquired
# lock treating empty directory as candidate
# for removal - try again
# race condition (3): another thread/process tried to create the lock at
# the same time (happened in Python 3.3 on Windows)
# https://ci.appveyor.com/project/pytestbot/py/build/1.0.21/job/ffi85j4c0lqwsfwa
if lastmax == maxnum:
raise
lastmax = maxnum
continue
break
def get_mtime(path):
""" read file modification time """
try:
return path.lstat().mtime
except py.error.Error:
pass
garbage_prefix = prefix + 'garbage-'
def is_garbage(path):
""" check if path denotes directory scheduled for removal """
bn = path.basename
return bn.startswith(garbage_prefix)
# prune old directories
udir_time = get_mtime(udir)
if keep and udir_time:
for path in rootdir.listdir():
num = parse_num(path)
if num is not None and num <= (maxnum - keep):
try:
# try acquiring lock to remove directory as exclusive user
if lock_timeout:
create_lockfile(path)
except (py.error.EEXIST, py.error.ENOENT, py.error.EBUSY):
path_time = get_mtime(path)
if not path_time:
# assume directory doesn't exist now
continue
if abs(udir_time - path_time) < lock_timeout:
# assume directory with lockfile exists
# and lock timeout hasn't expired yet
continue
# path dir locked for exclusive use
# and scheduled for removal to avoid another thread/process
# treating it as a new directory or removal candidate
garbage_path = rootdir.join(garbage_prefix + str(uuid.uuid4()))
try:
path.rename(garbage_path)
garbage_path.remove(rec=1)
except KeyboardInterrupt:
raise
except: # this might be py.error.Error, WindowsError ...
pass
if is_garbage(path):
try:
path.remove(rec=1)
except KeyboardInterrupt:
raise
except: # this might be py.error.Error, WindowsError ...
pass
# make link...
try:
username = os.environ['USER'] #linux, et al
except KeyError:
try:
username = os.environ['USERNAME'] #windows
except KeyError:
username = 'current'
src = str(udir)
dest = src[:src.rfind('-')] + '-' + username
try:
os.unlink(dest)
except OSError:
pass
try:
os.symlink(src, dest)
except (OSError, AttributeError, NotImplementedError):
pass
return udir
make_numbered_dir = classmethod(make_numbered_dir)
def copymode(src, dest):
""" copy permission from src to dst. """
import shutil
shutil.copymode(src, dest)
def copystat(src, dest):
""" copy permission, last modification time,
last access time, and flags from src to dst."""
import shutil
shutil.copystat(str(src), str(dest))
def copychunked(src, dest):
chunksize = 524288 # half a meg of bytes
fsrc = src.open('rb')
try:
fdest = dest.open('wb')
try:
while 1:
buf = fsrc.read(chunksize)
if not buf:
break
fdest.write(buf)
finally:
fdest.close()
finally:
fsrc.close()
def isimportable(name):
if name and (name[0].isalpha() or name[0] == '_'):
name = name.replace("_", '')
return not name or name.isalnum()
py-1.5.2/py/_path/common.py 0000664 0001750 0001750 00000034442 13202663755 016704 0 ustar vagrant vagrant 0000000 0000000 """
"""
import warnings
import os
import sys
import posixpath
import fnmatch
import py
# Moved from local.py.
iswin32 = sys.platform == "win32" or (getattr(os, '_name', False) == 'nt')
try:
from os import fspath
except ImportError:
def fspath(path):
"""
Return the string representation of the path.
If str or bytes is passed in, it is returned unchanged.
This code comes from PEP 519, modified to support earlier versions of
python.
This is required for python < 3.6.
"""
if isinstance(path, (py.builtin.text, py.builtin.bytes)):
return path
# Work from the object's type to match method resolution of other magic
# methods.
path_type = type(path)
try:
return path_type.__fspath__(path)
except AttributeError:
if hasattr(path_type, '__fspath__'):
raise
try:
import pathlib
except ImportError:
pass
else:
if isinstance(path, pathlib.PurePath):
return py.builtin.text(path)
raise TypeError("expected str, bytes or os.PathLike object, not "
+ path_type.__name__)
class Checkers:
_depend_on_existence = 'exists', 'link', 'dir', 'file'
def __init__(self, path):
self.path = path
def dir(self):
raise NotImplementedError
def file(self):
raise NotImplementedError
def dotfile(self):
return self.path.basename.startswith('.')
def ext(self, arg):
if not arg.startswith('.'):
arg = '.' + arg
return self.path.ext == arg
def exists(self):
raise NotImplementedError
def basename(self, arg):
return self.path.basename == arg
def basestarts(self, arg):
return self.path.basename.startswith(arg)
def relto(self, arg):
return self.path.relto(arg)
def fnmatch(self, arg):
return self.path.fnmatch(arg)
def endswith(self, arg):
return str(self.path).endswith(arg)
def _evaluate(self, kw):
for name, value in kw.items():
invert = False
meth = None
try:
meth = getattr(self, name)
except AttributeError:
if name[:3] == 'not':
invert = True
try:
meth = getattr(self, name[3:])
except AttributeError:
pass
if meth is None:
raise TypeError(
"no %r checker available for %r" % (name, self.path))
try:
if py.code.getrawcode(meth).co_argcount > 1:
if (not meth(value)) ^ invert:
return False
else:
if bool(value) ^ bool(meth()) ^ invert:
return False
except (py.error.ENOENT, py.error.ENOTDIR, py.error.EBUSY):
# EBUSY feels not entirely correct,
# but its kind of necessary since ENOMEDIUM
# is not accessible in python
for name in self._depend_on_existence:
if name in kw:
if kw.get(name):
return False
name = 'not' + name
if name in kw:
if not kw.get(name):
return False
return True
class NeverRaised(Exception):
pass
class PathBase(object):
""" shared implementation for filesystem path objects."""
Checkers = Checkers
def __div__(self, other):
return self.join(fspath(other))
__truediv__ = __div__ # py3k
def basename(self):
""" basename part of path. """
return self._getbyspec('basename')[0]
basename = property(basename, None, None, basename.__doc__)
def dirname(self):
""" dirname part of path. """
return self._getbyspec('dirname')[0]
dirname = property(dirname, None, None, dirname.__doc__)
def purebasename(self):
""" pure base name of the path."""
return self._getbyspec('purebasename')[0]
purebasename = property(purebasename, None, None, purebasename.__doc__)
def ext(self):
""" extension of the path (including the '.')."""
return self._getbyspec('ext')[0]
ext = property(ext, None, None, ext.__doc__)
def dirpath(self, *args, **kwargs):
""" return the directory path joined with any given path arguments. """
return self.new(basename='').join(*args, **kwargs)
def read_binary(self):
""" read and return a bytestring from reading the path. """
with self.open('rb') as f:
return f.read()
def read_text(self, encoding):
""" read and return a Unicode string from reading the path. """
with self.open("r", encoding=encoding) as f:
return f.read()
def read(self, mode='r'):
""" read and return a bytestring from reading the path. """
with self.open(mode) as f:
return f.read()
def readlines(self, cr=1):
""" read and return a list of lines from the path. if cr is False, the
newline will be removed from the end of each line. """
if sys.version_info < (3, ):
mode = 'rU'
else: # python 3 deprecates mode "U" in favor of "newline" option
mode = 'r'
if not cr:
content = self.read(mode)
return content.split('\n')
else:
f = self.open(mode)
try:
return f.readlines()
finally:
f.close()
def load(self):
""" (deprecated) return object unpickled from self.read() """
f = self.open('rb')
try:
import pickle
return py.error.checked_call(pickle.load, f)
finally:
f.close()
def move(self, target):
""" move this path to target. """
if target.relto(self):
raise py.error.EINVAL(
target,
"cannot move path into a subdirectory of itself")
try:
self.rename(target)
except py.error.EXDEV: # invalid cross-device link
self.copy(target)
self.remove()
def __repr__(self):
""" return a string representation of this path. """
return repr(str(self))
def check(self, **kw):
""" check a path for existence and properties.
Without arguments, return True if the path exists, otherwise False.
valid checkers::
file=1 # is a file
file=0 # is not a file (may not even exist)
dir=1 # is a dir
link=1 # is a link
exists=1 # exists
You can specify multiple checker definitions, for example::
path.check(file=1, link=1) # a link pointing to a file
"""
if not kw:
kw = {'exists': 1}
return self.Checkers(self)._evaluate(kw)
def fnmatch(self, pattern):
"""return true if the basename/fullname matches the glob-'pattern'.
valid pattern characters::
* matches everything
? matches any single character
[seq] matches any character in seq
[!seq] matches any char not in seq
If the pattern contains a path-separator then the full path
is used for pattern matching and a '*' is prepended to the
pattern.
if the pattern doesn't contain a path-separator the pattern
is only matched against the basename.
"""
return FNMatcher(pattern)(self)
def relto(self, relpath):
""" return a string which is the relative part of the path
to the given 'relpath'.
"""
if not isinstance(relpath, (str, PathBase)):
raise TypeError("%r: not a string or path object" %(relpath,))
strrelpath = str(relpath)
if strrelpath and strrelpath[-1] != self.sep:
strrelpath += self.sep
#assert strrelpath[-1] == self.sep
#assert strrelpath[-2] != self.sep
strself = self.strpath
if sys.platform == "win32" or getattr(os, '_name', None) == 'nt':
if os.path.normcase(strself).startswith(
os.path.normcase(strrelpath)):
return strself[len(strrelpath):]
elif strself.startswith(strrelpath):
return strself[len(strrelpath):]
return ""
def ensure_dir(self, *args):
""" ensure the path joined with args is a directory. """
return self.ensure(*args, **{"dir": True})
def bestrelpath(self, dest):
""" return a string which is a relative path from self
(assumed to be a directory) to dest such that
self.join(bestrelpath) == dest and if not such
path can be determined return dest.
"""
try:
if self == dest:
return os.curdir
base = self.common(dest)
if not base: # can be the case on windows
return str(dest)
self2base = self.relto(base)
reldest = dest.relto(base)
if self2base:
n = self2base.count(self.sep) + 1
else:
n = 0
l = [os.pardir] * n
if reldest:
l.append(reldest)
target = dest.sep.join(l)
return target
except AttributeError:
return str(dest)
def exists(self):
return self.check()
def isdir(self):
return self.check(dir=1)
def isfile(self):
return self.check(file=1)
def parts(self, reverse=False):
""" return a root-first list of all ancestor directories
plus the path itself.
"""
current = self
l = [self]
while 1:
last = current
current = current.dirpath()
if last == current:
break
l.append(current)
if not reverse:
l.reverse()
return l
def common(self, other):
""" return the common part shared with the other path
or None if there is no common part.
"""
last = None
for x, y in zip(self.parts(), other.parts()):
if x != y:
return last
last = x
return last
def __add__(self, other):
""" return new path object with 'other' added to the basename"""
return self.new(basename=self.basename+str(other))
def __cmp__(self, other):
""" return sort value (-1, 0, +1). """
try:
return cmp(self.strpath, other.strpath)
except AttributeError:
return cmp(str(self), str(other)) # self.path, other.path)
def __lt__(self, other):
try:
return self.strpath < other.strpath
except AttributeError:
return str(self) < str(other)
def visit(self, fil=None, rec=None, ignore=NeverRaised, bf=False, sort=False):
""" yields all paths below the current one
fil is a filter (glob pattern or callable), if not matching the
path will not be yielded, defaulting to None (everything is
returned)
rec is a filter (glob pattern or callable) that controls whether
a node is descended, defaulting to None
ignore is an Exception class that is ignoredwhen calling dirlist()
on any of the paths (by default, all exceptions are reported)
bf if True will cause a breadthfirst search instead of the
default depthfirst. Default: False
sort if True will sort entries within each directory level.
"""
for x in Visitor(fil, rec, ignore, bf, sort).gen(self):
yield x
def _sortlist(self, res, sort):
if sort:
if hasattr(sort, '__call__'):
warnings.warn(DeprecationWarning(
"listdir(sort=callable) is deprecated and breaks on python3"
), stacklevel=3)
res.sort(sort)
else:
res.sort()
def samefile(self, other):
""" return True if other refers to the same stat object as self. """
return self.strpath == str(other)
def __fspath__(self):
return self.strpath
class Visitor:
def __init__(self, fil, rec, ignore, bf, sort):
if isinstance(fil, py.builtin._basestring):
fil = FNMatcher(fil)
if isinstance(rec, py.builtin._basestring):
self.rec = FNMatcher(rec)
elif not hasattr(rec, '__call__') and rec:
self.rec = lambda path: True
else:
self.rec = rec
self.fil = fil
self.ignore = ignore
self.breadthfirst = bf
self.optsort = sort and sorted or (lambda x: x)
def gen(self, path):
try:
entries = path.listdir()
except self.ignore:
return
rec = self.rec
dirs = self.optsort([p for p in entries
if p.check(dir=1) and (rec is None or rec(p))])
if not self.breadthfirst:
for subdir in dirs:
for p in self.gen(subdir):
yield p
for p in self.optsort(entries):
if self.fil is None or self.fil(p):
yield p
if self.breadthfirst:
for subdir in dirs:
for p in self.gen(subdir):
yield p
class FNMatcher:
def __init__(self, pattern):
self.pattern = pattern
def __call__(self, path):
pattern = self.pattern
if (pattern.find(path.sep) == -1 and
iswin32 and
pattern.find(posixpath.sep) != -1):
# Running on Windows, the pattern has no Windows path separators,
# and the pattern has one or more Posix path separators. Replace
# the Posix path separators with the Windows path separator.
pattern = pattern.replace(posixpath.sep, path.sep)
if pattern.find(path.sep) == -1:
name = path.basename
else:
name = str(path) # path.strpath # XXX svn?
if not os.path.isabs(pattern):
pattern = '*' + path.sep + pattern
return fnmatch.fnmatch(name, pattern)
py-1.5.2/py/_path/__init__.py 0000664 0001750 0001750 00000000040 13202663617 017133 0 ustar vagrant vagrant 0000000 0000000 """ unified file system api """
py-1.5.2/doc/ 0000775 0001750 0001750 00000000000 13203150701 014034 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/doc/io.txt 0000664 0001750 0001750 00000002171 13202663617 015223 0 ustar vagrant vagrant 0000000 0000000 =======
py.io
=======
The 'py' lib provides helper classes for capturing IO during
execution of a program.
IO Capturing examples
===============================================
``py.io.StdCapture``
---------------------------
Basic Example::
>>> import py
>>> capture = py.io.StdCapture()
>>> print "hello"
>>> out,err = capture.reset()
>>> out.strip() == "hello"
True
For calling functions you may use a shortcut::
>>> import py
>>> def f(): print "hello"
>>> res, out, err = py.io.StdCapture.call(f)
>>> out.strip() == "hello"
True
``py.io.StdCaptureFD``
---------------------------
If you also want to capture writes to the stdout/stderr
filedescriptors you may invoke::
>>> import py, sys
>>> capture = py.io.StdCaptureFD(out=False, in_=False)
>>> sys.stderr.write("world")
>>> out,err = capture.reset()
>>> err
'world'
py.io object reference
============================
.. autoclass:: py.io.StdCaptureFD
:members:
:inherited-members:
.. autoclass:: py.io.StdCapture
:members:
:inherited-members:
.. autoclass:: py.io.TerminalWriter
:members:
:inherited-members:
py-1.5.2/doc/xml.txt 0000664 0001750 0001750 00000012777 13202663617 015431 0 ustar vagrant vagrant 0000000 0000000 ====================================================
py.xml: simple pythonic xml/html file generation
====================================================
Motivation
==========
There are a plethora of frameworks and libraries to generate
xml and html trees. However, many of them are large, have a
steep learning curve and are often hard to debug. Not to
speak of the fact that they are frameworks to begin with.
.. _xist: http://www.livinglogic.de/Python/xist/index.html
a pythonic object model , please
================================
The py lib offers a pythonic way to generate xml/html, based on
ideas from xist_ which `uses python class objects`_ to build
xml trees. However, xist_'s implementation is somewhat heavy
because it has additional goals like transformations and
supporting many namespaces. But its basic idea is very easy.
.. _`uses python class objects`: http://www.livinglogic.de/Python/xist/Howto.html
generating arbitrary xml structures
-----------------------------------
With ``py.xml.Namespace`` you have the basis
to generate custom xml-fragments on the fly::
class ns(py.xml.Namespace):
"my custom xml namespace"
doc = ns.books(
ns.book(
ns.author("May Day"),
ns.title("python for java programmers"),),
ns.book(
ns.author("why"),
ns.title("Java for Python programmers"),),
publisher="N.N",
)
print doc.unicode(indent=2).encode('utf8')
will give you this representation::
May Daypython for java programmerswhyJava for Python programmers
In a sentence: positional arguments are child-tags and
keyword-arguments are attributes.
On a side note, you'll see that the unicode-serializer
supports a nice indentation style which keeps your generated
html readable, basically through emulating python's white
space significance by putting closing-tags rightmost and
almost invisible at first glance :-)
basic example for generating html
---------------------------------
Consider this example::
from py.xml import html # html namespace
paras = "First Para", "Second para"
doc = html.html(
html.head(
html.meta(name="Content-Type", value="text/html; charset=latin1")),
html.body(
[html.p(p) for p in paras]))
print unicode(doc).encode('latin1')
Again, tags are objects which contain tags and have attributes.
More exactly, Tags inherit from the list type and thus can be
manipulated as list objects. They additionally support a default
way to represent themselves as a serialized unicode object.
If you happen to look at the py.xml implementation you'll
note that the tag/namespace implementation consumes some 50 lines
with another 50 lines for the unicode serialization code.
CSS-styling your html Tags
--------------------------
One aspect where many of the huge python xml/html generation
frameworks utterly fail is a clean and convenient integration
of CSS styling. Often, developers are left alone with keeping
CSS style definitions in sync with some style files
represented as strings (often in a separate .css file). Not
only is this hard to debug but the missing abstractions make
it hard to modify the styling of your tags or to choose custom
style representations (inline, html.head or external). Add the
Browers usual tolerance of messyness and errors in Style
references and welcome to hell, known as the domain of
developing web applications :-)
By contrast, consider this CSS styling example::
class my(html):
"my initial custom style"
class body(html.body):
style = html.Style(font_size = "120%")
class h2(html.h2):
style = html.Style(background = "grey")
class p(html.p):
style = html.Style(font_weight="bold")
doc = my.html(
my.head(),
my.body(
my.h2("hello world"),
my.p("bold as bold can")
)
)
print doc.unicode(indent=2)
This will give you a small'n mean self contained
represenation by default::
hello world
bold as bold can
Most importantly, note that the inline-styling is just an
implementation detail of the unicode serialization code.
You can easily modify the serialization to put your styling into the
``html.head`` or in a separate file and autogenerate CSS-class
names or ids.
Hey, you could even write tests that you are using correct
styles suitable for specific browser requirements. Did i mention
that the ability to easily write tests for your generated
html and its serialization could help to develop _stable_ user
interfaces?
More to come ...
----------------
For now, i don't think we should strive to offer much more
than the above. However, it is probably not hard to offer
*partial serialization* to allow generating maybe hundreds of
complex html documents per second. Basically we would allow
putting callables both as Tag content and as values of
attributes. A slightly more advanced Serialization would then
produce a list of unicode objects intermingled with callables.
At HTTP-Request time the callables would get called to
complete the probably request-specific serialization of
your Tags. Hum, it's probably harder to explain this than to
actually code it :-)
.. _`py.test`: test/index.html
py-1.5.2/doc/conf.py 0000664 0001750 0001750 00000020442 13202663617 015353 0 ustar vagrant vagrant 0000000 0000000 # -*- coding: utf-8 -*-
#
# py documentation build configuration file, created by
# sphinx-quickstart on Thu Oct 21 08:30:10 2010.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.txt'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'py'
copyright = u'2010, holger krekel et. al.'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
# The full version, including alpha/beta/rc tags.
import py
release = py.__version__
version = ".".join(release.split(".")[:2])
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'py'
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'py.tex', u'py Documentation',
u'holger krekel et. al.', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'py', u'py Documentation',
[u'holger krekel et. al.'], 1)
]
autodoc_member_order = "bysource"
autodoc_default_flags = "inherited-members"
# -- Options for Epub output ---------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = u'py'
epub_author = u'holger krekel et. al.'
epub_publisher = u'holger krekel et. al.'
epub_copyright = u'2010, holger krekel et. al.'
# The language of the text. It defaults to the language option
# or en if the language is not set.
#epub_language = ''
# The scheme of the identifier. Typical schemes are ISBN or URL.
#epub_scheme = ''
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#epub_identifier = ''
# A unique identification for the text.
#epub_uid = ''
# HTML files that should be inserted before the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_pre_files = []
# HTML files shat should be inserted after the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_post_files = []
# A list of files that should not be packed into the epub file.
#epub_exclude_files = []
# The depth of the table of contents in toc.ncx.
#epub_tocdepth = 3
# Allow duplicate toc entries.
#epub_tocdup = True
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'http://docs.python.org/': None}
py-1.5.2/doc/code.txt 0000664 0001750 0001750 00000011072 13202663617 015526 0 ustar vagrant vagrant 0000000 0000000 ================================================================================
py.code: higher level python code and introspection objects
================================================================================
``py.code`` provides higher level APIs and objects for Code, Frame, Traceback,
ExceptionInfo and source code construction. The ``py.code`` library
tries to simplify accessing the code objects as well as creating them.
There is a small set of interfaces a user needs to deal with, all nicely
bundled together, and with a rich set of 'Pythonic' functionality.
Contents of the library
=======================
Every object in the ``py.code`` library wraps a code Python object related
to code objects, source code, frames and tracebacks: the ``py.code.Code``
class wraps code objects, ``py.code.Source`` source snippets,
``py.code.Traceback` exception tracebacks, ``py.code.Frame`` frame
objects (as found in e.g. tracebacks) and ``py.code.ExceptionInfo`` the
tuple provided by sys.exc_info() (containing exception and traceback
information when an exception occurs). Also in the library is a helper function
``py.code.compile()`` that provides the same functionality as Python's
built-in 'compile()' function, but returns a wrapped code object.
The wrappers
============
``py.code.Code``
-------------------
Code objects are instantiated with a code object or a callable as argument,
and provide functionality to compare themselves with other Code objects, get to
the source file or its contents, create new Code objects from scratch, etc.
A quick example::
>>> import py
>>> c = py.code.Code(py.path.local.read)
>>> c.path.basename
'common.py'
>>> isinstance(c.source(), py.code.Source)
True
>>> str(c.source()).split('\n')[0]
"def read(self, mode='r'):"
.. autoclass:: py.code.Code
:members:
:inherited-members:
``py.code.Source``
---------------------
Source objects wrap snippets of Python source code, providing a simple yet
powerful interface to read, deindent, slice, compare, compile and manipulate
them, things that are not so easy in core Python.
Example::
>>> s = py.code.Source("""\
... def foo():
... print "foo"
... """)
>>> str(s).startswith('def') # automatic de-indentation!
True
>>> s.isparseable()
True
>>> sub = s.getstatement(1) # get the statement starting at line 1
>>> str(sub).strip() # XXX why is the strip() required?!?
'print "foo"'
.. autoclass:: py.code.Source
:members:
``py.code.Traceback``
------------------------
Tracebacks are usually not very easy to examine, you need to access certain
somewhat hidden attributes of the traceback's items (resulting in expressions
such as 'fname = tb.tb_next.tb_frame.f_code.co_filename'). The Traceback
interface (and its TracebackItem children) tries to improve this.
Example::
>>> import sys
>>> try:
... py.path.local(100) # illegal argument
... except:
... exc, e, tb = sys.exc_info()
>>> t = py.code.Traceback(tb)
>>> first = t[1] # get the second entry (first is in this doc)
>>> first.path.basename # second is in py/path/local.py
'local.py'
>>> isinstance(first.statement, py.code.Source)
True
>>> str(first.statement).strip().startswith('raise ValueError')
True
.. autoclass:: py.code.Traceback
:members:
``py.code.Frame``
--------------------
Frame wrappers are used in ``py.code.Traceback`` items, and will usually not
directly be instantiated. They provide some nice methods to evaluate code
'inside' the frame (using the frame's local variables), get to the underlying
code (frames have a code attribute that points to a ``py.code.Code`` object)
and examine the arguments.
Example (using the 'first' TracebackItem instance created above)::
>>> frame = first.frame
>>> isinstance(frame.code, py.code.Code)
True
>>> isinstance(frame.eval('self'), py.path.local)
True
>>> [namevalue[0] for namevalue in frame.getargs()]
['cls', 'path']
.. autoclass:: py.code.Frame
:members:
``py.code.ExceptionInfo``
----------------------------
A wrapper around the tuple returned by sys.exc_info() (will call sys.exc_info()
itself if the tuple is not provided as an argument), provides some handy
attributes to easily access the traceback and exception string.
Example::
>>> import sys
>>> try:
... foobar()
... except:
... excinfo = py.code.ExceptionInfo()
>>> excinfo.typename
'NameError'
>>> isinstance(excinfo.traceback, py.code.Traceback)
True
>>> excinfo.exconly()
"NameError: name 'foobar' is not defined"
.. autoclass:: py.code.ExceptionInfo
:members:
.. autoclass:: py.code.Traceback
:members:
py-1.5.2/doc/example/ 0000775 0001750 0001750 00000000000 13203150701 015467 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/doc/example/genhtml.py 0000664 0001750 0001750 00000000403 13202663617 017512 0 ustar vagrant vagrant 0000000 0000000 from py.xml import html
paras = "First Para", "Second para"
doc = html.html(
html.head(
html.meta(name="Content-Type", value="text/html; charset=latin1")),
html.body(
[html.p(p) for p in paras]))
print unicode(doc).encode('latin1')
py-1.5.2/doc/example/genhtmlcss.py 0000664 0001750 0001750 00000000652 13202663617 020231 0 ustar vagrant vagrant 0000000 0000000 import py
html = py.xml.html
class my(html):
"a custom style"
class body(html.body):
style = html.Style(font_size = "120%")
class h2(html.h2):
style = html.Style(background = "grey")
class p(html.p):
style = html.Style(font_weight="bold")
doc = my.html(
my.head(),
my.body(
my.h2("hello world"),
my.p("bold as bold can")
)
)
print doc.unicode(indent=2)
py-1.5.2/doc/example/genxml.py 0000664 0001750 0001750 00000000530 13202663617 017347 0 ustar vagrant vagrant 0000000 0000000
import py
class ns(py.xml.Namespace):
pass
doc = ns.books(
ns.book(
ns.author("May Day"),
ns.title("python for java programmers"),),
ns.book(
ns.author("why", class_="somecssclass"),
ns.title("Java for Python programmers"),),
publisher="N.N",
)
print doc.unicode(indent=2).encode('utf8')
py-1.5.2/doc/announce/ 0000775 0001750 0001750 00000000000 13203150701 015642 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/doc/announce/release-1.0.0.txt 0000664 0001750 0001750 00000005443 13202663617 020501 0 ustar vagrant vagrant 0000000 0000000
pylib 1.0.0 released: testing-with-python innovations continue
--------------------------------------------------------------------
Took a few betas but finally i uploaded a `1.0.0 py lib release`_,
featuring the mature and powerful py.test tool and "execnet-style"
*elastic* distributed programming. With the new release, there are
many new advanced automated testing features - here is a quick summary:
* funcargs_ - pythonic zero-boilerplate fixtures for Python test functions :
- totally separates test code, test configuration and test setup
- ideal for integration and functional tests
- allows for flexible and natural test parametrization schemes
* new `plugin architecture`_, allowing easy-to-write project-specific and cross-project single-file plugins. The most notable new external plugin is `oejskit`_ which naturally enables **running and reporting of javascript-unittests in real-life browsers**.
* many new features done in easy-to-improve `default plugins`_, highlights:
* xfail: mark tests as "expected to fail" and report separately.
* pastebin: automatically send tracebacks to pocoo paste service
* capture: flexibly capture stdout/stderr of subprocesses, per-test ...
* monkeypatch: safely monkeypatch modules/classes from within tests
* unittest: run and integrate traditional unittest.py tests
* figleaf: generate html coverage reports with the figleaf module
* resultlog: generate buildbot-friendly reporting output
* ...
* `distributed testing`_ and `elastic distributed execution`_:
- new unified "TX" URL scheme for specifying remote processes
- new distribution modes "--dist=each" and "--dist=load"
- new sync/async ways to handle 1:N communication
- improved documentation
The py lib continues to offer most of the functionality used by
the testing tool in `independent namespaces`_.
Some non-test related code, notably greenlets/co-routines and
api-generation now live as their own projects which simplifies the
installation procedure because no C-Extensions are required anymore.
The whole package should work well with Linux, Win32 and OSX, on Python
2.3, 2.4, 2.5 and 2.6. (Expect Python3 compatibility soon!)
For more info, see the py.test and py lib documentation:
http://pytest.org
http://pylib.org
have fun,
holger
.. _`independent namespaces`: http://pylib.org
.. _`funcargs`: http://codespeak.net/py/dist/test/funcargs.html
.. _`plugin architecture`: http://codespeak.net/py/dist/test/extend.html
.. _`default plugins`: http://codespeak.net/py/dist/test/plugin/index.html
.. _`distributed testing`: http://codespeak.net/py/dist/test/dist.html
.. _`elastic distributed execution`: http://codespeak.net/py/dist/execnet.html
.. _`1.0.0 py lib release`: http://pypi.python.org/pypi/py
.. _`oejskit`: http://codespeak.net/py/dist/test/plugin/oejskit.html
py-1.5.2/doc/announce/release-0.9.0.txt 0000664 0001750 0001750 00000000335 13202663617 020504 0 ustar vagrant vagrant 0000000 0000000 py lib 1.0.0: XXX
======================================================================
Welcome to the 1.0.0 py lib release - a library aiming to
support agile and test-driven python development on various levels.
XXX
py-1.5.2/doc/announce/release-1.2.1.txt 0000664 0001750 0001750 00000005610 13202663617 020500 0 ustar vagrant vagrant 0000000 0000000 py.test/pylib 1.2.1: little fixes and improvements
--------------------------------------------------------------------------------
py.test is an advanced automated testing tool working with
Python2, Python3 and Jython versions on all major operating
systems. It has a simple plugin architecture and can run many
existing common Python test suites without modification. It offers
some unique features not found in other testing tools.
See http://pytest.org for more info.
py.test 1.2.1 brings bug fixes and some new options and abilities triggered
by user feedback:
* --funcargs [testpath] will show available builtin- and project funcargs.
* display a short and concise traceback if funcarg lookup fails.
* early-load "conftest.py" files in non-dot first-level sub directories.
* --tb=line will print a single line for each failing test (issue67)
* py.cleanup has a number of new options, cleanups up setup.py related files
* fix issue78: always call python-level teardown functions even if the
according setup failed.
For more detailed information see the changelog below.
cheers and have fun,
holger
Changes between 1.2.1 and 1.2.0
=====================================
- refined usage and options for "py.cleanup"::
py.cleanup # remove "*.pyc" and "*$py.class" (jython) files
py.cleanup -e .swp -e .cache # also remove files with these extensions
py.cleanup -s # remove "build" and "dist" directory next to setup.py files
py.cleanup -d # also remove empty directories
py.cleanup -a # synonym for "-s -d -e 'pip-log.txt'"
py.cleanup -n # dry run, only show what would be removed
- add a new option "py.test --funcargs" which shows available funcargs
and their help strings (docstrings on their respective factory function)
for a given test path
- display a short and concise traceback if a funcarg lookup fails
- early-load "conftest.py" files in non-dot first-level sub directories.
allows to conveniently keep and access test-related options in a ``test``
subdir and still add command line options.
- fix issue67: new super-short traceback-printing option: "--tb=line" will print a single line for each failing (python) test indicating its filename, lineno and the failure value
- fix issue78: always call python-level teardown functions even if the
according setup failed. This includes refinements for calling setup_module/class functions
which will now only be called once instead of the previous behaviour where they'd be called
multiple times if they raise an exception (including a Skipped exception). Any exception
will be re-corded and associated with all tests in the according module/class scope.
- fix issue63: assume <40 columns to be a bogus terminal width, default to 80
- fix pdb debugging to be in the correct frame on raises-related errors
- update apipkg.py to fix an issue where recursive imports might
unnecessarily break importing
- fix plugin links
py-1.5.2/doc/announce/release-1.1.0.txt 0000664 0001750 0001750 00000007727 13202663617 020511 0 ustar vagrant vagrant 0000000 0000000 py.test/pylib 1.1.0: Python3, Jython, advanced skipping, cleanups ...
--------------------------------------------------------------------------------
Features:
* compatible to Python3 (single py2/py3 source), `easy to install`_
* conditional skipping_: skip/xfail based on platform/dependencies
* generalized marking_: mark tests one a whole-class or whole-module basis
Fixes:
* code reduction and "de-magification" (e.g. 23 KLoc -> 11 KLOC)
* distribute testing requires the now separately released execnet_ package
* funcarg-setup/caching, "same-name" test modules now cause an exlicit error
* de-cluttered reporting options, --report for skipped/xfail details
Compatibilities
1.1.0 should allow running test code that already worked well with 1.0.2
plus some more due to improved unittest/nose compatibility.
More information: http://pytest.org
thanks and have fun,
holger (http://twitter.com/hpk42)
.. _execnet: http://codespeak.net/execnet
.. _`easy to install`: ../install.html
.. _marking: ../test/plugin/mark.html
.. _skipping: ../test/plugin/skipping.html
Changelog 1.0.2 -> 1.1.0
-----------------------------------------------------------------------
* remove py.rest tool and internal namespace - it was
never really advertised and can still be used with
the old release if needed. If there is interest
it could be revived into its own tool i guess.
* fix issue48 and issue59: raise an Error if the module
from an imported test file does not seem to come from
the filepath - avoids "same-name" confusion that has
been reported repeatedly
* merged Ronny's nose-compatibility hacks: now
nose-style setup_module() and setup() functions are
supported
* introduce generalized py.test.mark function marking
* reshuffle / refine command line grouping
* deprecate parser.addgroup in favour of getgroup which creates option group
* add --report command line option that allows to control showing of skipped/xfailed sections
* generalized skipping: a new way to mark python functions with skipif or xfail
at function, class and modules level based on platform or sys-module attributes.
* extend py.test.mark decorator to allow for positional args
* introduce and test "py.cleanup -d" to remove empty directories
* fix issue #59 - robustify unittest test collection
* make bpython/help interaction work by adding an __all__ attribute
to ApiModule, cleanup initpkg
* use MIT license for pylib, add some contributors
* remove py.execnet code and substitute all usages with 'execnet' proper
* fix issue50 - cached_setup now caches more to expectations
for test functions with multiple arguments.
* merge Jarko's fixes, issue #45 and #46
* add the ability to specify a path for py.lookup to search in
* fix a funcarg cached_setup bug probably only occuring
in distributed testing and "module" scope with teardown.
* many fixes and changes for making the code base python3 compatible,
many thanks to Benjamin Peterson for helping with this.
* consolidate builtins implementation to be compatible with >=2.3,
add helpers to ease keeping 2 and 3k compatible code
* deprecate py.compat.doctest|subprocess|textwrap|optparse
* deprecate py.magic.autopath, remove py/magic directory
* move pytest assertion handling to py/code and a pytest_assertion
plugin, add "--no-assert" option, deprecate py.magic namespaces
in favour of (less) py.code ones.
* consolidate and cleanup py/code classes and files
* cleanup py/misc, move tests to bin-for-dist
* introduce delattr/delitem/delenv methods to py.test's monkeypatch funcarg
* consolidate py.log implementation, remove old approach.
* introduce py.io.TextIO and py.io.BytesIO for distinguishing between
text/unicode and byte-streams (uses underlying standard lib io.*
if available)
* make py.unittest_convert helper script available which converts "unittest.py"
style files into the simpler assert/direct-test-classes py.test/nosetests
style. The script was written by Laura Creighton.
* simplified internal localpath implementation
py-1.5.2/doc/announce/release-1.0.2.txt 0000664 0001750 0001750 00000000237 13202663617 020477 0 ustar vagrant vagrant 0000000 0000000 1.0.2: packaging fixes
-----------------------------------------------------------------------
this release is purely a release for fixing packaging issues.
py-1.5.2/doc/announce/release-1.0.1.txt 0000664 0001750 0001750 00000003340 13202663617 020474 0 ustar vagrant vagrant 0000000 0000000 1.0.1: improved reporting, nose/unittest.py support, bug fixes
-----------------------------------------------------------------------
This is a bugfix release of pylib/py.test also coming with:
* improved documentation, improved navigation
* test failure reporting improvements
* support for directly running existing nose/unittest.py style tests
visit here for more info, including quickstart and tutorials:
http://pytest.org and http://pylib.org
Changelog 1.0.0 to 1.0.1
------------------------
* added a default 'pytest_nose' plugin which handles nose.SkipTest,
nose-style function/method/generator setup/teardown and
tries to report functions correctly.
* improved documentation, better navigation: see http://pytest.org
* added a "--help-config" option to show conftest.py / ENV-var names for
all longopt cmdline options, and some special conftest.py variables.
renamed 'conf_capture' conftest setting to 'option_capture' accordingly.
* unicode fixes: capturing and unicode writes to sys.stdout
(through e.g a print statement) now work within tests,
they are encoded as "utf8" by default, also terminalwriting
was adapted and somewhat unified between windows and linux
* fix issue #27: better reporting on non-collectable items given on commandline
(e.g. pyc files)
* fix issue #33: added --version flag (thanks Benjamin Peterson)
* fix issue #32: adding support for "incomplete" paths to wcpath.status()
* "Test" prefixed classes are *not* collected by default anymore if they
have an __init__ method
* monkeypatch setenv() now accepts a "prepend" parameter
* improved reporting of collection error tracebacks
* simplified multicall mechanism and plugin architecture,
renamed some internal methods and argnames
py-1.5.2/doc/announce/release-1.3.4.txt 0000664 0001750 0001750 00000001762 13202663617 020510 0 ustar vagrant vagrant 0000000 0000000 py.test/pylib 1.3.4: fixes and new native traceback option
===========================================================================
pylib/py.test 1.3.4 is a minor maintenance release mostly containing bug fixes
and a new "--tb=native" traceback option to show "normal" Python standard
tracebacks instead of the py.test enhanced tracebacks. See below for more
change info and http://pytest.org for more general information on features
and configuration of the testing tool.
Thanks to the issue reporters and generally to Ronny Pfannschmidt for help.
cheers,
holger krekel
Changes between 1.3.3 and 1.3.4
==================================================
- fix issue111: improve install documentation for windows
- fix issue119: fix custom collectability of __init__.py as a module
- fix issue116: --doctestmodules work with __init__.py files as well
- fix issue115: unify internal exception passthrough/catching/GeneratorExit
- fix issue118: new --tb=native for presenting cpython-standard exceptions
py-1.5.2/doc/announce/release-0.9.2.txt 0000664 0001750 0001750 00000001554 13202663617 020512 0 ustar vagrant vagrant 0000000 0000000 py lib 0.9.2: bugfix release
=============================
Welcome to the 0.9.2 py lib and py.test release -
mainly fixing Windows issues, providing better
packaging and integration with setuptools.
Here is a quick summary of what the py lib provides:
* 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")
* py.path: path abstractions over local and subversion files
* rich documentation of py's exported API
* tested against Linux, Win32, OSX, works on python 2.3-2.6
See here for more information:
Pypi pages: http://pypi.python.org/pypi/py/
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,
holger krekel
py-1.5.2/doc/announce/release-1.3.1.txt 0000664 0001750 0001750 00000010410 13202663617 020473 0 ustar vagrant vagrant 0000000 0000000 py.test/pylib 1.3.1: new py.test.xfail, --maxfail, better reporting
===========================================================================
The pylib/py.test 1.3.1 release brings:
- the new imperative ``py.test.xfail()`` helper in order to have a test or
setup function result in an "expected failure"
- a new option ``--maxfail=NUM`` to stop the test run after some failures
- markers/decorators are now applicable to test classes (>=Python2.6)
- improved reporting, shorter tracebacks in several cases
- some simplified internals, more compatibility with Jython and PyPy
- bug fixes and various refinements
See the below CHANGELOG entry below for more details and
http://pylib.org/install.html for installation instructions.
If you used older versions of py.test you should be able to upgrade
to 1.3.1 without changes to your test source code.
py.test is an automated testing tool working with Python2,
Python3, Jython and PyPy versions on all major operating systems. It
offers a no-boilerplate testing approach and has inspired other testing
tools and enhancements in the standard Python library for more than five
years. It has a simple and extensive plugin architecture, configurable
reporting and provides unique ways to make it fit to your testing
process and needs.
See http://pytest.org for more info.
cheers and have fun,
holger krekel
Changes between 1.3.0 and 1.3.1
==================================================
New features
++++++++++++++++++
- issue91: introduce new py.test.xfail(reason) helper
to imperatively mark a test as expected to fail. Can
be used from within setup and test functions. This is
useful especially for parametrized tests when certain
configurations are expected-to-fail. In this case the
declarative approach with the @py.test.mark.xfail cannot
be used as it would mark all configurations as xfail.
- issue102: introduce new --maxfail=NUM option to stop
test runs after NUM failures. This is a generalization
of the '-x' or '--exitfirst' option which is now equivalent
to '--maxfail=1'. Both '-x' and '--maxfail' will
now also print a line near the end indicating the Interruption.
- issue89: allow py.test.mark decorators to be used on classes
(class decorators were introduced with python2.6) and
also allow to have multiple markers applied at class/module level
by specifying a list.
- improve and refine letter reporting in the progress bar:
. pass
f failed test
s skipped tests (reminder: use for dependency/platform mismatch only)
x xfailed test (test that was expected to fail)
X xpassed test (test that was expected to fail but passed)
You can use any combination of 'fsxX' with the '-r' extended
reporting option. The xfail/xpass results will show up as
skipped tests in the junitxml output - which also fixes
issue99.
- make py.test.cmdline.main() return the exitstatus instead of raising
SystemExit and also allow it to be called multiple times. This of
course requires that your application and tests are properly teared
down and don't have global state.
Fixes / Maintenance
++++++++++++++++++++++
- improved traceback presentation:
- improved and unified reporting for "--tb=short" option
- Errors during test module imports are much shorter, (using --tb=short style)
- raises shows shorter more relevant tracebacks
- --fulltrace now more systematically makes traces longer / inhibits cutting
- improve support for raises and other dynamically compiled code by
manipulating python's linecache.cache instead of the previous
rather hacky way of creating custom code objects. This makes
it seemlessly work on Jython and PyPy where it previously didn't.
- fix issue96: make capturing more resilient against Control-C
interruptions (involved somewhat substantial refactoring
to the underlying capturing functionality to avoid race
conditions).
- fix chaining of conditional skipif/xfail decorators - so it works now
as expected to use multiple @py.test.mark.skipif(condition) decorators,
including specific reporting which of the conditions lead to skipping.
- fix issue95: late-import zlib so that it's not required
for general py.test startup.
- fix issue94: make reporting more robust against bogus source code
(and internally be more careful when presenting unexpected byte sequences)
py-1.5.2/doc/announce/releases.txt 0000664 0001750 0001750 00000000315 13202663617 020223 0 ustar vagrant vagrant 0000000 0000000 =============
Release notes
=============
Contents:
.. toctree::
:maxdepth: 2
.. include: release-1.1.0
.. include: release-1.0.2
release-1.0.1
release-1.0.0
release-0.9.2
release-0.9.0
py-1.5.2/doc/announce/release-1.1.1.txt 0000664 0001750 0001750 00000003522 13202663617 020477 0 ustar vagrant vagrant 0000000 0000000 py.test/pylib 1.1.1: bugfix release, setuptools plugin registration
--------------------------------------------------------------------------------
This is a compatibility fixing release of pylib/py.test to work
better with previous 1.0.x test code bases. It also contains fixes
and changes to work with `execnet>=1.0.0`_ to provide distributed
testing and looponfailing testing modes. py-1.1.1 also introduces
a new mechanism for registering plugins via setuptools.
What is pylib/py.test?
-----------------------
py.test is an advanced automated testing tool working with
Python2, Python3 and Jython versions on all major operating
systems. It has an extensive plugin architecture and can run many
existing common Python test suites without modification. Moreover,
it offers some unique features not found in other
testing tools. See http://pytest.org for more info.
The pylib also contains a localpath and svnpath implementation
and some developer-oriented command line tools. See
http://pylib.org for more info.
thanks to all who helped and gave feedback,
have fun,
holger (http://twitter.com/hpk42)
.. _`execnet>=1.0.0`: http://codespeak.net/execnet
Changes between 1.1.1 and 1.1.0
=====================================
- introduce automatic plugin registration via 'pytest11'
entrypoints via setuptools' pkg_resources.iter_entry_points
- fix py.test dist-testing to work with execnet >= 1.0.0b4
- re-introduce py.test.cmdline.main() for better backward compatibility
- svn paths: fix a bug with path.check(versioned=True) for svn paths,
allow '%' in svn paths, make svnwc.update() default to interactive mode
like in 1.0.x and add svnwc.update(interactive=False) to inhibit interaction.
- refine distributed tarball to contain test and no pyc files
- try harder to have deprecation warnings for py.compat.* accesses
report a correct location
py-1.5.2/doc/announce/release-1.2.0.txt 0000664 0001750 0001750 00000011504 13202663617 020476 0 ustar vagrant vagrant 0000000 0000000 py.test/pylib 1.2.0: junitxml, standalone test scripts, pluginization
--------------------------------------------------------------------------------
py.test is an advanced automated testing tool working with
Python2, Python3 and Jython versions on all major operating
systems. It has a simple plugin architecture and can run many
existing common Python test suites without modification. It offers
some unique features not found in other testing tools.
See http://pytest.org for more info.
py.test 1.2.0 brings many bug fixes and interesting new abilities:
* --junitxml=path will create an XML file for use with CI processing
* --genscript=path creates a standalone py.test-equivalent test-script
* --ignore=path prevents collection of anything below that path
* --confcutdir=path only lookup conftest.py test configs below that path
* a 'pytest_report_header' hook to add info to the terminal report header
* a 'pytestconfig' function argument gives direct access to option values
* 'pytest_generate_tests' can now be put into a class as well
* on CPython py.test additionally installs as "py.test-VERSION", on
Jython as py.test-jython and on PyPy as py.test-pypy-XYZ
Apart from many bug fixes 1.2.0 also has better pluginization:
Distributed testing and looponfailing testing now live in the
separately installable 'pytest-xdist' plugin. The same is true for
'pytest-figleaf' for doing coverage reporting. Those two plugins
can serve well now as blue prints for doing your own.
thanks to all who helped and gave feedback,
have fun,
holger krekel, January 2010
Changes between 1.2.0 and 1.1.1
=====================================
- moved dist/looponfailing from py.test core into a new
separately released pytest-xdist plugin.
- new junitxml plugin: --junitxml=path will generate a junit style xml file
which is processable e.g. by the Hudson CI system.
- new option: --genscript=path will generate a standalone py.test script
which will not need any libraries installed. thanks to Ralf Schmitt.
- new option: --ignore will prevent specified path from collection.
Can be specified multiple times.
- new option: --confcutdir=dir will make py.test only consider conftest
files that are relative to the specified dir.
- new funcarg: "pytestconfig" is the pytest config object for access
to command line args and can now be easily used in a test.
- install 'py.test' and `py.which` with a ``-$VERSION`` suffix to
disambiguate between Python3, python2.X, Jython and PyPy installed versions.
- new "pytestconfig" funcarg allows access to test config object
- new "pytest_report_header" hook can return additional lines
to be displayed at the header of a test run.
- (experimental) allow "py.test path::name1::name2::..." for pointing
to a test within a test collection directly. This might eventually
evolve as a full substitute to "-k" specifications.
- streamlined plugin loading: order is now as documented in
customize.html: setuptools, ENV, commandline, conftest.
also setuptools entry point names are turned to canonical namees ("pytest_*")
- automatically skip tests that need 'capfd' but have no os.dup
- allow pytest_generate_tests to be defined in classes as well
- deprecate usage of 'disabled' attribute in favour of pytestmark
- deprecate definition of Directory, Module, Class and Function nodes
in conftest.py files. Use pytest collect hooks instead.
- collection/item node specific runtest/collect hooks are only called exactly
on matching conftest.py files, i.e. ones which are exactly below
the filesystem path of an item
- change: the first pytest_collect_directory hook to return something
will now prevent further hooks to be called.
- change: figleaf plugin now requires --figleaf to run. Also
change its long command line options to be a bit shorter (see py.test -h).
- change: pytest doctest plugin is now enabled by default and has a
new option --doctest-glob to set a pattern for file matches.
- change: remove internal py._* helper vars, only keep py._pydir
- robustify capturing to survive if custom pytest_runtest_setup
code failed and prevented the capturing setup code from running.
- make py.test.* helpers provided by default plugins visible early -
works transparently both for pydoc and for interactive sessions
which will regularly see e.g. py.test.mark and py.test.importorskip.
- simplify internal plugin manager machinery
- simplify internal collection tree by introducing a RootCollector node
- fix assert reinterpreation that sees a call containing "keyword=..."
- fix issue66: invoke pytest_sessionstart and pytest_sessionfinish
hooks on slaves during dist-testing, report module/session teardown
hooks correctly.
- fix issue65: properly handle dist-testing if no
execnet/py lib installed remotely.
- skip some install-tests if no execnet is available
- fix docs, fix internal bin/ script generation
py-1.5.2/doc/announce/release-1.3.3.txt 0000664 0001750 0001750 00000001743 13202663617 020506 0 ustar vagrant vagrant 0000000 0000000 py.test/pylib 1.3.3: windows and other fixes
===========================================================================
pylib/py.test 1.3.3 is a minor bugfix release featuring some improvements
and fixes. See changelog_ for full history.
have fun,
holger krekel
.. _changelog: ../changelog.html
Changes between 1.3.2 and 1.3.3
==================================================
- fix issue113: assertion representation problem with triple-quoted strings
(and possibly other cases)
- make conftest loading detect that a conftest file with the same
content was already loaded, avoids surprises in nested directory structures
which can be produced e.g. by Hudson. It probably removes the need to use
--confcutdir in most cases.
- fix terminal coloring for win32
(thanks Michael Foord for reporting)
- fix weirdness: make terminal width detection work on stdout instead of stdin
(thanks Armin Ronacher for reporting)
- remove trailing whitespace in all py/text distribution files
py-1.5.2/doc/announce/release-1.4.1.txt 0000664 0001750 0001750 00000003505 13202663617 020503 0 ustar vagrant vagrant 0000000 0000000
.. _`release-1.4.1`:
py-1.4.1: cross-python lib for fs path, code, io, ... manipulations
===========================================================================
This is a bug fix release of the "py" lib, see below for detailed changes.
The py lib is a small library comprising APIs for filesystem and svn path
manipulations, dynamic code construction and introspection, a Py2/Py3
compatibility namespace ("py.builtin"), IO capturing, terminal colored printing
(on windows and linux), ini-file parsing and a lazy import mechanism.
It runs unmodified on all Python interpreters compatible to Python2.4 up
until Python 3.2, PyPy and Jython. The general goal with "py" is to
provide stable APIs for some common tasks that are continously tested
against many Python interpreters and thus also to help transition. Here
are some docs:
http://pylib.org
NOTE: The prior py-1.3.X versions contained "py.test" which since py-1.4.0
comes as its own separate "pytest" distribution, see:
http://pytest.org
Also, the "py.cleanup|py.lookup|py.countloc" helpers are now part of
the pycmd distribution, see http://pypi.python.org/pypi/pycmd
Changes between 1.4.0 and 1.4.1
==================================================
- fix issue1 - py.error.* classes to be pickleable
- fix issue2 - on windows32 use PATHEXT as the list of potential
extensions to find find binaries with py.path.local.sysfind(commandname)
- fix (pytest-) issue10 and refine assertion reinterpretation
to avoid breaking if the __nonzero__ of an object fails
- fix (pytest-) issue17 where python3 does not like star-imports,
leading to misrepresentation of import-errors in test modules
- fix ``py.error.*`` attribute pypy access
- allow path.samefile(arg) to succeed when arg is a relative filename
- fix (pytest-) issue20 path.samefile(relpath) works as expected now
py-1.5.2/doc/announce/release-1.3.0.txt 0000664 0001750 0001750 00000053176 13202663617 020512 0 ustar vagrant vagrant 0000000 0000000 py.test/pylib 1.3.0: new options, per-plugin hooks, fixes ...
===========================================================================
The 1.3.0 release introduces new options, bug fixes and improved compatibility
with Python3 and Jython-2.5.1 on Windows. If you already use py-1.2 chances
are you can use py-1.3.0. See the below CHANGELOG for more details and
http://pylib.org/install.html for installation instructions.
py.test is an advanced automated testing tool working with Python2,
Python3, Jython and PyPy versions on all major operating systems. It
offers a no-boilerplate testing approach and has inspired other testing
tools and enhancements in the standard Python library for more than five
years. It has a simple and extensive plugin architecture, configurable
reporting and provides unique ways to make it fit to your testing
process and needs.
See http://pytest.org for more info.
cheers and have fun,
holger krekel
Changes between 1.2.1 and 1.3.0
==================================================
- deprecate --report option in favour of a new shorter and easier to
remember -r option: it takes a string argument consisting of any
combination of 'xfsX' characters. They relate to the single chars
you see during the dotted progress printing and will print an extra line
per test at the end of the test run. This extra line indicates the exact
position or test ID that you directly paste to the py.test cmdline in order
to re-run a particular test.
- allow external plugins to register new hooks via the new
pytest_addhooks(pluginmanager) hook. The new release of
the pytest-xdist plugin for distributed and looponfailing
testing requires this feature.
- add a new pytest_ignore_collect(path, config) hook to allow projects and
plugins to define exclusion behaviour for their directory structure -
for example you may define in a conftest.py this method::
def pytest_ignore_collect(path):
return path.check(link=1)
to prevent even collection of any tests in symlinked dirs.
- new pytest_pycollect_makemodule(path, parent) hook for
allowing customization of the Module collection object for a
matching test module.
- extend and refine xfail mechanism::
@py.test.mark.xfail(run=False) do not run the decorated test
@py.test.mark.xfail(reason="...") prints the reason string in xfail summaries
specifiying ``--runxfail`` on command line ignores xfail markers to show
you the underlying traceback.
- expose (previously internal) commonly useful methods:
py.io.get_terminal_with() -> return terminal width
py.io.ansi_print(...) -> print colored/bold text on linux/win32
py.io.saferepr(obj) -> return limited representation string
- expose test outcome related exceptions as py.test.skip.Exception,
py.test.raises.Exception etc., useful mostly for plugins
doing special outcome interpretation/tweaking
- (issue85) fix junitxml plugin to handle tests with non-ascii output
- fix/refine python3 compatibility (thanks Benjamin Peterson)
- fixes for making the jython/win32 combination work, note however:
jython2.5.1/win32 does not provide a command line launcher, see
http://bugs.jython.org/issue1491 . See pylib install documentation
for how to work around.
- fixes for handling of unicode exception values and unprintable objects
- (issue87) fix unboundlocal error in assertionold code
- (issue86) improve documentation for looponfailing
- refine IO capturing: stdin-redirect pseudo-file now has a NOP close() method
- ship distribute_setup.py version 0.6.10
- added links to the new capturelog and coverage plugins
Changes between 1.2.1 and 1.2.0
=====================================
- refined usage and options for "py.cleanup"::
py.cleanup # remove "*.pyc" and "*$py.class" (jython) files
py.cleanup -e .swp -e .cache # also remove files with these extensions
py.cleanup -s # remove "build" and "dist" directory next to setup.py files
py.cleanup -d # also remove empty directories
py.cleanup -a # synonym for "-s -d -e 'pip-log.txt'"
py.cleanup -n # dry run, only show what would be removed
- add a new option "py.test --funcargs" which shows available funcargs
and their help strings (docstrings on their respective factory function)
for a given test path
- display a short and concise traceback if a funcarg lookup fails
- early-load "conftest.py" files in non-dot first-level sub directories.
allows to conveniently keep and access test-related options in a ``test``
subdir and still add command line options.
- fix issue67: new super-short traceback-printing option: "--tb=line" will print a single line for each failing (python) test indicating its filename, lineno and the failure value
- fix issue78: always call python-level teardown functions even if the
according setup failed. This includes refinements for calling setup_module/class functions
which will now only be called once instead of the previous behaviour where they'd be called
multiple times if they raise an exception (including a Skipped exception). Any exception
will be re-corded and associated with all tests in the according module/class scope.
- fix issue63: assume <40 columns to be a bogus terminal width, default to 80
- fix pdb debugging to be in the correct frame on raises-related errors
- update apipkg.py to fix an issue where recursive imports might
unnecessarily break importing
- fix plugin links
Changes between 1.2 and 1.1.1
=====================================
- moved dist/looponfailing from py.test core into a new
separately released pytest-xdist plugin.
- new junitxml plugin: --junitxml=path will generate a junit style xml file
which is processable e.g. by the Hudson CI system.
- new option: --genscript=path will generate a standalone py.test script
which will not need any libraries installed. thanks to Ralf Schmitt.
- new option: --ignore will prevent specified path from collection.
Can be specified multiple times.
- new option: --confcutdir=dir will make py.test only consider conftest
files that are relative to the specified dir.
- new funcarg: "pytestconfig" is the pytest config object for access
to command line args and can now be easily used in a test.
- install 'py.test' and `py.which` with a ``-$VERSION`` suffix to
disambiguate between Python3, python2.X, Jython and PyPy installed versions.
- new "pytestconfig" funcarg allows access to test config object
- new "pytest_report_header" hook can return additional lines
to be displayed at the header of a test run.
- (experimental) allow "py.test path::name1::name2::..." for pointing
to a test within a test collection directly. This might eventually
evolve as a full substitute to "-k" specifications.
- streamlined plugin loading: order is now as documented in
customize.html: setuptools, ENV, commandline, conftest.
also setuptools entry point names are turned to canonical namees ("pytest_*")
- automatically skip tests that need 'capfd' but have no os.dup
- allow pytest_generate_tests to be defined in classes as well
- deprecate usage of 'disabled' attribute in favour of pytestmark
- deprecate definition of Directory, Module, Class and Function nodes
in conftest.py files. Use pytest collect hooks instead.
- collection/item node specific runtest/collect hooks are only called exactly
on matching conftest.py files, i.e. ones which are exactly below
the filesystem path of an item
- change: the first pytest_collect_directory hook to return something
will now prevent further hooks to be called.
- change: figleaf plugin now requires --figleaf to run. Also
change its long command line options to be a bit shorter (see py.test -h).
- change: pytest doctest plugin is now enabled by default and has a
new option --doctest-glob to set a pattern for file matches.
- change: remove internal py._* helper vars, only keep py._pydir
- robustify capturing to survive if custom pytest_runtest_setup
code failed and prevented the capturing setup code from running.
- make py.test.* helpers provided by default plugins visible early -
works transparently both for pydoc and for interactive sessions
which will regularly see e.g. py.test.mark and py.test.importorskip.
- simplify internal plugin manager machinery
- simplify internal collection tree by introducing a RootCollector node
- fix assert reinterpreation that sees a call containing "keyword=..."
- fix issue66: invoke pytest_sessionstart and pytest_sessionfinish
hooks on slaves during dist-testing, report module/session teardown
hooks correctly.
- fix issue65: properly handle dist-testing if no
execnet/py lib installed remotely.
- skip some install-tests if no execnet is available
- fix docs, fix internal bin/ script generation
Changes between 1.1.1 and 1.1.0
=====================================
- introduce automatic plugin registration via 'pytest11'
entrypoints via setuptools' pkg_resources.iter_entry_points
- fix py.test dist-testing to work with execnet >= 1.0.0b4
- re-introduce py.test.cmdline.main() for better backward compatibility
- svn paths: fix a bug with path.check(versioned=True) for svn paths,
allow '%' in svn paths, make svnwc.update() default to interactive mode
like in 1.0.x and add svnwc.update(interactive=False) to inhibit interaction.
- refine distributed tarball to contain test and no pyc files
- try harder to have deprecation warnings for py.compat.* accesses
report a correct location
Changes between 1.1.0 and 1.0.2
=====================================
* adjust and improve docs
* remove py.rest tool and internal namespace - it was
never really advertised and can still be used with
the old release if needed. If there is interest
it could be revived into its own tool i guess.
* fix issue48 and issue59: raise an Error if the module
from an imported test file does not seem to come from
the filepath - avoids "same-name" confusion that has
been reported repeatedly
* merged Ronny's nose-compatibility hacks: now
nose-style setup_module() and setup() functions are
supported
* introduce generalized py.test.mark function marking
* reshuffle / refine command line grouping
* deprecate parser.addgroup in favour of getgroup which creates option group
* add --report command line option that allows to control showing of skipped/xfailed sections
* generalized skipping: a new way to mark python functions with skipif or xfail
at function, class and modules level based on platform or sys-module attributes.
* extend py.test.mark decorator to allow for positional args
* introduce and test "py.cleanup -d" to remove empty directories
* fix issue #59 - robustify unittest test collection
* make bpython/help interaction work by adding an __all__ attribute
to ApiModule, cleanup initpkg
* use MIT license for pylib, add some contributors
* remove py.execnet code and substitute all usages with 'execnet' proper
* fix issue50 - cached_setup now caches more to expectations
for test functions with multiple arguments.
* merge Jarko's fixes, issue #45 and #46
* add the ability to specify a path for py.lookup to search in
* fix a funcarg cached_setup bug probably only occuring
in distributed testing and "module" scope with teardown.
* many fixes and changes for making the code base python3 compatible,
many thanks to Benjamin Peterson for helping with this.
* consolidate builtins implementation to be compatible with >=2.3,
add helpers to ease keeping 2 and 3k compatible code
* deprecate py.compat.doctest|subprocess|textwrap|optparse
* deprecate py.magic.autopath, remove py/magic directory
* move pytest assertion handling to py/code and a pytest_assertion
plugin, add "--no-assert" option, deprecate py.magic namespaces
in favour of (less) py.code ones.
* consolidate and cleanup py/code classes and files
* cleanup py/misc, move tests to bin-for-dist
* introduce delattr/delitem/delenv methods to py.test's monkeypatch funcarg
* consolidate py.log implementation, remove old approach.
* introduce py.io.TextIO and py.io.BytesIO for distinguishing between
text/unicode and byte-streams (uses underlying standard lib io.*
if available)
* make py.unittest_convert helper script available which converts "unittest.py"
style files into the simpler assert/direct-test-classes py.test/nosetests
style. The script was written by Laura Creighton.
* simplified internal localpath implementation
Changes between 1.0.1 and 1.0.2
=====================================
* fixing packaging issues, triggered by fedora redhat packaging,
also added doc, examples and contrib dirs to the tarball.
* added a documentation link to the new django plugin.
Changes between 1.0.0 and 1.0.1
=====================================
* added a 'pytest_nose' plugin which handles nose.SkipTest,
nose-style function/method/generator setup/teardown and
tries to report functions correctly.
* capturing of unicode writes or encoded strings to sys.stdout/err
work better, also terminalwriting was adapted and somewhat
unified between windows and linux.
* improved documentation layout and content a lot
* added a "--help-config" option to show conftest.py / ENV-var names for
all longopt cmdline options, and some special conftest.py variables.
renamed 'conf_capture' conftest setting to 'option_capture' accordingly.
* fix issue #27: better reporting on non-collectable items given on commandline
(e.g. pyc files)
* fix issue #33: added --version flag (thanks Benjamin Peterson)
* fix issue #32: adding support for "incomplete" paths to wcpath.status()
* "Test" prefixed classes are *not* collected by default anymore if they
have an __init__ method
* monkeypatch setenv() now accepts a "prepend" parameter
* improved reporting of collection error tracebacks
* simplified multicall mechanism and plugin architecture,
renamed some internal methods and argnames
Changes between 1.0.0b9 and 1.0.0
=====================================
* more terse reporting try to show filesystem path relatively to current dir
* improve xfail output a bit
Changes between 1.0.0b8 and 1.0.0b9
=====================================
* cleanly handle and report final teardown of test setup
* fix svn-1.6 compat issue with py.path.svnwc().versioned()
(thanks Wouter Vanden Hove)
* setup/teardown or collection problems now show as ERRORs
or with big "E"'s in the progress lines. they are reported
and counted separately.
* dist-testing: properly handle test items that get locally
collected but cannot be collected on the remote side - often
due to platform/dependency reasons
* simplified py.test.mark API - see keyword plugin documentation
* integrate better with logging: capturing now by default captures
test functions and their immediate setup/teardown in a single stream
* capsys and capfd funcargs now have a readouterr() and a close() method
(underlyingly py.io.StdCapture/FD objects are used which grew a
readouterr() method as well to return snapshots of captured out/err)
* make assert-reinterpretation work better with comparisons not
returning bools (reported with numpy from thanks maciej fijalkowski)
* reworked per-test output capturing into the pytest_iocapture.py plugin
and thus removed capturing code from config object
* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr)
Changes between 1.0.0b7 and 1.0.0b8
=====================================
* pytest_unittest-plugin is now enabled by default
* introduced pytest_keyboardinterrupt hook and
refined pytest_sessionfinish hooked, added tests.
* workaround a buggy logging module interaction ("closing already closed
files"). Thanks to Sridhar Ratnakumar for triggering.
* if plugins use "py.test.importorskip" for importing
a dependency only a warning will be issued instead
of exiting the testing process.
* many improvements to docs:
- refined funcargs doc , use the term "factory" instead of "provider"
- added a new talk/tutorial doc page
- better download page
- better plugin docstrings
- added new plugins page and automatic doc generation script
* fixed teardown problem related to partially failing funcarg setups
(thanks MrTopf for reporting), "pytest_runtest_teardown" is now
always invoked even if the "pytest_runtest_setup" failed.
* tweaked doctest output for docstrings in py modules,
thanks Radomir.
Changes between 1.0.0b3 and 1.0.0b7
=============================================
* renamed py.test.xfail back to py.test.mark.xfail to avoid
two ways to decorate for xfail
* re-added py.test.mark decorator for setting keywords on functions
(it was actually documented so removing it was not nice)
* remove scope-argument from request.addfinalizer() because
request.cached_setup has the scope arg. TOOWTDI.
* perform setup finalization before reporting failures
* apply modified patches from Andreas Kloeckner to allow
test functions to have no func_code (#22) and to make
"-k" and function keywords work (#20)
* apply patch from Daniel Peolzleithner (issue #23)
* resolve issue #18, multiprocessing.Manager() and
redirection clash
* make __name__ == "__channelexec__" for remote_exec code
Changes between 1.0.0b1 and 1.0.0b3
=============================================
* plugin classes are removed: one now defines
hooks directly in conftest.py or global pytest_*.py
files.
* added new pytest_namespace(config) hook that allows
to inject helpers directly to the py.test.* namespace.
* documented and refined many hooks
* added new style of generative tests via
pytest_generate_tests hook that integrates
well with function arguments.
Changes between 0.9.2 and 1.0.0b1
=============================================
* introduced new "funcarg" setup method,
see doc/test/funcarg.txt
* introduced plugin architecuture and many
new py.test plugins, see
doc/test/plugins.txt
* teardown_method is now guaranteed to get
called after a test method has run.
* new method: py.test.importorskip(mod,minversion)
will either import or call py.test.skip()
* completely revised internal py.test architecture
* new py.process.ForkedFunc object allowing to
fork execution of a function to a sub process
and getting a result back.
XXX lots of things missing here XXX
Changes between 0.9.1 and 0.9.2
===============================
* refined installation and metadata, created new setup.py,
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.
* 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
* removed previously accidentally added
py.test.broken and py.test.notimplemented helpers.
* there now is a py.__version__ attribute
Changes between 0.9.0 and 0.9.1
===============================
This is a fairly complete list of changes between 0.9 and 0.9.1, which can
serve as a reference for developers.
* allowing + signs in py.path.svn urls [39106]
* fixed support for Failed exceptions without excinfo in py.test [39340]
* added support for killing processes for Windows (as well as platforms that
support os.kill) in py.misc.killproc [39655]
* added setup/teardown for generative tests to py.test [40702]
* added detection of FAILED TO LOAD MODULE to py.test [40703, 40738, 40739]
* fixed problem with calling .remove() on wcpaths of non-versioned files in
py.path [44248]
* fixed some import and inheritance issues in py.test [41480, 44648, 44655]
* fail to run greenlet tests when pypy is available, but without stackless
[45294]
* small fixes in rsession tests [45295]
* fixed issue with 2.5 type representations in py.test [45483, 45484]
* made that internal reporting issues displaying is done atomically in py.test
[45518]
* made that non-existing files are igored by the py.lookup script [45519]
* improved exception name creation in py.test [45535]
* made that less threads are used in execnet [merge in 45539]
* removed lock required for atomical reporting issue displaying in py.test
[45545]
* removed globals from execnet [45541, 45547]
* refactored cleanup mechanics, made that setDaemon is set to 1 to make atexit
get called in 2.5 (py.execnet) [45548]
* fixed bug in joining threads in py.execnet's servemain [45549]
* refactored py.test.rsession tests to not rely on exact output format anymore
[45646]
* using repr() on test outcome [45647]
* added 'Reason' classes for py.test.skip() [45648, 45649]
* killed some unnecessary sanity check in py.test.collect [45655]
* avoid using os.tmpfile() in py.io.fdcapture because on Windows it's only
usable by Administrators [45901]
* added support for locking and non-recursive commits to py.path.svnwc [45994]
* locking files in py.execnet to prevent CPython from segfaulting [46010]
* added export() method to py.path.svnurl
* fixed -d -x in py.test [47277]
* fixed argument concatenation problem in py.path.svnwc [49423]
* restore py.test behaviour that it exits with code 1 when there are failures
[49974]
* don't fail on html files that don't have an accompanying .txt file [50606]
* fixed 'utestconvert.py < input' [50645]
* small fix for code indentation in py.code.source [50755]
* fix _docgen.py documentation building [51285]
* improved checks for source representation of code blocks in py.test [51292]
* added support for passing authentication to py.path.svn* objects [52000,
52001]
* removed sorted() call for py.apigen tests in favour of [].sort() to support
Python 2.3 [52481]
py-1.5.2/doc/announce/release-1.4.0.txt 0000664 0001750 0001750 00000003563 13202663617 020506 0 ustar vagrant vagrant 0000000 0000000
.. _`release-1.4.0`:
py-1.4.0: cross-python lib for path, code, io, ... manipulations
===========================================================================
"py" is a small library comprising APIs for filesystem and svn path
manipulations, dynamic code construction and introspection, a Py2/Py3
compatibility namespace ("py.builtin"), IO capturing, terminal colored printing
(on windows and linux), ini-file parsing and a lazy import mechanism.
It runs unmodified on all Python interpreters compatible to Python2.4 up
until Python 3.2. The general goal with "py" is to provide stable APIs
for some common tasks that are continously tested against many Python
interpreters and thus also to help transition. Here are some docs:
http://pylib.org
NOTE: The prior py-1.3.X versions contained "py.test" which now comes
as its own separate "pytest" distribution and was just released
as "pytest-2.0.0", see here for the revamped docs:
http://pytest.org
And "py.cleanup|py.lookup|py.countloc" etc. helpers are now part of
the pycmd distribution, see http://pypi.python.org/pypi/pycmd
This makes "py-1.4.0" a simple library which does not install
any command line utilities anymore.
cheers,
holger
Changes between 1.3.4 and 1.4.0
-------------------------------------
- py.test was moved to a separate "pytest" package. What remains is
a stub hook which will proxy ``import py.test`` to ``pytest``.
- all command line tools ("py.cleanup/lookup/countloc/..." moved
to "pycmd" package)
- removed the old and deprecated "py.magic" namespace
- use apipkg-1.1 and make py.apipkg.initpkg|ApiModule available
- add py.iniconfig module for brain-dead easy ini-config file parsing
- introduce py.builtin.any()
- path objects have a .dirname attribute now (equivalent to
os.path.dirname(path))
- path.visit() accepts breadthfirst (bf) and sort options
- remove deprecated py.compat namespace
py-1.5.2/doc/announce/release-1.3.2.txt 0000664 0001750 0001750 00000067033 13202663617 020511 0 ustar vagrant vagrant 0000000 0000000 py.test/pylib 1.3.2: API and reporting refinements, many fixes
===========================================================================
The pylib/py.test 1.3.2 release brings many bug fixes and some new
features. It was refined for and tested against the recently released
Python2.7 and remains compatibile to the usual armada of interpreters
(Python2.4 through to Python3.1.2, Jython and PyPy). Note that for using
distributed testing features you'll need to upgrade to the jointly released
pytest-xdist-1.4 because of some internal refactorings.
See http://pytest.org for general documentation and below for
a detailed CHANGELOG.
cheers & particular thanks to Benjamin Peterson, Ronny Pfannschmidt
and all issue and patch contributors,
holger krekel
Changes between 1.3.1 and 1.3.2
==================================================
New features
++++++++++++++++++
- fix issue103: introduce py.test.raises as context manager, examples::
with py.test.raises(ZeroDivisionError):
x = 0
1 / x
with py.test.raises(RuntimeError) as excinfo:
call_something()
# you may do extra checks on excinfo.value|type|traceback here
(thanks Ronny Pfannschmidt)
- Funcarg factories can now dynamically apply a marker to a
test invocation. This is for example useful if a factory
provides parameters to a test which are expected-to-fail::
def pytest_funcarg__arg(request):
request.applymarker(py.test.mark.xfail(reason="flaky config"))
...
def test_function(arg):
...
- improved error reporting on collection and import errors. This makes
use of a more general mechanism, namely that for custom test item/collect
nodes ``node.repr_failure(excinfo)`` is now uniformly called so that you can
override it to return a string error representation of your choice
which is going to be reported as a (red) string.
- introduce '--junitprefix=STR' option to prepend a prefix
to all reports in the junitxml file.
Bug fixes / Maintenance
++++++++++++++++++++++++++
- make tests and the ``pytest_recwarn`` plugin in particular fully compatible
to Python2.7 (if you use the ``recwarn`` funcarg warnings will be enabled so that
you can properly check for their existence in a cross-python manner).
- refine --pdb: ignore xfailed tests, unify its TB-reporting and
don't display failures again at the end.
- fix assertion interpretation with the ** operator (thanks Benjamin Peterson)
- fix issue105 assignment on the same line as a failing assertion (thanks Benjamin Peterson)
- fix issue104 proper escaping for test names in junitxml plugin (thanks anonymous)
- fix issue57 -f|--looponfail to work with xpassing tests (thanks Ronny)
- fix issue92 collectonly reporter and --pastebin (thanks Benjamin Peterson)
- fix py.code.compile(source) to generate unique filenames
- fix assertion re-interp problems on PyPy, by defering code
compilation to the (overridable) Frame.eval class. (thanks Amaury Forgeot)
- fix py.path.local.pyimport() to work with directories
- streamline py.path.local.mkdtemp implementation and usage
- don't print empty lines when showing junitxml-filename
- add optional boolean ignore_errors parameter to py.path.local.remove
- fix terminal writing on win32/python2.4
- py.process.cmdexec() now tries harder to return properly encoded unicode objects
on all python versions
- install plain py.test/py.which scripts also for Jython, this helps to
get canonical script paths in virtualenv situations
- make path.bestrelpath(path) return ".", note that when calling
X.bestrelpath the assumption is that X is a directory.
- make initial conftest discovery ignore "--" prefixed arguments
- fix resultlog plugin when used in an multicpu/multihost xdist situation
(thanks Jakub Gustak)
- perform distributed testing related reporting in the xdist-plugin
rather than having dist-related code in the generic py.test
distribution
- fix homedir detection on Windows
- ship distribute_setup.py version 0.6.13
Changes between 1.3.0 and 1.3.1
==================================================
New features
++++++++++++++++++
- issue91: introduce new py.test.xfail(reason) helper
to imperatively mark a test as expected to fail. Can
be used from within setup and test functions. This is
useful especially for parametrized tests when certain
configurations are expected-to-fail. In this case the
declarative approach with the @py.test.mark.xfail cannot
be used as it would mark all configurations as xfail.
- issue102: introduce new --maxfail=NUM option to stop
test runs after NUM failures. This is a generalization
of the '-x' or '--exitfirst' option which is now equivalent
to '--maxfail=1'. Both '-x' and '--maxfail' will
now also print a line near the end indicating the Interruption.
- issue89: allow py.test.mark decorators to be used on classes
(class decorators were introduced with python2.6) and
also allow to have multiple markers applied at class/module level
by specifying a list.
- improve and refine letter reporting in the progress bar:
. pass
f failed test
s skipped tests (reminder: use for dependency/platform mismatch only)
x xfailed test (test that was expected to fail)
X xpassed test (test that was expected to fail but passed)
You can use any combination of 'fsxX' with the '-r' extended
reporting option. The xfail/xpass results will show up as
skipped tests in the junitxml output - which also fixes
issue99.
- make py.test.cmdline.main() return the exitstatus instead of raising
SystemExit and also allow it to be called multiple times. This of
course requires that your application and tests are properly teared
down and don't have global state.
Fixes / Maintenance
++++++++++++++++++++++
- improved traceback presentation:
- improved and unified reporting for "--tb=short" option
- Errors during test module imports are much shorter, (using --tb=short style)
- raises shows shorter more relevant tracebacks
- --fulltrace now more systematically makes traces longer / inhibits cutting
- improve support for raises and other dynamically compiled code by
manipulating python's linecache.cache instead of the previous
rather hacky way of creating custom code objects. This makes
it seemlessly work on Jython and PyPy where it previously didn't.
- fix issue96: make capturing more resilient against Control-C
interruptions (involved somewhat substantial refactoring
to the underlying capturing functionality to avoid race
conditions).
- fix chaining of conditional skipif/xfail decorators - so it works now
as expected to use multiple @py.test.mark.skipif(condition) decorators,
including specific reporting which of the conditions lead to skipping.
- fix issue95: late-import zlib so that it's not required
for general py.test startup.
- fix issue94: make reporting more robust against bogus source code
(and internally be more careful when presenting unexpected byte sequences)
Changes between 1.2.1 and 1.3.0
==================================================
- deprecate --report option in favour of a new shorter and easier to
remember -r option: it takes a string argument consisting of any
combination of 'xfsX' characters. They relate to the single chars
you see during the dotted progress printing and will print an extra line
per test at the end of the test run. This extra line indicates the exact
position or test ID that you directly paste to the py.test cmdline in order
to re-run a particular test.
- allow external plugins to register new hooks via the new
pytest_addhooks(pluginmanager) hook. The new release of
the pytest-xdist plugin for distributed and looponfailing
testing requires this feature.
- add a new pytest_ignore_collect(path, config) hook to allow projects and
plugins to define exclusion behaviour for their directory structure -
for example you may define in a conftest.py this method::
def pytest_ignore_collect(path):
return path.check(link=1)
to prevent even a collection try of any tests in symlinked dirs.
- new pytest_pycollect_makemodule(path, parent) hook for
allowing customization of the Module collection object for a
matching test module.
- extend and refine xfail mechanism:
``@py.test.mark.xfail(run=False)`` do not run the decorated test
``@py.test.mark.xfail(reason="...")`` prints the reason string in xfail summaries
specifiying ``--runxfail`` on command line virtually ignores xfail markers
- expose (previously internal) commonly useful methods:
py.io.get_terminal_with() -> return terminal width
py.io.ansi_print(...) -> print colored/bold text on linux/win32
py.io.saferepr(obj) -> return limited representation string
- expose test outcome related exceptions as py.test.skip.Exception,
py.test.raises.Exception etc., useful mostly for plugins
doing special outcome interpretation/tweaking
- (issue85) fix junitxml plugin to handle tests with non-ascii output
- fix/refine python3 compatibility (thanks Benjamin Peterson)
- fixes for making the jython/win32 combination work, note however:
jython2.5.1/win32 does not provide a command line launcher, see
http://bugs.jython.org/issue1491 . See pylib install documentation
for how to work around.
- fixes for handling of unicode exception values and unprintable objects
- (issue87) fix unboundlocal error in assertionold code
- (issue86) improve documentation for looponfailing
- refine IO capturing: stdin-redirect pseudo-file now has a NOP close() method
- ship distribute_setup.py version 0.6.10
- added links to the new capturelog and coverage plugins
Changes between 1.2.1 and 1.2.0
=====================================
- refined usage and options for "py.cleanup"::
py.cleanup # remove "*.pyc" and "*$py.class" (jython) files
py.cleanup -e .swp -e .cache # also remove files with these extensions
py.cleanup -s # remove "build" and "dist" directory next to setup.py files
py.cleanup -d # also remove empty directories
py.cleanup -a # synonym for "-s -d -e 'pip-log.txt'"
py.cleanup -n # dry run, only show what would be removed
- add a new option "py.test --funcargs" which shows available funcargs
and their help strings (docstrings on their respective factory function)
for a given test path
- display a short and concise traceback if a funcarg lookup fails
- early-load "conftest.py" files in non-dot first-level sub directories.
allows to conveniently keep and access test-related options in a ``test``
subdir and still add command line options.
- fix issue67: new super-short traceback-printing option: "--tb=line" will print a single line for each failing (python) test indicating its filename, lineno and the failure value
- fix issue78: always call python-level teardown functions even if the
according setup failed. This includes refinements for calling setup_module/class functions
which will now only be called once instead of the previous behaviour where they'd be called
multiple times if they raise an exception (including a Skipped exception). Any exception
will be re-corded and associated with all tests in the according module/class scope.
- fix issue63: assume <40 columns to be a bogus terminal width, default to 80
- fix pdb debugging to be in the correct frame on raises-related errors
- update apipkg.py to fix an issue where recursive imports might
unnecessarily break importing
- fix plugin links
Changes between 1.2 and 1.1.1
=====================================
- moved dist/looponfailing from py.test core into a new
separately released pytest-xdist plugin.
- new junitxml plugin: --junitxml=path will generate a junit style xml file
which is processable e.g. by the Hudson CI system.
- new option: --genscript=path will generate a standalone py.test script
which will not need any libraries installed. thanks to Ralf Schmitt.
- new option: --ignore will prevent specified path from collection.
Can be specified multiple times.
- new option: --confcutdir=dir will make py.test only consider conftest
files that are relative to the specified dir.
- new funcarg: "pytestconfig" is the pytest config object for access
to command line args and can now be easily used in a test.
- install 'py.test' and `py.which` with a ``-$VERSION`` suffix to
disambiguate between Python3, python2.X, Jython and PyPy installed versions.
- new "pytestconfig" funcarg allows access to test config object
- new "pytest_report_header" hook can return additional lines
to be displayed at the header of a test run.
- (experimental) allow "py.test path::name1::name2::..." for pointing
to a test within a test collection directly. This might eventually
evolve as a full substitute to "-k" specifications.
- streamlined plugin loading: order is now as documented in
customize.html: setuptools, ENV, commandline, conftest.
also setuptools entry point names are turned to canonical namees ("pytest_*")
- automatically skip tests that need 'capfd' but have no os.dup
- allow pytest_generate_tests to be defined in classes as well
- deprecate usage of 'disabled' attribute in favour of pytestmark
- deprecate definition of Directory, Module, Class and Function nodes
in conftest.py files. Use pytest collect hooks instead.
- collection/item node specific runtest/collect hooks are only called exactly
on matching conftest.py files, i.e. ones which are exactly below
the filesystem path of an item
- change: the first pytest_collect_directory hook to return something
will now prevent further hooks to be called.
- change: figleaf plugin now requires --figleaf to run. Also
change its long command line options to be a bit shorter (see py.test -h).
- change: pytest doctest plugin is now enabled by default and has a
new option --doctest-glob to set a pattern for file matches.
- change: remove internal py._* helper vars, only keep py._pydir
- robustify capturing to survive if custom pytest_runtest_setup
code failed and prevented the capturing setup code from running.
- make py.test.* helpers provided by default plugins visible early -
works transparently both for pydoc and for interactive sessions
which will regularly see e.g. py.test.mark and py.test.importorskip.
- simplify internal plugin manager machinery
- simplify internal collection tree by introducing a RootCollector node
- fix assert reinterpreation that sees a call containing "keyword=..."
- fix issue66: invoke pytest_sessionstart and pytest_sessionfinish
hooks on slaves during dist-testing, report module/session teardown
hooks correctly.
- fix issue65: properly handle dist-testing if no
execnet/py lib installed remotely.
- skip some install-tests if no execnet is available
- fix docs, fix internal bin/ script generation
Changes between 1.1.1 and 1.1.0
=====================================
- introduce automatic plugin registration via 'pytest11'
entrypoints via setuptools' pkg_resources.iter_entry_points
- fix py.test dist-testing to work with execnet >= 1.0.0b4
- re-introduce py.test.cmdline.main() for better backward compatibility
- svn paths: fix a bug with path.check(versioned=True) for svn paths,
allow '%' in svn paths, make svnwc.update() default to interactive mode
like in 1.0.x and add svnwc.update(interactive=False) to inhibit interaction.
- refine distributed tarball to contain test and no pyc files
- try harder to have deprecation warnings for py.compat.* accesses
report a correct location
Changes between 1.1.0 and 1.0.2
=====================================
* adjust and improve docs
* remove py.rest tool and internal namespace - it was
never really advertised and can still be used with
the old release if needed. If there is interest
it could be revived into its own tool i guess.
* fix issue48 and issue59: raise an Error if the module
from an imported test file does not seem to come from
the filepath - avoids "same-name" confusion that has
been reported repeatedly
* merged Ronny's nose-compatibility hacks: now
nose-style setup_module() and setup() functions are
supported
* introduce generalized py.test.mark function marking
* reshuffle / refine command line grouping
* deprecate parser.addgroup in favour of getgroup which creates option group
* add --report command line option that allows to control showing of skipped/xfailed sections
* generalized skipping: a new way to mark python functions with skipif or xfail
at function, class and modules level based on platform or sys-module attributes.
* extend py.test.mark decorator to allow for positional args
* introduce and test "py.cleanup -d" to remove empty directories
* fix issue #59 - robustify unittest test collection
* make bpython/help interaction work by adding an __all__ attribute
to ApiModule, cleanup initpkg
* use MIT license for pylib, add some contributors
* remove py.execnet code and substitute all usages with 'execnet' proper
* fix issue50 - cached_setup now caches more to expectations
for test functions with multiple arguments.
* merge Jarko's fixes, issue #45 and #46
* add the ability to specify a path for py.lookup to search in
* fix a funcarg cached_setup bug probably only occuring
in distributed testing and "module" scope with teardown.
* many fixes and changes for making the code base python3 compatible,
many thanks to Benjamin Peterson for helping with this.
* consolidate builtins implementation to be compatible with >=2.3,
add helpers to ease keeping 2 and 3k compatible code
* deprecate py.compat.doctest|subprocess|textwrap|optparse
* deprecate py.magic.autopath, remove py/magic directory
* move pytest assertion handling to py/code and a pytest_assertion
plugin, add "--no-assert" option, deprecate py.magic namespaces
in favour of (less) py.code ones.
* consolidate and cleanup py/code classes and files
* cleanup py/misc, move tests to bin-for-dist
* introduce delattr/delitem/delenv methods to py.test's monkeypatch funcarg
* consolidate py.log implementation, remove old approach.
* introduce py.io.TextIO and py.io.BytesIO for distinguishing between
text/unicode and byte-streams (uses underlying standard lib io.*
if available)
* make py.unittest_convert helper script available which converts "unittest.py"
style files into the simpler assert/direct-test-classes py.test/nosetests
style. The script was written by Laura Creighton.
* simplified internal localpath implementation
Changes between 1.0.1 and 1.0.2
=====================================
* fixing packaging issues, triggered by fedora redhat packaging,
also added doc, examples and contrib dirs to the tarball.
* added a documentation link to the new django plugin.
Changes between 1.0.0 and 1.0.1
=====================================
* added a 'pytest_nose' plugin which handles nose.SkipTest,
nose-style function/method/generator setup/teardown and
tries to report functions correctly.
* capturing of unicode writes or encoded strings to sys.stdout/err
work better, also terminalwriting was adapted and somewhat
unified between windows and linux.
* improved documentation layout and content a lot
* added a "--help-config" option to show conftest.py / ENV-var names for
all longopt cmdline options, and some special conftest.py variables.
renamed 'conf_capture' conftest setting to 'option_capture' accordingly.
* fix issue #27: better reporting on non-collectable items given on commandline
(e.g. pyc files)
* fix issue #33: added --version flag (thanks Benjamin Peterson)
* fix issue #32: adding support for "incomplete" paths to wcpath.status()
* "Test" prefixed classes are *not* collected by default anymore if they
have an __init__ method
* monkeypatch setenv() now accepts a "prepend" parameter
* improved reporting of collection error tracebacks
* simplified multicall mechanism and plugin architecture,
renamed some internal methods and argnames
Changes between 1.0.0b9 and 1.0.0
=====================================
* more terse reporting try to show filesystem path relatively to current dir
* improve xfail output a bit
Changes between 1.0.0b8 and 1.0.0b9
=====================================
* cleanly handle and report final teardown of test setup
* fix svn-1.6 compat issue with py.path.svnwc().versioned()
(thanks Wouter Vanden Hove)
* setup/teardown or collection problems now show as ERRORs
or with big "E"'s in the progress lines. they are reported
and counted separately.
* dist-testing: properly handle test items that get locally
collected but cannot be collected on the remote side - often
due to platform/dependency reasons
* simplified py.test.mark API - see keyword plugin documentation
* integrate better with logging: capturing now by default captures
test functions and their immediate setup/teardown in a single stream
* capsys and capfd funcargs now have a readouterr() and a close() method
(underlyingly py.io.StdCapture/FD objects are used which grew a
readouterr() method as well to return snapshots of captured out/err)
* make assert-reinterpretation work better with comparisons not
returning bools (reported with numpy from thanks maciej fijalkowski)
* reworked per-test output capturing into the pytest_iocapture.py plugin
and thus removed capturing code from config object
* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr)
Changes between 1.0.0b7 and 1.0.0b8
=====================================
* pytest_unittest-plugin is now enabled by default
* introduced pytest_keyboardinterrupt hook and
refined pytest_sessionfinish hooked, added tests.
* workaround a buggy logging module interaction ("closing already closed
files"). Thanks to Sridhar Ratnakumar for triggering.
* if plugins use "py.test.importorskip" for importing
a dependency only a warning will be issued instead
of exiting the testing process.
* many improvements to docs:
- refined funcargs doc , use the term "factory" instead of "provider"
- added a new talk/tutorial doc page
- better download page
- better plugin docstrings
- added new plugins page and automatic doc generation script
* fixed teardown problem related to partially failing funcarg setups
(thanks MrTopf for reporting), "pytest_runtest_teardown" is now
always invoked even if the "pytest_runtest_setup" failed.
* tweaked doctest output for docstrings in py modules,
thanks Radomir.
Changes between 1.0.0b3 and 1.0.0b7
=============================================
* renamed py.test.xfail back to py.test.mark.xfail to avoid
two ways to decorate for xfail
* re-added py.test.mark decorator for setting keywords on functions
(it was actually documented so removing it was not nice)
* remove scope-argument from request.addfinalizer() because
request.cached_setup has the scope arg. TOOWTDI.
* perform setup finalization before reporting failures
* apply modified patches from Andreas Kloeckner to allow
test functions to have no func_code (#22) and to make
"-k" and function keywords work (#20)
* apply patch from Daniel Peolzleithner (issue #23)
* resolve issue #18, multiprocessing.Manager() and
redirection clash
* make __name__ == "__channelexec__" for remote_exec code
Changes between 1.0.0b1 and 1.0.0b3
=============================================
* plugin classes are removed: one now defines
hooks directly in conftest.py or global pytest_*.py
files.
* added new pytest_namespace(config) hook that allows
to inject helpers directly to the py.test.* namespace.
* documented and refined many hooks
* added new style of generative tests via
pytest_generate_tests hook that integrates
well with function arguments.
Changes between 0.9.2 and 1.0.0b1
=============================================
* introduced new "funcarg" setup method,
see doc/test/funcarg.txt
* introduced plugin architecuture and many
new py.test plugins, see
doc/test/plugins.txt
* teardown_method is now guaranteed to get
called after a test method has run.
* new method: py.test.importorskip(mod,minversion)
will either import or call py.test.skip()
* completely revised internal py.test architecture
* new py.process.ForkedFunc object allowing to
fork execution of a function to a sub process
and getting a result back.
XXX lots of things missing here XXX
Changes between 0.9.1 and 0.9.2
===============================
* refined installation and metadata, created new setup.py,
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.
* 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
* removed previously accidentally added
py.test.broken and py.test.notimplemented helpers.
* there now is a py.__version__ attribute
Changes between 0.9.0 and 0.9.1
===============================
This is a fairly complete list of changes between 0.9 and 0.9.1, which can
serve as a reference for developers.
* allowing + signs in py.path.svn urls [39106]
* fixed support for Failed exceptions without excinfo in py.test [39340]
* added support for killing processes for Windows (as well as platforms that
support os.kill) in py.misc.killproc [39655]
* added setup/teardown for generative tests to py.test [40702]
* added detection of FAILED TO LOAD MODULE to py.test [40703, 40738, 40739]
* fixed problem with calling .remove() on wcpaths of non-versioned files in
py.path [44248]
* fixed some import and inheritance issues in py.test [41480, 44648, 44655]
* fail to run greenlet tests when pypy is available, but without stackless
[45294]
* small fixes in rsession tests [45295]
* fixed issue with 2.5 type representations in py.test [45483, 45484]
* made that internal reporting issues displaying is done atomically in py.test
[45518]
* made that non-existing files are igored by the py.lookup script [45519]
* improved exception name creation in py.test [45535]
* made that less threads are used in execnet [merge in 45539]
* removed lock required for atomical reporting issue displaying in py.test
[45545]
* removed globals from execnet [45541, 45547]
* refactored cleanup mechanics, made that setDaemon is set to 1 to make atexit
get called in 2.5 (py.execnet) [45548]
* fixed bug in joining threads in py.execnet's servemain [45549]
* refactored py.test.rsession tests to not rely on exact output format anymore
[45646]
* using repr() on test outcome [45647]
* added 'Reason' classes for py.test.skip() [45648, 45649]
* killed some unnecessary sanity check in py.test.collect [45655]
* avoid using os.tmpfile() in py.io.fdcapture because on Windows it's only
usable by Administrators [45901]
* added support for locking and non-recursive commits to py.path.svnwc [45994]
* locking files in py.execnet to prevent CPython from segfaulting [46010]
* added export() method to py.path.svnurl
* fixed -d -x in py.test [47277]
* fixed argument concatenation problem in py.path.svnwc [49423]
* restore py.test behaviour that it exits with code 1 when there are failures
[49974]
* don't fail on html files that don't have an accompanying .txt file [50606]
* fixed 'utestconvert.py < input' [50645]
* small fix for code indentation in py.code.source [50755]
* fix _docgen.py documentation building [51285]
* improved checks for source representation of code blocks in py.test [51292]
* added support for passing authentication to py.path.svn* objects [52000,
52001]
* removed sorted() call for py.apigen tests in favour of [].sort() to support
Python 2.3 [52481]
py-1.5.2/doc/img/ 0000775 0001750 0001750 00000000000 13203150701 014610 5 ustar vagrant vagrant 0000000 0000000 py-1.5.2/doc/img/pylib.png 0000664 0001750 0001750 00000020124 13202663617 016452 0 ustar vagrant vagrant 0000000 0000000 ‰PNG
IHDR š r ë1\ gAMA ¯È7Šé tEXtSoftware Adobe ImageReadyqÉe<