diff options
author | Michael Howitz <mh@gocept.com> | 2021-07-22 08:15:28 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-22 08:15:28 +0200 |
commit | 67da8197f2b8ed8eae915aa5e28798bc5c884094 (patch) | |
tree | b4527b3ac8ff36d8ba4b74b496b3d4433495e5f2 | |
parent | b0f4124e950f2e8dde1d4e6c65c12e9e8b82c05d (diff) | |
parent | afe9e808349251851e25c8739d8d92c3a82bfd8b (diff) | |
download | zope-proxy-67da8197f2b8ed8eae915aa5e28798bc5c884094.tar.gz |
Merge pull request #47 from zopefoundation/config-with-c-code
Config with c code
-rw-r--r-- | .coveragerc | 24 | ||||
-rw-r--r-- | .editorconfig | 39 | ||||
-rw-r--r-- | .github/workflows/tests.yml | 450 | ||||
-rw-r--r-- | .gitignore | 39 | ||||
-rwxr-xr-x | .manylinux-install.sh | 31 | ||||
-rwxr-xr-x | .manylinux.sh | 13 | ||||
-rw-r--r-- | .meta.toml | 37 | ||||
-rw-r--r-- | .travis.yml | 116 | ||||
-rw-r--r-- | CHANGES.rst | 4 | ||||
-rw-r--r-- | MANIFEST.in | 24 | ||||
-rw-r--r-- | appveyor.yml | 27 | ||||
-rw-r--r-- | bootstrap.py | 210 | ||||
-rw-r--r-- | setup.cfg | 24 | ||||
-rw-r--r-- | setup.py | 7 | ||||
-rw-r--r-- | src/zope/proxy/__init__.py | 78 | ||||
-rw-r--r-- | src/zope/proxy/decorator.py | 3 | ||||
-rw-r--r-- | src/zope/proxy/interfaces.py | 1 | ||||
-rw-r--r-- | src/zope/proxy/tests/test_decorator.py | 24 | ||||
-rw-r--r-- | src/zope/proxy/tests/test_proxy.py | 229 | ||||
-rw-r--r-- | tox.ini | 80 |
20 files changed, 942 insertions, 518 deletions
diff --git a/.coveragerc b/.coveragerc index e901a7d..b412d61 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,9 +1,29 @@ +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/c-code [run] source = zope.proxy +# New in 5.0; required for the GHA coveralls submission. +relative_files = True +branch = true + +[paths] +source = + src/ + .tox/*/lib/python*/site-packages/ + .tox/pypy*/site-packages/ [report] +show_missing = true +precision = 2 exclude_lines = - pragma: no cover + except ImportError: if __name__ == '__main__': - raise NotImplementedError + pragma: no cover + pragma: nocover raise AssertionError + raise NotImplementedError + raise unittest.Skip + self.fail\( + +[html] +directory = parts/htmlcov diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9d3c4f2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,39 @@ +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/c-code +# +# EditorConfig Configuration file, for more details see: +# http://EditorConfig.org +# EditorConfig is a convention description, that could be interpreted +# by multiple editors to enforce common coding conventions for specific +# file types + +# top-most EditorConfig file: +# Will ignore other EditorConfig files in Home directory or upper tree level. +root = true + + +[*] # For All Files +# Unix-style newlines with a newline ending every file +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +# Set default charset +charset = utf-8 +# Indent style default +indent_style = space +# Max Line Length - a hard line wrap, should be disabled +max_line_length = off + +[*.{py,cfg,ini}] +# 4 space indentation +indent_size = 4 + +[*.{yml,zpt,pt,dtml,zcml}] +# 2 space indentation +indent_size = 2 + +[{Makefile,.gitmodules}] +# Tab indentation (no size specified, but view as 4 spaces) +indent_style = tab +indent_size = unset +tab_width = unset diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..b66af7a --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,450 @@ +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/c-code +### +# Initially copied from +# https://github.com/actions/starter-workflows/blob/main/ci/python-package.yml +# And later based on the version jamadden updated at +# gevent/gevent, and then at zodb/relstorage and zodb/perfmetrics +# +# Original comment follows. +### +### +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions +### + +### +# Important notes on GitHub actions: +# +# - We only get 2,000 free minutes a month (private repos) +# - We only get 500MB of artifact storage +# - Cache storage is limited to 7 days and 5GB. +# - macOS minutes are 10x as expensive as Linux minutes +# - windows minutes are twice as expensive. +# +# So keep those workflows light. Note: Currently, they seem to be free +# and unlimited for open source projects. But for how long... +# +# In December 2020, github only supports x86/64. If we wanted to test +# on other architectures, we can use docker emulation, but there's no +# native support. It works, but is slow. +# +# Another major downside: You can't just re-run the job for one part +# of the matrix. So if there's a transient test failure that hit, say, 3.8, +# to get a clean run every version of Python runs again. That's bad. +# https://github.community/t/ability-to-rerun-just-a-single-job-in-a-workflow/17234/65 + +name: tests + + +# Triggers the workflow on push or pull request events and periodically +on: + push: + pull_request: + schedule: + - cron: '0 12 * * 0' # run once a week on Sunday + # Allow to run this workflow manually from the Actions tab + workflow_dispatch: + +env: + # Weirdly, this has to be a top-level key, not ``defaults.env`` + PYTHONHASHSEED: 8675309 + PYTHONUNBUFFERED: 1 + PYTHONDONTWRITEBYTECODE: 1 + PYTHONDEVMODE: 1 + PYTHONFAULTHANDLER: 1 + ZOPE_INTERFACE_STRICT_IRO: 1 + + PIP_UPGRADE_STRATEGY: eager + # Don't get warnings about Python 2 support being deprecated. We + # know. The env var works for pip 20. + PIP_NO_PYTHON_VERSION_WARNING: 1 + PIP_NO_WARN_SCRIPT_LOCATION: 1 + + CFLAGS: -Ofast -pipe + CXXFLAGS: -Ofast -pipe + # Uploading built wheels for releases. + # TWINE_PASSWORD is encrypted and stored directly in the + # github repo settings. + TWINE_USERNAME: __token__ + + ### + # caching + # This is where we'd set up ccache, but this compiles so fast its not worth it. + ### + + +jobs: + # Because sharing code/steps is so hard, and because it can be + # extremely valuable to be able to get binary wheels without + # uploading to PyPI and even if there is some failure, (e.g., for + # other people to test/debug), the strategy is to divide the process + # into several different jobs. The first builds and saves the binary + # wheels. It has dependent jobs that download and install the wheel + # to run tests, build docs, and perform linting. Building the + # manylinux wheels is an independent set of jobs. + # + # This division is time-saving for projects that take awhile to + # build, but somewhat less of a clear-cut win given how quick this + # is to compile (at least at this writing). + build-package: + # Sigh. Note that the matrix must be kept in sync + # with `test`, and `docs` must use a subset. + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: + - 2.7 + - 3.5 + - pypy-2.7 + - pypy-3.6 + - 3.6 + - 3.7 + - 3.8 + - 3.9 + os: [ubuntu-20.04, macos-latest] + exclude: + - os: macos-latest + python-version: pypy-2.7 + - os: macos-latest + python-version: pypy-3.6 + - os: macos-latest + python-version: 3.5 + + steps: + - name: checkout + uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + ### + # Caching. + # This actually *restores* a cache and schedules a cleanup action + # to save the cache. So it must come before the thing we want to use + # the cache. + ### + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + + - name: pip cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip-${{ matrix.python-version }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install Build Dependencies + run: | + pip install -U pip + pip install -U setuptools wheel twine cffi + pip install -U coveralls coverage + + - name: Build zope.proxy + run: | + # Next, build the wheel *in place*. This helps ccache, and also lets us cache the configure + # output (pip install uses a random temporary directory, making this difficult). + python setup.py build_ext -i + python setup.py bdist_wheel + # Also install it, so that we get dependencies in the (pip) cache. + pip install -U coverage + pip install -U 'faulthandler; python_version == "2.7" and platform_python_implementation == "CPython"' + pip install .[test] + + - name: Check zope.proxy build + run: | + ls -l dist + twine check dist/* + - name: Upload zope.proxy wheel + uses: actions/upload-artifact@v2 + with: + name: zope.proxy-${{ runner.os }}-${{ matrix.python-version }}.whl + path: dist/*whl + - name: Publish package to PyPI (mac) + # We cannot 'uses: pypa/gh-action-pypi-publish@v1.4.1' because + # that's apparently a container action, and those don't run on + # the Mac. + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') && startsWith(runner.os, 'Mac') + env: + TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} + run: | + twine upload --skip-existing dist/* + + test: + needs: build-package + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: + - 2.7 + - 3.5 + - pypy-2.7 + - pypy-3.6 + - 3.6 + - 3.7 + - 3.8 + - 3.9 + os: [ubuntu-20.04, macos-latest] + exclude: + - os: macos-latest + python-version: pypy-2.7 + - os: macos-latest + python-version: pypy-3.6 + - os: macos-latest + python-version: 3.5 + + steps: + - name: checkout + uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + ### + # Caching. + # This actually *restores* a cache and schedules a cleanup action + # to save the cache. So it must come before the thing we want to use + # the cache. + ### + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + + - name: pip cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip-${{ matrix.python-version }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Download zope.proxy wheel + uses: actions/download-artifact@v2 + with: + name: zope.proxy-${{ runner.os }}-${{ matrix.python-version }}.whl + path: dist/ + - name: Install zope.proxy + run: | + pip install -U wheel + pip install -U coverage coverage-python-version + pip install -U 'faulthandler; python_version == "2.7" and platform_python_implementation == "CPython"' + # Unzip into src/ so that testrunner can find the .so files + # when we ask it to load tests from that directory. This + # might also save some build time? + unzip -n dist/zope.proxy-*whl -d src + pip install -U -e .[test] + - name: Run tests with C extensions + if: ${{ !startsWith(matrix.python-version, 'pypy') }} + run: | + python -m coverage run -p -m zope.testrunner --test-path=src --auto-color --auto-progress + - name: Run tests without C extensions + run: + # coverage makes PyPy run about 3x slower! + PURE_PYTHON=1 python -m coverage run -p -m zope.testrunner --test-path=src --auto-color --auto-progress + - name: Report Coverage + run: | + coverage combine + coverage report -i + - name: Submit to Coveralls + # This is a container action, which only runs on Linux. + if: ${{ startsWith(runner.os, 'Linux') }} + uses: AndreMiras/coveralls-python-action@develop + with: + parallel: true + + coveralls_finish: + needs: test + runs-on: ubuntu-20.04 + steps: + - name: Coveralls Finished + uses: AndreMiras/coveralls-python-action@develop + with: + parallel-finished: true + + docs: + needs: build-package + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: [3.9] + os: [ubuntu-20.04] + + steps: + - name: checkout + uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + ### + # Caching. + # This actually *restores* a cache and schedules a cleanup action + # to save the cache. So it must come before the thing we want to use + # the cache. + ### + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + + - name: pip cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip-${{ matrix.python-version }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Download zope.proxy wheel + uses: actions/download-artifact@v2 + with: + name: zope.proxy-${{ runner.os }}-${{ matrix.python-version }}.whl + path: dist/ + - name: Install zope.proxy + run: | + pip install -U wheel + pip install -U coverage + pip install -U "`ls dist/zope.proxy-*.whl`[docs]" + # Until repoze.sphinx.autointerface supports Sphinx 4.x we cannot use it: + pip install -U "Sphinx < 4" + - name: Build docs + env: + ZOPE_INTERFACE_STRICT_IRO: 1 + run: | + sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html + sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest + + lint: + needs: build-package + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: [3.9] + os: [ubuntu-20.04] + + steps: + - name: checkout + uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + ### + # Caching. + # This actually *restores* a cache and schedules a cleanup action + # to save the cache. So it must come before the thing we want to use + # the cache. + ### + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + + - name: pip cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip-${{ matrix.python-version }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Download zope.proxy wheel + uses: actions/download-artifact@v2 + with: + name: zope.proxy-${{ runner.os }}-${{ matrix.python-version }}.whl + path: dist/ + - name: Install zope.proxy + run: | + pip install -U pip + pip install -U wheel + pip install -U `ls dist/zope.proxy-*`[test] + - name: Lint + # We only need to do this on one version, and it should be Python 3, because + # pylint has stopped updating for Python 2. + # TODO: Pick a linter and configuration and make this step right. + run: | + pip install -U pylint + # python -m pylint --limit-inference-results=1 --rcfile=.pylintrc zope.proxy -f parseable -r n + + manylinux: + runs-on: ubuntu-20.04 + # We use a regular Python matrix entry to share as much code as possible. + strategy: + matrix: + python-version: [3.9] + image: [manylinux2010_x86_64, manylinux2010_i686, manylinux2014_aarch64] + + steps: + - name: checkout + uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + ### + # Caching. + # This actually *restores* a cache and schedules a cleanup action + # to save the cache. So it must come before the thing we want to use + # the cache. + ### + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + + - name: pip cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip_manylinux-${{ matrix.image }}-${{ matrix.python-version }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Update pip + run: pip install -U pip + - name: Build zope.proxy (x86_64) + if: matrix.image == 'manylinux2010_x86_64' + # An alternate way to do this is to run the container directly with a uses: + # and then the script runs inside it. That may work better with caching. + # See https://github.com/pyca/bcrypt/blob/f6b5ee2eda76d077c531362ac65e16f045cf1f29/.github/workflows/wheel-builder.yml + # The 2010 image is the most recent spec that comes with Python 2.7, + # and only up through the tag 2021-02-06-3d322a5 + env: + DOCKER_IMAGE: quay.io/pypa/${{ matrix.image }} + run: | + bash .manylinux.sh + - name: Build zope.proxy (i686) + if: matrix.image == 'manylinux2010_i686' + env: + DOCKER_IMAGE: quay.io/pypa/${{ matrix.image }} + PRE_CMD: linux32 + run: | + bash .manylinux.sh + - name: Build zope.proxy (aarch64) + if: matrix.image == 'manylinux2014_aarch64' + env: + DOCKER_IMAGE: quay.io/pypa/${{ matrix.image }} + run: | + # First we must enable emulation + docker run --rm --privileged hypriot/qemu-register + bash .manylinux.sh + + - name: Upload zope.proxy wheels + uses: actions/upload-artifact@v2 + with: + path: wheelhouse/*whl + name: manylinux_${{ matrix.image }}_wheels.zip + - name: Restore pip cache permissions + run: sudo chown -R $(whoami) ${{ steps.pip-cache.outputs.dir }} + - name: Publish package to PyPI + uses: pypa/gh-action-pypi-publish@v1.4.1 + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + with: + user: __token__ + password: ${{ secrets.TWINE_PASSWORD }} + skip_existing: true + packages_dir: wheelhouse/ @@ -1,16 +1,31 @@ +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/c-code +*.dll +*.egg-info/ +*.profraw *.pyc +*.pyo *.so -__pycache__ -build -*.egg-info -docs/_build -.installed.cfg -bin -develop-eggs -eggs -parts -.tox .coverage -htmlcov/ -nosetests.xml +.coverage.* +.eggs/ +.installed.cfg +.mr.developer.cfg +.tox/ +.vscode/ +__pycache__/ +bin/ +build/ coverage.xml +develop-eggs/ +develop/ +dist/ +docs/_build +eggs/ +etc/ +lib/ +lib64 +log/ +parts/ +pyvenv.cfg +var/ diff --git a/.manylinux-install.sh b/.manylinux-install.sh index 6ce44bf..57220ed 100755 --- a/.manylinux-install.sh +++ b/.manylinux-install.sh @@ -1,17 +1,44 @@ #!/usr/bin/env bash +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/c-code set -e -x +# Running inside docker +# Set a cache directory for pip. This was +# mounted to be the same as it is outside docker so it +# can be persisted. +export XDG_CACHE_HOME="/cache" +# XXX: This works for macOS, where everything bind-mounted +# is seen as owned by root in the container. But when the host is Linux +# the actual UIDs come through to the container, triggering +# pip to disable the cache when it detects that the owner doesn't match. +# The below is an attempt to fix that, taken from bcrypt. It seems to work on +# Github Actions. +if [ -n "$GITHUB_ACTIONS" ]; then + echo Adjusting pip cache permissions + mkdir -p $XDG_CACHE_HOME/pip + chown -R $(whoami) $XDG_CACHE_HOME +fi +ls -ld /cache +ls -ld /cache/pip + # Compile wheels for PYBIN in /opt/python/*/bin; do if [[ "${PYBIN}" == *"cp27"* ]] || \ [[ "${PYBIN}" == *"cp35"* ]] || \ [[ "${PYBIN}" == *"cp36"* ]] || \ [[ "${PYBIN}" == *"cp37"* ]] || \ - [[ "${PYBIN}" == *"cp38"* ]]; then - "${PYBIN}/pip" install -U pip setuptools cffi + [[ "${PYBIN}" == *"cp38"* ]] || \ + [[ "${PYBIN}" == *"cp39"* ]]; then "${PYBIN}/pip" install -e /io/ "${PYBIN}/pip" wheel /io/ -w wheelhouse/ + if [ `uname -m` == 'aarch64' ]; then + cd /io/ + "${PYBIN}/pip" install tox + "${PYBIN}/tox" -e py + cd .. + fi rm -rf /io/build /io/*.egg-info fi done diff --git a/.manylinux.sh b/.manylinux.sh index 2fed778..ea4ef41 100755 --- a/.manylinux.sh +++ b/.manylinux.sh @@ -1,5 +1,16 @@ #!/usr/bin/env bash +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/c-code set -e -x -docker run --rm -v "$(pwd)":/io $DOCKER_IMAGE $PRE_CMD /io/.manylinux-install.sh +# Mount the current directory as /io +# Mount the pip cache directory as /cache +# `pip cache` requires pip 20.1 +echo Setting up caching +python --version +python -mpip --version +LCACHE="$(dirname `python -mpip cache dir`)" +echo Sharing pip cache at $LCACHE $(ls -ld $LCACHE) + +docker run --rm -e GITHUB_ACTIONS -v "$(pwd)":/io -v "$LCACHE:/cache" $DOCKER_IMAGE $PRE_CMD /io/.manylinux-install.sh diff --git a/.meta.toml b/.meta.toml new file mode 100644 index 0000000..efa741c --- /dev/null +++ b/.meta.toml @@ -0,0 +1,37 @@ +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/c-code +[meta] +template = "c-code" +commit-id = "bf824746f106a5f6314af285f3d5642e3ce490ce" + +[python] +with-appveyor = true +with-windows = false +with-pypy = true +with-future-python = false +with-legacy-python = true +with-docs = true +with-sphinx-doctests = true + +[tox] +use-flake8 = true + +[coverage] +fail-under = 99 + +[manifest] +additional-rules = [ + "include *.sh", + "recursive-include docs *.bat", + "recursive-include src *.h", + ] + +[appveyor] +global-env-vars = [ + "# Currently the builds use @mgedmin's Appveyor account. The PyPI token belongs", + "# to zope.wheelbuilder, which is managed by @mgedmin and @dataflake.", + "global:", + " TWINE_USERNAME: __token__", + " TWINE_PASSWORD:", + " secure: aoZC/+rvJKg8B5GMGIxd1bg9UDShk28EhfPQFKI9zy7kzygdgj0XuaK619sLe3s4B08bIJaIUAThxEvWq13IvdLb5Oyk8B9qubd+NnDiNuw8WCGy4owYnbl+61fUVVKJIf1ETQyGDooYrEuBo798/+ycQbilTpmncAHZb2KyZkmA210fcWr7OhwmlRtC4IiW7GPCaxU6qhzLlP5pnS2Tl+yy/qx2DiW2fKWqUqynrb1ZMsk6ygN4qV72glTY6wV0eYboAGlghrws1x5+Z10Yug==", + ] diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index db51783..0000000 --- a/.travis.yml +++ /dev/null @@ -1,116 +0,0 @@ -language: python - -env: - global: - TWINE_USERNAME: zope.wheelbuilder - TWINE_PASSWORD: - secure: "BWyhZtoO9jM3vatjfwdloThv7RY2p8AaN3D8T/8nEIupcgT5E6QKfLlIDibtlMNds2UU0q/HOJiSM/CwBsm5nzMke55m8OhfxfyHG+5TuBafwHtEkMyXe1VQtxnyB7Rlya2ylI9f4/3qXYqyLinBr3F+osE1g2nQmqQL2tpNmRA=" - -python: - - 2.7 - - 3.5 - - 3.6 - - 3.7 - - 3.8 - - pypy - - pypy3 - -jobs: - include: - - name: "Python: 2.7, pure (no C extensions)" - python: "2.7" - env: PURE_PYTHON=1 - - # manylinux wheel builds - - name: 64-bit manylinux wheels (all Pythons) - services: docker - env: DOCKER_IMAGE=quay.io/pypa/manylinux2010_x86_64 - install: docker pull $DOCKER_IMAGE - script: bash .manylinux.sh - - - name: 32-bit manylinux wheels (all Pythons) - services: docker - env: DOCKER_IMAGE=quay.io/pypa/manylinux2010_i686 PRE_CMD=linux32 - install: docker pull $DOCKER_IMAGE - script: bash .manylinux.sh - - # It's important to use 'macpython' builds to get the least - # restrictive wheel tag. It's also important to avoid - # 'homebrew 3' because it floats instead of being a specific version. - - name: Python 2.7 wheels for MacOS - os: osx - language: generic - env: TERRYFY_PYTHON='macpython 2.7.17' - - name: Python 3.5 wheels for MacOS - os: osx - language: generic - env: TERRYFY_PYTHON='macpython 3.5' - - name: Python 3.6 wheels for MacOS - os: osx - language: generic - env: TERRYFY_PYTHON='macpython 3.6.2' - - name: Python 3.7 wheels for MacOS - os: osx - language: generic - env: TERRYFY_PYTHON='macpython 3.7.0' - - name: Python 3.8 wheels for MacOS - os: osx - language: generic - env: TERRYFY_PYTHON='macpython 3.8.0' - -before_install: - - | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - git clone https://github.com/MacPython/terryfy - source terryfy/travis_tools.sh - get_python_environment $TERRYFY_PYTHON venv - fi - -install: - - pip install -U pip - - pip install -U setuptools - - pip install -U coverage coveralls - # NB: let's install . (zope.proxy) first separately, because we have a nasty - # dependency loop: .[test] wants zope.security that setup_requires zope.proxy - # (and setup_requires is broken on Mac OS Python 3.5 due to TLS version - # compatibility issues that pip knows how to work around, but setuptools - # don't). - - pip install -e . - - pip install -e .[test,docs] - -script: - - python --version - - coverage run -m zope.testrunner --test-path=src - - sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html - - coverage run -a -m sphinx -b doctest -d docs/_build/doctrees docs docs/_build/doctest - - python setup.py bdist_wheel - -after_success: - - coveralls - - | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - # macpython 3.5 doesn't support recent TLS protocols which causes twine - # upload to fail, so we use the system Python to run twine - /usr/bin/python -m ensurepip --user - /usr/bin/python -m pip install --user -U pip - /usr/bin/python -m pip install --user -U -I twine - /usr/bin/python -m twine check dist/* - if [[ $TRAVIS_TAG ]]; then - /usr/bin/python -m twine upload --skip-existing dist/* - fi - fi - - | - if [[ -n "$DOCKER_IMAGE" ]]; then - pip install twine - twine check wheelhouse/* - if [[ $TRAVIS_TAG ]]; then - twine upload --skip-existing wheelhouse/* - fi - fi - -notifications: - email: false - -cache: pip -before_cache: - - rm -f $HOME/.cache/pip/log/debug.log diff --git a/CHANGES.rst b/CHANGES.rst index 1fd639a..2369fef 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,7 +5,9 @@ 4.3.6 (unreleased) ================== -- Nothing changed yet. +- Add support for Python 3.9. + +- Create aarch64 wheels. 4.3.5 (2020-03-16) diff --git a/MANIFEST.in b/MANIFEST.in index 2a03b09..c8bc394 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,18 +1,18 @@ +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/c-code include *.rst include *.txt -include *.py -include tox.ini include buildout.cfg -include .travis.yml +include tox.ini +include appveyor.yml include .coveragerc -recursive-include docs * -recursive-include src * - -global-exclude *.dll -global-exclude *.pyc -global-exclude *.pyo -global-exclude *.so +recursive-include docs *.py +recursive-include docs *.rst +recursive-include docs *.txt +recursive-include docs Makefile -# added by check_manifest.py -include *.yml +recursive-include src *.py +include *.sh +recursive-include docs *.bat +recursive-include src *.h diff --git a/appveyor.yml b/appveyor.yml index 9afaa42..4ebbe68 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,11 +1,12 @@ -# Currently the builds use @mgedmin's Appveyor account. The PyPI token belongs -# to zope.wheelbuilder, which is managed by @mgedmin and @dataflake. - +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/c-code environment: + # Currently the builds use @mgedmin's Appveyor account. The PyPI token belongs + # to zope.wheelbuilder, which is managed by @mgedmin and @dataflake. global: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: - secure: aoZC/+rvJKg8B5GMGIxd1bg9UDShk28EhfPQFKI9zy7kzygdgj0XuaK619sLe3s4B08bIJaIUAThxEvWq13IvdLb5Oyk8B9qubd+NnDiNuw8WCGy4owYnbl+61fUVVKJIf1ETQyGDooYrEuBo798/+ycQbilTpmncAHZb2KyZkmA210fcWr7OhwmlRtC4IiW7GPCaxU6qhzLlP5pnS2Tl+yy/qx2DiW2fKWqUqynrb1ZMsk6ygN4qV72glTY6wV0eYboAGlghrws1x5+Z10Yug== + TWINE_USERNAME: __token__ + TWINE_PASSWORD: + secure: aoZC/+rvJKg8B5GMGIxd1bg9UDShk28EhfPQFKI9zy7kzygdgj0XuaK619sLe3s4B08bIJaIUAThxEvWq13IvdLb5Oyk8B9qubd+NnDiNuw8WCGy4owYnbl+61fUVVKJIf1ETQyGDooYrEuBo798/+ycQbilTpmncAHZb2KyZkmA210fcWr7OhwmlRtC4IiW7GPCaxU6qhzLlP5pnS2Tl+yy/qx2DiW2fKWqUqynrb1ZMsk6ygN4qV72glTY6wV0eYboAGlghrws1x5+Z10Yug== matrix: - python: 27 @@ -18,8 +19,11 @@ environment: - python: 37-x64 - python: 38 - python: 38-x64 + - python: 39 + - python: 39-x64 install: + - "SET PYTHONVERSION=%PYTHON%" - "SET PATH=C:\\Python%PYTHON%;c:\\Python%PYTHON%\\scripts;%PATH%" - ps: | $env:PYTHON = "C:\\Python${env:PYTHON}" @@ -31,20 +35,21 @@ install: - echo "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 > "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\amd64\vcvars64.bat" - python -m pip install -U pip - pip install -U setuptools wheel - - pip install -e . + - pip install -U -e .[test] + +matrix: + fast_finish: true build_script: - - pip install wheel - python -W ignore setup.py -q bdist_wheel test_script: - - python setup.py test -q - + - zope-testrunner --test-path=src artifacts: - path: 'dist\*.whl' name: wheel deploy_script: - - ps: if ($env:APPVEYOR_REPO_TAG -eq $TRUE) { pip install twine; twine upload --skip-existing dist/* } + - ps: if ($env:APPVEYOR_REPO_TAG -eq $TRUE) { pip install twine; twine upload --skip-existing dist\*.whl } deploy: on diff --git a/bootstrap.py b/bootstrap.py deleted file mode 100644 index a459921..0000000 --- a/bootstrap.py +++ /dev/null @@ -1,210 +0,0 @@ -############################################################################## -# -# Copyright (c) 2006 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Bootstrap a buildout-based project - -Simply run this script in a directory containing a buildout.cfg. -The script accepts buildout command-line options, so you can -use the -c option to specify an alternate configuration file. -""" - -import os -import shutil -import sys -import tempfile - -from optparse import OptionParser - -__version__ = '2015-07-01' -# See zc.buildout's changelog if this version is up to date. - -tmpeggs = tempfile.mkdtemp(prefix='bootstrap-') - -usage = '''\ -[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options] - -Bootstraps a buildout-based project. - -Simply run this script in a directory containing a buildout.cfg, using the -Python that you want bin/buildout to use. - -Note that by using --find-links to point to local resources, you can keep -this script from going over the network. -''' - -parser = OptionParser(usage=usage) -parser.add_option("--version", - action="store_true", default=False, - help=("Return bootstrap.py version.")) -parser.add_option("-t", "--accept-buildout-test-releases", - dest='accept_buildout_test_releases', - action="store_true", default=False, - help=("Normally, if you do not specify a --version, the " - "bootstrap script and buildout gets the newest " - "*final* versions of zc.buildout and its recipes and " - "extensions for you. If you use this flag, " - "bootstrap and buildout will get the newest releases " - "even if they are alphas or betas.")) -parser.add_option("-c", "--config-file", - help=("Specify the path to the buildout configuration " - "file to be used.")) -parser.add_option("-f", "--find-links", - help=("Specify a URL to search for buildout releases")) -parser.add_option("--allow-site-packages", - action="store_true", default=False, - help=("Let bootstrap.py use existing site packages")) -parser.add_option("--buildout-version", - help="Use a specific zc.buildout version") -parser.add_option("--setuptools-version", - help="Use a specific setuptools version") -parser.add_option("--setuptools-to-dir", - help=("Allow for re-use of existing directory of " - "setuptools versions")) - -options, args = parser.parse_args() -if options.version: - print("bootstrap.py version %s" % __version__) - sys.exit(0) - - -###################################################################### -# load/install setuptools - -try: - from urllib.request import urlopen -except ImportError: - from urllib2 import urlopen - -ez = {} -if os.path.exists('ez_setup.py'): - exec(open('ez_setup.py').read(), ez) -else: - exec(urlopen('https://bootstrap.pypa.io/ez_setup.py').read(), ez) - -if not options.allow_site_packages: - # ez_setup imports site, which adds site packages - # this will remove them from the path to ensure that incompatible versions - # of setuptools are not in the path - import site - # inside a virtualenv, there is no 'getsitepackages'. - # We can't remove these reliably - if hasattr(site, 'getsitepackages'): - for sitepackage_path in site.getsitepackages(): - # Strip all site-packages directories from sys.path that - # are not sys.prefix; this is because on Windows - # sys.prefix is a site-package directory. - if sitepackage_path != sys.prefix: - sys.path[:] = [x for x in sys.path - if sitepackage_path not in x] - -setup_args = dict(to_dir=tmpeggs, download_delay=0) - -if options.setuptools_version is not None: - setup_args['version'] = options.setuptools_version -if options.setuptools_to_dir is not None: - setup_args['to_dir'] = options.setuptools_to_dir - -ez['use_setuptools'](**setup_args) -import setuptools -import pkg_resources - -# This does not (always?) update the default working set. We will -# do it. -for path in sys.path: - if path not in pkg_resources.working_set.entries: - pkg_resources.working_set.add_entry(path) - -###################################################################### -# Install buildout - -ws = pkg_resources.working_set - -setuptools_path = ws.find( - pkg_resources.Requirement.parse('setuptools')).location - -# Fix sys.path here as easy_install.pth added before PYTHONPATH -cmd = [sys.executable, '-c', - 'import sys; sys.path[0:0] = [%r]; ' % setuptools_path + - 'from setuptools.command.easy_install import main; main()', - '-mZqNxd', tmpeggs] - -find_links = os.environ.get( - 'bootstrap-testing-find-links', - options.find_links or - ('http://downloads.buildout.org/' - if options.accept_buildout_test_releases else None) - ) -if find_links: - cmd.extend(['-f', find_links]) - -requirement = 'zc.buildout' -version = options.buildout_version -if version is None and not options.accept_buildout_test_releases: - # Figure out the most recent final version of zc.buildout. - import setuptools.package_index - _final_parts = '*final-', '*final' - - def _final_version(parsed_version): - try: - return not parsed_version.is_prerelease - except AttributeError: - # Older setuptools - for part in parsed_version: - if (part[:1] == '*') and (part not in _final_parts): - return False - return True - - index = setuptools.package_index.PackageIndex( - search_path=[setuptools_path]) - if find_links: - index.add_find_links((find_links,)) - req = pkg_resources.Requirement.parse(requirement) - if index.obtain(req) is not None: - best = [] - bestv = None - for dist in index[req.project_name]: - distv = dist.parsed_version - if _final_version(distv): - if bestv is None or distv > bestv: - best = [dist] - bestv = distv - elif distv == bestv: - best.append(dist) - if best: - best.sort() - version = best[-1].version -if version: - requirement = '=='.join((requirement, version)) -cmd.append(requirement) - -import subprocess -if subprocess.call(cmd) != 0: - raise Exception( - "Failed to execute command:\n%s" % repr(cmd)[1:-1]) - -###################################################################### -# Import and run buildout - -ws.add_entry(tmpeggs) -ws.require(requirement) -import zc.buildout.buildout - -if not [a for a in args if '=' not in a]: - args.append('bootstrap') - -# if -c was provided, we push it back into args for buildout' main function -if options.config_file is not None: - args[0:0] = ['-c', options.config_file] - -zc.buildout.buildout.main(args) -shutil.rmtree(tmpeggs) @@ -1,13 +1,17 @@ -[nosetests] -nocapture=1 -cover-package=zope.proxy -cover-erase=1 -with-doctest=0 -where=src - -[aliases] -dev = develop easy_install zope.proxy[testing] -docs = easy_install zope.proxy[docs] +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/c-code +[bdist_wheel] +universal = 1 [zest.releaser] create-wheel = no + +[flake8] +doctests = 1 + +[check-manifest] +ignore = + .editorconfig + .meta.toml + docs/_build/html/_sources/* + docs/_build/doctest/* @@ -36,6 +36,7 @@ class optional_build_ext(build_ext): """This class subclasses build_ext and allows the building of C extensions to fail. """ + def run(self): try: build_ext.run(self) @@ -103,6 +104,7 @@ setup(name='zope.proxy', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Framework :: Zope :: 3', @@ -130,8 +132,9 @@ setup(name='zope.proxy', 'zope.testrunner', ], 'docs': [ - 'Sphinx<4', # Until repoze.sphinx.autointerface supports Sphinx 4.x we cannot use it + # Need < 4 until repoze.sphinx.autointerface supports Sphinx 4: + 'Sphinx < 4', 'repoze.sphinx.autointerface', ], }, -) + ) diff --git a/src/zope/proxy/__init__.py b/src/zope/proxy/__init__.py index 620e565..d17187a 100644 --- a/src/zope/proxy/__init__.py +++ b/src/zope/proxy/__init__.py @@ -16,7 +16,6 @@ import operator import os import pickle -import sys from zope.interface import moduleProvides from zope.proxy.interfaces import IProxyIntrospection @@ -24,6 +23,7 @@ from zope.proxy.interfaces import IProxyIntrospection moduleProvides(IProxyIntrospection) __all__ = tuple(IProxyIntrospection) + def ProxyIterator(p): yield p while isProxy(p): @@ -33,6 +33,7 @@ def ProxyIterator(p): _MARKER = object() + def _WrapperType_Lookup(type_, name): """ Looks up information in class dictionaries in MRO @@ -49,12 +50,14 @@ def _WrapperType_Lookup(type_, name): return res return _MARKER + def _get_wrapped(self): """ Helper method to access the wrapped object. """ return super(AbstractPyProxyBase, self).__getattribute__('_wrapped') + class _EmptyInterfaceDescriptor(object): """A descriptor for the attributes used on the class by the Python implementation of `zope.interface`. @@ -79,11 +82,13 @@ class _EmptyInterfaceDescriptor(object): raise StopIteration() next = __next__ + class _ProxyMetaclass(type): # The metaclass is applied after the class definition # for Py2/Py3 compatibility. __implemented__ = _EmptyInterfaceDescriptor() + class AbstractPyProxyBase(object): """ A reference implementation that cannot be instantiated. Most users @@ -116,10 +121,10 @@ class AbstractPyProxyBase(object): def __str__(self): return str(self._wrapped) - def __unicode__(self): - return unicode(self._wrapped) + def __unicode__(self): # pragma: no cover PY2 + return unicode(self._wrapped) # noqa: F821 undefined name - def __reduce__(self): # pragma: no cover (__reduce_ex__ prevents normal) + def __reduce__(self): # pragma: no cover (__reduce_ex__ prevents normal) raise pickle.PicklingError def __reduce_ex__(self, proto): @@ -146,7 +151,7 @@ class AbstractPyProxyBase(object): def __nonzero__(self): return bool(self._wrapped) - __bool__ = __nonzero__ # Python3 compat + __bool__ = __nonzero__ # Python3 compat def __hash__(self): return hash(self._wrapped) @@ -240,18 +245,19 @@ class AbstractPyProxyBase(object): del self._wrapped[key] def __iter__(self): - # This handles a custom __iter__ and generator support at the same time. + # This handles a custom __iter__ and generator support at the same + # time. return iter(self._wrapped) - def next(self): + def next(self): # pragma: no cover PY2 # Called when we wrap an iterator itself. return self._wrapped.next() - def __next__(self): # pragma: no cover Python3 + def __next__(self): return self._wrapped.__next__() # Python 2.7 won't let the C wrapper support __reversed__ :( - #def __reversed__(self): + # def __reversed__(self): # return reversed(self._wrapped) def __contains__(self, item): @@ -277,8 +283,8 @@ class AbstractPyProxyBase(object): def __int__(self): return int(self._wrapped) - def __long__(self): - return long(self._wrapped) + def __long__(self): # pragma: no cover PY2 + return long(self._wrapped) # noqa: F821 undefined name def __float__(self): return float(self._wrapped) @@ -293,9 +299,10 @@ class AbstractPyProxyBase(object): return operator.index(self._wrapped) # Numeric protocol: binary coercion - def __coerce__(self, other): - left, right = coerce(self._wrapped, other) - if left == self._wrapped and type(left) is type(self._wrapped): + def __coerce__(self, other): # pragma: no cover PY2 + left, right = coerce(self._wrapped, other) # noqa: F821 undefined name + if (left == self._wrapped + and type(left) is type(self._wrapped)): # noqa: E721 left = self return left, right @@ -312,11 +319,11 @@ class AbstractPyProxyBase(object): def __floordiv__(self, other): return self._wrapped // other - def __truediv__(self, other): # pragma: no cover + def __truediv__(self, other): # pragma: no cover # Only one of __truediv__ and __div__ is meaningful at any one time. return self._wrapped / other - def __div__(self, other): # pragma: no cover + def __div__(self, other): # pragma: no cover # Only one of __truediv__ and __div__ is meaningful at any one time. return self._wrapped / other @@ -343,11 +350,11 @@ class AbstractPyProxyBase(object): def __rfloordiv__(self, other): return other // self._wrapped - def __rtruediv__(self, other): # pragma: no cover + def __rtruediv__(self, other): # pragma: no cover # Only one of __rtruediv__ and __rdiv__ is meaningful at any one time. return other / self._wrapped - def __rdiv__(self, other): # pragma: no cover + def __rdiv__(self, other): # pragma: no cover # Only one of __rtruediv__ and __rdiv__ is meaningful at any one time. return other / self._wrapped @@ -361,7 +368,7 @@ class AbstractPyProxyBase(object): if modulus is None: return pow(other, self._wrapped) # We can't actually get here, because we can't lie about our type() - return pow(other, self._wrapped, modulus) # pragma: no cover + return pow(other, self._wrapped, modulus) # pragma: no cover # Numeric protocol: binary bitwise operators def __lshift__(self, other): @@ -407,12 +414,12 @@ class AbstractPyProxyBase(object): self._wrapped *= other return self - def __idiv__(self, other): # pragma: no cover + def __idiv__(self, other): # pragma: no cover # Only one of __itruediv__ and __idiv__ is meaningful at any one time. self._wrapped /= other return self - def __itruediv__(self, other): # pragma: no cover + def __itruediv__(self, other): # pragma: no cover # Only one of __itruediv__ and __idiv__ is meaningful at any one time. self._wrapped /= other return self @@ -448,14 +455,16 @@ class AbstractPyProxyBase(object): def __ipow__(self, other, modulus=None): if modulus is None: self._wrapped **= other - else: # pragma: no cover + else: # pragma: no cover # There is no syntax which triggers in-place pow w/ modulus self._wrapped = pow(self._wrapped, other, modulus) return self + AbstractPyProxyBase = _ProxyMetaclass(str('AbstractPyProxyBase'), (), dict(AbstractPyProxyBase.__dict__)) + class PyProxyBase(AbstractPyProxyBase): """Reference implementation. """ @@ -467,17 +476,20 @@ def py_getProxiedObject(obj): return obj._wrapped return obj + def py_setProxiedObject(obj, new_value): if not isinstance(obj, PyProxyBase): raise TypeError('Not a proxy') old, obj._wrapped = obj._wrapped, new_value return old + def py_isProxy(obj, klass=None): if klass is None: klass = PyProxyBase return isinstance(obj, klass) + def py_sameProxiedObjects(lhs, rhs): while isinstance(lhs, PyProxyBase): lhs = super(PyProxyBase, lhs).__getattribute__('_wrapped') @@ -485,6 +497,7 @@ def py_sameProxiedObjects(lhs, rhs): rhs = super(PyProxyBase, rhs).__getattribute__('_wrapped') return lhs is rhs + def py_queryProxy(obj, klass=None, default=None): if klass is None: klass = PyProxyBase @@ -494,36 +507,42 @@ def py_queryProxy(obj, klass=None, default=None): return obj return default + def py_queryInnerProxy(obj, klass=None, default=None): if klass is None: klass = PyProxyBase found = [] while obj is not None: if isinstance(obj, klass): - found.append(obj) # stack + found.append(obj) # stack obj = getattr(obj, '_wrapped', None) if found: return found[-1] return default + def py_removeAllProxies(obj): while isinstance(obj, PyProxyBase): obj = super(PyProxyBase, obj).__getattribute__('_wrapped') return obj + _c_available = False if 'PURE_PYTHON' not in os.environ: - try: + try: # pragma: no cover from zope.proxy._zope_proxy_proxy import ProxyBase as _c_available - except ImportError: # pragma: no cover + except ImportError: pass + class PyNonOverridable(object): "Deprecated, only for BWC." - def __init__(self, method_desc): # pragma: no cover PyPy + + def __init__(self, method_desc): # pragma: no cover PyPy self.desc = method_desc -if _c_available: + +if _c_available: # pragma: no cover # Python API: not used in this module from zope.proxy._zope_proxy_proxy import ProxyBase from zope.proxy._zope_proxy_proxy import getProxiedObject @@ -535,9 +554,9 @@ if _c_available: from zope.proxy._zope_proxy_proxy import removeAllProxies # API for proxy-using C extensions. - from zope.proxy._zope_proxy_proxy import _CAPI + from zope.proxy._zope_proxy_proxy import _CAPI # noqa: F401 unused -else: # pragma: no cover +else: # no C extension available, fall back ProxyBase = PyProxyBase getProxiedObject = py_getProxiedObject @@ -548,5 +567,6 @@ else: # pragma: no cover queryInnerProxy = py_queryInnerProxy removeAllProxies = py_removeAllProxies + def non_overridable(func): return property(lambda self: func.__get__(self)) diff --git a/src/zope/proxy/decorator.py b/src/zope/proxy/decorator.py index 9f2084f..9a53dfb 100644 --- a/src/zope/proxy/decorator.py +++ b/src/zope/proxy/decorator.py @@ -24,9 +24,11 @@ from zope.interface.declarations import getObjectSpecification from zope.interface.declarations import ObjectSpecification from zope.interface import providedBy + class DecoratorSpecificationDescriptor(ObjectSpecificationDescriptor): """Support for interface declarations on decorators """ + def __get__(self, inst, cls=None): if inst is None: return getObjectSpecification(cls) @@ -46,4 +48,3 @@ class SpecificationDecoratorBase(ProxyBase): """Base class for a proxy that provides additional interfaces.""" __providedBy__ = DecoratorSpecificationDescriptor() - diff --git a/src/zope/proxy/interfaces.py b/src/zope/proxy/interfaces.py index 71f113d..50a0547 100644 --- a/src/zope/proxy/interfaces.py +++ b/src/zope/proxy/interfaces.py @@ -16,6 +16,7 @@ from zope.interface import Interface + class IProxyIntrospection(Interface): """Provides methods for indentifying proxies and extracting proxied objects """ diff --git a/src/zope/proxy/tests/test_decorator.py b/src/zope/proxy/tests/test_decorator.py index 4003377..a2a27ee 100644 --- a/src/zope/proxy/tests/test_decorator.py +++ b/src/zope/proxy/tests/test_decorator.py @@ -29,10 +29,13 @@ class DecoratorSpecificationDescriptorTests(unittest.TestCase): from zope.interface import Interface from zope.interface import implementer from zope.interface import provider + class IContextFactory(Interface): pass + class IContext(Interface): pass + @provider(IContextFactory) @implementer(IContext) class Context(object): @@ -44,10 +47,13 @@ class DecoratorSpecificationDescriptorTests(unittest.TestCase): from zope.interface import Interface from zope.interface import implementer from zope.interface import provider + class IContextFactory(Interface): pass + class IContext(Interface): pass + @provider(IContextFactory) @implementer(IContext) class Context(object): @@ -60,10 +66,13 @@ class DecoratorSpecificationDescriptorTests(unittest.TestCase): from zope.interface import implementer from zope.interface import provider from zope.proxy import ProxyBase + class IContextFactory(Interface): pass + class IContext(Interface): pass + @provider(IContextFactory) @implementer(IContext) class Context(object): @@ -78,18 +87,24 @@ class DecoratorSpecificationDescriptorTests(unittest.TestCase): from zope.interface import implementer from zope.interface import provider from zope.proxy import ProxyBase + class IContextFactory(Interface): pass + class IContext(Interface): pass + @provider(IContextFactory) @implementer(IContext) class Context(object): pass + class IProxyFactory(Interface): pass + class IProxy(Interface): pass + @provider(IProxyFactory) @implementer(IProxy) class Proxy(ProxyBase): @@ -103,8 +118,10 @@ class DecoratorSpecificationDescriptorTests(unittest.TestCase): def test___set___not_allowed(self): from zope.interface import Interface from zope.interface import implementer + class IFoo(Interface): pass + @implementer(IFoo) class Foo(object): pass @@ -126,8 +143,10 @@ class SpecificationDecoratorBaseTests(unittest.TestCase): from zope.interface import Interface from zope.interface import implementer from zope.interface import providedBy + class IFoo(Interface): pass + @implementer(IFoo) class Foo(object): pass @@ -141,14 +160,17 @@ class SpecificationDecoratorBaseTests(unittest.TestCase): from zope.interface import Interface from zope.interface import implementer from zope.interface import providedBy + class IFoo(Interface): pass + @implementer(IFoo) class Foo(object): from_foo = 1 class IWrapper(Interface): pass + @implementer(IWrapper) class Proxy(self._getTargetClass()): pass @@ -157,7 +179,7 @@ class SpecificationDecoratorBaseTests(unittest.TestCase): proxy = Proxy(foo) self.assertEqual(proxy.from_foo, 1) - self.assertEqual(list(providedBy(proxy)), [IFoo,IWrapper]) + self.assertEqual(list(providedBy(proxy)), [IFoo, IWrapper]) def test_suite(): diff --git a/src/zope/proxy/tests/test_proxy.py b/src/zope/proxy/tests/test_proxy.py index c96492d..5d7124c 100644 --- a/src/zope/proxy/tests/test_proxy.py +++ b/src/zope/proxy/tests/test_proxy.py @@ -17,13 +17,15 @@ import unittest try: import zope.security -except ImportError: # pragma: no cover +except ImportError: # pragma: no cover _HAVE_ZOPE_SECURITY = False else: _HAVE_ZOPE_SECURITY = True + del zope.security from zope.proxy._compat import PY3 + class ModuleConformanceCase(unittest.TestCase): def test_module_conforms_to_IProxyIntrospection(self): @@ -39,6 +41,10 @@ class PyProxyBaseTestCase(unittest.TestCase): getslice = '__getitem__' if PY3 else '__getslice__' setslice = '__setitem__' if PY3 else '__setslice__' + # Avoid DeprecationWarning for assertRaisesRegexp on Python 3 while + # coping with Python 2 not having the Regex spelling variant + assertRaisesRegex = getattr(unittest.TestCase, 'assertRaisesRegex', + unittest.TestCase.assertRaisesRegexp) def _getTargetClass(self): from zope.proxy import PyProxyBase @@ -57,6 +63,7 @@ class PyProxyBaseTestCase(unittest.TestCase): class MyProxy(self._getTargetClass()): def __new__(cls, *args, **kwds): return super(MyProxy, cls).__new__(cls, *args, **kwds) + def __init__(self, *args, **kwds): super(MyProxy, self).__init__(*args, **kwds) o1 = object() @@ -115,30 +122,30 @@ class PyProxyBaseTestCase(unittest.TestCase): self.assertEqual(42.0, float(proxy)) @unittest.skipIf(PY3, "Gone in Py3") - def test___unicode__of_unicode(self): + def test___unicode__of_unicode(self): # pragma: no cover PY2 s = u'Hello, \u2603' proxy = self._makeOne(s) - self.assertEqual(unicode(proxy), s) + self.assertEqual(unicode(proxy), s) # noqa: F821 undef @unittest.skipIf(PY3, "Gone in Py3") - def test___unicode__of_custom_class(self): + def test___unicode__of_custom_class(self): # pragma: no cover PY2 class CustomClass(object): def __unicode__(self): return u'Hello, \u2603' cc = CustomClass() - self.assertEqual(unicode(cc), u'Hello, \u2603') + self.assertEqual(unicode(cc), u'Hello, \u2603') # noqa: F821 undef proxy = self._makeOne(cc) - self.assertEqual(unicode(proxy), u'Hello, \u2603') + self.assertEqual(unicode(proxy), u'Hello, \u2603') # noqa: F821 undef @unittest.skipIf(PY3, "Gone in Py3") - def test___unicode__of_custom_class_no_unicode(self): + def test___unicode__of_custom_class_no_unicode(self): # pragma: no cover class CustomClass(object): pass cc = CustomClass() - cc_unicode = unicode(cc) - self.assertEqual(type(cc_unicode), unicode) + cc_unicode = unicode(cc) # noqa: F821 undef + self.assertEqual(type(cc_unicode), unicode) # noqa: F821 undef proxy = self._makeOne(cc) - self.assertEqual(unicode(proxy), cc_unicode) + self.assertEqual(unicode(proxy), cc_unicode) # noqa: F821 undef def test___call__(self): def _foo(): @@ -147,7 +154,7 @@ class PyProxyBaseTestCase(unittest.TestCase): self.assertEqual(proxy(), 'FOO') @unittest.skipIf(PY3, "Gone in Py3") - def test_callable(self): + def test_callable(self): # pragma: no cover PY2 w = self._makeOne({}.get) self.assertTrue(callable(w)) @@ -164,14 +171,15 @@ class PyProxyBaseTestCase(unittest.TestCase): self.assertEqual(str(proxy), str(_foo)) @unittest.skipIf(PY3, "Gone in Py3") - def test___unicode__(self): + def test___unicode__(self): # pragma: no cover PY2 def _foo(): raise AssertionError("Not called") proxy = self._makeOne(_foo) - self.assertTrue(unicode(proxy).startswith('<function _foo')) + self.assertTrue( + unicode(proxy).startswith('<function _foo')) # noqa: F821 undef @unittest.skipIf(PY3, "No old-style classes in Python 3") - def test___reduce___via_pickling(self): + def test___reduce___via_pickling(self): # pragma: no cover PY2 import pickle # Proxies of old-style classes can't be pickled. @@ -246,6 +254,7 @@ class PyProxyBaseTestCase(unittest.TestCase): pass o = Foo() w = self._makeOne(o) + def _try(): return w.nonesuch self.assertRaises(AttributeError, _try) @@ -262,6 +271,7 @@ class PyProxyBaseTestCase(unittest.TestCase): class Proxy(self._getTargetClass()): def foo(self): raise AssertionError("Not called") + class Foo(object): def foo(self): return 'FOO' @@ -282,7 +292,8 @@ class PyProxyBaseTestCase(unittest.TestCase): bar = property( lambda s: s.__dict__.get('_bar'), lambda s, v: s.__dict__.__setitem__('_bar', v) - ) + ) + class Foo(object): pass o = Foo() @@ -297,6 +308,7 @@ class PyProxyBaseTestCase(unittest.TestCase): o = Foo() o.foo = 1 w = self._makeOne(o) + def _try(): del w._wrapped self.assertRaises(AttributeError, _try) @@ -311,10 +323,10 @@ class PyProxyBaseTestCase(unittest.TestCase): self.assertFalse('foo' in o.__dict__) def test___len__(self): - l = [] - w = self._makeOne(l) + l_ = [] + w = self._makeOne(l_) self.assertEqual(len(w), 0) - l.append(0) + l_.append(0) self.assertEqual(len(w), 1) def test___getitem_____setitem_____delitem__(self): @@ -324,6 +336,7 @@ class PyProxyBaseTestCase(unittest.TestCase): self.assertEqual(w[1], 'a') del w[1] self.assertRaises(KeyError, lambda: w[1]) + def del_w_1(): del w[1] self.assertRaises(KeyError, del_w_1) @@ -344,8 +357,9 @@ class PyProxyBaseTestCase(unittest.TestCase): def test___getitem__w_slice_against_derived_list(self): data = [1, 2] + class DerivedList(list): - def __getslice__(self, start, stop): + def __getslice__(self, start, stop): # pragma: no cover PY2 return list.__getslice__(self, start, stop) pList = self._makeOne(DerivedList(data)) @@ -357,21 +371,23 @@ class PyProxyBaseTestCase(unittest.TestCase): def test___getitem__w_slice_against_class_w_custom___getslice__(self): import sys test = self + class Slicer(object): def __len__(self): return 2 - def __getslice__(self, start, end): + def __getslice__(self, start, end): # pragma: no cover PY2 return (start, end) - def __getitem__(self, a_slice): # pragma: no cover + def __getitem__(self, a_slice): test.assertTrue(PY3) # On Python 3, we basically just return what the test expects. # Mostly that's the computed indices (yay!) but there are # a few special cases. indices = a_slice.indices(len(self)) - return (indices[0] if a_slice.start != -3 else -1, - indices[-1] if a_slice.stop is not None else sys.maxsize) + return ( + indices[0] if a_slice.start != -3 else -1, + indices[-1] if a_slice.stop is not None else sys.maxsize) pSlicer = self._makeOne(Slicer()) self.assertEqual(pSlicer[:1][0], 0) @@ -385,34 +401,36 @@ class PyProxyBaseTestCase(unittest.TestCase): def test___getslice___dne_uses_getitem(self): class Missing(Exception): pass + class Get(object): def __getitem__(self, x): raise Missing('__getitem__') target = Get() proxy = self._makeOne(target) - with self.assertRaisesRegexp(Missing, - '__getitem__'): + with self.assertRaisesRegex(Missing, '__getitem__'): proxy[1:2] def test___getslice___error_propagates(self): test = self + class Missing(Exception): pass + class Get(object): - def __getitem__(self, x): # pragma: no cover (only py3) + def __getitem__(self, x): test.assertTrue(PY3) raise Missing('__getitem__') - def __getslice__(self, start, stop): + + def __getslice__(self, start, stop): # pragma: no cover PY2 raise Missing("__getslice__") target = Get() proxy = self._makeOne(target) - with self.assertRaisesRegexp(Missing, - self.getslice): + with self.assertRaisesRegex(Missing, self.getslice): proxy[1:2] def test___setslice___against_list(self): - # Lists have special slicing bahvior for assignment as well. + # Lists have special slicing behavior for assignment as well. pList = self._makeOne([1, 2]) pList[-1:] = [3, 4] self.assertEqual(pList, [1, 3, 4]) @@ -441,31 +459,31 @@ class PyProxyBaseTestCase(unittest.TestCase): def test___setslice___error_propagates(self): class Missing(Exception): pass + class Set(object): def __setitem__(self, k, v): - raise Missing('__setitem__') # pragma: no cover (only py3) - def __setslice__(self, start, stop, value): + raise Missing('__setitem__') + + def __setslice__(self, start, stop, value): # pragma: no cover PY2 raise Missing("__setslice__") target = Set() proxy = self._makeOne(target) - with self.assertRaisesRegexp(Missing, - self.setslice): + with self.assertRaisesRegex(Missing, self.setslice): proxy[1:2] = 1 def test___setslice___dne_uses_setitem(self): class Missing(Exception): pass + class Set(object): def __setitem__(self, k, v): raise Missing('__setitem__') target = Set() proxy = self._makeOne(target) - with self.assertRaisesRegexp(Missing, - '__setitem__'): + with self.assertRaisesRegex(Missing, '__setitem__'): proxy[1:2] = 1 - def test___iter___w_wrapped_iterable(self): a = [1, 2, 3] b = [] @@ -489,6 +507,7 @@ class PyProxyBaseTestCase(unittest.TestCase): class MyIter(object): def __iter__(self): return self + def __next__(self): raise AssertionError("Not called") next = __next__ @@ -506,6 +525,7 @@ class PyProxyBaseTestCase(unittest.TestCase): def __init__(self, test, data): self.test = test self.data = data + def __iter__(self): return self.test._makeOne(iter(self.data)) @@ -516,7 +536,7 @@ class PyProxyBaseTestCase(unittest.TestCase): self.assertEqual(a, b) # Python 2.7 won't let the C wrapper support __reversed__ :( - #def test___reversed__(self): + # def test___reversed__(self): # w = self._makeOne([0, 1, 2, 3]) # self.assertEqual(list(reversed(w)), [3, 2, 1, 0]) @@ -543,8 +563,8 @@ class PyProxyBaseTestCase(unittest.TestCase): "float(x)", "complex(x)", ] - if not PY3: # long is gone in Python 3 - ops.append("long(x)") + if not PY3: # long is gone in Python 3 + ops.append("long(x)") # pragma: no cover PY2 return ops def test_unops(self): @@ -560,15 +580,15 @@ class PyProxyBaseTestCase(unittest.TestCase): # unops that don't return a proxy funcs = (lambda x: not x,) if not PY3: - funcs += (oct, hex) + funcs += (oct, hex) # pragma: no cover PY2 for func in funcs: self.assertEqual(func(self._makeOne(100)), func(100)) binops = [ "x+y", "x-y", "x*y", "x/y", "x//y", "x%y", "divmod(x, y)", - "x**y", #"pow(x,y,3)" (RHS coercion not supported w/ modulus) + "x**y", # "pow(x,y,3)" (RHS coercion not supported w/ modulus) "x<<y", "x>>y", "x&y", "x|y", "x^y", - ] + ] def test_binops(self): for expr in self.binops: @@ -631,72 +651,72 @@ class PyProxyBaseTestCase(unittest.TestCase): self.assertEqual(pa, 20) @unittest.skipIf(PY3, "No coercion in Py3") - def test_coerce(self): + def test_coerce(self): # pragma: no cover PY2 # Before 2.3, coerce() of two proxies returns them unchanged x = self._makeOne(1) y = self._makeOne(2) - a, b = coerce(x, y) + a, b = coerce(x, y) # noqa: F821 undefined name self.assertTrue(a is x and b is y) x = self._makeOne(1) y = self._makeOne(2.1) - a, b = coerce(x, y) - self.assertTrue(isinstance(a, float)) # a was coerced + a, b = coerce(x, y) # noqa: F821 undefined name + self.assertTrue(isinstance(a, float)) # a was coerced self.assertFalse(a is x) self.assertEqual(a, float(x)) self.assertTrue(b is y) x = self._makeOne(1.1) y = self._makeOne(2) - a, b = coerce(x, y) + a, b = coerce(x, y) # noqa: F821 undefined name self.assertTrue(a is x) - self.assertTrue(isinstance(b, float)) # b was coerced + self.assertTrue(isinstance(b, float)) # b was coerced self.assertFalse(b is y) self.assertEqual(b, float(y)) x = self._makeOne(1) y = 2 - a, b = coerce(x, y) - self.assertTrue(a is x) # neither was coerced + a, b = coerce(x, y) # noqa: F821 undefined name + self.assertTrue(a is x) # neither was coerced self.assertTrue(b is y) x = self._makeOne(1) y = 2.1 - a, b = coerce(x, y) - self.assertTrue(isinstance(a, float)) # a was coerced + a, b = coerce(x, y) # noqa: F821 undefined name + self.assertTrue(isinstance(a, float)) # a was coerced self.assertFalse(a is x) self.assertEqual(a, float(x)) self.assertTrue(b is y) x = self._makeOne(1.1) y = 2 - a, b = coerce(x, y) + a, b = coerce(x, y) # noqa: F821 undefined name self.assertTrue(a is x) - self.assertTrue(isinstance(b, float)) # b was coerced + self.assertTrue(isinstance(b, float)) # b was coerced self.assertFalse(b is y) - self.assertEqual(b,float(y)) + self.assertEqual(b, float(y)) x = 1 y = self._makeOne(2) - a, b = coerce(x, y) - self.assertTrue(a is x) # neither was coerced + a, b = coerce(x, y) # noqa: F821 undefined name + self.assertTrue(a is x) # neither was coerced self.assertTrue(b is y) x = 1.1 y = self._makeOne(2) - a, b = coerce(x, y) + a, b = coerce(x, y) # noqa: F821 undefined name self.assertTrue(a is x) - self.assertTrue(isinstance(b, float)) # b was coerced + self.assertTrue(isinstance(b, float)) # b was coerced self.assertFalse(b is y) - self.assertEqual(b, float(y)) + self.assertEqual(b, float(y)) x = 1 y = self._makeOne(2.1) - a, b = coerce(x, y) - self.assertTrue(isinstance(a, float)) # a was coerced + a, b = coerce(x, y) # noqa: F821 undefined name + self.assertTrue(isinstance(a, float)) # a was coerced self.assertFalse(a is x) - self.assertEqual(a, float(x)) + self.assertEqual(a, float(x)) self.assertTrue(b is y) def test___class__(self): @@ -709,11 +729,13 @@ class PyProxyBaseTestCase(unittest.TestCase): class Descriptor(object): value = None instance = None + def __set__(self, instance, value): self.value = value self.instance = instance descriptor = Descriptor() + class Proxy(self._getTargetClass()): attr = descriptor @@ -741,6 +763,7 @@ class PyProxyBaseTestCase(unittest.TestCase): descriptor = Descriptor() descriptor.value = "descriptor value" + class Proxy(self._getTargetClass()): attr = descriptor @@ -765,14 +788,14 @@ class PyProxyBaseTestCase(unittest.TestCase): self.assertRaises(AttributeError, setattr, proxy, 'attr', 42) self.assertEqual(proxy.attr, "constant value") - def _check_wrapping_builtin_returns_correct_provided_by(self, proxy_class, builtin_type): + def _check_wrapping_builtin_returns_correct_provided_by( + self, proxy_class, builtin_type): # We get the __implemented__ (fallback) of the type, not our own from zope.interface import Interface from zope.interface import classImplements from zope.interface import classImplementsOnly from zope.interface import implementedBy from zope.interface import providedBy - from zope.interface import implementedBy # Set up the builtin interface class IFoo(Interface): @@ -794,31 +817,37 @@ class PyProxyBaseTestCase(unittest.TestCase): self.assertTrue(IFoo in list(provided_instance)) proxy_type = proxy_class(builtin_type) - from zope.interface.declarations import BuiltinImplementationSpecifications + from zope.interface.declarations import \ + BuiltinImplementationSpecifications self.assertIn(proxy_type, BuiltinImplementationSpecifications) - self.assertIsNot(BuiltinImplementationSpecifications.get(proxy_type, self), - self) + self.assertIsNot( + BuiltinImplementationSpecifications.get(proxy_type, self), + self) provided_type = implementedBy(proxy_type) self.assertTrue(IFoo in list(provided_type)) finally: classImplementsOnly(builtin_type, *impl_before) def test_wrapping_builtin_type_returns_correct_provided_by(self): - self._check_wrapping_builtin_returns_correct_provided_by(self._getTargetClass(), list) + self._check_wrapping_builtin_returns_correct_provided_by( + self._getTargetClass(), list) - def _check_wrapping_builtin_with_subclass_returns_correct_provided_by(self, builtin_type): + def _check_wrapping_builtin_with_subclass_returns_correct_provided_by( + self, builtin_type): class Proxy(self._getTargetClass()): pass - self._check_wrapping_builtin_returns_correct_provided_by(Proxy, builtin_type) + self._check_wrapping_builtin_returns_correct_provided_by( + Proxy, builtin_type) # Our new class did not gain an __implemented__ attribute, unless we're # the pure-python version - if hasattr(Proxy, '__implemented__'): # pragma: no cover + if hasattr(Proxy, '__implemented__'): # pragma: no cover from zope.proxy import PyProxyBase self.assertTrue(self._getTargetClass() is PyProxyBase) def test_wrapping_builtin_with_subclass_returns_correct_provided_by(self): - self._check_wrapping_builtin_with_subclass_returns_correct_provided_by(list) + self._check_wrapping_builtin_with_subclass_returns_correct_provided_by( + list) def test_method_in_proxy_subclass(self): class Proxy(self._getTargetClass()): @@ -836,19 +865,22 @@ class PyProxyBaseTestCase(unittest.TestCase): proxy = self._makeOne("14") self.assertEqual(14, int(proxy)) + class ProxyBaseTestCase(PyProxyBaseTestCase): def _getTargetClass(self): from zope.proxy import ProxyBase return ProxyBase + class Test_py__module(unittest.TestCase): # Historically, proxying __module__ has been troublesome, # especially when subclasses of the proxy class are involved; # there was also a discrepancy between the C and Python implementations - # in that the C implementation only failed Test_subclass__module:test__module__in_instance, + # in that the C implementation only failed + # Test_subclass__module:test__module__in_instance, # whereas the Python version failed every test. - # See https://github.com/zopefoundation/zopetoolkit/pull/2#issuecomment-106075153 + # See https://github.com/zopefoundation/zopetoolkit/pull/2#issuecomment-106075153 # noqa: E501 line too long # and https://github.com/zopefoundation/zope.proxy/pull/8 def _getTargetClass(self): @@ -856,7 +888,6 @@ class Test_py__module(unittest.TestCase): return PyProxyBase def _makeProxy(self, obj): - from zope.proxy import PyProxyBase return self._getTargetClass()(obj) def _check_module(self, obj, expected): @@ -892,6 +923,7 @@ class Test_py__module(unittest.TestCase): class Module(object): def __init__(self): self.__module__ = __name__ + def __eq__(self, other): return self.__module__ == other.__module__ @@ -904,23 +936,28 @@ class Test_py__module(unittest.TestCase): self.assertEqual(module, self._makeProxy(module)) self.assertEqual(self._makeProxy(module), module) + class Test__module(Test_py__module): def _getTargetClass(self): from zope.proxy import ProxyBase return ProxyBase + class Test_py_subclass__module(Test_py__module): def _getTargetClass(self): - class ProxySubclass(super(Test_py_subclass__module, self)._getTargetClass()): + class ProxySubclass( + super(Test_py_subclass__module, self)._getTargetClass()): pass return ProxySubclass + class Test_subclass__module(Test__module): def _getTargetClass(self): - class ProxySubclass(super(Test_subclass__module, self)._getTargetClass()): + class ProxySubclass( + super(Test_subclass__module, self)._getTargetClass()): pass return ProxySubclass @@ -956,6 +993,7 @@ class Test_py_getProxiedObject(unittest.TestCase): proxy2 = self._makeProxy(proxy) self.assertTrue(self._callFUT(proxy2) is proxy) + class Test_getProxiedObject(Test_py_getProxiedObject): def _callFUT(self, *args): @@ -1041,6 +1079,7 @@ class Test_py_isProxy(unittest.TestCase): def test_proxy_no_class(self): class P1(self._proxyClass()): pass + class C(object): pass c = C() @@ -1050,6 +1089,7 @@ class Test_py_isProxy(unittest.TestCase): def test_proxy_w_same_class(self): class P1(self._proxyClass()): pass + class C(object): pass c = C() @@ -1059,8 +1099,10 @@ class Test_py_isProxy(unittest.TestCase): def test_proxy_w_other_class(self): class P1(self._proxyClass()): pass + class P2(self._proxyClass()): pass + class C(object): pass c = C() @@ -1126,6 +1168,7 @@ class Test_py_sameProxiedObjects(unittest.TestCase): def test_proxies_w_same_bare(self): _mP = self._makeProxy + class C(object): pass c1 = C() @@ -1133,6 +1176,7 @@ class Test_py_sameProxiedObjects(unittest.TestCase): def test_proxies_w_other_bare(self): _mP = self._makeProxy + class C(object): pass c1 = C() @@ -1142,6 +1186,7 @@ class Test_py_sameProxiedObjects(unittest.TestCase): def test_nested_proxy_and_same_bare(self): _mP = self._makeProxy + class C(object): pass c1 = C() @@ -1150,6 +1195,7 @@ class Test_py_sameProxiedObjects(unittest.TestCase): def test_nested_proxy_and_other_bare(self): _mP = self._makeProxy + class C(object): pass c1 = C() @@ -1172,6 +1218,7 @@ class Test_py_sameProxiedObjects(unittest.TestCase): proxy2 = self._makeSecurityProxy(c2) self.assertFalse(self._callFUT(proxy1, proxy2)) + class Test_sameProxiedObjects(Test_py_sameProxiedObjects): def _callFUT(self, *args): @@ -1188,6 +1235,7 @@ class Test_sameProxiedObjects(Test_py_sameProxiedObjects): checker = Checker({}) return Proxy(obj, checker) + class Test_py_queryProxy(unittest.TestCase): def _callFUT(self, *args): @@ -1207,6 +1255,7 @@ class Test_py_queryProxy(unittest.TestCase): def test_proxy_no_class(self): class P1(self._proxyClass()): pass + class C(object): pass c = C() @@ -1216,6 +1265,7 @@ class Test_py_queryProxy(unittest.TestCase): def test_proxy_w_same_class(self): class P1(self._proxyClass()): pass + class C(object): pass c = C() @@ -1226,8 +1276,10 @@ class Test_py_queryProxy(unittest.TestCase): def test_proxy_w_other_class(self): class P1(self._proxyClass()): pass + class P2(self._proxyClass()): pass + class C(object): pass c = C() @@ -1238,8 +1290,10 @@ class Test_py_queryProxy(unittest.TestCase): def test_proxy_w_base_class(self): class P1(self._proxyClass()): pass + class P2(self._proxyClass()): pass + class C(object): pass c = C() @@ -1278,6 +1332,7 @@ class Test_py_queryInnerProxy(unittest.TestCase): def test_proxy_no_class(self): class P1(self._proxyClass()): pass + class C(object): pass c = C() @@ -1287,6 +1342,7 @@ class Test_py_queryInnerProxy(unittest.TestCase): def test_proxy_w_same_class(self): class P1(self._proxyClass()): pass + class C(object): pass c = C() @@ -1297,8 +1353,10 @@ class Test_py_queryInnerProxy(unittest.TestCase): def test_nested_proxy(self): class P1(self._proxyClass()): pass + class P2(self._proxyClass()): pass + class C(object): pass c = C() @@ -1312,8 +1370,10 @@ class Test_py_queryInnerProxy(unittest.TestCase): def test_re_nested_proxy(self): class P1(self._proxyClass()): pass + class P2(self._proxyClass()): pass + class C(object): pass c = C() @@ -1381,6 +1441,7 @@ class Test_py_removeAllProxies(unittest.TestCase): proxy = self._makeSecurityProxy(c) self.assertIs(self._callFUT(proxy), c) + class Test_removeAllProxies(Test_py_removeAllProxies): def _callFUT(self, *args): @@ -1396,6 +1457,7 @@ class Test_removeAllProxies(Test_py_removeAllProxies): checker = object() return Proxy(obj, checker) + class Test_ProxyIterator(unittest.TestCase): def _callFUT(self, *args): @@ -1410,6 +1472,7 @@ class Test_ProxyIterator(unittest.TestCase): def test_w_simple_proxy(self): from zope.proxy import ProxyBase + class C(object): pass c = C() @@ -1418,6 +1481,7 @@ class Test_ProxyIterator(unittest.TestCase): def test_w_nested_proxies(self): from zope.proxy import ProxyBase + class C(object): pass c = C() @@ -1434,15 +1498,19 @@ class Test_nonOverridable(unittest.TestCase): def test_it(self): from zope.proxy import ProxyBase from zope.proxy import non_overridable + class Proxy(ProxyBase): def who(self): raise AssertionError("Not called") + @non_overridable def what(self): return 'PROXY' + class Foo(object): def who(self): return 'FOO' + def what(self): return 'FOO' p0 = ProxyBase(Foo()) @@ -1457,6 +1525,7 @@ class TestEmptyInterfaceDescriptor(unittest.TestCase): def _makeOne(self): from zope.proxy import _EmptyInterfaceDescriptor + class It(object): feature = _EmptyInterfaceDescriptor() return It() @@ -1500,7 +1569,7 @@ class Comparable(object): def __gt__(self, other): return not self.__le__(other) - def __repr__(self): # pragma: no cover + def __repr__(self): # pragma: no cover return "<Comparable: %r>" % self.value @@ -1,43 +1,67 @@ +# Generated from: +# https://github.com/zopefoundation/meta/tree/master/config/c-code [tox] +minversion = 3.18 envlist = - py27,py27-pure,py35,py36,py36-pure,py37,py38,pypy,coverage,docs + lint + py27,py27-pure + py35,py35-pure + py36,py36-pure + py37,py37-pure + py38,py38-pure + py39,py39-pure + pypy + pypy3 + docs + coverage [testenv] +usedevelop = true deps = - .[test,docs] + # Until repoze.sphinx.autointerface supports Sphinx 4.x we cannot use it: + Sphinx < 4 +setenv = + pure: PURE_PYTHON=1 + !pure-!pypy-!pypy3: PURE_PYTHON=0 commands = - zope-testrunner --test-path=src - sphinx-build -b doctest -d {envdir}/doctrees docs {envdir}/doctest + zope-testrunner --test-path=src {posargs:-vc} + !py27-!pypy: sphinx-build -b doctest -d {envdir}/.cache/doctrees docs {envdir}/.cache/doctest +extras = + test + docs [testenv:coverage] -usedevelop = true -basepython = - python2.7 -commands = - coverage run -m zope.testrunner --test-path=src [] - coverage run -a -m sphinx -b doctest -d {envdir}/.cache/doctrees docs {envdir}/.cache/doctest - coverage report --fail-under=100 +basepython = python3 +allowlist_externals = + mkdir deps = - {[testenv]deps} coverage +setenv = + PURE_PYTHON=1 +commands = + mkdir -p {toxinidir}/parts/htmlcov + coverage run -m zope.testrunner --test-path=src {posargs:-vc} + coverage html -i + coverage report -i -m --fail-under=99 + +[testenv:lint] +basepython = python3 +skip_install = true +deps = + flake8 + check-manifest + check-python-versions +commands = + flake8 src setup.py + check-manifest + check-python-versions [testenv:docs] -basepython = - python2.7 +basepython = python3 +skip_install = false +# Until repoze.sphinx.autointerface supports Sphinx 4.x we cannot use it: +deps = Sphinx < 4 +commands_pre = commands = sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest -deps = - .[test,docs] - -[testenv:py27-pure] -basepython = - python2.7 -setenv = - PURE_PYTHON = 1 - -[testenv:py36-pure] -basepython = - python3.6 -setenv = - PURE_PYTHON = 1 |