py-1.5.2/0000775000175000017500000000000013203150701013267 5ustar vagrantvagrant00000000000000py-1.5.2/conftest.py0000664000175000017500000000325313202663755015512 0ustar vagrantvagrant00000000000000import 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/CHANGELOG0000664000175000017500000012457713203150646014531 0ustar vagrantvagrant000000000000001.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/0000775000175000017500000000000013203150701014744 5ustar vagrantvagrant00000000000000py-1.5.2/testing/conftest.py0000664000175000017500000000003713202663617017161 0ustar vagrantvagrant00000000000000 pytest_plugins = "pytester", py-1.5.2/testing/root/0000775000175000017500000000000013203150701015727 5ustar vagrantvagrant00000000000000py-1.5.2/testing/root/test_py_imports.py0000664000175000017500000000367113203150646021564 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000000344013202663755020513 0ustar vagrantvagrant00000000000000 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__.py0000664000175000017500000000000213202663617020046 0ustar vagrantvagrant00000000000000# py-1.5.2/testing/root/test_std.py0000664000175000017500000000041613202663617020151 0ustar vagrantvagrant00000000000000 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.py0000664000175000017500000000764513202663755020667 0ustar vagrantvagrant00000000000000 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.py0000664000175000017500000001207113202663617021025 0ustar vagrantvagrant00000000000000import 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/0000775000175000017500000000000013203150701015700 5ustar vagrantvagrant00000000000000py-1.5.2/testing/path/conftest.py0000664000175000017500000000517513202663755020130 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000000555313202663755021303 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000004715213202663755020503 0ustar vagrantvagrant00000000000000import 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.dump0000664000175000017500000000632713202663617020462 0ustar vagrantvagrant00000000000000SVN-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.py0000664000175000017500000000163413202663617020635 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000000670413202663617020667 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000004003213202663755017562 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000010144113202663755020425 0ustar vagrantvagrant00000000000000# -*- 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.py0000664000175000017500000003752213202663755021033 0ustar vagrantvagrant00000000000000import 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('''\ guido 2008-02-11T12:12:18.476481Z Creating 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/0000775000175000017500000000000013203150701015525 5ustar vagrantvagrant00000000000000py-1.5.2/testing/log/test_log.py0000664000175000017500000001502613203150646017733 0ustar vagrantvagrant00000000000000import 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__.py0000664000175000017500000000000013202663617017642 0ustar vagrantvagrant00000000000000py-1.5.2/testing/log/test_warning.py0000664000175000017500000000475413202663755020636 0ustar vagrantvagrant00000000000000import 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_/0000775000175000017500000000000013203150701015512 5ustar vagrantvagrant00000000000000py-1.5.2/testing/io_/test_saferepr.py0000664000175000017500000000432713202663755020761 0ustar vagrantvagrant00000000000000# -*- 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.py0000664000175000017500000002247513202663755022226 0ustar vagrantvagrant00000000000000 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__.py0000664000175000017500000000000213202663617017631 0ustar vagrantvagrant00000000000000# py-1.5.2/testing/io_/test_capture.py0000664000175000017500000003471013202663755020614 0ustar vagrantvagrant00000000000000from __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/0000775000175000017500000000000013203150701015656 5ustar vagrantvagrant00000000000000py-1.5.2/testing/code/test_assertion.py0000664000175000017500000001716413202663755021330 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000001016413202663617020221 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000007506313202663755020756 0ustar vagrantvagrant00000000000000# -*- 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.py0000664000175000017500000004127613202663755020622 0ustar vagrantvagrant00000000000000from 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/0000775000175000017500000000000013203150701016422 5ustar vagrantvagrant00000000000000py-1.5.2/testing/process/test_killproc.py0000664000175000017500000000113313202663755021671 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000001100413202663755022176 0ustar vagrantvagrant00000000000000import 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__.py0000664000175000017500000000000213202663617020541 0ustar vagrantvagrant00000000000000# py-1.5.2/testing/process/test_cmdexec.py0000664000175000017500000000215713202663755021471 0ustar vagrantvagrant00000000000000import 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/AUTHORS0000664000175000017500000000115013202663617014352 0ustar vagrantvagrant00000000000000Holger 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.py0000664000175000017500000000425613203051015015006 0ustar vagrantvagrant00000000000000import 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.in0000664000175000017500000000024713202663617015046 0ustar vagrantvagrant00000000000000include 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.rst0000664000175000017500000000242613202663755015003 0ustar vagrantvagrant00000000000000.. 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/LICENSE0000664000175000017500000000204513202663617014313 0ustar vagrantvagrant00000000000000 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.ini0000664000175000017500000000165713202663755014634 0ustar vagrantvagrant00000000000000[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-INFO0000664000175000017500000000560513203150701014372 0ustar vagrantvagrant00000000000000Metadata-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/0000775000175000017500000000000013203150701015411 5ustar vagrantvagrant00000000000000py-1.5.2/py.egg-info/top_level.txt0000664000175000017500000000000313203150701020134 0ustar vagrantvagrant00000000000000py py-1.5.2/py.egg-info/SOURCES.txt0000664000175000017500000000510313203150701017274 0ustar vagrantvagrant00000000000000AUTHORS 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.pypy-1.5.2/py.egg-info/not-zip-safe0000664000175000017500000000000113202663736017657 0ustar vagrantvagrant00000000000000 py-1.5.2/py.egg-info/dependency_links.txt0000664000175000017500000000000113203150701021457 0ustar vagrantvagrant00000000000000 py-1.5.2/py.egg-info/PKG-INFO0000664000175000017500000000560513203150701016514 0ustar vagrantvagrant00000000000000Metadata-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.cfg0000664000175000017500000000022013203150701015102 0ustar vagrantvagrant00000000000000[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/0000775000175000017500000000000013203150701013717 5ustar vagrantvagrant00000000000000py-1.5.2/py/_code/0000775000175000017500000000000013203150701014770 5ustar vagrantvagrant00000000000000py-1.5.2/py/_code/source.py0000664000175000017500000003334213202663755016670 0ustar vagrantvagrant00000000000000from __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.py0000664000175000017500000006554413202663755016313 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000004271513202663755020241 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000002627213202663755020254 0ustar vagrantvagrant00000000000000""" 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__.py0000664000175000017500000000005613202663617017120 0ustar vagrantvagrant00000000000000""" python inspection/code generation API """ py-1.5.2/py/_code/_py2traceback.py0000664000175000017500000000531513202663617020075 0ustar vagrantvagrant00000000000000# 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.py0000664000175000017500000000614613202663755017401 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000000116713202663755015250 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000002025413202663617015743 0ustar vagrantvagrant00000000000000""" 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('') % 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>') % (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.py0000664000175000017500000000033613202663617015270 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000000554513202663755015613 0ustar vagrantvagrant00000000000000""" 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__] = errorpy-1.5.2/py/__metainfo.py0000664000175000017500000000006713202663617016412 0ustar vagrantvagrant00000000000000import py pydir = py.path.local(py.__file__).dirpath() py-1.5.2/py/_builtin.py0000664000175000017500000001457113202663617016124 0ustar vagrantvagrant00000000000000import 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__.py0000664000175000017500000001353713203150646016051 0ustar vagrantvagrant00000000000000""" 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/0000775000175000017500000000000013203150701014465 5ustar vagrantvagrant00000000000000py-1.5.2/py/_io/capture.py0000664000175000017500000002657013202663617016532 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000003225613202663755020140 0ustar vagrantvagrant00000000000000""" 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.py0000664000175000017500000000466313202663617016675 0ustar vagrantvagrant00000000000000import 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__.py0000664000175000017500000000003513202663617016612 0ustar vagrantvagrant00000000000000""" input/output helping """ py-1.5.2/py/_log/0000775000175000017500000000000013203150701014637 5ustar vagrantvagrant00000000000000py-1.5.2/py/_log/log.py0000664000175000017500000001356313203150646016012 0ustar vagrantvagrant00000000000000""" 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__.py0000664000175000017500000000011213202663617016760 0ustar vagrantvagrant00000000000000""" logging API ('producers' and 'consumers' connected via keywords) """ py-1.5.2/py/_log/warning.py0000664000175000017500000000500513202663755016677 0ustar vagrantvagrant00000000000000import 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/0000775000175000017500000000000013203150701017522 5ustar vagrantvagrant00000000000000py-1.5.2/py/_vendored_packages/iniconfig.py0000664000175000017500000001213013202663755022057 0ustar vagrantvagrant00000000000000""" 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.py0000664000175000017500000001442413202663755021375 0ustar vagrantvagrant00000000000000""" 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__.py0000664000175000017500000000000013202663755021642 0ustar vagrantvagrant00000000000000py-1.5.2/py/_process/0000775000175000017500000000000013203150701015534 5ustar vagrantvagrant00000000000000py-1.5.2/py/_process/forkedfunc.py0000664000175000017500000000715413202663617020261 0ustar vagrantvagrant00000000000000 """ 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.py0000664000175000017500000000342613202663617017541 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000000121013202663617017735 0ustar vagrantvagrant00000000000000import 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__.py0000664000175000017500000000005013202663617017656 0ustar vagrantvagrant00000000000000""" high-level sub-process handling """ py-1.5.2/py/_path/0000775000175000017500000000000013203150701015012 5ustar vagrantvagrant00000000000000py-1.5.2/py/_path/svnwc.py0000664000175000017500000012546113202663755016556 0ustar vagrantvagrant00000000000000""" 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.py0000664000175000017500000000640513202663617017350 0ustar vagrantvagrant00000000000000""" 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.py0000664000175000017500000003457313202663617016747 0ustar vagrantvagrant00000000000000""" 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.py0000664000175000017500000010502213202663755016477 0ustar vagrantvagrant00000000000000""" 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.py0000664000175000017500000003444213202663755016704 0ustar vagrantvagrant00000000000000""" """ 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__.py0000664000175000017500000000004013202663617017133 0ustar vagrantvagrant00000000000000""" unified file system api """ py-1.5.2/doc/0000775000175000017500000000000013203150701014034 5ustar vagrantvagrant00000000000000py-1.5.2/doc/io.txt0000664000175000017500000000217113202663617015223 0ustar vagrantvagrant00000000000000======= 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.txt0000664000175000017500000001277713202663617015431 0ustar vagrantvagrant00000000000000==================================================== 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 Day python for java programmers why Java 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.py0000664000175000017500000002044213202663617015353 0ustar vagrantvagrant00000000000000# -*- 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.txt0000664000175000017500000001107213202663617015526 0ustar vagrantvagrant00000000000000================================================================================ 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/0000775000175000017500000000000013203150701015467 5ustar vagrantvagrant00000000000000py-1.5.2/doc/example/genhtml.py0000664000175000017500000000040313202663617017512 0ustar vagrantvagrant00000000000000from 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.py0000664000175000017500000000065213202663617020231 0ustar vagrantvagrant00000000000000import 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.py0000664000175000017500000000053013202663617017347 0ustar vagrantvagrant00000000000000 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/0000775000175000017500000000000013203150701015642 5ustar vagrantvagrant00000000000000py-1.5.2/doc/announce/release-1.0.0.txt0000664000175000017500000000544313202663617020501 0ustar vagrantvagrant00000000000000 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.txt0000664000175000017500000000033513202663617020504 0ustar vagrantvagrant00000000000000py 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.txt0000664000175000017500000000561013202663617020500 0ustar vagrantvagrant00000000000000py.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.txt0000664000175000017500000000772713202663617020511 0ustar vagrantvagrant00000000000000py.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.txt0000664000175000017500000000023713202663617020477 0ustar vagrantvagrant000000000000001.0.2: packaging fixes ----------------------------------------------------------------------- this release is purely a release for fixing packaging issues. py-1.5.2/doc/announce/release-1.0.1.txt0000664000175000017500000000334013202663617020474 0ustar vagrantvagrant000000000000001.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.txt0000664000175000017500000000176213202663617020510 0ustar vagrantvagrant00000000000000py.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.txt0000664000175000017500000000155413202663617020512 0ustar vagrantvagrant00000000000000py 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.txt0000664000175000017500000001041013202663617020473 0ustar vagrantvagrant00000000000000py.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.txt0000664000175000017500000000031513202663617020223 0ustar vagrantvagrant00000000000000============= 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.txt0000664000175000017500000000352213202663617020477 0ustar vagrantvagrant00000000000000py.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.txt0000664000175000017500000001150413202663617020476 0ustar vagrantvagrant00000000000000py.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.txt0000664000175000017500000000174313202663617020506 0ustar vagrantvagrant00000000000000py.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.txt0000664000175000017500000000350513202663617020503 0ustar vagrantvagrant00000000000000 .. _`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.txt0000664000175000017500000005317613202663617020512 0ustar vagrantvagrant00000000000000py.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.txt0000664000175000017500000000356313202663617020506 0ustar vagrantvagrant00000000000000 .. _`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.txt0000664000175000017500000006703313202663617020511 0ustar vagrantvagrant00000000000000py.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/0000775000175000017500000000000013203150701014610 5ustar vagrantvagrant00000000000000py-1.5.2/doc/img/pylib.png0000664000175000017500000002012413202663617016452 0ustar vagrantvagrant00000000000000‰PNG  IHDRšrë1\gAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<`PLTE ‰Þòïïl³šœ¢bek<¦ìÔ~  ]cR ÆÆÆ•ÊéÍ!"Lµ==*GR¹¡¬±¸×kkïÉÉiºíš "D;Håä冃ÓÖ×>Eþûû™ÿÿÿž´œqzIDATxÚb`cfƒa6IÅ@ ürrBÌrrbr $$ØYˆA[–KV\–ƒOÈgb11€bà“åä’–e‘—““‘“àg’b` ~ fYYY9&!>!9> 91€š)+/+ cæeÁË@ A9aYF¨‘œr ,ÄÀÏÎ''$''.Ä, Ç$ÇÍ"'ÇË@ ü i&19AiFa ARRòòòÄ &“㔇@ ür¬BìF” AV Ù¼pAZ€Z$&Ç Õ+ ä‘  €và åòÄÀÈPA0-++@ lÜàP““\Àpä– 7Øå„äÀÄ ÅTÍ"ËÍ()''%a 9Fy&Y9 êàúYN €„A"’Œ²L ¬ìB@c䨍Nbr²ü@CùÀÆ@ ¬ŒÀÀ–Z²GœIŽ €d@V‚¬cYÉ$ÇÊÄ@ À€>9 iY.yy.Iyy€b`á”b…xEÄŒi^!^DŠÈË(ò¡æ„„#@1-—“€ŠIÈKÅøˆk°ð—æá”áä PX1CÅ„AÖË(EÉ °@c™è((`g`‚²YyY¤Ž]‚]‚EJ† èfA&&&r&^ `*ã•“c 6>vPÈpC˜¤ä…Øee9Á†0 ðƒ@ˆI€]ˆAŽ €€æ11•HHCQVœK–äfVVV +€JÙé– €€ê„@æ Í—+Î'Ät‡Œ8帙äXä> : pØ ƒL…xê) Bq€ªãÚ+ÀÇÀJò²ò03ãh/0‰ˆÍ  :AHb‘æ”ãÊÊÄÂÉ ^^ ^ 7Ø9%¸€Î  :V°*~v>v^ )y¬ €X˜À¾c`ãàB’`ç !Yy°N€bà9H† l1²Œì( €ÚËÀIΨ6Š‚Ó /4Ó(\XÅ„$P$L¢< ³xxxy$ˆ_†W‚T¢ ©ãáa—“<€ZTÆ Î fZ Í àT,îFs31°H*c`bà&}PЀ 8ù ð $';@ 9)°B bˆ™„Ä€u0WKD“xÅ8ehÔïÀf9~&&`þ’f0v9NV9^)IY.€b“€fCnN~q9^fiiYiaipV`faá–òÄÌ‘U,*‰5+8QrËKq^ i„Ø6˜@1ðÌ«Ìb(ÙTzJJ@ó@1€Ã\†IHHˆ— E,Kò€œ@À:Ô  áEÍZ° ÎÒ @À\*ɨbˆdv&AP.“ ˆi ™ ÙXN Z¿I0Éq`Ìj *„&ÊÍÀÇ)ÄÎ@pӀƢLf°Âd‚–pâRÌÌÌr0kX9„¹µ› ÌP1>Np  €ÀUˆŒ00H0KKJJ ó1±2ƒø\œÀ¬Ï-f AvN¨E |¼à“âAH`g `‹Œ‰TxÈJÊB°fKƒJ7)PÅÇð+P,”%€µ7+(Ÿóñ±ƒ 072CL“ebeâ3¸äÄA"òÀ+2—›Pâ vƒœXˆ ˜= Á)t@ÂMœU¬WhÈ) ÒK,$ÇÎ jÀq2Àjm9vqq -B   ™è&>°±ì,2MOæ9v˜iÜÌ`¿‚\-ŽH°[8„Yø@Å‹(A]®oÙÙYdš„€ Ä§R‚,Òàããå¹J$Á >N&V I¼ìȉƒWH‚ïÂÀ`g H št˜”†d".ˆ˜(ÈɬÀ²Qˆ“ ¨—ƒ[Œؤb‚§?&!`<€J[v€B6MŒO”KÍ3I¨¯Aõ&¤d烗­ply€Â  œ,4ä ¼àR\A€¶,’È­"XNàB€$q`Æbàc  i| BPÓ€‰‡•E˜=ÒàÖ8HU·,ƒÈ s‹±2CŽ”,€næc  iìÀZ(.È ,Y98A)ˆØü–ee09¤Ä…À X|ALeMH¶Öj KÙYhš8P† %sCJY1>H;—U€šG9XNe'`‰Á ,x€9UB€“——EJŠ…— €@QPbâD2‹bLàÌ(.Æ ,,Rš™K¸¤$89M ˆ”4¬¤ pgŽÙe @ø©XLòñ²pÉ㲌Œ(|€bà@Ä 8jXÁ%¶Ðù,ÌÂÀ…< €€%++Ì£bÐÀÌÇÉ"ŒÃX™ÏÆ#Rà p E02€€‰W —õŒ<ð*Tžü<20‹ˆM°â– 8Yq›&Ç,V!±ÃªE€úTj„ 1†Mã”F÷»¬Æ `Ï\zɰÂR='ŽÆÌ4FD-)' I˜ßœB˜€å+$CòKñ²à1 h„„,<>8ÁƈðH@ëT€‚”!LÐÒPœ›I€“o¢áa&aJò@€”<@Mãå,NglF|¦ñ³ C< °Ð2y…˜ €@¦q3±3!•¡Œäü ƒ€i›‹‡‡Ÿâ,^6`?…Q €€(t”¸¶¹¯Oº€YÒ’dd‘`fd”áä„4ˆÔ©aææfà ¤1D Ý iInnfÄX»¤ ,@1ˆƒkiô¦HB˜£LGâÀšX !4k/n>^i€‚˜jåÀŒã–€6íÙ±5‘€© XV  ‰ ±€ê N.€‚™,!03´<UÞˆŽ+j>Nt€,JqÜ4`æçDnp‹)~N99,Ž9\ßÃR²rrÂ4v>p3 ØD,.Å,Ì,%#³–…X‹Rq¨ˆ$‡sr4@X´q lqW‚ l0Ð$AP'Xÿ2 ÚƒÀ¦%¯°p†5ß À@ ,`ÃAM41>a`óKŠIÜ|vÛÄ…ÅX@Lfù¼PæPûA” Í&HÚçä `*%Ì nrpKIBÂr¼Ò`0)0 €Iâ(­R&DÊ5(¤€z âä `Ü21A0°Æˆ,‡¤¥l+sÊHÃ[0Àe¿ ,ªÄ@‰OØœæ$.€b–ì Ìz‹Þ¶›Ì L à†"7"IpÛ›LÐ:Zd'»È‘œ\n²‚M“d``„6d„Á /Pú7$ঠãFÒZ‚Œ @Ú–,L Ó§2P·‚AêUhÓRŽÔìbDÊwÐB˜€Ø…ÄA!'ÆÌ,2Øýä ib|`ÓDA)Ü̵ÿ¸ ¦‚= lió!L÷}€YØ–> GJM i¬PÓ@)SjšÄ«à9¨Ù&kD3€.Ð_ìLLІ%¨y* 8¹l/$NA‘$5C Ì–Y#Ãi‰K“¨Ý ÊHì ,| ìHnæä  iâ &®<¸¥ nòsˆ1€ƒ_X,,€ ξy‘i1I0»Bˆ†?'@Ü&À‰I)1pK\XOó²ÃÛÂ–Š¨…!À‹\|“´  ÅqrÈ4!9ˆi²Œ\†3#¸H†šÆ1 Ü‚h•â@ä PüS°(…$×CMƒ6Éå!Ý(hC]Ò¤•@­BÂÀæ)ð"z˜ `§€B2 –³äa¥!´…éqBKl`ÞáådgG*ÚYY‹‚œ\„l¨Ê–惕p>—8@Ã Ô E-xIX£ƒ‡m8¹Å4pËn˜8åÊK ëeBk¢ƒŠW°ùÂÀF @1€‹dˆi …Òèv p4H£T(2 ¨U…8Ôxf`«’— €ÄYànæ;hµ %ÌÌÌé/ Ô À„'RK A²?¨½Ï$À@@Ÿ# lš$°äưÌgŠBº\¨¦ !õŒ€.V`Ög5‡€ú8ùx¹hš ŸÄ4>P¹*ó9A`—1Ê¡¸ƒðüÏ ê[ wvvpWXLqÄ4)`Nfafv^Pκœ ˜^EA>·Ä¤¤ Ř+¬G* lVÅd€‰‡ƒäN.€bæ01„Àj9 =pÞ–6ûù 1-ìÿÀ­v`׉  K˜9å$¸h°÷ÖO„4¬9À£’BÀz”ð¡£oLœLÀÖ°Y ôxH‹‰صvh¤$x¹èS&`Kš 9Þ!c"¬ & êÛJ°ƒ[ŠRÀF#¸£$j‰€ª@A9v`%Ì, Lš’ÂÒ4M4ú"„’ y!Ý20‡ܽZ.îfI ƒºYÒÌ, Æ(¨¿jÁÁZw4M­ÓÆÁ îg1°ƒÛ\À¶P7°ƒÉÈ%ŒÔS’•– U…h+(gI d~1P$=,Ïy%¤ñõÚPÛ¡©³â˜ß€NÖñ _€¼AJ· €@¦±#ÜÆ ,ö€™XØ ä”bæ’d$¥ (@ÀH\Þ‹¦oи ¨C ìOb7 !ÊÃ/ »ƒ€5*XaQ Lw¤ÂÊË+%‰«QÎŒè[2ƒ»Jˆþ"@³>R4쬼=J\^äç÷NÄ9™Á!¸ÅÄ ÎÏR ìžò1ñáî·ñ#zmÒ`+eyàƒ¹ò,-‘zÎÀJ€Yܦñ2¢w¦á^ pMMµðŠˆWw? Ñ„›h 1x®#ª·ËÏ#!7 €àÍN9¸ñ9 Y/¼ 7 €À±nw²å4 iRcD†@ €$@IT:‹!œÆ,O”i ®,(‰pÂR@KK`‹A\þÁºõø:¨<È©Üû”DŒ9$Ÿ‚úTâ`‘`Æ—ÑyàSÀ8¯$¤Ì¶ p è8èX°ôæ•Â[j 0€=q)˜s¾Š¤åŒ PM'Ä ,½9¹ðP÷€Ÿ‡Wjš8¸;.)@Ó@ÃË ~°_(Ä)LÀ4Fh„òÀâRÜÏe €›& ̪à1P÷‰¯i²Ä%v ,¥ðð²°ñË€™Æ ,µ»”<ÞÁyYð@/Ô4ðhˆ„<8Jh?«;RË¿i"0bç•`šÉ&$Øyx„¤øAS%ò4 ØßV[2BÐÞ0'^Ó@FÊ.Yˆ£@lȈH@1pÛ6œœ¼àá>PØñ ã\VìÌhe ‡0KË3ƒtÓ@1³ )|cRò\@íðôjEƒNX¢ €D8ø¡@†…IXÀù· AEÀæ— D5—47·˜˜  ÐÅ22XT²{\ ËE¸@IUX €ø‘úØÀþ¨@‘@³á@™›öô! ¤Ä íg|@ØÈa’âÖ­à§œ=Bì| *4¨ ŒH.€BqTÕq2À– Š X@v\Æ@Øe·†òù@]9üêÅÁî¶mÙN t§AÌõÐ\Æ‹.¬&™A­Â.ƒLo€ú:B„óƒfø Þ9 €0‹X‡€mh XL3ÈH±òñb #!,.`‚¦q~fan`‚d‘ÁÔÊZ¨oAN d§ Á€0XÙÁvc‰M4”Ë „q€­äƒAvä$,LEÀd$Š/ µXYYÄ‘}!ÎŒÜ)9 €Ýi Dždàã5ÀASœÌRRR`sÅKX˜‰sp1#i€PI„l± +8V}(p°{|àîB jÒ9 €€îg‡Ì´Û¤°Ñ-`K KÂZÚ²Œ’œ@Ó ä¥^æ0y€&d ¢ð—cGÍÔà䆔 €}9 ã`#2 , …ÐiZð kPsj‡¤0ÜÐÁ3a }âÂp²ÀØc—,0µòlØÓ77¸fe”G- @n–q ñOµÀY¸ˆŸCÔ|LHN“…ÍH\.߸nf&PiÀ X–ÅšAÖ Ê À(B9pA’ä5³y¥ hÜdá PÉ jVd§É3CW’Hn ‡pH#œ íõJÁËÊ„¥Ü•U| n:;°ƒîD ŠP¸:^ / ÁFÀÃ, ´@ ,à‚P\ Ùiò¢ìÒH)y1‚¢‘®€ <1/ ‹N1kQp`†¤ d ‹8¢ß) N Xá!ÅŠ¡ W€†JA=_$·G‘¤$Qœ*s«<6·±°¢Œó#d.(šA9@ˆ“šÖÅÀ%Ÿ;dt ä4€‚: ˜Aø$Nƒ¤p—BD!8•³3K"øB @“‡:œªá“uHN óƒK=vp而—€úXoƒ8a³Ï§ÔiÀ@@rš¤84ƒóñ¡;C–Måe98€y@Îü%ƒä8q)HÖ­’äƒvàäÏŠLD Ãj`šg÷ §ÄiÀ´ÊÇ+ƒ”ÖD`:dœ&Èb„Ò‚RP6È٠Ȁä8H1ÏÏÅ .ø ™2® ­ AUØ…è H u/@§ØiÀtLª2È…0²8¤˜Y$à©Ë õ. ¢Œž'%¡.…'j>P6dÖ\ lna«Ø! @éYµºW¤ §,prÊ¡8 XêB‡qàiHR‡Œ4jy K ôŠ p!dòr²ãno€e@Á/rø¥@• ¨2§5€BªÞQ) Zû(Ž9¡ @ 2¼à•hõ€ ¼ÿº%1æªIMòŒ §q¢OÊ2KðÂC l³¯ìœRÒ”%5QX„Æ $$M YQx­Ê‡8Äi l¼ ‰N&,Nãä%Ûiˆ4q¸!Ê.bQÄ! NH™]ª@ §‰CÇÐ@CF¬àµg ¡WXë(²#êp«]Ô‘€–kÂ2ÀìÅî<ƒ;>\âPgE`"@1HÉ ú„Hý\8ƒüøå€Z ›QšK˜Ô׃.1d÷N‘ÑÄ +ÉEß›v‰¥%‰îß ª0¸'éwõ×jÂIEND®B`‚py-1.5.2/doc/links.inc0000664000175000017500000000136213202663617015667 0ustar vagrantvagrant00000000000000 .. _`skipping plugin`: plugin/skipping.html .. _`funcargs mechanism`: funcargs.html .. _`doctest.py`: http://docs.python.org/library/doctest.html .. _`xUnit style setup`: xunit_setup.html .. _`pytest_nose`: plugin/nose.html .. _`reStructured Text`: http://docutils.sourceforge.net .. _`Python debugger`: http://docs.python.org/lib/module-pdb.html .. _nose: http://somethingaboutorange.com/mrl/projects/nose/ .. _pytest: http://pypi.python.org/pypi/pytest .. _mercurial: http://mercurial.selenic.com/wiki/ .. _`setuptools`: http://pypi.python.org/pypi/setuptools .. _`distribute`: http://pypi.python.org/pypi/distribute .. _`pip`: http://pypi.python.org/pypi/pip .. _`virtualenv`: http://pypi.python.org/pypi/virtualenv .. _hudson: http://hudson-ci.org/ py-1.5.2/doc/index.txt0000664000175000017500000000117313202663617015724 0ustar vagrantvagrant00000000000000.. py documentation master file, created by sphinx-quickstart on Thu Oct 21 08:30:10 2010. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to py's documentation! ================================= see :ref:`CHANGELOG ` for latest changes. .. _`pytest distribution`: http://pytest.org Contents: .. toctree:: install path code io log xml misc :maxdepth: 2 .. toctree:: :hidden: announce/release-2.0.0 changelog announce/* Indices and tables ================== * :ref:`genindex` * :ref:`search` py-1.5.2/doc/install.txt0000664000175000017500000000444513202663755016273 0ustar vagrantvagrant00000000000000 .. _`py`: .. _`index page`: http://pypi.python.org/pypi/py/ installation info in a nutshell =================================================== **PyPI name**: py_ **Pythons**: CPython 2.7, 3.4, 3.5, 3.6, PyPy-5.4 **Operating systems**: Linux, Windows, OSX, Unix **Requirements**: setuptools_ or Distribute_ **Installers**: ``easy_install`` and ``pip`` **hg repository**: https://bitbucket.org/hpk42/py easy install or pip ``py`` ----------------------------- Both `Distribute`_ and setuptools_ provide the ``easy_install`` installation tool with which you can type into a command line window:: easy_install -U py to install the latest release of the py lib. The ``-U`` switch will trigger an upgrade if you already have an older version installed. .. note:: As of version 1.4 py does not contain py.test anymore - you need to install the new `pytest`_ distribution. .. _pytest: http://pytest.org Working from version control or a tarball ----------------------------------------------- To follow development or start experiments, checkout the complete code and documentation source with mercurial_:: hg clone https://bitbucket.org/hpk42/py Development takes place on the 'trunk' branch. You can also go to the python package index and download and unpack a TAR file:: http://pypi.python.org/pypi/py/ activating a checkout with setuptools -------------------------------------------- With a working `Distribute`_ or setuptools_ installation you can type:: python setup.py develop in order to work inline with the tools and the lib of your checkout. .. _`no-setuptools`: .. _`directly use a checkout`: .. _`setuptools`: http://pypi.python.org/pypi/setuptools Mailing list and issue tracker -------------------------------------- - `py-dev developers list`_ and `commit mailing list`_. - #pylib on irc.freenode.net IRC channel for random questions. - `bitbucket issue tracker`_ use this bitbucket issue tracker to report bugs or request features. .. _`bitbucket issue tracker`: http://bitbucket.org/hpk42/py/issues/ .. _codespeak: http://codespeak.net/ .. _`py-dev`: .. _`development mailing list`: .. _`py-dev developers list`: http://codespeak.net/mailman/listinfo/py-dev .. _`py-svn`: .. _`commit mailing list`: http://codespeak.net/mailman/listinfo/py-svn .. include:: links.inc py-1.5.2/doc/path.txt0000664000175000017500000001756113202663755015564 0ustar vagrantvagrant00000000000000======= py.path ======= The 'py' lib provides a uniform high-level api to deal with filesystems and filesystem-like interfaces: ``py.path``. It aims to offer a central object to fs-like object trees (reading from and writing to files, adding files/directories, examining the types and structure, etc.), and out-of-the-box provides a number of implementations of this API. py.path.local - local file system path =============================================== .. _`local`: basic interactive example ------------------------------------- The first and most obvious of the implementations is a wrapper around a local filesystem. It's just a bit nicer in usage than the regular Python APIs, and of course all the functionality is bundled together rather than spread over a number of modules. .. sourcecode:: pycon >>> import py >>> temppath = py.path.local('py.path_documentation') >>> foopath = temppath.join('foo') # get child 'foo' (lazily) >>> foopath.check() # check if child 'foo' exists False >>> foopath.write('bar') # write some data to it >>> foopath.check() True >>> foopath.read() 'bar' >>> foofile = foopath.open() # return a 'real' file object >>> foofile.read(1) 'b' reference documentation --------------------------------- .. autoclass:: py._path.local.LocalPath :members: :inherited-members: ``py.path.svnurl`` and ``py.path.svnwc`` ================================================== Two other ``py.path`` implementations that the py lib provides wrap the popular `Subversion`_ revision control system: the first (called 'svnurl') by interfacing with a remote server, the second by wrapping a local checkout. Both allow you to access relatively advanced features such as metadata and versioning, and both in a way more user-friendly manner than existing other solutions. Some example usage of ``py.path.svnurl``: .. sourcecode:: pycon .. >>> import py .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk') >>> url = py.path.svnurl('http://codespeak.net/svn/py') >>> info = url.info() >>> info.kind 'dir' >>> firstentry = url.log()[-1] >>> import time >>> time.strftime('%Y-%m-%d', time.gmtime(firstentry.date)) '2004-10-02' Example usage of ``py.path.svnwc``: .. sourcecode:: pycon .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk') >>> temp = py.path.local('py.path_documentation') >>> wc = py.path.svnwc(temp.join('svnwc')) >>> wc.checkout('http://codespeak.net/svn/py/dist/py/path/local') >>> wc.join('local.py').check() True .. _`Subversion`: http://subversion.tigris.org/ svn path related API reference ----------------------------------------- .. autoclass:: py._path.svnwc.SvnWCCommandPath :members: :inherited-members: .. autoclass:: py._path.svnurl.SvnCommandPath :members: :inherited-members: .. autoclass:: py._path.svnwc.SvnAuth :members: :inherited-members: Common vs. specific API, Examples ======================================== All Path objects support a common set of operations, suitable for many use cases and allowing to transparently switch the path object within an application (e.g. from "local" to "svnwc"). The common set includes functions such as `path.read()` to read all data from a file, `path.write()` to write data, `path.listdir()` to get a list of directory entries, `path.check()` to check if a node exists and is of a particular type, `path.join()` to get to a (grand)child, `path.visit()` to recursively walk through a node's children, etc. Only things that are not common on 'normal' filesystems (yet), such as handling metadata (e.g. the Subversion "properties") require using specific APIs. A quick 'cookbook' of small examples that will be useful 'in real life', which also presents parts of the 'common' API, and shows some non-common methods: Searching `.txt` files -------------------------------- Search for a particular string inside all files with a .txt extension in a specific directory. .. sourcecode:: pycon >>> dirpath = temppath.ensure('testdir', dir=True) >>> dirpath.join('textfile1.txt').write('foo bar baz') >>> dirpath.join('textfile2.txt').write('frob bar spam eggs') >>> subdir = dirpath.ensure('subdir', dir=True) >>> subdir.join('textfile1.txt').write('foo baz') >>> subdir.join('textfile2.txt').write('spam eggs spam foo bar spam') >>> results = [] >>> for fpath in dirpath.visit('*.txt'): ... if 'bar' in fpath.read(): ... results.append(fpath.basename) >>> results.sort() >>> results ['textfile1.txt', 'textfile2.txt', 'textfile2.txt'] Working with Paths ---------------------------- This example shows the ``py.path`` features to deal with filesystem paths Note that the filesystem is never touched, all operations are performed on a string level (so the paths don't have to exist, either): .. sourcecode:: pycon >>> p1 = py.path.local('/foo/bar') >>> p2 = p1.join('baz/qux') >>> p2 == py.path.local('/foo/bar/baz/qux') True >>> sep = py.path.local.sep >>> p2.relto(p1).replace(sep, '/') # os-specific path sep in the string 'baz/qux' >>> p2.bestrelpath(p1).replace(sep, '/') '../..' >>> p2.join(p2.bestrelpath(p1)) == p1 True >>> p3 = p1 / 'baz/qux' # the / operator allows joining, too >>> p2 == p3 True >>> p4 = p1 + ".py" >>> p4.basename == "bar.py" True >>> p4.ext == ".py" True >>> p4.purebasename == "bar" True This should be possible on every implementation of ``py.path``, so regardless of whether the implementation wraps a UNIX filesystem, a Windows one, or a database or object tree, these functions should be available (each with their own notion of path seperators and dealing with conversions, etc.). Checking path types ------------------------------- Now we will show a bit about the powerful 'check()' method on paths, which allows you to check whether a file exists, what type it is, etc.: .. sourcecode:: pycon >>> file1 = temppath.join('file1') >>> file1.check() # does it exist? False >>> file1 = file1.ensure(file=True) # 'touch' the file >>> file1.check() True >>> file1.check(dir=True) # is it a dir? False >>> file1.check(file=True) # or a file? True >>> file1.check(ext='.txt') # check the extension False >>> textfile = temppath.ensure('text.txt', file=True) >>> textfile.check(ext='.txt') True >>> file1.check(basename='file1') # we can use all the path's properties here True Setting svn-properties -------------------------------- As an example of 'uncommon' methods, we'll show how to read and write properties in an ``py.path.svnwc`` instance: .. sourcecode:: pycon .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk') >>> wc.propget('foo') '' >>> wc.propset('foo', 'bar') >>> wc.propget('foo') 'bar' >>> len(wc.status().prop_modified) # our own props 1 >>> msg = wc.revert() # roll back our changes >>> len(wc.status().prop_modified) 0 SVN authentication ---------------------------- Some uncommon functionality can also be provided as extensions, such as SVN authentication: .. sourcecode:: pycon .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk') >>> auth = py.path.SvnAuth('anonymous', 'user', cache_auth=False, ... interactive=False) >>> wc.auth = auth >>> wc.update() # this should work >>> path = wc.ensure('thisshouldnotexist.txt') >>> try: ... path.commit('testing') ... except py.process.cmdexec.Error, e: ... pass >>> 'authorization failed' in str(e) True Known problems / limitations =================================== * The SVN path objects require the "svn" command line, there is currently no support for python bindings. Parsing the svn output can lead to problems, particularly regarding if you have a non-english "locales" setting. * While the path objects basically work on windows, there is no attention yet on making unicode paths work or deal with the famous "8.3" filename issues. py-1.5.2/doc/faq.txt0000664000175000017500000001453113202663617015366 0ustar vagrantvagrant00000000000000================================== Frequently Asked Questions ================================== .. contents:: :local: :depth: 2 On naming, nosetests, licensing and magic =========================================== Why the ``py`` naming? Why not ``pytest``? ---------------------------------------------------- This mostly has historic reasons - the aim is to get away from the somewhat questionable 'py' name at some point. These days (2010) the 'py' library almost completely comprises APIs that are used by the ``py.test`` tool. There also are some other uses, e.g. of the ``py.path.local()`` and other path implementations. So it requires some work to factor them out and do the shift. Why the ``py.test`` naming? ------------------------------------ because of TAB-completion under Bash/Shells. If you hit ``py.`` you'll get a list of available development tools that all share the ``py.`` prefix. Another motivation was to unify the package ("py.test") and tool filename. What's py.test's relation to ``nosetests``? --------------------------------------------- py.test and nose_ share basic philosophy when it comes to running Python tests. In fact, with py.test-1.1.0 it is ever easier to run many test suites that currently work with ``nosetests``. nose_ was created as a clone of ``py.test`` when py.test was in the ``0.8`` release cycle so some of the newer features_ introduced with py.test-1.0 and py.test-1.1 have no counterpart in nose_. .. _nose: http://somethingaboutorange.com/mrl/projects/nose/0.11.1/ .. _features: test/features.html .. _apipkg: http://pypi.python.org/pypi/apipkg What's this "magic" with py.test? ---------------------------------------- issues where people have used the term "magic" in the past: * `py/__init__.py`_ uses the apipkg_ mechanism for lazy-importing and full control on what API you get when importing "import py". * when an ``assert`` statement fails, py.test re-interprets the expression to show intermediate values if a test fails. If your expression has side effects the intermediate values may not be the same, obfuscating the initial error (this is also explained at the command line if it happens). ``py.test --no-assert`` turns off assert re-intepretation. Sidenote: it is good practise to avoid asserts with side effects. .. _`py namespaces`: index.html .. _`py/__init__.py`: http://bitbucket.org/hpk42/py-trunk/src/trunk/py/__init__.py Where does my ``py.test`` come/import from? ---------------------------------------------- You can issue:: py.test --version which tells you both version and import location of the tool. function arguments, parametrized tests and setup ==================================================== .. _funcargs: test/funcargs.html Is using funcarg- versus xUnit-based setup a style question? --------------------------------------------------------------- It depends. For simple applications or for people experienced with nose_ or unittest-style test setup using `xUnit style setup`_ make some sense. For larger test suites, parametrized testing or setup of complex test resources using funcargs_ is recommended. Moreover, funcargs are ideal for writing advanced test support code (like e.g. the monkeypatch_, the tmpdir_ or capture_ funcargs) because the support code can register setup/teardown functions in a managed class/module/function scope. .. _monkeypatch: test/plugin/monkeypatch.html .. _tmpdir: test/plugin/tmpdir.html .. _capture: test/plugin/capture.html .. _`xUnit style setup`: test/xunit_setup.html .. _`pytest_nose`: test/plugin/nose.html .. _`why pytest_pyfuncarg__ methods?`: Why the ``pytest_funcarg__*`` name for funcarg factories? --------------------------------------------------------------- When experimenting with funcargs an explicit registration mechanism was considered. But lacking a good use case for this indirection and flexibility we decided to go for `Convention over Configuration`_ and allow to directly specify the factory. Besides removing the need for an indirection it allows to "grep" for ``pytest_funcarg__MYARG`` and will safely find all factory functions for the ``MYARG`` function argument. It helps to alleviate the de-coupling of function argument usage and creation. .. _`Convention over Configuration`: http://en.wikipedia.org/wiki/Convention_over_Configuration Can I yield multiple values from a factory function? ----------------------------------------------------- There are two conceptual reasons why yielding from a factory function is not possible: * Calling factories for obtaining test function arguments is part of setting up and running a test. At that point it is not possible to add new test calls to the test collection anymore. * If multiple factories yielded values there would be no natural place to determine the combination policy - in real-world examples some combinations often should not run. Use the `pytest_generate_tests`_ hook to solve both issues and implement the `parametrization scheme of your choice`_. .. _`pytest_generate_tests`: test/funcargs.html#parametrizing-tests .. _`parametrization scheme of your choice`: http://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/ py.test interaction with other packages =============================================== Issues with py.test, multiprocess and setuptools? ------------------------------------------------------------ On windows the multiprocess package will instantiate sub processes by pickling and thus implicitely re-import a lot of local modules. Unfortuantely, setuptools-0.6.11 does not ``if __name__=='__main__'`` protect its generated command line script. This leads to infinite recursion when running a test that instantiates Processes. There are these workarounds: * `install Distribute`_ as a drop-in replacement for setuptools and install py.test * `directly use a checkout`_ which avoids all setuptools/Distribute installation If those options are not available to you, you may also manually fix the script that is created by setuptools by inserting an ``if __name__ == '__main__'``. Or you can create a "pytest.py" script with this content and invoke that with the python version:: import py if __name__ == '__main__': py.cmdline.pytest() .. _`directly use a checkout`: install.html#directly-use-a-checkout .. _`install distribute`: http://pypi.python.org/pypi/distribute#installation-instructions py-1.5.2/doc/download.html0000664000175000017500000000103313202663617016544 0ustar vagrantvagrant00000000000000 py-1.5.2/doc/changelog.txt0000664000175000017500000000005413202663617016541 0ustar vagrantvagrant00000000000000.. _`changelog`: .. include:: ../CHANGELOG py-1.5.2/doc/_templates/0000775000175000017500000000000013203150701016171 5ustar vagrantvagrant00000000000000py-1.5.2/doc/_templates/layout.html0000664000175000017500000000105113202663617020407 0ustar vagrantvagrant00000000000000{% extends "!layout.html" %} {% block footer %} {{ super() }} {% endblock %} py-1.5.2/doc/style.css0000664000175000017500000004200613202663617015726 0ustar vagrantvagrant00000000000000body,body.editor,body.body { font: 110% "Times New Roman", Arial, Verdana, Helvetica, serif; background: White; color: Black; } a, a.reference { text-decoration: none; } a[href]:hover { text-decoration: underline; } img { border: none; vertical-align: middle; } p, div.text { text-align: left; line-height: 1.5em; margin: 0.5em 0em 0em 0em; } p a:active { color: Red; background-color: transparent; } p img { border: 0; margin: 0; } img.inlinephoto { padding: 0; padding-right: 1em; padding-top: 0.7em; float: left; } hr { clear: both; height: 1px; color: #8CACBB; background-color: transparent; } ul { line-height: 1.5em; /*list-style-image: url("bullet.gif"); */ margin-left: 1.5em; padding:0; } ol { line-height: 1.5em; margin-left: 1.5em; padding:0; } ul a, ol a { text-decoration: underline; } dl { } dd { line-height: 1.5em; margin-bottom: 1em; } blockquote { font-family: Times, "Times New Roman", serif; font-style: italic; font-size: 120%; } code { color: Black; /*background-color: #dee7ec;*/ /*background-color: #cccccc;*/ } pre { padding: 1em; border: 1px dotted #8cacbb; color: Black; /* background-color: #dee7ec; background-color: #cccccc; background-color: #dee7ec; */ overflow: auto; } .netscape4 { display: none; } /* main page styles */ /*a[href]:hover { color: black; text-decoration: underline; } a[href]:link { color: black; text-decoration: underline; } a[href] { color: black; text-decoration: underline; } */ span.menu_selected { color: black; text-decoration: none; padding-right: 0.3em; background-color: #cccccc; } a.menu { /*color: #3ba6ec; */ font: 120% Verdana, Helvetica, Arial, sans-serif; text-decoration: none; padding-right: 0.3em; } a.menu[href]:visited, a.menu[href]:link{ /*color: #3ba6ec; */ text-decoration: none; } a.menu[href]:hover { /*color: black;*/ } div#pagetitle{ /*border-spacing: 20px;*/ font: 160% Verdana, Helvetica, Arial, sans-serif; color: #3ba6ec; vertical-align: middle; left: 80 px; padding-bottom: 0.3em; } a.wikicurrent { font: 100% Verdana, Helvetica, Arial, sans-serif; color: #3ba6ec; vertical-align: middle; } table.body { border: 0; /*padding: 0; border-spacing: 0px; border-collapse: separate; */ } td.page-header-left { padding: 5px; /*border-bottom: 1px solid #444444;*/ } td.page-header-top { padding: 0; /*border-bottom: 1px solid #444444;*/ } td.sidebar { padding: 1 0 0 1; } td.sidebar p.classblock { padding: 0 5 0 5; margin: 1 1 1 1; border: 1px solid #444444; background-color: #eeeeee; } td.sidebar p.userblock { padding: 0 5 0 5; margin: 1 1 1 1; border: 1px solid #444444; background-color: #eeeeff; } td.content { padding: 1 5 1 5; vertical-align: top; width: 100%; } p.ok-message { background-color: #22bb22; padding: 5 5 5 5; color: white; font-weight: bold; } p.error-message { background-color: #bb2222; padding: 5 5 5 5; color: white; font-weight: bold; } p:first-child { margin: 0 ; padding: 0; } /* style for forms */ table.form { padding: 2; border-spacing: 0px; border-collapse: separate; } table.form th { color: #333388; text-align: right; vertical-align: top; font-weight: normal; } table.form th.header { font-weight: bold; background-color: #eeeeff; text-align: left; } table.form th.required { font-weight: bold; } table.form td { color: #333333; empty-cells: show; vertical-align: top; } table.form td.optional { font-weight: bold; font-style: italic; } table.form td.html { color: #777777; } /* style for lists */ table.list { border-spacing: 0px; border-collapse: separate; vertical-align: top; padding-top: 0; width: 100%; } table.list th { padding: 0 4 0 4; color: #404070; background-color: #eeeeff; border-right: 1px solid #404070; border-top: 1px solid #404070; border-bottom: 1px solid #404070; vertical-align: top; empty-cells: show; } table.list th a[href]:hover { color: #404070 } table.list th a[href]:link { color: #404070 } table.list th a[href] { color: #404070 } table.list th.group { background-color: #f4f4ff; text-align: center; font-size: 120%; } table.list td { padding: 0 4 0 4; border: 0 2 0 2; border-right: 1px solid #404070; color: #404070; background-color: white; vertical-align: top; empty-cells: show; } table.list tr.normal td { background-color: white; white-space: nowrap; } table.list tr.alt td { background-color: #efefef; white-space: nowrap; } table.list td:first-child { border-left: 1px solid #404070; border-right: 1px solid #404070; } table.list th:first-child { border-left: 1px solid #404070; border-right: 1px solid #404070; } table.list tr.navigation th { text-align: right; } table.list tr.navigation th:first-child { border-right: none; text-align: left; } /* style for message displays */ table.messages { border-spacing: 0px; border-collapse: separate; width: 100%; } table.messages th.header{ padding-top: 10px; border-bottom: 1px solid gray; font-weight: bold; background-color: white; color: #707040; } table.messages th { font-weight: bold; color: black; text-align: left; border-bottom: 1px solid #afafaf; } table.messages td { font-family: monospace; background-color: #efefef; border-bottom: 1px solid #afafaf; color: black; empty-cells: show; border-right: 1px solid #afafaf; vertical-align: top; padding: 2 5 2 5; } table.messages td:first-child { border-left: 1px solid #afafaf; border-right: 1px solid #afafaf; } /* style for file displays */ table.files { border-spacing: 0px; border-collapse: separate; width: 100%; } table.files th.header{ padding-top: 10px; border-bottom: 1px solid gray; font-weight: bold; background-color: white; color: #707040; } table.files th { border-bottom: 1px solid #afafaf; font-weight: bold; text-align: left; } table.files td { font-family: monospace; empty-cells: show; } /* style for history displays */ table.history { border-spacing: 0px; border-collapse: separate; width: 100%; } table.history th.header{ padding-top: 10px; border-bottom: 1px solid gray; font-weight: bold; background-color: white; color: #707040; font-size: 100%; } table.history th { border-bottom: 1px solid #afafaf; font-weight: bold; text-align: left; font-size: 90%; } table.history td { font-size: 90%; vertical-align: top; empty-cells: show; } /* style for class list */ table.classlist { border-spacing: 0px; border-collapse: separate; width: 100%; } table.classlist th.header{ padding-top: 10px; border-bottom: 1px solid gray; font-weight: bold; background-color: white; color: #707040; } table.classlist th { font-weight: bold; text-align: left; } /* style for class help display */ table.classhelp { border-spacing: 0px; border-collapse: separate; width: 100%; } table.classhelp th { font-weight: bold; text-align: left; color: #707040; } table.classhelp td { padding: 2 2 2 2; border: 1px solid black; text-align: left; vertical-align: top; empty-cells: show; } /* style for "other" displays */ table.otherinfo { border-spacing: 0px; border-collapse: separate; width: 100%; } table.otherinfo th.header{ padding-top: 10px; border-bottom: 1px solid gray; font-weight: bold; background-color: white; color: #707040; } table.otherinfo th { border-bottom: 1px solid #afafaf; font-weight: bold; text-align: left; } input { border: 1px solid #8cacbb; color: Black; background-color: white; vertical-align: middle; margin-bottom: 1px; /* IE bug fix */ padding: 0.1em; } select { border: 1px solid #8cacbb; color: Black; background-color: white; vertical-align: middle; margin-bottom: 1px; /* IE bug fix */ padding: 0.1em; } a.nonexistent { color: #FF2222; } a.nonexistent:visited { color: #FF2222; } a.external { color: #AA6600; } /* dl,ul,ol { margin-top: 1pt; } tt,pre { font-family: Lucida Console,Courier New,Courier,monotype; font-size: 12pt; } pre.code { margin-top: 8pt; margin-bottom: 8pt; background-color: #FFFFEE; white-space:pre; border-style:solid; border-width:1pt; border-color:#999999; color:#111111; padding:5px; width:100%; } */ div.diffold { background-color: #FFFF80; border-style:none; border-width:thin; width:100%; } div.diffnew { background-color: #80FF80; border-style:none; border-width:thin; width:100%; } div.message { margin-top: 6pt; background-color: #E8FFE8; border-style:solid; border-width:1pt; border-color:#999999; color:#440000; padding:5px; width:100%; } strong.highlight { background-color: #FFBBBB; /* as usual, NetScape fucks up with innocent CSS border-color: #FFAAAA; border-style: solid; border-width: 1pt; */ } table.navibar { background-color: #C8C8C8; border-spacing: 3px; } td.navibar { background-color: #E8E8E8; vertical-align: top; text-align: right; padding: 0px; } a#versioninfo { color: blue; } div#pagename { font-size: 140%; color: blue; text-align: center; font-weight: bold; background-color: white; padding: 0 ; } a.wikiaction, input.wikiaction { color: black; text-decoration: None; text-align: center; color: black; /*border: 1px solid #3ba6ec; */ margin: 4px; padding: 5; padding-bottom: 0; white-space: nowrap; } a.wikiaction[href]:hover { color: black; text-decoration: none; /*background-color: #dddddd; */ } div.legenditem { padding-top: 0.5em; padding-left: 0.3em; } span.wikitoken { background-color: #eeeeee; } div#contentspace h1:first-child, div.heading:first-child { padding-top: 0; margin-top: 0; } div#contentspace h2:first-child { padding-top: 0; margin-top: 0; } /* heading and paragraph text */ div.heading, h1 { font-family: Verdana, Helvetica, Arial, sans-serif; background-color: #58b3ef; background-color: #FFFFFF; /*color: #4893cf;*/ color: black; padding-top: 1.0em; padding-bottom:0.2em; text-align: left; margin-top: 0em; /*margin-bottom:8pt;*/ font-weight: bold; font-size: 115%; border-bottom: 1px solid #8CACBB; } h2 { border-bottom: 1px dotted #8CACBB; } h1, h2, h3, h4, h5, h6 { color: Black; clear: left; font: 100% Verdana, Helvetica, Arial, sans-serif; margin: 0; padding-left: 0em; padding-top: 1em; padding-bottom: 0.2em; /*border-bottom: 1px solid #8CACBB;*/ } /* h1,h2 { padding-top: 0; }*/ h1 { font-size: 145%; } h2 { font-size: 115%; } h3 { font-size: 105%; } h4 { font-size: 100%; } h5 { font-size: 100%; } h1 a { text-decoration: None;} div.exception { background-color: #bb2222; padding: 5 5 5 5; color: white; font-weight: bold; } pre.exception { font-size: 110%; padding: 1em; border: 1px solid #8cacbb; color: Black; background-color: #dee7ec; background-color: #cccccc; } /* defines for navgiation bar (documentation) */ div.direntry { padding-top: 0.3em; padding-bottom: 0.3em; margin-right: 1em; font-weight: bold; background-color: #dee7ec; font-size: 110%; } div.fileentry { font-family: Verdana, Helvetica, Arial, sans-serif; padding-bottom: 0.3em; white-space: nowrap; line-height: 150%; } a.fileentry { white-space: nowrap; } span.left { text-align: left; } span.right { text-align: right; } div.navbar { /*margin: 0;*/ font-size: 80% /*smaller*/; font-weight: bold; text-align: left; /* position: fixed; */ top: 100pt; left: 0pt; /* auto; */ width: 120pt; /* right: auto; right: 0pt; 2em; */ } div.history a { /* font-size: 70%; */ } div.wikiactiontitle { font-weight: bold; } /* REST defines */ div.document { margin: 0; } h1.title { margin: 0; margin-bottom: 0.5em; } td.toplist { vertical-align: top; } img#pyimg { float: left; padding-bottom: 1em; } div#navspace { position: absolute; font-size: 100%; width: 150px; overflow: hidden; /* scroll; */ } div#errorline { position: relative; top: 5px; float: right; } div#contentspace { position: absolute; /* font: 120% "Times New Roman", serif;*/ font: 110% Verdana, Helvetica, Arial, sans-serif; left: 170px; margin-right: 5px; } div#menubar { /* width: 400px; */ float: left; } /* for the documentation page */ div#title{ font-size: 110%; color: black; /*background-color: #dee7ec; #padding: 5pt; #padding-bottom: 1em; #color: black; border-width: 1pt; border-style: solid;*/ } div#docnavlist { /*background-color: #dee7ec; */ padding: 5pt; padding-bottom: 2em; color: black; border-width: 1pt; /*border-style: solid;*/ } /* text markup */ div.listtitle { color: Black; clear: left; font: 120% Verdana, Helvetica, Arial, sans-serif; margin: 0; padding-left: 0em; padding-top: 0em; padding-bottom: 0.2em; margin-right: 0.5em; border-bottom: 1px solid #8CACBB; } div.actionbox h3 { padding-top: 0; padding-right: 0.5em; padding-left: 0.5em; background-color: #fabf00; text-align: center; border: 1px solid black; /* 8cacbb; */ } div.actionbox a { display: block; padding-bottom: 0.5em; padding-top: 0.5em; margin-left: 0.5em; } div.actionbox a.history { display: block; padding-bottom: 0.5em; padding-top: 0.5em; margin-left: 0.5em; font-size: 90%; } div.actionbox { margin-bottom: 2em; padding-bottom: 1em; overflow: hidden; /* scroll; */ } /* taken from docutils (oh dear, a bit senseless) */ ol.simple, ul.simple { margin-bottom: 1em } ol.arabic { list-style: decimal } ol.loweralpha { list-style: lower-alpha } ol.upperalpha { list-style: upper-alpha } ol.lowerroman { list-style: lower-roman } ol.upperroman { list-style: upper-roman } /* :Author: David Goodger :Contact: goodger@users.sourceforge.net :date: $Date: 2003/01/22 22:26:48 $ :version: $Revision: 1.29 $ :copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. */ /* .first { margin-top: 0 } .last { margin-bottom: 0 } a.toc-backref { text-decoration: none ; color: black } dd { margin-bottom: 0.5em } div.abstract { margin: 2em 5em } div.abstract p.topic-title { font-weight: bold ; text-align: center } div.attention, div.caution, div.danger, div.error, div.hint, div.important, div.note, div.tip, div.warning { margin: 2em ; border: medium outset ; padding: 1em } div.attention p.admonition-title, div.caution p.admonition-title, div.danger p.admonition-title, div.error p.admonition-title, div.warning p.admonition-title { color: red ; font-weight: bold ; font-family: sans-serif } div.hint p.admonition-title, div.important p.admonition-title, div.note p.admonition-title, div.tip p.admonition-title { font-weight: bold ; font-family: sans-serif } div.dedication { margin: 2em 5em ; text-align: center ; font-style: italic } div.dedication p.topic-title { font-weight: bold ; font-style: normal } div.figure { margin-left: 2em } div.footer, div.header { font-size: smaller } div.system-messages { margin: 5em } div.system-messages h1 { color: red } div.system-message { border: medium outset ; padding: 1em } div.system-message p.system-message-title { color: red ; font-weight: bold } div.topic { margin: 2em } h1.title { text-align: center } h2.subtitle { text-align: center } hr { width: 75% } p.caption { font-style: italic } p.credits { font-style: italic ; font-size: smaller } p.label { white-space: nowrap } p.topic-title { font-weight: bold } pre.address { margin-bottom: 0 ; margin-top: 0 ; font-family: serif ; font-size: 100% } pre.line-block { font-family: serif ; font-size: 100% } pre.literal-block, pre.doctest-block { margin-left: 2em ; margin-right: 2em ; background-color: #eeeeee } span.classifier { font-family: sans-serif ; font-style: oblique } span.classifier-delimiter { font-family: sans-serif ; font-weight: bold } span.interpreted { font-family: sans-serif } span.option { white-space: nowrap } span.option-argument { font-style: italic } span.pre { white-space: pre } span.problematic { color: red } table { margin-top: 0.5em ; margin-bottom: 0.5em } table.citation { border-left: solid thin gray ; padding-left: 0.5ex } table.docinfo { margin: 2em 4em } table.footnote { border-left: solid thin black ; padding-left: 0.5ex } td, th { padding-left: 0.5em ; padding-right: 0.5em ; vertical-align: top } th.docinfo-name, th.field-name { font-weight: bold ; text-align: left ; white-space: nowrap } h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { font-size: 100% } tt { background-color: #eeeeee } ul.auto-toc { list-style-type: none } */ div.section { margin-top: 1.0em ; } py-1.5.2/doc/misc.txt0000664000175000017500000000634313202663617015554 0ustar vagrantvagrant00000000000000==================================== Miscellaneous features of the py lib ==================================== Mapping the standard python library into py =========================================== The ``py.std`` object allows lazy access to standard library modules. For example, to get to the print-exception functionality of the standard library you can write:: py.std.traceback.print_exc() without having to do anything else than the usual ``import py`` at the beginning. You can access any other top-level standard library module this way. This means that you will only trigger imports of modules that are actually needed. Note that no attempt is made to import submodules. Support for interaction with system utilities/binaries ====================================================== Currently, the py lib offers two ways to interact with system executables. ``py.process.cmdexec()`` invokes the shell in order to execute a string. The other one, ``py.path.local``'s 'sysexec()' method lets you directly execute a binary. Both approaches will raise an exception in case of a return- code other than 0 and otherwise return the stdout-output of the child process. The shell based approach ------------------------ You can execute a command via your system shell by doing something like:: out = py.process.cmdexec('ls -v') However, the ``cmdexec`` approach has a few shortcomings: - it relies on the underlying system shell - it neccessitates shell-escaping for expressing arguments - it does not easily allow to "fix" the binary you want to run. - it only allows to execute executables from the local filesystem .. _sysexec: local paths have ``sysexec`` ---------------------------- In order to synchronously execute an executable file you can use ``sysexec``:: binsvn.sysexec('ls', 'http://codespeak.net/svn') where ``binsvn`` is a path that points to the ``svn`` commandline binary. Note that this function does not offer any shell-escaping so you have to pass in already separated arguments. finding an executable local path -------------------------------- Finding an executable is quite different on multiple platforms. Currently, the ``PATH`` environment variable based search on unix platforms is supported:: py.path.local.sysfind('svn') which returns the first path whose ``basename`` matches ``svn``. In principle, `sysfind` deploys platform specific algorithms to perform the search. On Windows, for example, it may look at the registry (XXX). To make the story complete, we allow to pass in a second ``checker`` argument that is called for each found executable. For example, if you have multiple binaries available you may want to select the right version:: def mysvn(p): """ check that the given svn binary has version 1.1. """ line = p.execute('--version'').readlines()[0] if line.find('version 1.1'): return p binsvn = py.path.local.sysfind('svn', checker=mysvn) Cross-Python Version compatibility helpers ============================================= The ``py.builtin`` namespace provides a number of helpers that help to write python code compatible across Python interpreters, mainly Python2 and Python3. Type ``help(py.builtin)`` on a Python prompt for the selection of builtins. py-1.5.2/doc/log.txt0000664000175000017500000002210613202663617015375 0ustar vagrantvagrant00000000000000.. role:: code(literal) .. role:: file(literal) .. XXX figure out how the code literals should be dealt with in sphinx. There is probably something builtin. ======================================== py.log documentation and musings ======================================== Foreword ======== This document is an attempt to briefly state the actual specification of the :code:`py.log` module. It was written by Francois Pinard and also contains some ideas for enhancing the py.log facilities. NOTE that :code:`py.log` is subject to refactorings, it may change with the next release. This document is meant to trigger or facilitate discussions. It shamelessly steals from the `Agile Testing`__ comments, and from other sources as well, without really trying to sort them out. __ http://agiletesting.blogspot.com/2005/06/keyword-based-logging-with-py-library.html Logging organisation ==================== The :code:`py.log` module aims a niche comparable to the one of the `logging module`__ found within the standard Python distributions, yet with much simpler paradigms for configuration and usage. __ http://www.python.org/doc/2.4.2/lib/module-logging.html Holger Krekel, the main :code:`py` library developer, introduced the idea of keyword-based logging and the idea of logging *producers* and *consumers*. A log producer is an object used by the application code to send messages to various log consumers. When you create a log producer, you define a set of keywords that are then used to both route the logging messages to consumers, and to prefix those messages. In fact, each log producer has a few keywords associated with it for identification purposes. These keywords form a tuple of strings, and may be used to later retrieve a particular log producer. A log producer may (or may not) be associated with a log consumer, meant to handle log messages in particular ways. The log consumers can be ``STDOUT``, ``STDERR``, log files, syslog, the Windows Event Log, user defined functions, etc. (Yet, logging to syslog or to the Windows Event Log is only future plans for now). A log producer has never more than one consumer at a given time, but it is possible to dynamically switch a producer to use another consumer. On the other hand, a single log consumer may be associated with many producers. Note that creating and associating a producer and a consumer is done automatically when not otherwise overriden, so using :code:`py` logging is quite comfortable even in the smallest programs. More typically, the application programmer will likely design a hierarchy of producers, and will select keywords appropriately for marking the hierarchy tree. If a node of the hierarchical tree of producers has to be divided in sub-trees, all producers in the sub-trees share, as a common prefix, the keywords of the node being divided. In other words, we go further down in the hierarchy of producers merely by adding keywords. Using the py.log library ================================ To use the :code:`py.log` library, the user must import it into a Python application, create at least one log producer and one log consumer, have producers and consumers associated, and finally call the log producers as needed, giving them log messages. Importing --------- Once the :code:`py` library is installed on your system, a mere:: import py holds enough magic for lazily importing the various facilities of the :code:`py` library when they are first needed. This is really how :code:`py.log` is made available to the application. For example, after the above ``import py``, one may directly write ``py.log.Producer(...)`` and everything should work fine, the user does not have to worry about specifically importing more modules. Creating a producer ------------------- There are three ways for creating a log producer instance: + As soon as ``py.log`` is first evaluated within an application program, a default log producer is created, and made available under the name ``py.log.default``. The keyword ``default`` is associated with that producer. + The ``py.log.Producer()`` constructor may be explicitly called for creating a new instance of a log producer. That constructor accepts, as an argument, the keywords that should be associated with that producer. Keywords may be given either as a tuple of keyword strings, or as a single space-separated string of keywords. + Whenever an attribute is *taken* out of a log producer instance, for the first time that attribute is taken, a new log producer is created. The keywords associated with that new producer are those of the initial producer instance, to which is appended the name of the attribute being taken. The last point is especially useful, as it allows using log producers without further declarations, merely creating them *on-the-fly*. Creating a consumer ------------------- There are many ways for creating or denoting a log consumer: + A default consumer exists within the ``py.log`` facilities, which has the effect of writing log messages on the Python standard output stream. That consumer is associated at the very top of the producer hierarchy, and as such, is called whenever no other consumer is found. + The notation ``py.log.STDOUT`` accesses a log consumer which writes log messages on the Python standard output stream. + The notation ``py.log.STDERR`` accesses a log consumer which writes log messages on the Python standard error stream. + The ``py.log.File()`` constructor accepts, as argument, either a file already opened in write mode or any similar file-like object, and creates a log consumer able to write log messages onto that file. + The ``py.log.Path()`` constructor accepts a file name for its first argument, and creates a log consumer able to write log messages into that file. The constructor call accepts a few keyword parameters: + ``append``, which is ``False`` by default, may be used for opening the file in append mode instead of write mode. + ``delayed_create``, which is ``False`` by default, maybe be used for opening the file at the latest possible time. Consequently, the file will not be created if it did not exist, and no actual log message gets written to it. + ``buffering``, which is 1 by default, is used when opening the file. Buffering can be turned off by specifying a 0 value. The buffer size may also be selected through this argument. + Any user defined function may be used for a log consumer. Such a function should accept a single argument, which is the message to write, and do whatever is deemed appropriate by the programmer. When the need arises, this may be an especially useful and flexible feature. + The special value ``None`` means no consumer at all. This acts just like if there was a consumer which would silently discard all log messages sent to it. Associating producers and consumers ----------------------------------- Each log producer may have at most one log consumer associated with it. A log producer gets associated with a log consumer through a ``py.log.setconsumer()`` call. That function accepts two arguments, the first identifying a producer (a tuple of keyword strings or a single space-separated string of keywords), the second specifying the precise consumer to use for that producer. Until this function is called for a producer, that producer does not have any explicit consumer associated with it. Now, the hierarchy of log producers establishes which consumer gets used whenever a producer has no explicit consumer. When a log producer has no consumer explicitly associated with it, it dynamically and recursively inherits the consumer of its parent node, that is, that node being a bit closer to the root of the hierarchy. In other words, the rightmost keywords of that producer are dropped until another producer is found which has an explicit consumer. A nice side-effect is that, by explicitly associating a consumer with a producer, all consumer-less producers which appear under that producer, in the hierarchy tree, automatically *inherits* that consumer. Writing log messages -------------------- All log producer instances are also functions, and this is by calling them that log messages are generated. Each call to a producer object produces the text for one log entry, which in turn, is sent to the log consumer for that producer. The log entry displays, after a prefix identifying the log producer being used, all arguments given in the call, converted to strings and space-separated. (This is meant by design to be fairly similar to what the ``print`` statement does in Python). The prefix itself is made up of a colon-separated list of keywords associated with the producer, the whole being set within square brackets. Note that the consumer is responsible for adding the newline at the end of the log entry. That final newline is not part of the text for the log entry. .. Other details .. ------------- .. XXX: fill in details .. + Should speak about pickle-ability of :code:`py.log`. .. .. + What is :code:`log.get` (in :file:`logger.py`)? py-1.5.2/doc/Makefile0000664000175000017500000001103413202663617015511 0ustar vagrantvagrant00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* install: clean html rsync -avz _build/html/ code:www-pylib/ html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/py.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/py.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/py" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/py" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt."