diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..f9c71a7f --- /dev/null +++ b/.flake8 @@ -0,0 +1,4 @@ +[flake8] +max-line-length = 120 +per-file-ignores = + **/*.pyi:E252,E301,E302,E305,E501,E701,E704,F401,F811,F821 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..0192a42a --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,68 @@ +name: build + +on: [push, pull_request] + +jobs: + build: + + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + python: ["2.7", "3.5", "3.6", "3.7", "3.8", "pypy3"] + os: [ubuntu-latest, windows-latest] + include: + - python: "2.7" + tox_env: "py27-pytest30" + - python: "3.5" + tox_env: "py35-pytest30" + - python: "3.6" + tox_env: "py36-pytest30" + - python: "3.7" + tox_env: "py37-pytest30" + - python: "3.8" + tox_env: "py38-pytest30" + - python: "pypy3" + tox_env: "pypy3-pytest30" + + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python }} + - name: Install tox + run: | + python -m pip install --upgrade pip + pip install tox + - name: Test + run: | + tox -e ${{ matrix.tox_env }} + + deploy: + + if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') + + runs-on: ubuntu-latest + + needs: build + + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + with: + python-version: "3.7" + - name: Install wheel + run: | + python -m pip install --upgrade pip + pip install wheel + - name: Build package + run: | + python setup.py sdist bdist_wheel + - name: Publish package to PyPI + uses: pypa/gh-action-pypi-publish@master + with: + user: __token__ + password: ${{ secrets.pypi_token }} diff --git a/.gitignore b/.gitignore index 5bb0d457..fa936f15 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .cache/ .tox/ __pycache__/ +.mypy_cache/ *.pyc *.pyo @@ -10,3 +11,5 @@ __pycache__/ .eggs/ dist/* +/py/_version.py +.pytest_cache/ diff --git a/.hgignore b/.hgignore deleted file mode 100644 index 34976da5..00000000 --- a/.hgignore +++ /dev/null @@ -1,29 +0,0 @@ - -# Automatically generated by `hgimportsvn` -syntax:glob -.svn -.hgsvn - -# These lines are suggested according to the svn:ignore property -# Feel free to enable them by uncommenting them -syntax:glob -*.pyc -*.pyo -*.swp -*.html -*.class -*.orig -*~ - -doc/_build -build/ -dist/ -*.egg-info -issue/ -env/ -3rdparty/ -.tox -lib/ -bin/ -include/ -src/ diff --git a/.hgtags b/.hgtags deleted file mode 100644 index 9d48095b..00000000 --- a/.hgtags +++ /dev/null @@ -1,68 +0,0 @@ -52c6d9e78777a5a34e813123997dfc614a1a4767 1.0.0b3 -1c7aaa8c61f3b0945921a9acc7beb184201aed4b 1.0.0b4 -1c7aaa8c61f3b0945921a9acc7beb184201aed4b 1.0.0b4 -0000000000000000000000000000000000000000 1.0.0b4 -0000000000000000000000000000000000000000 1.0.0b4 -8cd6eb91eba313b012d6e568f37d844dc0751f2e 1.0.0b4 -8cd6eb91eba313b012d6e568f37d844dc0751f2e 1.0.0b4 -0000000000000000000000000000000000000000 1.0.0b4 -2cc0507f117ffe721dff7ee026648cfce00ec92f 1.0.0b6 -86f1e1b6e49bf5882a809f11edd1dbb08162cdad 1.0.0b8 -86f1e1b6e49bf5882a809f11edd1dbb08162cdad 1.0.0b8 -c63f35c266cbb26dad6b87b5e115d65685adf448 1.0.0b8 -c63f35c266cbb26dad6b87b5e115d65685adf448 1.0.0b8 -0eaa0fdf2ba0163cf534dc2eff4ba2e5fc66c261 1.0.0b8 -e2a60653cb490aeed81bbbd83c070b99401c211c 1.0.0b9 -5ea0cdf7854c3d4278d36eda94a2b68483a0e211 1.0.0 -5ea0cdf7854c3d4278d36eda94a2b68483a0e211 1.0.0 -7acde360d94b6a2690ce3d03ff39301da84c0a2b 1.0.0 -6bd221981ac99103002c1cb94fede400d23a96a1 1.0.1 -4816e8b80602a3fd3a0a120333ad85fbe7d8bab4 1.0.2 -60c44bdbf093285dc69d5462d4dbb4acad325ca6 1.1.0 -319187fcda66714c5eb1353492babeec3d3c826f 1.1.1 -4fc5212f7626a56b9eb6437b5c673f56dd7eb942 1.2.0 -c143a8c8840a1c68570890c8ac6165bbf92fd3c6 1.2.1 -eafd3c256e8732dfb0a4d49d051b5b4339858926 1.3.0 -d5eacf390af74553227122b85e20345d47b2f9e6 1.3.1 -d5eacf390af74553227122b85e20345d47b2f9e6 1.3.1 -8b8e7c25a13cf863f01b2dd955978285ae9daf6a 1.3.1 -3bff44b188a7ec1af328d977b9d39b6757bb38df 1.3.2 -c59d3fa8681a5b5966b8375b16fccd64a3a8dbeb 1.3.3 -79ef6377705184c55633d456832eea318fedcf61 1.3.4 -79ef6377705184c55633d456832eea318fedcf61 1.3.4 -90fffd35373e9f125af233f78b19416f0938d841 1.3.4 -5346ab41b059c95a48cbe1e8a7bae96ce6e0da27 1.4.0 -1f3125cba7976538952be268f107c1d0c36c5ce8 1.4.1 -04ab22db4ff737cf31e91d75a0f5d7077f324167 1.4.2 -9950bf9d684a984d511795013421c89c5cf88bef 1.4.3 -d9951e3bdbc765e73835ae13012f6a074d13d8bf 1.4.4 -b827dd156a36753e32c7f3f15ce82d6fe9e356c8 1.4.6 -f15726f9e5a67cc6221c499affa4840e9d591763 1.4.7 -abfabd07a1d328f13c730e8a50d80d2e470afd3b 1.4.9 -7f37ee0aff9be4b839d6759cfee336f60e8393a4 1.4.10 -fe4593263efa10ea7ba014db6e3379e0b82368a2 1.4.11 -f07af25a26786e4825b5170e17ad693245cb3426 1.4.12 -d3730d84ba7eda92fd3469a3f63fd6d8cb22c975 1.4.13 -12c1ae8e7c5345721e9ec9f8e27b1e36c07f74dc 1.4.14 -12c1ae8e7c5345721e9ec9f8e27b1e36c07f74dc 1.4.14 -0000000000000000000000000000000000000000 1.4.14 -0000000000000000000000000000000000000000 1.4.14 -1497e2efd0f8c73a0e3d529debf0c489e4cd6cab 1.4.14 -e065014c1ce8ad110a381e9baaaa5d647ba7ac6b 1.4.15 -e9e5b38f53dc35b35aa1f9ee9a9be9bbd2d2c3b1 1.4.16 -c603503945f52b78522d96a423605cbc953236d3 1.4.17 -c59201105a29801cc858eb9160b7a19791b91a35 1.4.18 -284cc172e294d48edc840012e1451c32c3963d92 1.4.19 -a3e0626aa0c5aecf271367dc77e476ab216ea3c8 1.4.20 -5e48016c4a3af8e7358a1267d33d021e71765bed 1.4.21 -01ae2cfcc61c4fcb3aa5031349adb5b467c31018 1.4.23 -5ffd982f4dff60b588f309cd9bdc61036547282a 1.4.24 -dc9ffbcaf1f7d72e96be3f68c11deebb7e7193c5 1.4.25 -6de1a44bf75de7af4fcae947c235e9072bbdbb9a 1.4.26 -7d650ba2657890a2253c8c4a83f170febebd90fa 1.4.27 -7d650ba2657890a2253c8c4a83f170febebd90fa 1.4.27 -1810003dec63dd1b506a23849861fffa5bc3ba13 1.4.27 -ba08706f08ddea1b77a426f00dfe2bdc244345e8 1.4.28 -4e8054ada63f3327bcf759ae7cd36c7c8652bc9b 1.4.29 -366ab346610c6de8aaa7617e24011794b40236c6 1.4.30 -657380e439f9b7e04918cb162cb2e46388244b42 1.4.31 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 917c59d1..00000000 --- a/.travis.yml +++ /dev/null @@ -1,27 +0,0 @@ -sudo: false -language: python -python: -- '2.7' -- '3.4' -- '3.5' -- '3.6' -- 'pypy-5.4' -env: -- DEPS="pytest~=2.9.0" -- DEPS="pytest~=3.0.0" -#- DEPS="pytest~=3.1.0" - -matrix: - - include: - - python: '2.7' - # using a different option due to pytest-addopts pytester issues - env: PYTEST_XADDOPTS="-n 3 --runslowtests" DEPS="pytest~=3.0.0 pytest-xdist" - allow_failures: - - python: 'pypy-5.4' -install: -- pip install -U setuptools setuptools_scm -- pip install $DEPS -- pip install -U . --force-reinstall -script: -- py.test --lsof $PYTEST_XADDOPTS diff --git a/AUTHORS b/AUTHORS index 8c0cf9b7..9c5dda9c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -22,3 +22,4 @@ Jan Balster Grig Gheorghiu Bob Ippolito Christian Tismer +Wim Glenn diff --git a/CHANGELOG b/CHANGELOG.rst similarity index 93% rename from CHANGELOG rename to CHANGELOG.rst index a3e4bf1b..90e5905a 100644 --- a/CHANGELOG +++ b/CHANGELOG.rst @@ -1,3 +1,83 @@ +1.10.0 (2020-12-12) +=================== + +- Fix a regular expression DoS vulnerability in the py.path.svnwc SVN blame functionality (CVE-2020-29651) +- Update vendored apipkg: 1.4 => 1.5 +- Update vendored iniconfig: 1.0.0 => 1.1.1 + +1.9.0 (2020-06-24) +================== + +- Add type annotation stubs for the following modules: + + * ``py.error`` + * ``py.iniconfig`` + * ``py.path`` (not including SVN paths) + * ``py.io`` + * ``py.xml`` + + There are no plans to type other modules at this time. + + The type annotations are provided in external .pyi files, not inline in the + code, and may therefore contain small errors or omissions. If you use ``py`` + in conjunction with a type checker, and encounter any type errors you believe + should be accepted, please report it in an issue. + +1.8.2 (2020-06-15) +================== + +- On Windows, ``py.path.local``s which differ only in case now have the same + Python hash value. Previously, such paths were considered equal but had + different hashes, which is not allowed and breaks the assumptions made by + dicts, sets and other users of hashes. + +1.8.1 (2019-12-27) +================== + +- Handle ``FileNotFoundError`` when trying to import pathlib in ``path.common`` + on Python 3.4 (#207). + +- ``py.path.local.samefile`` now works correctly in Python 3 on Windows when dealing with symlinks. + +1.8.0 (2019-02-21) +================== + +- add ``"importlib"`` pyimport mode for python3.5+, allowing unimportable test suites + to contain identically named modules. + +- fix ``LocalPath.as_cwd()`` not calling ``os.chdir()`` with ``None``, when + being invoked from a non-existing directory. + + +1.7.0 (2018-10-11) +================== + +- fix #174: use ``shutil.get_terminal_size()`` in Python 3.3+ to determine the size of the + terminal, which produces more accurate results than the previous method. + +- fix pytest-dev/pytest#2042: introduce new ``PY_IGNORE_IMPORTMISMATCH`` environment variable + that suppresses ``ImportMismatchError`` exceptions when set to ``1``. + + +1.6.0 (2018-08-27) +================== + +- add ``TerminalWriter.width_of_current_line`` (i18n version of + ``TerminalWriter.chars_on_current_line``), a read-only property + that tracks how wide the current line is, attempting to take + into account international characters in the calculation. + +1.5.4 (2018-06-27) +================== + +- fix pytest-dev/pytest#3451: don't make assumptions about fs case sensitivity + in ``make_numbered_dir``. + +1.5.3 +===== + +- fix #179: ensure we can support 'from py.error import ...' + 1.5.2 ===== @@ -11,6 +91,10 @@ 1.5.0 ===== +NOTE: **this release has been removed from PyPI** due to missing package +metadata which caused a number of problems to py26 and py33 users. +This issue was fixed in the 1.5.1 release. + - 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 @@ -65,7 +149,7 @@ 1.4.30 ================================================== -- fix issue68 an assert with a multiline list comprehension +- fix issue68 an assert with a multiline list comprehension was not reported correctly. Thanks Henrik Heibuerger. @@ -93,7 +177,7 @@ - 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 + sys.path - strike undocumented, untested argument to py.path.local.pypkgpath @@ -141,10 +225,10 @@ 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" + 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 +- 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. @@ -167,8 +251,8 @@ 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 + (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 @@ -180,7 +264,7 @@ - ignore unicode decode errors in xmlescape. Thanks Anatoly Bubenkoff. -- on python2 modify traceback.format_exception_only to match python3 +- 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 @@ -210,7 +294,7 @@ 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 + (if you start passing unicode particular in py2 you might still get problems, though) Changes between 1.4.16 and 1.4.17 @@ -277,7 +361,7 @@ Changes between 1.4.12 and 1.4.13 Changes between 1.4.11 and 1.4.12 ================================================== -- fix python2.4 support - for pre-AST interpreters re-introduce +- 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, @@ -295,7 +379,7 @@ Changes between 1.4.10 and 1.4.11 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 +- fix error handling on win32/py33 for ENODIR Changes between 1.4.9 and 1.4.10 ================================================== @@ -332,12 +416,12 @@ Changes between 1.4.6 and 1.4.7 Changes between 1.4.5 and 1.4.6 ================================================== -- help to fix pytest issue99: unify output of +- 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 +- 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) @@ -406,7 +490,7 @@ 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 +- 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 diff --git a/MANIFEST.in b/MANIFEST.in index 239ad228..6d255b1a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,10 +1,11 @@ -include CHANGELOG +include CHANGELOG.rst include AUTHORS include README.rst include setup.py include LICENSE include conftest.py include tox.ini +recursive-include py *.pyi graft doc graft testing global-exclude *.pyc diff --git a/README.rst b/README.rst index 7092ae4c..80800b2b 100644 --- a/README.rst +++ b/README.rst @@ -1,17 +1,14 @@ .. 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 +.. image:: https://img.shields.io/conda/vn/conda-forge/py.svg :target: https://anaconda.org/conda-forge/py -.. image:: https://img.shields.io/pypi/pyversions/pytest.svg +.. image:: https://img.shields.io/pypi/pyversions/py.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 +.. image:: https://github.com/pytest-dev/py/workflows/build/badge.svg + :target: https://github.com/pytest-dev/py/actions **NOTE**: this library is in **maintenance mode** and should not be used in new code. @@ -19,15 +16,15 @@ 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``). +* ``py.path``: uniform local and svn path objects -> please use pathlib/pathlib2 instead +* ``py.apipkg``: explicit API control and lazy-importing -> please use the standalone package instead +* ``py.iniconfig``: easy parsing of .ini files -> please use the standalone package instead +* ``py.code``: dynamic code generation and introspection (deprecated, moved to ``pytest`` as a implementation detail). **NOTE**: prior to the 1.4 release this distribution used to -contain py.test which is now its own package, see http://pytest.org +contain py.test which is now its own package, see https://docs.pytest.org -For questions and more information please visit http://py.readthedocs.org +For questions and more information please visit https://py.readthedocs.io Bugs and issues: https://github.com/pytest-dev/py diff --git a/RELEASING.rst b/RELEASING.rst new file mode 100644 index 00000000..fb588e3a --- /dev/null +++ b/RELEASING.rst @@ -0,0 +1,17 @@ +Release Procedure +----------------- + +#. Create a branch ``release-X.Y.Z`` from the latest ``master``. + +#. Manually update the ``CHANGELOG.rst`` and commit. + +#. Open a PR for this branch targeting ``master``. + +#. After all tests pass and the PR has been approved by at least another maintainer, publish to PyPI by creating and pushing a tag:: + + git tag X.Y.Z + git push git@github.com:pytest-dev/py X.Y.Z + + Wait for the deploy to complete, then make sure it is `available on PyPI `_. + +#. Merge your PR to ``master``. diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 5fbeca9a..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,26 +0,0 @@ -environment: - matrix: - # note: please use "tox --listenvs" to populate the build matrix below - - TOXENV: "py27-pytest29" - - TOXENV: "py27-pytest30" - - TOXENV: "py27-pytest31" - - TOXENV: "py34-pytest29" - - TOXENV: "py34-pytest30" - - TOXENV: "py34-pytest31" - - TOXENV: "py35-pytest29" - - TOXENV: "py35-pytest30" - - TOXENV: "py35-pytest31" - - TOXENV: "py36-pytest29" - - TOXENV: "py36-pytest30" - - TOXENV: "py36-pytest31" - -install: - - echo Installed Pythons - - dir c:\Python* - - - C:\Python36\python -m pip install --upgrade --pre tox - -build: false # Not a C# project, build stuff at the test step instead. - -test_script: - - C:\Python36\python -m tox diff --git a/bench/localpath.py b/bench/localpath.py index ad4fbd8e..aad44f2e 100644 --- a/bench/localpath.py +++ b/bench/localpath.py @@ -1,6 +1,4 @@ - import py -import timeit class Listdir: numiter = 100000 @@ -70,6 +68,6 @@ def run(self): for i in xrange(cls.numiter): inst.run() elapsed = time.time() - now - print "%s: %d loops took %.2f seconds, per call %.6f" %( + print("%s: %d loops took %.2f seconds, per call %.6f" %( cls.__name__, - cls.numiter, elapsed, elapsed / cls.numiter) + cls.numiter, elapsed, elapsed / cls.numiter)) diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..a0a30858 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,7 @@ +coverage: + status: + project: true + patch: true + changes: true + +comment: off diff --git a/doc/announce/release-0.9.2.txt b/doc/announce/release-0.9.2.txt index bc2d2ef2..8340dc44 100644 --- a/doc/announce/release-0.9.2.txt +++ b/doc/announce/release-0.9.2.txt @@ -16,7 +16,7 @@ Here is a quick summary of what the py lib provides: See here for more information: -Pypi pages: http://pypi.python.org/pypi/py/ +Pypi pages: https://pypi.org/project/py/ Download/Install: http://codespeak.net/py/0.9.2/download.html diff --git a/doc/announce/release-1.0.0.txt b/doc/announce/release-1.0.0.txt index 7024255a..aef25ec2 100644 --- a/doc/announce/release-1.0.0.txt +++ b/doc/announce/release-1.0.0.txt @@ -58,6 +58,6 @@ holger .. _`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 +.. _`1.0.0 py lib release`: https://pypi.org/project/py/ .. _`oejskit`: http://codespeak.net/py/dist/test/plugin/oejskit.html diff --git a/doc/announce/release-1.4.0.txt b/doc/announce/release-1.4.0.txt index 6f9a7714..1c9fa756 100644 --- a/doc/announce/release-1.4.0.txt +++ b/doc/announce/release-1.4.0.txt @@ -22,7 +22,7 @@ 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 +the pycmd distribution, see https://pypi.org/project/pycmd/ This makes "py-1.4.0" a simple library which does not install any command line utilities anymore. diff --git a/doc/announce/release-1.4.1.txt b/doc/announce/release-1.4.1.txt index a5aa76b1..6ed72aa4 100644 --- a/doc/announce/release-1.4.1.txt +++ b/doc/announce/release-1.4.1.txt @@ -23,7 +23,7 @@ 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 +the pycmd distribution, see https://pypi.org/project/pycmd/ Changes between 1.4.0 and 1.4.1 diff --git a/doc/changelog.txt b/doc/changelog.txt index 237daca3..0c9d0928 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -1,3 +1,3 @@ .. _`changelog`: -.. include:: ../CHANGELOG +.. include:: ../CHANGELOG.rst diff --git a/doc/example/genhtml.py b/doc/example/genhtml.py index b5c8f525..7a6d4934 100644 --- a/doc/example/genhtml.py +++ b/doc/example/genhtml.py @@ -8,6 +8,6 @@ html.body( [html.p(p) for p in paras])) -print unicode(doc).encode('latin1') +print(unicode(doc).encode('latin1')) diff --git a/doc/example/genhtmlcss.py b/doc/example/genhtmlcss.py index 3e6d0af5..facca77b 100644 --- a/doc/example/genhtmlcss.py +++ b/doc/example/genhtmlcss.py @@ -20,4 +20,4 @@ class p(html.p): ) ) -print doc.unicode(indent=2) +print(doc.unicode(indent=2)) diff --git a/doc/example/genxml.py b/doc/example/genxml.py index 5f754e88..444a4ca5 100644 --- a/doc/example/genxml.py +++ b/doc/example/genxml.py @@ -12,6 +12,6 @@ class ns(py.xml.Namespace): ns.title("Java for Python programmers"),), publisher="N.N", ) -print doc.unicode(indent=2).encode('utf8') +print(doc.unicode(indent=2).encode('utf8')) diff --git a/doc/faq.txt b/doc/faq.txt index 52cb4b3f..6d374e1d 100644 --- a/doc/faq.txt +++ b/doc/faq.txt @@ -41,9 +41,9 @@ 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/ +.. _nose: https://nose.readthedocs.io/ .. _features: test/features.html -.. _apipkg: http://pypi.python.org/pypi/apipkg +.. _apipkg: https://pypi.org/project/apipkg/ What's this "magic" with py.test? @@ -112,7 +112,7 @@ 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 +.. _`Convention over Configuration`: https://en.wikipedia.org/wiki/Convention_over_configuration Can I yield multiple values from a factory function? ----------------------------------------------------- @@ -134,7 +134,7 @@ 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/ +.. _`parametrization scheme of your choice`: https://holgerkrekel.net/2009/05/13/parametrizing-python-tests-generalized/ py.test interaction with other packages @@ -167,6 +167,4 @@ script with this content and invoke that with the python version:: .. _`directly use a checkout`: install.html#directly-use-a-checkout -.. _`install distribute`: http://pypi.python.org/pypi/distribute#installation-instructions - - +.. _`install distribute`: https://pypi.org/project/distribute/ diff --git a/doc/install.txt b/doc/install.txt index fb4056d1..5b662e0d 100644 --- a/doc/install.txt +++ b/doc/install.txt @@ -1,13 +1,13 @@ .. _`py`: -.. _`index page`: http://pypi.python.org/pypi/py/ +.. _`index page`: https://pypi.org/project/py/ installation info in a nutshell =================================================== **PyPI name**: py_ -**Pythons**: CPython 2.7, 3.4, 3.5, 3.6, PyPy-5.4 +**Pythons**: CPython 2.7, 3.5, 3.6, 3.7, PyPy-5.4 **Operating systems**: Linux, Windows, OSX, Unix @@ -15,7 +15,7 @@ installation info in a nutshell **Installers**: ``easy_install`` and ``pip`` -**hg repository**: https://bitbucket.org/hpk42/py +**Code repository**: https://github.com/pytest-dev/py easy install or pip ``py`` ----------------------------- @@ -39,16 +39,16 @@ Working from version control or a tarball ----------------------------------------------- To follow development or start experiments, checkout the -complete code and documentation source with mercurial_:: +complete code and documentation source:: - hg clone https://bitbucket.org/hpk42/py + git clone https://github.com/pytest-dev/py -Development takes place on the 'trunk' branch. +Development takes place on the 'master' branch. You can also go to the python package index and download and unpack a TAR file:: - http://pypi.python.org/pypi/py/ + https://pypi.org/project/py/ activating a checkout with setuptools -------------------------------------------- @@ -63,7 +63,7 @@ in order to work inline with the tools and the lib of your checkout. .. _`directly use a checkout`: -.. _`setuptools`: http://pypi.python.org/pypi/setuptools +.. _`setuptools`: https://pypi.org/project/setuptools/ Mailing list and issue tracker @@ -73,10 +73,10 @@ Mailing list and issue tracker - #pylib on irc.freenode.net IRC channel for random questions. -- `bitbucket issue tracker`_ use this bitbucket issue tracker to report +- `issue tracker`_ use the issue tracker to report bugs or request features. -.. _`bitbucket issue tracker`: http://bitbucket.org/hpk42/py/issues/ +.. _`issue tracker`: https://github.com/pytest-dev/py/issues .. _codespeak: http://codespeak.net/ .. _`py-dev`: diff --git a/doc/links.inc b/doc/links.inc index 9bcfe5cf..b61d01c6 100644 --- a/doc/links.inc +++ b/doc/links.inc @@ -1,16 +1,15 @@ .. _`skipping plugin`: plugin/skipping.html .. _`funcargs mechanism`: funcargs.html -.. _`doctest.py`: http://docs.python.org/library/doctest.html +.. _`doctest.py`: https://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 +.. _nose: https://nose.readthedocs.io/ +.. _pytest: https://pypi.org/project/pytest/ +.. _`setuptools`: https://pypi.org/project/setuptools/ +.. _`distribute`: https://pypi.org/project/distribute/ +.. _`pip`: https://pypi.org/project/pip/ +.. _`virtualenv`: https://pypi.org/project/virtualenv/ .. _hudson: http://hudson-ci.org/ diff --git a/doc/path.txt b/doc/path.txt index c9061790..8f506d49 100644 --- a/doc/path.txt +++ b/doc/path.txt @@ -2,6 +2,12 @@ py.path ======= + **Note**: The 'py' library is in "maintenance mode" and so is not + recommended for new projects. Please check out + `pathlib `_ or + `pathlib2 `_ for path + operations. + 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 diff --git a/doc/style.css b/doc/style.css index 1faf762c..95e3ef07 100644 --- a/doc/style.css +++ b/doc/style.css @@ -546,7 +546,7 @@ div.message { } strong.highlight { background-color: #FFBBBB; -/* as usual, NetScape fucks up with innocent CSS +/* as usual, NetScape breaks with innocent CSS border-color: #FFAAAA; border-style: solid; border-width: 1pt; diff --git a/py/__init__.py b/py/__init__.py index b5e0c163..b892ce1a 100644 --- a/py/__init__.py +++ b/py/__init__.py @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2014 """ -__version__ = '1.5.2' +from py._error import error try: from py._vendored_packages import apipkg @@ -19,14 +19,16 @@ lib_not_mangled_by_packagers = False vendor_prefix = '' -# so that py.error.* instances are picklable -import sys +try: + from ._version import version as __version__ +except ImportError: + # broken installation, we don't even try + __version__ = "unknown" + -apipkg.initpkg(__name__, attr={'_apipkg': apipkg}, exportdefs={ +apipkg.initpkg(__name__, attr={'_apipkg': apipkg, 'error': error}, 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 diff --git a/py/__init__.pyi b/py/__init__.pyi new file mode 100644 index 00000000..96859e31 --- /dev/null +++ b/py/__init__.pyi @@ -0,0 +1,20 @@ +from typing import Any + +# py allows to use e.g. py.path.local even without importing py.path. +# So import implicitly. +from . import error +from . import iniconfig +from . import path +from . import io +from . import xml + +__version__: str + +# Untyped modules below here. +std: Any +test: Any +process: Any +apipkg: Any +code: Any +builtin: Any +log: Any diff --git a/py/_builtin.py b/py/_builtin.py index 52ee9d79..ddc89fc7 100644 --- a/py/_builtin.py +++ b/py/_builtin.py @@ -1,120 +1,21 @@ import sys -try: - reversed = reversed -except NameError: - def reversed(sequence): - """reversed(sequence) -> reverse iterator over values of the sequence - - Return a reverse iterator - """ - if hasattr(sequence, '__reversed__'): - return sequence.__reversed__() - if not hasattr(sequence, '__getitem__'): - raise TypeError("argument to reversed() must be a sequence") - return reversed_iterator(sequence) - - class reversed_iterator(object): - - def __init__(self, seq): - self.seq = seq - self.remaining = len(seq) - - def __iter__(self): - return self - - def next(self): - i = self.remaining - if i > 0: - i -= 1 - item = self.seq[i] - self.remaining = i - return item - raise StopIteration - - def __length_hint__(self): - return self.remaining - -try: - any = any -except NameError: - def any(iterable): - for x in iterable: - if x: - return True - return False - -try: - all = all -except NameError: - def all(iterable): - for x in iterable: - if not x: - return False - return True - -try: - sorted = sorted -except NameError: - builtin_cmp = cmp # need to use cmp as keyword arg - - def sorted(iterable, cmp=None, key=None, reverse=0): - use_cmp = None - if key is not None: - if cmp is None: - def use_cmp(x, y): - return builtin_cmp(x[0], y[0]) - else: - def use_cmp(x, y): - return cmp(x[0], y[0]) - l = [(key(element), element) for element in iterable] - else: - if cmp is not None: - use_cmp = cmp - l = list(iterable) - if use_cmp is not None: - l.sort(use_cmp) - else: - l.sort() - if reverse: - l.reverse() - if key is not None: - return [element for (_, element) in l] - return l - -try: - set, frozenset = set, frozenset -except NameError: - from sets import set, frozenset - -# pass through -enumerate = enumerate - -try: - BaseException = BaseException -except NameError: - BaseException = Exception - -try: - GeneratorExit = GeneratorExit -except NameError: - class GeneratorExit(Exception): - """ This exception is never raised, it is there to make it possible to - write code compatible with CPython 2.5 even in lower CPython - versions.""" - pass - GeneratorExit.__module__ = 'exceptions' +# Passthrough for builtins supported with py27. +BaseException = BaseException +GeneratorExit = GeneratorExit _sysex = (KeyboardInterrupt, SystemExit, MemoryError, GeneratorExit) +all = all +any = any +callable = callable +enumerate = enumerate +reversed = reversed +set, frozenset = set, frozenset +sorted = sorted -try: - callable = callable -except NameError: - def callable(obj): - return hasattr(obj, "__call__") if sys.version_info >= (3, 0): - exec ("print_ = print ; exec_=exec") + exec("print_ = print ; exec_=exec") import builtins # some backward compatibility helpers @@ -131,13 +32,13 @@ def _totext(obj, encoding=None, errors=None): 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) diff --git a/py/_io/capture.py b/py/_io/capture.py index bc157ed9..cacf2fa7 100644 --- a/py/_io/capture.py +++ b/py/_io/capture.py @@ -13,7 +13,7 @@ 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) + return StringIO.write(self, data) else: TextIO = StringIO @@ -24,7 +24,7 @@ class BytesIO(StringIO): def write(self, data): if isinstance(data, unicode): raise TypeError("not a byte value: %r" %(data,)) - StringIO.write(self, data) + return StringIO.write(self, data) patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'} @@ -266,7 +266,7 @@ def readouterr(self): err = self._readsnapshot(self.err.tmpfile) else: err = "" - return [out, err] + return out, err def _readsnapshot(self, f): f.seek(0) diff --git a/py/_io/terminalwriter.py b/py/_io/terminalwriter.py index 74d31259..be559867 100644 --- a/py/_io/terminalwriter.py +++ b/py/_io/terminalwriter.py @@ -5,9 +5,10 @@ """ -import sys, os +import sys, os, unicodedata import py py3k = sys.version_info[0] >= 3 +py33 = sys.version_info >= (3, 3) from py.builtin import text, bytes win32_and_ctypes = False @@ -24,10 +25,15 @@ def _getdimensions(): - import termios,fcntl,struct - call = fcntl.ioctl(1,termios.TIOCGWINSZ,"\000"*8) - height,width = struct.unpack( "hhhh", call ) [:2] - return height, width + if py33: + import shutil + size = shutil.get_terminal_size() + return size.lines, size.columns + else: + 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(): @@ -53,6 +59,21 @@ def get_terminal_width(): terminal_width = get_terminal_width() +char_width = { + 'A': 1, # "Ambiguous" + 'F': 2, # Fullwidth + 'H': 1, # Halfwidth + 'N': 1, # Neutral + 'Na': 1, # Narrow + 'W': 2, # Wide +} + + +def get_line_width(text): + text = unicodedata.normalize('NFC', text) + return sum(char_width.get(unicodedata.east_asian_width(c), 1) for c in text) + + # XXX unify with _escaped func below def ansi_print(text, esc, file=None, newline=True, flush=False): if file is None: @@ -140,6 +161,7 @@ def __init__(self, file=None, stringio=False, encoding=None): self.hasmarkup = should_do_markup(file) self._lastlen = 0 self._chars_on_current_line = 0 + self._width_of_current_line = 0 @property def fullwidth(self): @@ -164,6 +186,16 @@ def chars_on_current_line(self): """ return self._chars_on_current_line + @property + def width_of_current_line(self): + """Return an estimate of the width so far in the current line. + + .. versionadded:: 1.6.0 + + :rtype: int + """ + return self._width_of_current_line + def _escaped(self, text, esc): if esc and self.hasmarkup: text = (''.join(['\x1b[%sm' % cod for cod in esc]) + @@ -195,7 +227,7 @@ def sep(self, sepchar, title=None, fullwidth=None, **kw): # 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)) + N = max((fullwidth - len(title) - 2) // (2*len(sepchar)), 1) fill = sepchar * N line = "%s %s %s" % (fill, title, fill) else: @@ -223,12 +255,17 @@ def write(self, msg, **kw): 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]) + def _update_chars_on_current_line(self, text_or_bytes): + newline = b'\n' if isinstance(text_or_bytes, bytes) else '\n' + current_line = text_or_bytes.rsplit(newline, 1)[-1] + if isinstance(current_line, bytes): + current_line = current_line.decode('utf-8', errors='replace') + if newline in text_or_bytes: + self._chars_on_current_line = len(current_line) + self._width_of_current_line = get_line_width(current_line) else: - self._chars_on_current_line += len(fields[-1]) + self._chars_on_current_line += len(current_line) + self._width_of_current_line += get_line_width(current_line) def line(self, s='', **kw): self.write(s, **kw) diff --git a/py/_path/common.py b/py/_path/common.py index 2d490b56..2364e5fe 100644 --- a/py/_path/common.py +++ b/py/_path/common.py @@ -10,6 +10,12 @@ # Moved from local.py. iswin32 = sys.platform == "win32" or (getattr(os, '_name', False) == 'nt') +try: + # FileNotFoundError might happen in py34, and is not available with py27. + import_errors = (ImportError, FileNotFoundError) +except NameError: + import_errors = (ImportError,) + try: from os import fspath except ImportError: @@ -35,7 +41,7 @@ def fspath(path): raise try: import pathlib - except ImportError: + except import_errors: pass else: if isinstance(path, pathlib.PurePath): diff --git a/py/_path/local.py b/py/_path/local.py index c550fa2f..1385a039 100644 --- a/py/_path/local.py +++ b/py/_path/local.py @@ -4,13 +4,13 @@ from __future__ import with_statement from contextlib import contextmanager -import sys, os, re, atexit, io, uuid +import sys, os, 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 +from os.path import abspath, normpath, isabs, exists, isdir, isfile, islink, dirname if sys.version_info > (3,0): def map_as_list(func, iter): @@ -18,6 +18,11 @@ def map_as_list(func, iter): else: map_as_list = map +ALLOW_IMPORTLIB_MODE = sys.version_info > (3,5) +if ALLOW_IMPORTLIB_MODE: + import importlib + + class Stat(object): def __getattr__(self, name): return getattr(self._osstatresult, "st_" + name) @@ -158,7 +163,10 @@ def __init__(self, path=None, expanduser=False): self.strpath = abspath(path) def __hash__(self): - return hash(self.strpath) + s = self.strpath + if iswin32: + s = s.lower() + return hash(s) def __eq__(self, other): s1 = fspath(self) @@ -191,8 +199,8 @@ def samefile(self, other): other = abspath(other) if self == other: return True - if iswin32: - return False # there is no samefile + if not hasattr(os.path, "samefile"): + return False return py.error.checked_call( os.path.samefile, self.strpath, other) @@ -574,14 +582,17 @@ def chdir(self): @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. + """ + Return a context manager, which changes to the path's dir during the + managed "with" context. + On __enter__ it returns the old dir, which might be ``None``. """ old = self.chdir() try: yield old finally: - old.chdir() + if old is not None: + old.chdir() def realpath(self): """ return a new path which contains no symbolic links.""" @@ -647,10 +658,35 @@ def pyimport(self, modname=None, ensuresyspath=True): 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. + + Special value of ensuresyspath=="importlib" is intended + purely for using in pytest, it is capable only of importing + separate .py files outside packages, e.g. for test suite + without any __init__.py file. It effectively allows having + same-named test modules in different places and offers + mild opt-in via this option. Note that it works only in + recent versions of python. """ if not self.check(): raise py.error.ENOENT(self) + if ensuresyspath == 'importlib': + if modname is None: + modname = self.purebasename + if not ALLOW_IMPORTLIB_MODE: + raise ImportError( + "Can't use importlib due to old version of Python") + spec = importlib.util.spec_from_file_location( + modname, str(self)) + if spec is None: + raise ImportError( + "Can't find module %s at location %s" % + (modname, str(self)) + ) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod + pkgpath = None if modname is None: pkgpath = self.pypkgpath() @@ -669,7 +705,7 @@ def pyimport(self, modname=None, ensuresyspath=True): 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 + # be in a namespace package ... too icky to check modfile = mod.__file__ if modfile[-4:] in ('.pyc', '.pyo'): modfile = modfile[:-1] @@ -683,7 +719,9 @@ def pyimport(self, modname=None, ensuresyspath=True): except py.error.ENOENT: issame = False if not issame: - raise self.ImportMismatchError(modname, modfile, self) + ignore = os.getenv('PY_IGNORE_IMPORTMISMATCH') + if ignore != '1': + raise self.ImportMismatchError(modname, modfile, self) return mod else: try: @@ -800,7 +838,7 @@ def mkdtemp(cls, rootdir=None): 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 + 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) @@ -810,10 +848,10 @@ def make_numbered_dir(cls, prefix='session-', rootdir=None, keep=3, if rootdir is None: rootdir = cls.get_temproot() - nprefix = normcase(prefix) + nprefix = prefix.lower() def parse_num(path): """ parse the number out of a path (if it matches the prefix) """ - nbasename = normcase(path.basename) + nbasename = path.basename.lower() if nbasename.startswith(nprefix): try: return int(nbasename[len(nprefix):]) diff --git a/py/_path/svnwc.py b/py/_path/svnwc.py index 3138dd85..b5b9d8d5 100644 --- a/py/_path/svnwc.py +++ b/py/_path/svnwc.py @@ -396,7 +396,7 @@ def makecmdoptions(self): def __str__(self): return "" %(self.username,) -rex_blame = re.compile(r'\s*(\d+)\s*(\S+) (.*)') +rex_blame = re.compile(r'\s*(\d+)\s+(\S+) (.*)') class SvnWCCommandPath(common.PathBase): """ path implementation offering access/modification to svn working copies. diff --git a/py/_std.py b/py/_std.py index 74d43672..66adb7b0 100644 --- a/py/_std.py +++ b/py/_std.py @@ -15,8 +15,9 @@ def __init__(self): self.__dict__ = sys.modules def __getattr__(self, name): - warnings.warn("py.std is deprecated, plase import %s directly" % name, - category=PyStdIsDeprecatedWarning) + warnings.warn("py.std is deprecated, please import %s directly" % name, + category=PyStdIsDeprecatedWarning, + stacklevel=2) try: m = __import__(name) except ImportError: diff --git a/py/_vendored_packages/apipkg-1.4.dist-info/DESCRIPTION.rst b/py/_vendored_packages/apipkg-1.4.dist-info/DESCRIPTION.rst deleted file mode 100644 index 54822200..00000000 --- a/py/_vendored_packages/apipkg-1.4.dist-info/DESCRIPTION.rst +++ /dev/null @@ -1,87 +0,0 @@ -Welcome to apipkg! ------------------------- - -With apipkg you can control the exported namespace of a -python package and greatly reduce the number of imports for your users. -It is a `small pure python module`_ that works on virtually all Python -versions, including CPython2.3 to Python3.1, Jython and PyPy. It co-operates -well with Python's ``help()`` system, custom importers (PEP302) and common -command line completion tools. - -Usage is very simple: you can require 'apipkg' as a dependency or you -can copy paste the <200 Lines of code into your project. - - -Tutorial example -------------------- - -Here is a simple ``mypkg`` package that specifies one namespace -and exports two objects imported from different modules:: - - # mypkg/__init__.py - import apipkg - apipkg.initpkg(__name__, { - 'path': { - 'Class1': "_mypkg.somemodule:Class1", - 'clsattr': "_mypkg.othermodule:Class2.attr", - } - } - -The package is initialized with a dictionary as namespace. - -You need to create a ``_mypkg`` package with a ``somemodule.py`` -and ``othermodule.py`` containing the respective classes. -The ``_mypkg`` is not special - it's a completely -regular python package. - -Namespace dictionaries contain ``name: value`` mappings -where the value may be another namespace dictionary or -a string specifying an import location. On accessing -an namespace attribute an import will be performed:: - - >>> import mypkg - >>> mypkg.path - - >>> mypkg.path.Class1 # '_mypkg.somemodule' gets imported now - - >>> mypkg.path.clsattr # '_mypkg.othermodule' gets imported now - 4 # the value of _mypkg.othermodule.Class2.attr - -The ``mypkg.path`` namespace and its two entries are -loaded when they are accessed. This means: - -* lazy loading - only what is actually needed is ever loaded - -* only the root "mypkg" ever needs to be imported to get - access to the complete functionality. - -* the underlying modules are also accessible, for example:: - - from mypkg.sub import Class1 - - -Including apipkg in your package --------------------------------------- - -If you don't want to add an ``apipkg`` dependency to your package you -can copy the `apipkg.py`_ file somewhere to your own package, -for example ``_mypkg/apipkg.py`` in the above example. You -then import the ``initpkg`` function from that new place and -are good to go. - -.. _`small pure python module`: -.. _`apipkg.py`: http://bitbucket.org/hpk42/apipkg/src/tip/apipkg.py - -Feedback? ------------------------ - -If you have questions you are welcome to - -* join the #pylib channel on irc.freenode.net -* subscribe to the http://codespeak.net/mailman/listinfo/py-dev list. -* create an issue on http://bitbucket.org/hpk42/apipkg/issues - -have fun, -holger krekel - - diff --git a/py/_vendored_packages/apipkg-1.4.dist-info/RECORD b/py/_vendored_packages/apipkg-1.4.dist-info/RECORD deleted file mode 100644 index dc72959d..00000000 --- a/py/_vendored_packages/apipkg-1.4.dist-info/RECORD +++ /dev/null @@ -1,9 +0,0 @@ -apipkg.py,sha256=BNnv_qvq8zZvku-uudoqgp3XTNFbwsNUmtzOKrVI7X0,6420 -apipkg-1.4.dist-info/top_level.txt,sha256=3TGS6nmN7kjxhUK4LpPCB3QkQI34QYGrT0ZQGWajoZ8,7 -apipkg-1.4.dist-info/METADATA,sha256=Fk_8BrHyXE--kvB3_ZBKgwvPaKusAZUjchH-kpB63Hs,3491 -apipkg-1.4.dist-info/DESCRIPTION.rst,sha256=RkMQqk5ljhGy0DiZkR_nbpjqvwCIhuIEHsyvkn3O96k,2803 -apipkg-1.4.dist-info/metadata.json,sha256=GdshYrA_7gAII3E3EQMH-31BHzU-klTZ6bPQzlDmuy4,779 -apipkg-1.4.dist-info/WHEEL,sha256=AvR0WeTpDaxT645bl5FQxUK6NPsTls2ttpcGJg3j1Xg,110 -apipkg-1.4.dist-info/RECORD,, -apipkg-1.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -__pycache__/apipkg.cpython-35.pyc,, diff --git a/py/_vendored_packages/apipkg-1.4.dist-info/metadata.json b/py/_vendored_packages/apipkg-1.4.dist-info/metadata.json deleted file mode 100644 index 05609b99..00000000 --- a/py/_vendored_packages/apipkg-1.4.dist-info/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"license": "MIT License", "name": "apipkg", "metadata_version": "2.0", "generator": "bdist_wheel (0.24.0)", "summary": "apipkg: namespace control and lazy-import mechanism", "platform": "unix", "version": "1.4", "extensions": {"python.details": {"project_urls": {"Home": "http://bitbucket.org/hpk42/apipkg"}, "document_names": {"description": "DESCRIPTION.rst"}, "contacts": [{"role": "author", "email": "holger at merlinux.eu", "name": "holger krekel"}]}}, "classifiers": ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", "Operating System :: Microsoft :: Windows", "Operating System :: MacOS :: MacOS X", "Topic :: Software Development :: Libraries", "Programming Language :: Python"]} \ No newline at end of file diff --git a/py/_vendored_packages/apipkg-1.4.dist-info/INSTALLER b/py/_vendored_packages/apipkg-1.5.dist-info/INSTALLER similarity index 100% rename from py/_vendored_packages/apipkg-1.4.dist-info/INSTALLER rename to py/_vendored_packages/apipkg-1.5.dist-info/INSTALLER diff --git a/py/_vendored_packages/apipkg-1.4.dist-info/METADATA b/py/_vendored_packages/apipkg-1.5.dist-info/METADATA similarity index 69% rename from py/_vendored_packages/apipkg-1.4.dist-info/METADATA rename to py/_vendored_packages/apipkg-1.5.dist-info/METADATA index eb7e60ac..ac14b4bb 100644 --- a/py/_vendored_packages/apipkg-1.4.dist-info/METADATA +++ b/py/_vendored_packages/apipkg-1.5.dist-info/METADATA @@ -1,10 +1,11 @@ -Metadata-Version: 2.0 +Metadata-Version: 2.1 Name: apipkg -Version: 1.4 +Version: 1.5 Summary: apipkg: namespace control and lazy-import mechanism -Home-page: http://bitbucket.org/hpk42/apipkg +Home-page: https://github.com/pytest-dev/apipkg Author: holger krekel -Author-email: holger at merlinux.eu +Maintainer: Ronny Pfannschmidt +Maintainer-email: opensource@ronnypfannschmidt.de License: MIT License Platform: unix Platform: linux @@ -19,19 +20,25 @@ Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: MacOS :: MacOS X Classifier: Topic :: Software Development :: Libraries 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 +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* Welcome to apipkg! ------------------------ -With apipkg you can control the exported namespace of a -python package and greatly reduce the number of imports for your users. -It is a `small pure python module`_ that works on virtually all Python -versions, including CPython2.3 to Python3.1, Jython and PyPy. It co-operates -well with Python's ``help()`` system, custom importers (PEP302) and common -command line completion tools. +With apipkg you can control the exported namespace of a Python package and +greatly reduce the number of imports for your users. +It is a `small pure Python module`_ that works on CPython 2.7 and 3.4+, +Jython and PyPy. It cooperates well with Python's ``help()`` system, +custom importers (PEP302) and common command-line completion tools. Usage is very simple: you can require 'apipkg' as a dependency or you -can copy paste the <200 Lines of code into your project. +can copy paste the ~200 lines of code into your project. Tutorial example @@ -54,7 +61,7 @@ The package is initialized with a dictionary as namespace. You need to create a ``_mypkg`` package with a ``somemodule.py`` and ``othermodule.py`` containing the respective classes. The ``_mypkg`` is not special - it's a completely -regular python package. +regular Python package. Namespace dictionaries contain ``name: value`` mappings where the value may be another namespace dictionary or @@ -75,7 +82,7 @@ loaded when they are accessed. This means: * lazy loading - only what is actually needed is ever loaded * only the root "mypkg" ever needs to be imported to get - access to the complete functionality. + access to the complete functionality * the underlying modules are also accessible, for example:: @@ -91,8 +98,8 @@ for example ``_mypkg/apipkg.py`` in the above example. You then import the ``initpkg`` function from that new place and are good to go. -.. _`small pure python module`: -.. _`apipkg.py`: http://bitbucket.org/hpk42/apipkg/src/tip/apipkg.py +.. _`small pure Python module`: +.. _`apipkg.py`: https://github.com/pytest-dev/apipkg/blob/master/src/apipkg/__init__.py Feedback? ----------------------- @@ -100,8 +107,7 @@ Feedback? If you have questions you are welcome to * join the #pylib channel on irc.freenode.net -* subscribe to the http://codespeak.net/mailman/listinfo/py-dev list. -* create an issue on http://bitbucket.org/hpk42/apipkg/issues +* create an issue on https://github.com/pytest-dev/apipkg/issues have fun, holger krekel diff --git a/py/_vendored_packages/apipkg-1.5.dist-info/RECORD b/py/_vendored_packages/apipkg-1.5.dist-info/RECORD new file mode 100644 index 00000000..8611704e --- /dev/null +++ b/py/_vendored_packages/apipkg-1.5.dist-info/RECORD @@ -0,0 +1,10 @@ +../../../../home/ran/.cache/pycache/tmp/pip-target-oxds71ih/lib/python/apipkg/__init__.cpython-39.pyc,, +../../../../home/ran/.cache/pycache/tmp/pip-target-oxds71ih/lib/python/apipkg/version.cpython-39.pyc,, +apipkg-1.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +apipkg-1.5.dist-info/METADATA,sha256=tIG1DSBzSeqmSRpOKHSEBmT1eOPdK8xK01xAIADuks4,3800 +apipkg-1.5.dist-info/RECORD,, +apipkg-1.5.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +apipkg-1.5.dist-info/WHEEL,sha256=gduuPyBvFJQSQ0zdyxF7k0zynDXbIbvg5ZBHoXum5uk,110 +apipkg-1.5.dist-info/top_level.txt,sha256=3TGS6nmN7kjxhUK4LpPCB3QkQI34QYGrT0ZQGWajoZ8,7 +apipkg/__init__.py,sha256=VogR4mDwYmeOdJnjGi-RoMB1qJnD6_puDYj_nRolzhM,6707 +apipkg/version.py,sha256=YN6DnKyEPqjDAauJuwJRG9vlKbWVLd9gAbH7mkQXXNo,114 diff --git a/py/_vendored_packages/apipkg-1.5.dist-info/REQUESTED b/py/_vendored_packages/apipkg-1.5.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/py/_vendored_packages/apipkg-1.4.dist-info/WHEEL b/py/_vendored_packages/apipkg-1.5.dist-info/WHEEL similarity index 70% rename from py/_vendored_packages/apipkg-1.4.dist-info/WHEEL rename to py/_vendored_packages/apipkg-1.5.dist-info/WHEEL index 9dff69d8..1316c41d 100644 --- a/py/_vendored_packages/apipkg-1.4.dist-info/WHEEL +++ b/py/_vendored_packages/apipkg-1.5.dist-info/WHEEL @@ -1,5 +1,5 @@ Wheel-Version: 1.0 -Generator: bdist_wheel (0.24.0) +Generator: bdist_wheel (0.31.1) Root-Is-Purelib: true Tag: py2-none-any Tag: py3-none-any diff --git a/py/_vendored_packages/apipkg-1.4.dist-info/top_level.txt b/py/_vendored_packages/apipkg-1.5.dist-info/top_level.txt similarity index 100% rename from py/_vendored_packages/apipkg-1.4.dist-info/top_level.txt rename to py/_vendored_packages/apipkg-1.5.dist-info/top_level.txt diff --git a/py/_vendored_packages/apipkg.py b/py/_vendored_packages/apipkg/__init__.py similarity index 90% rename from py/_vendored_packages/apipkg.py rename to py/_vendored_packages/apipkg/__init__.py index 9d56e0bc..93180484 100644 --- a/py/_vendored_packages/apipkg.py +++ b/py/_vendored_packages/apipkg/__init__.py @@ -1,7 +1,7 @@ """ -apipkg: control the exported namespace of a python package. +apipkg: control the exported namespace of a Python package. -see http://pypi.python.org/pypi/apipkg +see https://pypi.python.org/pypi/apipkg (c) holger krekel, 2009 - MIT license """ @@ -9,8 +9,7 @@ import sys from types import ModuleType - -__version__ = '1.4' +from .version import version as __version__ def _py_abspath(path): @@ -37,8 +36,9 @@ def distribution_version(name): return dist.version -def initpkg(pkgname, exportdefs, attr=dict(), eager=False): +def initpkg(pkgname, exportdefs, attr=None, eager=False): """ initialize given package from the export definitions. """ + attr = attr or {} oldmod = sys.modules.get(pkgname) d = {} f = getattr(oldmod, '__file__', None) @@ -51,6 +51,8 @@ def initpkg(pkgname, exportdefs, attr=dict(), eager=False): d['__loader__'] = oldmod.__loader__ if hasattr(oldmod, '__path__'): d['__path__'] = [_py_abspath(p) for p in oldmod.__path__] + if hasattr(oldmod, '__package__'): + d['__package__'] = oldmod.__package__ if '__doc__' not in exportdefs and getattr(oldmod, '__doc__', None): d['__doc__'] = oldmod.__doc__ d.update(attr) @@ -60,12 +62,13 @@ def initpkg(pkgname, exportdefs, attr=dict(), eager=False): 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(): + for module in list(sys.modules.values()): if isinstance(module, ApiModule): module.__dict__ def importobj(modpath, attrname): + """imports a module, then resolves the attrname on it""" module = __import__(modpath, None, None, ['__doc__']) if not attrname: return module @@ -78,6 +81,7 @@ def importobj(modpath, attrname): class ApiModule(ModuleType): + """the magical lazy-loading module standing""" def __docget(self): try: return self.__doc @@ -121,13 +125,13 @@ def __init__(self, name, importspec, implprefix=None, attr=None): self.__map__[name] = (modpath, attrname) def __repr__(self): - l = [] + repr_list = [] if hasattr(self, '__version__'): - l.append("version=" + repr(self.__version__)) + repr_list.append("version=" + repr(self.__version__)) if hasattr(self, '__file__'): - l.append('from ' + repr(self.__file__)) - if l: - return '' % (self.__name__, " ".join(l)) + repr_list.append('from ' + repr(self.__file__)) + if repr_list: + return '' % (self.__name__, " ".join(repr_list)) return '' % (self.__name__,) def __makeattr(self, name): diff --git a/py/_vendored_packages/apipkg/version.py b/py/_vendored_packages/apipkg/version.py new file mode 100644 index 00000000..c25fc7c9 --- /dev/null +++ b/py/_vendored_packages/apipkg/version.py @@ -0,0 +1,4 @@ +# coding: utf-8 +# file generated by setuptools_scm +# don't change, don't track in version control +version = '1.5' diff --git a/py/_vendored_packages/iniconfig-1.0.0.dist-info/DESCRIPTION.rst b/py/_vendored_packages/iniconfig-1.0.0.dist-info/DESCRIPTION.rst deleted file mode 100644 index 6d59bc22..00000000 --- a/py/_vendored_packages/iniconfig-1.0.0.dist-info/DESCRIPTION.rst +++ /dev/null @@ -1,53 +0,0 @@ -iniconfig: brain-dead simple parsing of ini files -======================================================= - -iniconfig is a small and simple INI-file parser module -having a unique set of features: - -* tested against Python2.4 across to Python3.2, Jython, PyPy -* maintains order of sections and entries -* supports multi-line values with or without line-continuations -* supports "#" comments everywhere -* raises errors with proper line-numbers -* no bells and whistles like automatic substitutions -* iniconfig raises an Error if two sections have the same name. - -If you encounter issues or have feature wishes please report them to: - - http://github.org/RonnyPfannschmidt/iniconfig/issues - -Basic Example -=================================== - -If you have an ini file like this:: - - # content of example.ini - [section1] # comment - name1=value1 # comment - name1b=value1,value2 # comment - - [section2] - name2= - line1 - line2 - -then you can do:: - - >>> import iniconfig - >>> ini = iniconfig.IniConfig("example.ini") - >>> ini['section1']['name1'] # raises KeyError if not exists - 'value1' - >>> ini.get('section1', 'name1b', [], lambda x: x.split(",")) - ['value1', 'value2'] - >>> ini.get('section1', 'notexist', [], lambda x: x.split(",")) - [] - >>> [x.name for x in list(ini)] - ['section1', 'section2'] - >>> list(list(ini)[0].items()) - [('name1', 'value1'), ('name1b', 'value1,value2')] - >>> 'section1' in ini - True - >>> 'inexistendsection' in ini - False - - diff --git a/py/_vendored_packages/iniconfig-1.0.0.dist-info/RECORD b/py/_vendored_packages/iniconfig-1.0.0.dist-info/RECORD deleted file mode 100644 index ec2f5e17..00000000 --- a/py/_vendored_packages/iniconfig-1.0.0.dist-info/RECORD +++ /dev/null @@ -1,9 +0,0 @@ -iniconfig.py,sha256=-pBe5AF_6aAwo1CxJQ8i_zJq6ejc6IxHta7qk2tNJhY,5208 -iniconfig-1.0.0.dist-info/DESCRIPTION.rst,sha256=BDLMwWqfjpwZ5yqXRvz1x6bf8Dnt_pZhElekAwtL19o,1522 -iniconfig-1.0.0.dist-info/METADATA,sha256=bb2T8WUSDXXiUVxZ4WXhbffq6stikMTlB1jyrPbLfyU,2405 -iniconfig-1.0.0.dist-info/RECORD,, -iniconfig-1.0.0.dist-info/WHEEL,sha256=3XK1Z4AI42GuJXciCpiHMOkbehxRV8QDBW8IU41k3ZU,96 -iniconfig-1.0.0.dist-info/metadata.json,sha256=UYYwW0p815nU4qz8Iq1gGqIYaAcsCyGju3jXvTOyXSI,950 -iniconfig-1.0.0.dist-info/top_level.txt,sha256=7KfM0fugdlToj9UW7enKXk2HYALQD8qHiyKtjhSzgN8,10 -iniconfig-1.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -__pycache__/iniconfig.cpython-35.pyc,, diff --git a/py/_vendored_packages/iniconfig-1.0.0.dist-info/WHEEL b/py/_vendored_packages/iniconfig-1.0.0.dist-info/WHEEL deleted file mode 100644 index 15b96c99..00000000 --- a/py/_vendored_packages/iniconfig-1.0.0.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.30.0.a0) -Root-Is-Purelib: true -Tag: cp35-none-any - diff --git a/py/_vendored_packages/iniconfig-1.0.0.dist-info/metadata.json b/py/_vendored_packages/iniconfig-1.0.0.dist-info/metadata.json deleted file mode 100644 index 084daa6c..00000000 --- a/py/_vendored_packages/iniconfig-1.0.0.dist-info/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"classifiers": ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", "Operating System :: Microsoft :: Windows", "Operating System :: MacOS :: MacOS X", "Topic :: Software Development :: Libraries", "Topic :: Utilities", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3"], "extensions": {"python.details": {"contacts": [{"email": "opensource@ronnypfannschmidt.de, holger.krekel@gmail.com", "name": "Ronny Pfannschmidt, Holger Krekel", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://github.com/RonnyPfannschmidt/iniconfig"}}}, "generator": "bdist_wheel (0.30.0.a0)", "license": "MIT License", "metadata_version": "2.0", "name": "iniconfig", "platform": "unix", "summary": "iniconfig: brain-dead simple config-ini parsing", "version": "1.0.0"} \ No newline at end of file diff --git a/py/_vendored_packages/iniconfig-1.0.0.dist-info/INSTALLER b/py/_vendored_packages/iniconfig-1.1.1.dist-info/INSTALLER similarity index 100% rename from py/_vendored_packages/iniconfig-1.0.0.dist-info/INSTALLER rename to py/_vendored_packages/iniconfig-1.1.1.dist-info/INSTALLER diff --git a/py/_vendored_packages/iniconfig-1.1.1.dist-info/LICENSE b/py/_vendored_packages/iniconfig-1.1.1.dist-info/LICENSE new file mode 100644 index 00000000..31ecdfb1 --- /dev/null +++ b/py/_vendored_packages/iniconfig-1.1.1.dist-info/LICENSE @@ -0,0 +1,19 @@ + + 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. + diff --git a/py/_vendored_packages/iniconfig-1.0.0.dist-info/METADATA b/py/_vendored_packages/iniconfig-1.1.1.dist-info/METADATA similarity index 96% rename from py/_vendored_packages/iniconfig-1.0.0.dist-info/METADATA rename to py/_vendored_packages/iniconfig-1.1.1.dist-info/METADATA index 79ea62dc..c078a753 100644 --- a/py/_vendored_packages/iniconfig-1.0.0.dist-info/METADATA +++ b/py/_vendored_packages/iniconfig-1.1.1.dist-info/METADATA @@ -1,6 +1,6 @@ -Metadata-Version: 2.0 +Metadata-Version: 2.1 Name: iniconfig -Version: 1.0.0 +Version: 1.1.1 Summary: iniconfig: brain-dead simple config-ini parsing Home-page: http://github.com/RonnyPfannschmidt/iniconfig Author: Ronny Pfannschmidt, Holger Krekel @@ -39,7 +39,7 @@ having a unique set of features: If you encounter issues or have feature wishes please report them to: - http://github.org/RonnyPfannschmidt/iniconfig/issues + http://github.com/RonnyPfannschmidt/iniconfig/issues Basic Example =================================== diff --git a/py/_vendored_packages/iniconfig-1.1.1.dist-info/RECORD b/py/_vendored_packages/iniconfig-1.1.1.dist-info/RECORD new file mode 100644 index 00000000..73a6fe1e --- /dev/null +++ b/py/_vendored_packages/iniconfig-1.1.1.dist-info/RECORD @@ -0,0 +1,11 @@ +../../../../home/ran/.cache/pycache/tmp/pip-target-oxds71ih/lib/python/iniconfig/__init__.cpython-39.pyc,, +iniconfig-1.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +iniconfig-1.1.1.dist-info/LICENSE,sha256=KvaAw570k_uCgwNW0dPfGstaBgM8ui3sehniHKp3qGY,1061 +iniconfig-1.1.1.dist-info/METADATA,sha256=_4-oFKpRXuZv5rzepScpXRwhq6DzqsgbnA5ZpgMUMcs,2405 +iniconfig-1.1.1.dist-info/RECORD,, +iniconfig-1.1.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +iniconfig-1.1.1.dist-info/WHEEL,sha256=ADKeyaGyKF5DwBNE0sRE5pvW-bSkFMJfBuhzZ3rceP4,110 +iniconfig-1.1.1.dist-info/top_level.txt,sha256=7KfM0fugdlToj9UW7enKXk2HYALQD8qHiyKtjhSzgN8,10 +iniconfig/__init__.py,sha256=-pBe5AF_6aAwo1CxJQ8i_zJq6ejc6IxHta7qk2tNJhY,5208 +iniconfig/__init__.pyi,sha256=-4KOctzq28ohRmTZsqlH6aylyFqsNKxYqtk1dteypi4,1205 +iniconfig/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/py/_vendored_packages/iniconfig-1.1.1.dist-info/REQUESTED b/py/_vendored_packages/iniconfig-1.1.1.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/py/_vendored_packages/iniconfig-1.1.1.dist-info/WHEEL b/py/_vendored_packages/iniconfig-1.1.1.dist-info/WHEEL new file mode 100644 index 00000000..6d38aa06 --- /dev/null +++ b/py/_vendored_packages/iniconfig-1.1.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.35.1) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/py/_vendored_packages/iniconfig-1.0.0.dist-info/top_level.txt b/py/_vendored_packages/iniconfig-1.1.1.dist-info/top_level.txt similarity index 100% rename from py/_vendored_packages/iniconfig-1.0.0.dist-info/top_level.txt rename to py/_vendored_packages/iniconfig-1.1.1.dist-info/top_level.txt diff --git a/py/_vendored_packages/iniconfig.py b/py/_vendored_packages/iniconfig/__init__.py similarity index 100% rename from py/_vendored_packages/iniconfig.py rename to py/_vendored_packages/iniconfig/__init__.py diff --git a/py/_vendored_packages/iniconfig/__init__.pyi b/py/_vendored_packages/iniconfig/__init__.pyi new file mode 100644 index 00000000..b6284bec --- /dev/null +++ b/py/_vendored_packages/iniconfig/__init__.pyi @@ -0,0 +1,31 @@ +from typing import Callable, Iterator, Mapping, Optional, Tuple, TypeVar, Union +from typing_extensions import Final + +_D = TypeVar('_D') +_T = TypeVar('_T') + +class ParseError(Exception): + # Private __init__. + path: Final[str] + lineno: Final[int] + msg: Final[str] + +class SectionWrapper: + # Private __init__. + config: Final[IniConfig] + name: Final[str] + def __getitem__(self, key: str) -> str: ... + def __iter__(self) -> Iterator[str]: ... + def get(self, key: str, default: _D = ..., convert: Callable[[str], _T] = ...) -> Union[_T, _D]: ... + def items(self) -> Iterator[Tuple[str, str]]: ... + def lineof(self, name: str) -> Optional[int]: ... + +class IniConfig: + path: Final[str] + sections: Final[Mapping[str, Mapping[str, str]]] + def __init__(self, path: str, data: Optional[str] = None): ... + def __contains__(self, arg: str) -> bool: ... + def __getitem__(self, name: str) -> SectionWrapper: ... + def __iter__(self) -> Iterator[SectionWrapper]: ... + def get(self, section: str, name: str, default: _D = ..., convert: Callable[[str], _T] = ...) -> Union[_T, _D]: ... + def lineof(self, section: str, name: Optional[str] = ...) -> Optional[int]: ... diff --git a/py/_vendored_packages/iniconfig/py.typed b/py/_vendored_packages/iniconfig/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/py/error.pyi b/py/error.pyi new file mode 100644 index 00000000..034eba60 --- /dev/null +++ b/py/error.pyi @@ -0,0 +1,129 @@ +from typing import Any, Callable, TypeVar + +_T = TypeVar('_T') + +def checked_call(func: Callable[..., _T], *args: Any, **kwargs: Any) -> _T: ... +class Error(EnvironmentError): ... +class EPERM(Error): ... +class ENOENT(Error): ... +class ESRCH(Error): ... +class EINTR(Error): ... +class EIO(Error): ... +class ENXIO(Error): ... +class E2BIG(Error): ... +class ENOEXEC(Error): ... +class EBADF(Error): ... +class ECHILD(Error): ... +class EAGAIN(Error): ... +class ENOMEM(Error): ... +class EACCES(Error): ... +class EFAULT(Error): ... +class ENOTBLK(Error): ... +class EBUSY(Error): ... +class EEXIST(Error): ... +class EXDEV(Error): ... +class ENODEV(Error): ... +class ENOTDIR(Error): ... +class EISDIR(Error): ... +class EINVAL(Error): ... +class ENFILE(Error): ... +class EMFILE(Error): ... +class ENOTTY(Error): ... +class ETXTBSY(Error): ... +class EFBIG(Error): ... +class ENOSPC(Error): ... +class ESPIPE(Error): ... +class EROFS(Error): ... +class EMLINK(Error): ... +class EPIPE(Error): ... +class EDOM(Error): ... +class ERANGE(Error): ... +class EDEADLCK(Error): ... +class ENAMETOOLONG(Error): ... +class ENOLCK(Error): ... +class ENOSYS(Error): ... +class ENOTEMPTY(Error): ... +class ELOOP(Error): ... +class EWOULDBLOCK(Error): ... +class ENOMSG(Error): ... +class EIDRM(Error): ... +class ECHRNG(Error): ... +class EL2NSYNC(Error): ... +class EL3HLT(Error): ... +class EL3RST(Error): ... +class ELNRNG(Error): ... +class EUNATCH(Error): ... +class ENOCSI(Error): ... +class EL2HLT(Error): ... +class EBADE(Error): ... +class EBADR(Error): ... +class EXFULL(Error): ... +class ENOANO(Error): ... +class EBADRQC(Error): ... +class EBADSLT(Error): ... +class EDEADLOCK(Error): ... +class EBFONT(Error): ... +class ENOSTR(Error): ... +class ENODATA(Error): ... +class ETIME(Error): ... +class ENOSR(Error): ... +class ENONET(Error): ... +class ENOPKG(Error): ... +class EREMOTE(Error): ... +class ENOLINK(Error): ... +class EADV(Error): ... +class ESRMNT(Error): ... +class ECOMM(Error): ... +class EPROTO(Error): ... +class EMULTIHOP(Error): ... +class EDOTDOT(Error): ... +class EBADMSG(Error): ... +class EOVERFLOW(Error): ... +class ENOTUNIQ(Error): ... +class EBADFD(Error): ... +class EREMCHG(Error): ... +class ELIBACC(Error): ... +class ELIBBAD(Error): ... +class ELIBSCN(Error): ... +class ELIBMAX(Error): ... +class ELIBEXEC(Error): ... +class EILSEQ(Error): ... +class ERESTART(Error): ... +class ESTRPIPE(Error): ... +class EUSERS(Error): ... +class ENOTSOCK(Error): ... +class EDESTADDRREQ(Error): ... +class EMSGSIZE(Error): ... +class EPROTOTYPE(Error): ... +class ENOPROTOOPT(Error): ... +class EPROTONOSUPPORT(Error): ... +class ESOCKTNOSUPPORT(Error): ... +class ENOTSUP(Error): ... +class EOPNOTSUPP(Error): ... +class EPFNOSUPPORT(Error): ... +class EAFNOSUPPORT(Error): ... +class EADDRINUSE(Error): ... +class EADDRNOTAVAIL(Error): ... +class ENETDOWN(Error): ... +class ENETUNREACH(Error): ... +class ENETRESET(Error): ... +class ECONNABORTED(Error): ... +class ECONNRESET(Error): ... +class ENOBUFS(Error): ... +class EISCONN(Error): ... +class ENOTCONN(Error): ... +class ESHUTDOWN(Error): ... +class ETOOMANYREFS(Error): ... +class ETIMEDOUT(Error): ... +class ECONNREFUSED(Error): ... +class EHOSTDOWN(Error): ... +class EHOSTUNREACH(Error): ... +class EALREADY(Error): ... +class EINPROGRESS(Error): ... +class ESTALE(Error): ... +class EUCLEAN(Error): ... +class ENOTNAM(Error): ... +class ENAVAIL(Error): ... +class EISNAM(Error): ... +class EREMOTEIO(Error): ... +class EDQUOT(Error): ... diff --git a/py/iniconfig.pyi b/py/iniconfig.pyi new file mode 100644 index 00000000..b6284bec --- /dev/null +++ b/py/iniconfig.pyi @@ -0,0 +1,31 @@ +from typing import Callable, Iterator, Mapping, Optional, Tuple, TypeVar, Union +from typing_extensions import Final + +_D = TypeVar('_D') +_T = TypeVar('_T') + +class ParseError(Exception): + # Private __init__. + path: Final[str] + lineno: Final[int] + msg: Final[str] + +class SectionWrapper: + # Private __init__. + config: Final[IniConfig] + name: Final[str] + def __getitem__(self, key: str) -> str: ... + def __iter__(self) -> Iterator[str]: ... + def get(self, key: str, default: _D = ..., convert: Callable[[str], _T] = ...) -> Union[_T, _D]: ... + def items(self) -> Iterator[Tuple[str, str]]: ... + def lineof(self, name: str) -> Optional[int]: ... + +class IniConfig: + path: Final[str] + sections: Final[Mapping[str, Mapping[str, str]]] + def __init__(self, path: str, data: Optional[str] = None): ... + def __contains__(self, arg: str) -> bool: ... + def __getitem__(self, name: str) -> SectionWrapper: ... + def __iter__(self) -> Iterator[SectionWrapper]: ... + def get(self, section: str, name: str, default: _D = ..., convert: Callable[[str], _T] = ...) -> Union[_T, _D]: ... + def lineof(self, section: str, name: Optional[str] = ...) -> Optional[int]: ... diff --git a/py/io.pyi b/py/io.pyi new file mode 100644 index 00000000..d377e240 --- /dev/null +++ b/py/io.pyi @@ -0,0 +1,130 @@ +from io import StringIO as TextIO +from io import BytesIO as BytesIO +from typing import Any, AnyStr, Callable, Generic, IO, List, Optional, Text, Tuple, TypeVar, Union, overload +from typing_extensions import Final +import sys + +_T = TypeVar("_T") + +class FDCapture(Generic[AnyStr]): + def __init__(self, targetfd: int, tmpfile: Optional[IO[AnyStr]] = ..., now: bool = ..., patchsys: bool = ...) -> None: ... + def start(self) -> None: ... + def done(self) -> IO[AnyStr]: ... + def writeorg(self, data: AnyStr) -> None: ... + +class StdCaptureFD: + def __init__( + self, + out: Union[bool, IO[str]] = ..., + err: Union[bool, IO[str]] = ..., + mixed: bool = ..., + in_: bool = ..., + patchsys: bool = ..., + now: bool = ..., + ) -> None: ... + @classmethod + def call(cls, func: Callable[..., _T], *args: Any, **kwargs: Any) -> Tuple[_T, str, str]: ... + def reset(self) -> Tuple[str, str]: ... + def suspend(self) -> Tuple[str, str]: ... + def startall(self) -> None: ... + def resume(self) -> None: ... + def done(self, save: bool = ...) -> Tuple[IO[str], IO[str]]: ... + def readouterr(self) -> Tuple[str, str]: ... + +class StdCapture: + def __init__( + self, + out: Union[bool, IO[str]] = ..., + err: Union[bool, IO[str]] = ..., + in_: bool = ..., + mixed: bool = ..., + now: bool = ..., + ) -> None: ... + @classmethod + def call(cls, func: Callable[..., _T], *args: Any, **kwargs: Any) -> Tuple[_T, str, str]: ... + def reset(self) -> Tuple[str, str]: ... + def suspend(self) -> Tuple[str, str]: ... + def startall(self) -> None: ... + def resume(self) -> None: ... + def done(self, save: bool = ...) -> Tuple[IO[str], IO[str]]: ... + def readouterr(self) -> Tuple[IO[str], IO[str]]: ... + +# XXX: The type here is not exactly right. If f is IO[bytes] and +# encoding is not None, returns some weird hybrid, not exactly IO[bytes]. +def dupfile( + f: IO[AnyStr], + mode: Optional[str] = ..., + buffering: int = ..., + raising: bool = ..., + encoding: Optional[str] = ..., +) -> IO[AnyStr]: ... +def get_terminal_width() -> int: ... +def ansi_print( + text: Union[str, Text], + esc: Union[Union[str, Text], Tuple[Union[str, Text], ...]], + file: Optional[IO[Any]] = ..., + newline: bool = ..., + flush: bool = ..., +) -> None: ... +def saferepr(obj, maxsize: int = ...) -> str: ... + +class TerminalWriter: + stringio: TextIO + encoding: Final[str] + hasmarkup: bool + def __init__(self, file: Optional[IO[str]] = ..., stringio: bool = ..., encoding: Optional[str] = ...) -> None: ... + @property + def fullwidth(self) -> int: ... + @fullwidth.setter + def fullwidth(self, value: int) -> None: ... + @property + def chars_on_current_line(self) -> int: ... + @property + def width_of_current_line(self) -> int: ... + def markup( + self, + text: str, + *, + black: int = ..., red: int = ..., green: int = ..., yellow: int = ..., blue: int = ..., purple: int = ..., + cyan: int = ..., white: int = ..., Black: int = ..., Red: int = ..., Green: int = ..., Yellow: int = ..., + Blue: int = ..., Purple: int = ..., Cyan: int = ..., White: int = ..., bold: int = ..., light: int = ..., + blink: int = ..., invert: int = ..., + ) -> str: ... + def sep( + self, + sepchar: str, + title: Optional[str] = ..., + fullwidth: Optional[int] = ..., + *, + black: int = ..., red: int = ..., green: int = ..., yellow: int = ..., blue: int = ..., purple: int = ..., + cyan: int = ..., white: int = ..., Black: int = ..., Red: int = ..., Green: int = ..., Yellow: int = ..., + Blue: int = ..., Purple: int = ..., Cyan: int = ..., White: int = ..., bold: int = ..., light: int = ..., + blink: int = ..., invert: int = ..., + ) -> None: ... + def write( + self, + msg: str, + *, + black: int = ..., red: int = ..., green: int = ..., yellow: int = ..., blue: int = ..., purple: int = ..., + cyan: int = ..., white: int = ..., Black: int = ..., Red: int = ..., Green: int = ..., Yellow: int = ..., + Blue: int = ..., Purple: int = ..., Cyan: int = ..., White: int = ..., bold: int = ..., light: int = ..., + blink: int = ..., invert: int = ..., + ) -> None: ... + def line( + self, + s: str = ..., + *, + black: int = ..., red: int = ..., green: int = ..., yellow: int = ..., blue: int = ..., purple: int = ..., + cyan: int = ..., white: int = ..., Black: int = ..., Red: int = ..., Green: int = ..., Yellow: int = ..., + Blue: int = ..., Purple: int = ..., Cyan: int = ..., White: int = ..., bold: int = ..., light: int = ..., + blink: int = ..., invert: int = ..., + ) -> None: ... + def reline( + self, + line: str, + *, + black: int = ..., red: int = ..., green: int = ..., yellow: int = ..., blue: int = ..., purple: int = ..., + cyan: int = ..., white: int = ..., Black: int = ..., Red: int = ..., Green: int = ..., Yellow: int = ..., + Blue: int = ..., Purple: int = ..., Cyan: int = ..., White: int = ..., bold: int = ..., light: int = ..., + blink: int = ..., invert: int = ..., + ) -> None: ... diff --git a/py/path.pyi b/py/path.pyi new file mode 100644 index 00000000..1ddab960 --- /dev/null +++ b/py/path.pyi @@ -0,0 +1,197 @@ +from typing import Any, AnyStr, Callable, ContextManager, Generic, IO, Iterable, Iterator, List, Optional, Text, Type, Union +from typing_extensions import Final, Literal +import os +import sys + +class _FNMatcher(Generic[AnyStr]): + pattern: AnyStr = ... + def __init__(self, pattern: AnyStr) -> None: ... + def __call__(self, path: local) -> bool: ... + +class _Stat: + path: Final[local] = ... + mode: Final[int] + ino: Final[int] + dev: Final[int] + nlink: Final[int] + uid: Final[int] + gid: Final[int] + size: Final[int] + atime: Final[float] + mtime: Final[float] + ctime: Final[float] + atime_ns: Final[int] + mtime_ns: Final[int] + ctime_ns: Final[int] + if sys.version_info >= (3, 8) and sys.platform == "win32": + reparse_tag: Final[int] + blocks: Final[int] + blksize: Final[int] + rdev: Final[int] + flags: Final[int] + gen: Final[int] + birthtime: Final[int] + rsize: Final[int] + creator: Final[int] + type: Final[int] + if sys.platform != 'win32': + @property + def owner(self) -> str: ... + @property + def group(self) -> str: ... + def isdir(self) -> bool: ... + def isfile(self) -> bool: ... + def islink(self) -> bool: ... + + +if sys.version_info >= (3, 6): + _PathLike = os.PathLike +else: + class _PathLike(Generic[AnyStr]): + def __fspath__(self) -> AnyStr: ... +_PathType = Union[bytes, Text, _PathLike[str], _PathLike[bytes], local] + +class local(_PathLike[str]): + class ImportMismatchError(ImportError): ... + + sep: Final[str] + strpath: Final[str] + + def __init__(self, path: _PathType = ..., expanduser: bool = ...) -> None: ... + def __hash__(self) -> int: ... + def __eq__(self, other: object) -> bool: ... + def __ne__(self, other: object) -> bool: ... + def __lt__(self, other: object) -> bool: ... + def __gt__(self, other: object) -> bool: ... + def __add__(self, other: object) -> local: ... + def __cmp__(self, other: object) -> int: ... + def __div__(self, other: _PathType) -> local: ... + def __truediv__(self, other: _PathType) -> local: ... + def __fspath__(self) -> str: ... + + @classmethod + def get_temproot(cls) -> local: ... + @classmethod + def make_numbered_dir( + cls, + prefix: str = ..., + rootdir: Optional[local] = ..., + keep: Optional[int] = ..., + lock_timeout: int = ..., + ) -> local: ... + @classmethod + def mkdtemp(cls, rootdir: Optional[local] = ...) -> local: ... + @classmethod + def sysfind( + cls, + name: _PathType, + checker: Optional[Callable[[local], bool]] = ..., + paths: Optional[Iterable[_PathType]] = ..., + ) -> Optional[local]: ... + + @property + def basename(self) -> str: ... + @property + def dirname(self) -> str: ... + @property + def purebasename(self) -> str: ... + @property + def ext(self) -> str: ... + + def as_cwd(self) -> ContextManager[Optional[local]]: ... + def atime(self) -> float: ... + def bestrelpath(self, dest: local) -> str: ... + def chdir(self) -> local: ... + def check( + self, + *, + basename: int = ..., notbasename: int = ..., + basestarts: int = ..., notbasestarts: int = ..., + dir: int = ..., notdir: int = ..., + dotfile: int = ..., notdotfile: int = ..., + endswith: int = ..., notendswith: int = ..., + exists: int = ..., notexists: int = ..., + ext: int = ..., notext: int = ..., + file: int = ..., notfile: int = ..., + fnmatch: int = ..., notfnmatch: int = ..., + link: int = ..., notlink: int = ..., + relto: int = ..., notrelto: int = ..., + ) -> bool: ... + def chmod(self, mode: int, rec: Union[int, str, Text, Callable[[local], bool]] = ...) -> None: ... + if sys.platform != 'win32': + def chown(self, user: Union[int, str], group: Union[int, str], rec: int = ...) -> None: ... + def common(self, other: local) -> Optional[local]: ... + def computehash(self, hashtype: str = ..., chunksize: int = ...) -> str: ... + def copy(self, target: local, mode: bool = ..., stat: bool = ...) -> None: ... + def dirpath(self, *args: _PathType, abs: int = ...) -> local: ... + def dump(self, obj: Any, bin: Optional[int] = ...) -> None: ... + def ensure(self, *args: _PathType, dir: int = ...) -> local: ... + def ensure_dir(self, *args: _PathType) -> local: ... + def exists(self) -> bool: ... + def fnmatch(self, pattern: str): _FNMatcher + def isdir(self) -> bool: ... + def isfile(self) -> bool: ... + def islink(self) -> bool: ... + def join(self, *args: _PathType, abs: int = ...) -> local: ... + def listdir( + self, + fil: Optional[Union[str, Text, Callable[[local], bool]]] = ..., + sort: Optional[bool] = ..., + ) -> List[local]: ... + def load(self) -> Any: ... + def lstat(self) -> _Stat: ... + def mkdir(self, *args: _PathType) -> local: ... + if sys.platform != 'win32': + def mklinkto(self, oldname: Union[str, local]) -> None: ... + def mksymlinkto(self, value: local, absolute: int = ...) -> None: ... + def move(self, target: local) -> None: ... + def mtime(self) -> float: ... + def new( + self, + *, + drive: str = ..., + dirname: str = ..., + basename: str = ..., + purebasename: str = ..., + ext: str = ..., + ) -> local: ... + def open(self, mode: str = ..., ensure: bool = ..., encoding: Optional[str] = ...) -> IO[Any]: ... + def parts(self, reverse: bool = ...) -> List[local]: ... + def pyimport( + self, + modname: Optional[str] = ..., + ensuresyspath: Union[bool, Literal["append", "importlib"]] = ..., + ) -> Any: ... + def pypkgpath(self) -> Optional[local]: ... + def read(self, mode: str = ...) -> Union[Text, bytes]: ... + def read_binary(self) -> bytes: ... + def read_text(self, encoding: str) -> Text: ... + def readlines(self, cr: int = ...) -> List[str]: ... + if sys.platform != 'win32': + def readlink(self) -> str: ... + def realpath(self) -> local: ... + def relto(self, relpath: Union[str, local]) -> str: ... + def remove(self, rec: int = ..., ignore_errors: bool = ...) -> None: ... + def rename(self, target: _PathType) -> None: ... + def samefile(self, other: _PathType) -> bool: ... + def setmtime(self, mtime: Optional[float] = ...) -> None: ... + def size(self) -> int: ... + def stat(self, raising: bool = ...) -> _Stat: ... + def sysexec(self, *argv: Any, **popen_opts: Any) -> Text: ... + def visit( + self, + fil: Optional[Union[str, Text, Callable[[local], bool]]] = ..., + rec: Optional[Union[Literal[1, True], str, Text, Callable[[local], bool]]] = ..., + ignore: Type[Exception] = ..., + bf: bool = ..., + sort: bool = ..., + ) -> Iterator[local]: ... + def write(self, data: Any, mode: str = ..., ensure: bool = ...) -> None: ... + def write_binary(self, data: bytes, ensure: bool = ...) -> None: ... + def write_text(self, data: Union[str, Text], encoding: str, ensure: bool = ...) -> None: ... + + +# Untyped types below here. +svnwc: Any +svnurl: Any +SvnAuth: Any diff --git a/py/py.typed b/py/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/py/xml.pyi b/py/xml.pyi new file mode 100644 index 00000000..9c44480a --- /dev/null +++ b/py/xml.pyi @@ -0,0 +1,25 @@ +from typing import ClassVar, Generic, Iterable, Text, Type, Union +from typing_extensions import Final + +class raw: + uniobj: Final[Text] + def __init__(self, uniobj: Text) -> None: ... + +class _NamespaceMetaclass(type): + def __getattr__(self, name: str) -> Type[Tag]: ... + +class Namespace(metaclass=_NamespaceMetaclass): ... + +class Tag(list): + class Attr: + def __getattr__(self, attr: str) -> Text: ... + attr: Final[Attr] + def __init__(self, *args: Union[Text, raw, Tag, Iterable[Tag]], **kwargs: Union[Text, raw]) -> None: ... + def unicode(self, indent: int = ...) -> Text: ... + +class html(Namespace): + class Style: + def __init__(self, **kw: Union[str, Text]) -> None: ... + style: ClassVar[Style] + +def escape(ustring: Union[str, Text]) -> Text: ... diff --git a/setup.py b/setup.py index 959323b0..d097daa5 100644 --- a/setup.py +++ b/setup.py @@ -1,26 +1,14 @@ -import os -import sys - from setuptools import setup, find_packages -def get_version(): - p = os.path.join(os.path.dirname( - os.path.abspath(__file__)), "py", "__init__.py") - with open(p) as f: - for line in f.readlines(): - if "__version__" in line: - return line.strip().split("=")[-1].strip(" '") - raise ValueError("could not read version") - - def main(): setup( name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description=open('README.rst').read(), - version=get_version(), - url='http://py.readthedocs.io/', + use_scm_version={"write_to": "py/_version.py"}, + setup_requires=["setuptools-scm"], + url='https://py.readthedocs.io/', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', @@ -39,14 +27,18 @@ def main(): '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 :: 3.7', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ], packages=find_packages(exclude=['tasks', 'testing']), + include_package_data=True, zip_safe=False, + package_data={ + "": ["py.typed"], + }, ) if __name__ == '__main__': diff --git a/tasks/__init__.py b/tasks/__init__.py index 5d74b649..e69de29b 100644 --- a/tasks/__init__.py +++ b/tasks/__init__.py @@ -1,12 +0,0 @@ -""" -Invoke tasks to help with pytest development and release process. -""" - -import invoke - -from . import vendoring - - -ns = invoke.Collection( - vendoring -) diff --git a/tasks/vendoring.py b/tasks/vendoring.py index fbc171bc..3c7d6015 100644 --- a/tasks/vendoring.py +++ b/tasks/vendoring.py @@ -1,23 +1,41 @@ from __future__ import absolute_import, print_function -import py -import invoke +import os.path +import shutil +import subprocess +import sys -VENDOR_TARGET = py.path.local("py/_vendored_packages") -GOOD_FILES = 'README.md', '__init__.py' +VENDOR_TARGET = "py/_vendored_packages" +GOOD_FILES = ('README.md', '__init__.py') -@invoke.task() -def remove_libs(ctx): + +def remove_libs(): print("removing vendored libs") - for path in VENDOR_TARGET.listdir(): - if path.basename not in GOOD_FILES: + for filename in os.listdir(VENDOR_TARGET): + if filename not in GOOD_FILES: + path = os.path.join(VENDOR_TARGET, filename) print(" ", path) - path.remove() + if os.path.isfile(path): + os.remove(path) + else: + shutil.rmtree(path) + -@invoke.task(pre=[remove_libs]) -def update_libs(ctx): +def update_libs(): print("installing libs") - ctx.run("pip install -t {target} apipkg iniconfig".format(target=VENDOR_TARGET)) - ctx.run("git add {target}".format(target=VENDOR_TARGET)) + subprocess.check_call(( + sys.executable, '-m', 'pip', 'install', + '--target', VENDOR_TARGET, 'apipkg', 'iniconfig', + )) + subprocess.check_call(('git', 'add', VENDOR_TARGET)) print("Please commit to finish the update after running the tests:") print() print(' git commit -am "Updated vendored libs"') + + +def main(): + remove_libs() + update_libs() + + +if __name__ == '__main__': + exit(main()) diff --git a/testing/code/test_source.py b/testing/code/test_source.py index 3492761a..ca9a4227 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -103,7 +103,7 @@ def test_source_strip_multiline(): 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.offset in (5, 7) # pypy/cpython difference assert ex.value.text.strip(), 'x x' def test_isparseable(): @@ -456,7 +456,9 @@ class A(object): class B: pass B.__name__ = "B2" - assert getfslineno(B)[1] == -1 + # TODO: On CPython 3.9 this actually returns the line, + # should it? + # assert getfslineno(B)[1] == -1 def test_code_of_object_instance_with_call(): class A: @@ -510,11 +512,17 @@ def test_comments(): 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)) == '"""' + for line in range(2, 6): + assert str(getstatement(line, source)) == " x = 1" + if sys.version_info >= (3, 8) or hasattr(sys, "pypy_version_info"): + tqs_start = 8 + else: + tqs_start = 10 + assert str(getstatement(10, source)) == '"""' + for line in range(6, tqs_start): + assert str(getstatement(line, source)) == " assert False" + for line in range(tqs_start, 10): + assert str(getstatement(line, source)) == '"""\ncomment 4\n"""' def test_comment_in_statement(): source = '''test(foo=1, diff --git a/testing/io_/test_terminalwriter.py b/testing/io_/test_terminalwriter.py index 7e9ebf40..1eef7f7d 100644 --- a/testing/io_/test_terminalwriter.py +++ b/testing/io_/test_terminalwriter.py @@ -1,3 +1,4 @@ +from collections import namedtuple import py import os, sys @@ -10,16 +11,22 @@ def test_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 + if sys.version_info >= (3, 3): + import shutil + Size = namedtuple('Size', 'lines columns') + monkeypatch.setattr(shutil, 'get_terminal_size', lambda: Size(60, 100)) + assert terminalwriter._getdimensions() == (60, 100) + else: + 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 @@ -158,6 +165,12 @@ def test_sep_with_title(self, tw): assert len(l) == 1 assert l[0] == "-" * 26 + " hello " + "-" * (27-win32) + "\n" + def test_sep_longer_than_width(self, tw): + tw.sep('-', 'a' * 10, fullwidth=5) + line, = tw.getlines() + # even though the string is wider than the line, still have a separator + assert line == '- aaaaaaaaaa -\n' + @py.test.mark.skipif("sys.platform == 'win32'") def test__escaped(self, tw): text2 = tw._escaped("hello", (31)) diff --git a/testing/io_/test_terminalwriter_linewidth.py b/testing/io_/test_terminalwriter_linewidth.py new file mode 100644 index 00000000..e6d84fbf --- /dev/null +++ b/testing/io_/test_terminalwriter_linewidth.py @@ -0,0 +1,56 @@ +# coding: utf-8 +from __future__ import unicode_literals + +from py._io.terminalwriter import TerminalWriter + + +def test_terminal_writer_line_width_init(): + tw = TerminalWriter() + assert tw.chars_on_current_line == 0 + assert tw.width_of_current_line == 0 + + +def test_terminal_writer_line_width_update(): + tw = TerminalWriter() + tw.write('hello world') + assert tw.chars_on_current_line == 11 + assert tw.width_of_current_line == 11 + + +def test_terminal_writer_line_width_update_with_newline(): + tw = TerminalWriter() + tw.write('hello\nworld') + assert tw.chars_on_current_line == 5 + assert tw.width_of_current_line == 5 + + +def test_terminal_writer_line_width_update_with_wide_text(): + tw = TerminalWriter() + tw.write('乇乂ㄒ尺卂 ㄒ卄丨匚匚') + assert tw.chars_on_current_line == 11 + assert tw.width_of_current_line == 21 # 5*2 + 1 + 5*2 + + +def test_terminal_writer_line_width_update_with_wide_bytes(): + tw = TerminalWriter() + tw.write('乇乂ㄒ尺卂 ㄒ卄丨匚匚'.encode('utf-8')) + assert tw.chars_on_current_line == 11 + assert tw.width_of_current_line == 21 + + +def test_terminal_writer_line_width_composed(): + tw = TerminalWriter() + text = 'café food' + assert len(text) == 9 + tw.write(text) + assert tw.chars_on_current_line == 9 + assert tw.width_of_current_line == 9 + + +def test_terminal_writer_line_width_combining(): + tw = TerminalWriter() + text = 'café food' + assert len(text) == 10 + tw.write(text) + assert tw.chars_on_current_line == 10 + assert tw.width_of_current_line == 9 diff --git a/testing/path/test_local.py b/testing/path/test_local.py index c9075d6f..1b9a7923 100644 --- a/testing/path/test_local.py +++ b/testing/path/test_local.py @@ -125,6 +125,19 @@ def test_chdir_gone(self, path1): assert path1.chdir() is None assert os.getcwd() == str(path1) + with pytest.raises(py.error.ENOENT): + with p.as_cwd(): + raise NotImplementedError + + @skiponwin32 + def test_chdir_gone_in_as_cwd(self, path1): + p = path1.ensure("dir_to_be_removed", dir=1) + p.chdir() + p.remove() + + with path1.as_cwd() as old: + assert old is None + def test_as_cwd(self, path1): dir = path1.ensure("subdir", dir=1) old = py.path.local() @@ -151,6 +164,17 @@ def test_tilde_expansion(self, monkeypatch, tmpdir): p = py.path.local("~", expanduser=True) assert p == os.path.expanduser("~") + @pytest.mark.skipif( + not sys.platform.startswith("win32"), reason="case insensitive only on windows" + ) + def test_eq_hash_are_case_insensitive_on_windows(self): + a = py.path.local("/some/path") + b = py.path.local("/some/PATH") + assert a == b + assert hash(a) == hash(b) + assert a in {b} + assert a in {b: 'b'} + def test_eq_with_strings(self, path1): path1 = path1.join('sampledir') path2 = str(path1) @@ -161,7 +185,31 @@ def test_eq_with_strings(self, path1): assert path2 != path3 def test_eq_with_none(self, path1): - assert path1 != None # noqa + assert path1 != None # noqa: E711 + + @pytest.mark.skipif( + sys.platform.startswith("win32"), reason="cannot remove cwd on Windows" + ) + @pytest.mark.skipif( + sys.version_info < (3, 0) or sys.version_info >= (3, 5), + reason="only with Python 3 before 3.5" + ) + def test_eq_with_none_and_custom_fspath(self, monkeypatch, path1): + import os + import shutil + import tempfile + + d = tempfile.mkdtemp() + monkeypatch.chdir(d) + shutil.rmtree(d) + + monkeypatch.delitem(sys.modules, 'pathlib', raising=False) + monkeypatch.setattr(sys, 'path', [''] + sys.path) + + with pytest.raises(FileNotFoundError): + import pathlib # noqa: F401 + + assert path1 != None # noqa: E711 def test_eq_non_ascii_unicode(self, path1): path2 = path1.join(u'temp') @@ -425,24 +473,23 @@ def test_make_numbered_dir(self, tmpdir): 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_case(self, tmpdir): + """make_numbered_dir does not make assumptions on the underlying + filesystem based on the platform and will assume it _could_ be case + insensitive. + + See issues: + - https://github.com/pytest-dev/pytest/issues/708 + - https://github.com/pytest-dev/pytest/issues/3451 + """ + d1 = local.make_numbered_dir( + prefix='CAse.', rootdir=tmpdir, keep=2, lock_timeout=0, + ) + d2 = local.make_numbered_dir( + prefix='caSE.', rootdir=tmpdir, keep=2, lock_timeout=0, + ) + assert str(d1).lower() != str(d2).lower() + assert str(d2).endswith('.1') def test_make_numbered_dir_NotImplemented_Error(self, tmpdir, monkeypatch): def notimpl(x, y): @@ -476,13 +523,22 @@ def test_pyimport(self, path1): assert obj.x == 42 assert obj.__name__ == 'execfile' - def test_pyimport_renamed_dir_creates_mismatch(self, tmpdir): + def test_pyimport_renamed_dir_creates_mismatch(self, tmpdir, monkeypatch): 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() + # Errors can be ignored. + monkeypatch.setenv('PY_IGNORE_IMPORTMISMATCH', '1') + tmpdir.join("b", "test_x123.py").pyimport() + + # PY_IGNORE_IMPORTMISMATCH=0 does not ignore error. + monkeypatch.setenv('PY_IGNORE_IMPORTMISMATCH', '0') + 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') @@ -573,6 +629,39 @@ def test_ensuresyspath_append(self, tmpdir): assert str(root1) not in sys.path[:-1] +class TestImportlibImport: + pytestmark = py.test.mark.skipif("sys.version_info < (3, 5)") + + OPTS = {'ensuresyspath': 'importlib'} + + def test_pyimport(self, path1): + obj = path1.join('execfile.py').pyimport(**self.OPTS) + assert obj.x == 42 + assert obj.__name__ == 'execfile' + + def test_pyimport_dir_fails(self, tmpdir): + p = tmpdir.join("hello_123") + p.ensure("__init__.py") + with pytest.raises(ImportError): + p.pyimport(**self.OPTS) + + def test_pyimport_execfile_different_name(self, path1): + obj = path1.join('execfile.py').pyimport(modname="0x.y.z", **self.OPTS) + assert obj.x == 42 + assert obj.__name__ == '0x.y.z' + + def test_pyimport_relative_import_fails(self, path1): + otherdir = path1.join('otherdir') + with pytest.raises(ImportError): + otherdir.join('a.py').pyimport(**self.OPTS) + + def test_pyimport_doesnt_use_sys_modules(self, tmpdir): + p = tmpdir.ensure('file738jsk.py') + mod = p.pyimport(**self.OPTS) + assert mod.__name__ == 'file738jsk' + assert 'file738jsk' not in sys.modules + + def test_pypkgdir(tmpdir): pkg = tmpdir.ensure('pkg1', dir=1) pkg.ensure("__init__.py") @@ -626,6 +715,18 @@ def test_samefile(tmpdir): p2 = p.__class__(str(p).upper()) assert p1.samefile(p2) +@pytest.mark.skipif(not hasattr(os, "symlink"), reason="os.symlink not available") +def test_samefile_symlink(tmpdir): + p1 = tmpdir.ensure("foo.txt") + p2 = tmpdir.join("linked.txt") + try: + os.symlink(str(p1), str(p2)) + except (OSError, NotImplementedError) as e: + # on Windows this might fail if the user doesn't have special symlink permissions + # pypy3 on Windows doesn't implement os.symlink and raises NotImplementedError + pytest.skip(str(e.args[0])) + + assert p1.samefile(p2) def test_listdir_single_arg(tmpdir): tmpdir.ensure("hello") diff --git a/testing/root/test_builtin.py b/testing/root/test_builtin.py index a6f1a3c7..287c60d5 100644 --- a/testing/root/test_builtin.py +++ b/testing/root/test_builtin.py @@ -1,7 +1,7 @@ import sys import types import py -from py.builtin import set, frozenset, reversed, sorted +from py.builtin import set, frozenset def test_enumerate(): l = [0,1,2] @@ -53,29 +53,6 @@ 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_ @@ -116,7 +93,7 @@ class A: def test_getfuncdict(): def f(): - pass + raise NotImplementedError f.x = 4 assert py.builtin._getfuncdict(f)["x"] == 4 assert py.builtin._getfuncdict(2) is None diff --git a/testing/root/test_error.py b/testing/root/test_error.py index a1185f33..7bfbef3b 100644 --- a/testing/root/test_error.py +++ b/testing/root/test_error.py @@ -2,6 +2,8 @@ import py import errno +import sys +import subprocess def test_error_classes(): @@ -33,7 +35,7 @@ def test_unknown_error(): assert cls is cls2 -def test_error_conversion_ENOTDIR(testdir): +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) @@ -46,6 +48,12 @@ def test_checked_call_supports_kwargs(tmpdir): py.error.checked_call(tempfile.mkdtemp, dir=str(tmpdir)) +def test_error_importable(): + """Regression test for #179""" + subprocess.check_call( + [sys.executable, '-c', 'from py.error import ENOENT']) + + try: import unittest unittest.TestCase.assertWarns @@ -56,13 +64,13 @@ def test_checked_call_supports_kwargs(tmpdir): import warnings class Case(unittest.TestCase): - def test_assertWarns(self): + def test_assert_warns(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 + __import__('py') with self.assertWarns(UserWarning): warnings.warn('this should work') diff --git a/tox.ini b/tox.ini index 601661cf..f3203507 100644 --- a/tox.ini +++ b/tox.ini @@ -1,16 +1,16 @@ [tox] -envlist=py{27,34,35,36}-pytest{29,30,31} +# Skip py37-pytest29 as such a combination does not work (#192) +envlist=py{27,35,36}-pytest{29,30,31},py37-pytest{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 [] + pip install -U . # hande the install order fallout since pytest depends on pip + py.test --confcutdir=. --junitxml={envlogdir}/junit-{envname}.xml [] deps= - pytest29: pytest~=2.9.0 - pytest30: pytest~=3.0.0 - pytest31: pytest~=3.1.0 + attrs + pytest29: pytest~=2.9.0 + pytest30: pytest~=3.0.0 + pytest31: pytest~=3.1.0 [testenv:py27-xdist] basepython=python2.7 @@ -19,15 +19,26 @@ deps= 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 \ + py.test -n3 --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} + {envpython} -m pytest --confcutdir=.. --junitxml={envlogdir}/junit-{envname}0.xml {posargs:io_ code} [pytest] rsyncdirs = conftest.py py doc testing addopts = -ra +testpaths = testing + +[coverage:run] +branch = 1 +source = . +parallel = 1 +[coverage:report] +include = py/*,testing/* +exclude_lines = + #\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(cover|COVER) + ^\s*raise NotImplementedError\b