pax_global_header00006660000000000000000000000064146212135010014505gustar00rootroot0000000000000052 comment=4e589d295ff430c4325a53504344216c106ade5a zope.interface-6.4/000077500000000000000000000000001462121350100142725ustar00rootroot00000000000000zope.interface-6.4/.coveragerc000066400000000000000000000011141462121350100164100ustar00rootroot00000000000000# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/c-code [run] source = zope.interface # 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 = except ImportError: if __name__ == '__main__': pragma: no cover pragma: nocover raise AssertionError raise NotImplementedError raise unittest.Skip self.fail\( [html] directory = parts/htmlcov zope.interface-6.4/.editorconfig000066400000000000000000000020031462121350100167420ustar00rootroot00000000000000# 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 zope.interface-6.4/.github/000077500000000000000000000000001462121350100156325ustar00rootroot00000000000000zope.interface-6.4/.github/workflows/000077500000000000000000000000001462121350100176675ustar00rootroot00000000000000zope.interface-6.4/.github/workflows/tests.yml000066400000000000000000000534751462121350100215720ustar00rootroot00000000000000# 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: -O3 -pipe CXXFLAGS: -O3 -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 }} if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name strategy: fail-fast: false matrix: python-version: - "pypy-3.10" - "3.7" - "3.8" - "3.9" - "3.10" - "3.11" - "3.12" - "3.13.0-alpha - 3.13.0" os: [ubuntu-latest, macos-latest, windows-latest] exclude: - os: macos-latest python-version: "3.7" - os: macos-latest python-version: "pypy-3.10" include: - python-version: "3.7" os: macos-12 steps: - name: checkout uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 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 (default) id: pip-cache-default if: ${{ !startsWith(runner.os, 'Windows') }} run: | echo "dir=$(pip cache dir)" >>$GITHUB_OUTPUT - name: Get pip cache dir (Windows) id: pip-cache-windows if: ${{ startsWith(runner.os, 'Windows') }} run: | echo "dir=$(pip cache dir)" >> $Env:GITHUB_OUTPUT - name: pip cache (default) uses: actions/cache@v4 if: ${{ !startsWith(runner.os, 'Windows') }} with: path: ${{ steps.pip-cache-default.outputs.dir }} key: ${{ runner.os }}-pip-${{ matrix.python-version }} restore-keys: | ${{ runner.os }}-pip- - name: pip cache (Windows) uses: actions/cache@v4 if: ${{ startsWith(runner.os, 'Windows') }} with: path: ${{ steps.pip-cache-windows.outputs.dir }} key: ${{ runner.os }}-pip-${{ matrix.python-version }} restore-keys: | ${{ runner.os }}-pip- - name: Install Build Dependencies (3.13.0-alpha - 3.13.0) if: matrix.python-version == '3.13.0-alpha - 3.13.0' run: | pip install -U pip pip install -U setuptools wheel twine - name: Install Build Dependencies if: matrix.python-version != '3.13.0-alpha - 3.13.0' run: | pip install -U pip pip install -U setuptools wheel twine - name: Build zope.interface (macOS x86_64, Python 3.8+) if: > startsWith(runner.os, 'Mac') && !(startsWith(matrix.python-version, 'pypy') || matrix.python-version == '3.7') env: MACOSX_DEPLOYMENT_TARGET: 10.9 _PYTHON_HOST_PLATFORM: macosx-10.9-x86_64 ARCHFLAGS: -arch x86_64 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 - name: Build zope.interface (macOS arm64, Python 3.8+) if: > startsWith(runner.os, 'Mac') && !(startsWith(matrix.python-version, 'pypy') || matrix.python-version == '3.7') env: MACOSX_DEPLOYMENT_TARGET: 11.0 _PYTHON_HOST_PLATFORM: macosx-11.0-arm64 ARCHFLAGS: -arch arm64 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 - name: Build zope.interface (all other versions) if: > !startsWith(runner.os, 'Mac') || startsWith(matrix.python-version, 'pypy') || matrix.python-version == '3.7' 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 - name: Install zope.interface and dependencies (3.13.0-alpha - 3.13.0) if: matrix.python-version == '3.13.0-alpha - 3.13.0' run: | # Install to collect dependencies into the (pip) cache. # Use "--pre" here because dependencies with support for this future # Python release may only be available as pre-releases pip install --pre .[test] - name: Install zope.interface and dependencies if: matrix.python-version != '3.13.0-alpha - 3.13.0' run: | # Install to collect dependencies into the (pip) cache. pip install .[test] - name: Check zope.interface build run: | ls -l dist twine check dist/* - name: Upload zope.interface wheel (macOS x86_64) if: > startsWith(runner.os, 'Mac') uses: actions/upload-artifact@v4 with: name: zope.interface-${{ runner.os }}-${{ matrix.python-version }}.whl path: dist/*x86_64.whl - name: Upload zope.interface wheel (macOS arm64) if: > startsWith(runner.os, 'Mac') && !(startsWith(matrix.python-version, 'pypy') || matrix.python-version == '3.7') uses: actions/upload-artifact@v4 with: # The arm64 wheel is uploaded with a different name just so it can be # manually downloaded when desired. The wheel itself *cannot* be tested # on the GHA runner, which uses x86_64 architecture. name: zope.interface-${{ runner.os }}-${{ matrix.python-version }}-arm64.whl path: dist/*arm64.whl - name: Upload zope.interface wheel (all other platforms) if: > !startsWith(runner.os, 'Mac') uses: actions/upload-artifact@v4 with: name: zope.interface-${{ 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') && !startsWith(matrix.python-version, 'pypy') && !startsWith(matrix.python-version, '3.13.0-alpha - 3.13.0') env: TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} run: | twine upload --skip-existing dist/* test: needs: build-package runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: python-version: - "pypy-3.10" - "3.7" - "3.8" - "3.9" - "3.10" - "3.11" - "3.12" - "3.13.0-alpha - 3.13.0" os: [ubuntu-latest, macos-latest, windows-latest] exclude: - os: macos-latest python-version: "3.7" - os: macos-latest python-version: "pypy-3.10" include: - python-version: "3.7" os: macos-12 steps: - name: checkout uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 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 (default) id: pip-cache-default if: ${{ !startsWith(runner.os, 'Windows') }} run: | echo "dir=$(pip cache dir)" >>$GITHUB_OUTPUT - name: Get pip cache dir (Windows) id: pip-cache-windows if: ${{ startsWith(runner.os, 'Windows') }} run: | echo "dir=$(pip cache dir)" >> $Env:GITHUB_OUTPUT - name: pip cache (default) uses: actions/cache@v4 if: ${{ !startsWith(runner.os, 'Windows') }} with: path: ${{ steps.pip-cache-default.outputs.dir }} key: ${{ runner.os }}-pip-${{ matrix.python-version }} restore-keys: | ${{ runner.os }}-pip- - name: pip cache (Windows) uses: actions/cache@v4 if: ${{ startsWith(runner.os, 'Windows') }} with: path: ${{ steps.pip-cache-windows.outputs.dir }} key: ${{ runner.os }}-pip-${{ matrix.python-version }} restore-keys: | ${{ runner.os }}-pip- - name: Download zope.interface wheel uses: actions/download-artifact@v4 with: name: zope.interface-${{ runner.os }}-${{ matrix.python-version }}.whl path: dist/ - name: Install zope.interface # ``python -m unittest discover`` only works with editable # installs, so we have to duplicate some work and can't # install the built wheel. (zope.testrunner # works fine with non-editable installs.) run: | pip install -U wheel pip install -U --no-binary :all: coverage # 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.interface-*whl -d src pip install -U -e .[test] - name: Run tests with C extensions if: ${{ !startsWith(matrix.python-version, 'pypy') }} run: | coverage run -p -m unittest discover -s src - name: Run tests without C extensions run: | coverage run -p -m unittest discover -s src env: PURE_PYTHON: 1 - name: Report Coverage run: | coverage combine coverage report --ignore-errors --show-missing - 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-latest 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-latest] steps: - name: checkout uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 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 (default) id: pip-cache-default if: ${{ !startsWith(runner.os, 'Windows') }} run: | echo "dir=$(pip cache dir)" >>$GITHUB_OUTPUT - name: Get pip cache dir (Windows) id: pip-cache-windows if: ${{ startsWith(runner.os, 'Windows') }} run: | echo "dir=$(pip cache dir)" >> $Env:GITHUB_OUTPUT - name: pip cache (default) uses: actions/cache@v4 if: ${{ !startsWith(runner.os, 'Windows') }} with: path: ${{ steps.pip-cache-default.outputs.dir }} key: ${{ runner.os }}-pip-${{ matrix.python-version }} restore-keys: | ${{ runner.os }}-pip- - name: pip cache (Windows) uses: actions/cache@v4 if: ${{ startsWith(runner.os, 'Windows') }} with: path: ${{ steps.pip-cache-windows.outputs.dir }} key: ${{ runner.os }}-pip-${{ matrix.python-version }} restore-keys: | ${{ runner.os }}-pip- - name: Download zope.interface wheel uses: actions/download-artifact@v4 with: name: zope.interface-${{ runner.os }}-${{ matrix.python-version }}.whl path: dist/ - name: Install zope.interface run: | pip install -U wheel pip install -U coverage pip install -U "`ls dist/zope.interface-*.whl`[docs]" - 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-latest] steps: - name: checkout uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 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 (default) id: pip-cache-default if: ${{ !startsWith(runner.os, 'Windows') }} run: | echo "dir=$(pip cache dir)" >>$GITHUB_OUTPUT - name: Get pip cache dir (Windows) id: pip-cache-windows if: ${{ startsWith(runner.os, 'Windows') }} run: | echo "dir=$(pip cache dir)" >> $Env:GITHUB_OUTPUT - name: pip cache (default) uses: actions/cache@v4 if: ${{ !startsWith(runner.os, 'Windows') }} with: path: ${{ steps.pip-cache-default.outputs.dir }} key: ${{ runner.os }}-pip-${{ matrix.python-version }} restore-keys: | ${{ runner.os }}-pip- - name: pip cache (Windows) uses: actions/cache@v4 if: ${{ startsWith(runner.os, 'Windows') }} with: path: ${{ steps.pip-cache-windows.outputs.dir }} key: ${{ runner.os }}-pip-${{ matrix.python-version }} restore-keys: | ${{ runner.os }}-pip- - name: Download zope.interface wheel uses: actions/download-artifact@v4 with: name: zope.interface-${{ runner.os }}-${{ matrix.python-version }}.whl path: dist/ - name: Install zope.interface run: | pip install -U pip pip install -U wheel pip install -U `ls dist/zope.interface-*`[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.interface -f parseable -r n manylinux: runs-on: ubuntu-latest if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name # We use a regular Python matrix entry to share as much code as possible. strategy: matrix: python-version: ["3.9"] image: [manylinux2014_x86_64, manylinux2014_i686, manylinux2014_aarch64] steps: - name: checkout uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 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 (default) id: pip-cache-default if: ${{ !startsWith(runner.os, 'Windows') }} run: | echo "dir=$(pip cache dir)" >>$GITHUB_OUTPUT - name: Get pip cache dir (Windows) id: pip-cache-windows if: ${{ startsWith(runner.os, 'Windows') }} run: | echo "dir=$(pip cache dir)" >> $Env:GITHUB_OUTPUT - name: pip cache (default) uses: actions/cache@v4 if: ${{ !startsWith(runner.os, 'Windows') }} with: path: ${{ steps.pip-cache-default.outputs.dir }} key: ${{ runner.os }}-pip_manylinux-${{ matrix.image }}-${{ matrix.python-version }} restore-keys: | ${{ runner.os }}-pip- - name: pip cache (Windows) uses: actions/cache@v4 if: ${{ startsWith(runner.os, 'Windows') }} with: path: ${{ steps.pip-cache-windows.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.interface (x86_64) if: matrix.image == 'manylinux2014_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 env: DOCKER_IMAGE: quay.io/pypa/${{ matrix.image }} run: | bash .manylinux.sh - name: Build zope.interface (i686) if: matrix.image == 'manylinux2014_i686' env: DOCKER_IMAGE: quay.io/pypa/${{ matrix.image }} PRE_CMD: linux32 run: | bash .manylinux.sh - name: Build zope.interface (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.interface wheels uses: actions/upload-artifact@v4 with: path: wheelhouse/*whl name: manylinux_${{ matrix.image }}_wheels.zip - name: Restore pip cache permissions run: sudo chown -R $(whoami) ${{ steps.pip-cache-default.outputs.dir }} - name: Publish package to PyPI uses: pypa/gh-action-pypi-publish@release/v1 if: > github.event_name == 'push' && startsWith(github.ref, 'refs/tags') with: user: __token__ password: ${{ secrets.TWINE_PASSWORD }} skip_existing: true packages_dir: wheelhouse/ zope.interface-6.4/.gitignore000066400000000000000000000005341462121350100162640ustar00rootroot00000000000000# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/c-code *.dll *.egg-info/ *.profraw *.pyc *.pyo *.so .coverage .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 testing.log var/ zope.interface-6.4/.manylinux-install.sh000077500000000000000000000043231462121350100204010ustar00rootroot00000000000000#!/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 # We need some libraries because we build wheels from scratch: yum -y install libffi-devel tox_env_map() { case $1 in *"cp313"*) echo 'py313';; *"cp37"*) echo 'py37';; *"cp38"*) echo 'py38';; *"cp39"*) echo 'py39';; *"cp310"*) echo 'py310';; *"cp311"*) echo 'py311';; *"cp312"*) echo 'py312';; *) echo 'py';; esac } # Compile wheels for PYBIN in /opt/python/*/bin; do if \ [[ "${PYBIN}" == *"cp313"* ]] || \ [[ "${PYBIN}" == *"cp311"* ]] || \ [[ "${PYBIN}" == *"cp312"* ]] || \ [[ "${PYBIN}" == *"cp37"* ]] || \ [[ "${PYBIN}" == *"cp38"* ]] || \ [[ "${PYBIN}" == *"cp39"* ]] || \ [[ "${PYBIN}" == *"cp310"* ]] ; then if [[ "${PYBIN}" == *"cp313"* ]] ; then "${PYBIN}/pip" install --pre -e /io/ "${PYBIN}/pip" wheel /io/ --pre -w wheelhouse/ else "${PYBIN}/pip" install -e /io/ "${PYBIN}/pip" wheel /io/ -w wheelhouse/ fi if [ `uname -m` == 'aarch64' ]; then cd /io/ ${PYBIN}/pip install tox TOXENV=$(tox_env_map "${PYBIN}") ${PYBIN}/tox -e ${TOXENV} cd .. fi rm -rf /io/build /io/*.egg-info fi done # Bundle external shared libraries into the wheels for whl in wheelhouse/zope.interface*.whl; do auditwheel repair "$whl" -w /io/wheelhouse/ done zope.interface-6.4/.manylinux.sh000077500000000000000000000007751462121350100167440ustar00rootroot00000000000000#!/usr/bin/env bash # Generated from: # https://github.com/zopefoundation/meta/tree/master/config/c-code set -e -x # 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 zope.interface-6.4/.meta.toml000066400000000000000000000045611462121350100162010ustar00rootroot00000000000000# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/c-code [meta] template = "c-code" commit-id = "d00a7004" [python] with-appveyor = true with-pypy = true with-sphinx-doctests = true with-windows = true with-future-python = true with-docs = true with-macos = false [tox] use-flake8 = true testenv-commands = [ "coverage run -p -m unittest discover -s src {posargs}", ] testenv-deps = [ "py37: urllib3 < 2", ] testenv-setenv = [ "ZOPE_INTERFACE_STRICT_IRO=1", ] coverage-command = "coverage combine" coverage-additional = [ "depends = py37,py37-pure,py38,py38-pure,py39,py39-pure,py310,py310-pure,py311,py311-pure,pypy,pypy3,docs", "parallel_show_output = true", ] [coverage] fail-under = 99 [manifest] additional-rules = [ "include *.yaml", "include *.cmd", "include *.sh", "include *.yml", "include .coveragerc", "recursive-include benchmarks *.py", "recursive-include docs *.bat", "recursive-include docs *.py", "recursive-include docs *.rst", "recursive-include docs Makefile", ] [check-manifest] additional-ignores = [ "docs/_build/doctest/output.txt", "docs/_build/html/_sources/*", "docs/_build/html/_sources/api/*", ] [github-actions] additional-install = [ "- name: Install zope.interface", " # ``python -m unittest discover`` only works with editable", " # installs, so we have to duplicate some work and can't", " # install the built wheel. (zope.testrunner", " # works fine with non-editable installs.)", " run: |", " pip install -U wheel", " pip install -U --no-binary :all: coverage", " # 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.interface-*whl -d src", " pip install -U -e .[test]", "- name: Run tests with C extensions", " if: ${{ !startsWith(matrix.python-version, 'pypy') }}", " run: |", " coverage run -p -m unittest discover -s src", "- name: Run tests without C extensions", " run: |", " coverage run -p -m unittest discover -s src", " env:", " PURE_PYTHON: 1", "- name: Report Coverage", " run: |", " coverage combine", " coverage report --ignore-errors --show-missing", ] zope.interface-6.4/.readthedocs.yaml000066400000000000000000000012301462121350100175150ustar00rootroot00000000000000# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/c-code # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # We recommend specifying your dependencies to enable reproducible builds: # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - requirements: docs/requirements.txt - method: pip path: . zope.interface-6.4/CHANGES.rst000066400000000000000000001143671462121350100161100ustar00rootroot00000000000000========= Changes ========= 6.4 (2024-05-15) ================ - Adjust for incompatible changes in Python 3.13b1. (`#292 `) - Build windows wheels on GHA. 6.3 (2024-04-12) ================ - Add preliminary support for Python 3.13 as of 3.13a6. 6.2 (2024-02-16) ================ - Add preliminary support for Python 3.13 as of 3.13a3. - Add support to use the pipe (``|``) syntax for ``typing.Union``. (`#280 `_) 6.1 (2023-10-05) ================ - Build Linux binary wheels for Python 3.12. - Add support for Python 3.12. - Fix building of the docs for non-final versions. 6.0 (2023-03-17) ================ - Build Linux binary wheels for Python 3.11. - Drop support for Python 2.7, 3.5, 3.6. - Fix test deprecation warning on Python 3.11. - Add preliminary support for Python 3.12 as of 3.12a5. - Drop: + `zope.interface.implements` + `zope.interface.implementsOnly` + `zope.interface.classProvides` 5.5.2 (2022-11-17) ================== - Add support for building arm64 wheels on macOS. 5.5.1 (2022-11-03) ================== - Add support for final Python 3.11 release. 5.5.0 (2022-10-10) ================== - Add support for Python 3.10 and 3.11 (as of 3.11.0rc2). - Add missing Trove classifier showing support for Python 3.9. - Add some more entries to ``zope.interface.interfaces.__all__``. - Disable unsafe math optimizations in C code. See `pull request 262 `_. 5.4.0 (2021-04-15) ================== - Make the C implementation of the ``__providedBy__`` descriptor stop ignoring all errors raised when accessing the instance's ``__provides__``. Now it behaves like the Python version and only catches ``AttributeError``. The previous behaviour could lead to crashing the interpreter in cases of recursion and errors. See `issue 239 `_. - Update the ``repr()`` and ``str()`` of various objects to be shorter and more informative. In many cases, the ``repr()`` is now something that can be evaluated to produce an equal object. For example, what was previously printed as ```` is now shown as ``classImplements(list, IMutableSequence, IIterable)``. See `issue 236 `_. - Make ``Declaration.__add__`` (as in ``implementedBy(Cls) + ISomething``) try harder to preserve a consistent resolution order when the two arguments share overlapping pieces of the interface inheritance hierarchy. Previously, the right hand side was always put at the end of the resolution order, which could easily produce invalid orders. See `issue 193 `_. 5.3.0 (2020-03-21) ================== - No changes from 5.3.0a1 5.3.0a1 (2021-03-18) ==================== - Improve the repr of ``zope.interface.Provides`` to remove ambiguity about what is being provided. This is especially helpful diagnosing IRO issues. - Allow subclasses of ``BaseAdapterRegistry`` (including ``AdapterRegistry`` and ``VerifyingAdapterRegistry``) to have control over the data structures. This allows persistent implementations such as those based on ZODB to choose more scalable options (e.g., BTrees instead of dicts). See `issue 224 `_. - Fix a reference counting issue in ``BaseAdapterRegistry`` that could lead to references to interfaces being kept around even when all utilities/adapters/subscribers providing that interface have been removed. This is mostly an issue for persistent implementations. Note that this only corrects the issue moving forward, it does not solve any already corrupted reference counts. See `issue 227 `_. - Add the method ``BaseAdapterRegistry.rebuild()``. This can be used to fix the reference counting issue mentioned above, as well as to update the data structures when custom data types have changed. - Add the interface method ``IAdapterRegistry.subscribed()`` and implementation ``BaseAdapterRegistry.subscribed()`` for querying directly registered subscribers. See `issue 230 `_. - Add the maintenance method ``Components.rebuildUtilityRegistryFromLocalCache()``. Most users will not need this, but it can be useful if the ``Components.utilities`` registry is suspected to be out of sync with the ``Components`` object itself (this might happen to persistent ``Components`` implementations in the face of bugs). - Fix the ``Provides`` and ``ClassProvides`` descriptors to stop allowing redundant interfaces (those already implemented by the underlying class or meta class) to produce an inconsistent resolution order. This is similar to the change in ``@implementer`` in 5.1.0, and resolves inconsistent resolution orders with ``zope.proxy`` and ``zope.location``. See `issue 207 `_. 5.2.0 (2020-11-05) ================== - Add documentation section ``Persistency and Equality`` (`#218 `_). - Create arm64 wheels. - Add support for Python 3.9. 5.1.2 (2020-10-01) ================== - Make sure to call each invariant only once when validating invariants. Previously, invariants could be called multiple times because when an invariant is defined in an interface, it's found by in all interfaces inheriting from that interface. See `pull request 215 `_. 5.1.1 (2020-09-30) ================== - Fix the method definitions of ``IAdapterRegistry.subscribe``, ``subscriptions`` and ``subscribers``. Previously, they all were defined to accept a ``name`` keyword argument, but subscribers have no names and the implementation of that interface did not accept that argument. See `issue 208 `_. - Fix a potential reference leak in the C optimizations. Previously, applications that dynamically created unique ``Specification`` objects (e.g., used ``@implementer`` on dynamic classes) could notice a growth of small objects over time leading to increased garbage collection times. See `issue 216 `_. .. caution:: This leak could prevent interfaces used as the bases of other interfaces from being garbage collected. Those interfaces will now be collected. One way in which this would manifest was that ``weakref.ref`` objects (and things built upon them, like ``Weak[Key|Value]Dictionary``) would continue to have access to the original object even if there were no other visible references to Python and the original object *should* have been collected. This could be especially problematic for the ``WeakKeyDictionary`` when combined with dynamic or local (created in the scope of a function) interfaces, since interfaces are hashed based just on their name and module name. See the linked issue for an example of a resulting ``KeyError``. Note that such potential errors are not new, they are just once again a possibility. 5.1.0 (2020-04-08) ================== - Make ``@implementer(*iface)`` and ``classImplements(cls, *iface)`` ignore redundant interfaces. If the class already implements an interface through inheritance, it is no longer redeclared specifically for *cls*. This solves many instances of inconsistent resolution orders, while still allowing the interface to be declared for readability and maintenance purposes. See `issue 199 `_. - Remove all bare ``except:`` statements. Previously, when accessing special attributes such as ``__provides__``, ``__providedBy__``, ``__class__`` and ``__conform__``, this package wrapped such access in a bare ``except:`` statement, meaning that many errors could pass silently; typically this would result in a fallback path being taken and sometimes (like with ``providedBy()``) the result would be non-sensical. This is especially true when those attributes are implemented with descriptors. Now, only ``AttributeError`` is caught. This makes errors more obvious. Obviously, this means that some exceptions will be propagated differently than before. In particular, ``RuntimeError`` raised by Acquisition in the case of circular containment will now be propagated. Previously, when adapting such a broken object, a ``TypeError`` would be the common result, but now it will be a more informative ``RuntimeError``. In addition, ZODB errors like ``POSKeyError`` could now be propagated where previously they would ignored by this package. See `issue 200 `_. - Require that the second argument (*bases*) to ``InterfaceClass`` is a tuple. This only matters when directly using ``InterfaceClass`` to create new interfaces dynamically. Previously, an individual interface was allowed, but did not work correctly. Now it is consistent with ``type`` and requires a tuple. - Let interfaces define custom ``__adapt__`` methods. This implements the other side of the :pep:`246` adaptation protocol: objects being adapted could already implement ``__conform__`` if they know about the interface, and now interfaces can implement ``__adapt__`` if they know about particular objects. There is no performance penalty for interfaces that do not supply custom ``__adapt__`` methods. This includes the ability to add new methods, or override existing interface methods using the new ``@interfacemethod`` decorator. See `issue 3 `_. - Make the internal singleton object returned by APIs like ``implementedBy`` and ``directlyProvidedBy`` for objects that implement or provide no interfaces more immutable. Previously an internal cache could be mutated. See `issue 204 `_. 5.0.2 (2020-03-30) ================== - Ensure that objects that implement no interfaces (such as direct subclasses of ``object``) still include ``Interface`` itself in their ``__iro___`` and ``__sro___``. This fixes adapter registry lookups for such objects when the adapter is registered for ``Interface``. See `issue 197 `_. 5.0.1 (2020-03-21) ================== - Ensure the resolution order for ``InterfaceClass`` is consistent. See `issue 192 `_. - Ensure the resolution order for ``collections.OrderedDict`` is consistent on CPython 2. (It was already consistent on Python 3 and PyPy). - Fix the handling of the ``ZOPE_INTERFACE_STRICT_IRO`` environment variable. Previously, ``ZOPE_INTERFACE_STRICT_RO`` was read, in contrast with the documentation. See `issue 194 `_. 5.0.0 (2020-03-19) ================== - Make an internal singleton object returned by APIs like ``implementedBy`` and ``directlyProvidedBy`` immutable. Previously, it was fully mutable and allowed changing its ``__bases___``. That could potentially lead to wrong results in pathological corner cases. See `issue 158 `_. - Support the ``PURE_PYTHON`` environment variable at runtime instead of just at wheel build time. A value of 0 forces the C extensions to be used (even on PyPy) failing if they aren't present. Any other value forces the Python implementation to be used, ignoring the C extensions. See `PR 151 `_. - Cache the result of ``__hash__`` method in ``InterfaceClass`` as a speed optimization. The method is called very often (i.e several hundred thousand times during Plone 5.2 startup). Because the hash value never changes it can be cached. This improves test performance from 0.614s down to 0.575s (1.07x faster). In a real world Plone case a reindex index came down from 402s to 320s (1.26x faster). See `PR 156 `_. - Change the C classes ``SpecificationBase`` and its subclass ``ClassProvidesBase`` to store implementation attributes in their structures instead of their instance dictionaries. This eliminates the use of an undocumented private C API function, and helps make some instances require less memory. See `PR 154 `_. - Reduce memory usage in other ways based on observations of usage patterns in Zope (3) and Plone code bases. - Specifications with no dependents are common (more than 50%) so avoid allocating a ``WeakKeyDictionary`` unless we need it. - Likewise, tagged values are relatively rare, so don't allocate a dictionary to hold them until they are used. - Use ``__slots___`` or the C equivalent ``tp_members`` in more common places. Note that this removes the ability to set arbitrary instance variables on certain objects. See `PR 155 `_. The changes in this release resulted in a 7% memory reduction after loading about 6,000 modules that define about 2,200 interfaces. .. caution:: Details of many private attributes have changed, and external use of those private attributes may break. In particular, the lifetime and default value of ``_v_attrs`` has changed. - Remove support for hashing uninitialized interfaces. This could only be done by subclassing ``InterfaceClass``. This has generated a warning since it was first added in 2011 (3.6.5). Please call the ``InterfaceClass`` constructor or otherwise set the appropriate fields in your subclass before attempting to hash or sort it. See `issue 157 `_. - Remove unneeded override of the ``__hash__`` method from ``zope.interface.declarations.Implements``. Watching a reindex index process in ZCatalog with on a Py-Spy after 10k samples the time for ``.adapter._lookup`` was reduced from 27.5s to 18.8s (~1.5x faster). Overall reindex index time shrunk from 369s to 293s (1.26x faster). See `PR 161 `_. - Make the Python implementation closer to the C implementation by ignoring all exceptions, not just ``AttributeError``, during (parts of) interface adaptation. See `issue 163 `_. - Micro-optimization in ``.adapter._lookup`` , ``.adapter._lookupAll`` and ``.adapter._subscriptions``: By loading ``components.get`` into a local variable before entering the loop a bytcode "LOAD_FAST 0 (components)" in the loop can be eliminated. In Plone, while running all tests, average speedup of the "owntime" of ``_lookup`` is ~5x. See `PR 167 `_. - Add ``__all__`` declarations to all modules. This helps tools that do auto-completion and documentation and results in less cluttered results. Wildcard ("*") are not recommended and may be affected. See `issue 153 `_. - Fix ``verifyClass`` and ``verifyObject`` for builtin types like ``dict`` that have methods taking an optional, unnamed argument with no default value like ``dict.pop``. On PyPy3, the verification is strict, but on PyPy2 (as on all versions of CPython) those methods cannot be verified and are ignored. See `issue 118 `_. - Update the common interfaces ``IEnumerableMapping``, ``IExtendedReadMapping``, ``IExtendedWriteMapping``, ``IReadSequence`` and ``IUniqueMemberWriteSequence`` to no longer require methods that were removed from Python 3 on Python 3, such as ``__setslice___``. Now, ``dict``, ``list`` and ``tuple`` properly verify as ``IFullMapping``, ``ISequence`` and ``IReadSequence,`` respectively on all versions of Python. - Add human-readable ``__str___`` and ``__repr___`` to ``Attribute`` and ``Method``. These contain the name of the defining interface and the attribute. For methods, it also includes the signature. - Change the error strings raised by ``verifyObject`` and ``verifyClass``. They now include more human-readable information and exclude extraneous lines and spaces. See `issue 170 `_. .. caution:: This will break consumers (such as doctests) that depended on the exact error messages. - Make ``verifyObject`` and ``verifyClass`` report all errors, if the candidate object has multiple detectable violations. Previously they reported only the first error. See `issue `_. Like the above, this will break consumers depending on the exact output of error messages if more than one error is present. - Add ``zope.interface.common.collections``, ``zope.interface.common.numbers``, and ``zope.interface.common.io``. These modules define interfaces based on the ABCs defined in the standard library ``collections.abc``, ``numbers`` and ``io`` modules, respectively. Importing these modules will make the standard library concrete classes that are registered with those ABCs declare the appropriate interface. See `issue 138 `_. - Add ``zope.interface.common.builtins``. This module defines interfaces of common builtin types, such as ``ITextString`` and ``IByteString``, ``IDict``, etc. These interfaces extend the appropriate interfaces from ``collections`` and ``numbers``, and the standard library classes implement them after importing this module. This is intended as a replacement for third-party packages like `dolmen.builtins `_. See `issue 138 `_. - Make ``providedBy()`` and ``implementedBy()`` respect ``super`` objects. For instance, if class ``Derived`` implements ``IDerived`` and extends ``Base`` which in turn implements ``IBase``, then ``providedBy(super(Derived, derived))`` will return ``[IBase]``. Previously it would have returned ``[IDerived]`` (in general, it would previously have returned whatever would have been returned without ``super``). Along with this change, adapter registries will unpack ``super`` objects into their ``__self___`` before passing it to the factory. Together, this means that ``component.getAdapter(super(Derived, self), ITarget)`` is now meaningful. See `issue 11 `_. - Fix a potential interpreter crash in the low-level adapter registry lookup functions. See issue 11. - Adopt Python's standard `C3 resolution order `_ to compute the ``__iro__`` and ``__sro__`` of interfaces, with tweaks to support additional cases that are common in interfaces but disallowed for Python classes. Previously, an ad-hoc ordering that made no particular guarantees was used. This has many beneficial properties, including the fact that base interface and base classes tend to appear near the end of the resolution order instead of the beginning. The resolution order in general should be more predictable and consistent. .. caution:: In some cases, especially with complex interface inheritance trees or when manually providing or implementing interfaces, the resulting IRO may be quite different. This may affect adapter lookup. The C3 order enforces some constraints in order to be able to guarantee a sensible ordering. Older versions of zope.interface did not impose similar constraints, so it was possible to create interfaces and declarations that are inconsistent with the C3 constraints. In that event, zope.interface will still produce a resolution order equal to the old order, but it won't be guaranteed to be fully C3 compliant. In the future, strict enforcement of C3 order may be the default. A set of environment variables and module constants allows controlling several aspects of this new behaviour. It is possible to request warnings about inconsistent resolution orders encountered, and even to forbid them. Differences between the C3 resolution order and the previous order can be logged, and, in extreme cases, the previous order can still be used (this ability will be removed in the future). For details, see the documentation for ``zope.interface.ro``. - Make inherited tagged values in interfaces respect the resolution order (``__iro__``), as method and attribute lookup does. Previously tagged values could give inconsistent results. See `issue 190 `_. - Add ``getDirectTaggedValue`` (and related methods) to interfaces to allow accessing tagged values irrespective of inheritance. See `issue 190 `_. - Ensure that ``Interface`` is always the last item in the ``__iro__`` and ``__sro__``. This is usually the case, but if classes that do not implement any interfaces are part of a class inheritance hierarchy, ``Interface`` could be assigned too high a priority. See `issue 8 `_. - Implement sorting, equality, and hashing in C for ``Interface`` objects. In micro benchmarks, this makes those operations 40% to 80% faster. This translates to a 20% speed up in querying adapters. Note that this changes certain implementation details. In particular, ``InterfaceClass`` now has a non-default metaclass, and it is enforced that ``__module__`` in instances of ``InterfaceClass`` is read-only. See `PR 183 `_. 4.7.2 (2020-03-10) ================== - Remove deprecated use of setuptools features. See `issue 30 `_. 4.7.1 (2019-11-11) ================== - Use Python 3 syntax in the documentation. See `issue 119 `_. 4.7.0 (2019-11-11) ================== - Drop support for Python 3.4. - Change ``queryTaggedValue``, ``getTaggedValue``, ``getTaggedValueTags`` in interfaces. They now include inherited values by following ``__bases__``. See `PR 144 `_. .. caution:: This may be a breaking change. - Add support for Python 3.8. 4.6.0 (2018-10-23) ================== - Add support for Python 3.7 - Fix ``verifyObject`` for class objects with staticmethods on Python 3. See `issue 126 `_. 4.5.0 (2018-04-19) ================== - Drop support for 3.3, avoid accidental dependence breakage via setup.py. See `PR 110 `_. - Allow registering and unregistering instance methods as listeners. See `issue 12 `_ and `PR 102 `_. - Synchronize and simplify zope/__init__.py. See `issue 114 `_ 4.4.3 (2017-09-22) ================== - Avoid exceptions when the ``__annotations__`` attribute is added to interface definitions with Python 3.x type hints. See `issue 98 `_. - Fix the possibility of a rare crash in the C extension when deallocating items. See `issue 100 `_. 4.4.2 (2017-06-14) ================== - Fix a regression storing ``zope.component.persistentregistry.PersistentRegistry`` instances. See `issue 85 `_. - Fix a regression that could lead to the utility registration cache of ``Components`` getting out of sync. See `issue 93 `_. 4.4.1 (2017-05-13) ================== - Simplify the caching of utility-registration data. In addition to simplification, avoids spurious test failures when checking for leaks in tests with persistent registries. See `pull 84 `_. - Raise ``ValueError`` when non-text names are passed to adapter registry methods: prevents corruption of lookup caches. 4.4.0 (2017-04-21) ================== - Avoid a warning from the C compiler. (https://github.com/zopefoundation/zope.interface/issues/71) - Add support for Python 3.6. 4.3.3 (2016-12-13) ================== - Correct typos and ReST formatting errors in documentation. - Add API documentation for the adapter registry. - Ensure that the ``LICENSE.txt`` file is included in built wheels. - Fix C optimizations broken on Py3k. See the Python bug at: http://bugs.python.org/issue15657 (https://github.com/zopefoundation/zope.interface/issues/60) 4.3.2 (2016-09-05) ================== - Fix equality testing of ``implementedBy`` objects and proxies. (https://github.com/zopefoundation/zope.interface/issues/55) 4.3.1 (2016-08-31) ================== - Support Components subclasses that are not hashable. (https://github.com/zopefoundation/zope.interface/issues/53) 4.3.0 (2016-08-31) ================== - Add the ability to sort the objects returned by ``implementedBy``. This is compatible with the way interface classes sort so they can be used together in ordered containers like BTrees. (https://github.com/zopefoundation/zope.interface/issues/42) - Make ``setuptools`` a hard dependency of ``setup.py``. (https://github.com/zopefoundation/zope.interface/issues/13) - Change a linear algorithm (O(n)) in ``Components.registerUtility`` and ``Components.unregisterUtility`` into a dictionary lookup (O(1)) for hashable components. This substantially improves the time taken to manipulate utilities in large registries at the cost of some additional memory usage. (https://github.com/zopefoundation/zope.interface/issues/46) 4.2.0 (2016-06-10) ================== - Add support for Python 3.5 - Drop support for Python 2.6 and 3.2. 4.1.3 (2015-10-05) ================== - Fix installation without a C compiler on Python 3.5 (https://github.com/zopefoundation/zope.interface/issues/24). 4.1.2 (2014-12-27) ================== - Add support for PyPy3. - Remove unittest assertions deprecated in Python3.x. - Add ``zope.interface.document.asReStructuredText``, which formats the generated text for an interface using ReST double-backtick markers. 4.1.1 (2014-03-19) ================== - Add support for Python 3.4. 4.1.0 (2014-02-05) ================== - Update ``boostrap.py`` to version 2.2. - Add ``@named(name)`` declaration, that specifies the component name, so it does not have to be passed in during registration. 4.0.5 (2013-02-28) ================== - Fix a bug where a decorated method caused false positive failures on ``verifyClass()``. 4.0.4 (2013-02-21) ================== - Fix a bug that was revealed by porting zope.traversing. During a loop, the loop body modified a weakref dict causing a ``RuntimeError`` error. 4.0.3 (2012-12-31) ================== - Fleshed out PyPI Trove classifiers. 4.0.2 (2012-11-21) ================== - Add support for Python 3.3. - Restored ability to install the package in the absence of ``setuptools``. - LP #1055223: Fix test which depended on dictionary order and failed randomly in Python 3.3. 4.0.1 (2012-05-22) ================== - Drop explicit ``DeprecationWarnings`` for "class advice" APIS (these APIs are still deprecated under Python 2.x, and still raise an exception under Python 3.x, but no longer cause a warning to be emitted under Python 2.x). 4.0.0 (2012-05-16) ================== - Automated build of Sphinx HTML docs and running doctest snippets via tox. - Deprecate the "class advice" APIs from ``zope.interface.declarations``: ``implements``, ``implementsOnly``, and ``classProvides``. In their place, prefer the equivalent class decorators: ``@implementer``, ``@implementer_only``, and ``@provider``. Code which uses the deprecated APIs will not work as expected under Py3k. - Remove use of '2to3' and associated fixers when installing under Py3k. The code is now in a "compatible subset" which supports Python 2.6, 2.7, and 3.2, including PyPy 1.8 (the version compatible with the 2.7 language spec). - Drop explicit support for Python 2.4 / 2.5 / 3.1. - Add support for PyPy. - Add support for continuous integration using ``tox`` and ``jenkins``. - Add 'setup.py dev' alias (runs ``setup.py develop`` plus installs ``nose`` and ``coverage``). - Add 'setup.py docs' alias (installs ``Sphinx`` and dependencies). - Replace all unittest coverage previously accomplished via doctests with unittests. The doctests have been moved into a ``docs`` section, managed as a Sphinx collection. - LP #910987: Ensure that the semantics of the ``lookup`` method of ``zope.interface.adapter.LookupBase`` are the same in both the C and Python implementations. - LP #900906: Avoid exceptions due to tne new ``__qualname__`` attribute added in Python 3.3 (see PEP 3155 for rationale). Thanks to Antoine Pitrou for the patch. 3.8.0 (2011-09-22) ================== - New module ``zope.interface.registry``. This is code moved from ``zope.component.registry`` which implements a basic nonperistent component registry as ``zope.interface.registry.Components``. This class was moved from ``zope.component`` to make porting systems (such as Pyramid) that rely only on a basic component registry to Python 3 possible without needing to port the entirety of the ``zope.component`` package. Backwards compatibility import shims have been left behind in ``zope.component``, so this change will not break any existing code. - New ``tests_require`` dependency: ``zope.event`` to test events sent by Components implementation. The ``zope.interface`` package does not have a hard dependency on ``zope.event``, but if ``zope.event`` is importable, it will send component registration events when methods of an instance of ``zope.interface.registry.Components`` are called. - New interfaces added to support ``zope.interface.registry.Components`` addition: ``ComponentLookupError``, ``Invalid``, ``IObjectEvent``, ``ObjectEvent``, ``IComponentLookup``, ``IRegistration``, ``IUtilityRegistration``, ``IAdapterRegistration``, ``ISubscriptionAdapterRegistration``, ``IHandlerRegistration``, ``IRegistrationEvent``, ``RegistrationEvent``, ``IRegistered``, ``Registered``, ``IUnregistered``, ``Unregistered``, ``IComponentRegistry``, and ``IComponents``. - No longer Python 2.4 compatible (tested under 2.5, 2.6, 2.7, and 3.2). 3.7.0 (2011-08-13) ================== - Move changes from 3.6.2 - 3.6.5 to a new 3.7.x release line. 3.6.7 (2011-08-20) ================== - Fix sporadic failures on x86-64 platforms in tests of rich comparisons of interfaces. 3.6.6 (2011-08-13) ================== - LP #570942: Now correctly compare interfaces from different modules but with the same names. N.B.: This is a less intrusive / destabilizing fix than the one applied in 3.6.3: we only fix the underlying cmp-alike function, rather than adding the other "rich comparison" functions. - Revert to software as released with 3.6.1 for "stable" 3.6 release branch. 3.6.5 (2011-08-11) ================== - LP #811792: work around buggy behavior in some subclasses of ``zope.interface.interface.InterfaceClass``, which invoke ``__hash__`` before initializing ``__module__`` and ``__name__``. The workaround returns a fixed constant hash in such cases, and issues a ``UserWarning``. - LP #804832: Under PyPy, ``zope.interface`` should not build its C extension. Also, prevent attempting to build it under Jython. - Add a tox.ini for easier xplatform testing. - Fix testing deprecation warnings issued when tested under Py3K. 3.6.4 (2011-07-04) ================== - LP 804951: InterfaceClass instances were unhashable under Python 3.x. 3.6.3 (2011-05-26) ================== - LP #570942: Now correctly compare interfaces from different modules but with the same names. 3.6.2 (2011-05-17) ================== - Moved detailed documentation out-of-line from PyPI page, linking instead to http://docs.zope.org/zope.interface . - Fixes for small issues when running tests under Python 3.2 using ``zope.testrunner``. - LP # 675064: Specify return value type for C optimizations module init under Python 3: undeclared value caused warnings, and segfaults on some 64 bit architectures. - setup.py now raises RuntimeError if you don't have Distutils installed when running under Python 3. 3.6.1 (2010-05-03) ================== - A non-ASCII character in the changelog made 3.6.0 uninstallable on Python 3 systems with another default encoding than UTF-8. - Fix compiler warnings under GCC 4.3.3. 3.6.0 (2010-04-29) ================== - LP #185974: Clear the cache used by ``Specificaton.get`` inside ``Specification.changed``. Thanks to Jacob Holm for the patch. - Add support for Python 3.1. Contributors: Lennart Regebro Martin v Loewis Thomas Lotze Wolfgang Schnerring The 3.1 support is completely backwards compatible. However, the implements syntax used under Python 2.X does not work under 3.X, since it depends on how metaclasses are implemented and this has changed. Instead it now supports a decorator syntax (also under Python 2.X):: class Foo: implements(IFoo) ... can now also be written:: @implementer(IFoo): class Foo: ... There are 2to3 fixers available to do this change automatically in the zope.fixers package. - Python 2.3 is no longer supported. 3.5.4 (2009-12-23) ================== - Use the standard Python doctest module instead of zope.testing.doctest, which has been deprecated. 3.5.3 (2009-12-08) ================== - Fix an edge case: make providedBy() work when a class has '__provides__' in its __slots__ (see http://thread.gmane.org/gmane.comp.web.zope.devel/22490) 3.5.2 (2009-07-01) ================== - BaseAdapterRegistry.unregister, unsubscribe: Remove empty portions of the data structures when something is removed. This avoids leaving references to global objects (interfaces) that may be slated for removal from the calling application. 3.5.1 (2009-03-18) ================== - verifyObject: use getattr instead of hasattr to test for object attributes in order to let exceptions other than AttributeError raised by properties propagate to the caller - Add Sphinx-based documentation building to the package buildout configuration. Use the ``bin/docs`` command after buildout. - Improve package description a bit. Unify changelog entries formatting. - Change package's mailing list address to zope-dev at zope.org as zope3-dev at zope.org is now retired. 3.5.0 (2008-10-26) ================== - Fix declaration of _zope_interface_coptimizations, it's not a top level package. - Add a DocTestSuite for odd.py module, so their tests are run. - Allow to bootstrap on Jython. - Fix https://bugs.launchpad.net/zope3/3.3/+bug/98388: ISpecification was missing a declaration for __iro__. - Add optional code optimizations support, which allows the building of C code optimizations to fail (Jython). - Replace `_flatten` with a non-recursive implementation, effectively making it 3x faster. 3.4.1 (2007-10-02) ================== - Fix a setup bug that prevented installation from source on systems without setuptools. 3.4.0 (2007-07-19) ================== - Final release for 3.4.0. 3.4.0b3 (2007-05-22) ==================== - When checking whether an object is already registered, use identity comparison, to allow adding registering with picky custom comparison methods. 3.3.0.1 (2007-01-03) ==================== - Made a reference to OverflowWarning, which disappeared in Python 2.5, conditional. 3.3.0 (2007/01/03) ================== New Features ------------ - Refactor the adapter-lookup algorithim to make it much simpler and faster. Also, implement more of the adapter-lookup logic in C, making debugging of application code easier, since there is less infrastructre code to step through. - Treat objects without interface declarations as if they declared that they provide ``zope.interface.Interface``. - Add a number of richer new adapter-registration interfaces that provide greater control and introspection. - Add a new interface decorator to zope.interface that allows the setting of tagged values on an interface at definition time (see zope.interface.taggedValue). Bug Fixes --------- - A bug in multi-adapter lookup sometimes caused incorrect adapters to be returned. 3.2.0.2 (2006-04-15) ==================== - Fix packaging bug: 'package_dir' must be a *relative* path. 3.2.0.1 (2006-04-14) ==================== - Packaging change: suppress inclusion of 'setup.cfg' in 'sdist' builds. 3.2.0 (2006-01-05) ================== - Corresponds to the version of the zope.interface package shipped as part of the Zope 3.2.0 release. 3.1.0 (2005-10-03) ================== - Corresponds to the version of the zope.interface package shipped as part of the Zope 3.1.0 release. - Made attribute resolution order consistent with component lookup order, i.e. new-style class MRO semantics. - Deprecate 'isImplementedBy' and 'isImplementedByInstancesOf' APIs in favor of 'implementedBy' and 'providedBy'. 3.0.1 (2005-07-27) ================== - Corresponds to the version of the zope.interface package shipped as part of the Zope X3.0.1 release. - Fix a bug reported by James Knight, which caused adapter registries to fail occasionally to reflect declaration changes. 3.0.0 (2004-11-07) ================== - Corresponds to the version of the zope.interface package shipped as part of the Zope X3.0.0 release. zope.interface-6.4/CONTRIBUTING.md000066400000000000000000000014371462121350100165300ustar00rootroot00000000000000 # Contributing to zopefoundation projects The projects under the zopefoundation GitHub organization are open source and welcome contributions in different forms: * bug reports * code improvements and bug fixes * documentation improvements * pull request reviews For any changes in the repository besides trivial typo fixes you are required to sign the contributor agreement. See https://www.zope.dev/developer/becoming-a-committer.html for details. Please visit our [Developer Guidelines](https://www.zope.dev/developer/guidelines.html) if you'd like to contribute code changes and our [guidelines for reporting bugs](https://www.zope.dev/developer/reporting-bugs.html) if you want to file a bug report. zope.interface-6.4/COPYRIGHT.txt000066400000000000000000000000401462121350100163750ustar00rootroot00000000000000Zope Foundation and Contributorszope.interface-6.4/LICENSE.txt000066400000000000000000000040261462121350100161170ustar00rootroot00000000000000Zope Public License (ZPL) Version 2.1 A copyright notice accompanies this license document that identifies the copyright holders. This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of the copyright holders. Use of them is covered by separate agreement with the copyright holders. 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. zope.interface-6.4/MANIFEST.in000066400000000000000000000010561462121350100160320ustar00rootroot00000000000000# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/c-code include *.md include *.rst include *.txt include buildout.cfg include tox.ini include .coveragerc recursive-include docs *.py recursive-include docs *.rst recursive-include docs *.txt recursive-include docs Makefile recursive-include src *.py include *.yaml include *.cmd include *.sh include *.yml include .coveragerc recursive-include benchmarks *.py recursive-include docs *.bat recursive-include docs *.py recursive-include docs *.rst recursive-include docs Makefile zope.interface-6.4/README.rst000066400000000000000000000025111462121350100157600ustar00rootroot00000000000000==================== ``zope.interface`` ==================== .. image:: https://img.shields.io/pypi/v/zope.interface.svg :target: https://pypi.python.org/pypi/zope.interface/ :alt: Latest Version .. image:: https://img.shields.io/pypi/pyversions/zope.interface.svg :target: https://pypi.org/project/zope.interface/ :alt: Supported Python versions .. image:: https://github.com/zopefoundation/zope.interface/actions/workflows/tests.yml/badge.svg :target: https://github.com/zopefoundation/zope.interface/actions/workflows/tests.yml .. image:: https://readthedocs.org/projects/zopeinterface/badge/?version=latest :target: https://zopeinterface.readthedocs.io/en/latest/ :alt: Documentation Status This package is intended to be independently reusable in any Python project. It is maintained by the `Zope Toolkit project `_. This package provides an implementation of "object interfaces" for Python. Interfaces are a mechanism for labeling objects as conforming to a given API or contract. So, this package can be considered as implementation of the `Design By Contract`_ methodology support in Python. .. _Design By Contract: http://en.wikipedia.org/wiki/Design_by_contract For detailed documentation, please see https://zopeinterface.readthedocs.io/en/latest/ zope.interface-6.4/benchmarks/000077500000000000000000000000001462121350100164075ustar00rootroot00000000000000zope.interface-6.4/benchmarks/.gitignore000066400000000000000000000000071462121350100203740ustar00rootroot00000000000000*.json zope.interface-6.4/benchmarks/micro.py000066400000000000000000000204611462121350100200750ustar00rootroot00000000000000import pyperf from zope.interface import Interface from zope.interface import classImplements from zope.interface import implementedBy from zope.interface.interface import InterfaceClass from zope.interface.registry import Components # Long, mostly similar names are a worst case for equality # comparisons. ifaces = [ InterfaceClass('I' + ('0' * 20) + str(i), (Interface,), {}) for i in range(100) ] class IWideInheritance(*ifaces): """ Inherits from 100 unrelated interfaces. """ class WideInheritance(object): pass classImplements(WideInheritance, IWideInheritance) def make_deep_inheritance(): children = [] base = Interface for iface in ifaces: child = InterfaceClass('IDerived' + base.__name__, (iface, base,), {}) base = child children.append(child) return children deep_ifaces = make_deep_inheritance() class DeepestInheritance(object): pass classImplements(DeepestInheritance, deep_ifaces[-1]) class ImplementsNothing(object): pass class HasConformReturnNone(object): def __conform__(self, iface): return None class HasConformReturnObject(object): def __conform__(self, iface): return self def make_implementer(iface): c = type('Implementer' + iface.__name__, (object,), {}) classImplements(c, iface) return c implementers = [ make_implementer(iface) for iface in ifaces ] providers = [ implementer() for implementer in implementers ] INNER = 100 def bench_in(loops, o): t0 = pyperf.perf_counter() for _ in range(loops): for _ in range(INNER): o.__contains__(Interface) return pyperf.perf_counter() - t0 def bench_sort(loops, objs): import random rand = random.Random(8675309) shuffled = list(objs) rand.shuffle(shuffled) t0 = pyperf.perf_counter() for _ in range(loops): for _ in range(INNER): sorted(shuffled) return pyperf.perf_counter() - t0 def bench_query_adapter(loops, components, objs=providers): components_queryAdapter = components.queryAdapter # One time through to prime the caches for iface in ifaces: for provider in providers: components_queryAdapter(provider, iface) t0 = pyperf.perf_counter() for _ in range(loops): for iface in ifaces: for provider in objs: components_queryAdapter(provider, iface) return pyperf.perf_counter() - t0 def bench_getattr(loops, name, get=getattr): t0 = pyperf.perf_counter() for _ in range(loops): for _ in range(INNER): get(Interface, name) # 1 get(Interface, name) # 2 get(Interface, name) # 3 get(Interface, name) # 4 get(Interface, name) # 5 get(Interface, name) # 6 get(Interface, name) # 7 get(Interface, name) # 8 get(Interface, name) # 9 get(Interface, name) # 10 return pyperf.perf_counter() - t0 def bench_iface_call_no_conform_no_alternate_not_provided(loops): inst = ImplementsNothing() t0 = pyperf.perf_counter() for _ in range(loops): for _ in range(INNER): for iface in ifaces: try: iface(inst) except TypeError: pass else: raise TypeError("Should have failed") return pyperf.perf_counter() - t0 def bench_iface_call_no_conform_w_alternate_not_provided(loops): inst = ImplementsNothing() t0 = pyperf.perf_counter() for _ in range(loops): for _ in range(INNER): for iface in ifaces: iface(inst, 42) return pyperf.perf_counter() - t0 def bench_iface_call_w_conform_return_none_not_provided(loops): inst = HasConformReturnNone() t0 = pyperf.perf_counter() for _ in range(loops): for _ in range(INNER): for iface in ifaces: iface(inst, 42) return pyperf.perf_counter() - t0 def bench_iface_call_w_conform_return_non_none_not_provided(loops): inst = HasConformReturnObject() t0 = pyperf.perf_counter() for _ in range(loops): for _ in range(INNER): for iface in ifaces: iface(inst) return pyperf.perf_counter() - t0 def _bench_iface_call_simple(loops, inst): t0 = pyperf.perf_counter() for _ in range(loops): for _ in range(INNER): for iface in ifaces: iface(inst) return pyperf.perf_counter() - t0 def bench_iface_call_no_conform_provided_wide(loops): return _bench_iface_call_simple(loops, WideInheritance()) def bench_iface_call_no_conform_provided_deep(loops): return _bench_iface_call_simple(loops, DeepestInheritance()) runner = pyperf.Runner() runner.bench_time_func( 'call interface (provides; deep)', bench_iface_call_no_conform_provided_deep, inner_loops=INNER * len(ifaces) ) runner.bench_time_func( 'call interface (provides; wide)', bench_iface_call_no_conform_provided_wide, inner_loops=INNER * len(ifaces) ) runner.bench_time_func( 'call interface (no alternate, no conform, not provided)', bench_iface_call_no_conform_no_alternate_not_provided, inner_loops=INNER * len(ifaces) ) runner.bench_time_func( 'call interface (alternate, no conform, not provided)', bench_iface_call_no_conform_w_alternate_not_provided, inner_loops=INNER * len(ifaces) ) runner.bench_time_func( 'call interface (no alternate, valid conform, not provided)', bench_iface_call_w_conform_return_non_none_not_provided, inner_loops=INNER * len(ifaces) ) runner.bench_time_func( 'call interface (alternate, invalid conform, not provided)', bench_iface_call_w_conform_return_none_not_provided, inner_loops=INNER * len(ifaces) ) runner.bench_time_func( 'read __module__', # stored in C, accessed through __getattribute__ bench_getattr, '__module__', inner_loops=INNER * 10 ) runner.bench_time_func( 'read __name__', # stored in C, accessed through PyMemberDef bench_getattr, '__name__', inner_loops=INNER * 10 ) runner.bench_time_func( 'read __doc__', # stored in Python instance dictionary directly bench_getattr, '__doc__', inner_loops=INNER * 10 ) runner.bench_time_func( 'read providedBy', # from the class, wrapped into a method object. bench_getattr, 'providedBy', inner_loops=INNER * 10 ) runner.bench_time_func( 'query adapter (no registrations)', bench_query_adapter, Components(), inner_loops=1 ) def populate_components(): def factory(o): return 42 pop_components = Components() for iface in ifaces: for other_iface in ifaces: pop_components.registerAdapter(factory, (iface,), other_iface, event=False) return pop_components runner.bench_time_func( 'query adapter (all trivial registrations)', bench_query_adapter, populate_components(), inner_loops=1 ) runner.bench_time_func( 'query adapter (all trivial registrations, wide inheritance)', bench_query_adapter, populate_components(), [WideInheritance()], inner_loops=1 ) runner.bench_time_func( 'query adapter (all trivial registrations, deep inheritance)', bench_query_adapter, populate_components(), [DeepestInheritance()], inner_loops=1 ) runner.bench_time_func( 'sort interfaces', bench_sort, ifaces, inner_loops=INNER, ) runner.bench_time_func( 'sort implementedBy', bench_sort, [implementedBy(p) for p in implementers], inner_loops=INNER, ) runner.bench_time_func( 'sort mixed', bench_sort, [implementedBy(p) for p in implementers] + ifaces, inner_loops=INNER, ) runner.bench_time_func( 'contains (empty dict)', bench_in, {}, inner_loops=INNER ) runner.bench_time_func( 'contains (populated dict: interfaces)', bench_in, {k: k for k in ifaces}, inner_loops=INNER ) runner.bench_time_func( 'contains (populated list: interfaces)', bench_in, ifaces, inner_loops=INNER ) runner.bench_time_func( 'contains (populated dict: implementedBy)', bench_in, {implementedBy(p): 1 for p in implementers}, inner_loops=INNER ) runner.bench_time_func( 'contains (populated list: implementedBy)', bench_in, [implementedBy(p) for p in implementers], inner_loops=INNER ) zope.interface-6.4/build.cmd000066400000000000000000000015061462121350100160600ustar00rootroot00000000000000@echo off :: To build extensions for 64 bit Python 3, we need to configure environment :: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of: :: MS Windows SDK for Windows 7 and .NET Framework 4 :: :: More details at: :: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows IF "%DISTUTILS_USE_SDK%"=="1" ( ECHO Configuring environment to build with MSVC on a 64bit architecture ECHO Using Windows SDK 7.1 "C:\Program Files\Microsoft SDKs\Windows\v7.1\Setup\WindowsSdkVer.exe" -q -version:v7.1 CALL "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 /release SET MSSdk=1 REM Need the following to allow tox to see the SDK compiler SET TOX_TESTENV_PASSENV=DISTUTILS_USE_SDK MSSdk INCLUDE LIB ) ELSE ( ECHO Using default MSVC build environment ) CALL %* zope.interface-6.4/buildout.cfg000066400000000000000000000003271462121350100166040ustar00rootroot00000000000000[buildout] develop = . parts = test python [test] recipe = zc.recipe.testrunner eggs = zope.interface zope.event [python] recipe = zc.recipe.egg eggs = zope.interface zope.event interpreter = python zope.interface-6.4/docs/000077500000000000000000000000001462121350100152225ustar00rootroot00000000000000zope.interface-6.4/docs/Makefile000066400000000000000000000127301462121350100166650ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/zopeinterface.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/zopeinterface.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/zopeinterface" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/zopeinterface" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." zope.interface-6.4/docs/README.rst000066400000000000000000001201331462121350100167110ustar00rootroot00000000000000============ Interfaces ============ .. currentmodule:: zope.interface Interfaces are objects that specify (document) the external behavior of objects that "provide" them. An interface specifies behavior through: - Informal documentation in a doc string - Attribute definitions - Invariants, which are conditions that must hold for objects that provide the interface Attribute definitions specify specific attributes. They define the attribute name and provide documentation and constraints of attribute values. Attribute definitions can take a number of forms, as we'll see below. Defining interfaces =================== Interfaces are defined using Python ``class`` statements: .. doctest:: >>> import zope.interface >>> class IFoo(zope.interface.Interface): ... """Foo blah blah""" ... ... x = zope.interface.Attribute("""X blah blah""") ... ... def bar(q, r=None): ... """bar blah blah""" In the example above, we've created an interface, :class:`IFoo`. We subclassed :class:`zope.interface.Interface`, which is an ancestor interface for all interfaces, much as ``object`` is an ancestor of all new-style classes [#create]_. The interface is not a class, it's an Interface, an instance of :class:`zope.interface.interface.InterfaceClass`: .. doctest:: >>> type(IFoo) We can ask for the interface's documentation: .. doctest:: >>> IFoo.__doc__ 'Foo blah blah' and its name: .. doctest:: >>> IFoo.__name__ 'IFoo' and even its module: .. doctest:: >>> IFoo.__module__ 'builtins' The interface defined two attributes: ``x`` This is the simplest form of attribute definition. It has a name and a doc string. It doesn't formally specify anything else. ``bar`` This is a method. A method is defined via a function definition. A method is simply an attribute constrained to be a callable with a particular signature, as provided by the function definition. Note that ``bar`` doesn't take a ``self`` argument. Interfaces document how an object is *used*. When calling instance methods, you don't pass a ``self`` argument, so a ``self`` argument isn't included in the interface signature. The ``self`` argument in instance methods is really an implementation detail of Python instances. Other objects, besides instances can provide interfaces and their methods might not be instance methods. For example, modules can provide interfaces and their methods are usually just functions. Even instances can have methods that are not instance methods. You can access the attributes defined by an interface using mapping syntax: .. doctest:: >>> x = IFoo['x'] >>> type(x) >>> x.__name__ 'x' >>> x.__doc__ 'X blah blah' >>> IFoo.get('x').__name__ 'x' >>> IFoo.get('y') You can use ``in`` to determine if an interface defines a name: .. doctest:: >>> 'x' in IFoo True You can iterate over interfaces to get the names they define: .. doctest:: >>> names = list(IFoo) >>> names.sort() >>> names ['bar', 'x'] Remember that interfaces aren't classes. You can't access attribute definitions as attributes of interfaces: .. doctest:: >>> IFoo.x Traceback (most recent call last): File "", line 1, in ? AttributeError: 'InterfaceClass' object has no attribute 'x' Methods provide access to the method signature: .. doctest:: >>> bar = IFoo['bar'] >>> bar.getSignatureString() '(q, r=None)' TODO Methods really should have a better API. This is something that needs to be improved. Declaring interfaces ==================== Having defined interfaces, we can *declare* that objects provide them. Before we describe the details, lets define some terms: *provide* We say that objects *provide* interfaces. If an object provides an interface, then the interface specifies the behavior of the object. In other words, interfaces specify the behavior of the objects that provide them. *implement* We normally say that classes *implement* interfaces. If a class implements an interface, then the instances of the class provide the interface. Objects provide interfaces that their classes implement [#factory]_. (Objects can provide interfaces directly, in addition to what their classes implement.) It is important to note that classes don't usually provide the interfaces that they implement. We can generalize this to factories. For any callable object we can declare that it produces objects that provide some interfaces by saying that the factory implements the interfaces. Now that we've defined these terms, we can talk about the API for declaring interfaces. Declaring implemented interfaces -------------------------------- The most common way to declare interfaces is using the `implementer` decorator on a class: .. doctest:: >>> @zope.interface.implementer(IFoo) ... class Foo: ... ... def __init__(self, x=None): ... self.x = x ... ... def bar(self, q, r=None): ... return q, r, self.x ... ... def __repr__(self): ... return "Foo(%s)" % self.x In this example, we declared that ``Foo`` implements ``IFoo``. This means that instances of ``Foo`` provide ``IFoo``. Having made this declaration, there are several ways we can introspect the declarations. First, we can ask an interface whether it is implemented by a class: .. doctest:: >>> IFoo.implementedBy(Foo) True And we can ask whether an interface is provided by an object: .. doctest:: >>> foo = Foo() >>> IFoo.providedBy(foo) True Of course, ``Foo`` doesn't *provide* ``IFoo``, it *implements* it: .. doctest:: >>> IFoo.providedBy(Foo) False We can also ask what interfaces are implemented by a class: .. doctest:: >>> list(zope.interface.implementedBy(Foo)) [] It's an error to ask for interfaces implemented by a non-callable object: .. doctest:: >>> IFoo.implementedBy(foo) Traceback (most recent call last): ... TypeError: ('ImplementedBy called for non-factory', Foo(None)) >>> list(zope.interface.implementedBy(foo)) Traceback (most recent call last): ... TypeError: ('ImplementedBy called for non-factory', Foo(None)) Similarly, we can ask what interfaces are provided by an object: .. doctest:: >>> list(zope.interface.providedBy(foo)) [] >>> list(zope.interface.providedBy(Foo)) [] We can declare interfaces implemented by other factories (besides classes). We do this using the same `implementer` decorator. .. doctest:: >>> @zope.interface.implementer(IFoo) ... def yfoo(y): ... foo = Foo() ... foo.y = y ... return foo >>> list(zope.interface.implementedBy(yfoo)) [] Note that the implementer decorator may modify its argument. Callers should not assume that a new object is created. Using implementer also works on callable objects. This is used by :py:mod:`zope.formlib`, as an example: .. doctest:: >>> class yfactory: ... def __call__(self, y): ... foo = Foo() ... foo.y = y ... return foo >>> yfoo = yfactory() >>> yfoo = zope.interface.implementer(IFoo)(yfoo) >>> list(zope.interface.implementedBy(yfoo)) [] XXX: Double check and update these version numbers: In :py:mod:`zope.interface` 3.5.2 and lower, the implementer decorator can not be used for classes, but in 3.6.0 and higher it can: .. doctest:: >>> Foo = zope.interface.implementer(IFoo)(Foo) >>> list(zope.interface.providedBy(Foo())) [] Note that class decorators using the ``@implementer(IFoo)`` syntax are only supported in Python 2.6 and later. .. autofunction:: implementer :noindex: .. XXX: Duplicate description. Declaring provided interfaces ----------------------------- We can declare interfaces directly provided by objects. Suppose that we want to document what the ``__init__`` method of the ``Foo`` class does. It's not *really* part of ``IFoo``. You wouldn't normally call the ``__init__`` method on Foo instances. Rather, the ``__init__`` method is part of ``Foo``'s ``__call__`` method: .. doctest:: >>> class IFooFactory(zope.interface.Interface): ... """Create foos""" ... ... def __call__(x=None): ... """Create a foo ... ... The argument provides the initial value for x ... ... """ It's the class that provides this interface, so we declare the interface on the class: .. doctest:: >>> zope.interface.directlyProvides(Foo, IFooFactory) And then, we'll see that Foo provides some interfaces: .. doctest:: >>> list(zope.interface.providedBy(Foo)) [] >>> IFooFactory.providedBy(Foo) True Declaring class interfaces is common enough that there's a special decorator for it, `provider`: .. doctest:: >>> @zope.interface.implementer(IFoo) ... @zope.interface.provider(IFooFactory) ... class Foo2: ... ... def __init__(self, x=None): ... self.x = x ... ... def bar(self, q, r=None): ... return q, r, self.x ... ... def __repr__(self): ... return "Foo(%s)" % self.x >>> list(zope.interface.providedBy(Foo2)) [] >>> IFooFactory.providedBy(Foo2) True There's a similar function, ``moduleProvides``, that supports interface declarations from within module definitions. For example, see the use of ``moduleProvides`` call in ``zope.interface.__init__``, which declares that the package ``zope.interface`` provides ``IInterfaceDeclaration``. Sometimes, we want to declare interfaces on instances, even though those instances get interfaces from their classes. Suppose we create a new interface, ``ISpecial``: .. doctest:: >>> class ISpecial(zope.interface.Interface): ... reason = zope.interface.Attribute("Reason why we're special") ... def brag(): ... "Brag about being special" We can make an existing foo instance special by providing ``reason`` and ``brag`` attributes: .. doctest:: >>> foo.reason = 'I just am' >>> def brag(): ... return "I'm special!" >>> foo.brag = brag >>> foo.reason 'I just am' >>> foo.brag() "I'm special!" and by declaring the interface: .. doctest:: >>> zope.interface.directlyProvides(foo, ISpecial) then the new interface is included in the provided interfaces: .. doctest:: >>> ISpecial.providedBy(foo) True >>> list(zope.interface.providedBy(foo)) [, ] We can find out what interfaces are directly provided by an object: .. doctest:: >>> list(zope.interface.directlyProvidedBy(foo)) [] >>> newfoo = Foo() >>> list(zope.interface.directlyProvidedBy(newfoo)) [] .. autofunction:: provider :noindex: .. XXX: Duplicate description. Inherited declarations ---------------------- Normally, declarations are inherited: .. doctest:: >>> @zope.interface.implementer(ISpecial) ... class SpecialFoo(Foo): ... reason = 'I just am' ... def brag(self): ... return "I'm special because %s" % self.reason >>> list(zope.interface.implementedBy(SpecialFoo)) [, ] >>> list(zope.interface.providedBy(SpecialFoo())) [, ] Sometimes, you don't want to inherit declarations. In that case, you can use ``implementer_only``, instead of ``implementer``: .. doctest:: >>> @zope.interface.implementer_only(ISpecial) ... class Special(Foo): ... reason = 'I just am' ... def brag(self): ... return "I'm special because %s" % self.reason >>> list(zope.interface.implementedBy(Special)) [] >>> list(zope.interface.providedBy(Special())) [] External declarations --------------------- Normally, we make implementation declarations as part of a class definition. Sometimes, we may want to make declarations from outside the class definition. For example, we might want to declare interfaces for classes that we didn't write. The function ``classImplements`` can be used for this purpose: .. doctest:: >>> class C: ... pass >>> zope.interface.classImplements(C, IFoo) >>> list(zope.interface.implementedBy(C)) [] .. autofunction:: classImplements :noindex: We can use ``classImplementsOnly`` to exclude inherited interfaces: .. doctest:: >>> class C(Foo): ... pass >>> zope.interface.classImplementsOnly(C, ISpecial) >>> list(zope.interface.implementedBy(C)) [] .. autofunction:: classImplementsOnly :noindex: .. XXX: Duplicate description. Declaration Objects ------------------- When we declare interfaces, we create *declaration* objects. When we query declarations, declaration objects are returned: .. doctest:: >>> type(zope.interface.implementedBy(Special)) Declaration objects and interface objects are similar in many ways. In fact, they share a common base class. The important thing to realize about them is that they can be used where interfaces are expected in declarations. Here's a silly example: .. doctest:: >>> @zope.interface.implementer_only( ... zope.interface.implementedBy(Foo), ... ISpecial, ... ) ... class Special2(object): ... reason = 'I just am' ... def brag(self): ... return "I'm special because %s" % self.reason The declaration here is almost the same as ``zope.interface.implementer(ISpecial)``, except that the order of interfaces in the resulting declaration is different: .. doctest:: >>> list(zope.interface.implementedBy(Special2)) [, ] Interface Inheritance ===================== Interfaces can extend other interfaces. They do this simply by listing the other interfaces as base interfaces: .. doctest:: >>> class IBlat(zope.interface.Interface): ... """Blat blah blah""" ... ... y = zope.interface.Attribute("y blah blah") ... def eek(): ... """eek blah blah""" >>> IBlat.__bases__ (,) >>> class IBaz(IFoo, IBlat): ... """Baz blah""" ... def eek(a=1): ... """eek in baz blah""" ... >>> IBaz.__bases__ (, ) >>> names = list(IBaz) >>> names.sort() >>> names ['bar', 'eek', 'x', 'y'] Note that ``IBaz`` overrides ``eek``: .. doctest:: >>> IBlat['eek'].__doc__ 'eek blah blah' >>> IBaz['eek'].__doc__ 'eek in baz blah' We were careful to override ``eek`` in a compatible way. When extending an interface, the extending interface should be compatible [#compat]_ with the extended interfaces. We can ask whether one interface extends another: .. doctest:: >>> IBaz.extends(IFoo) True >>> IBlat.extends(IFoo) False Note that interfaces don't extend themselves: .. doctest:: >>> IBaz.extends(IBaz) False Sometimes we wish they did, but we can instead use ``isOrExtends``: .. doctest:: >>> IBaz.isOrExtends(IBaz) True >>> IBaz.isOrExtends(IFoo) True >>> IFoo.isOrExtends(IBaz) False When we iterate over an interface, we get all of the names it defines, including names defined by base interfaces. Sometimes, we want *just* the names defined by the interface directly. We can use the ``names`` method for that: .. doctest:: >>> list(IBaz.names()) ['eek'] Inheritance of attribute specifications --------------------------------------- An interface may override attribute definitions from base interfaces. If two base interfaces define the same attribute, the attribute is inherited from the most specific interface. For example, with: .. doctest:: >>> class IBase(zope.interface.Interface): ... ... def foo(): ... "base foo doc" >>> class IBase1(IBase): ... pass >>> class IBase2(IBase): ... ... def foo(): ... "base2 foo doc" >>> class ISub(IBase1, IBase2): ... pass ``ISub``'s definition of ``foo`` is the one from ``IBase2``, since ``IBase2`` is more specific than ``IBase``: .. doctest:: >>> ISub['foo'].__doc__ 'base2 foo doc' Note that this differs from a depth-first search. Sometimes, it's useful to ask whether an interface defines an attribute directly. You can use the direct method to get a directly defined definitions: .. doctest:: >>> IBase.direct('foo').__doc__ 'base foo doc' >>> ISub.direct('foo') Specifications -------------- Interfaces and declarations are both special cases of specifications. What we described above for interface inheritance applies to both declarations and specifications. Declarations actually extend the interfaces that they declare: .. doctest:: >>> @zope.interface.implementer(IBaz) ... class Baz(object): ... pass >>> baz_implements = zope.interface.implementedBy(Baz) >>> baz_implements.__bases__ (, classImplements(object)) >>> baz_implements.extends(IFoo) True >>> baz_implements.isOrExtends(IFoo) True >>> baz_implements.isOrExtends(baz_implements) True Specifications (interfaces and declarations) provide an ``__sro__`` that lists the specification and all of it's ancestors: .. doctest:: >>> from pprint import pprint >>> pprint(baz_implements.__sro__) (classImplements(Baz, IBaz), , , , classImplements(object), ) >>> class IBiz(zope.interface.Interface): ... pass >>> @zope.interface.implementer(IBiz) ... class Biz(Baz): ... pass >>> pprint(zope.interface.implementedBy(Biz).__sro__) (classImplements(Biz, IBiz), , classImplements(Baz, IBaz), , , , classImplements(object), ) Tagged Values ============= .. autofunction:: taggedValue Interfaces and attribute descriptions support an extension mechanism, borrowed from UML, called "tagged values" that lets us store extra data: .. doctest:: >>> IFoo.setTaggedValue('date-modified', '2004-04-01') >>> IFoo.setTaggedValue('author', 'Jim Fulton') >>> IFoo.getTaggedValue('date-modified') '2004-04-01' >>> IFoo.queryTaggedValue('date-modified') '2004-04-01' >>> IFoo.queryTaggedValue('datemodified') >>> tags = list(IFoo.getTaggedValueTags()) >>> tags.sort() >>> tags ['author', 'date-modified'] Function attributes are converted to tagged values when method attribute definitions are created: .. doctest:: >>> class IBazFactory(zope.interface.Interface): ... def __call__(): ... "create one" ... __call__.return_type = IBaz >>> IBazFactory['__call__'].getTaggedValue('return_type') Tagged values can also be defined from within an interface definition: .. doctest:: >>> class IWithTaggedValues(zope.interface.Interface): ... zope.interface.taggedValue('squish', 'squash') >>> IWithTaggedValues.getTaggedValue('squish') 'squash' Tagged values are inherited in the same way that attribute and method descriptions are. Inheritance can be ignored by using the "direct" versions of functions. .. doctest:: >>> class IExtendsIWithTaggedValues(IWithTaggedValues): ... zope.interface.taggedValue('child', True) >>> IExtendsIWithTaggedValues.getTaggedValue('child') True >>> IExtendsIWithTaggedValues.getDirectTaggedValue('child') True >>> IExtendsIWithTaggedValues.getTaggedValue('squish') 'squash' >>> print(IExtendsIWithTaggedValues.queryDirectTaggedValue('squish')) None >>> IExtendsIWithTaggedValues.setTaggedValue('squish', 'SQUASH') >>> IExtendsIWithTaggedValues.getTaggedValue('squish') 'SQUASH' >>> IExtendsIWithTaggedValues.getDirectTaggedValue('squish') 'SQUASH' Invariants ========== .. autofunction:: invariant Interfaces can express conditions that must hold for objects that provide them. These conditions are expressed using one or more invariants. Invariants are callable objects that will be called with an object that provides an interface. An invariant raises an ``Invalid`` exception if the condition doesn't hold. Here's an example: .. doctest:: >>> class RangeError(zope.interface.Invalid): ... """A range has invalid limits""" ... def __repr__(self): ... return "RangeError(%r)" % self.args >>> def range_invariant(ob): ... if ob.max < ob.min: ... raise RangeError(ob) Given this invariant, we can use it in an interface definition: .. doctest:: >>> class IRange(zope.interface.Interface): ... min = zope.interface.Attribute("Lower bound") ... max = zope.interface.Attribute("Upper bound") ... ... zope.interface.invariant(range_invariant) Interfaces have a method for checking their invariants: .. doctest:: >>> @zope.interface.implementer(IRange) ... class Range(object): ... def __init__(self, min, max): ... self.min, self.max = min, max ... ... def __repr__(self): ... return "Range(%s, %s)" % (self.min, self.max) >>> IRange.validateInvariants(Range(1,2)) >>> IRange.validateInvariants(Range(1,1)) >>> IRange.validateInvariants(Range(2,1)) Traceback (most recent call last): ... RangeError: Range(2, 1) If you have multiple invariants, you may not want to stop checking after the first error. If you pass a list to ``validateInvariants``, then a single ``Invalid`` exception will be raised with the list of exceptions as its argument: .. doctest:: >>> from zope.interface.exceptions import Invalid >>> errors = [] >>> try: ... IRange.validateInvariants(Range(2,1), errors) ... except Invalid as e: ... str(e) '[RangeError(Range(2, 1))]' And the list will be filled with the individual exceptions: .. doctest:: >>> errors [RangeError(Range(2, 1))] >>> del errors[:] Adaptation ========== Interfaces can be called to perform *adaptation*. Adaptation is the process of converting an object to an object implementing the interface. For example, in mathematics, to represent a point in space or on a graph there's the familiar Cartesian coordinate system using ``CartesianPoint(x, y)``, and there's also the Polar coordinate system using ``PolarPoint(r, theta)``, plus several others (homogeneous, log-polar, etc). Polar points are most convenient for some types of operations, but cartesian points may make more intuitive sense to most people. Before printing an arbitrary point, we might want to *adapt* it to ``ICartesianPoint``, or before performing some mathematical operation you might want to adapt the arbitrary point to ``IPolarPoint``. The semantics are based on those of the :pep:`246` ``adapt`` function. If an object cannot be adapted, then a ``TypeError`` is raised: .. doctest:: >>> class ICartesianPoint(zope.interface.Interface): ... x = zope.interface.Attribute("Distance from origin along x axis") ... y = zope.interface.Attribute("Distance from origin along y axis") >>> ICartesianPoint(0) Traceback (most recent call last): ... TypeError: ('Could not adapt', 0, ) unless a default value is provided as a second positional argument; this value is not checked to see if it implements the interface: .. doctest:: >>> ICartesianPoint(0, 'bob') 'bob' If an object already implements the interface, then it will be returned: .. doctest:: >>> @zope.interface.implementer(ICartesianPoint) ... class CartesianPoint(object): ... """The default cartesian point is the origin.""" ... def __init__(self, x=0, y=0): ... self.x = x ... self.y = y ... def __repr__(self): ... return "CartesianPoint(%s, %s)" % (self.x, self.y) >>> obj = CartesianPoint() >>> ICartesianPoint(obj) is obj True ``__conform__`` --------------- :pep:`246` outlines a requirement: When the object knows about the [interface], and either considers itself compliant, or knows how to wrap itself suitably. This is handled with ``__conform__``. If an object implements ``__conform__``, then it will be used to give the object the chance to decide if it knows about the interface. This is true even if the class declares that it implements the interface. .. doctest:: >>> @zope.interface.implementer(ICartesianPoint) ... class C(object): ... def __conform__(self, proto): ... return "This could be anything." >>> ICartesianPoint(C()) 'This could be anything.' If ``__conform__`` returns ``None`` (because the object is unaware of the interface), then the rest of the adaptation process will continue. Here, we demonstrate that if the object already provides the interface, it is returned. .. doctest:: >>> @zope.interface.implementer(ICartesianPoint) ... class C(object): ... def __conform__(self, proto): ... return None >>> c = C() >>> ICartesianPoint(c) is c True Adapter hooks (see :ref:`adapt_adapter_hooks`) will also be used, if present (after a ``__conform__`` method, if any, has been tried): .. doctest:: >>> from zope.interface.interface import adapter_hooks >>> def adapt_tuple_to_point(iface, obj): ... if isinstance(obj, tuple) and len(obj) == 2: ... return CartesianPoint(*obj) >>> adapter_hooks.append(adapt_tuple_to_point) >>> ICartesianPoint((1, 1)) CartesianPoint(1, 1) >>> adapter_hooks.remove(adapt_tuple_to_point) >>> ICartesianPoint((1, 1)) Traceback (most recent call last): ... TypeError: ('Could not adapt', (1, 1), ) .. _adapt_adapter_hooks: ``__adapt__`` and adapter hooks ------------------------------- Interfaces implement the :pep:`246` ``__adapt__`` method to satisfy the requirement: When the [interface] knows about the object, and either the object already complies or the [interface] knows how to suitably wrap the object. This method is normally not called directly. It is called by the :pep:`246` adapt framework and by the interface ``__call__`` operator once ``__conform__`` (if any) has failed. The ``__adapt__`` method is responsible for adapting an object to the receiver. The default version returns ``None`` (because by default no interface "knows how to suitably wrap the object"): .. doctest:: >>> ICartesianPoint.__adapt__(0) unless the object given provides the interface ("the object already complies"): .. doctest:: >>> @zope.interface.implementer(ICartesianPoint) ... class C(object): ... pass >>> obj = C() >>> ICartesianPoint.__adapt__(obj) is obj True .. rubric:: Customizing ``__adapt__`` in an interface It is possible to replace or customize the ``__adapt___`` functionality for particular interfaces, if that interface "knows how to suitably wrap [an] object". This method should return the adapted object if it knows how, or call the super class to continue with the default adaptation process. .. doctest:: >>> import math >>> class IPolarPoint(zope.interface.Interface): ... r = zope.interface.Attribute("Distance from center.") ... theta = zope.interface.Attribute("Angle from horizontal.") ... @zope.interface.interfacemethod ... def __adapt__(self, obj): ... if ICartesianPoint.providedBy(obj): ... # Convert to polar coordinates. ... r = math.sqrt(obj.x ** 2 + obj.y ** 2) ... theta = math.acos(obj.x / r) ... theta = math.degrees(theta) ... return PolarPoint(r, theta) ... return super(type(IPolarPoint), self).__adapt__(obj) >>> @zope.interface.implementer(IPolarPoint) ... class PolarPoint(object): ... def __init__(self, r=0, theta=0): ... self.r = r; self.theta = theta ... def __repr__(self): ... return "PolarPoint(%s, %s)" % (self.r, self.theta) >>> IPolarPoint(CartesianPoint(0, 1)) PolarPoint(1.0, 90.0) >>> IPolarPoint(PolarPoint()) PolarPoint(0, 0) .. seealso:: :func:`zope.interface.interfacemethod`, which explains how to override functions in interface definitions and why, prior to Python 3.6, the zero-argument version of `super` cannot be used. .. rubric:: Using adapter hooks for loose coupling Commonly, the author of the interface doesn't know how to wrap all possible objects, and neither does the author of an object know how to ``__conform__`` to all possible interfaces. To support decoupling interfaces and objects, interfaces support the concept of "adapter hooks." Adapter hooks are a global sequence of callables ``hook(interface, object)`` that are called, in order, from the default ``__adapt__`` method until one returns a non-``None`` result. .. note:: In many applications, a :doc:`adapter` is installed as the first or only adapter hook. We'll install a hook that adapts from a 2D ``(x, y)`` Cartesian point on a plane to a three-dimensional point ``(x, y, z)`` by assuming the ``z`` coordinate is 0. First, we'll define this new interface and an implementation: .. doctest:: >>> class ICartesianPoint3D(ICartesianPoint): ... z = zope.interface.Attribute("Depth.") >>> @zope.interface.implementer(ICartesianPoint3D) ... class CartesianPoint3D(CartesianPoint): ... def __init__(self, x=0, y=0, z=0): ... CartesianPoint.__init__(self, x, y) ... self.z = 0 ... def __repr__(self): ... return "CartesianPoint3D(%s, %s, %s)" % (self.x, self.y, self.z) We install a hook by simply adding it to the ``adapter_hooks`` list: .. doctest:: >>> from zope.interface.interface import adapter_hooks >>> def returns_none(iface, obj): ... print("(First adapter hook returning None.)") >>> def adapt_2d_to_3d(iface, obj): ... if iface == ICartesianPoint3D and ICartesianPoint.providedBy(obj): ... return CartesianPoint3D(obj.x, obj.y, 0) >>> adapter_hooks.append(returns_none) >>> adapter_hooks.append(adapt_2d_to_3d) >>> ICartesianPoint3D.__adapt__(CartesianPoint()) (First adapter hook returning None.) CartesianPoint3D(0, 0, 0) >>> ICartesianPoint3D(CartesianPoint()) (First adapter hook returning None.) CartesianPoint3D(0, 0, 0) Hooks can be uninstalled by removing them from the list: .. doctest:: >>> adapter_hooks.remove(returns_none) >>> adapter_hooks.remove(adapt_2d_to_3d) >>> ICartesianPoint3D.__adapt__(CartesianPoint()) .. _global_persistence: Persistence, Sorting, Equality and Hashing ========================================== .. tip:: For the practical implications of what's discussed below, and some potential problems, see :ref:`spec_eq_hash`. Just like Python classes, interfaces are designed to inexpensively support persistence using Python's standard :mod:`pickle` module. This means that one process can send a *reference* to an interface to another process in the form of a byte string, and that other process can load that byte string and get the object that is that interface. The processes may be separated in time (one after the other), in space (running on different machines) or even be parts of the same process communicating with itself. We can demonstrate this. Observe how small the byte string needed to capture the reference is. Also note that since this is the same process, the identical object is found and returned: .. doctest:: >>> import sys >>> import pickle >>> class Foo(object): ... pass >>> sys.modules[__name__].Foo = Foo # XXX, see below >>> pickled_byte_string = pickle.dumps(Foo, 0) >>> len(pickled_byte_string) 21 >>> imported = pickle.loads(pickled_byte_string) >>> imported == Foo True >>> imported is Foo True >>> class IFoo(zope.interface.Interface): ... pass >>> sys.modules[__name__].IFoo = IFoo # XXX, see below >>> pickled_byte_string = pickle.dumps(IFoo, 0) >>> len(pickled_byte_string) 22 >>> imported = pickle.loads(pickled_byte_string) >>> imported is IFoo True >>> imported == IFoo True .. rubric:: References to Global Objects The eagle-eyed reader will have noticed the two funny lines like ``sys.modules[__name__].Foo = Foo``. What's that for? To understand, we must know a bit about how Python "pickles" (``pickle.dump`` or ``pickle.dumps``) classes or interfaces. When Python pickles a class or an interface, it does so as a "global object" [#global_object]_. Global objects are expected to already exist (contrast this with pickling a string or an object instance, which creates a new object in the receiving process) with all their necessary state information (for classes and interfaces, the state information would be things like the list of methods and defined attributes) in the receiving process, so the pickled byte string needs only contain enough data to look up that existing object; this data is a *reference*. Not only does this minimize the amount of data required to persist such an object, it also facilitates changing the definition of the object over time: if a class or interface gains or loses methods or attributes, loading a previously pickled reference will use the *current definition* of the object. The *reference* to a global object that's stored in the byte string consists only of the object's ``__name__`` and ``__module__``. Before a global object *obj* is pickled, Python makes sure that the object being pickled is the same one that can be found at ``getattr(sys.modules[obj.__module__], obj.__name__)``; if there is no such object, or it refers to a different object, pickling fails. The two funny lines make sure that holds, no matter how this example is run (using some doctest runners, it doesn't hold by default, unlike it normally would). We can show some examples of what happens when that condition doesn't hold. First, what if we change the global object and try to pickle the old one? .. doctest:: >>> sys.modules[__name__].Foo = 42 >>> pickle.dumps(Foo) Traceback (most recent call last): ... _pickle.PicklingError: Can't pickle : it's not the same object as builtins.Foo A consequence of this is that only one object of the given name can be defined and pickled at a time. If we were to try to define a new ``Foo`` class (remembering that normally the ``sys.modules[__name__].Foo =`` line is automatic), we still cannot pickle the old one: .. doctest:: >>> orig_Foo = Foo >>> class Foo(object): ... pass >>> sys.modules[__name__].Foo = Foo # XXX, usually automatic >>> pickle.dumps(orig_Foo) Traceback (most recent call last): ... _pickle.PicklingError: Can't pickle : it's not the same object as builtins.Foo Or what if there simply is no global object? .. doctest:: >>> del sys.modules[__name__].Foo >>> pickle.dumps(Foo) Traceback (most recent call last): ... _pickle.PicklingError: Can't pickle : attribute lookup Foo on builtins failed Interfaces and classes behave the same in all those ways. .. rubric:: What's This Have To Do With Sorting, Equality and Hashing? Another important design consideration for interfaces is that they should be sortable. This permits them to be used, for example, as keys in a (persistent) `BTree `_. As such, they define a total ordering, meaning that any given interface can definitively said to be greater than, less than, or equal to, any other interface. This relationship must be *stable* and hold the same across any two processes. An object becomes sortable by overriding the equality method ``__eq__`` and at least one of the comparison methods (such as ``__lt__``). Classes, on the other hand, are not sortable [#class_sort]_. Classes can only be tested for equality, and they implement this using object identity: ``class_a == class_b`` is equivalent to ``class_a is class_b``. In addition to being sortable, it's important for interfaces to be hashable so they can be used as keys in dictionaries or members of sets. This is done by implementing the ``__hash__`` method [#hashable]_. Classes are hashable, and they also implement this based on object identity, with the equivalent of ``id(class_a)``. To be both hashable and sortable, the hash method and the equality and comparison methods **must** `be consistent with each other `_. That is, they must all be based on the same principle. Classes use the principle of identity to implement equality and hashing, but they don't implement sorting because identity isn't a stable sorting method (it is different in every process). Interfaces need to be sortable. In order for all three of hashing, equality and sorting to be consistent, interfaces implement them using the same principle as persistence. Interfaces are treated like "global objects" and sort and hash using the same information a *reference* to them would: their ``__name__`` and ``__module__``. In this way, hashing, equality and sorting are consistent with each other, and consistent with pickling: .. doctest:: >>> class IFoo(zope.interface.Interface): ... pass >>> sys.modules[__name__].IFoo = IFoo # XXX, usually automatic >>> f1 = IFoo >>> pickled_f1 = pickle.dumps(f1) >>> class IFoo(zope.interface.Interface): ... pass >>> sys.modules[__name__].IFoo = IFoo # XXX, usually automatic >>> IFoo == f1 True >>> unpickled_f1 = pickle.loads(pickled_f1) >>> unpickled_f1 == IFoo == f1 True This isn't quite the case for classes; note how ``f1`` wasn't equal to ``Foo`` before pickling, but the unpickled value is: .. doctest:: >>> class Foo(object): ... pass >>> sys.modules[__name__].Foo = Foo # XXX, usually automatic >>> f1 = Foo >>> pickled_f1 = pickle.dumps(Foo) >>> class Foo(object): ... pass >>> sys.modules[__name__].Foo = Foo # XXX, usually automatic >>> f1 == Foo False >>> unpickled_f1 = pickle.loads(pickled_f1) >>> unpickled_f1 == Foo # Surprise! True >>> unpickled_f1 == f1 False For more information, and some rare potential pitfalls, see :ref:`spec_eq_hash`. .. rubric:: Footnotes .. [#create] The main reason we subclass ``Interface`` is to cause the Python class statement to create an interface, rather than a class. It's possible to create interfaces by calling a special interface class directly. Doing this, it's possible (and, on rare occasions, useful) to create interfaces that don't descend from ``Interface``. Using this technique is beyond the scope of this document. .. [#factory] Classes are factories. They can be called to create their instances. We expect that we will eventually extend the concept of implementation to other kinds of factories, so that we can declare the interfaces provided by the objects created. .. [#compat] The goal is substitutability. An object that provides an extending interface should be substitutable for an object that provides the extended interface. In our example, an object that provides ``IBaz`` should be usable wherever an object that provides ``IBlat`` is expected. The interface implementation doesn't enforce this, but maybe it should do some checks. .. [#class_sort] In Python 2, classes could be sorted, but the sort was not stable (it also used the identity principle) and not useful for persistence; this was considered a bug that was fixed in Python 3. .. [#hashable] In order to be hashable, you must implement both ``__eq__`` and ``__hash__``. If you only implement ``__eq__``, Python makes sure the type cannot be used in a dictionary, set, or with :func:`hash`. In Python 2, this wasn't the case, and forgetting to override ``__hash__`` was a constant source of bugs. .. [#global_object] From the name of the pickle bytecode operator; it varies depending on the protocol but always includes "GLOBAL". zope.interface-6.4/docs/README.ru.rst000066400000000000000000001023161462121350100173410ustar00rootroot00000000000000========== Интерфейсы ========== .. contents:: Интерфейсы - это объекты специфицирующие (документирующие) внешнее поведение объектов которые их "предоставляют". Интерфейсы определяют поведение через следующие составляющие: - Неформальную документацию в строках документации - Определения атрибутов - Инварианты - условия, которые должны соблюдаться для объектов предоставляющих интерфейс Определения атрибутов описывают конкретные атрибуты. Они определяют имя атрибута и предоставляют документацию и ограничения для значений атрибута. Определения атрибутов могут быть заданы несколькими путями как мы увидим ниже. Определение интерфейсов ======================= Интерфейсы определяются с использованием ключевого слова class:: >>> import zope.interface >>> class IFoo(zope.interface.Interface): ... """Foo blah blah""" ... ... x = zope.interface.Attribute("""X blah blah""") ... ... def bar(q, r=None): ... """bar blah blah""" В примере выше мы создали интерфейс `IFoo`. Мы наследуем его от класса `zope.interface.Interface`, который является родительским интерфейсом для всех интерфейсов, как `object` - это родительский класс для всех новых классов [#create]_. Данный интерфейс не является классом, а является Интерфейсом, экземпляром `InterfaceClass`:: >>> type(IFoo) Мы можем запросить у интерфейса его документацию:: >>> IFoo.__doc__ 'Foo blah blah' и его имя:: >>> IFoo.__name__ 'IFoo' и даже модуль в котором он определен:: >>> IFoo.__module__ '__main__' Наш интерфейс определяет два атрибута: `x` Это простейшая форма определения атрибутов. Определяются имя и строка документации. Формально здесь не определяется ничего более. `bar` Это метод. Методы определяются как обычные функции. Метод - это просто атрибут который должен быть вызываемым с указанием сигнатуры, предоставляемой определением функции. Надо отметить, что аргумент `self` не указывается для `bar`. Интерфейс документирует как объект *используется*. Когда методы экземпляров классов вызываются мы не передаем аргумент `self`, таким образом аргумент `self` не включается и в сигнатуру интерфейса. Аргумент `self` в методах экземпляров классов на самом деле деталь реализации экземпляров классов в Python. Другие объекты кроме экземпляров классов могут предоставлять интерфейсы и их методы могут не быть методами экземпляров классов. Для примера модули могут предоставлять интерфейсы и их методы обычно просто функции. Даже экземпляры могут иметь методы не являющиеся методами экземпляров класса. Мы можем получить доступ к атрибутам определенным интерфейсом используя синтаксис доступа к элементам массива:: >>> x = IFoo['x'] >>> type(x) >>> x.__name__ 'x' >>> x.__doc__ 'X blah blah' >>> IFoo.get('x').__name__ 'x' >>> IFoo.get('y') Можно использовать `in` для определения содержит ли интерфейс определенное имя:: >>> 'x' in IFoo True Мы можем использовать итератор для интерфейсов чтобы получить все имена которые интерфейсы определяют:: >>> names = list(IFoo) >>> names.sort() >>> names ['bar', 'x'] Надо помнить, что интерфейсы не являются классами. Мы не можем получить доступ к определениям атрибутов через доступ к атрибутам интерфейсов:: >>> IFoo.x Traceback (most recent call last): File "", line 1, in ? AttributeError: 'InterfaceClass' object has no attribute 'x' Методы также предоставляют доступ к сигнатуре метода:: >>> bar = IFoo['bar'] >>> bar.getSignatureString() '(q, r=None)' Объявление интерфейсов ====================== Определив интерфейс мы можем теперь *объявить*, что объекты предоставляют их. Перед описанием деталей определим некоторые термины: *предоставлять* Мы говорим, что объекты *предоставляют* интерфейсы. Если объект предоставляет интерфейс, тогда интерфейс специфицирует поведение объекта. Другими словами, интерфейсы специфицируют поведение объектов которые предоставляют их. *реализовать* Мы обычно говорим что классы *реализуют* интерфейсы. Если класс реализует интерфейс, тогда экземпляры этого класса предоставляют данный интерфейс. Объекты предоставляют интерфейсы которые их классы реализуют [#factory]_. (Объекты также могут предоставлять интерфейсы напрямую плюс к тем которые реализуют их классы.) Важно помнить, что классы обычно не предоставляют интерфейсы которые они реализуют. Мы можем обобщить это до фабрик. Для любого вызываемого объекта мы можем объявить что он производит объекты которые предоставляют какие-либо интерфейсы сказав, что фабрика реализует данные интерфейсы. Теперь после того как мы определили эти термины мы можем поговорить об API для объявления интерфейсов. Объявление реализуемых интерфейсов ---------------------------------- Наиболее часто используемый путь для объявления интерфейсов - это использование функции implements в определении класса:: >>> class Foo: ... zope.interface.implements(IFoo) ... ... def __init__(self, x=None): ... self.x = x ... ... def bar(self, q, r=None): ... return q, r, self.x ... ... def __repr__(self): ... return "Foo(%s)" % self.x В этом примере мы объявили, что `Foo` реализует `IFoo`. Это значит, что экземпляры `Foo` предоставляют `IFoo`. После данного объявления есть несколько путей для анализа объявлений. Во-первых мы можем спросить что интерфейс реализован классом:: >>> IFoo.implementedBy(Foo) True Также мы можем спросить если интерфейс предоставляется объектами класса:: >>> foo = Foo() >>> IFoo.providedBy(foo) True Конечно `Foo` не предоставляет `IFoo`, он реализует его:: >>> IFoo.providedBy(Foo) False Мы можем также узнать какие интерфейсы реализуются объектами:: >>> list(zope.interface.implementedBy(Foo)) [] Это ошибка спрашивать про интерфейсы реализуемые не вызываемым объектом:: >>> IFoo.implementedBy(foo) Traceback (most recent call last): ... TypeError: ('ImplementedBy called for non-factory', Foo(None)) >>> list(zope.interface.implementedBy(foo)) Traceback (most recent call last): ... TypeError: ('ImplementedBy called for non-factory', Foo(None)) Также можно узнать какие интерфейсы предоставляются объектами:: >>> list(zope.interface.providedBy(foo)) [] >>> list(zope.interface.providedBy(Foo)) [] Мы можем объявить интерфейсы реализуемые другими фабриками (кроме классов). Это можно сделать используя декоратор `implementer` (в стиле Python 2.4). Для версий Python ниже 2.4 это будет выглядеть следующим образом:: >>> def yfoo(y): ... foo = Foo() ... foo.y = y ... return foo >>> yfoo = zope.interface.implementer(IFoo)(yfoo) >>> list(zope.interface.implementedBy(yfoo)) [] Надо заметить, что декоратор implementer может модифицировать свои аргументы. Вызывающая сторона не должна предполагать, что всегда будет создаваться новый объект. XXX: Double check and update these version numbers, and translate to russian: In zope.interface 3.5.1 and lower, the implementer decorator can not be used for classes, but in 3.5.2 and higher it can:: >>> Foo = zope.interface.implementer(IFoo)(Foo) >>> list(zope.interface.providedBy(Foo())) [] Note that class decorators using the @implementer(IFoo) syntax are only supported in Python 2.6 and later. Объявление предоставляемых интерфейсов -------------------------------------- Мы можем объявлять интерфейсы напрямую предоставляемые объектами. Предположим что мы хотим документировать что делает метод `__init__` класса `Foo`. Это *точно* не часть `IFoo`. Обычно мы не должны напрямую вызывать метод `__init__` для экземпляров Foo. Скорее метод `__init__` является частью метода `__call__` класса `Foo`:: >>> class IFooFactory(zope.interface.Interface): ... """Create foos""" ... ... def __call__(x=None): ... """Create a foo ... ... The argument provides the initial value for x ... ... """ У нас есть класс предоставляющий данный интерфейс, таким образом мы можем объявить интерфейс класса:: >>> zope.interface.directlyProvides(Foo, IFooFactory) Теперь мы видим, что Foo уже предоставляет интерфейсы:: >>> list(zope.interface.providedBy(Foo)) [] >>> IFooFactory.providedBy(Foo) True Объявление интерфейсов класса достаточно частая операция и для нее есть специальная функция объявления `classProvides`, которая позволяет объявлять интерфейсы при определении класса:: >>> class Foo2: ... zope.interface.implements(IFoo) ... zope.interface.classProvides(IFooFactory) ... ... def __init__(self, x=None): ... self.x = x ... ... def bar(self, q, r=None): ... return q, r, self.x ... ... def __repr__(self): ... return "Foo(%s)" % self.x >>> list(zope.interface.providedBy(Foo2)) [] >>> IFooFactory.providedBy(Foo2) True Похожая функция `moduleProvides` поддерживает объявление интерфейсов при определении модуля. Для примера смотрите использование вызова `moduleProvides` в `zope.interface.__init__`, который объявляет, что пакет `zope.interface` предоставляет `IInterfaceDeclaration`. Иногда мы хотим объявить интерфейсы экземпляров, даже если эти экземпляры уже берут интерфейсы от своих классов. Предположим, что мы создаем новый интерфейс `ISpecial`:: >>> class ISpecial(zope.interface.Interface): ... reason = zope.interface.Attribute("Reason why we're special") ... def brag(): ... "Brag about being special" Мы можем сделать созданный экземпляр foo специальным, предоставив атрибуты `reason` и `brag`:: >>> foo.reason = 'I just am' >>> def brag(): ... return "I'm special!" >>> foo.brag = brag >>> foo.reason 'I just am' >>> foo.brag() "I'm special!" и объявив интерфейс:: >>> zope.interface.directlyProvides(foo, ISpecial) таким образом новый интерфейс включается в список предоставляемых интерфейсов:: >>> ISpecial.providedBy(foo) True >>> list(zope.interface.providedBy(foo)) [, ] Мы также можем определить, что интерфейсы напрямую предоставляются объектами:: >>> list(zope.interface.directlyProvidedBy(foo)) [] >>> newfoo = Foo() >>> list(zope.interface.directlyProvidedBy(newfoo)) [] Наследуемые объявления ---------------------- Обычно объявления наследуются:: >>> class SpecialFoo(Foo): ... zope.interface.implements(ISpecial) ... reason = 'I just am' ... def brag(self): ... return "I'm special because %s" % self.reason >>> list(zope.interface.implementedBy(SpecialFoo)) [, ] >>> list(zope.interface.providedBy(SpecialFoo())) [, ] Иногда мы не хотим наследовать объявления. В этом случае мы можем использовать `implementsOnly` вместо `implements`:: >>> class Special(Foo): ... zope.interface.implementsOnly(ISpecial) ... reason = 'I just am' ... def brag(self): ... return "I'm special because %s" % self.reason >>> list(zope.interface.implementedBy(Special)) [] >>> list(zope.interface.providedBy(Special())) [] Внешние объявления ------------------ Обычно мы создаем объявления реализации как часть объявления класса. Иногда мы можем захотеть создать объявления вне объявления класса. Для примера, мы можем хотеть объявить интерфейсы для классов которые писали не мы. Для этого может использоваться функция `classImplements`:: >>> class C: ... pass >>> zope.interface.classImplements(C, IFoo) >>> list(zope.interface.implementedBy(C)) [] Мы можем использовать `classImplementsOnly` для исключения наследуемых интерфейсов:: >>> class C(Foo): ... pass >>> zope.interface.classImplementsOnly(C, ISpecial) >>> list(zope.interface.implementedBy(C)) [] Объекты объявлений ------------------ Когда мы объявляем интерфейсы, мы создаем объект *объявления*. Когда мы запрашиваем объявления возвращается объект объявления:: >>> type(zope.interface.implementedBy(Special)) Объекты объявления и объекты интерфейсов во многом похожи друг на друга. На самом деле они даже имеют общий базовый класс. Важно понять, что они могут использоваться там, где в объявлениях ожидаются интерфейсы. Вот простой пример:: >>> class Special2(Foo): ... zope.interface.implementsOnly( ... zope.interface.implementedBy(Foo), ... ISpecial, ... ) ... reason = 'I just am' ... def brag(self): ... return "I'm special because %s" % self.reason Объявление здесь практически такое же как ``zope.interface.implements(ISpecial)``, отличие только в порядке интерфейсов в итоговом объявления:: >>> list(zope.interface.implementedBy(Special2)) [, ] Наследование интерфейсов ======================== Интерфейсы могут расширять другие интерфейсы. Они делают это просто показывая эти интерфейсы как базовые:: >>> class IBlat(zope.interface.Interface): ... """Blat blah blah""" ... ... y = zope.interface.Attribute("y blah blah") ... def eek(): ... """eek blah blah""" >>> IBlat.__bases__ (,) >>> class IBaz(IFoo, IBlat): ... """Baz blah""" ... def eek(a=1): ... """eek in baz blah""" ... >>> IBaz.__bases__ (, ) >>> names = list(IBaz) >>> names.sort() >>> names ['bar', 'eek', 'x', 'y'] Заметим, что `IBaz` переопределяет eek:: >>> IBlat['eek'].__doc__ 'eek blah blah' >>> IBaz['eek'].__doc__ 'eek in baz blah' Мы были осторожны, переопределяя eek совместимым путем. Когда интерфейс расширяется, расширенный интерфейс должен быть совместимым [#compat]_ с расширяемыми интерфейсами. Мы можем запросить расширяет ли один из интерфейсов другой:: >>> IBaz.extends(IFoo) True >>> IBlat.extends(IFoo) False Заметим, что интерфейсы не расширяют сами себя:: >>> IBaz.extends(IBaz) False Если мы хотим видеть, что интерфейс расширяет сам себя,то мы можем использовать `isOrExtends`:: >>> IBaz.isOrExtends(IBaz) True >>> IBaz.isOrExtends(IFoo) True >>> IFoo.isOrExtends(IBaz) False Когда мы применяем итерацию к интерфейсу мы получаем все имена которые он определяет, включая имена определенные для базовых интерфейсов. Иногда мы хотим получить *только* имена определенные интерфейсом напрямую. Для этого мы используем метод `names`:: >>> list(IBaz.names()) ['eek'] Наследование в случае определения атрибутов -------------------------------------------- Интерфейс может переопределять определения атрибутов из базовых интерфейсов. Если два базовых интерфейса определяют один и тот же атрибут, то данный атрибут наследуется от более специфичного интерфейса. Для примера:: >>> class IBase(zope.interface.Interface): ... ... def foo(): ... "base foo doc" >>> class IBase1(IBase): ... pass >>> class IBase2(IBase): ... ... def foo(): ... "base2 foo doc" >>> class ISub(IBase1, IBase2): ... pass Определение ISub для foo будет из IBase2 т.к. IBase2 более специфичен для IBase:: >>> ISub['foo'].__doc__ 'base2 foo doc' Заметим, что это отличается от поиска в глубину. Иногда полезно узнать, что интерфейс определяет атрибут напрямую. Мы можем использовать метод `direct` для получения напрямую определенных атрибутов:: >>> IBase.direct('foo').__doc__ 'base foo doc' >>> ISub.direct('foo') Спецификации ------------ Интерфейсы и объявления - это специальные случаи спецификаций. Описание выше для наследования интерфейсов можно применить и к объявлениям и к спецификациям. Объявления фактически расширяют интерфейсы которые они объявляют:: >>> class Baz(object): ... zope.interface.implements(IBaz) >>> baz_implements = zope.interface.implementedBy(Baz) >>> baz_implements.__bases__ (, ) >>> baz_implements.extends(IFoo) True >>> baz_implements.isOrExtends(IFoo) True >>> baz_implements.isOrExtends(baz_implements) True Спецификации (интерфейсы и объявления) предоставляют атрибут `__sro__` который описывает спецификацию и всех ее предков:: >>> baz_implements.__sro__ (, , , , , ) Помеченные значения =================== Интерфейсы и описания атрибутов поддерживают механизм расширения, заимствованный из UML и называемый "помеченные значения", который позволяет сохранять дополнительные данные:: >>> IFoo.setTaggedValue('date-modified', '2004-04-01') >>> IFoo.setTaggedValue('author', 'Jim Fulton') >>> IFoo.getTaggedValue('date-modified') '2004-04-01' >>> IFoo.queryTaggedValue('date-modified') '2004-04-01' >>> IFoo.queryTaggedValue('datemodified') >>> tags = list(IFoo.getTaggedValueTags()) >>> tags.sort() >>> tags ['author', 'date-modified'] Атрибуты функций конвертируются в помеченные значения, когда создаются определения атрибутов метода:: >>> class IBazFactory(zope.interface.Interface): ... def __call__(): ... "create one" ... __call__.return_type = IBaz >>> IBazFactory['__call__'].getTaggedValue('return_type') Помеченные значения также могут быть определены внутри определения интерфейса:: >>> class IWithTaggedValues(zope.interface.Interface): ... zope.interface.taggedValue('squish', 'squash') >>> IWithTaggedValues.getTaggedValue('squish') 'squash' Инварианты ========== Интерфейсы могут описывать условия, которые должны быть соблюдены для объектов, которые их предоставляют. Эти условия описываются одним или несколькими инвариантами. Инварианты - это вызываемые объекты, которые будут вызваны с объектом, предоставляющим интерфейс в качестве параметра. Инвариант должен выкинуть исключение `Invalid` если условие не соблюдено. Например:: >>> class RangeError(zope.interface.Invalid): ... """A range has invalid limits""" ... def __repr__(self): ... return "RangeError(%r)" % self.args >>> def range_invariant(ob): ... if ob.max < ob.min: ... raise RangeError(ob) Определив этот инвариант, мы можем использовать его в определении интерфейсов:: >>> class IRange(zope.interface.Interface): ... min = zope.interface.Attribute("Lower bound") ... max = zope.interface.Attribute("Upper bound") ... ... zope.interface.invariant(range_invariant) Интерфейсы имеют метод для проверки своих инвариантов:: >>> class Range(object): ... zope.interface.implements(IRange) ... ... def __init__(self, min, max): ... self.min, self.max = min, max ... ... def __repr__(self): ... return "Range(%s, %s)" % (self.min, self.max) >>> IRange.validateInvariants(Range(1,2)) >>> IRange.validateInvariants(Range(1,1)) >>> IRange.validateInvariants(Range(2,1)) Traceback (most recent call last): ... RangeError: Range(2, 1) В случае нескольких инвариантов мы можем захотеть остановить проверку после первой ошибки. Если мы передадим в `validateInvariants` пустой список, тогда будет выкинуто единственное исключение `Invalid` со списком исключений как аргументом:: >>> from zope.interface.exceptions import Invalid >>> errors = [] >>> try: ... IRange.validateInvariants(Range(2,1), errors) ... except Invalid, e: ... str(e) '[RangeError(Range(2, 1))]' И список будет заполнен индивидуальными исключениями:: >>> errors [RangeError(Range(2, 1))] >>> del errors[:] Адаптация ========= Интерфейсы могут быть вызваны для осуществления адаптации. Эта семантика основана на функции adapt из PEP 246. Если объект не может быть адаптирован, будет выкинут TypeError:: >>> class I(zope.interface.Interface): ... pass >>> I(0) Traceback (most recent call last): ... TypeError: ('Could not adapt', 0, ) только если альтернативное значение не передано как второй аргумент:: >>> I(0, 'bob') 'bob' Если объект уже реализует нужный интерфейс, он будет возвращен:: >>> class C(object): ... zope.interface.implements(I) >>> obj = C() >>> I(obj) is obj True Если объект реализует __conform__, тогда она будет использована:: >>> class C(object): ... zope.interface.implements(I) ... def __conform__(self, proto): ... return 0 >>> I(C()) 0 Также если присутствуют функции для вызова адаптации (см. __adapt__) они будут использованы:: >>> from zope.interface.interface import adapter_hooks >>> def adapt_0_to_42(iface, obj): ... if obj == 0: ... return 42 >>> adapter_hooks.append(adapt_0_to_42) >>> I(0) 42 >>> adapter_hooks.remove(adapt_0_to_42) >>> I(0) Traceback (most recent call last): ... TypeError: ('Could not adapt', 0, ) __adapt__ --------- :: >>> class I(zope.interface.Interface): ... pass Интерфейсы реализуют метод __adapt__ из PEP 246. Этот метод обычно не вызывается напрямую. Он вызывается архитектурой адаптации из PEP 246 и методом __call__ интерфейсов. Метод адаптации отвечает за адаптацию объекта к получателю. Версия по умолчанию возвращает `None`:: >>> I.__adapt__(0) если только переданный объект не предоставляет нужный интерфейс:: >>> class C(object): ... zope.interface.implements(I) >>> obj = C() >>> I.__adapt__(obj) is obj True Функции для вызова адаптации могут быть добавлены (или удалены) для предоставления адаптации "на заказ". Мы установим глупую функцию которая адаптирует 0 к 42. Мы устанавливаем функцию просто добавляя ее к списку `adapter_hooks`:: >>> from zope.interface.interface import adapter_hooks >>> def adapt_0_to_42(iface, obj): ... if obj == 0: ... return 42 >>> adapter_hooks.append(adapt_0_to_42) >>> I.__adapt__(0) 42 Функции должны возвращать либо адаптер, либо `None`, если адаптер не найден. Функции могут быть удалены удалением их из списка:: >>> adapter_hooks.remove(adapt_0_to_42) >>> I.__adapt__(0) .. [#create] Основная причина по которой мы наследуемся от `Interface` - это что бы быть уверенными в том, что ключевое слово class будет создавать интерфейс, а не класс. Есть возможность создать интерфейсы вызвав специальный класс интерфейса напрямую. Делая это, возможно (и в редких случаях полезно) создать интерфейсы которые не наследуются от `Interface`. Однако использование этой техники выходит за рамки данного документа. .. [#factory] Классы - это фабрики. Они могут быть вызваны для создания своих экземпляров. Мы ожидаем что в итоге мы расширим концепцию реализации на другие типы фабрик, таким образом мы сможем объявлять интерфейсы предоставляемые созданными фабриками объектами. .. [#compat] Цель - заменяемость. Объект который предоставляет расширенный интерфейс должен быть заменяем в качестве объектов которые предоставляют расширяемый интерфейс. В нашем примере объект который предоставляет IBaz должен быть используемым и в случае если ожидается объект который предоставляет IBlat. Реализация интерфейса не требует этого. Но возможно в дальнейшем она должна будет делать какие-либо проверки. zope.interface-6.4/docs/_static/000077500000000000000000000000001462121350100166505ustar00rootroot00000000000000zope.interface-6.4/docs/_static/.gitignore000066400000000000000000000000001462121350100206260ustar00rootroot00000000000000zope.interface-6.4/docs/adapter.rst000066400000000000000000000446011462121350100174010ustar00rootroot00000000000000.. _adapter-registry: ================== Adapter Registry ================== Adapter registries provide a way to register objects that depend on one or more interface specifications and provide (perhaps indirectly) some interface. In addition, the registrations have names. (You can think of the names as qualifiers of the provided interfaces.) The term "interface specification" refers both to interfaces and to interface declarations, such as declarations of interfaces implemented by a class. Single Adapters =============== Let's look at a simple example, using a single required specification: .. doctest:: >>> from zope.interface.adapter import AdapterRegistry >>> import zope.interface >>> class IRequireBase(zope.interface.Interface): ... pass >>> class IProvideBase(zope.interface.Interface): ... pass >>> class IProvideChild(IProvideBase): ... pass >>> registry = AdapterRegistry() We'll register an object that depends on ``IRequireBase`` and "provides" ``IProvideChild``: .. doctest:: >>> registry.register([IRequireBase], IProvideChild, '', 'Base->Child') Given the registration, we can look it up again: .. doctest:: >>> registry.lookup([IRequireBase], IProvideChild, '') 'Base->Child' Note that we used an integer in the example. In real applications, one would use some objects that actually depend on or provide interfaces. The registry doesn't care about what gets registered, so we'll use integers and strings to keep the examples simple. There is one exception. Registering a value of ``None`` unregisters any previously-registered value. If an object depends on a specification, it can be looked up with a specification that extends the specification that it depends on: .. doctest:: >>> class IRequireChild(IRequireBase): ... pass >>> registry.lookup([IRequireChild], IProvideChild, '') 'Base->Child' We can use a class implementation specification to look up the object: .. doctest:: >>> @zope.interface.implementer(IRequireChild) ... class C2: ... pass >>> registry.lookup([zope.interface.implementedBy(C2)], IProvideChild, '') 'Base->Child' and it can be looked up for interfaces that its provided interface extends: .. doctest:: >>> registry.lookup([IRequireBase], IProvideBase, '') 'Base->Child' >>> registry.lookup([IRequireChild], IProvideBase, '') 'Base->Child' But if you require a specification that doesn't extend the specification the object depends on, you won't get anything: .. doctest:: >>> registry.lookup([zope.interface.Interface], IProvideBase, '') By the way, you can pass a default value to lookup: .. doctest:: >>> registry.lookup([zope.interface.Interface], IProvideBase, '', 42) 42 If you try to get an interface the object doesn't provide, you also won't get anything: .. doctest:: >>> class IProvideGrandchild(IProvideChild): ... pass >>> registry.lookup([IRequireBase], IProvideGrandchild, '') You also won't get anything if you use the wrong name: .. doctest:: >>> registry.lookup([IRequireBase], IProvideBase, 'bob') >>> registry.register([IRequireBase], IProvideChild, 'bob', "Bob's 12") >>> registry.lookup([IRequireBase], IProvideBase, 'bob') "Bob's 12" You can leave the name off when doing a lookup: .. doctest:: >>> registry.lookup([IRequireBase], IProvideBase) 'Base->Child' If we register an object that provides ``IProvideBase``: .. doctest:: >>> registry.register([IRequireBase], IProvideBase, '', 'Base->Base') then that object will be preferred over ``O('Base->Child')``: .. doctest:: >>> registry.lookup([IRequireBase], IProvideBase, '') 'Base->Base' Also, if we register an object for ``IRequireChild``, then that will be preferred when using ``IRequireChild``: .. doctest:: >>> registry.register([IRequireChild], IProvideBase, '', 'Child->Base') >>> registry.lookup([IRequireChild], IProvideBase, '') 'Child->Base' Finding out what, if anything, is registered -------------------------------------------- We can ask if there is an adapter registered for a collection of interfaces. This is different than lookup, because it looks for an exact match: .. doctest:: >>> print(registry.registered([IRequireBase], IProvideBase)) Base->Base >>> print(registry.registered([IRequireBase], IProvideChild)) Base->Child >>> print(registry.registered([IRequireBase], IProvideChild, 'bob')) Bob's 12 >>> print(registry.registered([IRequireChild], IProvideBase)) Child->Base >>> print(registry.registered([IRequireChild], IProvideChild)) None In the last example, ``None`` was returned because nothing was registered exactly for the given interfaces. lookup1 ------- Lookup of single adapters is common enough that there is a specialized version of lookup that takes a single required interface: .. doctest:: >>> registry.lookup1(IRequireChild, IProvideBase, '') 'Child->Base' >>> registry.lookup1(IRequireChild, IProvideBase) 'Child->Base' Actual Adaptation ----------------- The adapter registry is intended to support adaptation, where one object that implements an interface is adapted to another object that supports a different interface. The adapter registry supports the computation of adapters. In this case, we have to register adapter factories: .. doctest:: >>> class IR(zope.interface.Interface): ... pass >>> @zope.interface.implementer(IR) ... class X(object): ... pass >>> @zope.interface.implementer(IProvideBase) ... class Y(object): ... def __init__(self, context): ... self.context = context >>> registry.register([IR], IProvideBase, '', Y) In this case, we registered a class as the factory. Now we can call ``queryAdapter`` to get the adapted object: .. doctest:: >>> x = X() >>> y = registry.queryAdapter(x, IProvideBase) >>> y.__class__.__name__ 'Y' >>> y.context is x True We can register and lookup by name too: .. doctest:: >>> class Y2(Y): ... pass >>> registry.register([IR], IProvideBase, 'bob', Y2) >>> y = registry.queryAdapter(x, IProvideBase, 'bob') >>> y.__class__.__name__ 'Y2' >>> y.context is x True Passing ``super`` objects works as expected to find less specific adapters: .. doctest:: >>> class IDerived(IR): ... pass >>> @zope.interface.implementer(IDerived) ... class Derived(X): ... pass >>> class DerivedAdapter(Y): ... def query_next(self): ... return registry.queryAdapter( ... super(type(self.context), self.context), ... IProvideBase) >>> registry.register([IDerived], IProvideBase, '', DerivedAdapter) >>> derived = Derived() >>> adapter = registry.queryAdapter(derived, IProvideBase) >>> adapter.__class__.__name__ 'DerivedAdapter' >>> adapter = adapter.query_next() >>> adapter.__class__.__name__ 'Y' When the adapter factory produces ``None``, then this is treated as if no adapter has been found. This allows us to prevent adaptation (when desired) and let the adapter factory determine whether adaptation is possible based on the state of the object being adapted: .. doctest:: >>> def factory(context): ... if context.name == 'object': ... return 'adapter' ... return None >>> @zope.interface.implementer(IR) ... class Object(object): ... name = 'object' >>> registry.register([IR], IProvideBase, 'conditional', factory) >>> obj = Object() >>> registry.queryAdapter(obj, IProvideBase, 'conditional') 'adapter' >>> obj.name = 'no object' >>> registry.queryAdapter(obj, IProvideBase, 'conditional') is None True >>> registry.queryAdapter(obj, IProvideBase, 'conditional', 'default') 'default' An alternate method that provides the same function as ``queryAdapter()`` is `adapter_hook()`: .. doctest:: >>> y = registry.adapter_hook(IProvideBase, x) >>> y.__class__.__name__ 'Y' >>> y.context is x True >>> y = registry.adapter_hook(IProvideBase, x, 'bob') >>> y.__class__.__name__ 'Y2' >>> y.context is x True The ``adapter_hook()`` simply switches the order of the object and interface arguments. It is used to hook into the interface call mechanism. Default Adapters ---------------- Sometimes, you want to provide an adapter that will adapt anything. For that, provide ``None`` as the required interface: .. doctest:: >>> registry.register([None], IProvideBase, '', 1) then we can use that adapter for interfaces we don't have specific adapters for: .. doctest:: >>> class IQ(zope.interface.Interface): ... pass >>> registry.lookup([IQ], IProvideBase, '') 1 Of course, specific adapters are still used when applicable: .. doctest:: >>> registry.lookup([IRequireChild], IProvideBase, '') 'Child->Base' Class adapters -------------- You can register adapters for class declarations, which is almost the same as registering them for a class: .. doctest:: >>> registry.register([zope.interface.implementedBy(C2)], IProvideBase, '', 'C21') >>> registry.lookup([zope.interface.implementedBy(C2)], IProvideBase, '') 'C21' Dict adapters ------------- At some point it was impossible to register dictionary-based adapters due a bug. Let's make sure this works now: .. doctest:: >>> adapter = {} >>> registry.register((), IQ, '', adapter) >>> registry.lookup((), IQ, '') is adapter True Unregistering ------------- You can unregister by registering ``None``, rather than an object: .. doctest:: >>> registry.register([zope.interface.implementedBy(C2)], IProvideBase, '', None) >>> registry.lookup([zope.interface.implementedBy(C2)], IProvideBase, '') 'Child->Base' Of course, this means that ``None`` can't be registered. This is an exception to the statement, made earlier, that the registry doesn't care what gets registered. Multi-adapters ============== You can adapt multiple specifications: .. doctest:: >>> registry.register([IRequireBase, IQ], IProvideChild, '', '1q2') >>> registry.lookup([IRequireBase, IQ], IProvideChild, '') '1q2' >>> registry.lookup([IRequireChild, IQ], IProvideBase, '') '1q2' >>> class IS(zope.interface.Interface): ... pass >>> registry.lookup([IRequireChild, IS], IProvideBase, '') >>> class IQ2(IQ): ... pass >>> registry.lookup([IRequireChild, IQ2], IProvideBase, '') '1q2' >>> registry.register([IRequireBase, IQ2], IProvideChild, '', '(Base,Q2)->Child') >>> registry.lookup([IRequireChild, IQ2], IProvideBase, '') '(Base,Q2)->Child' Multi-adaptation ---------------- You can adapt multiple objects: .. doctest:: >>> @zope.interface.implementer(IQ) ... class Q: ... pass As with single adapters, we register a factory, which is often a class: .. doctest:: >>> class IM(zope.interface.Interface): ... pass >>> @zope.interface.implementer(IM) ... class M: ... def __init__(self, x, q): ... self.x, self.q = x, q >>> registry.register([IR, IQ], IM, '', M) And then we can call ``queryMultiAdapter`` to compute an adapter: .. doctest:: >>> q = Q() >>> m = registry.queryMultiAdapter((x, q), IM) >>> m.__class__.__name__ 'M' >>> m.x is x and m.q is q True and, of course, we can use names: .. doctest:: >>> class M2(M): ... pass >>> registry.register([IR, IQ], IM, 'bob', M2) >>> m = registry.queryMultiAdapter((x, q), IM, 'bob') >>> m.__class__.__name__ 'M2' >>> m.x is x and m.q is q True Default Adapters ---------------- As with single adapters, you can define default adapters by specifying ``None`` for the *first* specification: .. doctest:: >>> registry.register([None, IQ], IProvideChild, '', '(None,Q)->Child') >>> registry.lookup([IS, IQ], IProvideChild, '') '(None,Q)->Child' Null Adapters ============= You can also adapt **no** specification: .. doctest:: >>> registry.register([], IProvideChild, '', '[]->Child') >>> registry.lookup([], IProvideChild, '') '[]->Child' >>> registry.lookup([], IProvideBase, '') '[]->Child' Listing named adapters ---------------------- Adapters are named. Sometimes, it's useful to get all of the named adapters for given interfaces: .. doctest:: >>> adapters = list(registry.lookupAll([IRequireBase], IProvideBase)) >>> adapters.sort() >>> assert adapters == [(u'', 'Base->Base'), (u'bob', "Bob's 12")] This works for multi-adapters too: .. doctest:: >>> registry.register([IRequireBase, IQ2], IProvideChild, 'bob', '(Base,Q2)->Child for bob') >>> adapters = list(registry.lookupAll([IRequireChild, IQ2], IProvideBase)) >>> adapters.sort() >>> assert adapters == [(u'', '(Base,Q2)->Child'), (u'bob', '(Base,Q2)->Child for bob')] And even null adapters: .. doctest:: >>> registry.register([], IProvideChild, 'bob', 3) >>> adapters = list(registry.lookupAll([], IProvideBase)) >>> adapters.sort() >>> assert adapters == [(u'', '[]->Child'), (u'bob', 3)] Subscriptions ============= Normally, we want to look up an object that most closely matches a specification. Sometimes, we want to get all of the objects that match some specification. We use *subscriptions* for this. We subscribe objects against specifications and then later find all of the subscribed objects: .. doctest:: >>> registry.subscribe([IRequireBase], IProvideChild, 'Base->Child (1)') >>> registry.subscriptions([IRequireBase], IProvideChild) ['Base->Child (1)'] Note that, unlike regular adapters, subscriptions are unnamed. You can have multiple subscribers for the same specification: .. doctest:: >>> registry.subscribe([IRequireBase], IProvideChild, 'Base->Child (2)') >>> registry.subscriptions([IRequireBase], IProvideChild) ['Base->Child (1)', 'Base->Child (2)'] If subscribers are registered for the same required interfaces, they are returned in the order of definition. You can register subscribers for all specifications using ``None``: .. doctest:: >>> registry.subscribe([None], IProvideBase, 'None->Base') >>> registry.subscriptions([IRequireChild], IProvideBase) ['None->Base', 'Base->Child (1)', 'Base->Child (2)'] Note that the new subscriber is returned first. Subscribers defined for less specific required interfaces are returned before subscribers for more specific interfaces: .. doctest:: >>> class IRequireGrandchild(IRequireChild): ... pass >>> registry.subscribe([IRequireChild], IProvideBase, 'Child->Base') >>> registry.subscribe([IRequireGrandchild], IProvideBase, 'Grandchild->Base') >>> registry.subscriptions([IRequireGrandchild], IProvideBase) ['None->Base', 'Base->Child (1)', 'Base->Child (2)', 'Child->Base', 'Grandchild->Base'] Subscriptions may be combined over multiple compatible specifications: .. doctest:: >>> registry.subscriptions([IRequireChild], IProvideBase) ['None->Base', 'Base->Child (1)', 'Base->Child (2)', 'Child->Base'] >>> registry.subscribe([IRequireBase], IProvideBase, 'Base->Base') >>> registry.subscriptions([IRequireChild], IProvideBase) ['None->Base', 'Base->Child (1)', 'Base->Child (2)', 'Base->Base', 'Child->Base'] >>> registry.subscribe([IRequireChild], IProvideChild, 'Child->Child') >>> registry.subscriptions([IRequireChild], IProvideBase) ['None->Base', 'Base->Child (1)', 'Base->Child (2)', 'Base->Base', 'Child->Child', 'Child->Base'] >>> registry.subscriptions([IRequireChild], IProvideChild) ['Base->Child (1)', 'Base->Child (2)', 'Child->Child'] Subscriptions can be on multiple specifications: .. doctest:: >>> registry.subscribe([IRequireBase, IQ], IProvideChild, '(Base,Q)->Child') >>> registry.subscriptions([IRequireBase, IQ], IProvideChild) ['(Base,Q)->Child'] As with single subscriptions and non-subscription adapters, you can specify ``None`` for the first required interface, to specify a default: .. doctest:: >>> registry.subscribe([None, IQ], IProvideChild, '(None,Q)->Child') >>> registry.subscriptions([IS, IQ], IProvideChild) ['(None,Q)->Child'] >>> registry.subscriptions([IRequireBase, IQ], IProvideChild) ['(None,Q)->Child', '(Base,Q)->Child'] You can have subscriptions that are independent of any specifications: .. doctest:: >>> list(registry.subscriptions([], IProvideBase)) [] >>> registry.subscribe([], IProvideChild, 'sub2') >>> registry.subscriptions([], IProvideBase) ['sub2'] >>> registry.subscribe([], IProvideBase, 'sub1') >>> registry.subscriptions([], IProvideBase) ['sub2', 'sub1'] >>> registry.subscriptions([], IProvideChild) ['sub2'] Unregistering subscribers ------------------------- We can unregister subscribers. When unregistering a subscriber, we can unregister a *specific* subscriber: .. doctest:: >>> registry.unsubscribe([IRequireBase], IProvideBase, 'Base->Base') >>> registry.subscriptions([IRequireBase], IProvideBase) ['None->Base', 'Base->Child (1)', 'Base->Child (2)'] If we don't specify a value, then *all* subscribers matching the given interfaces will be unsubscribed: .. doctest:: >>> registry.unsubscribe([IRequireBase], IProvideChild) >>> registry.subscriptions([IRequireBase], IProvideBase) ['None->Base'] Subscription adapters --------------------- We normally register adapter factories, which then allow us to compute adapters, but with subscriptions, we get multiple adapters. Here's an example of multiple-object subscribers: .. doctest:: >>> registry.subscribe([IR, IQ], IM, M) >>> registry.subscribe([IR, IQ], IM, M2) >>> subscribers = registry.subscribers((x, q), IM) >>> len(subscribers) 2 >>> class_names = [s.__class__.__name__ for s in subscribers] >>> class_names.sort() >>> class_names ['M', 'M2'] >>> [(s.x is x and s.q is q) for s in subscribers] [True, True] Adapter factory subscribers can't return ``None`` values: .. doctest:: >>> def M3(x, y): ... return None >>> registry.subscribe([IR, IQ], IM, M3) >>> subscribers = registry.subscribers((x, q), IM) >>> len(subscribers) 2 Handlers -------- A handler is a subscriber factory that doesn't produce any normal output. It returns ``None``. A handler is unlike adapters in that it does all of its work when the factory is called. To register a handler, simply provide ``None`` as the provided interface: .. doctest:: >>> def handler(event): ... print('handler', event) >>> registry.subscribe([IRequireBase], None, handler) >>> registry.subscriptions([IRequireBase], None) == [handler] True Components ========== A :class:`zope.interface.registry.Components` object implements the :class:`zope.interface.interfaces.IComponents` interface. This interface uses multiple adapter registries to implement multiple higher-level concerns (utilities, adapters and handlers), while also providing event notifications and query capabilities. zope.interface-6.4/docs/adapter.ru.rst000066400000000000000000000512611462121350100200260ustar00rootroot00000000000000================ Реестр адаптеров ================ .. contents:: Реестры адаптеров предоставляют возможность для регистрации объектов которые зависят от одной, или нескольких спецификаций интерфейсов и предоставляют (возможно не напрямую) какой-либо интерфейс. В дополнение, регистрации имеют имена. (Можно думать об именах как о спецификаторах предоставляемого интерфейса.) Термин "спецификация интерфейса" ссылается и на интерфейсы и на определения интерфейсов, такие как определения интерфейсов реализованных некоторым классом. Одиночные адаптеры ================== Давайте рассмотрим простой пример использующий единственную требуемую спецификацию:: >>> from zope.interface.adapter import AdapterRegistry >>> import zope.interface >>> class IR1(zope.interface.Interface): ... pass >>> class IP1(zope.interface.Interface): ... pass >>> class IP2(IP1): ... pass >>> registry = AdapterRegistry() Мы зарегистрируем объект который зависит от IR1 и "предоставляет" IP2:: >>> registry.register([IR1], IP2, '', 12) После регистрации мы можем запросить объект снова:: >>> registry.lookup([IR1], IP2, '') 12 Заметьте, что мы используем целое в этом примере. В реальных приложениях вы можете использовать объекты которые на самом деле зависят или предоставляют интерфейсы. Реестр не заботиться о том, что регистрируется и таким образом мы можем использовать целые, или строки что бы упростить наши примеры. Здесь есть одно исключение. Регистрация значения None удаляет регистрацию для любого зарегистрированного прежде значения. Если объект зависит от спецификации он может быть запрошен с помощью спецификации которая расширяет спецификацию от которой он зависит:: >>> class IR2(IR1): ... pass >>> registry.lookup([IR2], IP2, '') 12 Мы можем использовать класс реализующий спецификацию для запроса объекта:: >>> class C2: ... zope.interface.implements(IR2) >>> registry.lookup([zope.interface.implementedBy(C2)], IP2, '') 12 и объект может быть запрошен для интерфейсов которые предоставляемый объектом интерфейс расширяет:: >>> registry.lookup([IR1], IP1, '') 12 >>> registry.lookup([IR2], IP1, '') 12 Но если вы требуете спецификацию которая не расширяет спецификацию от которой зависит объект, вы не получите ничего:: >>> registry.lookup([zope.interface.Interface], IP1, '') Между прочим, вы можете передать значение по умолчанию при запросе:: >>> registry.lookup([zope.interface.Interface], IP1, '', 42) 42 Если вы пробуете получить интерфейс который объект не предоставляет вы также не получите ничего:: >>> class IP3(IP2): ... pass >>> registry.lookup([IR1], IP3, '') Вы также не получите ничего если вы используете неверное имя:: >>> registry.lookup([IR1], IP1, 'bob') >>> registry.register([IR1], IP2, 'bob', "Bob's 12") >>> registry.lookup([IR1], IP1, 'bob') "Bob's 12" Вы можете не использовать имя при запросе:: >>> registry.lookup([IR1], IP1) 12 Если мы регистрируем объект который предоставляет IP1:: >>> registry.register([IR1], IP1, '', 11) тогда этот объект будет иметь преимущество перед O(12):: >>> registry.lookup([IR1], IP1, '') 11 Также, если мы регистрируем объект для IR2 тогда он будет иметь преимущество когда используется IR2:: >>> registry.register([IR2], IP1, '', 21) >>> registry.lookup([IR2], IP1, '') 21 Поиск того, что (если вообще что-то) зарегистрировано ----------------------------------------------------- Мы можем спросить есть-ли адаптер зарегистрированный для набора интерфейсов. Это отличается от обычного запроса так как здесь мы ищем точное совпадение:: >>> print registry.registered([IR1], IP1) 11 >>> print registry.registered([IR1], IP2) 12 >>> print registry.registered([IR1], IP2, 'bob') Bob's 12 >>> print registry.registered([IR2], IP1) 21 >>> print registry.registered([IR2], IP2) None В последнем примере, None был возвращен потому, что для данного интерфейса ничего не было зарегистрировано. lookup1 ------- Запрос одиночного адаптера - это наиболее частая операция и для нее есть специализированная версия запроса которая получает на вход единственный требуемый интерфейс:: >>> registry.lookup1(IR2, IP1, '') 21 >>> registry.lookup1(IR2, IP1) 21 Адаптация на практике --------------------- Реестр адаптеров предназначен для поддержки адаптации когда один объект реализующий интерфейс адаптируется к другому объекту который поддерживает другой интерфейс. Реестр адаптеров также поддерживает вычисление адаптеров. В этом случае мы должны регистрировать фабрики для адаптеров:: >>> class IR(zope.interface.Interface): ... pass >>> class X: ... zope.interface.implements(IR) >>> class Y: ... zope.interface.implements(IP1) ... def __init__(self, context): ... self.context = context >>> registry.register([IR], IP1, '', Y) В этом случае мы регистрируем класс как фабрику. Теперь мы можем вызвать `queryAdapter` для получения адаптированного объекта:: >>> x = X() >>> y = registry.queryAdapter(x, IP1) >>> y.__class__.__name__ 'Y' >>> y.context is x True Мы также можем регистрировать и запрашивать по имени:: >>> class Y2(Y): ... pass >>> registry.register([IR], IP1, 'bob', Y2) >>> y = registry.queryAdapter(x, IP1, 'bob') >>> y.__class__.__name__ 'Y2' >>> y.context is x True Когда фабрика для адаптера возвращает `None` - это рассматривается как если бы адаптер не был найден. Это позволяет нам избежать адаптации (по желанию) и дает возможность фабрике адаптера определить возможна ли адаптация основываясь на состоянии объекта который адаптируется:: >>> def factory(context): ... if context.name == 'object': ... return 'adapter' ... return None >>> class Object(object): ... zope.interface.implements(IR) ... name = 'object' >>> registry.register([IR], IP1, 'conditional', factory) >>> obj = Object() >>> registry.queryAdapter(obj, IP1, 'conditional') 'adapter' >>> obj.name = 'no object' >>> registry.queryAdapter(obj, IP1, 'conditional') is None True >>> registry.queryAdapter(obj, IP1, 'conditional', 'default') 'default' Альтернативный метод для предоставления такой же функциональности как и `queryAdapter()` - это `adapter_hook()`:: >>> y = registry.adapter_hook(IP1, x) >>> y.__class__.__name__ 'Y' >>> y.context is x True >>> y = registry.adapter_hook(IP1, x, 'bob') >>> y.__class__.__name__ 'Y2' >>> y.context is x True `adapter_hook()` просто меняет порядок аргументов для объекта и интерфейса. Это используется для встраивания в механизм вызовов интерфейсов. Адаптеры по умолчанию --------------------- Иногда вы можете захотеть предоставить адаптер который не будет ничего адаптировать. Для этого нужно передать None как требуемый интерфейс:: >>> registry.register([None], IP1, '', 1) после этого вы можете использовать этот адаптер для интерфейсов для которых у вас нет конкретного адаптера:: >>> class IQ(zope.interface.Interface): ... pass >>> registry.lookup([IQ], IP1, '') 1 Конечно, конкретные адаптеры все еще используются когда необходимо:: >>> registry.lookup([IR2], IP1, '') 21 Адаптеры классов ---------------- Вы можете регистрировать адаптеры для определений классов, что будет похоже на регистрацию их для классов:: >>> registry.register([zope.interface.implementedBy(C2)], IP1, '', 'C21') >>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '') 'C21' Адаптеры для словарей --------------------- В какой-то момент было невозможно регистрировать адаптеры основанные на словарях из-за ошибки. Давайте удостоверимся что это теперь работает:: >>> adapter = {} >>> registry.register((), IQ, '', adapter) >>> registry.lookup((), IQ, '') is adapter True Удаление регистрации -------------------- Вы можете удалить регистрацию регистрируя None вместо объекта:: >>> registry.register([zope.interface.implementedBy(C2)], IP1, '', None) >>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '') 21 Конечно это значит, что None не может быть зарегистрирован. Это исключение к утверждению выше о том, что реестр не заботиться о том, что регистрируется. Мульти-адаптеры =============== Вы можете адаптировать несколько спецификаций:: >>> registry.register([IR1, IQ], IP2, '', '1q2') >>> registry.lookup([IR1, IQ], IP2, '') '1q2' >>> registry.lookup([IR2, IQ], IP1, '') '1q2' >>> class IS(zope.interface.Interface): ... pass >>> registry.lookup([IR2, IS], IP1, '') >>> class IQ2(IQ): ... pass >>> registry.lookup([IR2, IQ2], IP1, '') '1q2' >>> registry.register([IR1, IQ2], IP2, '', '1q22') >>> registry.lookup([IR2, IQ2], IP1, '') '1q22' Мульти-адаптация ---------------- Вы можете адаптировать несколько объектов:: >>> class Q: ... zope.interface.implements(IQ) Как и с одиночными адаптерами, мы регистрируем фабрику которая возвращает класс:: >>> class IM(zope.interface.Interface): ... pass >>> class M: ... zope.interface.implements(IM) ... def __init__(self, x, q): ... self.x, self.q = x, q >>> registry.register([IR, IQ], IM, '', M) И затем мы можем вызвать `queryMultiAdapter` для вычисления адаптера:: >>> q = Q() >>> m = registry.queryMultiAdapter((x, q), IM) >>> m.__class__.__name__ 'M' >>> m.x is x and m.q is q True и, конечно, мы можем использовать имена:: >>> class M2(M): ... pass >>> registry.register([IR, IQ], IM, 'bob', M2) >>> m = registry.queryMultiAdapter((x, q), IM, 'bob') >>> m.__class__.__name__ 'M2' >>> m.x is x and m.q is q True Адаптеры по умолчанию --------------------- Как и для одиночных адаптеров вы можете определить адаптер по умолчанию передав None вместо *первой* спецификации:: >>> registry.register([None, IQ], IP2, '', 'q2') >>> registry.lookup([IS, IQ], IP2, '') 'q2' Нулевые адаптеры ================ Вы можете также адаптировать без спецификации:: >>> registry.register([], IP2, '', 2) >>> registry.lookup([], IP2, '') 2 >>> registry.lookup([], IP1, '') 2 Перечисление именованных адаптеров ---------------------------------- Адаптеры имеют имена. Иногда это полезно для получения всех именованных адаптеров для заданного интерфейса:: >>> adapters = list(registry.lookupAll([IR1], IP1)) >>> adapters.sort() >>> assert adapters == [(u'', 11), (u'bob', "Bob's 12")] Это работает также и для мульти-адаптеров:: >>> registry.register([IR1, IQ2], IP2, 'bob', '1q2 for bob') >>> adapters = list(registry.lookupAll([IR2, IQ2], IP1)) >>> adapters.sort() >>> assert adapters == [(u'', '1q22'), (u'bob', '1q2 for bob')] И даже для нулевых адаптеров:: >>> registry.register([], IP2, 'bob', 3) >>> adapters = list(registry.lookupAll([], IP1)) >>> adapters.sort() >>> assert adapters == [(u'', 2), (u'bob', 3)] Подписки ======== Обычно мы хотим запросить объект который наиболее близко соответствует спецификации. Иногда мы хотим получить все объекты которые соответствуют какой-либо спецификации. Мы используем подписки для этого. Мы подписываем объекты для спецификаций и затем позже находим все подписанные объекты:: >>> registry.subscribe([IR1], IP2, 'sub12 1') >>> registry.subscriptions([IR1], IP2) ['sub12 1'] Заметьте, что в отличие от обычных адаптеров подписки не имеют имен. Вы можете иметь несколько подписчиков для одной спецификации:: >>> registry.subscribe([IR1], IP2, 'sub12 2') >>> registry.subscriptions([IR1], IP2) ['sub12 1', 'sub12 2'] Если подписчики зарегистрированы для одних и тех же требуемых интерфейсов, они возвращаются в порядке определения. Вы можете зарегистрировать подписчики для всех спецификаций используя None:: >>> registry.subscribe([None], IP1, 'sub_1') >>> registry.subscriptions([IR2], IP1) ['sub_1', 'sub12 1', 'sub12 2'] Заметьте, что новый подписчик возвращается первым. Подписчики определенные для менее общих требуемых интерфейсов возвращаются перед подписчиками для более общих интерфейсов. Подписки могут смешиваться между несколькими совместимыми спецификациями:: >>> registry.subscriptions([IR2], IP1) ['sub_1', 'sub12 1', 'sub12 2'] >>> registry.subscribe([IR1], IP1, 'sub11') >>> registry.subscriptions([IR2], IP1) ['sub_1', 'sub12 1', 'sub12 2', 'sub11'] >>> registry.subscribe([IR2], IP2, 'sub22') >>> registry.subscriptions([IR2], IP1) ['sub_1', 'sub12 1', 'sub12 2', 'sub11', 'sub22'] >>> registry.subscriptions([IR2], IP2) ['sub12 1', 'sub12 2', 'sub22'] Подписки могут существовать для нескольких спецификаций:: >>> registry.subscribe([IR1, IQ], IP2, 'sub1q2') >>> registry.subscriptions([IR1, IQ], IP2) ['sub1q2'] Как и с одиночными подписчиками и адаптерами без подписок, вы можете определить None для первого требуемого интерфейса, что бы задать значение по умолчанию:: >>> registry.subscribe([None, IQ], IP2, 'sub_q2') >>> registry.subscriptions([IS, IQ], IP2) ['sub_q2'] >>> registry.subscriptions([IR1, IQ], IP2) ['sub_q2', 'sub1q2'] Вы можете создать подписки которые независимы от любых спецификаций:: >>> list(registry.subscriptions([], IP1)) [] >>> registry.subscribe([], IP2, 'sub2') >>> registry.subscriptions([], IP1) ['sub2'] >>> registry.subscribe([], IP1, 'sub1') >>> registry.subscriptions([], IP1) ['sub2', 'sub1'] >>> registry.subscriptions([], IP2) ['sub2'] Удаление регистрации подписчиков -------------------------------- Мы можем удалять регистрацию подписчиков. При удалении регистрации подписчика мы можем удалить регистрацию заданного адаптера:: >>> registry.unsubscribe([IR1], IP1, 'sub11') >>> registry.subscriptions([IR1], IP1) ['sub_1', 'sub12 1', 'sub12 2'] Если мы не задаем никакого значения тогда подписки будут удалены для всех подписчиков совпадающих с заданным интерфейсом:: >>> registry.unsubscribe([IR1], IP2) >>> registry.subscriptions([IR1], IP1) ['sub_1'] Адаптеры подписки ----------------- Обычно мы регистрируем фабрики для адаптеров которые затем позволяют нам вычислять адаптеры, но с подписками мы получаем несколько адаптеров. Это пример подписчика для нескольких объектов:: >>> registry.subscribe([IR, IQ], IM, M) >>> registry.subscribe([IR, IQ], IM, M2) >>> subscribers = registry.subscribers((x, q), IM) >>> len(subscribers) 2 >>> class_names = [s.__class__.__name__ for s in subscribers] >>> class_names.sort() >>> class_names ['M', 'M2'] >>> [(s.x is x and s.q is q) for s in subscribers] [True, True] подписчики фабрик адаптеров не могут возвращать None:: >>> def M3(x, y): ... return None >>> registry.subscribe([IR, IQ], IM, M3) >>> subscribers = registry.subscribers((x, q), IM) >>> len(subscribers) 2 Обработчики ----------- Обработчик - это подписанная фабрика которая не возвращает нормального значения. Она возвращает None. Обработчик отличается от адаптеров тем, что он делает всю работу когда вызывается фабрика. Для регистрации обработчика надо просто передать None как предоставляемый интерфейс:: >>> def handler(event): ... print 'handler', event >>> registry.subscribe([IR1], None, handler) >>> registry.subscriptions([IR1], None) == [handler] True zope.interface-6.4/docs/api/000077500000000000000000000000001462121350100157735ustar00rootroot00000000000000zope.interface-6.4/docs/api/adapters.rst000066400000000000000000000012631462121350100203320ustar00rootroot00000000000000================== Adapter Registry ================== Usage of the adapter registry is documented in :ref:`adapter-registry`. The adapter registry's API is defined by :class:`zope.interface.interfaces.IAdapterRegistry`: .. autointerface:: zope.interface.interfaces.IAdapterRegistry :members: :member-order: bysource The concrete implementations of ``IAdapterRegistry`` provided by this package allows for some customization opportunities. .. autoclass:: zope.interface.adapter.BaseAdapterRegistry :members: :private-members: .. autoclass:: zope.interface.adapter.AdapterRegistry :members: .. autoclass:: zope.interface.adapter.VerifyingAdapterRegistry :members: zope.interface-6.4/docs/api/common.rst000066400000000000000000000024031462121350100200140ustar00rootroot00000000000000==================================== Python Standard Library Interfaces ==================================== The ``zope.interface.common`` package provides interfaces for objects distributed as part of the Python standard library. Importing these modules (usually) has the effect of making the standard library objects implement the correct interface. zope.interface.common.interface =============================== .. automodule:: zope.interface.common.interfaces zope.interface.common.idatetime =============================== .. automodule:: zope.interface.common.idatetime zope.interface.common.collections ================================= .. automodule:: zope.interface.common.collections zope.interface.common.numbers ============================= .. automodule:: zope.interface.common.numbers zope.interface.common.builtins ============================== .. automodule:: zope.interface.common.builtins zope.interface.common.io ======================== .. automodule:: zope.interface.common.io .. Deprecated or discouraged modules below this zope.interface.common.mapping ============================= .. automodule:: zope.interface.common.mapping zope.interface.common.sequence ============================== .. automodule:: zope.interface.common.sequence zope.interface-6.4/docs/api/components.rst000066400000000000000000000046411462121350100207170ustar00rootroot00000000000000====================== Component Registries ====================== The component registry's API is defined by ``zope.interface.interfaces.IComponents``: .. autointerface:: zope.interface.interfaces.IComponentLookup :members: :member-order: bysource .. autointerface:: zope.interface.interfaces.IComponentRegistry :members: :member-order: bysource .. autointerface:: zope.interface.interfaces.IComponents :members: :member-order: bysource One default implementation of `~zope.interface.interfaces.IComponents` is provided. .. autoclass:: zope.interface.registry.Components Events ====== Adding and removing components from the component registry create registration and unregistration events. Like most things, they are defined by an interface and a default implementation is provided. Registration ------------ .. autointerface:: zope.interface.interfaces.IObjectEvent .. autointerface:: zope.interface.interfaces.IRegistrationEvent .. autointerface:: zope.interface.interfaces.IRegistered .. autoclass:: zope.interface.interfaces.Registered .. autointerface:: zope.interface.interfaces.IUnregistered .. autoclass:: zope.interface.interfaces.Unregistered Details ------- These are all types of ``IObjectEvent``, meaning they have an object that provides specific details about the event. Component registries create detail objects for four types of components they manage. All four share a common base interface. .. autointerface:: zope.interface.interfaces.IRegistration * Utilities .. autointerface:: zope.interface.interfaces.IUtilityRegistration .. autoclass:: zope.interface.registry.UtilityRegistration * Handlers .. autointerface:: zope.interface.interfaces.IHandlerRegistration .. autoclass:: zope.interface.registry.HandlerRegistration * Adapters For ease of implementation, a shared base class is used for these events. It should not be referenced by clients, but it is documented to show the common attributes. .. autointerface:: zope.interface.interfaces._IBaseAdapterRegistration .. autointerface:: zope.interface.interfaces.IAdapterRegistration .. autoclass:: zope.interface.registry.AdapterRegistration .. autointerface:: zope.interface.interfaces.ISubscriptionAdapterRegistration .. autoclass:: zope.interface.registry.SubscriptionRegistration Exceptions ========== .. autoclass:: zope.interface.interfaces.ComponentLookupError .. autoclass:: zope.interface.interfaces.Invalid zope.interface-6.4/docs/api/declarations.rst000066400000000000000000000523621462121350100212050ustar00rootroot00000000000000================================================== Declaring and Checking The Interfaces of Objects ================================================== Declaring what interfaces an object implements or provides, and later being able to check those, is an important part of this package. Declaring interfaces, in particular, can be done both statically at object definition time and dynamically later on. The functionality that allows declaring and checking interfaces is provided directly in the ``zope.interface`` module. It is described by the interface ``zope.interface.interfaces.IInterfaceDeclaration``. We will first look at that interface, and then we will look more carefully at each object it documents, including providing examples. .. autointerface:: zope.interface.interfaces.IInterfaceDeclaration .. currentmodule:: zope.interface Declaring Interfaces ==================== To declare an interface itself, extend the ``Interface`` base class. .. autointerface:: Interface :noindex: .. autofunction:: taggedValue :noindex: .. documented more thoroughly in README.rst .. autofunction:: invariant :noindex: .. documented in README.rst .. autofunction:: interfacemethod Declaring The Interfaces of Objects =================================== implementer ----------- .. autoclass:: implementer implementer_only ---------------- .. autoclass:: implementer_only classImplementsOnly ------------------- .. autofunction:: classImplementsOnly Consider the following example: .. doctest:: >>> from zope.interface import implementedBy >>> from zope.interface import implementer >>> from zope.interface import classImplementsOnly >>> from zope.interface import Interface >>> class I1(Interface): pass ... >>> class I2(Interface): pass ... >>> class I3(Interface): pass ... >>> class I4(Interface): pass ... >>> @implementer(I3) ... class A(object): ... pass >>> @implementer(I4) ... class B(object): ... pass >>> class C(A, B): ... pass >>> classImplementsOnly(C, I1, I2) >>> [i.getName() for i in implementedBy(C)] ['I1', 'I2'] Instances of ``C`` provide only ``I1``, ``I2``, and regardless of whatever interfaces instances of ``A`` and ``B`` implement. classImplements --------------- .. autofunction:: classImplements Consider the following example: .. doctest:: >>> from zope.interface import Interface >>> from zope.interface import classImplements >>> from zope.interface.ro import is_consistent >>> class I1(Interface): pass ... >>> class I2(Interface): pass ... >>> class IA(Interface): pass ... >>> class IB(Interface): pass ... >>> class I5(Interface): pass ... >>> @implementer(IA) ... class A(object): ... pass >>> @implementer(IB) ... class B(object): ... pass >>> class C(A, B): ... pass >>> classImplements(C, I1, I2) >>> [i.getName() for i in implementedBy(C)] ['I1', 'I2', 'IA', 'IB'] Instances of ``C`` provide ``I1`` and ``I2``, plus whatever instances of ``A`` and ``B`` provide. .. doctest:: >>> classImplements(C, I5) >>> [i.getName() for i in implementedBy(C)] ['I1', 'I2', 'I5', 'IA', 'IB'] Instances of ``C`` now also provide ``I5``. Notice how ``I5`` was added to the *end* of the list of things provided directly by ``C``. If we ask a class to implement an interface that extends an interface it already implements, that interface will go at the *beginning* of the list, in order to preserve a consistent resolution order. .. doctest:: >>> class I6(I5): pass >>> class I7(IA): pass >>> classImplements(C, I6, I7) >>> [i.getName() for i in implementedBy(C)] ['I6', 'I1', 'I2', 'I5', 'I7', 'IA', 'IB'] >>> is_consistent(implementedBy(C)) True This cannot be used to introduce duplicates. .. doctest:: >>> classImplements(C, IA, IB, I1, I2) >>> [i.getName() for i in implementedBy(C)] ['I6', 'I1', 'I2', 'I5', 'I7', 'IA', 'IB'] classImplementsFirst -------------------- .. autofunction:: classImplementsFirst Consider the following example: .. doctest:: >>> from zope.interface import Interface >>> from zope.interface import classImplements >>> from zope.interface import classImplementsFirst >>> class I1(Interface): pass ... >>> class I2(Interface): pass ... >>> class IA(Interface): pass ... >>> class IB(Interface): pass ... >>> class I5(Interface): pass ... >>> @implementer(IA) ... class A(object): ... pass >>> @implementer(IB) ... class B(object): ... pass >>> class C(A, B): ... pass >>> classImplementsFirst(C, I2) >>> classImplementsFirst(C, I1) >>> [i.getName() for i in implementedBy(C)] ['I1', 'I2', 'IA', 'IB'] Instances of ``C`` provide ``I1``, ``I2``, ``I5``, and whatever interfaces instances of ``A`` and ``B`` provide. .. doctest:: >>> classImplementsFirst(C, I5) >>> [i.getName() for i in implementedBy(C)] ['I5', 'I1', 'I2', 'IA', 'IB'] Instances of ``C`` now also provide ``I5``. Notice how ``I5`` was added to the *beginning* of the list of things provided directly by ``C``. Unlike `classImplements`, this ignores interface inheritance and does not attempt to ensure a consistent resolution order (except that it continues to elide interfaces already implemented through class inheritance):: .. doctest:: >>> class IBA(IB, IA): ... pass >>> classImplementsFirst(C, IBA) >>> classImplementsFirst(C, IA) >>> [i.getName() for i in implementedBy(C)] ['IBA', 'I5', 'I1', 'I2', 'IA', 'IB'] This cannot be used to introduce duplicates. .. doctest:: >>> len(implementedBy(C).declared) 4 >>> classImplementsFirst(C, IA) >>> classImplementsFirst(C, IBA) >>> classImplementsFirst(C, IA) >>> classImplementsFirst(C, IBA) >>> [i.getName() for i in implementedBy(C)] ['IBA', 'I5', 'I1', 'I2', 'IA', 'IB'] >>> len(implementedBy(C).declared) 4 directlyProvides ---------------- .. autofunction:: directlyProvides Consider the following example: .. doctest:: >>> from zope.interface import Interface >>> from zope.interface import providedBy >>> from zope.interface import directlyProvides >>> class I1(Interface): pass ... >>> class I2(Interface): pass ... >>> class IA1(Interface): pass ... >>> class IA2(Interface): pass ... >>> class IB(Interface): pass ... >>> class IC(Interface): pass ... >>> @implementer(IA1, IA2) ... class A(object): ... pass >>> @implementer(IB) ... class B(object): ... pass >>> @implementer(IC) ... class C(A, B): ... pass >>> ob = C() >>> directlyProvides(ob, I1, I2) >>> int(I1 in providedBy(ob)) 1 >>> int(I2 in providedBy(ob)) 1 >>> int(IA1 in providedBy(ob)) 1 >>> int(IA2 in providedBy(ob)) 1 >>> int(IB in providedBy(ob)) 1 >>> int(IC in providedBy(ob)) 1 The object, ``ob`` provides ``I1``, ``I2``, and whatever interfaces instances have been declared for instances of ``C``. To remove directly provided interfaces, use ``directlyProvidedBy`` and subtract the unwanted interfaces. For example: .. doctest:: >>> from zope.interface import directlyProvidedBy >>> directlyProvides(ob, directlyProvidedBy(ob)-I2) >>> int(I1 in providedBy(ob)) 1 >>> int(I2 in providedBy(ob)) 0 removes ``I2`` from the interfaces directly provided by ``ob``. The object, ``ob`` no longer directly provides ``I2``, although it might still provide ``I2`` if its class implements ``I2``. To add directly provided interfaces, use ``directlyProvidedBy`` and include additional interfaces. For example: .. doctest:: >>> int(I2 in providedBy(ob)) 0 >>> from zope.interface import directlyProvidedBy >>> directlyProvides(ob, directlyProvidedBy(ob), I2) adds ``I2`` to the interfaces directly provided by ``ob``: .. doctest:: >>> int(I2 in providedBy(ob)) 1 We need to avoid setting this attribute on meta classes that don't support descriptors. We can do away with this check when we get rid of the old EC alsoProvides ------------ .. autofunction:: alsoProvides Consider the following example: .. doctest:: >>> from zope.interface import Interface >>> from zope.interface import alsoProvides >>> class I1(Interface): pass ... >>> class I2(Interface): pass ... >>> class IA1(Interface): pass ... >>> class IA2(Interface): pass ... >>> class IB(Interface): pass ... >>> class IC(Interface): pass ... >>> @implementer(IA1, IA2) ... class A(object): ... pass >>> @implementer(IB) ... class B(object): ... pass >>> @implementer(IC) ... class C(A, B): ... pass >>> ob = C() >>> directlyProvides(ob, I1) >>> int(I1 in providedBy(ob)) 1 >>> int(I2 in providedBy(ob)) 0 >>> int(IA1 in providedBy(ob)) 1 >>> int(IA2 in providedBy(ob)) 1 >>> int(IB in providedBy(ob)) 1 >>> int(IC in providedBy(ob)) 1 >>> alsoProvides(ob, I2) >>> int(I1 in providedBy(ob)) 1 >>> int(I2 in providedBy(ob)) 1 >>> int(IA1 in providedBy(ob)) 1 >>> int(IA2 in providedBy(ob)) 1 >>> int(IB in providedBy(ob)) 1 >>> int(IC in providedBy(ob)) 1 The object, ``ob`` provides ``I1``, ``I2``, and whatever interfaces instances have been declared for instances of ``C``. Notice that the ``alsoProvides`` just extends the provided interfaces. noLongerProvides ---------------- .. autofunction:: noLongerProvides Consider the following two interfaces: .. doctest:: >>> from zope.interface import Interface >>> class I1(Interface): pass ... >>> class I2(Interface): pass ... ``I1`` is provided through the class, ``I2`` is directly provided by the object: .. doctest:: >>> @implementer(I1) ... class C(object): ... pass >>> c = C() >>> alsoProvides(c, I2) >>> I2.providedBy(c) True Remove ``I2`` from ``c`` again: .. doctest:: >>> from zope.interface import noLongerProvides >>> noLongerProvides(c, I2) >>> I2.providedBy(c) False Removing an interface that is provided through the class is not possible: .. doctest:: >>> noLongerProvides(c, I1) Traceback (most recent call last): ... ValueError: Can only remove directly provided interfaces. provider -------- .. autoclass:: provider For example: .. doctest:: >>> from zope.interface import Interface >>> from zope.interface import implementer >>> from zope.interface import provider >>> class IFooFactory(Interface): ... pass >>> class IFoo(Interface): ... pass >>> @implementer(IFoo) ... @provider(IFooFactory) ... class C(object): ... pass >>> [i.getName() for i in C.__provides__] ['IFooFactory'] >>> [i.getName() for i in C().__provides__] ['IFoo'] Which is equivalent to: .. doctest:: >>> from zope.interface import Interface >>> class IFoo(Interface): pass ... >>> class IFooFactory(Interface): pass ... >>> @implementer(IFoo) ... class C(object): ... pass >>> directlyProvides(C, IFooFactory) >>> [i.getName() for i in C.__providedBy__] ['IFooFactory'] >>> [i.getName() for i in C().__providedBy__] ['IFoo'] moduleProvides -------------- .. autofunction:: moduleProvides named ----- .. autoclass:: zope.interface.declarations.named For example: .. doctest:: >>> from zope.interface.declarations import named >>> @named('foo') ... class Foo(object): ... pass >>> Foo.__component_name__ 'foo' When registering an adapter or utility component, the registry looks for the ``__component_name__`` attribute and uses it, if no name was explicitly provided. Querying The Interfaces Of Objects ================================== All of these functions return an `~zope.interface.interfaces.IDeclaration`. You'll notice that an ``IDeclaration`` is a type of `~zope.interface.interfaces.ISpecification`, as is ``zope.interface.Interface``, so they share some common behaviour. .. autointerface:: zope.interface.interfaces.IDeclaration :members: :member-order: bysource implementedBy ------------- .. autofunction:: implementedBy Consider the following example: .. doctest:: >>> from zope.interface import Interface >>> from zope.interface import implementer >>> from zope.interface import classImplementsOnly >>> from zope.interface import implementedBy >>> class I1(Interface): pass ... >>> class I2(Interface): pass ... >>> class I3(Interface): pass ... >>> class I4(Interface): pass ... >>> @implementer(I3) ... class A(object): ... pass >>> @implementer(I4) ... class B(object): ... pass >>> class C(A, B): ... pass >>> classImplementsOnly(C, I1, I2) >>> [i.getName() for i in implementedBy(C)] ['I1', 'I2'] Instances of ``C`` provide only ``I1``, ``I2``, and regardless of whatever interfaces instances of ``A`` and ``B`` implement. Another example: .. doctest:: >>> from zope.interface import Interface >>> class I1(Interface): pass ... >>> class I2(I1): pass ... >>> class I3(Interface): pass ... >>> class I4(I3): pass ... >>> @implementer(I2) ... class C1(object): ... pass >>> @implementer(I3) ... class C2(C1): ... pass >>> [i.getName() for i in implementedBy(C2)] ['I3', 'I2'] Really, any object should be able to receive a successful answer, even an instance: .. doctest:: >>> class Callable(object): ... def __call__(self): ... return self >>> implementedBy(Callable()) classImplements(builtins.?) Note that the name of the spec ends with a '?', because the ``Callable`` instance does not have a ``__name__`` attribute. This also manages storage of implementation specifications. providedBy ---------- .. autofunction:: providedBy directlyProvidedBy ------------------ .. autofunction:: directlyProvidedBy Classes ======= Declarations ------------ Declaration objects implement the API defined by :class:`~zope.interface.interfaces.IDeclaration`. .. autoclass:: Declaration Exmples for :meth:`Declaration.__contains__`: .. doctest:: >>> from zope.interface.declarations import Declaration >>> from zope.interface import Interface >>> class I1(Interface): pass ... >>> class I2(I1): pass ... >>> class I3(Interface): pass ... >>> class I4(I3): pass ... >>> spec = Declaration(I2, I3) >>> spec = Declaration(I4, spec) >>> int(I1 in spec) 0 >>> int(I2 in spec) 1 >>> int(I3 in spec) 1 >>> int(I4 in spec) 1 Exmples for :meth:`Declaration.__iter__`: .. doctest:: >>> from zope.interface import Interface >>> class I1(Interface): pass ... >>> class I2(I1): pass ... >>> class I3(Interface): pass ... >>> class I4(I3): pass ... >>> spec = Declaration(I2, I3) >>> spec = Declaration(I4, spec) >>> i = iter(spec) >>> [x.getName() for x in i] ['I4', 'I2', 'I3'] >>> list(i) [] Exmples for :meth:`Declaration.flattened`: .. doctest:: >>> from zope.interface import Interface >>> class I1(Interface): pass ... >>> class I2(I1): pass ... >>> class I3(Interface): pass ... >>> class I4(I3): pass ... >>> spec = Declaration(I2, I3) >>> spec = Declaration(I4, spec) >>> i = spec.flattened() >>> [x.getName() for x in i] ['I4', 'I2', 'I3', 'I1', 'Interface'] >>> list(i) [] Exmples for :meth:`Declaration.__sub__`: .. doctest:: >>> from zope.interface import Interface >>> class I1(Interface): pass ... >>> class I2(I1): pass ... >>> class I3(Interface): pass ... >>> class I4(I3): pass ... >>> spec = Declaration() >>> [iface.getName() for iface in spec] [] >>> spec -= I1 >>> [iface.getName() for iface in spec] [] >>> spec -= Declaration(I2) >>> [iface.getName() for iface in spec] [] >>> spec = Declaration(I2, I4) >>> [iface.getName() for iface in spec] ['I2', 'I4'] >>> [iface.getName() for iface in spec - I4] ['I2'] >>> [iface.getName() for iface in spec - I1] ['I4'] >>> [iface.getName() for iface ... in spec - Declaration(I4)] ['I2'] Exmples for :meth:`Declaration.__add__`: .. doctest:: >>> from zope.interface import Interface >>> class IRoot1(Interface): pass ... >>> class IDerived1(IRoot1): pass ... >>> class IRoot2(Interface): pass ... >>> class IDerived2(IRoot2): pass ... >>> spec = Declaration() >>> [iface.getName() for iface in spec] [] >>> [iface.getName() for iface in spec+IRoot1] ['IRoot1'] >>> [iface.getName() for iface in IRoot1+spec] ['IRoot1'] >>> spec2 = spec >>> spec += IRoot1 >>> [iface.getName() for iface in spec] ['IRoot1'] >>> [iface.getName() for iface in spec2] [] >>> spec2 += Declaration(IDerived2, IRoot2) >>> [iface.getName() for iface in spec2] ['IDerived2', 'IRoot2'] >>> [iface.getName() for iface in spec+spec2] ['IRoot1', 'IDerived2', 'IRoot2'] >>> [iface.getName() for iface in spec2+spec] ['IDerived2', 'IRoot2', 'IRoot1'] >>> [iface.getName() for iface in (spec+spec2).__bases__] ['IRoot1', 'IDerived2', 'IRoot2'] >>> [iface.getName() for iface in (spec2+spec).__bases__] ['IDerived2', 'IRoot2', 'IRoot1'] ProvidesClass ------------- .. autoclass:: zope.interface.declarations.ProvidesClass Descriptor semantics (via ``Provides.__get__``): .. doctest:: >>> from zope.interface import Interface >>> class IFooFactory(Interface): ... pass >>> class C(object): ... pass >>> from zope.interface.declarations import ProvidesClass >>> C.__provides__ = ProvidesClass(C, IFooFactory) >>> [i.getName() for i in C.__provides__] ['IFooFactory'] >>> getattr(C(), '__provides__', 0) 0 Implementation Details ====================== The following section discusses some implementation details and demonstrates their use. You'll notice that they are all demonstrated using the previously-defined functions. Provides -------- .. autofunction:: Provides In the examples below, we are going to make assertions about the size of the weakvalue dictionary. For the assertions to be meaningful, we need to force garbage collection to make sure garbage objects are, indeed, removed from the system. Depending on how Python is run, we may need to make multiple calls to be sure. We provide a collect function to help with this: .. doctest:: >>> import gc >>> def collect(): ... for i in range(4): ... gc.collect() >>> collect() >>> from zope.interface import directlyProvides >>> from zope.interface.declarations import InstanceDeclarations >>> before = len(InstanceDeclarations) >>> class C(object): ... pass >>> from zope.interface import Interface >>> class I(Interface): ... pass >>> c1 = C() >>> c2 = C() >>> len(InstanceDeclarations) == before True >>> directlyProvides(c1, I) >>> len(InstanceDeclarations) == before + 1 True >>> directlyProvides(c2, I) >>> len(InstanceDeclarations) == before + 1 True >>> del c1 >>> collect() >>> len(InstanceDeclarations) == before + 1 True >>> del c2 >>> collect() >>> len(InstanceDeclarations) == before True ObjectSpecification ------------------- .. autofunction:: zope.interface.declarations.ObjectSpecification For example: .. doctest:: >>> from zope.interface import Interface >>> from zope.interface import implementer_only >>> class I1(Interface): pass ... >>> class I2(Interface): pass ... >>> class I3(Interface): pass ... >>> class I31(I3): pass ... >>> class I4(Interface): pass ... >>> class I5(Interface): pass ... >>> @implementer(I1) ... class A(object): ... pass >>> class B(object): ... __implemented__ = I2 >>> @implementer(I31) ... class C(A, B): ... pass >>> c = C() >>> directlyProvides(c, I4) >>> [i.getName() for i in providedBy(c)] ['I4', 'I31', 'I1', 'I2'] >>> [i.getName() for i in providedBy(c).flattened()] ['I4', 'I31', 'I3', 'I1', 'I2', 'Interface'] >>> int(I1 in providedBy(c)) 1 >>> int(I3 in providedBy(c)) 0 >>> int(providedBy(c).extends(I3)) 1 >>> int(providedBy(c).extends(I31)) 1 >>> int(providedBy(c).extends(I5)) 0 >>> @implementer_only(I31) ... class COnly(A, B): ... pass >>> @implementer(I5) ... class D(COnly): ... pass >>> c = D() >>> directlyProvides(c, I4) >>> [i.getName() for i in providedBy(c)] ['I4', 'I5', 'I31'] >>> [i.getName() for i in providedBy(c).flattened()] ['I4', 'I5', 'I31', 'I3', 'Interface'] >>> int(I1 in providedBy(c)) 0 >>> int(I3 in providedBy(c)) 0 >>> int(providedBy(c).extends(I3)) 1 >>> int(providedBy(c).extends(I1)) 0 >>> int(providedBy(c).extends(I31)) 1 >>> int(providedBy(c).extends(I5)) 1 ObjectSpecificationDescriptor ----------------------------- .. autoclass:: zope.interface.declarations.ObjectSpecificationDescriptor For example: .. doctest:: >>> from zope.interface import Interface >>> class IFoo(Interface): pass ... >>> class IFooFactory(Interface): pass ... >>> @implementer(IFoo) ... @provider(IFooFactory) ... class C(object): ... pass >>> [i.getName() for i in C.__providedBy__] ['IFooFactory'] >>> [i.getName() for i in C().__providedBy__] ['IFoo'] Get an ObjectSpecification bound to either an instance or a class, depending on how we were accessed. zope.interface-6.4/docs/api/index.rst000066400000000000000000000002451462121350100176350ustar00rootroot00000000000000=============== API Reference =============== Contents: .. toctree:: :maxdepth: 2 specifications declarations adapters components common ro zope.interface-6.4/docs/api/ro.rst000066400000000000000000000012571462121350100171520ustar00rootroot00000000000000=========================================== Computing The Resolution Order (Priority) =========================================== Just as Python classes have a method resolution order that determines which implementation of a method gets used when inheritance is used, interfaces have a resolution order that determines their ordering when searching for adapters. That order is computed by ``zope.interface.ro.ro``. This is an internal module not generally needed by a user of ``zope.interface``, but its documentation can be helpful to understand how orders are computed. ``zope.interface.ro`` ===================== .. automodule:: zope.interface.ro :member-order: alphabetical zope.interface-6.4/docs/api/specifications.rst000066400000000000000000000233341462121350100215350ustar00rootroot00000000000000========================== Interface Specifications ========================== .. currentmodule:: zope.interface.interfaces This document discusses the actual interface objects themselves. We begin with a basic concept of specifying an object's behaviour (with an `ISpecification`), and then we describe the way we write such a specification (`IInterface`). Combinations of specifications (e.g., an object that provides multiple interfaces) are covered by `IDeclaration`. Specification ============= Specification objects implement the API defined by :class:`ISpecification`: .. autointerface:: ISpecification :members: :member-order: bysource .. autoclass:: zope.interface.interface.Specification :no-members: For example: .. doctest:: >>> from zope.interface.interface import Specification >>> from zope.interface import Interface >>> class I1(Interface): ... pass >>> class I2(I1): ... pass >>> class I3(I2): ... pass >>> [i.__name__ for i in I1.__bases__] ['Interface'] >>> [i.__name__ for i in I2.__bases__] ['I1'] >>> I3.extends(I1) True >>> I2.__bases__ = (Interface, ) >>> [i.__name__ for i in I2.__bases__] ['Interface'] >>> I3.extends(I1) False Exmples for :meth:`.Specification.providedBy`: .. doctest:: >>> from zope.interface import * >>> class I1(Interface): ... pass >>> @implementer(I1) ... class C(object): ... pass >>> c = C() >>> class X(object): ... pass >>> x = X() >>> I1.providedBy(x) False >>> I1.providedBy(C) False >>> I1.providedBy(c) True >>> directlyProvides(x, I1) >>> I1.providedBy(x) True >>> directlyProvides(C, I1) >>> I1.providedBy(C) True Examples for :meth:`.Specification.isOrExtends`: .. doctest:: >>> from zope.interface import Interface >>> from zope.interface.declarations import Declaration >>> class I1(Interface): pass ... >>> class I2(I1): pass ... >>> class I3(Interface): pass ... >>> class I4(I3): pass ... >>> spec = Declaration() >>> int(spec.extends(Interface)) 1 >>> spec = Declaration(I2) >>> int(spec.extends(Interface)) 1 >>> int(spec.extends(I1)) 1 >>> int(spec.extends(I2)) 1 >>> int(spec.extends(I3)) 0 >>> int(spec.extends(I4)) 0 Examples for :meth:`.Specification.interfaces`: .. doctest:: >>> from zope.interface import Interface >>> class I1(Interface): pass ... >>> class I2(I1): pass ... >>> class I3(Interface): pass ... >>> class I4(I3): pass ... >>> spec = Specification((I2, I3)) >>> spec = Specification((I4, spec)) >>> i = spec.interfaces() >>> [x.getName() for x in i] ['I4', 'I2', 'I3'] >>> list(i) [] Exmples for :meth:`.Specification.extends`: .. doctest:: >>> from zope.interface import Interface >>> from zope.interface.declarations import Declaration >>> class I1(Interface): pass ... >>> class I2(I1): pass ... >>> class I3(Interface): pass ... >>> class I4(I3): pass ... >>> spec = Declaration() >>> int(spec.extends(Interface)) 1 >>> spec = Declaration(I2) >>> int(spec.extends(Interface)) 1 >>> int(spec.extends(I1)) 1 >>> int(spec.extends(I2)) 1 >>> int(spec.extends(I3)) 0 >>> int(spec.extends(I4)) 0 >>> I2.extends(I2) False >>> I2.extends(I2, False) True >>> I2.extends(I2, strict=False) True .. _spec_eq_hash: Equality, Hashing, and Comparisons ---------------------------------- Specifications (including their notable subclass `Interface`), are hashed and compared (sorted) based solely on their ``__name__`` and ``__module__``, not including any information about their enclosing scope, if any (e.g., their ``__qualname__``). This means that any two objects created with the same name and module are considered equal and map to the same value in a dictionary. .. doctest:: >>> from zope.interface import Interface >>> class I1(Interface): pass >>> orig_I1 = I1 >>> class I1(Interface): pass >>> I1 is orig_I1 False >>> I1 == orig_I1 True >>> d = {I1: 42} >>> d[orig_I1] 42 >>> def make_nested(): ... class I1(Interface): pass ... return I1 >>> nested_I1 = make_nested() >>> I1 == orig_I1 == nested_I1 True Caveats ~~~~~~~ While this behaviour works well with :ref:`pickling (persistence) `, it has some potential downsides to be aware of. .. rubric:: Weak References The first downside involves weak references. Because weak references hash the same as their underlying object, this can lead to surprising results when weak references are involved, especially if there are cycles involved or if the garbage collector is not based on reference counting (e.g., PyPy). For example, if you redefine an interface named the same as an interface being used in a ``WeakKeyDictionary``, you can get a ``KeyError``, even if you put the new interface into the dictionary. .. doctest:: >>> from zope.interface import Interface >>> import gc >>> from weakref import WeakKeyDictionary >>> wr_dict = WeakKeyDictionary() >>> class I1(Interface): pass >>> wr_dict[I1] = 42 >>> orig_I1 = I1 # Make sure it stays alive >>> class I1(Interface): pass >>> wr_dict[I1] = 2020 >>> del orig_I1 >>> _ = gc.collect() # Sometime later, gc runs and makes sure the original is gone >>> wr_dict[I1] # Cleaning up the original weakref removed the new one Traceback (most recent call last): ... KeyError: ... This is mostly likely a problem in test cases where it is tempting to use the same named interfaces in different test methods. If references to them escape, especially if they are used as the bases of other interfaces, you may find surprising ``KeyError`` exceptions. For this reason, it is best to use distinct names for local interfaces within the same test module. .. rubric:: Providing Dynamic Interfaces If you return an interface created inside a function or method, or otherwise let it escape outside the bounds of that function (such as by having an object provide it), it's important to be aware that it will compare and hash equal to *any* other interface defined in that same module with the same name. This includes interface objects created by other invocations of that function. This can lead to surprising results when querying against those interfaces. We can demonstrate by creating a module-level interface with a common name, and checking that it is provided by an object: .. doctest:: >>> from zope.interface import Interface, alsoProvides, providedBy >>> class ICommon(Interface): ... pass >>> class Obj(object): ... pass >>> obj = Obj() >>> alsoProvides(obj, ICommon) >>> len(list(providedBy(obj))) 1 >>> ICommon.providedBy(obj) True Next, in the same module, we will define a function that dynamically creates an interface of the same name and adds it to an object. .. doctest:: >>> def add_interfaces(obj): ... class ICommon(Interface): ... pass ... class I2(Interface): ... pass ... alsoProvides(obj, ICommon, I2) ... return ICommon ... >>> dynamic_ICommon = add_interfaces(obj) The two instances are *not* identical, but they are equal, and *obj* provides them both: .. doctest:: >>> ICommon is dynamic_ICommon False >>> ICommon == dynamic_ICommon True >>> ICommon.providedBy(obj) True >>> dynamic_ICommon.providedBy(obj) True At this point, we've effectively called ``alsoProvides(obj, ICommon, dynamic_ICommon, I2)``, where the last two interfaces were locally defined in the function. So checking how many interfaces *obj* now provides should return three, right? .. doctest:: >>> len(list(providedBy(obj))) 2 Because ``ICommon == dynamic_ICommon`` due to having the same ``__name__`` and ``__module__``, only one of them is actually provided by the object, for a total of two provided interfaces. (Exactly which one is undefined.) Likewise, if we run the same function again, *obj* will still only provide two interfaces .. doctest:: >>> _ = add_interfaces(obj) >>> len(list(providedBy(obj))) 2 Interface ========= Interfaces are a particular type of `ISpecification` and implement the API defined by :class:`IInterface`. Before we get there, we need to discuss two related concepts. The first is that of an "element", which provides us a simple way to query for information generically (this is important because we'll see that ``IInterface`` implements this interface): .. IElement defines __doc__ to be an Attribute, so the docstring in the class isn't used._ .. autointerface:: IElement Objects that have basic documentation and tagged values. Known derivatives include :class:`IAttribute` and its derivative :class:`IMethod`; these have no notion of inheritance. :class:`IInterface` is also a derivative, and it does have a notion of inheritance, expressed through its ``__bases__`` and ordered in its ``__iro__`` (both defined by :class:`ISpecification`). .. autoclass:: zope.interface.interface.Element :no-members: Next, we look at ``IAttribute`` and ``IMethod``. These make up the content, or body, of an ``Interface``. .. autointerface:: zope.interface.interfaces.IAttribute .. autoclass:: zope.interface.interface.Attribute :no-members: .. autointerface:: IMethod .. autoclass:: zope.interface.interface.Method :no-members: Finally we can look at the definition of ``IInterface``. .. autointerface:: IInterface .. autointerface:: zope.interface.Interface Usage ----- Exmples for :meth:`InterfaceClass.extends`: .. doctest:: >>> from zope.interface import Interface >>> class I1(Interface): pass ... >>> >>> i = I1.interfaces() >>> [x.getName() for x in i] ['I1'] >>> list(i) [] zope.interface-6.4/docs/changes.rst000066400000000000000000000000341462121350100173610ustar00rootroot00000000000000.. include:: ../CHANGES.rst zope.interface-6.4/docs/conf.py000066400000000000000000000216521462121350100165270ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # zope.interface documentation build configuration file, created by # sphinx-quickstart on Mon Mar 26 16:31:31 2012. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # import sys, os # All configuration values have a default; values that are commented out # serve to show the default. import sys import os import pkg_resources sys.path.append(os.path.abspath('../src')) rqmt = pkg_resources.require('zope.interface')[0] # Import and document the pure-python versions of things; they tend to have better # docstrings and signatures. os.environ['PURE_PYTHON'] = '1' # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', 'repoze.sphinx.autointerface', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'zope.interface' copyright = '2012-2023, Zope Foundation contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '%s.%s' % tuple(rqmt.version.split('.')[:2]) # The full version, including alpha/beta/rc tags. release = rqmt.version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. default_role = 'obj' # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'zopeinterfacedoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # pdflatex can't handle Cyrillic out of the box, but xetext/lualatex should be # able to cope latex_engine = 'lualatex' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'zopeinterface.tex', 'zope.interface Documentation', 'Zope Foundation contributors', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'zopeinterface', 'zope.interface Documentation', ['Zope Foundation contributors'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'zopeinterface', 'zope.interface Documentation', 'Zope Foundation contributors', 'zopeinterface', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'persistent': ('https://persistent.readthedocs.io/en/latest/', None), 'btrees': ('https://btrees.readthedocs.io/en/latest/', None), } # Sphinx 1.8+ prefers this to `autodoc_default_flags`. It's documented that # either True or None mean the same thing as just setting the flag, but # only None works in 1.8 (True works in 2.0) autodoc_default_options = { 'members': None, 'show-inheritance': None, } autodoc_member_order = 'bysource' autoclass_content = 'both' zope.interface-6.4/docs/foodforthought.rst000066400000000000000000000033161462121350100210200ustar00rootroot00000000000000================================ Food-based subscription examples ================================ This file gives more subscription examples using a cooking-based example: .. doctest:: >>> from zope.interface.adapter import AdapterRegistry >>> registry = AdapterRegistry() >>> import zope.interface >>> class IAnimal(zope.interface.Interface): ... pass >>> class IPoultry(IAnimal): ... pass >>> class IChicken(IPoultry): ... pass >>> class ISeafood(IAnimal): ... pass Adapting to some other interface for which there is no subscription adapter returns an empty sequence: .. doctest:: >>> class IRecipe(zope.interface.Interface): ... pass >>> class ISausages(IRecipe): ... pass >>> class INoodles(IRecipe): ... pass >>> class IKFC(IRecipe): ... pass >>> list(registry.subscriptions([IPoultry], IRecipe)) [] unless we define a subscription: .. doctest:: >>> registry.subscribe([IAnimal], ISausages, 'sausages') >>> list(registry.subscriptions([IPoultry], ISausages)) ['sausages'] And define another subscription adapter: .. doctest:: >>> registry.subscribe([IPoultry], INoodles, 'noodles') >>> meals = list(registry.subscriptions([IPoultry], IRecipe)) >>> meals.sort() >>> meals ['noodles', 'sausages'] >>> registry.subscribe([IChicken], IKFC, 'kfc') >>> meals = list(registry.subscriptions([IChicken], IRecipe)) >>> meals.sort() >>> meals ['kfc', 'noodles', 'sausages'] And the answer for poultry hasn't changed: .. doctest:: >>> meals = list(registry.subscriptions([IPoultry], IRecipe)) >>> meals.sort() >>> meals ['noodles', 'sausages'] zope.interface-6.4/docs/hacking.rst000066400000000000000000000224451462121350100173670ustar00rootroot00000000000000Hacking on :mod:`zope.interface` ================================ Getting the Code ################ The main repository for :mod:`zope.interface` is in the Zope Foundation Github repository: https://github.com/zopefoundation/zope.interface You can get a read-only checkout from there: .. code-block:: sh $ git clone https://github.com/zopefoundation/zope.interface.git or fork it and get a writeable checkout of your fork: .. code-block:: sh $ git clone git@github.com/jrandom/zope.interface.git The project also mirrors the trunk from the Github repository as a Bazaar branch on Launchpad: https://code.launchpad.net/zope.interface You can branch the trunk from there using Bazaar: .. code-block:: sh $ bzr branch lp:zope.interface Working in a ``virtualenv`` ########################### Running the tests ----------------- If you use the ``virtualenv`` package to create lightweight Python development environments, you can run the tests using nothing more than the ``python`` binary in a virtualenv. First, create a scratch environment: .. code-block:: sh $ /path/to/virtualenv --no-site-packages /tmp/hack-zope.interface Next, get this package registered as a "development egg" in the environment: .. code-block:: sh $ /tmp/hack-zope.interface/bin/python setup.py develop Finally, run the tests using the built-in ``setuptools`` testrunner: .. code-block:: sh $ /tmp/hack-zope.interface/bin/python setup.py test -q running test ... ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK The ``dev`` command alias downloads and installs extra tools, like the :mod:`nose` testrunner and the :mod:`coverage` coverage analyzer: .. code-block:: sh $ /tmp/hack-zope.interface/bin/python setup.py dev $ /tmp/hack-zope.interface/bin/nosetests running nosetests .................................... (lots more dots) ---------------------------------------------------------------------- Ran 707 tests in 2.166s OK If you have the :mod:`coverage` package installed in the virtualenv, you can see how well the tests cover the code: .. code-block:: sh $ /tmp/hack-zope.interface/bin/nosetests --with coverage running nosetests .................................... (lots more dots) Name Stmts Miss Cover Missing ---------------------------------------------------------------- zope.interface 30 0 100% zope.interface.adapter 440 0 100% zope.interface.advice 69 0 100% zope.interface.common 0 0 100% zope.interface.common.idatetime 98 0 100% zope.interface.common.interfaces 81 0 100% zope.interface.common.mapping 32 0 100% zope.interface.common.sequence 38 0 100% zope.interface.declarations 312 0 100% zope.interface.document 54 0 100% zope.interface.exceptions 21 0 100% zope.interface.interface 378 0 100% zope.interface.interfaces 137 0 100% zope.interface.registry 300 0 100% zope.interface.ro 25 0 100% zope.interface.verify 48 0 100% ---------------------------------------------------------------- TOTAL 2063 0 100% ---------------------------------------------------------------------- Ran 707 tests in 2.166s OK Building the documentation -------------------------- :mod:`zope.interface` uses the nifty :mod:`Sphinx` documentation system for building its docs. Using the same virtualenv you set up to run the tests, you can build the docs: The ``docs`` command alias downloads and installs Sphinx and its dependencies: .. code-block:: sh $ /tmp/hack-zope.interface/bin/python setup.py docs ... $ bin/sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html ... build succeeded. Build finished. The HTML pages are in docs/_build/html. You can also test the code snippets in the documentation: .. code-block:: sh $ bin/sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest ... running tests... Document: index --------------- 1 items passed all tests: 17 tests in default 17 tests in 1 items. 17 passed and 0 failed. Test passed. Doctest summary =============== 17 tests 0 failures in tests 0 failures in setup code build succeeded. Testing of doctests in the sources finished, look at the \ results in docs/_build/doctest/output.txt. Using :mod:`zc.buildout` ######################## Setting up the buildout ----------------------- :mod:`zope.interface` ships with its own :file:`buildout.cfg` file and :file:`bootstrap.py` for setting up a development buildout: .. code-block:: sh $ /path/to/python2.7 bootstrap.py ... Generated script '.../bin/buildout' $ bin/buildout Develop: '/home/jrandom/projects/Zope/BTK/interface/.' ... Generated script '.../bin/sphinx-quickstart'. Generated script '.../bin/sphinx-build'. Running the tests ----------------- You can now run the tests: .. code-block:: sh $ bin/test --all Running zope.testing.testrunner.layer.UnitTests tests: Set up zope.testing.testrunner.layer.UnitTests in 0.000 seconds. Ran 702 tests with 0 failures and 0 errors in 0.000 seconds. Tearing down left over layers: Tear down zope.testing.testrunner.layer.UnitTests in 0.000 seconds. Using :mod:`tox` ################ Running Tests on Multiple Python Versions ----------------------------------------- `tox `_ is a Python-based test automation tool designed to run tests against multiple Python versions. It creates a ``virtualenv`` for each configured version, installs the current package and configured dependencies into each ``virtualenv``, and then runs the configured commands. :mod:`zope.interface` configures the following :mod:`tox` environments via its ``tox.ini`` file: - The defined Python environments build a ``virtualenv`` with various Python 2, Python 3, PyPy 2 and PyPy 3 versions, install :mod:`zope.interface` and dependencies, and run the tests via ``python setup.py test -q``. - The ``coverage`` environment builds a ``virtualenv`` with ``python2.7``, installs :mod:`zope.interface` and dependencies, installs :mod:`nose` and :mod:`coverage`, and runs ``nosetests`` with statement coverage. - The ``docs`` environment builds a virtualenv with ``python2.7``, installs :mod:`zope.interface` and dependencies, installs ``Sphinx`` and dependencies, and then builds the docs and exercises the doctest snippets. This example requires that you have a working ``python2.7`` on your path, as well as installing ``tox``: .. code-block:: sh $ tox -e py26 GLOB sdist-make: .../zope.interface/setup.py py26 sdist-reinst: .../zope.interface/.tox/dist/zope.interface-4.0.2dev.zip py26 runtests: commands[0] ... ---------------------------------------------------------------------- Ran 1341 tests in 0.477s OK ___________________________________ summary ____________________________________ py26: commands succeeded congratulations :) Running ``tox`` with no arguments runs all the configured environments, including building the docs and testing their snippets: .. code-block:: sh $ tox GLOB sdist-make: .../zope.interface/setup.py py26 sdist-reinst: .../zope.interface/.tox/dist/zope.interface-4.0.2dev.zip py26 runtests: commands[0] ... Doctest summary =============== 678 tests 0 failures in tests 0 failures in setup code 0 failures in cleanup code build succeeded. ___________________________________ summary ____________________________________ py26: commands succeeded py27: commands succeeded py32: commands succeeded pypy: commands succeeded coverage: commands succeeded docs: commands succeeded congratulations :) Contributing to :mod:`zope.interface` ##################################### Submitting a Bug Report ----------------------- :mod:`zope.interface` tracks its bugs on Github: https://github.com/zopefoundation/zope.interface/issues Please submit bug reports and feature requests there. Sharing Your Changes -------------------- .. note:: Please ensure that all tests are passing before you submit your code. If possible, your submission should include new tests for new features or bug fixes, although it is possible that you may have tested your new code by updating existing tests. If have made a change you would like to share, the best route is to fork the Githb repository, check out your fork, make your changes on a branch in your fork, and push it. You can then submit a pull request from your branch: https://github.com/zopefoundation/zope.interface/pulls If you branched the code from Launchpad using Bazaar, you have another option: you can "push" your branch to Launchpad: .. code-block:: sh $ bzr push lp:~jrandom/zope.interface/cool_feature After pushing your branch, you can link it to a bug report on Launchpad, or request that the maintainers merge your branch using the Launchpad "merge request" feature. zope.interface-6.4/docs/human.rst000066400000000000000000000146111462121350100170670ustar00rootroot00000000000000========================== Using the Adapter Registry ========================== This is a small demonstration of the :mod:`zope.interface` package including its adapter registry. It is intended to provide a concrete but narrow example on how to use interfaces and adapters outside of Zope 3. First we have to import the interface package: .. doctest:: >>> import zope.interface We now develop an interface for our object, which is a simple file in this case. For now we simply support one attribute, the body, which contains the actual file contents: .. doctest:: >>> class IFile(zope.interface.Interface): ... ... body = zope.interface.Attribute('Contents of the file.') ... For statistical reasons we often want to know the size of a file. However, it would be clumsy to implement the size directly in the file object, since the size really represents meta-data. Thus we create another interface that provides the size of something: .. doctest:: >>> class ISize(zope.interface.Interface): ... ... def getSize(): ... 'Return the size of an object.' ... Now we need to implement the file interface. It is essential that the object states that it implements the ``IFile`` interface. We also provide a default body value (just to make things simpler for this example): .. doctest:: >>> @zope.interface.implementer(IFile) ... class File(object): ... ... body = 'foo bar' ... Next we implement an adapter that can provide the ``ISize`` interface given any object providing ``IFile``. By convention we use ``__used_for__`` to specify the interface that we expect the adapted object to provide, in our case ``IFile``. However, this attribute is not used for anything. If you have multiple interfaces for which an adapter is used, just specify the interfaces via a tuple. Again by convention, the constructor of an adapter takes one argument, the context. The context in this case is an instance of ``File`` (providing ``IFile``) that is used to extract the size from. Also by convention the context is stored in an attribute named ``context`` on the adapter. The `twisted`_ community refers to the context as the ``original`` object. However, you may feel free to use a specific argument name, such as ``file``: .. doctest:: >>> @zope.interface.implementer(ISize) ... class FileSize(object): ... ... __used_for__ = IFile ... ... def __init__(self, context): ... self.context = context ... ... def getSize(self): ... return len(self.context.body) ... Now that we have written our adapter, we have to register it with an adapter registry, so that it can be looked up when needed. There is no such thing as a global registry; thus we have to instantiate one for our example manually: .. doctest:: >>> from zope.interface.adapter import AdapterRegistry >>> registry = AdapterRegistry() The registry keeps a map of what adapters implement based on another interface the object already provides. Therefore, we next have to register an adapter that adapts from ``IFile`` to ``ISize``. The first argument to the registry's ``register()`` method is a list of original interfaces.In our cause we have only one original interface, ``IFile``. A list makes sense, since the interface package has the concept of multi-adapters, which are adapters that require multiple objects to adapt to a new interface. In these situations, your adapter constructor will require an argument for each specified interface. The second argument is the interface the adapter provides, in our case ``ISize``. The third argument is the name of the adapter. Since we do not care about names, we simply leave it as an empty string. Names are commonly useful, if you have adapters for the same set of interfaces, but they are useful in different situations. The last argument is simply the adapter class: .. doctest:: >>> registry.register([IFile], ISize, '', FileSize) You can now use the the registry to lookup the adapter: .. doctest:: >>> registry.lookup1(IFile, ISize, '') Let's get a little bit more practical. Let's create a ``File`` instance and create the adapter using a registry lookup. Then we see whether the adapter returns the correct size by calling ``getSize()``: .. doctest:: >>> file = File() >>> size = registry.lookup1(IFile, ISize, '')(file) >>> size.getSize() 7 However, this is not very practical, since I have to manually pass in the arguments to the lookup method. There is some syntactic candy that will allow us to get an adapter instance by simply calling ``ISize(file)``. To make use of this functionality, we need to add our registry to the ``adapter_hooks`` list, which is a member of the adapters module. This list stores a collection of callables that are automatically invoked when ``IFoo(obj)`` is called; their purpose is to locate adapters that implement an interface for a certain context instance. You are required to implement your own adapter hook; this example covers one of the simplest hooks that use the registry, but you could implement one that used an adapter cache or persistent adapters, for instance. The helper hook is required to expect as first argument the desired output interface (for us ``ISize``) and as the second argument the context of the adapter (here ``file``). The function returns an adapter, i.e. a ``FileSize`` instance: .. doctest:: >>> def hook(provided, object): ... adapter = registry.lookup1(zope.interface.providedBy(object), ... provided, '') ... return adapter(object) ... We now just add the hook to an ``adapter_hooks`` list: .. doctest:: >>> from zope.interface.interface import adapter_hooks >>> adapter_hooks.append(hook) Once the hook is registered, you can use the desired syntax: .. doctest:: >>> size = ISize(file) >>> size.getSize() 7 Now we have to clean up after ourselves, so that others after us have a clean ``adapter_hooks`` list: .. doctest:: >>> adapter_hooks.remove(hook) That's it. I have intentionally left out a discussion of named adapters and multi-adapters, since this text is intended as a practical and simple introduction to Zope 3 interfaces and adapters. You might want to read the ``adapter.txt`` in the ``zope.interface`` package for a more formal, referential and complete treatment of the package. Warning: People have reported that ``adapter.txt`` makes their brain feel soft! .. _twisted: https://twistedmatrix.com/ zope.interface-6.4/docs/human.ru.rst000066400000000000000000000246711462121350100175230ustar00rootroot00000000000000=============================== Использование реестра адаптеров =============================== Данный документ содержит небольшую демонстрацию пакета ``zope.interface`` и его реестра адаптеров. Документ рассчитывался как конкретный, но более узкий пример того как использовать интерфейсы и адаптеры вне Zope 3. Сначала нам необходимо импортировать пакет для работы с интерфейсами:: >>> import zope.interface Теперь мы разработаем интерфейс для нашего объекта - простого файла. Наш файл будет содержать всего один атрибут - body, в котором фактически будет сохранено содержимое файла:: >>> class IFile(zope.interface.Interface): ... ... body = zope.interface.Attribute(u'Содержимое файла.') ... Для статистики нам часто необходимо знать размер файла. Но было бы несколько топорно реализовывать определение размера прямо для объекта файла, т.к. размер больше относится к мета-данным. Таким образом мы создаем еще один интерфейс для представления размера какого-либо объекта:: >>> class ISize(zope.interface.Interface): ... ... def getSize(): ... 'Return the size of an object.' ... Теперь мы должны создать класс реализующий наш файл. Необходимо что бы наш объект хранил информацию о том, что он реализует интерфейс `IFile`. Мы также создаем атрибут с содержимым файла по умолчанию (для упрощения нашего примера):: >>> class File(object): ... ... zope.interface.implements(IFile) ... body = 'foo bar' ... Дальше мы создаем адаптер, который будет предоставлять интерфейс `ISize` получая любой объект предоставляющий интерфейс `IFile`. По соглашению мы используем атрибут `__used_for__` для указания интерфейса который как мы ожидаем предоставляет адаптируемый объект, `IFile` в нашем случае. На самом деле этот атрибут используется только для документирования. В случае если адаптер используется для нескольких интерфейсов можно указать их все в виде кортежа. Опять же по соглашению конструктор адаптера получает один аргумент - context (контекст). В нашем случае контекст - это экземпляр `IFile` (объект, предоставляющий `IFile`) который используется для получения из него размера. Так же по соглашению контекст сохраняется а адаптере в атрибуте с именем `context`. Twisted комьюнити ссылается на контекст как на объект `original`. Таким образом можно также дать аргументу любое подходящее имя, например `file`:: >>> class FileSize(object): ... ... zope.interface.implements(ISize) ... __used_for__ = IFile ... ... def __init__(self, context): ... self.context = context ... ... def getSize(self): ... return len(self.context.body) ... Теперь когда мы написали наш адаптер мы должны зарегистрировать его в реестре адаптеров, что бы его можно было запросить когда он понадобится. Здесь нет какого-либо глобального реестра адаптеров, таким образом мы должны самостоятельно создать для нашего примера реестр:: >>> from zope.interface.adapter import AdapterRegistry >>> registry = AdapterRegistry() Реестр содержит отображение того, что адаптер реализует на основе другого интерфейса который предоставляет объект. Поэтому дальше мы регистрируем адаптер который адаптирует интерфейс `IFile` к интерфейсу `ISize`. Первый аргумент к методу `register()` реестра - это список адаптируемых интерфейсов. В нашем случае мы имеем только один адаптируемый интерфейс - `IFile`. Список интерфейсов имеет смысл для использования концепции мульти-адаптеров, которые требуют нескольких оригинальных объектов для адаптации к новому интерфейсу. В этой ситуации конструктор адаптера будет требовать новый аргумент для каждого оригинального интерфейса. Второй аргумент метода `register()` - это интерфейс который предоставляет адаптер, в нашем случае `ISize`. Третий аргумент - имя адаптера. Сейчас нам не важно имя адаптера и мы передаем его как пустую строку. Обычно имена полезны если используются адаптеры для одинакового набора интерфейсов, но в различных ситуациях. Последний аргумент - это класс адаптера:: >>> registry.register([IFile], ISize, '', FileSize) Теперь мы можем использовать реестр для запроса адаптера:: >>> registry.lookup1(IFile, ISize, '') Попробуем более практичный пример. Создадим экземпляр `File` и создадим адаптер использующий запрос реестра. Затем мы увидим возвращает ли адаптер корректный размер при вызове `getSize()`:: >>> file = File() >>> size = registry.lookup1(IFile, ISize, '')(file) >>> size.getSize() 7 На самом деле это не очень практично, т.к. нам нужно самим передавать все аргументы методу запроса. Существует некоторый синтаксический леденец который позволяет нам получить экземпляр адаптера просто вызвав `ISize(file)`. Что бы использовать эту функциональность нам понадобится добавить наш реестр к списку adapter_hooks, который находится в модуле с адаптерами. Этот список хранит коллекцию вызываемых объектов которые вызываются автоматически когда вызывается IFoo(obj); их предназначение - найти адаптеры которые реализуют интерфейс для определенного экземпляра контекста. Необходимо реализовать свою собственную функцию для поиска адаптера; данный пример описывает одну из простейших функций для использования с реестром, но также можно реализовать поисковые функции которые, например, используют кэширование, или адаптеры сохраняемые в базе. Функция поиска должна принимать желаемый на выходе интерфейс (в нашем случае `ISize`) как первый аргумент и контекст для адаптации (`file`) как второй. Функция должна вернуть адаптер, т.е. экземпляр `FileSize`:: >>> def hook(provided, object): ... adapter = registry.lookup1(zope.interface.providedBy(object), ... provided, '') ... return adapter(object) ... Теперь мы просто добавляем нашу функцию к списку `adapter_hooks`:: >>> from zope.interface.interface import adapter_hooks >>> adapter_hooks.append(hook) Как только функция зарегистрирована мы можем использовать желаемый синтаксис:: >>> size = ISize(file) >>> size.getSize() 7 После нам нужно прибраться за собой, что бы другие получили чистый список `adaper_hooks` после нас:: >>> adapter_hooks.remove(hook) Это все. Здесь намеренно отложена дискуссия об именованных и мульти-адаптерах, т.к. данный текст рассчитан как практическое и простое введение в интерфейсы и адаптеры Zope 3. Для более подробной информации имеет смысл прочитать `adapter.txt` из пакета `zope.interface`, что бы получить более формальное, справочное и полное трактование пакета. Внимание: многие жаловались, что `adapter.txt` приводит их мозг к расплавленному состоянию! zope.interface-6.4/docs/index.rst000066400000000000000000000011771462121350100170710ustar00rootroot00000000000000.. zope.interface documentation master file, created by sphinx-quickstart on Mon Mar 26 16:31:31 2012. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to zope.interface's documentation! ========================================== Contents: .. toctree:: :maxdepth: 2 README adapter human verify foodforthought api/index hacking changes По-русски ========= .. toctree:: :maxdepth: 2 README.ru adapter.ru human.ru Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` zope.interface-6.4/docs/make.bat000066400000000000000000000117661462121350100166420ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\zopeinterface.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\zopeinterface.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end zope.interface-6.4/docs/requirements.txt000066400000000000000000000001041462121350100205010ustar00rootroot00000000000000Sphinx sphinx_rtd_theme>1 docutils<0.19 repoze.sphinx.autointerface zope.interface-6.4/docs/verify.rst000066400000000000000000000237561462121350100172750ustar00rootroot00000000000000===================================== Verifying interface implementations ===================================== The ``zope.interface.verify`` module provides functions that test whether a given interface is implemented by a class or provided by an object. .. currentmodule:: zope.interface.verify Verifying objects ================= .. autofunction:: verifyObject .. autoexception:: zope.interface.Invalid Let's demonstrate. We'll begin by defining a simple interface hierarchy requiring two attributes, and a helper method that will instantiate and verify that an object provides this interface. .. doctest:: >>> from zope.interface import Interface, Attribute, implementer >>> from zope.interface import Invalid >>> from zope.interface.verify import verifyObject >>> oname, __name__ = __name__, 'base' # Pretend we're in a module, not a doctest >>> class IBase(Interface): ... x = Attribute("The X attribute") >>> __name__ = 'module' # Pretend to be a different module. >>> class IFoo(IBase): ... y = Attribute("The Y attribute") >>> __name__ = oname; del oname >>> class Foo(object): ... pass >>> def verify_foo(**kwargs): ... foo = Foo() ... try: ... return verifyObject(IFoo, foo, **kwargs) ... except Invalid as e: ... print(e) If we try to verify an instance of this ``Foo`` class, three errors will be reported. The declarations (does the object provide ``IFoo``) are checked, as are the attributes specified in the interface being validated (and its ancestors). Notice that the interface being verified is shown, as is the interface where the attribute was defined. .. doctest:: >>> verify_foo() The object has failed to implement interface ...IFoo: Does not declaratively implement the interface The base.IBase.x attribute was not provided The module.IFoo.y attribute was not provided If we add the two missing attributes, we still have the error about not declaring the correct interface. .. doctest:: >>> Foo.x = Foo.y = 42 >>> verify_foo() The object has failed to implement interface ...IFoo: Does not declaratively implement the interface. If we want to only check the structure of the object, without examining its declarations, we can use the ``tentative`` argument. .. doctest:: >>> verify_foo(tentative=True) True Of course, we can always mark a particular instance as providing the desired interface. .. doctest:: >>> from zope.interface import alsoProvides >>> foo = Foo() >>> alsoProvides(foo, IFoo) >>> verifyObject(IFoo, foo) True If all instances will provide the interface, we can mark a class as implementing it. But we have to remove the interface from the instance first so a consistent interface resolution order can be achieved. (Calling ``gc.collect()`` is also necessary because we use weakrefs.) .. doctest:: >>> from zope.interface import classImplements >>> from zope.interface import noLongerProvides >>> import gc >>> noLongerProvides(foo, IFoo) >>> _ = gc.collect() >>> classImplements(Foo, IFoo) >>> verify_foo() True Testing for attributes ---------------------- Attributes of the object, be they defined by its class or added by its ``__init__`` method, will be recognized: .. doctest:: >>> @implementer(IFoo) ... class Foo(object): ... x = 1 ... def __init__(self): ... self.y = 2 >>> verifyObject(IFoo, Foo()) True If either attribute is missing, verification will fail by raising an exception. .. doctest:: >>> @implementer(IFoo) ... class Foo(object): ... x = 1 >>> verify_foo() The object has failed to implement interface ...IFoo: The module.IFoo.y attribute was not provided. >>> @implementer(IFoo) ... class Foo(object): ... def __init__(self): ... self.y = 2 >>> verify_foo() The object has failed to implement interface ...IFoo: The base.IBase.x attribute was not provided. If both attributes are missing, an exception is raised reporting both errors. .. doctest:: >>> @implementer(IFoo) ... class Foo(object): ... pass >>> verify_foo() The object has failed to implement interface ...IFoo: The base.IBase.x attribute was not provided The module.IFoo.y attribute was not provided If an attribute is implemented as a property that raises an ``AttributeError`` when trying to get its value, the attribute is considered missing: .. doctest:: >>> oname, __name__ = __name__, 'module' >>> class IFoo(Interface): ... x = Attribute('The X attribute') >>> __name__ = oname; del oname >>> @implementer(IFoo) ... class Foo(object): ... @property ... def x(self): ... raise AttributeError >>> verify_foo() The object has failed to implement interface ...IFoo: The module.IFoo.x attribute was not provided. Any other exception raised by a property will propagate to the caller of ``verifyObject``: .. doctest:: >>> @implementer(IFoo) ... class Foo(object): ... @property ... def x(self): ... raise Exception >>> verify_foo() Traceback (most recent call last): Exception Of course, broken properties that are not required by the interface don't do any harm: .. doctest:: >>> @implementer(IFoo) ... class Foo(object): ... x = 1 ... @property ... def y(self): ... raise Exception >>> verify_foo() True Testing For Methods ------------------- Methods are also validated to exist. We'll start by defining a method that takes one argument. If we don't provide it, we get an error. .. doctest:: >>> oname, __name__ = __name__, 'module' >>> class IFoo(Interface): ... def simple(arg1): "Takes one positional argument" >>> __name__ = oname; del oname >>> @implementer(IFoo) ... class Foo(object): ... pass >>> verify_foo() The object has failed to implement interface ...IFoo: The module.IFoo.simple(arg1) attribute was not provided. Once they exist, they are checked to be callable, and for compatible signatures. Not being callable is an error. .. doctest:: >>> Foo.simple = 42 >>> verify_foo() The object has failed to implement interface ...IFoo: The contract of module.IFoo.simple(arg1) is violated because '42' is not a method. Taking too few arguments is an error. (Recall that the ``self`` argument is implicit.) .. doctest:: >>> Foo.simple = lambda self: "I take no arguments" >>> verify_foo() The object has failed to implement interface ...IFoo: The contract of module.IFoo.simple(arg1) is violated because '()' doesn't allow enough arguments. Requiring too many arguments is an error. .. doctest:: >>> Foo.simple = lambda self, a, b: "I require two arguments" >>> verify_foo() The object has failed to implement interface ...IFoo: The contract of module.IFoo.simple(arg1) is violated because '(a, b)' requires too many arguments. Variable arguments can be used to implement the required number, as can arguments with defaults. .. doctest:: >>> Foo.simple = lambda self, *args: "Varargs work." >>> verify_foo() True >>> Foo.simple = lambda self, a=1, b=2: "Default args work." >>> verify_foo() True If our interface defines a method that uses variable positional or variable keyword arguments, the implementation must also accept them. .. doctest:: >>> oname, __name__ = __name__, 'module' >>> class IFoo(Interface): ... def needs_kwargs(**kwargs): pass >>> __name__ = oname; del oname >>> @implementer(IFoo) ... class Foo(object): ... def needs_kwargs(self, a=1, b=2): pass >>> verify_foo() The object has failed to implement interface ...IFoo: The contract of module.IFoo.needs_kwargs(**kwargs) is violated because 'Foo.needs_kwargs(a=1, b=2)' doesn't support keyword arguments. >>> oname, __name__ = __name__, 'module' >>> class IFoo(Interface): ... def needs_varargs(*args): pass >>> __name__ = oname; del oname >>> @implementer(IFoo) ... class Foo(object): ... def needs_varargs(self, **kwargs): pass >>> verify_foo() The object has failed to implement interface ...IFoo: The contract of module.IFoo.needs_varargs(*args) is violated because 'Foo.needs_varargs(**kwargs)' doesn't support variable arguments. Of course, missing attributes are also found and reported, and the source interface of the missing attribute is included. Similarly, when the failing method is from a parent class, that is also reported. .. doctest:: >>> oname, __name__ = __name__, 'base' >>> class IBase(Interface): ... def method(arg1): "Takes one positional argument" >>> __name__ = 'module' >>> class IFoo(IBase): ... x = Attribute('The X attribute') >>> __name__ = oname; del oname >>> class Base(object): ... def method(self): "I don't have enough arguments" >>> @implementer(IFoo) ... class Foo(Base): ... pass >>> verify_foo() The object has failed to implement interface ...IFoo: The contract of base.IBase.method(arg1) is violated because 'Base.method()' doesn't allow enough arguments The module.IFoo.x attribute was not provided Verifying Classes ================= The function `verifyClass` is used to check that a class implements an interface properly, meaning that its instances properly provide the interface. Many of the same things that `verifyObject` checks can be checked for classes, but certain conditions, such as the presence of attributes, cannot be verified. .. autofunction:: verifyClass .. doctest:: >>> from zope.interface.verify import verifyClass >>> def verify_foo_class(): ... try: ... return verifyClass(IFoo, Foo) ... except Invalid as e: ... print(e) >>> verify_foo_class() The object has failed to implement interface ...IFoo: The contract of base.IBase.method(arg1) is violated because 'Base.method(self)' doesn't allow enough arguments. zope.interface-6.4/setup.cfg000066400000000000000000000011731462121350100161150ustar00rootroot00000000000000# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/c-code [zest.releaser] create-wheel = no [flake8] doctests = 1 [check-manifest] ignore = .editorconfig .meta.toml docs/_build/html/_sources/* docs/_build/doctest/* docs/_build/doctest/output.txt docs/_build/html/_sources/* docs/_build/html/_sources/api/* [isort] force_single_line = True combine_as_imports = True sections = FUTURE,STDLIB,THIRDPARTY,ZOPE,FIRSTPARTY,LOCALFOLDER known_third_party = docutils, pkg_resources, pytz known_zope = known_first_party = default_section = ZOPE line_length = 79 lines_after_imports = 2 zope.interface-6.4/setup.py000066400000000000000000000116261462121350100160120ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2004-2007 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. # ############################################################################## # This package is developed by the Zope Toolkit project, documented here: # http://docs.zope.org/zopetoolkit # When developing and releasing this package, please follow the documented # Zope Toolkit policies as described by this documentation. ############################################################################## """Setup for zope.interface package """ import os import sys from distutils.errors import CCompilerError from distutils.errors import DistutilsExecError from distutils.errors import DistutilsPlatformError from setuptools import Extension from setuptools import find_packages from setuptools import setup from setuptools.command.build_ext import build_ext 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) except DistutilsPlatformError as e: self._unavailable(e) def build_extension(self, ext): try: build_ext.build_extension(self, ext) except (CCompilerError, DistutilsExecError, OSError) as e: self._unavailable(e) def _unavailable(self, e): print('*' * 80) print("""WARNING: An optional code optimization (C extension) could not be compiled. Optimizations for this package will not be available!""") print("") print(e) print('*' * 80) codeoptimization_c = os.path.join('src', 'zope', 'interface', '_zope_interface_coptimizations.c') codeoptimization = [ Extension( "zope.interface._zope_interface_coptimizations", [os.path.normcase(codeoptimization_c)] ), ] is_jython = 'java' in sys.platform is_pypy = hasattr(sys, 'pypy_version_info') # Jython cannot build the C optimizations. Nor, as of 7.3, can PyPy ( # it doesn't have PySuper_Type) Everywhere else, defer the decision to # runtime. if is_jython or is_pypy: ext_modules = [] else: ext_modules = codeoptimization tests_require = [ # The test dependencies should NOT have direct or transitive # dependencies on zope.interface. 'coverage >= 5.0.3', 'zope.event', 'zope.testing', ] testing_extras = tests_require def read(*rnames): with open(os.path.join(os.path.dirname(__file__), *rnames)) as f: return f.read() long_description = ( read('README.rst') + '\n' + read('CHANGES.rst') ) setup(name='zope.interface', version='6.4', url='https://github.com/zopefoundation/zope.interface', license='ZPL 2.1', description='Interfaces for Python', author='Zope Foundation and Contributors', author_email='zope-dev@zope.org', long_description=long_description, classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: Zope Public License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Framework :: Zope :: 3", "Topic :: Software Development :: Libraries :: Python Modules", ], packages=find_packages('src'), package_dir={'': 'src'}, namespace_packages=["zope"], cmdclass={ 'build_ext': optional_build_ext, }, test_suite='zope.interface.tests', include_package_data=True, zip_safe=False, tests_require=tests_require, install_requires=['setuptools'], python_requires='>=3.7', extras_require={ 'docs': ['Sphinx', 'repoze.sphinx.autointerface', 'sphinx_rtd_theme'], 'test': tests_require, 'testing': testing_extras, }, ext_modules=ext_modules, keywords=['interface', 'components', 'plugins'], ) zope.interface-6.4/src/000077500000000000000000000000001462121350100150615ustar00rootroot00000000000000zope.interface-6.4/src/zope/000077500000000000000000000000001462121350100160365ustar00rootroot00000000000000zope.interface-6.4/src/zope/__init__.py000066400000000000000000000000701462121350100201440ustar00rootroot00000000000000__import__('pkg_resources').declare_namespace(__name__) zope.interface-6.4/src/zope/interface/000077500000000000000000000000001462121350100177765ustar00rootroot00000000000000zope.interface-6.4/src/zope/interface/__init__.py000066400000000000000000000066041462121350100221150ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2001, 2002 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. # ############################################################################## """Interfaces This package implements the Python "scarecrow" proposal. The package exports two objects, `Interface` and `Attribute` directly. It also exports several helper methods. Interface is used to create an interface with a class statement, as in: class IMyInterface(Interface): '''Interface documentation ''' def meth(arg1, arg2): '''Documentation for meth ''' # Note that there is no self argument To find out what you can do with interfaces, see the interface interface, `IInterface` in the `interfaces` module. The package has several public modules: o `declarations` provides utilities to declare interfaces on objects. It also provides a wide range of helpful utilities that aid in managing declared interfaces. Most of its public names are however imported here. o `document` has a utility for documenting an interface as structured text. o `exceptions` has the interface-defined exceptions o `interfaces` contains a list of all public interfaces for this package. o `verify` has utilities for verifying implementations of interfaces. See the module doc strings for more information. """ __docformat__ = 'restructuredtext' # pylint:disable=wrong-import-position,unused-import from zope.interface.interface import Interface from zope.interface.interface import _wire # Need to actually get the interface elements to implement the right interfaces _wire() del _wire from zope.interface.declarations import Declaration # The following are to make spec pickles cleaner from zope.interface.declarations import Provides from zope.interface.declarations import alsoProvides from zope.interface.declarations import classImplements from zope.interface.declarations import classImplementsFirst from zope.interface.declarations import classImplementsOnly from zope.interface.declarations import directlyProvidedBy from zope.interface.declarations import directlyProvides from zope.interface.declarations import implementedBy from zope.interface.declarations import implementer from zope.interface.declarations import implementer_only from zope.interface.declarations import moduleProvides from zope.interface.declarations import named from zope.interface.declarations import noLongerProvides from zope.interface.declarations import providedBy from zope.interface.declarations import provider from zope.interface.exceptions import Invalid from zope.interface.interface import Attribute from zope.interface.interface import interfacemethod from zope.interface.interface import invariant from zope.interface.interface import taggedValue from zope.interface.interfaces import IInterfaceDeclaration moduleProvides(IInterfaceDeclaration) __all__ = ('Interface', 'Attribute') + tuple(IInterfaceDeclaration) assert all(k in globals() for k in __all__) zope.interface-6.4/src/zope/interface/_compat.py000066400000000000000000000104211462121350100217700ustar00rootroot00000000000000############################################################################## # # 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. # ############################################################################## """ Support functions for dealing with differences in platforms, including Python versions and implementations. This file should have no imports from the rest of zope.interface because it is used during early bootstrapping. """ import os import sys def _normalize_name(name): if isinstance(name, bytes): name = str(name, 'ascii') if isinstance(name, str): return name raise TypeError("name must be a string or ASCII-only bytes") PYPY = hasattr(sys, 'pypy_version_info') def _c_optimizations_required(): """ Return a true value if the C optimizations are required. This uses the ``PURE_PYTHON`` variable as documented in `_use_c_impl`. """ pure_env = os.environ.get('PURE_PYTHON') require_c = pure_env == "0" return require_c def _c_optimizations_available(): """ Return the C optimization module, if available, otherwise a false value. If the optimizations are required but not available, this raises the ImportError. This does not say whether they should be used or not. """ catch = () if _c_optimizations_required() else (ImportError,) try: from zope.interface import _zope_interface_coptimizations as c_opt return c_opt except catch: # pragma: no cover (only Jython doesn't build extensions) return False def _c_optimizations_ignored(): """ The opposite of `_c_optimizations_required`. """ pure_env = os.environ.get('PURE_PYTHON') return pure_env is not None and pure_env != "0" def _should_attempt_c_optimizations(): """ Return a true value if we should attempt to use the C optimizations. This takes into account whether we're on PyPy and the value of the ``PURE_PYTHON`` environment variable, as defined in `_use_c_impl`. """ is_pypy = hasattr(sys, 'pypy_version_info') if _c_optimizations_required(): return True if is_pypy: return False return not _c_optimizations_ignored() def _use_c_impl(py_impl, name=None, globs=None): """ Decorator. Given an object implemented in Python, with a name like ``Foo``, import the corresponding C implementation from ``zope.interface._zope_interface_coptimizations`` with the name ``Foo`` and use it instead. If the ``PURE_PYTHON`` environment variable is set to any value other than ``"0"``, or we're on PyPy, ignore the C implementation and return the Python version. If the C implementation cannot be imported, return the Python version. If ``PURE_PYTHON`` is set to 0, *require* the C implementation (let the ImportError propagate); note that PyPy can import the C implementation in this case (and all tests pass). In all cases, the Python version is kept available. in the module globals with the name ``FooPy`` and the name ``FooFallback`` (both conventions have been used; the C implementation of some functions looks for the ``Fallback`` version, as do some of the Sphinx documents). Example:: @_use_c_impl class Foo(object): ... """ name = name or py_impl.__name__ globs = globs or sys._getframe(1).f_globals def find_impl(): if not _should_attempt_c_optimizations(): return py_impl c_opt = _c_optimizations_available() if not c_opt: # pragma: no cover (only Jython doesn't build extensions) return py_impl __traceback_info__ = c_opt return getattr(c_opt, name) c_impl = find_impl() # Always make available by the FooPy name and FooFallback # name (for testing and documentation) globs[name + 'Py'] = py_impl globs[name + 'Fallback'] = py_impl return c_impl zope.interface-6.4/src/zope/interface/_flatten.py000066400000000000000000000020411462121350100221410ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2002 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. # ############################################################################## """Adapter-style interface registry See Adapter class. """ from zope.interface import Declaration def _flatten(implements, include_None=0): try: r = implements.flattened() except AttributeError: if implements is None: r=() else: r = Declaration(implements).flattened() if not include_None: return r r = list(r) r.append(None) return r zope.interface-6.4/src/zope/interface/_zope_interface_coptimizations.c000066400000000000000000001575651462121350100264550ustar00rootroot00000000000000/*########################################################################### # # Copyright (c) 2003 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. # ############################################################################*/ #include "Python.h" #include "structmember.h" #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" #pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif #define TYPE(O) ((PyTypeObject*)(O)) #define OBJECT(O) ((PyObject*)(O)) #define CLASSIC(O) ((PyClassObject*)(O)) #ifndef PyVarObject_HEAD_INIT #define PyVarObject_HEAD_INIT(a, b) PyObject_HEAD_INIT(a) b, #endif #ifndef Py_TYPE #define Py_TYPE(o) ((o)->ob_type) #endif #define PyNative_FromString PyUnicode_FromString static PyObject *str__dict__, *str__implemented__, *strextends; static PyObject *BuiltinImplementationSpecifications, *str__provides__; static PyObject *str__class__, *str__providedBy__; static PyObject *empty, *fallback; static PyObject *str__conform__, *str_call_conform, *adapter_hooks; static PyObject *str_uncached_lookup, *str_uncached_lookupAll; static PyObject *str_uncached_subscriptions; static PyObject *str_registry, *strro, *str_generation, *strchanged; static PyObject *str__self__; static PyObject *str__module__; static PyObject *str__name__; static PyObject *str__adapt__; static PyObject *str_CALL_CUSTOM_ADAPT; static PyTypeObject *Implements; static int imported_declarations = 0; static int import_declarations(void) { PyObject *declarations, *i; declarations = PyImport_ImportModule("zope.interface.declarations"); if (declarations == NULL) return -1; BuiltinImplementationSpecifications = PyObject_GetAttrString( declarations, "BuiltinImplementationSpecifications"); if (BuiltinImplementationSpecifications == NULL) return -1; empty = PyObject_GetAttrString(declarations, "_empty"); if (empty == NULL) return -1; fallback = PyObject_GetAttrString(declarations, "implementedByFallback"); if (fallback == NULL) return -1; i = PyObject_GetAttrString(declarations, "Implements"); if (i == NULL) return -1; if (! PyType_Check(i)) { PyErr_SetString(PyExc_TypeError, "zope.interface.declarations.Implements is not a type"); return -1; } Implements = (PyTypeObject *)i; Py_DECREF(declarations); imported_declarations = 1; return 0; } static PyTypeObject SpecificationBaseType; /* Forward */ static PyObject * implementedByFallback(PyObject *cls) { if (imported_declarations == 0 && import_declarations() < 0) return NULL; return PyObject_CallFunctionObjArgs(fallback, cls, NULL); } static PyObject * implementedBy(PyObject *ignored, PyObject *cls) { /* Fast retrieval of implements spec, if possible, to optimize common case. Use fallback code if we get stuck. */ PyObject *dict = NULL, *spec; if (PyObject_TypeCheck(cls, &PySuper_Type)) { // Let merging be handled by Python. return implementedByFallback(cls); } if (PyType_Check(cls)) { dict = TYPE(cls)->tp_dict; Py_XINCREF(dict); } if (dict == NULL) dict = PyObject_GetAttr(cls, str__dict__); if (dict == NULL) { /* Probably a security proxied class, use more expensive fallback code */ PyErr_Clear(); return implementedByFallback(cls); } spec = PyObject_GetItem(dict, str__implemented__); Py_DECREF(dict); if (spec) { if (imported_declarations == 0 && import_declarations() < 0) return NULL; if (PyObject_TypeCheck(spec, Implements)) return spec; /* Old-style declaration, use more expensive fallback code */ Py_DECREF(spec); return implementedByFallback(cls); } PyErr_Clear(); /* Maybe we have a builtin */ if (imported_declarations == 0 && import_declarations() < 0) return NULL; spec = PyDict_GetItem(BuiltinImplementationSpecifications, cls); if (spec != NULL) { Py_INCREF(spec); return spec; } /* We're stuck, use fallback */ return implementedByFallback(cls); } static PyObject * getObjectSpecification(PyObject *ignored, PyObject *ob) { PyObject *cls, *result; result = PyObject_GetAttr(ob, str__provides__); if (!result) { if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { /* Propagate non AttributeError exceptions. */ return NULL; } PyErr_Clear(); } else { int is_instance = -1; is_instance = PyObject_IsInstance(result, (PyObject*)&SpecificationBaseType); if (is_instance < 0) { /* Propagate all errors */ return NULL; } if (is_instance) { return result; } } /* We do a getattr here so as not to be defeated by proxies */ cls = PyObject_GetAttr(ob, str__class__); if (cls == NULL) { if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { /* Propagate non-AttributeErrors */ return NULL; } PyErr_Clear(); if (imported_declarations == 0 && import_declarations() < 0) return NULL; Py_INCREF(empty); return empty; } result = implementedBy(NULL, cls); Py_DECREF(cls); return result; } static PyObject * providedBy(PyObject *ignored, PyObject *ob) { PyObject *result, *cls, *cp; int is_instance = -1; result = NULL; is_instance = PyObject_IsInstance(ob, (PyObject*)&PySuper_Type); if (is_instance < 0) { if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { /* Propagate non-AttributeErrors */ return NULL; } PyErr_Clear(); } if (is_instance) { return implementedBy(NULL, ob); } result = PyObject_GetAttr(ob, str__providedBy__); if (result == NULL) { if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { return NULL; } PyErr_Clear(); return getObjectSpecification(NULL, ob); } /* We want to make sure we have a spec. We can't do a type check because we may have a proxy, so we'll just try to get the only attribute. */ if (PyObject_TypeCheck(result, &SpecificationBaseType) || PyObject_HasAttr(result, strextends) ) return result; /* The object's class doesn't understand descriptors. Sigh. We need to get an object descriptor, but we have to be careful. We want to use the instance's __provides__,l if there is one, but only if it didn't come from the class. */ Py_DECREF(result); cls = PyObject_GetAttr(ob, str__class__); if (cls == NULL) return NULL; result = PyObject_GetAttr(ob, str__provides__); if (result == NULL) { /* No __provides__, so just fall back to implementedBy */ PyErr_Clear(); result = implementedBy(NULL, cls); Py_DECREF(cls); return result; } cp = PyObject_GetAttr(cls, str__provides__); if (cp == NULL) { /* The the class has no provides, assume we're done: */ PyErr_Clear(); Py_DECREF(cls); return result; } if (cp == result) { /* Oops, we got the provides from the class. This means the object doesn't have it's own. We should use implementedBy */ Py_DECREF(result); result = implementedBy(NULL, cls); } Py_DECREF(cls); Py_DECREF(cp); return result; } typedef struct { PyObject_HEAD PyObject* weakreflist; /* In the past, these fields were stored in the __dict__ and were technically allowed to contain any Python object, though other type checks would fail or fall back to generic code paths if they didn't have the expected type. We preserve that behaviour and don't make any assumptions about contents. */ PyObject* _implied; /* The remainder aren't used in C code but must be stored here to prevent instance layout conflicts. */ PyObject* _dependents; PyObject* _bases; PyObject* _v_attrs; PyObject* __iro__; PyObject* __sro__; } Spec; /* We know what the fields are *supposed* to define, but they could have anything, so we need to traverse them. */ static int Spec_traverse(Spec* self, visitproc visit, void* arg) { Py_VISIT(self->_implied); Py_VISIT(self->_dependents); Py_VISIT(self->_bases); Py_VISIT(self->_v_attrs); Py_VISIT(self->__iro__); Py_VISIT(self->__sro__); return 0; } static int Spec_clear(Spec* self) { Py_CLEAR(self->_implied); Py_CLEAR(self->_dependents); Py_CLEAR(self->_bases); Py_CLEAR(self->_v_attrs); Py_CLEAR(self->__iro__); Py_CLEAR(self->__sro__); return 0; } static void Spec_dealloc(Spec* self) { /* PyType_GenericAlloc that you get when you don't specify a tp_alloc always tracks the object. */ PyObject_GC_UnTrack((PyObject *)self); if (self->weakreflist != NULL) { PyObject_ClearWeakRefs(OBJECT(self)); } Spec_clear(self); Py_TYPE(self)->tp_free(OBJECT(self)); } static PyObject * Spec_extends(Spec *self, PyObject *other) { PyObject *implied; implied = self->_implied; if (implied == NULL) { return NULL; } if (PyDict_GetItem(implied, other) != NULL) Py_RETURN_TRUE; Py_RETURN_FALSE; } static char Spec_extends__doc__[] = "Test whether a specification is or extends another" ; static char Spec_providedBy__doc__[] = "Test whether an interface is implemented by the specification" ; static PyObject * Spec_call(Spec *self, PyObject *args, PyObject *kw) { PyObject *spec; if (! PyArg_ParseTuple(args, "O", &spec)) return NULL; return Spec_extends(self, spec); } static PyObject * Spec_providedBy(PyObject *self, PyObject *ob) { PyObject *decl, *item; decl = providedBy(NULL, ob); if (decl == NULL) return NULL; if (PyObject_TypeCheck(decl, &SpecificationBaseType)) item = Spec_extends((Spec*)decl, self); else /* decl is probably a security proxy. We have to go the long way around. */ item = PyObject_CallFunctionObjArgs(decl, self, NULL); Py_DECREF(decl); return item; } static char Spec_implementedBy__doc__[] = "Test whether the specification is implemented by a class or factory.\n" "Raise TypeError if argument is neither a class nor a callable." ; static PyObject * Spec_implementedBy(PyObject *self, PyObject *cls) { PyObject *decl, *item; decl = implementedBy(NULL, cls); if (decl == NULL) return NULL; if (PyObject_TypeCheck(decl, &SpecificationBaseType)) item = Spec_extends((Spec*)decl, self); else item = PyObject_CallFunctionObjArgs(decl, self, NULL); Py_DECREF(decl); return item; } static struct PyMethodDef Spec_methods[] = { {"providedBy", (PyCFunction)Spec_providedBy, METH_O, Spec_providedBy__doc__}, {"implementedBy", (PyCFunction)Spec_implementedBy, METH_O, Spec_implementedBy__doc__}, {"isOrExtends", (PyCFunction)Spec_extends, METH_O, Spec_extends__doc__}, {NULL, NULL} /* sentinel */ }; static PyMemberDef Spec_members[] = { {"_implied", T_OBJECT_EX, offsetof(Spec, _implied), 0, ""}, {"_dependents", T_OBJECT_EX, offsetof(Spec, _dependents), 0, ""}, {"_bases", T_OBJECT_EX, offsetof(Spec, _bases), 0, ""}, {"_v_attrs", T_OBJECT_EX, offsetof(Spec, _v_attrs), 0, ""}, {"__iro__", T_OBJECT_EX, offsetof(Spec, __iro__), 0, ""}, {"__sro__", T_OBJECT_EX, offsetof(Spec, __sro__), 0, ""}, {NULL}, }; static PyTypeObject SpecificationBaseType = { PyVarObject_HEAD_INIT(NULL, 0) /* tp_name */ "_interface_coptimizations." "SpecificationBase", /* tp_basicsize */ sizeof(Spec), /* tp_itemsize */ 0, /* tp_dealloc */ (destructor)Spec_dealloc, /* tp_print */ (printfunc)0, /* tp_getattr */ (getattrfunc)0, /* tp_setattr */ (setattrfunc)0, /* tp_compare */ 0, /* tp_repr */ (reprfunc)0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ (hashfunc)0, /* tp_call */ (ternaryfunc)Spec_call, /* tp_str */ (reprfunc)0, /* tp_getattro */ (getattrofunc)0, /* tp_setattro */ (setattrofunc)0, /* tp_as_buffer */ 0, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, "Base type for Specification objects", /* tp_traverse */ (traverseproc)Spec_traverse, /* tp_clear */ (inquiry)Spec_clear, /* tp_richcompare */ (richcmpfunc)0, /* tp_weaklistoffset */ offsetof(Spec, weakreflist), /* tp_iter */ (getiterfunc)0, /* tp_iternext */ (iternextfunc)0, /* tp_methods */ Spec_methods, /* tp_members */ Spec_members, }; static PyObject * OSD_descr_get(PyObject *self, PyObject *inst, PyObject *cls) { PyObject *provides; if (inst == NULL) return getObjectSpecification(NULL, cls); provides = PyObject_GetAttr(inst, str__provides__); /* Return __provides__ if we got it, or return NULL and propagate non-AttributeError. */ if (provides != NULL || !PyErr_ExceptionMatches(PyExc_AttributeError)) return provides; PyErr_Clear(); return implementedBy(NULL, cls); } static PyTypeObject OSDType = { PyVarObject_HEAD_INIT(NULL, 0) /* tp_name */ "_interface_coptimizations." "ObjectSpecificationDescriptor", /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ (destructor)0, /* tp_print */ (printfunc)0, /* tp_getattr */ (getattrfunc)0, /* tp_setattr */ (setattrfunc)0, /* tp_compare */ 0, /* tp_repr */ (reprfunc)0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ (hashfunc)0, /* tp_call */ (ternaryfunc)0, /* tp_str */ (reprfunc)0, /* tp_getattro */ (getattrofunc)0, /* tp_setattro */ (setattrofunc)0, /* tp_as_buffer */ 0, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE , "Object Specification Descriptor", /* tp_traverse */ (traverseproc)0, /* tp_clear */ (inquiry)0, /* tp_richcompare */ (richcmpfunc)0, /* tp_weaklistoffset */ (long)0, /* tp_iter */ (getiterfunc)0, /* tp_iternext */ (iternextfunc)0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* internal use */ /* tp_descr_get */ (descrgetfunc)OSD_descr_get, }; typedef struct { Spec spec; /* These members are handled generically, as for Spec members. */ PyObject* _cls; PyObject* _implements; } CPB; static PyObject * CPB_descr_get(CPB *self, PyObject *inst, PyObject *cls) { PyObject *implements; if (self->_cls == NULL) return NULL; if (cls == self->_cls) { if (inst == NULL) { Py_INCREF(self); return OBJECT(self); } implements = self->_implements; Py_XINCREF(implements); return implements; } PyErr_SetObject(PyExc_AttributeError, str__provides__); return NULL; } static int CPB_traverse(CPB* self, visitproc visit, void* arg) { Py_VISIT(self->_cls); Py_VISIT(self->_implements); return Spec_traverse((Spec*)self, visit, arg); } static int CPB_clear(CPB* self) { Py_CLEAR(self->_cls); Py_CLEAR(self->_implements); Spec_clear((Spec*)self); return 0; } static void CPB_dealloc(CPB* self) { PyObject_GC_UnTrack((PyObject *)self); CPB_clear(self); Spec_dealloc((Spec*)self); } static PyMemberDef CPB_members[] = { {"_cls", T_OBJECT_EX, offsetof(CPB, _cls), 0, "Defining class."}, {"_implements", T_OBJECT_EX, offsetof(CPB, _implements), 0, "Result of implementedBy."}, {NULL} }; static PyTypeObject CPBType = { PyVarObject_HEAD_INIT(NULL, 0) /* tp_name */ "_interface_coptimizations." "ClassProvidesBase", /* tp_basicsize */ sizeof(CPB), /* tp_itemsize */ 0, /* tp_dealloc */ (destructor)CPB_dealloc, /* tp_print */ (printfunc)0, /* tp_getattr */ (getattrfunc)0, /* tp_setattr */ (setattrfunc)0, /* tp_compare */ 0, /* tp_repr */ (reprfunc)0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ (hashfunc)0, /* tp_call */ (ternaryfunc)0, /* tp_str */ (reprfunc)0, /* tp_getattro */ (getattrofunc)0, /* tp_setattro */ (setattrofunc)0, /* tp_as_buffer */ 0, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, "C Base class for ClassProvides", /* tp_traverse */ (traverseproc)CPB_traverse, /* tp_clear */ (inquiry)CPB_clear, /* tp_richcompare */ (richcmpfunc)0, /* tp_weaklistoffset */ (long)0, /* tp_iter */ (getiterfunc)0, /* tp_iternext */ (iternextfunc)0, /* tp_methods */ 0, /* tp_members */ CPB_members, /* tp_getset */ 0, /* tp_base */ &SpecificationBaseType, /* tp_dict */ 0, /* internal use */ /* tp_descr_get */ (descrgetfunc)CPB_descr_get, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ 0, }; /* ==================================================================== */ /* ========== Begin: __call__ and __adapt__ =========================== */ /* def __adapt__(self, obj): """Adapt an object to the receiver """ if self.providedBy(obj): return obj for hook in adapter_hooks: adapter = hook(self, obj) if adapter is not None: return adapter */ static PyObject * __adapt__(PyObject *self, PyObject *obj) { PyObject *decl, *args, *adapter; int implements, i, l; decl = providedBy(NULL, obj); if (decl == NULL) return NULL; if (PyObject_TypeCheck(decl, &SpecificationBaseType)) { PyObject *implied; implied = ((Spec*)decl)->_implied; if (implied == NULL) { Py_DECREF(decl); return NULL; } implements = PyDict_GetItem(implied, self) != NULL; Py_DECREF(decl); } else { /* decl is probably a security proxy. We have to go the long way around. */ PyObject *r; r = PyObject_CallFunctionObjArgs(decl, self, NULL); Py_DECREF(decl); if (r == NULL) return NULL; implements = PyObject_IsTrue(r); Py_DECREF(r); } if (implements) { Py_INCREF(obj); return obj; } l = PyList_GET_SIZE(adapter_hooks); args = PyTuple_New(2); if (args == NULL) return NULL; Py_INCREF(self); PyTuple_SET_ITEM(args, 0, self); Py_INCREF(obj); PyTuple_SET_ITEM(args, 1, obj); for (i = 0; i < l; i++) { adapter = PyObject_CallObject(PyList_GET_ITEM(adapter_hooks, i), args); if (adapter == NULL || adapter != Py_None) { Py_DECREF(args); return adapter; } Py_DECREF(adapter); } Py_DECREF(args); Py_INCREF(Py_None); return Py_None; } typedef struct { Spec spec; PyObject* __name__; PyObject* __module__; Py_hash_t _v_cached_hash; } IB; static struct PyMethodDef ib_methods[] = { {"__adapt__", (PyCFunction)__adapt__, METH_O, "Adapt an object to the receiver"}, {NULL, NULL} /* sentinel */ }; /* def __call__(self, obj, alternate=_marker): try: conform = obj.__conform__ except AttributeError: # pylint:disable=bare-except conform = None if conform is not None: adapter = self._call_conform(conform) if adapter is not None: return adapter adapter = self.__adapt__(obj) if adapter is not None: return adapter if alternate is not _marker: return alternate raise TypeError("Could not adapt", obj, self) */ static PyObject * IB_call(PyObject *self, PyObject *args, PyObject *kwargs) { PyObject *conform, *obj, *alternate, *adapter; static char *kwlist[] = {"obj", "alternate", NULL}; conform = obj = alternate = adapter = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", kwlist, &obj, &alternate)) return NULL; conform = PyObject_GetAttr(obj, str__conform__); if (conform == NULL) { if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { /* Propagate non-AttributeErrors */ return NULL; } PyErr_Clear(); Py_INCREF(Py_None); conform = Py_None; } if (conform != Py_None) { adapter = PyObject_CallMethodObjArgs(self, str_call_conform, conform, NULL); Py_DECREF(conform); if (adapter == NULL || adapter != Py_None) return adapter; Py_DECREF(adapter); } else { Py_DECREF(conform); } /* We differ from the Python code here. For speed, instead of always calling self.__adapt__(), we check to see if the type has defined it. Checking in the dict for __adapt__ isn't sufficient because there's no cheap way to tell if it's the __adapt__ that InterfaceBase itself defines (our type will *never* be InterfaceBase, we're always subclassed by InterfaceClass). Instead, we cooperate with InterfaceClass in Python to set a flag in a new subclass when this is necessary. */ if (PyDict_GetItem(self->ob_type->tp_dict, str_CALL_CUSTOM_ADAPT)) { /* Doesn't matter what the value is. Simply being present is enough. */ adapter = PyObject_CallMethodObjArgs(self, str__adapt__, obj, NULL); } else { adapter = __adapt__(self, obj); } if (adapter == NULL || adapter != Py_None) { return adapter; } Py_DECREF(adapter); if (alternate != NULL) { Py_INCREF(alternate); return alternate; } adapter = Py_BuildValue("sOO", "Could not adapt", obj, self); if (adapter != NULL) { PyErr_SetObject(PyExc_TypeError, adapter); Py_DECREF(adapter); } return NULL; } static int IB_traverse(IB* self, visitproc visit, void* arg) { Py_VISIT(self->__name__); Py_VISIT(self->__module__); return Spec_traverse((Spec*)self, visit, arg); } static int IB_clear(IB* self) { Py_CLEAR(self->__name__); Py_CLEAR(self->__module__); return Spec_clear((Spec*)self); } static void IB_dealloc(IB* self) { PyObject_GC_UnTrack((PyObject *)self); IB_clear(self); Spec_dealloc((Spec*)self); } static PyMemberDef IB_members[] = { {"__name__", T_OBJECT_EX, offsetof(IB, __name__), 0, ""}, // The redundancy between __module__ and __ibmodule__ is because // __module__ is often shadowed by subclasses. {"__module__", T_OBJECT_EX, offsetof(IB, __module__), READONLY, ""}, {"__ibmodule__", T_OBJECT_EX, offsetof(IB, __module__), 0, ""}, {NULL} }; static Py_hash_t IB_hash(IB* self) { PyObject* tuple; if (!self->__module__) { PyErr_SetString(PyExc_AttributeError, "__module__"); return -1; } if (!self->__name__) { PyErr_SetString(PyExc_AttributeError, "__name__"); return -1; } if (self->_v_cached_hash) { return self->_v_cached_hash; } tuple = PyTuple_Pack(2, self->__name__, self->__module__); if (!tuple) { return -1; } self->_v_cached_hash = PyObject_Hash(tuple); Py_CLEAR(tuple); return self->_v_cached_hash; } static PyTypeObject InterfaceBaseType; static PyObject* IB_richcompare(IB* self, PyObject* other, int op) { PyObject* othername; PyObject* othermod; PyObject* oresult; IB* otherib; int result; otherib = NULL; oresult = othername = othermod = NULL; if (OBJECT(self) == other) { switch(op) { case Py_EQ: case Py_LE: case Py_GE: Py_RETURN_TRUE; break; case Py_NE: Py_RETURN_FALSE; } } if (other == Py_None) { switch(op) { case Py_LT: case Py_LE: case Py_NE: Py_RETURN_TRUE; default: Py_RETURN_FALSE; } } if (PyObject_TypeCheck(other, &InterfaceBaseType)) { // This branch borrows references. No need to clean // up if otherib is not null. otherib = (IB*)other; othername = otherib->__name__; othermod = otherib->__module__; } else { othername = PyObject_GetAttrString(other, "__name__"); if (othername) { othermod = PyObject_GetAttrString(other, "__module__"); } if (!othername || !othermod) { if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_AttributeError)) { PyErr_Clear(); oresult = Py_NotImplemented; } goto cleanup; } } #if 0 // This is the simple, straightforward version of what Python does. PyObject* pt1 = PyTuple_Pack(2, self->__name__, self->__module__); PyObject* pt2 = PyTuple_Pack(2, othername, othermod); oresult = PyObject_RichCompare(pt1, pt2, op); #endif // tuple comparison is decided by the first non-equal element. result = PyObject_RichCompareBool(self->__name__, othername, Py_EQ); if (result == 0) { result = PyObject_RichCompareBool(self->__name__, othername, op); } else if (result == 1) { result = PyObject_RichCompareBool(self->__module__, othermod, op); } // If either comparison failed, we have an error set. // Leave oresult NULL so we raise it. if (result == -1) { goto cleanup; } oresult = result ? Py_True : Py_False; cleanup: Py_XINCREF(oresult); if (!otherib) { Py_XDECREF(othername); Py_XDECREF(othermod); } return oresult; } static int IB_init(IB* self, PyObject* args, PyObject* kwargs) { static char *kwlist[] = {"__name__", "__module__", NULL}; PyObject* module = NULL; PyObject* name = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO:InterfaceBase.__init__", kwlist, &name, &module)) { return -1; } IB_clear(self); self->__module__ = module ? module : Py_None; Py_INCREF(self->__module__); self->__name__ = name ? name : Py_None; Py_INCREF(self->__name__); return 0; } static PyTypeObject InterfaceBaseType = { PyVarObject_HEAD_INIT(NULL, 0) /* tp_name */ "_zope_interface_coptimizations." "InterfaceBase", /* tp_basicsize */ sizeof(IB), /* tp_itemsize */ 0, /* tp_dealloc */ (destructor)IB_dealloc, /* tp_print */ (printfunc)0, /* tp_getattr */ (getattrfunc)0, /* tp_setattr */ (setattrfunc)0, /* tp_compare */ 0, /* tp_repr */ (reprfunc)0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ (hashfunc)IB_hash, /* tp_call */ (ternaryfunc)IB_call, /* tp_str */ (reprfunc)0, /* tp_getattro */ (getattrofunc)0, /* tp_setattro */ (setattrofunc)0, /* tp_as_buffer */ 0, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_doc */ "Interface base type providing __call__ and __adapt__", /* tp_traverse */ (traverseproc)IB_traverse, /* tp_clear */ (inquiry)IB_clear, /* tp_richcompare */ (richcmpfunc)IB_richcompare, /* tp_weaklistoffset */ (long)0, /* tp_iter */ (getiterfunc)0, /* tp_iternext */ (iternextfunc)0, /* tp_methods */ ib_methods, /* tp_members */ IB_members, /* tp_getset */ 0, /* tp_base */ &SpecificationBaseType, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ (initproc)IB_init, }; /* =================== End: __call__ and __adapt__ ==================== */ /* ==================================================================== */ /* ==================================================================== */ /* ========================== Begin: Lookup Bases ===================== */ typedef struct { PyObject_HEAD PyObject *_cache; PyObject *_mcache; PyObject *_scache; } lookup; typedef struct { PyObject_HEAD PyObject *_cache; PyObject *_mcache; PyObject *_scache; PyObject *_verify_ro; PyObject *_verify_generations; } verify; static int lookup_traverse(lookup *self, visitproc visit, void *arg) { int vret; if (self->_cache) { vret = visit(self->_cache, arg); if (vret != 0) return vret; } if (self->_mcache) { vret = visit(self->_mcache, arg); if (vret != 0) return vret; } if (self->_scache) { vret = visit(self->_scache, arg); if (vret != 0) return vret; } return 0; } static int lookup_clear(lookup *self) { Py_CLEAR(self->_cache); Py_CLEAR(self->_mcache); Py_CLEAR(self->_scache); return 0; } static void lookup_dealloc(lookup *self) { PyObject_GC_UnTrack((PyObject *)self); lookup_clear(self); Py_TYPE(self)->tp_free((PyObject*)self); } /* def changed(self, ignored=None): self._cache.clear() self._mcache.clear() self._scache.clear() */ static PyObject * lookup_changed(lookup *self, PyObject *ignored) { lookup_clear(self); Py_INCREF(Py_None); return Py_None; } #define ASSURE_DICT(N) if (N == NULL) { N = PyDict_New(); \ if (N == NULL) return NULL; \ } /* def _getcache(self, provided, name): cache = self._cache.get(provided) if cache is None: cache = {} self._cache[provided] = cache if name: c = cache.get(name) if c is None: c = {} cache[name] = c cache = c return cache */ static PyObject * _subcache(PyObject *cache, PyObject *key) { PyObject *subcache; subcache = PyDict_GetItem(cache, key); if (subcache == NULL) { int status; subcache = PyDict_New(); if (subcache == NULL) return NULL; status = PyDict_SetItem(cache, key, subcache); Py_DECREF(subcache); if (status < 0) return NULL; } return subcache; } static PyObject * _getcache(lookup *self, PyObject *provided, PyObject *name) { PyObject *cache; ASSURE_DICT(self->_cache); cache = _subcache(self->_cache, provided); if (cache == NULL) return NULL; if (name != NULL && PyObject_IsTrue(name)) cache = _subcache(cache, name); return cache; } /* def lookup(self, required, provided, name=u'', default=None): cache = self._getcache(provided, name) if len(required) == 1: result = cache.get(required[0], _not_in_mapping) else: result = cache.get(tuple(required), _not_in_mapping) if result is _not_in_mapping: result = self._uncached_lookup(required, provided, name) if len(required) == 1: cache[required[0]] = result else: cache[tuple(required)] = result if result is None: return default return result */ static PyObject * _lookup(lookup *self, PyObject *required, PyObject *provided, PyObject *name, PyObject *default_) { PyObject *result, *key, *cache; result = key = cache = NULL; if ( name && !PyUnicode_Check(name) ) { PyErr_SetString(PyExc_ValueError, "name is not a string or unicode"); return NULL; } /* If `required` is a lazy sequence, it could have arbitrary side-effects, such as clearing our caches. So we must not retrieve the cache until after resolving it. */ required = PySequence_Tuple(required); if (required == NULL) return NULL; cache = _getcache(self, provided, name); if (cache == NULL) return NULL; if (PyTuple_GET_SIZE(required) == 1) key = PyTuple_GET_ITEM(required, 0); else key = required; result = PyDict_GetItem(cache, key); if (result == NULL) { int status; result = PyObject_CallMethodObjArgs(OBJECT(self), str_uncached_lookup, required, provided, name, NULL); if (result == NULL) { Py_DECREF(required); return NULL; } status = PyDict_SetItem(cache, key, result); Py_DECREF(required); if (status < 0) { Py_DECREF(result); return NULL; } } else { Py_INCREF(result); Py_DECREF(required); } if (result == Py_None && default_ != NULL) { Py_DECREF(Py_None); Py_INCREF(default_); return default_; } return result; } static PyObject * lookup_lookup(lookup *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"required", "provided", "name", "default", NULL}; PyObject *required, *provided, *name=NULL, *default_=NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO:LookupBase.lookup", kwlist, &required, &provided, &name, &default_)) return NULL; return _lookup(self, required, provided, name, default_); } /* def lookup1(self, required, provided, name=u'', default=None): cache = self._getcache(provided, name) result = cache.get(required, _not_in_mapping) if result is _not_in_mapping: return self.lookup((required, ), provided, name, default) if result is None: return default return result */ static PyObject * _lookup1(lookup *self, PyObject *required, PyObject *provided, PyObject *name, PyObject *default_) { PyObject *result, *cache; if ( name && !PyUnicode_Check(name) ) { PyErr_SetString(PyExc_ValueError, "name is not a string or unicode"); return NULL; } cache = _getcache(self, provided, name); if (cache == NULL) return NULL; result = PyDict_GetItem(cache, required); if (result == NULL) { PyObject *tup; tup = PyTuple_New(1); if (tup == NULL) return NULL; Py_INCREF(required); PyTuple_SET_ITEM(tup, 0, required); result = _lookup(self, tup, provided, name, default_); Py_DECREF(tup); } else { if (result == Py_None && default_ != NULL) { result = default_; } Py_INCREF(result); } return result; } static PyObject * lookup_lookup1(lookup *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"required", "provided", "name", "default", NULL}; PyObject *required, *provided, *name=NULL, *default_=NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO:LookupBase.lookup1", kwlist, &required, &provided, &name, &default_)) return NULL; return _lookup1(self, required, provided, name, default_); } /* def adapter_hook(self, provided, object, name=u'', default=None): required = providedBy(object) cache = self._getcache(provided, name) factory = cache.get(required, _not_in_mapping) if factory is _not_in_mapping: factory = self.lookup((required, ), provided, name) if factory is not None: if isinstance(object, super): object = object.__self__ result = factory(object) if result is not None: return result return default */ static PyObject * _adapter_hook(lookup *self, PyObject *provided, PyObject *object, PyObject *name, PyObject *default_) { PyObject *required, *factory, *result; if ( name && !PyUnicode_Check(name) ) { PyErr_SetString(PyExc_ValueError, "name is not a string or unicode"); return NULL; } required = providedBy(NULL, object); if (required == NULL) return NULL; factory = _lookup1(self, required, provided, name, Py_None); Py_DECREF(required); if (factory == NULL) return NULL; if (factory != Py_None) { if (PyObject_TypeCheck(object, &PySuper_Type)) { PyObject* self = PyObject_GetAttr(object, str__self__); if (self == NULL) { Py_DECREF(factory); return NULL; } // Borrow the reference to self Py_DECREF(self); object = self; } result = PyObject_CallFunctionObjArgs(factory, object, NULL); Py_DECREF(factory); if (result == NULL || result != Py_None) return result; } else result = factory; /* None */ if (default_ == NULL || default_ == result) /* No default specified, */ return result; /* Return None. result is owned None */ Py_DECREF(result); Py_INCREF(default_); return default_; } static PyObject * lookup_adapter_hook(lookup *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"provided", "object", "name", "default", NULL}; PyObject *object, *provided, *name=NULL, *default_=NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO:LookupBase.adapter_hook", kwlist, &provided, &object, &name, &default_)) return NULL; return _adapter_hook(self, provided, object, name, default_); } static PyObject * lookup_queryAdapter(lookup *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"object", "provided", "name", "default", NULL}; PyObject *object, *provided, *name=NULL, *default_=NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO:LookupBase.queryAdapter", kwlist, &object, &provided, &name, &default_)) return NULL; return _adapter_hook(self, provided, object, name, default_); } /* def lookupAll(self, required, provided): cache = self._mcache.get(provided) if cache is None: cache = {} self._mcache[provided] = cache required = tuple(required) result = cache.get(required, _not_in_mapping) if result is _not_in_mapping: result = self._uncached_lookupAll(required, provided) cache[required] = result return result */ static PyObject * _lookupAll(lookup *self, PyObject *required, PyObject *provided) { PyObject *cache, *result; /* resolve before getting cache. See note in _lookup. */ required = PySequence_Tuple(required); if (required == NULL) return NULL; ASSURE_DICT(self->_mcache); cache = _subcache(self->_mcache, provided); if (cache == NULL) return NULL; result = PyDict_GetItem(cache, required); if (result == NULL) { int status; result = PyObject_CallMethodObjArgs(OBJECT(self), str_uncached_lookupAll, required, provided, NULL); if (result == NULL) { Py_DECREF(required); return NULL; } status = PyDict_SetItem(cache, required, result); Py_DECREF(required); if (status < 0) { Py_DECREF(result); return NULL; } } else { Py_INCREF(result); Py_DECREF(required); } return result; } static PyObject * lookup_lookupAll(lookup *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"required", "provided", NULL}; PyObject *required, *provided; if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO:LookupBase.lookupAll", kwlist, &required, &provided)) return NULL; return _lookupAll(self, required, provided); } /* def subscriptions(self, required, provided): cache = self._scache.get(provided) if cache is None: cache = {} self._scache[provided] = cache required = tuple(required) result = cache.get(required, _not_in_mapping) if result is _not_in_mapping: result = self._uncached_subscriptions(required, provided) cache[required] = result return result */ static PyObject * _subscriptions(lookup *self, PyObject *required, PyObject *provided) { PyObject *cache, *result; /* resolve before getting cache. See note in _lookup. */ required = PySequence_Tuple(required); if (required == NULL) return NULL; ASSURE_DICT(self->_scache); cache = _subcache(self->_scache, provided); if (cache == NULL) return NULL; result = PyDict_GetItem(cache, required); if (result == NULL) { int status; result = PyObject_CallMethodObjArgs( OBJECT(self), str_uncached_subscriptions, required, provided, NULL); if (result == NULL) { Py_DECREF(required); return NULL; } status = PyDict_SetItem(cache, required, result); Py_DECREF(required); if (status < 0) { Py_DECREF(result); return NULL; } } else { Py_INCREF(result); Py_DECREF(required); } return result; } static PyObject * lookup_subscriptions(lookup *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"required", "provided", NULL}; PyObject *required, *provided; if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &required, &provided)) return NULL; return _subscriptions(self, required, provided); } static struct PyMethodDef lookup_methods[] = { {"changed", (PyCFunction)lookup_changed, METH_O, ""}, {"lookup", (PyCFunction)lookup_lookup, METH_KEYWORDS | METH_VARARGS, ""}, {"lookup1", (PyCFunction)lookup_lookup1, METH_KEYWORDS | METH_VARARGS, ""}, {"queryAdapter", (PyCFunction)lookup_queryAdapter, METH_KEYWORDS | METH_VARARGS, ""}, {"adapter_hook", (PyCFunction)lookup_adapter_hook, METH_KEYWORDS | METH_VARARGS, ""}, {"lookupAll", (PyCFunction)lookup_lookupAll, METH_KEYWORDS | METH_VARARGS, ""}, {"subscriptions", (PyCFunction)lookup_subscriptions, METH_KEYWORDS | METH_VARARGS, ""}, {NULL, NULL} /* sentinel */ }; static PyTypeObject LookupBase = { PyVarObject_HEAD_INIT(NULL, 0) /* tp_name */ "_zope_interface_coptimizations." "LookupBase", /* tp_basicsize */ sizeof(lookup), /* tp_itemsize */ 0, /* tp_dealloc */ (destructor)&lookup_dealloc, /* tp_print */ (printfunc)0, /* tp_getattr */ (getattrfunc)0, /* tp_setattr */ (setattrfunc)0, /* tp_compare */ 0, /* tp_repr */ (reprfunc)0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ (hashfunc)0, /* tp_call */ (ternaryfunc)0, /* tp_str */ (reprfunc)0, /* tp_getattro */ (getattrofunc)0, /* tp_setattro */ (setattrofunc)0, /* tp_as_buffer */ 0, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_doc */ "", /* tp_traverse */ (traverseproc)lookup_traverse, /* tp_clear */ (inquiry)lookup_clear, /* tp_richcompare */ (richcmpfunc)0, /* tp_weaklistoffset */ (long)0, /* tp_iter */ (getiterfunc)0, /* tp_iternext */ (iternextfunc)0, /* tp_methods */ lookup_methods, }; static int verifying_traverse(verify *self, visitproc visit, void *arg) { int vret; vret = lookup_traverse((lookup *)self, visit, arg); if (vret != 0) return vret; if (self->_verify_ro) { vret = visit(self->_verify_ro, arg); if (vret != 0) return vret; } if (self->_verify_generations) { vret = visit(self->_verify_generations, arg); if (vret != 0) return vret; } return 0; } static int verifying_clear(verify *self) { lookup_clear((lookup *)self); Py_CLEAR(self->_verify_generations); Py_CLEAR(self->_verify_ro); return 0; } static void verifying_dealloc(verify *self) { PyObject_GC_UnTrack((PyObject *)self); verifying_clear(self); Py_TYPE(self)->tp_free((PyObject*)self); } /* def changed(self, originally_changed): super(VerifyingBasePy, self).changed(originally_changed) self._verify_ro = self._registry.ro[1:] self._verify_generations = [r._generation for r in self._verify_ro] */ static PyObject * _generations_tuple(PyObject *ro) { int i, l; PyObject *generations; l = PyTuple_GET_SIZE(ro); generations = PyTuple_New(l); for (i=0; i < l; i++) { PyObject *generation; generation = PyObject_GetAttr(PyTuple_GET_ITEM(ro, i), str_generation); if (generation == NULL) { Py_DECREF(generations); return NULL; } PyTuple_SET_ITEM(generations, i, generation); } return generations; } static PyObject * verifying_changed(verify *self, PyObject *ignored) { PyObject *t, *ro; verifying_clear(self); t = PyObject_GetAttr(OBJECT(self), str_registry); if (t == NULL) return NULL; ro = PyObject_GetAttr(t, strro); Py_DECREF(t); if (ro == NULL) return NULL; t = PyObject_CallFunctionObjArgs(OBJECT(&PyTuple_Type), ro, NULL); Py_DECREF(ro); if (t == NULL) return NULL; ro = PyTuple_GetSlice(t, 1, PyTuple_GET_SIZE(t)); Py_DECREF(t); if (ro == NULL) return NULL; self->_verify_generations = _generations_tuple(ro); if (self->_verify_generations == NULL) { Py_DECREF(ro); return NULL; } self->_verify_ro = ro; Py_INCREF(Py_None); return Py_None; } /* def _verify(self): if ([r._generation for r in self._verify_ro] != self._verify_generations): self.changed(None) */ static int _verify(verify *self) { PyObject *changed_result; if (self->_verify_ro != NULL && self->_verify_generations != NULL) { PyObject *generations; int changed; generations = _generations_tuple(self->_verify_ro); if (generations == NULL) return -1; changed = PyObject_RichCompareBool(self->_verify_generations, generations, Py_NE); Py_DECREF(generations); if (changed == -1) return -1; if (changed == 0) return 0; } changed_result = PyObject_CallMethodObjArgs(OBJECT(self), strchanged, Py_None, NULL); if (changed_result == NULL) return -1; Py_DECREF(changed_result); return 0; } static PyObject * verifying_lookup(verify *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"required", "provided", "name", "default", NULL}; PyObject *required, *provided, *name=NULL, *default_=NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, &required, &provided, &name, &default_)) return NULL; if (_verify(self) < 0) return NULL; return _lookup((lookup *)self, required, provided, name, default_); } static PyObject * verifying_lookup1(verify *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"required", "provided", "name", "default", NULL}; PyObject *required, *provided, *name=NULL, *default_=NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, &required, &provided, &name, &default_)) return NULL; if (_verify(self) < 0) return NULL; return _lookup1((lookup *)self, required, provided, name, default_); } static PyObject * verifying_adapter_hook(verify *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"provided", "object", "name", "default", NULL}; PyObject *object, *provided, *name=NULL, *default_=NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, &provided, &object, &name, &default_)) return NULL; if (_verify(self) < 0) return NULL; return _adapter_hook((lookup *)self, provided, object, name, default_); } static PyObject * verifying_queryAdapter(verify *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"object", "provided", "name", "default", NULL}; PyObject *object, *provided, *name=NULL, *default_=NULL; if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO|OO", kwlist, &object, &provided, &name, &default_)) return NULL; if (_verify(self) < 0) return NULL; return _adapter_hook((lookup *)self, provided, object, name, default_); } static PyObject * verifying_lookupAll(verify *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"required", "provided", NULL}; PyObject *required, *provided; if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &required, &provided)) return NULL; if (_verify(self) < 0) return NULL; return _lookupAll((lookup *)self, required, provided); } static PyObject * verifying_subscriptions(verify *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"required", "provided", NULL}; PyObject *required, *provided; if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &required, &provided)) return NULL; if (_verify(self) < 0) return NULL; return _subscriptions((lookup *)self, required, provided); } static struct PyMethodDef verifying_methods[] = { {"changed", (PyCFunction)verifying_changed, METH_O, ""}, {"lookup", (PyCFunction)verifying_lookup, METH_KEYWORDS | METH_VARARGS, ""}, {"lookup1", (PyCFunction)verifying_lookup1, METH_KEYWORDS | METH_VARARGS, ""}, {"queryAdapter", (PyCFunction)verifying_queryAdapter, METH_KEYWORDS | METH_VARARGS, ""}, {"adapter_hook", (PyCFunction)verifying_adapter_hook, METH_KEYWORDS | METH_VARARGS, ""}, {"lookupAll", (PyCFunction)verifying_lookupAll, METH_KEYWORDS | METH_VARARGS, ""}, {"subscriptions", (PyCFunction)verifying_subscriptions, METH_KEYWORDS | METH_VARARGS, ""}, {NULL, NULL} /* sentinel */ }; static PyTypeObject VerifyingBase = { PyVarObject_HEAD_INIT(NULL, 0) /* tp_name */ "_zope_interface_coptimizations." "VerifyingBase", /* tp_basicsize */ sizeof(verify), /* tp_itemsize */ 0, /* tp_dealloc */ (destructor)&verifying_dealloc, /* tp_print */ (printfunc)0, /* tp_getattr */ (getattrfunc)0, /* tp_setattr */ (setattrfunc)0, /* tp_compare */ 0, /* tp_repr */ (reprfunc)0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ (hashfunc)0, /* tp_call */ (ternaryfunc)0, /* tp_str */ (reprfunc)0, /* tp_getattro */ (getattrofunc)0, /* tp_setattro */ (setattrofunc)0, /* tp_as_buffer */ 0, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_doc */ "", /* tp_traverse */ (traverseproc)verifying_traverse, /* tp_clear */ (inquiry)verifying_clear, /* tp_richcompare */ (richcmpfunc)0, /* tp_weaklistoffset */ (long)0, /* tp_iter */ (getiterfunc)0, /* tp_iternext */ (iternextfunc)0, /* tp_methods */ verifying_methods, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ &LookupBase, }; /* ========================== End: Lookup Bases ======================= */ /* ==================================================================== */ static struct PyMethodDef m_methods[] = { {"implementedBy", (PyCFunction)implementedBy, METH_O, "Interfaces implemented by a class or factory.\n" "Raises TypeError if argument is neither a class nor a callable."}, {"getObjectSpecification", (PyCFunction)getObjectSpecification, METH_O, "Get an object's interfaces (internal api)"}, {"providedBy", (PyCFunction)providedBy, METH_O, "Get an object's interfaces"}, {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */ }; static char module_doc[] = "C optimizations for zope.interface\n\n"; static struct PyModuleDef _zic_module = { PyModuleDef_HEAD_INIT, "_zope_interface_coptimizations", module_doc, -1, m_methods, NULL, NULL, NULL, NULL }; static PyObject * init(void) { PyObject *m; #define DEFINE_STRING(S) \ if(! (str ## S = PyUnicode_FromString(# S))) return NULL DEFINE_STRING(__dict__); DEFINE_STRING(__implemented__); DEFINE_STRING(__provides__); DEFINE_STRING(__class__); DEFINE_STRING(__providedBy__); DEFINE_STRING(extends); DEFINE_STRING(__conform__); DEFINE_STRING(_call_conform); DEFINE_STRING(_uncached_lookup); DEFINE_STRING(_uncached_lookupAll); DEFINE_STRING(_uncached_subscriptions); DEFINE_STRING(_registry); DEFINE_STRING(_generation); DEFINE_STRING(ro); DEFINE_STRING(changed); DEFINE_STRING(__self__); DEFINE_STRING(__name__); DEFINE_STRING(__module__); DEFINE_STRING(__adapt__); DEFINE_STRING(_CALL_CUSTOM_ADAPT); #undef DEFINE_STRING adapter_hooks = PyList_New(0); if (adapter_hooks == NULL) return NULL; /* Initialize types: */ SpecificationBaseType.tp_new = PyBaseObject_Type.tp_new; if (PyType_Ready(&SpecificationBaseType) < 0) return NULL; OSDType.tp_new = PyBaseObject_Type.tp_new; if (PyType_Ready(&OSDType) < 0) return NULL; CPBType.tp_new = PyBaseObject_Type.tp_new; if (PyType_Ready(&CPBType) < 0) return NULL; InterfaceBaseType.tp_new = PyBaseObject_Type.tp_new; if (PyType_Ready(&InterfaceBaseType) < 0) return NULL; LookupBase.tp_new = PyBaseObject_Type.tp_new; if (PyType_Ready(&LookupBase) < 0) return NULL; VerifyingBase.tp_new = PyBaseObject_Type.tp_new; if (PyType_Ready(&VerifyingBase) < 0) return NULL; m = PyModule_Create(&_zic_module); if (m == NULL) return NULL; /* Add types: */ if (PyModule_AddObject(m, "SpecificationBase", OBJECT(&SpecificationBaseType)) < 0) return NULL; if (PyModule_AddObject(m, "ObjectSpecificationDescriptor", (PyObject *)&OSDType) < 0) return NULL; if (PyModule_AddObject(m, "ClassProvidesBase", OBJECT(&CPBType)) < 0) return NULL; if (PyModule_AddObject(m, "InterfaceBase", OBJECT(&InterfaceBaseType)) < 0) return NULL; if (PyModule_AddObject(m, "LookupBase", OBJECT(&LookupBase)) < 0) return NULL; if (PyModule_AddObject(m, "VerifyingBase", OBJECT(&VerifyingBase)) < 0) return NULL; if (PyModule_AddObject(m, "adapter_hooks", adapter_hooks) < 0) return NULL; return m; } PyMODINIT_FUNC PyInit__zope_interface_coptimizations(void) { return init(); } #ifdef __clang__ #pragma clang diagnostic pop #endif zope.interface-6.4/src/zope/interface/adapter.py000066400000000000000000001065721462121350100220030ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2004 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. # ############################################################################## """Adapter management """ import itertools import weakref from zope.interface import Interface from zope.interface import implementer from zope.interface import providedBy from zope.interface import ro from zope.interface._compat import _normalize_name from zope.interface._compat import _use_c_impl from zope.interface.interfaces import IAdapterRegistry __all__ = [ 'AdapterRegistry', 'VerifyingAdapterRegistry', ] # In the CPython implementation, # ``tuple`` and ``list`` cooperate so that ``tuple([some list])`` # directly allocates and iterates at the C level without using a # Python iterator. That's not the case for # ``tuple(generator_expression)`` or ``tuple(map(func, it))``. ## # 3.8 # ``tuple([t for t in range(10)])`` -> 610ns # ``tuple(t for t in range(10))`` -> 696ns # ``tuple(map(lambda t: t, range(10)))`` -> 881ns ## # 2.7 # ``tuple([t fon t in range(10)])`` -> 625ns # ``tuple(t for t in range(10))`` -> 665ns # ``tuple(map(lambda t: t, range(10)))`` -> 958ns # # All three have substantial variance. ## # On PyPy, this is also the best option. ## # PyPy 2.7.18-7.3.3 # ``tuple([t fon t in range(10)])`` -> 128ns # ``tuple(t for t in range(10))`` -> 175ns # ``tuple(map(lambda t: t, range(10)))`` -> 153ns ## # PyPy 3.7.9 7.3.3-beta # ``tuple([t fon t in range(10)])`` -> 82ns # ``tuple(t for t in range(10))`` -> 177ns # ``tuple(map(lambda t: t, range(10)))`` -> 168ns # class BaseAdapterRegistry: """ A basic implementation of the data storage and algorithms required for a :class:`zope.interface.interfaces.IAdapterRegistry`. Subclasses can set the following attributes to control how the data is stored; in particular, these hooks can be helpful for ZODB persistence. They can be class attributes that are the named (or similar) type, or they can be methods that act as a constructor for an object that behaves like the types defined here; this object will not assume that they are type objects, but subclasses are free to do so: _sequenceType = list This is the type used for our two mutable top-level "byorder" sequences. Must support mutation operations like ``append()`` and ``del seq[index]``. These are usually small (< 10). Although at least one of them is accessed when performing lookups or queries on this object, the other is untouched. In many common scenarios, both are only required when mutating registrations and subscriptions (like what :meth:`zope.interface.interfaces.IComponents.registerUtility` does). This use pattern makes it an ideal candidate to be a :class:`~persistent.list.PersistentList`. _leafSequenceType = tuple This is the type used for the leaf sequences of subscribers. It could be set to a ``PersistentList`` to avoid many unnecessary data loads when subscribers aren't being used. Mutation operations are directed through :meth:`_addValueToLeaf` and :meth:`_removeValueFromLeaf`; if you use a mutable type, you'll need to override those. _mappingType = dict This is the mutable mapping type used for the keyed mappings. A :class:`~persistent.mapping.PersistentMapping` could be used to help reduce the number of data loads when the registry is large and parts of it are rarely used. Further reductions in data loads can come from using a :class:`~BTrees.OOBTree.OOBTree`, but care is required to be sure that all required/provided values are fully ordered (e.g., no required or provided values that are classes can be used). _providedType = dict This is the mutable mapping type used for the ``_provided`` mapping. This is separate from the generic mapping type because the values are always integers, so one might choose to use a more optimized data structure such as a :class:`~BTrees.OIBTree.OIBTree`. The same caveats regarding key types apply as for ``_mappingType``. It is possible to also set these on an instance, but because of the need to potentially also override :meth:`_addValueToLeaf` and :meth:`_removeValueFromLeaf`, this may be less useful in a persistent scenario; using a subclass is recommended. .. versionchanged:: 5.3.0 Add support for customizing the way internal data structures are created. .. versionchanged:: 5.3.0 Add methods :meth:`rebuild`, :meth:`allRegistrations` and :meth:`allSubscriptions`. """ # List of methods copied from lookup sub-objects: _delegated = ('lookup', 'queryMultiAdapter', 'lookup1', 'queryAdapter', 'adapter_hook', 'lookupAll', 'names', 'subscriptions', 'subscribers') # All registries maintain a generation that can be used by verifying # registries _generation = 0 def __init__(self, bases=()): # The comments here could be improved. Possibly this bit needs # explaining in a separate document, as the comments here can # be quite confusing. /regebro # {order -> {required -> {provided -> {name -> value}}}} # Here "order" is actually an index in a list, "required" and # "provided" are interfaces, and "required" is really a nested # key. So, for example: # for order == 0 (that is, self._adapters[0]), we have: # {provided -> {name -> value}} # but for order == 2 (that is, self._adapters[2]), we have: # {r1 -> {r2 -> {provided -> {name -> value}}}} # self._adapters = self._sequenceType() # {order -> {required -> {provided -> {name -> [value]}}}} # where the remarks about adapters above apply self._subscribers = self._sequenceType() # Set, with a reference count, keeping track of the interfaces # for which we have provided components: self._provided = self._providedType() # Create ``_v_lookup`` object to perform lookup. We make this a # separate object to to make it easier to implement just the # lookup functionality in C. This object keeps track of cache # invalidation data in two kinds of registries. # Invalidating registries have caches that are invalidated # when they or their base registies change. An invalidating # registry can only have invalidating registries as bases. # See LookupBaseFallback below for the pertinent logic. # Verifying registies can't rely on getting invalidation messages, # so have to check the generations of base registries to determine # if their cache data are current. See VerifyingBasePy below # for the pertinent object. self._createLookup() # Setting the bases causes the registries described above # to be initialized (self._setBases -> self.changed -> # self._v_lookup.changed). self.__bases__ = bases def _setBases(self, bases): """ If subclasses need to track when ``__bases__`` changes, they can override this method. Subclasses must still call this method. """ self.__dict__['__bases__'] = bases self.ro = ro.ro(self) self.changed(self) __bases__ = property(lambda self: self.__dict__['__bases__'], lambda self, bases: self._setBases(bases), ) def _createLookup(self): self._v_lookup = self.LookupClass(self) for name in self._delegated: self.__dict__[name] = getattr(self._v_lookup, name) # Hooks for subclasses to define the types of objects used in # our data structures. # These have to be documented in the docstring, instead of local # comments, because Sphinx autodoc ignores the comment and just writes # "alias of list" _sequenceType = list _leafSequenceType = tuple _mappingType = dict _providedType = dict def _addValueToLeaf(self, existing_leaf_sequence, new_item): """ Add the value *new_item* to the *existing_leaf_sequence*, which may be ``None``. Subclasses that redefine `_leafSequenceType` should override this method. :param existing_leaf_sequence: If *existing_leaf_sequence* is not *None*, it will be an instance of `_leafSequenceType`. (Unless the object has been unpickled from an old pickle and the class definition has changed, in which case it may be an instance of a previous definition, commonly a `tuple`.) :return: This method returns the new value to be stored. It may mutate the sequence in place if it was not ``None`` and the type is mutable, but it must also return it. .. versionadded:: 5.3.0 """ if existing_leaf_sequence is None: return (new_item,) return existing_leaf_sequence + (new_item,) def _removeValueFromLeaf(self, existing_leaf_sequence, to_remove): """ Remove the item *to_remove* from the (non-``None``, non-empty) *existing_leaf_sequence* and return the mutated sequence. If there is more than one item that is equal to *to_remove* they must all be removed. Subclasses that redefine `_leafSequenceType` should override this method. Note that they can call this method to help in their implementation; this implementation will always return a new tuple constructed by iterating across the *existing_leaf_sequence* and omitting items equal to *to_remove*. :param existing_leaf_sequence: As for `_addValueToLeaf`, probably an instance of `_leafSequenceType` but possibly an older type; never `None`. :return: A version of *existing_leaf_sequence* with all items equal to *to_remove* removed. Must not return `None`. However, returning an empty object, even of another type such as the empty tuple, ``()`` is explicitly allowed; such an object will never be stored. .. versionadded:: 5.3.0 """ return tuple([v for v in existing_leaf_sequence if v != to_remove]) def changed(self, originally_changed): self._generation += 1 self._v_lookup.changed(originally_changed) def register(self, required, provided, name, value): if not isinstance(name, str): raise ValueError('name is not a string') if value is None: self.unregister(required, provided, name, value) return required = tuple([_convert_None_to_Interface(r) for r in required]) name = _normalize_name(name) order = len(required) byorder = self._adapters while len(byorder) <= order: byorder.append(self._mappingType()) components = byorder[order] key = required + (provided,) for k in key: d = components.get(k) if d is None: d = self._mappingType() components[k] = d components = d if components.get(name) is value: return components[name] = value n = self._provided.get(provided, 0) + 1 self._provided[provided] = n if n == 1: self._v_lookup.add_extendor(provided) self.changed(self) def _find_leaf(self, byorder, required, provided, name): # Find the leaf value, if any, in the *byorder* list # for the interface sequence *required* and the interface # *provided*, given the already normalized *name*. # # If no such leaf value exists, returns ``None`` required = tuple([_convert_None_to_Interface(r) for r in required]) order = len(required) if len(byorder) <= order: return None components = byorder[order] key = required + (provided,) for k in key: d = components.get(k) if d is None: return None components = d return components.get(name) def registered(self, required, provided, name=''): return self._find_leaf( self._adapters, required, provided, _normalize_name(name) ) @classmethod def _allKeys(cls, components, i, parent_k=()): if i == 0: for k, v in components.items(): yield parent_k + (k,), v else: for k, v in components.items(): new_parent_k = parent_k + (k,) yield from cls._allKeys(v, i - 1, new_parent_k) def _all_entries(self, byorder): # Recurse through the mapping levels of the `byorder` sequence, # reconstructing a flattened sequence of ``(required, provided, name, value)`` # tuples that can be used to reconstruct the sequence with the appropriate # registration methods. # # Locally reference the `byorder` data; it might be replaced while # this method is running (see ``rebuild``). for i, components in enumerate(byorder): # We will have *i* levels of dictionaries to go before # we get to the leaf. for key, value in self._allKeys(components, i + 1): assert len(key) == i + 2 required = key[:i] provided = key[-2] name = key[-1] yield (required, provided, name, value) def allRegistrations(self): """ Yields tuples ``(required, provided, name, value)`` for all the registrations that this object holds. These tuples could be passed as the arguments to the :meth:`register` method on another adapter registry to duplicate the registrations this object holds. .. versionadded:: 5.3.0 """ yield from self._all_entries(self._adapters) def unregister(self, required, provided, name, value=None): required = tuple([_convert_None_to_Interface(r) for r in required]) order = len(required) byorder = self._adapters if order >= len(byorder): return False components = byorder[order] key = required + (provided,) # Keep track of how we got to `components`: lookups = [] for k in key: d = components.get(k) if d is None: return lookups.append((components, k)) components = d old = components.get(name) if old is None: return if (value is not None) and (old is not value): return del components[name] if not components: # Clean out empty containers, since we don't want our keys # to reference global objects (interfaces) unnecessarily. # This is often a problem when an interface is slated for # removal; a hold-over entry in the registry can make it # difficult to remove such interfaces. for comp, k in reversed(lookups): d = comp[k] if d: break else: del comp[k] while byorder and not byorder[-1]: del byorder[-1] n = self._provided[provided] - 1 if n == 0: del self._provided[provided] self._v_lookup.remove_extendor(provided) else: self._provided[provided] = n self.changed(self) def subscribe(self, required, provided, value): required = tuple([_convert_None_to_Interface(r) for r in required]) name = '' order = len(required) byorder = self._subscribers while len(byorder) <= order: byorder.append(self._mappingType()) components = byorder[order] key = required + (provided,) for k in key: d = components.get(k) if d is None: d = self._mappingType() components[k] = d components = d components[name] = self._addValueToLeaf(components.get(name), value) if provided is not None: n = self._provided.get(provided, 0) + 1 self._provided[provided] = n if n == 1: self._v_lookup.add_extendor(provided) self.changed(self) def subscribed(self, required, provided, subscriber): subscribers = self._find_leaf( self._subscribers, required, provided, '' ) or () return subscriber if subscriber in subscribers else None def allSubscriptions(self): """ Yields tuples ``(required, provided, value)`` for all the subscribers that this object holds. These tuples could be passed as the arguments to the :meth:`subscribe` method on another adapter registry to duplicate the registrations this object holds. .. versionadded:: 5.3.0 """ for required, provided, _name, value in self._all_entries(self._subscribers): for v in value: yield (required, provided, v) def unsubscribe(self, required, provided, value=None): required = tuple([_convert_None_to_Interface(r) for r in required]) order = len(required) byorder = self._subscribers if order >= len(byorder): return components = byorder[order] key = required + (provided,) # Keep track of how we got to `components`: lookups = [] for k in key: d = components.get(k) if d is None: return lookups.append((components, k)) components = d old = components.get('') if not old: # this is belt-and-suspenders against the failure of cleanup below return # pragma: no cover len_old = len(old) if value is None: # Removing everything; note that the type of ``new`` won't # necessarily match the ``_leafSequenceType``, but that's # OK because we're about to delete the entire entry # anyway. new = () else: new = self._removeValueFromLeaf(old, value) # ``new`` may be the same object as ``old``, just mutated in place, # so we cannot compare it to ``old`` to check for changes. Remove # our reference to it now to avoid trying to do so below. del old if len(new) == len_old: # No changes, so nothing could have been removed. return if new: components[''] = new else: # Instead of setting components[u''] = new, we clean out # empty containers, since we don't want our keys to # reference global objects (interfaces) unnecessarily. This # is often a problem when an interface is slated for # removal; a hold-over entry in the registry can make it # difficult to remove such interfaces. del components[''] for comp, k in reversed(lookups): d = comp[k] if d: break else: del comp[k] while byorder and not byorder[-1]: del byorder[-1] if provided is not None: n = self._provided[provided] + len(new) - len_old if n == 0: del self._provided[provided] self._v_lookup.remove_extendor(provided) else: self._provided[provided] = n self.changed(self) def rebuild(self): """ Rebuild (and replace) all the internal data structures of this object. This is useful, especially for persistent implementations, if you suspect an issue with reference counts keeping interfaces alive even though they are no longer used. It is also useful if you or a subclass change the data types (``_mappingType`` and friends) that are to be used. This method replaces all internal data structures with new objects; it specifically does not re-use any storage. .. versionadded:: 5.3.0 """ # Grab the iterators, we're about to discard their data. registrations = self.allRegistrations() subscriptions = self.allSubscriptions() def buffer(it): # The generator doesn't actually start running until we # ask for its next(), by which time the attributes will change # unless we do so before calling __init__. try: first = next(it) except StopIteration: return iter(()) return itertools.chain((first,), it) registrations = buffer(registrations) subscriptions = buffer(subscriptions) # Replace the base data structures as well as _v_lookup. self.__init__(self.__bases__) # Re-register everything previously registered and subscribed. # # XXX: This is going to call ``self.changed()`` a lot, all of # which is unnecessary (because ``self.__init__`` just # re-created those dependent objects and also called # ``self.changed()``). Is this a bottleneck that needs fixed? # (We could do ``self.changed = lambda _: None`` before # beginning and remove it after to disable the presumably expensive # part of passing that notification to the change of objects.) for args in registrations: self.register(*args) for args in subscriptions: self.subscribe(*args) # XXX hack to fake out twisted's use of a private api. We need to get them # to use the new registered method. def get(self, _): # pragma: no cover class XXXTwistedFakeOut: selfImplied = {} return XXXTwistedFakeOut _not_in_mapping = object() @_use_c_impl class LookupBase: def __init__(self): self._cache = {} self._mcache = {} self._scache = {} def changed(self, ignored=None): self._cache.clear() self._mcache.clear() self._scache.clear() def _getcache(self, provided, name): cache = self._cache.get(provided) if cache is None: cache = {} self._cache[provided] = cache if name: c = cache.get(name) if c is None: c = {} cache[name] = c cache = c return cache def lookup(self, required, provided, name='', default=None): if not isinstance(name, str): raise ValueError('name is not a string') cache = self._getcache(provided, name) required = tuple(required) if len(required) == 1: result = cache.get(required[0], _not_in_mapping) else: result = cache.get(tuple(required), _not_in_mapping) if result is _not_in_mapping: result = self._uncached_lookup(required, provided, name) if len(required) == 1: cache[required[0]] = result else: cache[tuple(required)] = result if result is None: return default return result def lookup1(self, required, provided, name='', default=None): if not isinstance(name, str): raise ValueError('name is not a string') cache = self._getcache(provided, name) result = cache.get(required, _not_in_mapping) if result is _not_in_mapping: return self.lookup((required, ), provided, name, default) if result is None: return default return result def queryAdapter(self, object, provided, name='', default=None): return self.adapter_hook(provided, object, name, default) def adapter_hook(self, provided, object, name='', default=None): if not isinstance(name, str): raise ValueError('name is not a string') required = providedBy(object) cache = self._getcache(provided, name) factory = cache.get(required, _not_in_mapping) if factory is _not_in_mapping: factory = self.lookup((required, ), provided, name) if factory is not None: if isinstance(object, super): object = object.__self__ result = factory(object) if result is not None: return result return default def lookupAll(self, required, provided): cache = self._mcache.get(provided) if cache is None: cache = {} self._mcache[provided] = cache required = tuple(required) result = cache.get(required, _not_in_mapping) if result is _not_in_mapping: result = self._uncached_lookupAll(required, provided) cache[required] = result return result def subscriptions(self, required, provided): cache = self._scache.get(provided) if cache is None: cache = {} self._scache[provided] = cache required = tuple(required) result = cache.get(required, _not_in_mapping) if result is _not_in_mapping: result = self._uncached_subscriptions(required, provided) cache[required] = result return result @_use_c_impl class VerifyingBase(LookupBaseFallback): # Mixin for lookups against registries which "chain" upwards, and # whose lookups invalidate their own caches whenever a parent registry # bumps its own '_generation' counter. E.g., used by # zope.component.persistentregistry def changed(self, originally_changed): LookupBaseFallback.changed(self, originally_changed) self._verify_ro = self._registry.ro[1:] self._verify_generations = [r._generation for r in self._verify_ro] def _verify(self): if ([r._generation for r in self._verify_ro] != self._verify_generations): self.changed(None) def _getcache(self, provided, name): self._verify() return LookupBaseFallback._getcache(self, provided, name) def lookupAll(self, required, provided): self._verify() return LookupBaseFallback.lookupAll(self, required, provided) def subscriptions(self, required, provided): self._verify() return LookupBaseFallback.subscriptions(self, required, provided) class AdapterLookupBase: def __init__(self, registry): self._registry = registry self._required = {} self.init_extendors() super().__init__() def changed(self, ignored=None): super().changed(None) for r in self._required.keys(): r = r() if r is not None: r.unsubscribe(self) self._required.clear() # Extendors # --------- # When given an target interface for an adapter lookup, we need to consider # adapters for interfaces that extend the target interface. This is # what the extendors dictionary is about. It tells us all of the # interfaces that extend an interface for which there are adapters # registered. # We could separate this by order and name, thus reducing the # number of provided interfaces to search at run time. The tradeoff, # however, is that we have to store more information. For example, # if the same interface is provided for multiple names and if the # interface extends many interfaces, we'll have to keep track of # a fair bit of information for each name. It's better to # be space efficient here and be time efficient in the cache # implementation. # TODO: add invalidation when a provided interface changes, in case # the interface's __iro__ has changed. This is unlikely enough that # we'll take our chances for now. def init_extendors(self): self._extendors = {} for p in self._registry._provided: self.add_extendor(p) def add_extendor(self, provided): _extendors = self._extendors for i in provided.__iro__: extendors = _extendors.get(i, ()) _extendors[i] = ( [e for e in extendors if provided.isOrExtends(e)] + [provided] + [e for e in extendors if not provided.isOrExtends(e)] ) def remove_extendor(self, provided): _extendors = self._extendors for i in provided.__iro__: _extendors[i] = [e for e in _extendors.get(i, ()) if e != provided] def _subscribe(self, *required): _refs = self._required for r in required: ref = r.weakref() if ref not in _refs: r.subscribe(self) _refs[ref] = 1 def _uncached_lookup(self, required, provided, name=''): required = tuple(required) result = None order = len(required) for registry in self._registry.ro: byorder = registry._adapters if order >= len(byorder): continue extendors = registry._v_lookup._extendors.get(provided) if not extendors: continue components = byorder[order] result = _lookup(components, required, extendors, name, 0, order) if result is not None: break self._subscribe(*required) return result def queryMultiAdapter(self, objects, provided, name='', default=None): factory = self.lookup([providedBy(o) for o in objects], provided, name) if factory is None: return default result = factory(*[o.__self__ if isinstance(o, super) else o for o in objects]) if result is None: return default return result def _uncached_lookupAll(self, required, provided): required = tuple(required) order = len(required) result = {} for registry in reversed(self._registry.ro): byorder = registry._adapters if order >= len(byorder): continue extendors = registry._v_lookup._extendors.get(provided) if not extendors: continue components = byorder[order] _lookupAll(components, required, extendors, result, 0, order) self._subscribe(*required) return tuple(result.items()) def names(self, required, provided): return [c[0] for c in self.lookupAll(required, provided)] def _uncached_subscriptions(self, required, provided): required = tuple(required) order = len(required) result = [] for registry in reversed(self._registry.ro): byorder = registry._subscribers if order >= len(byorder): continue if provided is None: extendors = (provided, ) else: extendors = registry._v_lookup._extendors.get(provided) if extendors is None: continue _subscriptions(byorder[order], required, extendors, '', result, 0, order) self._subscribe(*required) return result def subscribers(self, objects, provided): subscriptions = self.subscriptions([providedBy(o) for o in objects], provided) if provided is None: result = () for subscription in subscriptions: subscription(*objects) else: result = [] for subscription in subscriptions: subscriber = subscription(*objects) if subscriber is not None: result.append(subscriber) return result class AdapterLookup(AdapterLookupBase, LookupBase): pass @implementer(IAdapterRegistry) class AdapterRegistry(BaseAdapterRegistry): """ A full implementation of ``IAdapterRegistry`` that adds support for sub-registries. """ LookupClass = AdapterLookup def __init__(self, bases=()): # AdapterRegisties are invalidating registries, so # we need to keep track of our invalidating subregistries. self._v_subregistries = weakref.WeakKeyDictionary() super().__init__(bases) def _addSubregistry(self, r): self._v_subregistries[r] = 1 def _removeSubregistry(self, r): if r in self._v_subregistries: del self._v_subregistries[r] def _setBases(self, bases): old = self.__dict__.get('__bases__', ()) for r in old: if r not in bases: r._removeSubregistry(self) for r in bases: if r not in old: r._addSubregistry(self) super()._setBases(bases) def changed(self, originally_changed): super().changed(originally_changed) for sub in self._v_subregistries.keys(): sub.changed(originally_changed) class VerifyingAdapterLookup(AdapterLookupBase, VerifyingBase): pass @implementer(IAdapterRegistry) class VerifyingAdapterRegistry(BaseAdapterRegistry): """ The most commonly-used adapter registry. """ LookupClass = VerifyingAdapterLookup def _convert_None_to_Interface(x): if x is None: return Interface else: return x def _lookup(components, specs, provided, name, i, l): # this function is called very often. # The components.get in loops is executed 100 of 1000s times. # by loading get into a local variable the bytecode # "LOAD_FAST 0 (components)" in the loop can be eliminated. components_get = components.get if i < l: for spec in specs[i].__sro__: comps = components_get(spec) if comps: r = _lookup(comps, specs, provided, name, i+1, l) if r is not None: return r else: for iface in provided: comps = components_get(iface) if comps: r = comps.get(name) if r is not None: return r return None def _lookupAll(components, specs, provided, result, i, l): components_get = components.get # see _lookup above if i < l: for spec in reversed(specs[i].__sro__): comps = components_get(spec) if comps: _lookupAll(comps, specs, provided, result, i+1, l) else: for iface in reversed(provided): comps = components_get(iface) if comps: result.update(comps) def _subscriptions(components, specs, provided, name, result, i, l): components_get = components.get # see _lookup above if i < l: for spec in reversed(specs[i].__sro__): comps = components_get(spec) if comps: _subscriptions(comps, specs, provided, name, result, i+1, l) else: for iface in reversed(provided): comps = components_get(iface) if comps: comps = comps.get(name) if comps: result.extend(comps) zope.interface-6.4/src/zope/interface/advice.py000066400000000000000000000074641462121350100216160ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2003 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. # ############################################################################## """Class advice. This module was adapted from 'protocols.advice', part of the Python Enterprise Application Kit (PEAK). Please notify the PEAK authors (pje@telecommunity.com and tsarna@sarna.org) if bugs are found or Zope-specific changes are required, so that the PEAK version of this module can be kept in sync. PEAK is a Python application framework that interoperates with (but does not require) Zope 3 and Twisted. It provides tools for manipulating UML models, object-relational persistence, aspect-oriented programming, and more. Visit the PEAK home page at http://peak.telecommunity.com for more information. """ from types import FunctionType __all__ = [ 'determineMetaclass', 'getFrameInfo', 'isClassAdvisor', 'minimalBases', ] import sys def getFrameInfo(frame): """Return (kind,module,locals,globals) for a frame 'kind' is one of "exec", "module", "class", "function call", or "unknown". """ f_locals = frame.f_locals f_globals = frame.f_globals sameNamespace = f_locals is f_globals hasModule = '__module__' in f_locals hasName = '__name__' in f_globals sameName = hasModule and hasName sameName = sameName and f_globals['__name__']==f_locals['__module__'] module = hasName and sys.modules.get(f_globals['__name__']) or None namespaceIsModule = module and module.__dict__ is f_globals if not namespaceIsModule: # some kind of funky exec kind = "exec" elif sameNamespace and not hasModule: kind = "module" elif sameName and not sameNamespace: kind = "class" elif not sameNamespace: kind = "function call" else: # pragma: no cover # How can you have f_locals is f_globals, and have '__module__' set? # This is probably module-level code, but with a '__module__' variable. kind = "unknown" return kind, module, f_locals, f_globals def isClassAdvisor(ob): """True if 'ob' is a class advisor function""" return isinstance(ob,FunctionType) and hasattr(ob,'previousMetaclass') def determineMetaclass(bases, explicit_mc=None): """Determine metaclass from 1+ bases and optional explicit __metaclass__""" meta = [getattr(b,'__class__',type(b)) for b in bases] if explicit_mc is not None: # The explicit metaclass needs to be verified for compatibility # as well, and allowed to resolve the incompatible bases, if any meta.append(explicit_mc) if len(meta)==1: # easy case return meta[0] candidates = minimalBases(meta) # minimal set of metaclasses if len(candidates)>1: # We could auto-combine, but for now we won't... raise TypeError("Incompatible metatypes", bases) # Just one, return it return candidates[0] def minimalBases(classes): """Reduce a list of base classes to its ordered minimum equivalent""" candidates = [] for m in classes: for n in classes: if issubclass(n,m) and m is not n: break else: # m has no subclasses in 'classes' if m in candidates: candidates.remove(m) # ensure that we're later in the list candidates.append(m) return candidates zope.interface-6.4/src/zope/interface/common/000077500000000000000000000000001462121350100212665ustar00rootroot00000000000000zope.interface-6.4/src/zope/interface/common/__init__.py000066400000000000000000000243361462121350100234070ustar00rootroot00000000000000############################################################################## # Copyright (c) 2020 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. ############################################################################## import itertools from types import FunctionType from zope.interface import Interface from zope.interface import classImplements from zope.interface.interface import InterfaceClass from zope.interface.interface import _decorator_non_return from zope.interface.interface import fromFunction __all__ = [ # Nothing public here. ] # pylint:disable=inherit-non-class, # pylint:disable=no-self-argument,no-method-argument # pylint:disable=unexpected-special-method-signature class optional: # Apply this decorator to a method definition to make it # optional (remove it from the list of required names), overriding # the definition inherited from the ABC. def __init__(self, method): self.__doc__ = method.__doc__ class ABCInterfaceClass(InterfaceClass): """ An interface that is automatically derived from a :class:`abc.ABCMeta` type. Internal use only. The body of the interface definition *must* define a property ``abc`` that is the ABC to base the interface on. If ``abc`` is *not* in the interface definition, a regular interface will be defined instead (but ``extra_classes`` is still respected). Use the ``@optional`` decorator on method definitions if the ABC defines methods that are not actually required in all cases because the Python language has multiple ways to implement a protocol. For example, the ``iter()`` protocol can be implemented with ``__iter__`` or the pair ``__len__`` and ``__getitem__``. When created, any existing classes that are registered to conform to the ABC are declared to implement this interface. This is *not* automatically updated as the ABC registry changes. If the body of the interface definition defines ``extra_classes``, it should be a tuple giving additional classes to declare implement the interface. Note that this is not fully symmetric. For example, it is usually the case that a subclass relationship carries the interface declarations over:: >>> from zope.interface import Interface >>> class I1(Interface): ... pass ... >>> from zope.interface import implementer >>> @implementer(I1) ... class Root(object): ... pass ... >>> class Child(Root): ... pass ... >>> child = Child() >>> isinstance(child, Root) True >>> from zope.interface import providedBy >>> list(providedBy(child)) [] However, that's not the case with ABCs and ABC interfaces. Just because ``isinstance(A(), AnABC)`` and ``isinstance(B(), AnABC)`` are both true, that doesn't mean there's any class hierarchy relationship between ``A`` and ``B``, or between either of them and ``AnABC``. Thus, if ``AnABC`` implemented ``IAnABC``, it would not follow that either ``A`` or ``B`` implements ``IAnABC`` (nor their instances provide it):: >>> class SizedClass(object): ... def __len__(self): return 1 ... >>> from collections.abc import Sized >>> isinstance(SizedClass(), Sized) True >>> from zope.interface import classImplements >>> classImplements(Sized, I1) None >>> list(providedBy(SizedClass())) [] Thus, to avoid conflicting assumptions, ABCs should not be declared to implement their parallel ABC interface. Only concrete classes specifically registered with the ABC should be declared to do so. .. versionadded:: 5.0.0 """ # If we could figure out invalidation, and used some special # Specification/Declaration instances, and override the method ``providedBy`` here, # perhaps we could more closely integrate with ABC virtual inheritance? def __init__(self, name, bases, attrs): # go ahead and give us a name to ease debugging. self.__name__ = name extra_classes = attrs.pop('extra_classes', ()) ignored_classes = attrs.pop('ignored_classes', ()) if 'abc' not in attrs: # Something like ``IList(ISequence)``: We're extending # abc interfaces but not an ABC interface ourself. InterfaceClass.__init__(self, name, bases, attrs) ABCInterfaceClass.__register_classes(self, extra_classes, ignored_classes) self.__class__ = InterfaceClass return based_on = attrs.pop('abc') self.__abc = based_on self.__extra_classes = tuple(extra_classes) self.__ignored_classes = tuple(ignored_classes) assert name[1:] == based_on.__name__, (name, based_on) methods = { # Passing the name is important in case of aliases, # e.g., ``__ror__ = __or__``. k: self.__method_from_function(v, k) for k, v in vars(based_on).items() if isinstance(v, FunctionType) and not self.__is_private_name(k) and not self.__is_reverse_protocol_name(k) } methods['__doc__'] = self.__create_class_doc(attrs) # Anything specified in the body takes precedence. methods.update(attrs) InterfaceClass.__init__(self, name, bases, methods) self.__register_classes() @staticmethod def __optional_methods_to_docs(attrs): optionals = {k: v for k, v in attrs.items() if isinstance(v, optional)} for k in optionals: attrs[k] = _decorator_non_return if not optionals: return '' docs = "\n\nThe following methods are optional:\n - " + "\n-".join( "{}\n{}".format(k, v.__doc__) for k, v in optionals.items() ) return docs def __create_class_doc(self, attrs): based_on = self.__abc def ref(c): mod = c.__module__ name = c.__name__ if mod == str.__module__: return "`%s`" % name if mod == '_io': mod = 'io' return "`{}.{}`".format(mod, name) implementations_doc = "\n - ".join( ref(c) for c in sorted(self.getRegisteredConformers(), key=ref) ) if implementations_doc: implementations_doc = "\n\nKnown implementations are:\n\n - " + implementations_doc based_on_doc = (based_on.__doc__ or '') based_on_doc = based_on_doc.splitlines() based_on_doc = based_on_doc[0] if based_on_doc else '' doc = """Interface for the ABC `{}.{}`.\n\n{}{}{}""".format( based_on.__module__, based_on.__name__, attrs.get('__doc__', based_on_doc), self.__optional_methods_to_docs(attrs), implementations_doc ) return doc @staticmethod def __is_private_name(name): if name.startswith('__') and name.endswith('__'): return False return name.startswith('_') @staticmethod def __is_reverse_protocol_name(name): # The reverse names, like __rand__, # aren't really part of the protocol. The interpreter has # very complex behaviour around invoking those. PyPy # doesn't always even expose them as attributes. return name.startswith('__r') and name.endswith('__') def __method_from_function(self, function, name): method = fromFunction(function, self, name=name) # Eliminate the leading *self*, which is implied in # an interface, but explicit in an ABC. method.positional = method.positional[1:] return method def __register_classes(self, conformers=None, ignored_classes=None): # Make the concrete classes already present in our ABC's registry # declare that they implement this interface. conformers = conformers if conformers is not None else self.getRegisteredConformers() ignored = ignored_classes if ignored_classes is not None else self.__ignored_classes for cls in conformers: if cls in ignored: continue classImplements(cls, self) def getABC(self): """ Return the ABC this interface represents. """ return self.__abc def getRegisteredConformers(self): """ Return an iterable of the classes that are known to conform to the ABC this interface parallels. """ based_on = self.__abc # The registry only contains things that aren't already # known to be subclasses of the ABC. But the ABC is in charge # of checking that, so its quite possible that registrations # are in fact ignored, winding up just in the _abc_cache. try: registered = list(based_on._abc_registry) + list(based_on._abc_cache) except AttributeError: # Rewritten in C in CPython 3.7. # These expose the underlying weakref. from abc import _get_dump data = _get_dump(based_on) registry = data[0] cache = data[1] registered = [x() for x in itertools.chain(registry, cache)] registered = [x for x in registered if x is not None] return set(itertools.chain(registered, self.__extra_classes)) def _create_ABCInterface(): # It's a two-step process to create the root ABCInterface, because # without specifying a corresponding ABC, using the normal constructor # gets us a plain InterfaceClass object, and there is no ABC to associate with the # root. abc_name_bases_attrs = ('ABCInterface', (Interface,), {}) instance = ABCInterfaceClass.__new__(ABCInterfaceClass, *abc_name_bases_attrs) InterfaceClass.__init__(instance, *abc_name_bases_attrs) return instance ABCInterface = _create_ABCInterface() zope.interface-6.4/src/zope/interface/common/builtins.py000066400000000000000000000057231462121350100235000ustar00rootroot00000000000000############################################################################## # Copyright (c) 2020 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. ############################################################################## """ Interface definitions for builtin types. After this module is imported, the standard library types will declare that they implement the appropriate interface. .. versionadded:: 5.0.0 """ from zope.interface import classImplements from zope.interface.common import collections from zope.interface.common import io from zope.interface.common import numbers __all__ = [ 'IList', 'ITuple', 'ITextString', 'IByteString', 'INativeString', 'IBool', 'IDict', 'IFile', ] # pylint:disable=no-self-argument class IList(collections.IMutableSequence): """ Interface for :class:`list` """ extra_classes = (list,) def sort(key=None, reverse=False): """ Sort the list in place and return None. *key* and *reverse* must be passed by name only. """ class ITuple(collections.ISequence): """ Interface for :class:`tuple` """ extra_classes = (tuple,) class ITextString(collections.ISequence): """ Interface for text ("unicode") strings. This is :class:`str` """ extra_classes = (str,) class IByteString(collections.IByteString): """ Interface for immutable byte strings. On all Python versions this is :class:`bytes`. Unlike :class:`zope.interface.common.collections.IByteString` (the parent of this interface) this does *not* include :class:`bytearray`. """ extra_classes = (bytes,) class INativeString(ITextString): """ Interface for native strings. On all Python versions, this is :class:`str`. Tt extends :class:`ITextString`. """ # We're not extending ABCInterface so extra_classes won't work classImplements(str, INativeString) class IBool(numbers.IIntegral): """ Interface for :class:`bool` """ extra_classes = (bool,) class IDict(collections.IMutableMapping): """ Interface for :class:`dict` """ extra_classes = (dict,) class IFile(io.IIOBase): """ Interface for :class:`file`. It is recommended to use the interfaces from :mod:`zope.interface.common.io` instead of this interface. On Python 3, there is no single implementation of this interface; depending on the arguments, the :func:`open` builtin can return many different classes that implement different interfaces from :mod:`zope.interface.common.io`. """ extra_classes = () zope.interface-6.4/src/zope/interface/common/collections.py000066400000000000000000000152101462121350100241550ustar00rootroot00000000000000############################################################################## # Copyright (c) 2020 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. ############################################################################## """ Interface definitions paralleling the abstract base classes defined in :mod:`collections.abc`. After this module is imported, the standard library types will declare that they implement the appropriate interface. While most standard library types will properly implement that interface (that is, ``verifyObject(ISequence, list()))`` will pass, for example), a few might not: - `memoryview` doesn't feature all the defined methods of ``ISequence`` such as ``count``; it is still declared to provide ``ISequence`` though. - `collections.deque.pop` doesn't accept the ``index`` argument of `collections.abc.MutableSequence.pop` - `range.index` does not accept the ``start`` and ``stop`` arguments. .. versionadded:: 5.0.0 """ import sys from abc import ABCMeta from collections import OrderedDict from collections import UserDict from collections import UserList from collections import UserString from collections import abc from zope.interface.common import ABCInterface from zope.interface.common import optional # pylint:disable=inherit-non-class, # pylint:disable=no-self-argument,no-method-argument # pylint:disable=unexpected-special-method-signature # pylint:disable=no-value-for-parameter def _new_in_ver(name, ver, bases_if_missing=(ABCMeta,), register_if_missing=()): if ver: return getattr(abc, name) # TODO: It's a shame to have to repeat the bases when # the ABC is missing. Can we DRY that? missing = ABCMeta(name, bases_if_missing, { '__doc__': "The ABC %s is not defined in this version of Python." % ( name ), }) for c in register_if_missing: missing.register(c) return missing __all__ = [ 'IAsyncGenerator', 'IAsyncIterable', 'IAsyncIterator', 'IAwaitable', 'ICollection', 'IContainer', 'ICoroutine', 'IGenerator', 'IHashable', 'IItemsView', 'IIterable', 'IIterator', 'IKeysView', 'IMapping', 'IMappingView', 'IMutableMapping', 'IMutableSequence', 'IMutableSet', 'IReversible', 'ISequence', 'ISet', 'ISized', 'IValuesView', ] class IContainer(ABCInterface): abc = abc.Container @optional def __contains__(other): """ Optional method. If not provided, the interpreter will use ``__iter__`` or the old ``__getitem__`` protocol to implement ``in``. """ class IHashable(ABCInterface): abc = abc.Hashable class IIterable(ABCInterface): abc = abc.Iterable @optional def __iter__(): """ Optional method. If not provided, the interpreter will implement `iter` using the old ``__getitem__`` protocol. """ class IIterator(IIterable): abc = abc.Iterator class IReversible(IIterable): abc = _new_in_ver('Reversible', True, (IIterable.getABC(),)) @optional def __reversed__(): """ Optional method. If this isn't present, the interpreter will use ``__len__`` and ``__getitem__`` to implement the `reversed` builtin. """ class IGenerator(IIterator): # New in Python 3.5 abc = _new_in_ver('Generator', True, (IIterator.getABC(),)) class ISized(ABCInterface): abc = abc.Sized # ICallable is not defined because there's no standard signature. class ICollection(ISized, IIterable, IContainer): abc = _new_in_ver('Collection', True, (ISized.getABC(), IIterable.getABC(), IContainer.getABC())) class ISequence(IReversible, ICollection): abc = abc.Sequence extra_classes = (UserString,) # On Python 2, basestring is registered as an ISequence, and # its subclass str is an IByteString. If we also register str as # an ISequence, that tends to lead to inconsistent resolution order. ignored_classes = (basestring,) if str is bytes else () # pylint:disable=undefined-variable @optional def __reversed__(): """ Optional method. If this isn't present, the interpreter will use ``__len__`` and ``__getitem__`` to implement the `reversed` builtin. """ @optional def __iter__(): """ Optional method. If not provided, the interpreter will implement `iter` using the old ``__getitem__`` protocol. """ class IMutableSequence(ISequence): abc = abc.MutableSequence extra_classes = (UserList,) class IByteString(ISequence): """ This unifies `bytes` and `bytearray`. """ abc = _new_in_ver('ByteString', True, (ISequence.getABC(),), (bytes, bytearray)) class ISet(ICollection): abc = abc.Set class IMutableSet(ISet): abc = abc.MutableSet class IMapping(ICollection): abc = abc.Mapping extra_classes = (dict,) # OrderedDict is a subclass of dict. On CPython 2, # it winds up registered as a IMutableMapping, which # produces an inconsistent IRO if we also try to register it # here. ignored_classes = (OrderedDict,) class IMutableMapping(IMapping): abc = abc.MutableMapping extra_classes = (dict, UserDict,) ignored_classes = (OrderedDict,) class IMappingView(ISized): abc = abc.MappingView class IItemsView(IMappingView, ISet): abc = abc.ItemsView class IKeysView(IMappingView, ISet): abc = abc.KeysView class IValuesView(IMappingView, ICollection): abc = abc.ValuesView @optional def __contains__(other): """ Optional method. If not provided, the interpreter will use ``__iter__`` or the old ``__len__`` and ``__getitem__`` protocol to implement ``in``. """ class IAwaitable(ABCInterface): abc = _new_in_ver('Awaitable', True) class ICoroutine(IAwaitable): abc = _new_in_ver('Coroutine', True) class IAsyncIterable(ABCInterface): abc = _new_in_ver('AsyncIterable', True) class IAsyncIterator(IAsyncIterable): abc = _new_in_ver('AsyncIterator', True) class IAsyncGenerator(IAsyncIterator): abc = _new_in_ver('AsyncGenerator', True) zope.interface-6.4/src/zope/interface/common/idatetime.py000066400000000000000000000507441462121350100236170ustar00rootroot00000000000000############################################################################## # Copyright (c) 2002 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. ############################################################################## """Datetime interfaces. This module is called idatetime because if it were called datetime the import of the real datetime would fail. """ from datetime import date from datetime import datetime from datetime import time from datetime import timedelta from datetime import tzinfo from zope.interface import Attribute from zope.interface import Interface from zope.interface import classImplements class ITimeDeltaClass(Interface): """This is the timedelta class interface. This is symbolic; this module does **not** make `datetime.timedelta` provide this interface. """ min = Attribute("The most negative timedelta object") max = Attribute("The most positive timedelta object") resolution = Attribute( "The smallest difference between non-equal timedelta objects") class ITimeDelta(ITimeDeltaClass): """Represent the difference between two datetime objects. Implemented by `datetime.timedelta`. Supported operators: - add, subtract timedelta - unary plus, minus, abs - compare to timedelta - multiply, divide by int/long In addition, `.datetime` supports subtraction of two `.datetime` objects returning a `.timedelta`, and addition or subtraction of a `.datetime` and a `.timedelta` giving a `.datetime`. Representation: (days, seconds, microseconds). """ days = Attribute("Days between -999999999 and 999999999 inclusive") seconds = Attribute("Seconds between 0 and 86399 inclusive") microseconds = Attribute("Microseconds between 0 and 999999 inclusive") class IDateClass(Interface): """This is the date class interface. This is symbolic; this module does **not** make `datetime.date` provide this interface. """ min = Attribute("The earliest representable date") max = Attribute("The latest representable date") resolution = Attribute( "The smallest difference between non-equal date objects") def today(): """Return the current local time. This is equivalent to ``date.fromtimestamp(time.time())``""" def fromtimestamp(timestamp): """Return the local date from a POSIX timestamp (like time.time()) This may raise `ValueError`, if the timestamp is out of the range of values supported by the platform C ``localtime()`` function. It's common for this to be restricted to years from 1970 through 2038. Note that on non-POSIX systems that include leap seconds in their notion of a timestamp, leap seconds are ignored by `fromtimestamp`. """ def fromordinal(ordinal): """Return the date corresponding to the proleptic Gregorian ordinal. January 1 of year 1 has ordinal 1. `ValueError` is raised unless 1 <= ordinal <= date.max.toordinal(). For any date *d*, ``date.fromordinal(d.toordinal()) == d``. """ class IDate(IDateClass): """Represents a date (year, month and day) in an idealized calendar. Implemented by `datetime.date`. Operators: __repr__, __str__ __cmp__, __hash__ __add__, __radd__, __sub__ (add/radd only with timedelta arg) """ year = Attribute("Between MINYEAR and MAXYEAR inclusive.") month = Attribute("Between 1 and 12 inclusive") day = Attribute( "Between 1 and the number of days in the given month of the given year.") def replace(year, month, day): """Return a date with the same value. Except for those members given new values by whichever keyword arguments are specified. For example, if ``d == date(2002, 12, 31)``, then ``d.replace(day=26) == date(2000, 12, 26)``. """ def timetuple(): """Return a 9-element tuple of the form returned by `time.localtime`. The hours, minutes and seconds are 0, and the DST flag is -1. ``d.timetuple()`` is equivalent to ``(d.year, d.month, d.day, 0, 0, 0, d.weekday(), d.toordinal() - date(d.year, 1, 1).toordinal() + 1, -1)`` """ def toordinal(): """Return the proleptic Gregorian ordinal of the date January 1 of year 1 has ordinal 1. For any date object *d*, ``date.fromordinal(d.toordinal()) == d``. """ def weekday(): """Return the day of the week as an integer. Monday is 0 and Sunday is 6. For example, ``date(2002, 12, 4).weekday() == 2``, a Wednesday. .. seealso:: `isoweekday`. """ def isoweekday(): """Return the day of the week as an integer. Monday is 1 and Sunday is 7. For example, date(2002, 12, 4).isoweekday() == 3, a Wednesday. .. seealso:: `weekday`, `isocalendar`. """ def isocalendar(): """Return a 3-tuple, (ISO year, ISO week number, ISO weekday). The ISO calendar is a widely used variant of the Gregorian calendar. See http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm for a good explanation. The ISO year consists of 52 or 53 full weeks, and where a week starts on a Monday and ends on a Sunday. The first week of an ISO year is the first (Gregorian) calendar week of a year containing a Thursday. This is called week number 1, and the ISO year of that Thursday is the same as its Gregorian year. For example, 2004 begins on a Thursday, so the first week of ISO year 2004 begins on Monday, 29 Dec 2003 and ends on Sunday, 4 Jan 2004, so that ``date(2003, 12, 29).isocalendar() == (2004, 1, 1)`` and ``date(2004, 1, 4).isocalendar() == (2004, 1, 7)``. """ def isoformat(): """Return a string representing the date in ISO 8601 format. This is 'YYYY-MM-DD'. For example, ``date(2002, 12, 4).isoformat() == '2002-12-04'``. """ def __str__(): """For a date *d*, ``str(d)`` is equivalent to ``d.isoformat()``.""" def ctime(): """Return a string representing the date. For example date(2002, 12, 4).ctime() == 'Wed Dec 4 00:00:00 2002'. d.ctime() is equivalent to time.ctime(time.mktime(d.timetuple())) on platforms where the native C ctime() function (which `time.ctime` invokes, but which date.ctime() does not invoke) conforms to the C standard. """ def strftime(format): """Return a string representing the date. Controlled by an explicit format string. Format codes referring to hours, minutes or seconds will see 0 values. """ class IDateTimeClass(Interface): """This is the datetime class interface. This is symbolic; this module does **not** make `datetime.datetime` provide this interface. """ min = Attribute("The earliest representable datetime") max = Attribute("The latest representable datetime") resolution = Attribute( "The smallest possible difference between non-equal datetime objects") def today(): """Return the current local datetime, with tzinfo None. This is equivalent to ``datetime.fromtimestamp(time.time())``. .. seealso:: `now`, `fromtimestamp`. """ def now(tz=None): """Return the current local date and time. If optional argument *tz* is None or not specified, this is like `today`, but, if possible, supplies more precision than can be gotten from going through a `time.time` timestamp (for example, this may be possible on platforms supplying the C ``gettimeofday()`` function). Else tz must be an instance of a class tzinfo subclass, and the current date and time are converted to tz's time zone. In this case the result is equivalent to tz.fromutc(datetime.utcnow().replace(tzinfo=tz)). .. seealso:: `today`, `utcnow`. """ def utcnow(): """Return the current UTC date and time, with tzinfo None. This is like `now`, but returns the current UTC date and time, as a naive datetime object. .. seealso:: `now`. """ def fromtimestamp(timestamp, tz=None): """Return the local date and time corresponding to the POSIX timestamp. Same as is returned by time.time(). If optional argument tz is None or not specified, the timestamp is converted to the platform's local date and time, and the returned datetime object is naive. Else tz must be an instance of a class tzinfo subclass, and the timestamp is converted to tz's time zone. In this case the result is equivalent to ``tz.fromutc(datetime.utcfromtimestamp(timestamp).replace(tzinfo=tz))``. fromtimestamp() may raise `ValueError`, if the timestamp is out of the range of values supported by the platform C localtime() or gmtime() functions. It's common for this to be restricted to years in 1970 through 2038. Note that on non-POSIX systems that include leap seconds in their notion of a timestamp, leap seconds are ignored by fromtimestamp(), and then it's possible to have two timestamps differing by a second that yield identical datetime objects. .. seealso:: `utcfromtimestamp`. """ def utcfromtimestamp(timestamp): """Return the UTC datetime from the POSIX timestamp with tzinfo None. This may raise `ValueError`, if the timestamp is out of the range of values supported by the platform C ``gmtime()`` function. It's common for this to be restricted to years in 1970 through 2038. .. seealso:: `fromtimestamp`. """ def fromordinal(ordinal): """Return the datetime from the proleptic Gregorian ordinal. January 1 of year 1 has ordinal 1. `ValueError` is raised unless 1 <= ordinal <= datetime.max.toordinal(). The hour, minute, second and microsecond of the result are all 0, and tzinfo is None. """ def combine(date, time): """Return a new datetime object. Its date members are equal to the given date object's, and whose time and tzinfo members are equal to the given time object's. For any datetime object *d*, ``d == datetime.combine(d.date(), d.timetz())``. If date is a datetime object, its time and tzinfo members are ignored. """ class IDateTime(IDate, IDateTimeClass): """Object contains all the information from a date object and a time object. Implemented by `datetime.datetime`. """ year = Attribute("Year between MINYEAR and MAXYEAR inclusive") month = Attribute("Month between 1 and 12 inclusive") day = Attribute( "Day between 1 and the number of days in the given month of the year") hour = Attribute("Hour in range(24)") minute = Attribute("Minute in range(60)") second = Attribute("Second in range(60)") microsecond = Attribute("Microsecond in range(1000000)") tzinfo = Attribute( """The object passed as the tzinfo argument to the datetime constructor or None if none was passed""") def date(): """Return date object with same year, month and day.""" def time(): """Return time object with same hour, minute, second, microsecond. tzinfo is None. .. seealso:: Method :meth:`timetz`. """ def timetz(): """Return time object with same hour, minute, second, microsecond, and tzinfo. .. seealso:: Method :meth:`time`. """ def replace(year, month, day, hour, minute, second, microsecond, tzinfo): """Return a datetime with the same members, except for those members given new values by whichever keyword arguments are specified. Note that ``tzinfo=None`` can be specified to create a naive datetime from an aware datetime with no conversion of date and time members. """ def astimezone(tz): """Return a datetime object with new tzinfo member tz, adjusting the date and time members so the result is the same UTC time as self, but in tz's local time. tz must be an instance of a tzinfo subclass, and its utcoffset() and dst() methods must not return None. self must be aware (self.tzinfo must not be None, and self.utcoffset() must not return None). If self.tzinfo is tz, self.astimezone(tz) is equal to self: no adjustment of date or time members is performed. Else the result is local time in time zone tz, representing the same UTC time as self: after astz = dt.astimezone(tz), astz - astz.utcoffset() will usually have the same date and time members as dt - dt.utcoffset(). The discussion of class `datetime.tzinfo` explains the cases at Daylight Saving Time transition boundaries where this cannot be achieved (an issue only if tz models both standard and daylight time). If you merely want to attach a time zone object *tz* to a datetime *dt* without adjustment of date and time members, use ``dt.replace(tzinfo=tz)``. If you merely want to remove the time zone object from an aware datetime dt without conversion of date and time members, use ``dt.replace(tzinfo=None)``. Note that the default `tzinfo.fromutc` method can be overridden in a tzinfo subclass to effect the result returned by `astimezone`. """ def utcoffset(): """Return the timezone offset in minutes east of UTC (negative west of UTC).""" def dst(): """Return 0 if DST is not in effect, or the DST offset (in minutes eastward) if DST is in effect. """ def tzname(): """Return the timezone name.""" def timetuple(): """Return a 9-element tuple of the form returned by `time.localtime`.""" def utctimetuple(): """Return UTC time tuple compatilble with `time.gmtime`.""" def toordinal(): """Return the proleptic Gregorian ordinal of the date. The same as self.date().toordinal(). """ def weekday(): """Return the day of the week as an integer. Monday is 0 and Sunday is 6. The same as self.date().weekday(). See also isoweekday(). """ def isoweekday(): """Return the day of the week as an integer. Monday is 1 and Sunday is 7. The same as self.date().isoweekday. .. seealso:: `weekday`, `isocalendar`. """ def isocalendar(): """Return a 3-tuple, (ISO year, ISO week number, ISO weekday). The same as self.date().isocalendar(). """ def isoformat(sep='T'): """Return a string representing the date and time in ISO 8601 format. YYYY-MM-DDTHH:MM:SS.mmmmmm or YYYY-MM-DDTHH:MM:SS if microsecond is 0 If `utcoffset` does not return None, a 6-character string is appended, giving the UTC offset in (signed) hours and minutes: YYYY-MM-DDTHH:MM:SS.mmmmmm+HH:MM or YYYY-MM-DDTHH:MM:SS+HH:MM if microsecond is 0. The optional argument sep (default 'T') is a one-character separator, placed between the date and time portions of the result. """ def __str__(): """For a datetime instance *d*, ``str(d)`` is equivalent to ``d.isoformat(' ')``. """ def ctime(): """Return a string representing the date and time. ``datetime(2002, 12, 4, 20, 30, 40).ctime() == 'Wed Dec 4 20:30:40 2002'``. ``d.ctime()`` is equivalent to ``time.ctime(time.mktime(d.timetuple()))`` on platforms where the native C ``ctime()`` function (which `time.ctime` invokes, but which `datetime.ctime` does not invoke) conforms to the C standard. """ def strftime(format): """Return a string representing the date and time. This is controlled by an explicit format string. """ class ITimeClass(Interface): """This is the time class interface. This is symbolic; this module does **not** make `datetime.time` provide this interface. """ min = Attribute("The earliest representable time") max = Attribute("The latest representable time") resolution = Attribute( "The smallest possible difference between non-equal time objects") class ITime(ITimeClass): """Represent time with time zone. Implemented by `datetime.time`. Operators: __repr__, __str__ __cmp__, __hash__ """ hour = Attribute("Hour in range(24)") minute = Attribute("Minute in range(60)") second = Attribute("Second in range(60)") microsecond = Attribute("Microsecond in range(1000000)") tzinfo = Attribute( """The object passed as the tzinfo argument to the time constructor or None if none was passed.""") def replace(hour, minute, second, microsecond, tzinfo): """Return a time with the same value. Except for those members given new values by whichever keyword arguments are specified. Note that tzinfo=None can be specified to create a naive time from an aware time, without conversion of the time members. """ def isoformat(): """Return a string representing the time in ISO 8601 format. That is HH:MM:SS.mmmmmm or, if self.microsecond is 0, HH:MM:SS If utcoffset() does not return None, a 6-character string is appended, giving the UTC offset in (signed) hours and minutes: HH:MM:SS.mmmmmm+HH:MM or, if self.microsecond is 0, HH:MM:SS+HH:MM """ def __str__(): """For a time t, str(t) is equivalent to t.isoformat().""" def strftime(format): """Return a string representing the time. This is controlled by an explicit format string. """ def utcoffset(): """Return the timezone offset in minutes east of UTC (negative west of UTC). If tzinfo is None, returns None, else returns self.tzinfo.utcoffset(None), and raises an exception if the latter doesn't return None or a timedelta object representing a whole number of minutes with magnitude less than one day. """ def dst(): """Return 0 if DST is not in effect, or the DST offset (in minutes eastward) if DST is in effect. If tzinfo is None, returns None, else returns self.tzinfo.dst(None), and raises an exception if the latter doesn't return None, or a timedelta object representing a whole number of minutes with magnitude less than one day. """ def tzname(): """Return the timezone name. If tzinfo is None, returns None, else returns self.tzinfo.tzname(None), or raises an exception if the latter doesn't return None or a string object. """ class ITZInfo(Interface): """Time zone info class. """ def utcoffset(dt): """Return offset of local time from UTC, in minutes east of UTC. If local time is west of UTC, this should be negative. Note that this is intended to be the total offset from UTC; for example, if a tzinfo object represents both time zone and DST adjustments, utcoffset() should return their sum. If the UTC offset isn't known, return None. Else the value returned must be a timedelta object specifying a whole number of minutes in the range -1439 to 1439 inclusive (1440 = 24*60; the magnitude of the offset must be less than one day). """ def dst(dt): """Return the daylight saving time (DST) adjustment, in minutes east of UTC, or None if DST information isn't known. """ def tzname(dt): """Return the time zone name corresponding to the datetime object as a string. """ def fromutc(dt): """Return an equivalent datetime in self's local time.""" classImplements(timedelta, ITimeDelta) classImplements(date, IDate) classImplements(datetime, IDateTime) classImplements(time, ITime) classImplements(tzinfo, ITZInfo) ## directlyProvides(timedelta, ITimeDeltaClass) ## directlyProvides(date, IDateClass) ## directlyProvides(datetime, IDateTimeClass) ## directlyProvides(time, ITimeClass) zope.interface-6.4/src/zope/interface/common/interfaces.py000066400000000000000000000123701462121350100237660ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2003 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. # ############################################################################## """Interfaces for standard python exceptions """ from zope.interface import Interface from zope.interface import classImplements class IException(Interface): "Interface for `Exception`" classImplements(Exception, IException) class IStandardError(IException): "Interface for `StandardError` (no longer existing.)" class IWarning(IException): "Interface for `Warning`" classImplements(Warning, IWarning) class ISyntaxError(IStandardError): "Interface for `SyntaxError`" classImplements(SyntaxError, ISyntaxError) class ILookupError(IStandardError): "Interface for `LookupError`" classImplements(LookupError, ILookupError) class IValueError(IStandardError): "Interface for `ValueError`" classImplements(ValueError, IValueError) class IRuntimeError(IStandardError): "Interface for `RuntimeError`" classImplements(RuntimeError, IRuntimeError) class IArithmeticError(IStandardError): "Interface for `ArithmeticError`" classImplements(ArithmeticError, IArithmeticError) class IAssertionError(IStandardError): "Interface for `AssertionError`" classImplements(AssertionError, IAssertionError) class IAttributeError(IStandardError): "Interface for `AttributeError`" classImplements(AttributeError, IAttributeError) class IDeprecationWarning(IWarning): "Interface for `DeprecationWarning`" classImplements(DeprecationWarning, IDeprecationWarning) class IEOFError(IStandardError): "Interface for `EOFError`" classImplements(EOFError, IEOFError) class IEnvironmentError(IStandardError): "Interface for `EnvironmentError`" classImplements(EnvironmentError, IEnvironmentError) class IFloatingPointError(IArithmeticError): "Interface for `FloatingPointError`" classImplements(FloatingPointError, IFloatingPointError) class IIOError(IEnvironmentError): "Interface for `IOError`" classImplements(IOError, IIOError) class IImportError(IStandardError): "Interface for `ImportError`" classImplements(ImportError, IImportError) class IIndentationError(ISyntaxError): "Interface for `IndentationError`" classImplements(IndentationError, IIndentationError) class IIndexError(ILookupError): "Interface for `IndexError`" classImplements(IndexError, IIndexError) class IKeyError(ILookupError): "Interface for `KeyError`" classImplements(KeyError, IKeyError) class IKeyboardInterrupt(IStandardError): "Interface for `KeyboardInterrupt`" classImplements(KeyboardInterrupt, IKeyboardInterrupt) class IMemoryError(IStandardError): "Interface for `MemoryError`" classImplements(MemoryError, IMemoryError) class INameError(IStandardError): "Interface for `NameError`" classImplements(NameError, INameError) class INotImplementedError(IRuntimeError): "Interface for `NotImplementedError`" classImplements(NotImplementedError, INotImplementedError) class IOSError(IEnvironmentError): "Interface for `OSError`" classImplements(OSError, IOSError) class IOverflowError(IArithmeticError): "Interface for `ArithmeticError`" classImplements(OverflowError, IOverflowError) class IOverflowWarning(IWarning): """Deprecated, no standard class implements this. This was the interface for ``OverflowWarning`` prior to Python 2.5, but that class was removed for all versions after that. """ class IReferenceError(IStandardError): "Interface for `ReferenceError`" classImplements(ReferenceError, IReferenceError) class IRuntimeWarning(IWarning): "Interface for `RuntimeWarning`" classImplements(RuntimeWarning, IRuntimeWarning) class IStopIteration(IException): "Interface for `StopIteration`" classImplements(StopIteration, IStopIteration) class ISyntaxWarning(IWarning): "Interface for `SyntaxWarning`" classImplements(SyntaxWarning, ISyntaxWarning) class ISystemError(IStandardError): "Interface for `SystemError`" classImplements(SystemError, ISystemError) class ISystemExit(IException): "Interface for `SystemExit`" classImplements(SystemExit, ISystemExit) class ITabError(IIndentationError): "Interface for `TabError`" classImplements(TabError, ITabError) class ITypeError(IStandardError): "Interface for `TypeError`" classImplements(TypeError, ITypeError) class IUnboundLocalError(INameError): "Interface for `UnboundLocalError`" classImplements(UnboundLocalError, IUnboundLocalError) class IUnicodeError(IValueError): "Interface for `UnicodeError`" classImplements(UnicodeError, IUnicodeError) class IUserWarning(IWarning): "Interface for `UserWarning`" classImplements(UserWarning, IUserWarning) class IZeroDivisionError(IArithmeticError): "Interface for `ZeroDivisionError`" classImplements(ZeroDivisionError, IZeroDivisionError) zope.interface-6.4/src/zope/interface/common/io.py000066400000000000000000000023321462121350100222470ustar00rootroot00000000000000############################################################################## # Copyright (c) 2020 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. ############################################################################## """ Interface definitions paralleling the abstract base classes defined in :mod:`io`. After this module is imported, the standard library types will declare that they implement the appropriate interface. .. versionadded:: 5.0.0 """ import io as abc from zope.interface.common import ABCInterface # pylint:disable=inherit-non-class, # pylint:disable=no-member class IIOBase(ABCInterface): abc = abc.IOBase class IRawIOBase(IIOBase): abc = abc.RawIOBase class IBufferedIOBase(IIOBase): abc = abc.BufferedIOBase extra_classes = () class ITextIOBase(IIOBase): abc = abc.TextIOBase zope.interface-6.4/src/zope/interface/common/mapping.py000066400000000000000000000111061462121350100232720ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2001, 2002 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. # ############################################################################## """ Mapping Interfaces. Importing this module does *not* mark any standard classes as implementing any of these interfaces. While this module is not deprecated, new code should generally use :mod:`zope.interface.common.collections`, specifically :class:`~zope.interface.common.collections.IMapping` and :class:`~zope.interface.common.collections.IMutableMapping`. This module is occasionally useful for its extremely fine grained breakdown of interfaces. The standard library :class:`dict` and :class:`collections.UserDict` implement ``IMutableMapping``, but *do not* implement any of the interfaces in this module. """ from zope.interface import Interface from zope.interface.common import collections class IItemMapping(Interface): """Simplest readable mapping object """ def __getitem__(key): """Get a value for a key A `KeyError` is raised if there is no value for the key. """ class IReadMapping(collections.IContainer, IItemMapping): """ Basic mapping interface. .. versionchanged:: 5.0.0 Extend ``IContainer`` """ def get(key, default=None): """Get a value for a key The default is returned if there is no value for the key. """ def __contains__(key): """Tell if a key exists in the mapping.""" # Optional in IContainer, required by this interface. class IWriteMapping(Interface): """Mapping methods for changing data""" def __delitem__(key): """Delete a value from the mapping using the key.""" def __setitem__(key, value): """Set a new item in the mapping.""" class IEnumerableMapping(collections.ISized, IReadMapping): """ Mapping objects whose items can be enumerated. .. versionchanged:: 5.0.0 Extend ``ISized`` """ def keys(): """Return the keys of the mapping object. """ def __iter__(): """Return an iterator for the keys of the mapping object. """ def values(): """Return the values of the mapping object. """ def items(): """Return the items of the mapping object. """ class IMapping(IWriteMapping, IEnumerableMapping): ''' Simple mapping interface ''' class IIterableMapping(IEnumerableMapping): """A mapping that has distinct methods for iterating without copying. """ class IClonableMapping(Interface): """Something that can produce a copy of itself. This is available in `dict`. """ def copy(): "return copy of dict" class IExtendedReadMapping(IIterableMapping): """ Something with a particular method equivalent to ``__contains__``. On Python 2, `dict` provided the ``has_key`` method, but it was removed in Python 3. """ class IExtendedWriteMapping(IWriteMapping): """Additional mutation methods. These are all provided by `dict`. """ def clear(): "delete all items" def update(d): " Update D from E: for k in E.keys(): D[k] = E[k]" def setdefault(key, default=None): "D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D" def pop(k, default=None): """ pop(k[,default]) -> value Remove specified key and return the corresponding value. If key is not found, *default* is returned if given, otherwise `KeyError` is raised. Note that *default* must not be passed by name. """ def popitem(): """remove and return some (key, value) pair as a 2-tuple; but raise KeyError if mapping is empty""" class IFullMapping( collections.IMutableMapping, IExtendedReadMapping, IExtendedWriteMapping, IClonableMapping, IMapping,): """ Full mapping interface. Most uses of this interface should instead use :class:`~zope.interface.commons.collections.IMutableMapping` (one of the bases of this interface). The required methods are the same. .. versionchanged:: 5.0.0 Extend ``IMutableMapping`` """ zope.interface-6.4/src/zope/interface/common/numbers.py000066400000000000000000000032221462121350100233120ustar00rootroot00000000000000############################################################################## # Copyright (c) 2020 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. ############################################################################## """ Interface definitions paralleling the abstract base classes defined in :mod:`numbers`. After this module is imported, the standard library types will declare that they implement the appropriate interface. .. versionadded:: 5.0.0 """ import numbers as abc from zope.interface.common import ABCInterface from zope.interface.common import optional # pylint:disable=inherit-non-class, # pylint:disable=no-self-argument,no-method-argument # pylint:disable=unexpected-special-method-signature # pylint:disable=no-value-for-parameter class INumber(ABCInterface): abc = abc.Number class IComplex(INumber): abc = abc.Complex @optional def __complex__(): """ Rarely implemented, even in builtin types. """ class IReal(IComplex): abc = abc.Real @optional def __complex__(): """ Rarely implemented, even in builtin types. """ __floor__ = __ceil__ = __complex__ class IRational(IReal): abc = abc.Rational class IIntegral(IRational): abc = abc.Integral zope.interface-6.4/src/zope/interface/common/sequence.py000066400000000000000000000126261462121350100234570ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2001, 2002 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. # ############################################################################## """ Sequence Interfaces Importing this module does *not* mark any standard classes as implementing any of these interfaces. While this module is not deprecated, new code should generally use :mod:`zope.interface.common.collections`, specifically :class:`~zope.interface.common.collections.ISequence` and :class:`~zope.interface.common.collections.IMutableSequence`. This module is occasionally useful for its fine-grained breakdown of interfaces. The standard library :class:`list`, :class:`tuple` and :class:`collections.UserList`, among others, implement ``ISequence`` or ``IMutableSequence`` but *do not* implement any of the interfaces in this module. """ __docformat__ = 'restructuredtext' from zope.interface import Interface from zope.interface.common import collections class IMinimalSequence(collections.IIterable): """Most basic sequence interface. All sequences are iterable. This requires at least one of the following: - a `__getitem__()` method that takes a single argument; integer values starting at 0 must be supported, and `IndexError` should be raised for the first index for which there is no value, or - an `__iter__()` method that returns an iterator as defined in the Python documentation (http://docs.python.org/lib/typeiter.html). """ def __getitem__(index): """``x.__getitem__(index) <==> x[index]`` Declaring this interface does not specify whether `__getitem__` supports slice objects.""" class IFiniteSequence(collections.ISized, IMinimalSequence): """ A sequence of bound size. .. versionchanged:: 5.0.0 Extend ``ISized`` """ class IReadSequence(collections.IContainer, IFiniteSequence): """ read interface shared by tuple and list This interface is similar to :class:`~zope.interface.common.collections.ISequence`, but requires that all instances be totally ordered. Most users should prefer ``ISequence``. .. versionchanged:: 5.0.0 Extend ``IContainer`` """ def __contains__(item): """``x.__contains__(item) <==> item in x``""" # Optional in IContainer, required here. def __lt__(other): """``x.__lt__(other) <==> x < other``""" def __le__(other): """``x.__le__(other) <==> x <= other``""" def __eq__(other): """``x.__eq__(other) <==> x == other``""" def __ne__(other): """``x.__ne__(other) <==> x != other``""" def __gt__(other): """``x.__gt__(other) <==> x > other``""" def __ge__(other): """``x.__ge__(other) <==> x >= other``""" def __add__(other): """``x.__add__(other) <==> x + other``""" def __mul__(n): """``x.__mul__(n) <==> x * n``""" def __rmul__(n): """``x.__rmul__(n) <==> n * x``""" class IExtendedReadSequence(IReadSequence): """Full read interface for lists""" def count(item): """Return number of occurrences of value""" def index(item, *args): """index(value, [start, [stop]]) -> int Return first index of *value* """ class IUniqueMemberWriteSequence(Interface): """The write contract for a sequence that may enforce unique members""" def __setitem__(index, item): """``x.__setitem__(index, item) <==> x[index] = item`` Declaring this interface does not specify whether `__setitem__` supports slice objects. """ def __delitem__(index): """``x.__delitem__(index) <==> del x[index]`` Declaring this interface does not specify whether `__delitem__` supports slice objects. """ def __iadd__(y): """``x.__iadd__(y) <==> x += y``""" def append(item): """Append item to end""" def insert(index, item): """Insert item before index""" def pop(index=-1): """Remove and return item at index (default last)""" def remove(item): """Remove first occurrence of value""" def reverse(): """Reverse *IN PLACE*""" def sort(cmpfunc=None): """Stable sort *IN PLACE*; `cmpfunc(x, y)` -> -1, 0, 1""" def extend(iterable): """Extend list by appending elements from the iterable""" class IWriteSequence(IUniqueMemberWriteSequence): """Full write contract for sequences""" def __imul__(n): """``x.__imul__(n) <==> x *= n``""" class ISequence(IReadSequence, IWriteSequence): """ Full sequence contract. New code should prefer :class:`~zope.interface.common.collections.IMutableSequence`. Compared to that interface, which is implemented by :class:`list` (:class:`~zope.interface.common.builtins.IList`), among others, this interface is missing the following methods: - clear - count - index This interface adds the following methods: - sort """ zope.interface-6.4/src/zope/interface/common/tests/000077500000000000000000000000001462121350100224305ustar00rootroot00000000000000zope.interface-6.4/src/zope/interface/common/tests/__init__.py000066400000000000000000000125441462121350100245470ustar00rootroot00000000000000############################################################################## # Copyright (c) 2020 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. ############################################################################## import unittest from zope.interface.common import ABCInterface from zope.interface.common import ABCInterfaceClass from zope.interface.verify import verifyClass from zope.interface.verify import verifyObject def iter_abc_interfaces(predicate=lambda iface: True): # Iterate ``(iface, classes)``, where ``iface`` is a descendent of # the ABCInterfaceClass passing the *predicate* and ``classes`` is # an iterable of classes registered to conform to that interface. # # Note that some builtin classes are registered for two distinct # parts of the ABC/interface tree. For example, bytearray is both ByteString # and MutableSequence. seen = set() stack = list(ABCInterface.dependents) # subclasses, but also implementedBy objects while stack: iface = stack.pop(0) if iface in seen or not isinstance(iface, ABCInterfaceClass): continue seen.add(iface) stack.extend(list(iface.dependents)) if not predicate(iface): continue registered = set(iface.getRegisteredConformers()) registered -= set(iface._ABCInterfaceClass__ignored_classes) if registered: yield iface, registered def add_abc_interface_tests(cls, module): def predicate(iface): return iface.__module__ == module add_verify_tests(cls, iter_abc_interfaces(predicate)) def add_verify_tests(cls, iface_classes_iter): cls.maxDiff = None for iface, registered_classes in iface_classes_iter: for stdlib_class in registered_classes: def test(self, stdlib_class=stdlib_class, iface=iface): if stdlib_class in self.UNVERIFIABLE or stdlib_class.__name__ in self.UNVERIFIABLE: self.skipTest("Unable to verify %s" % stdlib_class) self.assertTrue(self.verify(iface, stdlib_class)) suffix = "{}_{}_{}_{}".format( stdlib_class.__module__.replace('.', '_'), stdlib_class.__name__, iface.__module__.replace('.', '_'), iface.__name__ ) name = 'test_auto_' + suffix test.__name__ = name assert not hasattr(cls, name), (name, list(cls.__dict__)) setattr(cls, name, test) def test_ro(self, stdlib_class=stdlib_class, iface=iface): from zope.interface import Interface from zope.interface import implementedBy from zope.interface import ro self.assertEqual( tuple(ro.ro(iface, strict=True)), iface.__sro__) implements = implementedBy(stdlib_class) sro = implements.__sro__ self.assertIs(sro[-1], Interface) if stdlib_class not in self.UNVERIFIABLE_RO: # Check that we got the strict C3 resolution order, unless # we know we cannot. Note that 'Interface' is virtual base # that doesn't necessarily appear at the same place in the # calculated SRO as in the final SRO. strict = stdlib_class not in self.NON_STRICT_RO isro = ro.ro(implements, strict=strict) isro.remove(Interface) isro.append(Interface) self.assertEqual(tuple(isro), sro) name = 'test_auto_ro_' + suffix test_ro.__name__ = name assert not hasattr(cls, name) setattr(cls, name, test_ro) class VerifyClassMixin(unittest.TestCase): verifier = staticmethod(verifyClass) UNVERIFIABLE = () NON_STRICT_RO = () UNVERIFIABLE_RO = () def _adjust_object_before_verify(self, iface, x): return x def verify(self, iface, klass, **kwargs): return self.verifier(iface, self._adjust_object_before_verify(iface, klass), **kwargs) class VerifyObjectMixin(VerifyClassMixin): verifier = staticmethod(verifyObject) CONSTRUCTORS = { } def _adjust_object_before_verify(self, iface, x): constructor = self.CONSTRUCTORS.get(x) if not constructor: constructor = self.CONSTRUCTORS.get(iface) if not constructor: constructor = self.CONSTRUCTORS.get(x.__name__) if not constructor: constructor = x if constructor is unittest.SkipTest: self.skipTest("Cannot create " + str(x)) try: result = constructor() except Exception as e: # pragma: no cover raise TypeError( f'Failed to create instance of {constructor}') from e if hasattr(result, 'close'): self.addCleanup(result.close) return result zope.interface-6.4/src/zope/interface/common/tests/basemapping.py000066400000000000000000000075031462121350100252750ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2001, 2002 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. # ############################################################################## """Base Mapping tests """ from operator import __getitem__ def testIReadMapping(self, inst, state, absent): for key in state: self.assertEqual(inst[key], state[key]) self.assertEqual(inst.get(key, None), state[key]) self.assertTrue(key in inst) for key in absent: self.assertEqual(inst.get(key, None), None) self.assertEqual(inst.get(key), None) self.assertEqual(inst.get(key, self), self) self.assertRaises(KeyError, __getitem__, inst, key) def test_keys(self, inst, state): # Return the keys of the mapping object inst_keys = list(inst.keys()); inst_keys.sort() state_keys = list(state.keys()) ; state_keys.sort() self.assertEqual(inst_keys, state_keys) def test_iter(self, inst, state): # Return the keys of the mapping object inst_keys = list(inst); inst_keys.sort() state_keys = list(state.keys()) ; state_keys.sort() self.assertEqual(inst_keys, state_keys) def test_values(self, inst, state): # Return the values of the mapping object inst_values = list(inst.values()); inst_values.sort() state_values = list(state.values()) ; state_values.sort() self.assertEqual(inst_values, state_values) def test_items(self, inst, state): # Return the items of the mapping object inst_items = list(inst.items()); inst_items.sort() state_items = list(state.items()) ; state_items.sort() self.assertEqual(inst_items, state_items) def test___len__(self, inst, state): # Return the number of items self.assertEqual(len(inst), len(state)) def testIEnumerableMapping(self, inst, state): test_keys(self, inst, state) test_items(self, inst, state) test_values(self, inst, state) test___len__(self, inst, state) class BaseTestIReadMapping: def testIReadMapping(self): inst = self._IReadMapping__sample() state = self._IReadMapping__stateDict() absent = self._IReadMapping__absentKeys() testIReadMapping(self, inst, state, absent) class BaseTestIEnumerableMapping(BaseTestIReadMapping): # Mapping objects whose items can be enumerated def test_keys(self): # Return the keys of the mapping object inst = self._IEnumerableMapping__sample() state = self._IEnumerableMapping__stateDict() test_keys(self, inst, state) def test_values(self): # Return the values of the mapping object inst = self._IEnumerableMapping__sample() state = self._IEnumerableMapping__stateDict() test_values(self, inst, state) def test_items(self): # Return the items of the mapping object inst = self._IEnumerableMapping__sample() state = self._IEnumerableMapping__stateDict() test_items(self, inst, state) def test___len__(self): # Return the number of items inst = self._IEnumerableMapping__sample() state = self._IEnumerableMapping__stateDict() test___len__(self, inst, state) def _IReadMapping__stateDict(self): return self._IEnumerableMapping__stateDict() def _IReadMapping__sample(self): return self._IEnumerableMapping__sample() def _IReadMapping__absentKeys(self): return self._IEnumerableMapping__absentKeys() zope.interface-6.4/src/zope/interface/common/tests/test_builtins.py000066400000000000000000000025011462121350100256700ustar00rootroot00000000000000############################################################################## # Copyright (c) 2020 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. ############################################################################## import unittest from zope.interface.common import builtins from . import VerifyClassMixin from . import VerifyObjectMixin from . import add_verify_tests class TestVerifyClass(VerifyClassMixin, unittest.TestCase): pass add_verify_tests(TestVerifyClass, ( (builtins.IList, (list,)), (builtins.ITuple, (tuple,)), (builtins.ITextString, (str,)), (builtins.IByteString, (bytes,)), (builtins.INativeString, (str,)), (builtins.IBool, (bool,)), (builtins.IDict, (dict,)), (builtins.IFile, ()), )) class TestVerifyObject(VerifyObjectMixin, TestVerifyClass): CONSTRUCTORS = { builtins.IFile: lambda: open(__file__) } zope.interface-6.4/src/zope/interface/common/tests/test_collections.py000066400000000000000000000131261462121350100263620ustar00rootroot00000000000000############################################################################## # Copyright (c) 2020 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. ############################################################################## import array import unittest from collections import OrderedDict from collections import abc from collections import deque from types import MappingProxyType from zope.interface import Invalid from zope.interface._compat import PYPY # Note that importing z.i.c.collections does work on import. from zope.interface.common import collections from . import VerifyClassMixin from . import VerifyObjectMixin from . import add_abc_interface_tests class TestVerifyClass(VerifyClassMixin, unittest.TestCase): # Here we test some known builtin classes that are defined to implement # various collection interfaces as a quick sanity test. def test_frozenset(self): self.assertIsInstance(frozenset(), abc.Set) self.assertTrue(self.verify(collections.ISet, frozenset)) def test_list(self): self.assertIsInstance(list(), abc.MutableSequence) self.assertTrue(self.verify(collections.IMutableSequence, list)) # Here we test some derived classes. def test_UserList(self): self.assertTrue(self.verify(collections.IMutableSequence, collections.UserList)) def test_UserDict(self): self.assertTrue(self.verify(collections.IMutableMapping, collections.UserDict)) def test_UserString(self): self.assertTrue(self.verify(collections.ISequence, collections.UserString)) # Now we go through the registry, which should have several things, # mostly builtins, but if we've imported other libraries already, # it could contain things from outside of there too. We aren't concerned # about third-party code here, just standard library types. We start with a # blacklist of things to exclude, but if that gets out of hand we can figure # out a better whitelisting. UNVERIFIABLE = { # This is declared to be an ISequence, but is missing lots of methods, # including some that aren't part of a language protocol, such as # ``index`` and ``count``. memoryview, # 'pkg_resources._vendor.pyparsing.ParseResults' is registered as a # MutableMapping but is missing methods like ``popitem`` and ``setdefault``. # It's imported due to namespace packages. 'ParseResults', # sqlite3.Row claims ISequence but also misses ``index`` and ``count``. # It's imported because...? Coverage imports it, but why do we have it without # coverage? 'Row', # In Python 3.10 ``array.array`` appears as ``IMutableSequence`` but it # does not provide a ``clear()`` method and it cannot be instantiated # using ``array.array()``. array.array, } if PYPY: UNVERIFIABLE.update({ # collections.deque.pop() doesn't support the index= argument to # MutableSequence.pop(). We can't verify this on CPython because we can't # get the signature, but on PyPy we /can/ get the signature, and of course # it doesn't match. deque, # Likewise for index range, }) UNVERIFIABLE_RO = { # ``array.array`` fails the ``test_auto_ro_*`` tests with and # without strict RO but only on Windows (AppVeyor) on Python 3.10.0 # (in older versions ``array.array`` does not appear as # ``IMutableSequence``). array.array, } add_abc_interface_tests(TestVerifyClass, collections.ISet.__module__) class TestVerifyObject(VerifyObjectMixin, TestVerifyClass): CONSTRUCTORS = { collections.IValuesView: {}.values, collections.IItemsView: {}.items, collections.IKeysView: {}.keys, memoryview: lambda: memoryview(b'abc'), range: lambda: range(10), MappingProxyType: lambda: MappingProxyType({}), collections.UserString: lambda: collections.UserString('abc'), type(iter(bytearray())): lambda: iter(bytearray()), type(iter(b'abc')): lambda: iter(b'abc'), 'coroutine': unittest.SkipTest, type(iter({}.keys())): lambda: iter({}.keys()), type(iter({}.items())): lambda: iter({}.items()), type(iter({}.values())): lambda: iter({}.values()), type(i for i in range(1)): lambda: (i for i in range(3)), type(iter([])): lambda: iter([]), type(reversed([])): lambda: reversed([]), 'longrange_iterator': unittest.SkipTest, 'range_iterator': lambda: iter(range(3)), 'rangeiterator': lambda: iter(range(3)), type(iter(set())): lambda: iter(set()), type(iter('')): lambda: iter(''), 'async_generator': unittest.SkipTest, type(iter(tuple())): lambda: iter(tuple()), } UNVERIFIABLE_RO = { # ``array.array`` fails the ``test_auto_ro_*`` tests with and # without strict RO but only on Windows (AppVeyor) on Python 3.10.0 # (in older versions ``array.array`` does not appear as # ``IMutableSequence``). array.array, } zope.interface-6.4/src/zope/interface/common/tests/test_idatetime.py000066400000000000000000000036031462121350100260100ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2003 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. # ############################################################################## """Test for datetime interfaces """ import unittest from datetime import date from datetime import datetime from datetime import time from datetime import timedelta from datetime import tzinfo from zope.interface.common.idatetime import IDate from zope.interface.common.idatetime import IDateClass from zope.interface.common.idatetime import IDateTime from zope.interface.common.idatetime import IDateTimeClass from zope.interface.common.idatetime import ITime from zope.interface.common.idatetime import ITimeClass from zope.interface.common.idatetime import ITimeDelta from zope.interface.common.idatetime import ITimeDeltaClass from zope.interface.common.idatetime import ITZInfo from zope.interface.verify import verifyClass from zope.interface.verify import verifyObject class TestDateTimeInterfaces(unittest.TestCase): def test_interfaces(self): verifyObject(ITimeDelta, timedelta(minutes=20)) verifyObject(IDate, date(2000, 1, 2)) verifyObject(IDateTime, datetime(2000, 1, 2, 10, 20)) verifyObject(ITime, time(20, 30, 15, 1234)) verifyObject(ITZInfo, tzinfo()) verifyClass(ITimeDeltaClass, timedelta) verifyClass(IDateClass, date) verifyClass(IDateTimeClass, datetime) verifyClass(ITimeClass, time) zope.interface-6.4/src/zope/interface/common/tests/test_import_interfaces.py000066400000000000000000000014551462121350100275630ustar00rootroot00000000000000############################################################################## # # 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. # ############################################################################## import unittest class TestInterfaceImport(unittest.TestCase): def test_import(self): import zope.interface.common.interfaces as x self.assertIsNotNone(x) zope.interface-6.4/src/zope/interface/common/tests/test_io.py000066400000000000000000000031771462121350100244600ustar00rootroot00000000000000############################################################################## # Copyright (c) 2020 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. ############################################################################## import io as abc import unittest # Note that importing z.i.c.io does work on import. from zope.interface.common import io from . import VerifyClassMixin from . import VerifyObjectMixin from . import add_abc_interface_tests class TestVerifyClass(VerifyClassMixin, unittest.TestCase): pass add_abc_interface_tests(TestVerifyClass, io.IIOBase.__module__) class TestVerifyObject(VerifyObjectMixin, TestVerifyClass): CONSTRUCTORS = { abc.BufferedWriter: lambda: abc.BufferedWriter(abc.StringIO()), abc.BufferedReader: lambda: abc.BufferedReader(abc.StringIO()), abc.TextIOWrapper: lambda: abc.TextIOWrapper(abc.BytesIO()), abc.BufferedRandom: lambda: abc.BufferedRandom(abc.BytesIO()), abc.BufferedRWPair: lambda: abc.BufferedRWPair(abc.BytesIO(), abc.BytesIO()), abc.FileIO: lambda: abc.FileIO(__file__), '_WindowsConsoleIO': unittest.SkipTest, 'WinConsoleIO': unittest.SkipTest, # breaks on PyPy-3.10 } zope.interface-6.4/src/zope/interface/common/tests/test_numbers.py000066400000000000000000000025621462121350100255210ustar00rootroot00000000000000############################################################################## # Copyright (c) 2020 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. ############################################################################## import numbers as abc import unittest # Note that importing z.i.c.numbers does work on import. from zope.interface.common import numbers from . import VerifyClassMixin from . import VerifyObjectMixin from . import add_abc_interface_tests class TestVerifyClass(VerifyClassMixin, unittest.TestCase): def test_int(self): self.assertIsInstance(int(), abc.Integral) self.assertTrue(self.verify(numbers.IIntegral, int)) def test_float(self): self.assertIsInstance(float(), abc.Real) self.assertTrue(self.verify(numbers.IReal, float)) add_abc_interface_tests(TestVerifyClass, numbers.INumber.__module__) class TestVerifyObject(VerifyObjectMixin, TestVerifyClass): pass zope.interface-6.4/src/zope/interface/declarations.py000066400000000000000000001237771462121350100230410ustar00rootroot00000000000000############################################################################## # Copyright (c) 2003 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. ############################################################################## """Implementation of interface declarations There are three flavors of declarations: - Declarations are used to simply name declared interfaces. - ImplementsDeclarations are used to express the interfaces that a class implements (that instances of the class provides). Implements specifications support inheriting interfaces. - ProvidesDeclarations are used to express interfaces directly provided by objects. """ __docformat__ = 'restructuredtext' import sys import weakref from types import FunctionType from types import MethodType from types import ModuleType from zope.interface._compat import _use_c_impl from zope.interface.interface import Interface from zope.interface.interface import InterfaceClass from zope.interface.interface import NameAndModuleComparisonMixin from zope.interface.interface import Specification from zope.interface.interface import SpecificationBase __all__ = [ # None. The public APIs of this module are # re-exported from zope.interface directly. ] # pylint:disable=too-many-lines # Registry of class-implementation specifications BuiltinImplementationSpecifications = {} def _next_super_class(ob): # When ``ob`` is an instance of ``super``, return # the next class in the MRO that we should actually be # looking at. Watch out for diamond inheritance! self_class = ob.__self_class__ class_that_invoked_super = ob.__thisclass__ complete_mro = self_class.__mro__ next_class = complete_mro[complete_mro.index(class_that_invoked_super) + 1] return next_class class named: def __init__(self, name): self.name = name def __call__(self, ob): ob.__component_name__ = self.name return ob class Declaration(Specification): """Interface declarations""" __slots__ = () def __init__(self, *bases): Specification.__init__(self, _normalizeargs(bases)) def __contains__(self, interface): """Test whether an interface is in the specification """ return self.extends(interface) and interface in self.interfaces() def __iter__(self): """Return an iterator for the interfaces in the specification """ return self.interfaces() def flattened(self): """Return an iterator of all included and extended interfaces """ return iter(self.__iro__) def __sub__(self, other): """Remove interfaces from a specification """ return Declaration(*[ i for i in self.interfaces() if not [ j for j in other.interfaces() if i.extends(j, 0) # non-strict extends ] ]) def __add__(self, other): """ Add two specifications or a specification and an interface and produce a new declaration. .. versionchanged:: 5.4.0 Now tries to preserve a consistent resolution order. Interfaces being added to this object are added to the front of the resulting resolution order if they already extend an interface in this object. Previously, they were always added to the end of the order, which easily resulted in invalid orders. """ before = [] result = list(self.interfaces()) seen = set(result) for i in other.interfaces(): if i in seen: continue seen.add(i) if any(i.extends(x) for x in result): # It already extends us, e.g., is a subclass, # so it needs to go at the front of the RO. before.append(i) else: result.append(i) return Declaration(*(before + result)) # XXX: Is __radd__ needed? No tests break if it's removed. # If it is needed, does it need to handle the C3 ordering differently? # I (JAM) don't *think* it does. __radd__ = __add__ @staticmethod def _add_interfaces_to_cls(interfaces, cls): # Strip redundant interfaces already provided # by the cls so we don't produce invalid # resolution orders. implemented_by_cls = implementedBy(cls) interfaces = tuple([ iface for iface in interfaces if not implemented_by_cls.isOrExtends(iface) ]) return interfaces + (implemented_by_cls,) @staticmethod def _argument_names_for_repr(interfaces): # These don't actually have to be interfaces, they could be other # Specification objects like Implements. Also, the first # one is typically/nominally the cls. ordered_names = [] names = set() for iface in interfaces: duplicate_transform = repr if isinstance(iface, InterfaceClass): # Special case to get 'foo.bar.IFace' # instead of '' this_name = iface.__name__ duplicate_transform = str elif isinstance(iface, type): # Likewise for types. (Ignoring legacy old-style # classes.) this_name = iface.__name__ duplicate_transform = _implements_name elif (isinstance(iface, Implements) and not iface.declared and iface.inherit in interfaces): # If nothing is declared, there's no need to even print this; # it would just show as ``classImplements(Class)``, and the # ``Class`` has typically already. continue else: this_name = repr(iface) already_seen = this_name in names names.add(this_name) if already_seen: this_name = duplicate_transform(iface) ordered_names.append(this_name) return ', '.join(ordered_names) class _ImmutableDeclaration(Declaration): # A Declaration that is immutable. Used as a singleton to # return empty answers for things like ``implementedBy``. # We have to define the actual singleton after normalizeargs # is defined, and that in turn is defined after InterfaceClass and # Implements. __slots__ = () __instance = None def __new__(cls): if _ImmutableDeclaration.__instance is None: _ImmutableDeclaration.__instance = object.__new__(cls) return _ImmutableDeclaration.__instance def __reduce__(self): return "_empty" @property def __bases__(self): return () @__bases__.setter def __bases__(self, new_bases): # We expect the superclass constructor to set ``self.__bases__ = ()``. # Rather than attempt to special case that in the constructor and allow # setting __bases__ only at that time, it's easier to just allow setting # the empty tuple at any time. That makes ``x.__bases__ = x.__bases__`` a nice # no-op too. (Skipping the superclass constructor altogether is a recipe # for maintenance headaches.) if new_bases != (): raise TypeError("Cannot set non-empty bases on shared empty Declaration.") # As the immutable empty declaration, we cannot be changed. # This means there's no logical reason for us to have dependents # or subscriptions: we'll never notify them. So there's no need for # us to keep track of any of that. @property def dependents(self): return {} changed = subscribe = unsubscribe = lambda self, _ignored: None def interfaces(self): # An empty iterator return iter(()) def extends(self, interface, strict=True): return interface is self._ROOT def get(self, name, default=None): return default def weakref(self, callback=None): # We're a singleton, we never go away. So there's no need to return # distinct weakref objects here; their callbacks will never # be called. Instead, we only need to return a callable that # returns ourself. The easiest one is to return _ImmutableDeclaration # itself; testing on Python 3.8 shows that's faster than a function that # returns _empty. (Remember, one goal is to avoid allocating any # object, and that includes a method.) return _ImmutableDeclaration @property def _v_attrs(self): # _v_attrs is not a public, documented property, but some client code # uses it anyway as a convenient place to cache things. To keep the # empty declaration truly immutable, we must ignore that. That includes # ignoring assignments as well. return {} @_v_attrs.setter def _v_attrs(self, new_attrs): pass ############################################################################## # # Implementation specifications # # These specify interfaces implemented by instances of classes class Implements(NameAndModuleComparisonMixin, Declaration): # Inherit from NameAndModuleComparisonMixin to be # mutually comparable with InterfaceClass objects. # (The two must be mutually comparable to be able to work in e.g., BTrees.) # Instances of this class generally don't have a __module__ other than # `zope.interface.declarations`, whereas they *do* have a __name__ that is the # fully qualified name of the object they are representing. # Note, though, that equality and hashing are still identity based. This # accounts for things like nested objects that have the same name (typically # only in tests) and is consistent with pickling. As far as comparisons to InterfaceClass # goes, we'll never have equal name and module to those, so we're still consistent there. # Instances of this class are essentially intended to be unique and are # heavily cached (note how our __reduce__ handles this) so having identity # based hash and eq should also work. # We want equality and hashing to be based on identity. However, we can't actually # implement __eq__/__ne__ to do this because sometimes we get wrapped in a proxy. # We need to let the proxy types implement these methods so they can handle unwrapping # and then rely on: (1) the interpreter automatically changing `implements == proxy` into # `proxy == implements` (which will call proxy.__eq__ to do the unwrapping) and then # (2) the default equality and hashing semantics being identity based. # class whose specification should be used as additional base inherit = None # interfaces actually declared for a class declared = () # Weak cache of {class: } for super objects. # Created on demand. These are rare, as of 5.0 anyway. Using a class # level default doesn't take space in instances. Using _v_attrs would be # another place to store this without taking space unless needed. _super_cache = None __name__ = '?' @classmethod def named(cls, name, *bases): # Implementation method: Produce an Implements interface with # a fully fleshed out __name__ before calling the constructor, which # sets bases to the given interfaces and which may pass this object to # other objects (e.g., to adjust dependents). If they're sorting or comparing # by name, this needs to be set. inst = cls.__new__(cls) inst.__name__ = name inst.__init__(*bases) return inst def changed(self, originally_changed): try: del self._super_cache except AttributeError: pass return super().changed(originally_changed) def __repr__(self): if self.inherit: name = getattr(self.inherit, '__name__', None) or _implements_name(self.inherit) else: name = self.__name__ declared_names = self._argument_names_for_repr(self.declared) if declared_names: declared_names = ', ' + declared_names return 'classImplements({}{})'.format(name, declared_names) def __reduce__(self): return implementedBy, (self.inherit, ) def _implements_name(ob): # Return the __name__ attribute to be used by its __implemented__ # property. # This must be stable for the "same" object across processes # because it is used for sorting. It needn't be unique, though, in cases # like nested classes named Foo created by different functions, because # equality and hashing is still based on identity. # It might be nice to use __qualname__ on Python 3, but that would produce # different values between Py2 and Py3. return (getattr(ob, '__module__', '?') or '?') + \ '.' + (getattr(ob, '__name__', '?') or '?') def _implementedBy_super(sup): # TODO: This is now simple enough we could probably implement # in C if needed. # If the class MRO is strictly linear, we could just # follow the normal algorithm for the next class in the # search order (e.g., just return # ``implemented_by_next``). But when diamond inheritance # or mixins + interface declarations are present, we have # to consider the whole MRO and compute a new Implements # that excludes the classes being skipped over but # includes everything else. implemented_by_self = implementedBy(sup.__self_class__) cache = implemented_by_self._super_cache # pylint:disable=protected-access if cache is None: cache = implemented_by_self._super_cache = weakref.WeakKeyDictionary() key = sup.__thisclass__ try: return cache[key] except KeyError: pass next_cls = _next_super_class(sup) # For ``implementedBy(cls)``: # .__bases__ is .declared + [implementedBy(b) for b in cls.__bases__] # .inherit is cls implemented_by_next = implementedBy(next_cls) mro = sup.__self_class__.__mro__ ix_next_cls = mro.index(next_cls) classes_to_keep = mro[ix_next_cls:] new_bases = [implementedBy(c) for c in classes_to_keep] new = Implements.named( implemented_by_self.__name__ + ':' + implemented_by_next.__name__, *new_bases ) new.inherit = implemented_by_next.inherit new.declared = implemented_by_next.declared # I don't *think* that new needs to subscribe to ``implemented_by_self``; # it auto-subscribed to its bases, and that should be good enough. cache[key] = new return new @_use_c_impl def implementedBy(cls): # pylint:disable=too-many-return-statements,too-many-branches """Return the interfaces implemented for a class' instances The value returned is an `~zope.interface.interfaces.IDeclaration`. """ try: if isinstance(cls, super): # Yes, this needs to be inside the try: block. Some objects # like security proxies even break isinstance. return _implementedBy_super(cls) spec = cls.__dict__.get('__implemented__') except AttributeError: # we can't get the class dict. This is probably due to a # security proxy. If this is the case, then probably no # descriptor was installed for the class. # We don't want to depend directly on zope.security in # zope.interface, but we'll try to make reasonable # accommodations in an indirect way. # We'll check to see if there's an implements: spec = getattr(cls, '__implemented__', None) if spec is None: # There's no spec stred in the class. Maybe its a builtin: spec = BuiltinImplementationSpecifications.get(cls) if spec is not None: return spec return _empty if spec.__class__ == Implements: # we defaulted to _empty or there was a spec. Good enough. # Return it. return spec # TODO: need old style __implements__ compatibility? # Hm, there's an __implemented__, but it's not a spec. Must be # an old-style declaration. Just compute a spec for it return Declaration(*_normalizeargs((spec, ))) if isinstance(spec, Implements): return spec if spec is None: spec = BuiltinImplementationSpecifications.get(cls) if spec is not None: return spec # TODO: need old style __implements__ compatibility? spec_name = _implements_name(cls) if spec is not None: # old-style __implemented__ = foo declaration spec = (spec, ) # tuplefy, as it might be just an int spec = Implements.named(spec_name, *_normalizeargs(spec)) spec.inherit = None # old-style implies no inherit del cls.__implemented__ # get rid of the old-style declaration else: try: bases = cls.__bases__ except AttributeError: if not callable(cls): raise TypeError("ImplementedBy called for non-factory", cls) bases = () spec = Implements.named(spec_name, *[implementedBy(c) for c in bases]) spec.inherit = cls try: cls.__implemented__ = spec if not hasattr(cls, '__providedBy__'): cls.__providedBy__ = objectSpecificationDescriptor if isinstance(cls, type) and '__provides__' not in cls.__dict__: # Make sure we get a __provides__ descriptor cls.__provides__ = ClassProvides( cls, getattr(cls, '__class__', type(cls)), ) except TypeError: if not isinstance(cls, type): raise TypeError("ImplementedBy called for non-type", cls) BuiltinImplementationSpecifications[cls] = spec return spec def classImplementsOnly(cls, *interfaces): """ Declare the only interfaces implemented by instances of a class The arguments after the class are one or more interfaces or interface specifications (`~zope.interface.interfaces.IDeclaration` objects). The interfaces given (including the interfaces in the specifications) replace any previous declarations, *including* inherited definitions. If you wish to preserve inherited declarations, you can pass ``implementedBy(cls)`` in *interfaces*. This can be used to alter the interface resolution order. """ spec = implementedBy(cls) # Clear out everything inherited. It's important to # also clear the bases right now so that we don't improperly discard # interfaces that are already implemented by *old* bases that we're # about to get rid of. spec.declared = () spec.inherit = None spec.__bases__ = () _classImplements_ordered(spec, interfaces, ()) def classImplements(cls, *interfaces): """ Declare additional interfaces implemented for instances of a class The arguments after the class are one or more interfaces or interface specifications (`~zope.interface.interfaces.IDeclaration` objects). The interfaces given (including the interfaces in the specifications) are added to any interfaces previously declared. An effort is made to keep a consistent C3 resolution order, but this cannot be guaranteed. .. versionchanged:: 5.0.0 Each individual interface in *interfaces* may be added to either the beginning or end of the list of interfaces declared for *cls*, based on inheritance, in order to try to maintain a consistent resolution order. Previously, all interfaces were added to the end. .. versionchanged:: 5.1.0 If *cls* is already declared to implement an interface (or derived interface) in *interfaces* through inheritance, the interface is ignored. Previously, it would redundantly be made direct base of *cls*, which often produced inconsistent interface resolution orders. Now, the order will be consistent, but may change. Also, if the ``__bases__`` of the *cls* are later changed, the *cls* will no longer be considered to implement such an interface (changing the ``__bases__`` of *cls* has never been supported). """ spec = implementedBy(cls) interfaces = tuple(_normalizeargs(interfaces)) before = [] after = [] # Take steps to try to avoid producing an invalid resolution # order, while still allowing for BWC (in the past, we always # appended) for iface in interfaces: for b in spec.declared: if iface.extends(b): before.append(iface) break else: after.append(iface) _classImplements_ordered(spec, tuple(before), tuple(after)) def classImplementsFirst(cls, iface): """ Declare that instances of *cls* additionally provide *iface*. The second argument is an interface or interface specification. It is added as the highest priority (first in the IRO) interface; no attempt is made to keep a consistent resolution order. .. versionadded:: 5.0.0 """ spec = implementedBy(cls) _classImplements_ordered(spec, (iface,), ()) def _classImplements_ordered(spec, before=(), after=()): # Elide everything already inherited. # Except, if it is the root, and we don't already declare anything else # that would imply it, allow the root through. (TODO: When we disallow non-strict # IRO, this part of the check can be removed because it's not possible to re-declare # like that.) before = [ x for x in before if not spec.isOrExtends(x) or (x is Interface and not spec.declared) ] after = [ x for x in after if not spec.isOrExtends(x) or (x is Interface and not spec.declared) ] # eliminate duplicates new_declared = [] seen = set() for l in before, spec.declared, after: for b in l: if b not in seen: new_declared.append(b) seen.add(b) spec.declared = tuple(new_declared) # compute the bases bases = new_declared # guaranteed no dupes if spec.inherit is not None: for c in spec.inherit.__bases__: b = implementedBy(c) if b not in seen: seen.add(b) bases.append(b) spec.__bases__ = tuple(bases) def _implements_advice(cls): interfaces, do_classImplements = cls.__dict__['__implements_advice_data__'] del cls.__implements_advice_data__ do_classImplements(cls, *interfaces) return cls class implementer: """ Declare the interfaces implemented by instances of a class. This function is called as a class decorator. The arguments are one or more interfaces or interface specifications (`~zope.interface.interfaces.IDeclaration` objects). The interfaces given (including the interfaces in the specifications) are added to any interfaces previously declared, unless the interface is already implemented. Previous declarations include declarations for base classes unless implementsOnly was used. This function is provided for convenience. It provides a more convenient way to call `classImplements`. For example:: @implementer(I1) class C(object): pass is equivalent to calling:: classImplements(C, I1) after the class has been created. .. seealso:: `classImplements` The change history provided there applies to this function too. """ __slots__ = ('interfaces',) def __init__(self, *interfaces): self.interfaces = interfaces def __call__(self, ob): if isinstance(ob, type): # This is the common branch for classes. classImplements(ob, *self.interfaces) return ob spec_name = _implements_name(ob) spec = Implements.named(spec_name, *self.interfaces) try: ob.__implemented__ = spec except AttributeError: raise TypeError("Can't declare implements", ob) return ob class implementer_only: """Declare the only interfaces implemented by instances of a class This function is called as a class decorator. The arguments are one or more interfaces or interface specifications (`~zope.interface.interfaces.IDeclaration` objects). Previous declarations including declarations for base classes are overridden. This function is provided for convenience. It provides a more convenient way to call `classImplementsOnly`. For example:: @implementer_only(I1) class C(object): pass is equivalent to calling:: classImplementsOnly(I1) after the class has been created. """ def __init__(self, *interfaces): self.interfaces = interfaces def __call__(self, ob): if isinstance(ob, (FunctionType, MethodType)): # XXX Does this decorator make sense for anything but classes? # I don't think so. There can be no inheritance of interfaces # on a method or function.... raise ValueError('The implementer_only decorator is not ' 'supported for methods or functions.') # Assume it's a class: classImplementsOnly(ob, *self.interfaces) return ob ############################################################################## # # Instance declarations class Provides(Declaration): # Really named ProvidesClass """Implement ``__provides__``, the instance-specific specification When an object is pickled, we pickle the interfaces that it implements. """ def __init__(self, cls, *interfaces): self.__args = (cls, ) + interfaces self._cls = cls Declaration.__init__(self, *self._add_interfaces_to_cls(interfaces, cls)) # Added to by ``moduleProvides``, et al _v_module_names = () def __repr__(self): # The typical way to create instances of this # object is via calling ``directlyProvides(...)`` or ``alsoProvides()``, # but that's not the only way. Proxies, for example, # directly use the ``Provides(...)`` function (which is the # more generic method, and what we pickle as). We're after the most # readable, useful repr in the common case, so we use the most # common name. # # We also cooperate with ``moduleProvides`` to attempt to do the # right thing for that API. See it for details. function_name = 'directlyProvides' if self._cls is ModuleType and self._v_module_names: # See notes in ``moduleProvides``/``directlyProvides`` providing_on_module = True interfaces = self.__args[1:] else: providing_on_module = False interfaces = (self._cls,) + self.__bases__ ordered_names = self._argument_names_for_repr(interfaces) if providing_on_module: mod_names = self._v_module_names if len(mod_names) == 1: mod_names = "sys.modules[%r]" % mod_names[0] ordered_names = ( '{}, '.format(mod_names) ) + ordered_names return "{}({})".format( function_name, ordered_names, ) def __reduce__(self): # This reduces to the Provides *function*, not # this class. return Provides, self.__args __module__ = 'zope.interface' def __get__(self, inst, cls): """Make sure that a class __provides__ doesn't leak to an instance """ if inst is None and cls is self._cls: # We were accessed through a class, so we are the class' # provides spec. Just return this object, but only if we are # being called on the same class that we were defined for: return self raise AttributeError('__provides__') ProvidesClass = Provides # Registry of instance declarations # This is a memory optimization to allow objects to share specifications. InstanceDeclarations = weakref.WeakValueDictionary() def Provides(*interfaces): # pylint:disable=function-redefined """Declaration for an instance of *cls*. The correct signature is ``cls, *interfaces``. The *cls* is necessary to avoid the construction of inconsistent resolution orders. Instance declarations are shared among instances that have the same declaration. The declarations are cached in a weak value dictionary. """ spec = InstanceDeclarations.get(interfaces) if spec is None: spec = ProvidesClass(*interfaces) InstanceDeclarations[interfaces] = spec return spec Provides.__safe_for_unpickling__ = True def directlyProvides(object, *interfaces): # pylint:disable=redefined-builtin """Declare interfaces declared directly for an object The arguments after the object are one or more interfaces or interface specifications (`~zope.interface.interfaces.IDeclaration` objects). The interfaces given (including the interfaces in the specifications) replace interfaces previously declared for the object. """ cls = getattr(object, '__class__', None) if cls is not None and getattr(cls, '__class__', None) is cls: # It's a meta class (well, at least it it could be an extension class) # Note that we can't get here from the tests: there is no normal # class which isn't descriptor aware. if not isinstance(object, type): raise TypeError("Attempt to make an interface declaration on a " "non-descriptor-aware class") interfaces = _normalizeargs(interfaces) if cls is None: cls = type(object) if issubclass(cls, type): # we have a class or type. We'll use a special descriptor # that provides some extra caching object.__provides__ = ClassProvides(object, cls, *interfaces) else: provides = object.__provides__ = Provides(cls, *interfaces) # See notes in ``moduleProvides``. if issubclass(cls, ModuleType) and hasattr(object, '__name__'): provides._v_module_names += (object.__name__,) def alsoProvides(object, *interfaces): # pylint:disable=redefined-builtin """Declare interfaces declared directly for an object The arguments after the object are one or more interfaces or interface specifications (`~zope.interface.interfaces.IDeclaration` objects). The interfaces given (including the interfaces in the specifications) are added to the interfaces previously declared for the object. """ directlyProvides(object, directlyProvidedBy(object), *interfaces) def noLongerProvides(object, interface): # pylint:disable=redefined-builtin """ Removes a directly provided interface from an object. """ directlyProvides(object, directlyProvidedBy(object) - interface) if interface.providedBy(object): raise ValueError("Can only remove directly provided interfaces.") @_use_c_impl class ClassProvidesBase(SpecificationBase): __slots__ = ( '_cls', '_implements', ) def __get__(self, inst, cls): # member slots are set by subclass # pylint:disable=no-member if cls is self._cls: # We only work if called on the class we were defined for if inst is None: # We were accessed through a class, so we are the class' # provides spec. Just return this object as is: return self return self._implements raise AttributeError('__provides__') class ClassProvides(Declaration, ClassProvidesBase): """Special descriptor for class ``__provides__`` The descriptor caches the implementedBy info, so that we can get declarations for objects without instance-specific interfaces a bit quicker. """ __slots__ = ( '__args', ) def __init__(self, cls, metacls, *interfaces): self._cls = cls self._implements = implementedBy(cls) self.__args = (cls, metacls, ) + interfaces Declaration.__init__(self, *self._add_interfaces_to_cls(interfaces, metacls)) def __repr__(self): # There are two common ways to get instances of this object: # The most interesting way is calling ``@provider(..)`` as a decorator # of a class; this is the same as calling ``directlyProvides(cls, ...)``. # # The other way is by default: anything that invokes ``implementedBy(x)`` # will wind up putting an instance in ``type(x).__provides__``; this includes # the ``@implementer(...)`` decorator. Those instances won't have any # interfaces. # # Thus, as our repr, we go with the ``directlyProvides()`` syntax. interfaces = (self._cls, ) + self.__args[2:] ordered_names = self._argument_names_for_repr(interfaces) return "directlyProvides({})".format(ordered_names) def __reduce__(self): return self.__class__, self.__args # Copy base-class method for speed __get__ = ClassProvidesBase.__get__ def directlyProvidedBy(object): # pylint:disable=redefined-builtin """Return the interfaces directly provided by the given object The value returned is an `~zope.interface.interfaces.IDeclaration`. """ provides = getattr(object, "__provides__", None) if ( provides is None # no spec # We might have gotten the implements spec, as an # optimization. If so, it's like having only one base, that we # lop off to exclude class-supplied declarations: or isinstance(provides, Implements) ): return _empty # Strip off the class part of the spec: return Declaration(provides.__bases__[:-1]) class provider: """Declare interfaces provided directly by a class This function is called in a class definition. The arguments are one or more interfaces or interface specifications (`~zope.interface.interfaces.IDeclaration` objects). The given interfaces (including the interfaces in the specifications) are used to create the class's direct-object interface specification. An error will be raised if the module class has an direct interface specification. In other words, it is an error to call this function more than once in a class definition. Note that the given interfaces have nothing to do with the interfaces implemented by instances of the class. This function is provided for convenience. It provides a more convenient way to call `directlyProvides` for a class. For example:: @provider(I1) class C: pass is equivalent to calling:: directlyProvides(C, I1) after the class has been created. """ def __init__(self, *interfaces): self.interfaces = interfaces def __call__(self, ob): directlyProvides(ob, *self.interfaces) return ob def moduleProvides(*interfaces): """Declare interfaces provided by a module This function is used in a module definition. The arguments are one or more interfaces or interface specifications (`~zope.interface.interfaces.IDeclaration` objects). The given interfaces (including the interfaces in the specifications) are used to create the module's direct-object interface specification. An error will be raised if the module already has an interface specification. In other words, it is an error to call this function more than once in a module definition. This function is provided for convenience. It provides a more convenient way to call directlyProvides. For example:: moduleProvides(I1) is equivalent to:: directlyProvides(sys.modules[__name__], I1) """ frame = sys._getframe(1) # pylint:disable=protected-access locals = frame.f_locals # pylint:disable=redefined-builtin # Try to make sure we were called from a module body if (locals is not frame.f_globals) or ('__name__' not in locals): raise TypeError( "moduleProvides can only be used from a module definition.") if '__provides__' in locals: raise TypeError( "moduleProvides can only be used once in a module definition.") # Note: This is cached based on the key ``(ModuleType, *interfaces)``; # One consequence is that any module that provides the same interfaces # gets the same ``__repr__``, meaning that you can't tell what module # such a declaration came from. Adding the module name to ``_v_module_names`` # attempts to correct for this; it works in some common situations, but fails # (1) after pickling (the data is lost) and (2) if declarations are # actually shared and (3) if the alternate spelling of ``directlyProvides()`` # is used. Problem (3) is fixed by cooperating with ``directlyProvides`` # to maintain this information, and problem (2) is worked around by # printing all the names, but (1) is unsolvable without introducing # new classes or changing the stored data...but it doesn't actually matter, # because ``ModuleType`` can't be pickled! p = locals["__provides__"] = Provides(ModuleType, *_normalizeargs(interfaces)) p._v_module_names += (locals['__name__'],) ############################################################################## # # Declaration querying support # XXX: is this a fossil? Nobody calls it, no unit tests exercise it, no # doctests import it, and the package __init__ doesn't import it. # (Answer: Versions of zope.container prior to 4.4.0 called this, # and zope.proxy.decorator up through at least 4.3.5 called this.) def ObjectSpecification(direct, cls): """Provide object specifications These combine information for the object and for it's classes. """ return Provides(cls, direct) # pragma: no cover fossil @_use_c_impl def getObjectSpecification(ob): try: provides = ob.__provides__ except AttributeError: provides = None if provides is not None: if isinstance(provides, SpecificationBase): return provides try: cls = ob.__class__ except AttributeError: # We can't get the class, so just consider provides return _empty return implementedBy(cls) @_use_c_impl def providedBy(ob): """ Return the interfaces provided by *ob*. If *ob* is a :class:`super` object, then only interfaces implemented by the remainder of the classes in the method resolution order are considered. Interfaces directly provided by the object underlying *ob* are not. """ # Here we have either a special object, an old-style declaration # or a descriptor # Try to get __providedBy__ try: if isinstance(ob, super): # Some objects raise errors on isinstance() return implementedBy(ob) r = ob.__providedBy__ except AttributeError: # Not set yet. Fall back to lower-level thing that computes it return getObjectSpecification(ob) try: # We might have gotten a descriptor from an instance of a # class (like an ExtensionClass) that doesn't support # descriptors. We'll make sure we got one by trying to get # the only attribute, which all specs have. r.extends except AttributeError: # The object's class doesn't understand descriptors. # Sigh. We need to get an object descriptor, but we have to be # careful. We want to use the instance's __provides__, if # there is one, but only if it didn't come from the class. try: r = ob.__provides__ except AttributeError: # No __provides__, so just fall back to implementedBy return implementedBy(ob.__class__) # We need to make sure we got the __provides__ from the # instance. We'll do this by making sure we don't get the same # thing from the class: try: cp = ob.__class__.__provides__ except AttributeError: # The ob doesn't have a class or the class has no # provides, assume we're done: return r if r is cp: # Oops, we got the provides from the class. This means # the object doesn't have it's own. We should use implementedBy return implementedBy(ob.__class__) return r @_use_c_impl class ObjectSpecificationDescriptor: """Implement the ``__providedBy__`` attribute The ``__providedBy__`` attribute computes the interfaces provided by an object. If an object has an ``__provides__`` attribute, that is returned. Otherwise, `implementedBy` the *cls* is returned. .. versionchanged:: 5.4.0 Both the default (C) implementation and the Python implementation now let exceptions raised by accessing ``__provides__`` propagate. Previously, the C version ignored all exceptions. .. versionchanged:: 5.4.0 The Python implementation now matches the C implementation and lets a ``__provides__`` of ``None`` override what the class is declared to implement. """ def __get__(self, inst, cls): """Get an object specification for an object """ if inst is None: return getObjectSpecification(cls) try: return inst.__provides__ except AttributeError: return implementedBy(cls) ############################################################################## def _normalizeargs(sequence, output=None): """Normalize declaration arguments Normalization arguments might contain Declarions, tuples, or single interfaces. Anything but individual interfaces or implements specs will be expanded. """ if output is None: output = [] cls = sequence.__class__ if InterfaceClass in cls.__mro__ or Implements in cls.__mro__: output.append(sequence) else: for v in sequence: _normalizeargs(v, output) return output _empty = _ImmutableDeclaration() objectSpecificationDescriptor = ObjectSpecificationDescriptor() zope.interface-6.4/src/zope/interface/document.py000066400000000000000000000077441462121350100222020ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2001, 2002 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. # ############################################################################## """ Pretty-Print an Interface object as structured text (Yum) This module provides a function, asStructuredText, for rendering an interface as structured text. """ import zope.interface __all__ = [ 'asReStructuredText', 'asStructuredText', ] def asStructuredText(I, munge=0, rst=False): """ Output structured text format. Note, this will whack any existing 'structured' format of the text. If `rst=True`, then the output will quote all code as inline literals in accordance with 'reStructuredText' markup principles. """ if rst: inline_literal = lambda s: "``{}``".format(s) else: inline_literal = lambda s: s r = [inline_literal(I.getName())] outp = r.append level = 1 if I.getDoc(): outp(_justify_and_indent(_trim_doc_string(I.getDoc()), level)) bases = [base for base in I.__bases__ if base is not zope.interface.Interface ] if bases: outp(_justify_and_indent("This interface extends:", level, munge)) level += 1 for b in bases: item = "o %s" % inline_literal(b.getName()) outp(_justify_and_indent(_trim_doc_string(item), level, munge)) level -= 1 namesAndDescriptions = sorted(I.namesAndDescriptions()) outp(_justify_and_indent("Attributes:", level, munge)) level += 1 for name, desc in namesAndDescriptions: if not hasattr(desc, 'getSignatureString'): # ugh... item = "{} -- {}".format(inline_literal(desc.getName()), desc.getDoc() or 'no documentation') outp(_justify_and_indent(_trim_doc_string(item), level, munge)) level -= 1 outp(_justify_and_indent("Methods:", level, munge)) level += 1 for name, desc in namesAndDescriptions: if hasattr(desc, 'getSignatureString'): # ugh... _call = "{}{}".format(desc.getName(), desc.getSignatureString()) item = "{} -- {}".format(inline_literal(_call), desc.getDoc() or 'no documentation') outp(_justify_and_indent(_trim_doc_string(item), level, munge)) return "\n\n".join(r) + "\n\n" def asReStructuredText(I, munge=0): """ Output reStructuredText format. Note, this will whack any existing 'structured' format of the text.""" return asStructuredText(I, munge=munge, rst=True) def _trim_doc_string(text): """ Trims a doc string to make it format correctly with structured text. """ lines = text.replace('\r\n', '\n').split('\n') nlines = [lines.pop(0)] if lines: min_indent = min([len(line) - len(line.lstrip()) for line in lines]) for line in lines: nlines.append(line[min_indent:]) return '\n'.join(nlines) def _justify_and_indent(text, level, munge=0, width=72): """ indent and justify text, rejustify (munge) if specified """ indent = " " * level if munge: lines = [] line = indent text = text.split() for word in text: line = ' '.join([line, word]) if len(line) > width: lines.append(line) line = indent else: lines.append(line) return '\n'.join(lines) else: return indent + \ text.strip().replace("\r\n", "\n") .replace("\n", "\n" + indent) zope.interface-6.4/src/zope/interface/exceptions.py000066400000000000000000000206741462121350100225420ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2002 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. # ############################################################################## """Interface-specific exceptions """ __all__ = [ # Invalid tree 'Invalid', 'DoesNotImplement', 'BrokenImplementation', 'BrokenMethodImplementation', 'MultipleInvalid', # Other 'BadImplements', 'InvalidInterface', ] class Invalid(Exception): """A specification is violated """ class _TargetInvalid(Invalid): # Internal use. Subclass this when you're describing # a particular target object that's invalid according # to a specific interface. # # For backwards compatibility, the *target* and *interface* are # optional, and the signatures are inconsistent in their ordering. # # We deal with the inconsistency in ordering by defining the index # of the two values in ``self.args``. *target* uses a marker object to # distinguish "not given" from "given, but None", because the latter # can be a value that gets passed to validation. For this reason, it must # always be the last argument (we detect absence by the ``IndexError``). _IX_INTERFACE = 0 _IX_TARGET = 1 # The exception to catch when indexing self.args indicating that # an argument was not given. If all arguments are expected, # a subclass should set this to (). _NOT_GIVEN_CATCH = IndexError _NOT_GIVEN = '' def _get_arg_or_default(self, ix, default=None): try: return self.args[ix] # pylint:disable=unsubscriptable-object except self._NOT_GIVEN_CATCH: return default @property def interface(self): return self._get_arg_or_default(self._IX_INTERFACE) @property def target(self): return self._get_arg_or_default(self._IX_TARGET, self._NOT_GIVEN) ### # str # # The ``__str__`` of self is implemented by concatenating (%s), in order, # these properties (none of which should have leading or trailing # whitespace): # # - self._str_subject # Begin the message, including a description of the target. # - self._str_description # Provide a general description of the type of error, including # the interface name if possible and relevant. # - self._str_conjunction # Join the description to the details. Defaults to ": ". # - self._str_details # Provide details about how this particular instance of the error. # - self._str_trailer # End the message. Usually just a period. ### @property def _str_subject(self): target = self.target if target is self._NOT_GIVEN: return "An object" return "The object {!r}".format(target) @property def _str_description(self): return "has failed to implement interface %s" % ( self.interface or '' ) _str_conjunction = ": " _str_details = "" _str_trailer = '.' def __str__(self): return "{} {}{}{}{}".format( self._str_subject, self._str_description, self._str_conjunction, self._str_details, self._str_trailer ) class DoesNotImplement(_TargetInvalid): """ DoesNotImplement(interface[, target]) The *target* (optional) does not implement the *interface*. .. versionchanged:: 5.0.0 Add the *target* argument and attribute, and change the resulting string value of this object accordingly. """ _str_details = "Does not declaratively implement the interface" class BrokenImplementation(_TargetInvalid): """ BrokenImplementation(interface, name[, target]) The *target* (optional) is missing the attribute *name*. .. versionchanged:: 5.0.0 Add the *target* argument and attribute, and change the resulting string value of this object accordingly. The *name* can either be a simple string or a ``Attribute`` object. """ _IX_NAME = _TargetInvalid._IX_INTERFACE + 1 _IX_TARGET = _IX_NAME + 1 @property def name(self): return self.args[1] # pylint:disable=unsubscriptable-object @property def _str_details(self): return "The %s attribute was not provided" % ( repr(self.name) if isinstance(self.name, str) else self.name ) class BrokenMethodImplementation(_TargetInvalid): """ BrokenMethodImplementation(method, message[, implementation, interface, target]) The *target* (optional) has a *method* in *implementation* that violates its contract in a way described by *mess*. .. versionchanged:: 5.0.0 Add the *interface* and *target* argument and attribute, and change the resulting string value of this object accordingly. The *method* can either be a simple string or a ``Method`` object. .. versionchanged:: 5.0.0 If *implementation* is given, then the *message* will have the string "implementation" replaced with an short but informative representation of *implementation*. """ _IX_IMPL = 2 _IX_INTERFACE = _IX_IMPL + 1 _IX_TARGET = _IX_INTERFACE + 1 @property def method(self): return self.args[0] # pylint:disable=unsubscriptable-object @property def mess(self): return self.args[1] # pylint:disable=unsubscriptable-object @staticmethod def __implementation_str(impl): # It could be a callable or some arbitrary object, we don't # know yet. import inspect # Inspect is a heavy-weight dependency, lots of imports try: sig = inspect.signature formatsig = str except AttributeError: sig = inspect.getargspec f = inspect.formatargspec formatsig = lambda sig: f(*sig) # pylint:disable=deprecated-method try: sig = sig(impl) except (ValueError, TypeError): # Unable to introspect. Darn. # This could be a non-callable, or a particular builtin, # or a bound method that doesn't even accept 'self', e.g., # ``Class.method = lambda: None; Class().method`` return repr(impl) try: name = impl.__qualname__ except AttributeError: name = impl.__name__ return name + formatsig(sig) @property def _str_details(self): impl = self._get_arg_or_default(self._IX_IMPL, self._NOT_GIVEN) message = self.mess if impl is not self._NOT_GIVEN and 'implementation' in message: message = message.replace("implementation", '%r') message = message % (self.__implementation_str(impl),) return 'The contract of {} is violated because {}'.format( repr(self.method) if isinstance(self.method, str) else self.method, message, ) class MultipleInvalid(_TargetInvalid): """ The *target* has failed to implement the *interface* in multiple ways. The failures are described by *exceptions*, a collection of other `Invalid` instances. .. versionadded:: 5.0 """ _NOT_GIVEN_CATCH = () def __init__(self, interface, target, exceptions): super().__init__(interface, target, tuple(exceptions)) @property def exceptions(self): return self.args[2] # pylint:disable=unsubscriptable-object @property def _str_details(self): # It would be nice to use tabs here, but that # is hard to represent in doctests. return '\n ' + '\n '.join( x._str_details.strip() if isinstance(x, _TargetInvalid) else str(x) for x in self.exceptions ) _str_conjunction = ':' # We don't want a trailing space, messes up doctests _str_trailer = '' class InvalidInterface(Exception): """The interface has invalid contents """ class BadImplements(TypeError): """An implementation assertion is invalid because it doesn't contain an interface or a sequence of valid implementation assertions. """ zope.interface-6.4/src/zope/interface/interface.py000066400000000000000000001147611462121350100223220ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2001, 2002 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. # ############################################################################## """Interface object implementation """ # pylint:disable=protected-access import sys import weakref from types import FunctionType from types import MethodType from typing import Union from zope.interface import ro from zope.interface._compat import _use_c_impl from zope.interface.exceptions import Invalid from zope.interface.ro import ro as calculate_ro __all__ = [ # Most of the public API from this module is directly exported # from zope.interface. The only remaining public API intended to # be imported from here should be those few things documented as # such. 'InterfaceClass', 'Specification', 'adapter_hooks', ] CO_VARARGS = 4 CO_VARKEYWORDS = 8 # Put in the attrs dict of an interface by ``taggedValue`` and ``invariants`` TAGGED_DATA = '__interface_tagged_values__' # Put in the attrs dict of an interface by ``interfacemethod`` INTERFACE_METHODS = '__interface_methods__' _decorator_non_return = object() _marker = object() def invariant(call): f_locals = sys._getframe(1).f_locals tags = f_locals.setdefault(TAGGED_DATA, {}) invariants = tags.setdefault('invariants', []) invariants.append(call) return _decorator_non_return def taggedValue(key, value): """Attaches a tagged value to an interface at definition time.""" f_locals = sys._getframe(1).f_locals tagged_values = f_locals.setdefault(TAGGED_DATA, {}) tagged_values[key] = value return _decorator_non_return class Element: """ Default implementation of `zope.interface.interfaces.IElement`. """ # We can't say this yet because we don't have enough # infrastructure in place. # #implements(IElement) def __init__(self, __name__, __doc__=''): # pylint:disable=redefined-builtin if not __doc__ and __name__.find(' ') >= 0: __doc__ = __name__ __name__ = None self.__name__ = __name__ self.__doc__ = __doc__ # Tagged values are rare, especially on methods or attributes. # Deferring the allocation can save substantial memory. self.__tagged_values = None def getName(self): """ Returns the name of the object. """ return self.__name__ def getDoc(self): """ Returns the documentation for the object. """ return self.__doc__ ### # Tagged values. # # Direct tagged values are set only in this instance. Others # may be inherited (for those subclasses that have that concept). ### def getTaggedValue(self, tag): """ Returns the value associated with 'tag'. """ if not self.__tagged_values: raise KeyError(tag) return self.__tagged_values[tag] def queryTaggedValue(self, tag, default=None): """ Returns the value associated with 'tag'. """ return self.__tagged_values.get(tag, default) if self.__tagged_values else default def getTaggedValueTags(self): """ Returns a collection of all tags. """ return self.__tagged_values.keys() if self.__tagged_values else () def setTaggedValue(self, tag, value): """ Associates 'value' with 'key'. """ if self.__tagged_values is None: self.__tagged_values = {} self.__tagged_values[tag] = value queryDirectTaggedValue = queryTaggedValue getDirectTaggedValue = getTaggedValue getDirectTaggedValueTags = getTaggedValueTags SpecificationBasePy = object # filled by _use_c_impl. @_use_c_impl class SpecificationBase: # This object is the base of the inheritance hierarchy for ClassProvides: # # ClassProvides < ClassProvidesBase, Declaration # Declaration < Specification < SpecificationBase # ClassProvidesBase < SpecificationBase # # In order to have compatible instance layouts, we need to declare # the storage used by Specification and Declaration here (and # those classes must have ``__slots__ = ()``); fortunately this is # not a waste of space because those are the only two inheritance # trees. These all translate into tp_members in C. __slots__ = ( # Things used here. '_implied', # Things used in Specification. '_dependents', '_bases', '_v_attrs', '__iro__', '__sro__', '__weakref__', ) def providedBy(self, ob): """Is the interface implemented by an object """ spec = providedBy(ob) return self in spec._implied def implementedBy(self, cls): """Test whether the specification is implemented by a class or factory. Raise TypeError if argument is neither a class nor a callable. """ spec = implementedBy(cls) return self in spec._implied def isOrExtends(self, interface): """Is the interface the same as or extend the given interface """ return interface in self._implied # pylint:disable=no-member __call__ = isOrExtends class NameAndModuleComparisonMixin: # Internal use. Implement the basic sorting operators (but not (in)equality # or hashing). Subclasses must provide ``__name__`` and ``__module__`` # attributes. Subclasses will be mutually comparable; but because equality # and hashing semantics are missing from this class, take care in how # you define those two attributes: If you stick with the default equality # and hashing (identity based) you should make sure that all possible ``__name__`` # and ``__module__`` pairs are unique ACROSS ALL SUBCLASSES. (Actually, pretty # much the same thing goes if you define equality and hashing to be based on # those two attributes: they must still be consistent ACROSS ALL SUBCLASSES.) # pylint:disable=assigning-non-slot __slots__ = () def _compare(self, other): """ Compare *self* to *other* based on ``__name__`` and ``__module__``. Return 0 if they are equal, return 1 if *self* is greater than *other*, and return -1 if *self* is less than *other*. If *other* does not have ``__name__`` or ``__module__``, then return ``NotImplemented``. .. caution:: This allows comparison to things well outside the type hierarchy, perhaps not symmetrically. For example, ``class Foo(object)`` and ``class Foo(Interface)`` in the same file would compare equal, depending on the order of operands. Writing code like this by hand would be unusual, but it could happen with dynamic creation of types and interfaces. None is treated as a pseudo interface that implies the loosest contact possible, no contract. For that reason, all interfaces sort before None. """ if other is self: return 0 if other is None: return -1 n1 = (self.__name__, self.__module__) try: n2 = (other.__name__, other.__module__) except AttributeError: return NotImplemented # This spelling works under Python3, which doesn't have cmp(). return (n1 > n2) - (n1 < n2) def __lt__(self, other): c = self._compare(other) if c is NotImplemented: return c return c < 0 def __le__(self, other): c = self._compare(other) if c is NotImplemented: return c return c <= 0 def __gt__(self, other): c = self._compare(other) if c is NotImplemented: return c return c > 0 def __ge__(self, other): c = self._compare(other) if c is NotImplemented: return c return c >= 0 @_use_c_impl class InterfaceBase(NameAndModuleComparisonMixin, SpecificationBasePy): """Base class that wants to be replaced with a C base :) """ __slots__ = ( '__name__', '__ibmodule__', '_v_cached_hash', ) def __init__(self, name=None, module=None): self.__name__ = name self.__ibmodule__ = module def _call_conform(self, conform): raise NotImplementedError @property def __module_property__(self): # This is for _InterfaceMetaClass return self.__ibmodule__ def __call__(self, obj, alternate=_marker): """Adapt an object to the interface """ try: conform = obj.__conform__ except AttributeError: conform = None if conform is not None: adapter = self._call_conform(conform) if adapter is not None: return adapter adapter = self.__adapt__(obj) if adapter is not None: return adapter if alternate is not _marker: return alternate raise TypeError("Could not adapt", obj, self) def __adapt__(self, obj): """Adapt an object to the receiver """ if self.providedBy(obj): return obj for hook in adapter_hooks: adapter = hook(self, obj) if adapter is not None: return adapter return None def __hash__(self): # pylint:disable=assigning-non-slot,attribute-defined-outside-init try: return self._v_cached_hash except AttributeError: self._v_cached_hash = hash((self.__name__, self.__module__)) return self._v_cached_hash def __eq__(self, other): c = self._compare(other) if c is NotImplemented: return c return c == 0 def __ne__(self, other): if other is self: return False c = self._compare(other) if c is NotImplemented: return c return c != 0 adapter_hooks = _use_c_impl([], 'adapter_hooks') class Specification(SpecificationBase): """Specifications An interface specification is used to track interface declarations and component registrations. This class is a base class for both interfaces themselves and for interface specifications (declarations). Specifications are mutable. If you reassign their bases, their relations with other specifications are adjusted accordingly. """ __slots__ = () # The root of all Specifications. This will be assigned `Interface`, # once it is defined. _ROOT = None # Copy some base class methods for speed isOrExtends = SpecificationBase.isOrExtends providedBy = SpecificationBase.providedBy def __init__(self, bases=()): # There are many leaf interfaces with no dependents, # and a few with very many. It's a heavily left-skewed # distribution. In a survey of Plone and Zope related packages # that loaded 2245 InterfaceClass objects and 2235 ClassProvides # instances, there were a total of 7000 Specification objects created. # 4700 had 0 dependents, 1400 had 1, 382 had 2 and so on. Only one # for had 1664. So there's savings to be had deferring # the creation of dependents. self._dependents = None # type: weakref.WeakKeyDictionary self._bases = () self._implied = {} self._v_attrs = None self.__iro__ = () self.__sro__ = () self.__bases__ = tuple(bases) @property def dependents(self): if self._dependents is None: self._dependents = weakref.WeakKeyDictionary() return self._dependents def subscribe(self, dependent): self._dependents[dependent] = self.dependents.get(dependent, 0) + 1 def unsubscribe(self, dependent): try: n = self._dependents[dependent] except TypeError: raise KeyError(dependent) n -= 1 if not n: del self.dependents[dependent] else: assert n > 0 self.dependents[dependent] = n def __setBases(self, bases): # Remove ourselves as a dependent of our old bases for b in self.__bases__: b.unsubscribe(self) # Register ourselves as a dependent of our new bases self._bases = bases for b in bases: b.subscribe(self) self.changed(self) __bases__ = property( lambda self: self._bases, __setBases, ) # This method exists for tests to override the way we call # ro.calculate_ro(), usually by adding extra kwargs. We don't # want to have a mutable dictionary as a class member that we pass # ourself because mutability is bad, and passing **kw is slower than # calling the bound function. _do_calculate_ro = calculate_ro def _calculate_sro(self): """ Calculate and return the resolution order for this object, using its ``__bases__``. Ensures that ``Interface`` is always the last (lowest priority) element. """ # We'd like to make Interface the lowest priority as a # property of the resolution order algorithm. That almost # works out naturally, but it fails when class inheritance has # some bases that DO implement an interface, and some that DO # NOT. In such a mixed scenario, you wind up with a set of # bases to consider that look like this: [[..., Interface], # [..., object], ...]. Depending on the order of inheritance, # Interface can wind up before or after object, and that can # happen at any point in the tree, meaning Interface can wind # up somewhere in the middle of the order. Since Interface is # treated as something that everything winds up implementing # anyway (a catch-all for things like adapters), having it high up # the order is bad. It's also bad to have it at the end, just before # some concrete class: concrete classes should be HIGHER priority than # interfaces (because there's only one class, but many implementations). # # One technically nice way to fix this would be to have # ``implementedBy(object).__bases__ = (Interface,)`` # # But: (1) That fails for old-style classes and (2) that causes # everything to appear to *explicitly* implement Interface, when up # to this point it's been an implicit virtual sort of relationship. # # So we force the issue by mutating the resolution order. # Note that we let C3 use pre-computed __sro__ for our bases. # This requires that by the time this method is invoked, our bases # have settled their SROs. Thus, ``changed()`` must first # update itself before telling its descendents of changes. sro = self._do_calculate_ro(base_mros={ b: b.__sro__ for b in self.__bases__ }) root = self._ROOT if root is not None and sro and sro[-1] is not root: # In one dataset of 1823 Interface objects, 1117 ClassProvides objects, # sro[-1] was root 4496 times, and only not root 118 times. So it's # probably worth checking. # Once we don't have to deal with old-style classes, # we can add a check and only do this if base_count > 1, # if we tweak the bootstrapping for ```` sro = [ x for x in sro if x is not root ] sro.append(root) return sro def changed(self, originally_changed): """ We, or something we depend on, have changed. By the time this is called, the things we depend on, such as our bases, should themselves be stable. """ self._v_attrs = None implied = self._implied implied.clear() ancestors = self._calculate_sro() self.__sro__ = tuple(ancestors) self.__iro__ = tuple([ancestor for ancestor in ancestors if isinstance(ancestor, InterfaceClass) ]) for ancestor in ancestors: # We directly imply our ancestors: implied[ancestor] = () # Now, advise our dependents of change # (being careful not to create the WeakKeyDictionary if not needed): for dependent in tuple(self._dependents.keys() if self._dependents else ()): dependent.changed(originally_changed) # Just in case something called get() at some point # during that process and we have a cycle of some sort # make sure we didn't cache incomplete results. self._v_attrs = None def interfaces(self): """Return an iterator for the interfaces in the specification. """ seen = {} for base in self.__bases__: for interface in base.interfaces(): if interface not in seen: seen[interface] = 1 yield interface def extends(self, interface, strict=True): """Does the specification extend the given interface? Test whether an interface in the specification extends the given interface """ return ((interface in self._implied) and ((not strict) or (self != interface)) ) def weakref(self, callback=None): return weakref.ref(self, callback) def get(self, name, default=None): """Query for an attribute description """ attrs = self._v_attrs if attrs is None: attrs = self._v_attrs = {} attr = attrs.get(name) if attr is None: for iface in self.__iro__: attr = iface.direct(name) if attr is not None: attrs[name] = attr break return default if attr is None else attr class _InterfaceMetaClass(type): # Handling ``__module__`` on ``InterfaceClass`` is tricky. We need # to be able to read it on a type and get the expected string. We # also need to be able to set it on an instance and get the value # we set. So far so good. But what gets tricky is that we'd like # to store the value in the C structure (``InterfaceBase.__ibmodule__``) for # direct access during equality, sorting, and hashing. "No # problem, you think, I'll just use a property" (well, the C # equivalents, ``PyMemberDef`` or ``PyGetSetDef``). # # Except there is a problem. When a subclass is created, the # metaclass (``type``) always automatically puts the expected # string in the class's dictionary under ``__module__``, thus # overriding the property inherited from the superclass. Writing # ``Subclass.__module__`` still works, but # ``Subclass().__module__`` fails. # # There are multiple ways to work around this: # # (1) Define ``InterfaceBase.__getattribute__`` to watch for # ``__module__`` and return the C storage. # # This works, but slows down *all* attribute access (except, # ironically, to ``__module__``) by about 25% (40ns becomes 50ns) # (when implemented in C). Since that includes methods like # ``providedBy``, that's probably not acceptable. # # All the other methods involve modifying subclasses. This can be # done either on the fly in some cases, as instances are # constructed, or by using a metaclass. These next few can be done on the fly. # # (2) Make ``__module__`` a descriptor in each subclass dictionary. # It can't be a straight up ``@property`` descriptor, though, because accessing # it on the class returns a ``property`` object, not the desired string. # # (3) Implement a data descriptor (``__get__`` and ``__set__``) # that is both a subclass of string, and also does the redirect of # ``__module__`` to ``__ibmodule__`` and does the correct thing # with the ``instance`` argument to ``__get__`` is None (returns # the class's value.) (Why must it be a subclass of string? Because # when it' s in the class's dict, it's defined on an *instance* of the # metaclass; descriptors in an instance's dict aren't honored --- their # ``__get__`` is never invoked --- so it must also *be* the value we want # returned.) # # This works, preserves the ability to read and write # ``__module__``, and eliminates any penalty accessing other # attributes. But it slows down accessing ``__module__`` of # instances by 200% (40ns to 124ns), requires editing class dicts on the fly # (in InterfaceClass.__init__), thus slightly slowing down all interface creation, # and is ugly. # # (4) As in the last step, but make it a non-data descriptor (no ``__set__``). # # If you then *also* store a copy of ``__ibmodule__`` in # ``__module__`` in the instance's dict, reading works for both # class and instance and is full speed for instances. But the cost # is storage space, and you can't write to it anymore, not without # things getting out of sync. # # (Actually, ``__module__`` was never meant to be writable. Doing # so would break BTrees and normal dictionaries, as well as the # repr, maybe more.) # # That leaves us with a metaclass. (Recall that a class is an # instance of its metaclass, so properties/descriptors defined in # the metaclass are used when accessing attributes on the # instance/class. We'll use that to define ``__module__``.) Here # we can have our cake and eat it too: no extra storage, and # C-speed access to the underlying storage. The only substantial # cost is that metaclasses tend to make people's heads hurt. (But # still less than the descriptor-is-string, hopefully.) __slots__ = () def __new__(cls, name, bases, attrs): # Figure out what module defined the interface. # This is copied from ``InterfaceClass.__init__``; # reviewers aren't sure how AttributeError or KeyError # could be raised. __module__ = sys._getframe(1).f_globals['__name__'] # Get the C optimized __module__ accessor and give it # to the new class. moduledescr = InterfaceBase.__dict__['__module__'] if isinstance(moduledescr, str): # We're working with the Python implementation, # not the C version moduledescr = InterfaceBase.__dict__['__module_property__'] attrs['__module__'] = moduledescr kind = type.__new__(cls, name, bases, attrs) kind.__module = __module__ return kind @property def __module__(cls): return cls.__module def __repr__(cls): return "".format( cls.__module, cls.__name__, ) _InterfaceClassBase = _InterfaceMetaClass( 'InterfaceClass', # From least specific to most specific. (InterfaceBase, Specification, Element), {'__slots__': ()} ) def interfacemethod(func): """ Convert a method specification to an actual method of the interface. This is a decorator that functions like `staticmethod` et al. The primary use of this decorator is to allow interface definitions to define the ``__adapt__`` method, but other interface methods can be overridden this way too. .. seealso:: `zope.interface.interfaces.IInterfaceDeclaration.interfacemethod` """ f_locals = sys._getframe(1).f_locals methods = f_locals.setdefault(INTERFACE_METHODS, {}) methods[func.__name__] = func return _decorator_non_return class InterfaceClass(_InterfaceClassBase): """ Prototype (scarecrow) Interfaces Implementation. Note that it is not possible to change the ``__name__`` or ``__module__`` after an instance of this object has been constructed. """ # We can't say this yet because we don't have enough # infrastructure in place. # #implements(IInterface) def __new__(cls, name=None, bases=(), attrs=None, __doc__=None, # pylint:disable=redefined-builtin __module__=None): assert isinstance(bases, tuple) attrs = attrs or {} needs_custom_class = attrs.pop(INTERFACE_METHODS, None) if needs_custom_class: needs_custom_class.update( {'__classcell__': attrs.pop('__classcell__')} if '__classcell__' in attrs else {} ) if '__adapt__' in needs_custom_class: # We need to tell the C code to call this. needs_custom_class['_CALL_CUSTOM_ADAPT'] = 1 if issubclass(cls, _InterfaceClassWithCustomMethods): cls_bases = (cls,) elif cls is InterfaceClass: cls_bases = (_InterfaceClassWithCustomMethods,) else: cls_bases = (cls, _InterfaceClassWithCustomMethods) cls = type(cls)( # pylint:disable=self-cls-assignment name + "", cls_bases, needs_custom_class ) return _InterfaceClassBase.__new__(cls) def __init__(self, name, bases=(), attrs=None, __doc__=None, # pylint:disable=redefined-builtin __module__=None): # We don't call our metaclass parent directly # pylint:disable=non-parent-init-called # pylint:disable=super-init-not-called if not all(isinstance(base, InterfaceClass) for base in bases): raise TypeError('Expected base interfaces') if attrs is None: attrs = {} if __module__ is None: __module__ = attrs.get('__module__') if isinstance(__module__, str): del attrs['__module__'] else: try: # Figure out what module defined the interface. # This is how cPython figures out the module of # a class, but of course it does it in C. :-/ __module__ = sys._getframe(1).f_globals['__name__'] except (AttributeError, KeyError): # pragma: no cover pass InterfaceBase.__init__(self, name, __module__) # These asserts assisted debugging the metaclass # assert '__module__' not in self.__dict__ # assert self.__ibmodule__ is self.__module__ is __module__ d = attrs.get('__doc__') if d is not None: if not isinstance(d, Attribute): if __doc__ is None: __doc__ = d del attrs['__doc__'] if __doc__ is None: __doc__ = '' Element.__init__(self, name, __doc__) tagged_data = attrs.pop(TAGGED_DATA, None) if tagged_data is not None: for key, val in tagged_data.items(): self.setTaggedValue(key, val) Specification.__init__(self, bases) self.__attrs = self.__compute_attrs(attrs) self.__identifier__ = "{}.{}".format(__module__, name) def __compute_attrs(self, attrs): # Make sure that all recorded attributes (and methods) are of type # `Attribute` and `Method` def update_value(aname, aval): if isinstance(aval, Attribute): aval.interface = self if not aval.__name__: aval.__name__ = aname elif isinstance(aval, FunctionType): aval = fromFunction(aval, self, name=aname) else: raise InvalidInterface("Concrete attribute, " + aname) return aval return { aname: update_value(aname, aval) for aname, aval in attrs.items() if aname not in ( # __locals__: Python 3 sometimes adds this. '__locals__', # __qualname__: PEP 3155 (Python 3.3+) '__qualname__', # __annotations__: PEP 3107 (Python 3.0+) '__annotations__', # __static_attributes__: Python 3.13a6+ # https://github.com/python/cpython/pull/115913 '__static_attributes__', # __firstlineno__: Python 3.13b1+ # https://github.com/python/cpython/pull/118475 '__firstlineno__', ) and aval is not _decorator_non_return } def interfaces(self): """Return an iterator for the interfaces in the specification. """ yield self def getBases(self): return self.__bases__ def isEqualOrExtendedBy(self, other): """Same interface or extends?""" return self == other or other.extends(self) def names(self, all=False): # pylint:disable=redefined-builtin """Return the attribute names defined by the interface.""" if not all: return self.__attrs.keys() r = self.__attrs.copy() for base in self.__bases__: r.update(dict.fromkeys(base.names(all))) return r.keys() def __iter__(self): return iter(self.names(all=True)) def namesAndDescriptions(self, all=False): # pylint:disable=redefined-builtin """Return attribute names and descriptions defined by interface.""" if not all: return self.__attrs.items() r = {} for base in self.__bases__[::-1]: r.update(dict(base.namesAndDescriptions(all))) r.update(self.__attrs) return r.items() def getDescriptionFor(self, name): """Return the attribute description for the given name.""" r = self.get(name) if r is not None: return r raise KeyError(name) __getitem__ = getDescriptionFor def __contains__(self, name): return self.get(name) is not None def direct(self, name): return self.__attrs.get(name) def queryDescriptionFor(self, name, default=None): return self.get(name, default) def validateInvariants(self, obj, errors=None): """validate object to defined invariants.""" for iface in self.__iro__: for invariant in iface.queryDirectTaggedValue('invariants', ()): try: invariant(obj) except Invalid as error: if errors is not None: errors.append(error) else: raise if errors: raise Invalid(errors) def queryTaggedValue(self, tag, default=None): """ Queries for the value associated with *tag*, returning it from the nearest interface in the ``__iro__``. If not found, returns *default*. """ for iface in self.__iro__: value = iface.queryDirectTaggedValue(tag, _marker) if value is not _marker: return value return default def getTaggedValue(self, tag): """ Returns the value associated with 'tag'. """ value = self.queryTaggedValue(tag, default=_marker) if value is _marker: raise KeyError(tag) return value def getTaggedValueTags(self): """ Returns a list of all tags. """ keys = set() for base in self.__iro__: keys.update(base.getDirectTaggedValueTags()) return keys def __repr__(self): try: return self._v_repr except AttributeError: name = str(self) r = "<{} {}>".format(self.__class__.__name__, name) self._v_repr = r # pylint:disable=attribute-defined-outside-init return r def __str__(self): name = self.__name__ m = self.__ibmodule__ if m: name = '{}.{}'.format(m, name) return name def _call_conform(self, conform): try: return conform(self) except TypeError: # pragma: no cover # We got a TypeError. It might be an error raised by # the __conform__ implementation, or *we* may have # made the TypeError by calling an unbound method # (object is a class). In the later case, we behave # as though there is no __conform__ method. We can # detect this case by checking whether there is more # than one traceback object in the traceback chain: if sys.exc_info()[2].tb_next is not None: # There is more than one entry in the chain, so # reraise the error: raise # This clever trick is from Phillip Eby return None # pragma: no cover def __reduce__(self): return self.__name__ def __or__(self, other): """Allow type hinting syntax: Interface | None.""" return Union[self, other] def __ror__(self, other): """Allow type hinting syntax: None | Interface.""" return Union[other, self] Interface = InterfaceClass("Interface", __module__='zope.interface') # Interface is the only member of its own SRO. Interface._calculate_sro = lambda: (Interface,) Interface.changed(Interface) assert Interface.__sro__ == (Interface,) Specification._ROOT = Interface ro._ROOT = Interface class _InterfaceClassWithCustomMethods(InterfaceClass): """ Marker class for interfaces with custom methods that override InterfaceClass methods. """ class Attribute(Element): """Attribute descriptions """ # We can't say this yet because we don't have enough # infrastructure in place. # # implements(IAttribute) interface = None def _get_str_info(self): """Return extra data to put at the end of __str__.""" return "" def __str__(self): of = '' if self.interface is not None: of = self.interface.__module__ + '.' + self.interface.__name__ + '.' # self.__name__ may be None during construction (e.g., debugging) return of + (self.__name__ or '') + self._get_str_info() def __repr__(self): return "<{}.{} object at 0x{:x} {}>".format( type(self).__module__, type(self).__name__, id(self), self ) class Method(Attribute): """Method interfaces The idea here is that you have objects that describe methods. This provides an opportunity for rich meta-data. """ # We can't say this yet because we don't have enough # infrastructure in place. # # implements(IMethod) positional = required = () _optional = varargs = kwargs = None def _get_optional(self): if self._optional is None: return {} return self._optional def _set_optional(self, opt): self._optional = opt def _del_optional(self): self._optional = None optional = property(_get_optional, _set_optional, _del_optional) def __call__(self, *args, **kw): raise BrokenImplementation(self.interface, self.__name__) def getSignatureInfo(self): return {'positional': self.positional, 'required': self.required, 'optional': self.optional, 'varargs': self.varargs, 'kwargs': self.kwargs, } def getSignatureString(self): sig = [] for v in self.positional: sig.append(v) if v in self.optional.keys(): sig[-1] += "=" + repr(self.optional[v]) if self.varargs: sig.append("*" + self.varargs) if self.kwargs: sig.append("**" + self.kwargs) return "(%s)" % ", ".join(sig) _get_str_info = getSignatureString def fromFunction(func, interface=None, imlevel=0, name=None): name = name or func.__name__ method = Method(name, func.__doc__) defaults = getattr(func, '__defaults__', None) or () code = func.__code__ # Number of positional arguments na = code.co_argcount - imlevel names = code.co_varnames[imlevel:] opt = {} # Number of required arguments defaults_count = len(defaults) if not defaults_count: # PyPy3 uses ``__defaults_count__`` for builtin methods # like ``dict.pop``. Surprisingly, these don't have recorded # ``__defaults__`` defaults_count = getattr(func, '__defaults_count__', 0) nr = na - defaults_count if nr < 0: defaults = defaults[-nr:] nr = 0 # Determine the optional arguments. opt.update(dict(zip(names[nr:], defaults))) method.positional = names[:na] method.required = names[:nr] method.optional = opt argno = na # Determine the function's variable argument's name (i.e. *args) if code.co_flags & CO_VARARGS: method.varargs = names[argno] argno = argno + 1 else: method.varargs = None # Determine the function's keyword argument's name (i.e. **kw) if code.co_flags & CO_VARKEYWORDS: method.kwargs = names[argno] else: method.kwargs = None method.interface = interface for key, value in func.__dict__.items(): method.setTaggedValue(key, value) return method def fromMethod(meth, interface=None, name=None): if isinstance(meth, MethodType): func = meth.__func__ else: func = meth return fromFunction(func, interface, imlevel=1, name=name) # Now we can create the interesting interfaces and wire them up: def _wire(): from zope.interface.declarations import classImplements # From lest specific to most specific. from zope.interface.interfaces import IElement classImplements(Element, IElement) from zope.interface.interfaces import IAttribute classImplements(Attribute, IAttribute) from zope.interface.interfaces import IMethod classImplements(Method, IMethod) from zope.interface.interfaces import ISpecification classImplements(Specification, ISpecification) from zope.interface.interfaces import IInterface classImplements(InterfaceClass, IInterface) # We import this here to deal with module dependencies. # pylint:disable=wrong-import-position from zope.interface.declarations import implementedBy from zope.interface.declarations import providedBy from zope.interface.exceptions import BrokenImplementation from zope.interface.exceptions import InvalidInterface # This ensures that ``Interface`` winds up in the flattened() # list of the immutable declaration. It correctly overrides changed() # as a no-op, so we bypass that. from zope.interface.declarations import _empty Specification.changed(_empty, _empty) zope.interface-6.4/src/zope/interface/interfaces.py000066400000000000000000001417161462121350100225050ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2002 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. # ############################################################################## """Interface Package Interfaces """ __docformat__ = 'restructuredtext' from zope.interface.declarations import implementer from zope.interface.interface import Attribute from zope.interface.interface import Interface __all__ = [ 'ComponentLookupError', 'IAdapterRegistration', 'IAdapterRegistry', 'IAttribute', 'IComponentLookup', 'IComponentRegistry', 'IComponents', 'IDeclaration', 'IElement', 'IHandlerRegistration', 'IInterface', 'IInterfaceDeclaration', 'IMethod', 'Invalid', 'IObjectEvent', 'IRegistered', 'IRegistration', 'IRegistrationEvent', 'ISpecification', 'ISubscriptionAdapterRegistration', 'IUnregistered', 'IUtilityRegistration', 'ObjectEvent', 'Registered', 'Unregistered', ] # pylint:disable=inherit-non-class,no-method-argument,no-self-argument # pylint:disable=unexpected-special-method-signature # pylint:disable=too-many-lines class IElement(Interface): """ Objects that have basic documentation and tagged values. Known derivatives include :class:`IAttribute` and its derivative :class:`IMethod`; these have no notion of inheritance. :class:`IInterface` is also a derivative, and it does have a notion of inheritance, expressed through its ``__bases__`` and ordered in its ``__iro__`` (both defined by :class:`ISpecification`). """ # pylint:disable=arguments-differ # Note that defining __doc__ as an Attribute hides the docstring # from introspection. When changing it, also change it in the Sphinx # ReST files. __name__ = Attribute('__name__', 'The object name') __doc__ = Attribute('__doc__', 'The object doc string') ### # Tagged values. # # Direct values are established in this instance. Others may be # inherited. Although ``IElement`` itself doesn't have a notion of # inheritance, ``IInterface`` *does*. It might have been better to # make ``IInterface`` define new methods # ``getIndirectTaggedValue``, etc, to include inheritance instead # of overriding ``getTaggedValue`` to do that, but that ship has sailed. # So to keep things nice and symmetric, we define the ``Direct`` methods here. ### def getTaggedValue(tag): """Returns the value associated with *tag*. Raise a `KeyError` if the tag isn't set. If the object has a notion of inheritance, this searches through the inheritance hierarchy and returns the nearest result. If there is no such notion, this looks only at this object. .. versionchanged:: 4.7.0 This method should respect inheritance if present. """ def queryTaggedValue(tag, default=None): """ As for `getTaggedValue`, but instead of raising a `KeyError`, returns *default*. .. versionchanged:: 4.7.0 This method should respect inheritance if present. """ def getTaggedValueTags(): """ Returns a collection of all tags in no particular order. If the object has a notion of inheritance, this includes all the inherited tagged values. If there is no such notion, this looks only at this object. .. versionchanged:: 4.7.0 This method should respect inheritance if present. """ def setTaggedValue(tag, value): """ Associates *value* with *key* directly in this object. """ def getDirectTaggedValue(tag): """ As for `getTaggedValue`, but never includes inheritance. .. versionadded:: 5.0.0 """ def queryDirectTaggedValue(tag, default=None): """ As for `queryTaggedValue`, but never includes inheritance. .. versionadded:: 5.0.0 """ def getDirectTaggedValueTags(): """ As for `getTaggedValueTags`, but includes only tags directly set on this object. .. versionadded:: 5.0.0 """ class IAttribute(IElement): """Attribute descriptors""" interface = Attribute('interface', 'Stores the interface instance in which the ' 'attribute is located.') class IMethod(IAttribute): """Method attributes""" def getSignatureInfo(): """Returns the signature information. This method returns a dictionary with the following string keys: - positional A sequence of the names of positional arguments. - required A sequence of the names of required arguments. - optional A dictionary mapping argument names to their default values. - varargs The name of the varargs argument (or None). - kwargs The name of the kwargs argument (or None). """ def getSignatureString(): """Return a signature string suitable for inclusion in documentation. This method returns the function signature string. For example, if you have ``def func(a, b, c=1, d='f')``, then the signature string is ``"(a, b, c=1, d='f')"``. """ class ISpecification(Interface): """Object Behavioral specifications""" # pylint:disable=arguments-differ def providedBy(object): # pylint:disable=redefined-builtin """Test whether the interface is implemented by the object Return true of the object asserts that it implements the interface, including asserting that it implements an extended interface. """ def implementedBy(class_): """Test whether the interface is implemented by instances of the class Return true of the class asserts that its instances implement the interface, including asserting that they implement an extended interface. """ def isOrExtends(other): """Test whether the specification is or extends another """ def extends(other, strict=True): """Test whether a specification extends another The specification extends other if it has other as a base interface or if one of it's bases extends other. If strict is false, then the specification extends itself. """ def weakref(callback=None): """Return a weakref to the specification This method is, regrettably, needed to allow weakrefs to be computed to security-proxied specifications. While the zope.interface package does not require zope.security or zope.proxy, it has to be able to coexist with it. """ __bases__ = Attribute("""Base specifications A tuple of specifications from which this specification is directly derived. """) __sro__ = Attribute("""Specification-resolution order A tuple of the specification and all of it's ancestor specifications from most specific to least specific. The specification itself is the first element. (This is similar to the method-resolution order for new-style classes.) """) __iro__ = Attribute("""Interface-resolution order A tuple of the specification's ancestor interfaces from most specific to least specific. The specification itself is included if it is an interface. (This is similar to the method-resolution order for new-style classes.) """) def get(name, default=None): """Look up the description for a name If the named attribute is not defined, the default is returned. """ class IInterface(ISpecification, IElement): """Interface objects Interface objects describe the behavior of an object by containing useful information about the object. This information includes: - Prose documentation about the object. In Python terms, this is called the "doc string" of the interface. In this element, you describe how the object works in prose language and any other useful information about the object. - Descriptions of attributes. Attribute descriptions include the name of the attribute and prose documentation describing the attributes usage. - Descriptions of methods. Method descriptions can include: - Prose "doc string" documentation about the method and its usage. - A description of the methods arguments; how many arguments are expected, optional arguments and their default values, the position or arguments in the signature, whether the method accepts arbitrary arguments and whether the method accepts arbitrary keyword arguments. - Optional tagged data. Interface objects (and their attributes and methods) can have optional, application specific tagged data associated with them. Examples uses for this are examples, security assertions, pre/post conditions, and other possible information you may want to associate with an Interface or its attributes. Not all of this information is mandatory. For example, you may only want the methods of your interface to have prose documentation and not describe the arguments of the method in exact detail. Interface objects are flexible and let you give or take any of these components. Interfaces are created with the Python class statement using either `zope.interface.Interface` or another interface, as in:: from zope.interface import Interface class IMyInterface(Interface): '''Interface documentation''' def meth(arg1, arg2): '''Documentation for meth''' # Note that there is no self argument class IMySubInterface(IMyInterface): '''Interface documentation''' def meth2(): '''Documentation for meth2''' You use interfaces in two ways: - You assert that your object implement the interfaces. There are several ways that you can declare that an object provides an interface: 1. Call `zope.interface.implementer` on your class definition. 2. Call `zope.interface.directlyProvides` on your object. 3. Call `zope.interface.classImplements` to declare that instances of a class implement an interface. For example:: from zope.interface import classImplements classImplements(some_class, some_interface) This approach is useful when it is not an option to modify the class source. Note that this doesn't affect what the class itself implements, but only what its instances implement. - You query interface meta-data. See the IInterface methods and attributes for details. """ # pylint:disable=arguments-differ def names(all=False): # pylint:disable=redefined-builtin """Get the interface attribute names Return a collection of the names of the attributes, including methods, included in the interface definition. Normally, only directly defined attributes are included. If a true positional or keyword argument is given, then attributes defined by base classes will be included. """ def namesAndDescriptions(all=False): # pylint:disable=redefined-builtin """Get the interface attribute names and descriptions Return a collection of the names and descriptions of the attributes, including methods, as name-value pairs, included in the interface definition. Normally, only directly defined attributes are included. If a true positional or keyword argument is given, then attributes defined by base classes will be included. """ def __getitem__(name): """Get the description for a name If the named attribute is not defined, a `KeyError` is raised. """ def direct(name): """Get the description for the name if it was defined by the interface If the interface doesn't define the name, returns None. """ def validateInvariants(obj, errors=None): """Validate invariants Validate object to defined invariants. If errors is None, raises first Invalid error; if errors is a list, appends all errors to list, then raises Invalid with the errors as the first element of the "args" tuple.""" def __contains__(name): """Test whether the name is defined by the interface""" def __iter__(): """Return an iterator over the names defined by the interface The names iterated include all of the names defined by the interface directly and indirectly by base interfaces. """ __module__ = Attribute("""The name of the module defining the interface""") class IDeclaration(ISpecification): """Interface declaration Declarations are used to express the interfaces implemented by classes or provided by objects. """ def __contains__(interface): """Test whether an interface is in the specification Return true if the given interface is one of the interfaces in the specification and false otherwise. """ def __iter__(): """Return an iterator for the interfaces in the specification """ def flattened(): """Return an iterator of all included and extended interfaces An iterator is returned for all interfaces either included in or extended by interfaces included in the specifications without duplicates. The interfaces are in "interface resolution order". The interface resolution order is such that base interfaces are listed after interfaces that extend them and, otherwise, interfaces are included in the order that they were defined in the specification. """ def __sub__(interfaces): """Create an interface specification with some interfaces excluded The argument can be an interface or an interface specifications. The interface or interfaces given in a specification are subtracted from the interface specification. Removing an interface that is not in the specification does not raise an error. Doing so has no effect. Removing an interface also removes sub-interfaces of the interface. """ def __add__(interfaces): """Create an interface specification with some interfaces added The argument can be an interface or an interface specifications. The interface or interfaces given in a specification are added to the interface specification. Adding an interface that is already in the specification does not raise an error. Doing so has no effect. """ def __nonzero__(): """Return a true value of the interface specification is non-empty """ class IInterfaceDeclaration(Interface): """ Declare and check the interfaces of objects. The functions defined in this interface are used to declare the interfaces that objects provide and to query the interfaces that have been declared. Interfaces can be declared for objects in two ways: - Interfaces are declared for instances of the object's class - Interfaces are declared for the object directly. The interfaces declared for an object are, therefore, the union of interfaces declared for the object directly and the interfaces declared for instances of the object's class. Note that we say that a class implements the interfaces provided by it's instances. An instance can also provide interfaces directly. The interfaces provided by an object are the union of the interfaces provided directly and the interfaces implemented by the class. This interface is implemented by :mod:`zope.interface`. """ # pylint:disable=arguments-differ ### # Defining interfaces ### Interface = Attribute("The base class used to create new interfaces") def taggedValue(key, value): """ Attach a tagged value to an interface while defining the interface. This is a way of executing :meth:`IElement.setTaggedValue` from the definition of the interface. For example:: class IFoo(Interface): taggedValue('key', 'value') .. seealso:: `zope.interface.taggedValue` """ def invariant(checker_function): """ Attach an invariant checker function to an interface while defining it. Invariants can later be validated against particular implementations by calling :meth:`IInterface.validateInvariants`. For example:: def check_range(ob): if ob.max < ob.min: raise ValueError("max value is less than min value") class IRange(Interface): min = Attribute("The min value") max = Attribute("The max value") invariant(check_range) .. seealso:: `zope.interface.invariant` """ def interfacemethod(method): """ A decorator that transforms a method specification into an implementation method. This is used to override methods of ``Interface`` or provide new methods. Definitions using this decorator will not appear in :meth:`IInterface.names()`. It is possible to have an implementation method and a method specification of the same name. For example:: class IRange(Interface): @interfacemethod def __adapt__(self, obj): if isinstance(obj, range): # Return the builtin ``range`` as-is return obj return super(type(IRange), self).__adapt__(obj) You can use ``super`` to call the parent class functionality. Note that the zero-argument version (``super().__adapt__``) works on Python 3.6 and above, but prior to that the two-argument version must be used, and the class must be explicitly passed as the first argument. .. versionadded:: 5.1.0 .. seealso:: `zope.interface.interfacemethod` """ ### # Querying interfaces ### def providedBy(ob): """ Return the interfaces provided by an object. This is the union of the interfaces directly provided by an object and interfaces implemented by it's class. The value returned is an `IDeclaration`. .. seealso:: `zope.interface.providedBy` """ def implementedBy(class_): """ Return the interfaces implemented for a class's instances. The value returned is an `IDeclaration`. .. seealso:: `zope.interface.implementedBy` """ ### # Declaring interfaces ### def classImplements(class_, *interfaces): """ Declare additional interfaces implemented for instances of a class. The arguments after the class are one or more interfaces or interface specifications (`IDeclaration` objects). The interfaces given (including the interfaces in the specifications) are added to any interfaces previously declared. Consider the following example:: class C(A, B): ... classImplements(C, I1, I2) Instances of ``C`` provide ``I1``, ``I2``, and whatever interfaces instances of ``A`` and ``B`` provide. This is equivalent to:: @implementer(I1, I2) class C(A, B): pass .. seealso:: `zope.interface.classImplements` .. seealso:: `zope.interface.implementer` """ def classImplementsFirst(cls, interface): """ See :func:`zope.interface.classImplementsFirst`. """ def implementer(*interfaces): """ Create a decorator for declaring interfaces implemented by a factory. A callable is returned that makes an implements declaration on objects passed to it. .. seealso:: :meth:`classImplements` """ def classImplementsOnly(class_, *interfaces): """ Declare the only interfaces implemented by instances of a class. The arguments after the class are one or more interfaces or interface specifications (`IDeclaration` objects). The interfaces given (including the interfaces in the specifications) replace any previous declarations. Consider the following example:: class C(A, B): ... classImplements(C, IA, IB. IC) classImplementsOnly(C. I1, I2) Instances of ``C`` provide only ``I1``, ``I2``, and regardless of whatever interfaces instances of ``A`` and ``B`` implement. .. seealso:: `zope.interface.classImplementsOnly` """ def implementer_only(*interfaces): """ Create a decorator for declaring the only interfaces implemented. A callable is returned that makes an implements declaration on objects passed to it. .. seealso:: `zope.interface.implementer_only` """ def directlyProvidedBy(object): # pylint:disable=redefined-builtin """ Return the interfaces directly provided by the given object. The value returned is an `IDeclaration`. .. seealso:: `zope.interface.directlyProvidedBy` """ def directlyProvides(object, *interfaces): # pylint:disable=redefined-builtin """ Declare interfaces declared directly for an object. The arguments after the object are one or more interfaces or interface specifications (`IDeclaration` objects). .. caution:: The interfaces given (including the interfaces in the specifications) *replace* interfaces previously declared for the object. See :meth:`alsoProvides` to add additional interfaces. Consider the following example:: class C(A, B): ... ob = C() directlyProvides(ob, I1, I2) The object, ``ob`` provides ``I1``, ``I2``, and whatever interfaces instances have been declared for instances of ``C``. To remove directly provided interfaces, use `directlyProvidedBy` and subtract the unwanted interfaces. For example:: directlyProvides(ob, directlyProvidedBy(ob)-I2) removes I2 from the interfaces directly provided by ``ob``. The object, ``ob`` no longer directly provides ``I2``, although it might still provide ``I2`` if it's class implements ``I2``. To add directly provided interfaces, use `directlyProvidedBy` and include additional interfaces. For example:: directlyProvides(ob, directlyProvidedBy(ob), I2) adds I2 to the interfaces directly provided by ob. .. seealso:: `zope.interface.directlyProvides` """ def alsoProvides(object, *interfaces): # pylint:disable=redefined-builtin """ Declare additional interfaces directly for an object. For example:: alsoProvides(ob, I1) is equivalent to:: directlyProvides(ob, directlyProvidedBy(ob), I1) .. seealso:: `zope.interface.alsoProvides` """ def noLongerProvides(object, interface): # pylint:disable=redefined-builtin """ Remove an interface from the list of an object's directly provided interfaces. For example:: noLongerProvides(ob, I1) is equivalent to:: directlyProvides(ob, directlyProvidedBy(ob) - I1) with the exception that if ``I1`` is an interface that is provided by ``ob`` through the class's implementation, `ValueError` is raised. .. seealso:: `zope.interface.noLongerProvides` """ def provider(*interfaces): """ Declare interfaces provided directly by a class. .. seealso:: `zope.interface.provider` """ def moduleProvides(*interfaces): """ Declare interfaces provided by a module. This function is used in a module definition. The arguments are one or more interfaces or interface specifications (`IDeclaration` objects). The given interfaces (including the interfaces in the specifications) are used to create the module's direct-object interface specification. An error will be raised if the module already has an interface specification. In other words, it is an error to call this function more than once in a module definition. This function is provided for convenience. It provides a more convenient way to call `directlyProvides` for a module. For example:: moduleImplements(I1) is equivalent to:: directlyProvides(sys.modules[__name__], I1) .. seealso:: `zope.interface.moduleProvides` """ def Declaration(*interfaces): """ Create an interface specification. The arguments are one or more interfaces or interface specifications (`IDeclaration` objects). A new interface specification (`IDeclaration`) with the given interfaces is returned. .. seealso:: `zope.interface.Declaration` """ class IAdapterRegistry(Interface): """Provide an interface-based registry for adapters This registry registers objects that are in some sense "from" a sequence of specification to an interface and a name. No specific semantics are assumed for the registered objects, however, the most common application will be to register factories that adapt objects providing required specifications to a provided interface. """ def register(required, provided, name, value): """Register a value A value is registered for a *sequence* of required specifications, a provided interface, and a name, which must be text. """ def registered(required, provided, name=''): """Return the component registered for the given interfaces and name name must be text. Unlike the lookup method, this methods won't retrieve components registered for more specific required interfaces or less specific provided interfaces. If no component was registered exactly for the given interfaces and name, then None is returned. """ def lookup(required, provided, name='', default=None): """Lookup a value A value is looked up based on a *sequence* of required specifications, a provided interface, and a name, which must be text. """ def queryMultiAdapter(objects, provided, name='', default=None): """Adapt a sequence of objects to a named, provided, interface """ def lookup1(required, provided, name='', default=None): """Lookup a value using a single required interface A value is looked up based on a single required specifications, a provided interface, and a name, which must be text. """ def queryAdapter(object, provided, name='', default=None): # pylint:disable=redefined-builtin """Adapt an object using a registered adapter factory. """ def adapter_hook(provided, object, name='', default=None): # pylint:disable=redefined-builtin """Adapt an object using a registered adapter factory. name must be text. """ def lookupAll(required, provided): """Find all adapters from the required to the provided interfaces An iterable object is returned that provides name-value two-tuples. """ def names(required, provided): # pylint:disable=arguments-differ """Return the names for which there are registered objects """ def subscribe(required, provided, subscriber): # pylint:disable=arguments-differ """Register a subscriber A subscriber is registered for a *sequence* of required specifications, a provided interface, and a name. Multiple subscribers may be registered for the same (or equivalent) interfaces. .. versionchanged:: 5.1.1 Correct the method signature to remove the ``name`` parameter. Subscribers have no names. """ def subscribed(required, provided, subscriber): """ Check whether the object *subscriber* is registered directly with this object via a previous call to ``subscribe(required, provided, subscriber)``. If the *subscriber*, or one equal to it, has been subscribed, for the given *required* sequence and *provided* interface, return that object. (This does not guarantee whether the *subscriber* itself is returned, or an object equal to it.) If it has not, return ``None``. Unlike :meth:`subscriptions`, this method won't retrieve components registered for more specific required interfaces or less specific provided interfaces. .. versionadded:: 5.3.0 """ def subscriptions(required, provided): """ Get a sequence of subscribers. Subscribers for a sequence of *required* interfaces, and a *provided* interface are returned. This takes into account subscribers registered with this object, as well as those registered with base adapter registries in the resolution order, and interfaces that extend *provided*. .. versionchanged:: 5.1.1 Correct the method signature to remove the ``name`` parameter. Subscribers have no names. """ def subscribers(objects, provided): """ Get a sequence of subscription **adapters**. This is like :meth:`subscriptions`, but calls the returned subscribers with *objects* (and optionally returns the results of those calls), instead of returning the subscribers directly. :param objects: A sequence of objects; they will be used to determine the *required* argument to :meth:`subscriptions`. :param provided: A single interface, or ``None``, to pass as the *provided* parameter to :meth:`subscriptions`. If an interface is given, the results of calling each returned subscriber with the the *objects* are collected and returned from this method; each result should be an object implementing the *provided* interface. If ``None``, the resulting subscribers are still called, but the results are ignored. :return: A sequence of the results of calling the subscribers if *provided* is not ``None``. If there are no registered subscribers, or *provided* is ``None``, this will be an empty sequence. .. versionchanged:: 5.1.1 Correct the method signature to remove the ``name`` parameter. Subscribers have no names. """ # begin formerly in zope.component class ComponentLookupError(LookupError): """A component could not be found.""" class Invalid(Exception): """A component doesn't satisfy a promise.""" class IObjectEvent(Interface): """An event related to an object. The object that generated this event is not necessarily the object referred to by location. """ object = Attribute("The subject of the event.") @implementer(IObjectEvent) class ObjectEvent: def __init__(self, object): # pylint:disable=redefined-builtin self.object = object class IComponentLookup(Interface): """Component Manager for a Site This object manages the components registered at a particular site. The definition of a site is intentionally vague. """ adapters = Attribute( "Adapter Registry to manage all registered adapters.") utilities = Attribute( "Adapter Registry to manage all registered utilities.") def queryAdapter(object, interface, name='', default=None): # pylint:disable=redefined-builtin """Look for a named adapter to an interface for an object If a matching adapter cannot be found, returns the default. """ def getAdapter(object, interface, name=''): # pylint:disable=redefined-builtin """Look for a named adapter to an interface for an object If a matching adapter cannot be found, a `ComponentLookupError` is raised. """ def queryMultiAdapter(objects, interface, name='', default=None): """Look for a multi-adapter to an interface for multiple objects If a matching adapter cannot be found, returns the default. """ def getMultiAdapter(objects, interface, name=''): """Look for a multi-adapter to an interface for multiple objects If a matching adapter cannot be found, a `ComponentLookupError` is raised. """ def getAdapters(objects, provided): """Look for all matching adapters to a provided interface for objects Return an iterable of name-adapter pairs for adapters that provide the given interface. """ def subscribers(objects, provided): """Get subscribers Subscribers are returned that provide the provided interface and that depend on and are computed from the sequence of required objects. """ def handle(*objects): """Call handlers for the given objects Handlers registered for the given objects are called. """ def queryUtility(interface, name='', default=None): """Look up a utility that provides an interface. If one is not found, returns default. """ def getUtilitiesFor(interface): """Look up the registered utilities that provide an interface. Returns an iterable of name-utility pairs. """ def getAllUtilitiesRegisteredFor(interface): """Return all registered utilities for an interface This includes overridden utilities. An iterable of utility instances is returned. No names are returned. """ class IRegistration(Interface): """A registration-information object """ registry = Attribute("The registry having the registration") name = Attribute("The registration name") info = Attribute("""Information about the registration This is information deemed useful to people browsing the configuration of a system. It could, for example, include commentary or information about the source of the configuration. """) class IUtilityRegistration(IRegistration): """Information about the registration of a utility """ factory = Attribute("The factory used to create the utility. Optional.") component = Attribute("The object registered") provided = Attribute("The interface provided by the component") class _IBaseAdapterRegistration(IRegistration): """Information about the registration of an adapter """ factory = Attribute("The factory used to create adapters") required = Attribute("""The adapted interfaces This is a sequence of interfaces adapters by the registered factory. The factory will be caled with a sequence of objects, as positional arguments, that provide these interfaces. """) provided = Attribute("""The interface provided by the adapters. This interface is implemented by the factory """) class IAdapterRegistration(_IBaseAdapterRegistration): """Information about the registration of an adapter """ class ISubscriptionAdapterRegistration(_IBaseAdapterRegistration): """Information about the registration of a subscription adapter """ class IHandlerRegistration(IRegistration): handler = Attribute("An object called used to handle an event") required = Attribute("""The handled interfaces This is a sequence of interfaces handled by the registered handler. The handler will be caled with a sequence of objects, as positional arguments, that provide these interfaces. """) class IRegistrationEvent(IObjectEvent): """An event that involves a registration""" @implementer(IRegistrationEvent) class RegistrationEvent(ObjectEvent): """There has been a change in a registration """ def __repr__(self): return "{} event:\n{!r}".format(self.__class__.__name__, self.object) class IRegistered(IRegistrationEvent): """A component or factory was registered """ @implementer(IRegistered) class Registered(RegistrationEvent): pass class IUnregistered(IRegistrationEvent): """A component or factory was unregistered """ @implementer(IUnregistered) class Unregistered(RegistrationEvent): """A component or factory was unregistered """ class IComponentRegistry(Interface): """Register components """ def registerUtility(component=None, provided=None, name='', info='', factory=None): """Register a utility :param factory: Factory for the component to be registered. :param component: The registered component :param provided: This is the interface provided by the utility. If the component provides a single interface, then this argument is optional and the component-implemented interface will be used. :param name: The utility name. :param info: An object that can be converted to a string to provide information about the registration. Only one of *component* and *factory* can be used. A `IRegistered` event is generated with an `IUtilityRegistration`. """ def unregisterUtility(component=None, provided=None, name='', factory=None): """Unregister a utility :returns: A boolean is returned indicating whether the registry was changed. If the given *component* is None and there is no component registered, or if the given *component* is not None and is not registered, then the function returns False, otherwise it returns True. :param factory: Factory for the component to be unregistered. :param component: The registered component The given component can be None, in which case any component registered to provide the given provided interface with the given name is unregistered. :param provided: This is the interface provided by the utility. If the component is not None and provides a single interface, then this argument is optional and the component-implemented interface will be used. :param name: The utility name. Only one of *component* and *factory* can be used. An `IUnregistered` event is generated with an `IUtilityRegistration`. """ def registeredUtilities(): """Return an iterable of `IUtilityRegistration` instances. These registrations describe the current utility registrations in the object. """ def registerAdapter(factory, required=None, provided=None, name='', info=''): """Register an adapter factory :param factory: The object used to compute the adapter :param required: This is a sequence of specifications for objects to be adapted. If omitted, then the value of the factory's ``__component_adapts__`` attribute will be used. The ``__component_adapts__`` attribute is normally set in class definitions using the `.adapter` decorator. If the factory doesn't have a ``__component_adapts__`` adapts attribute, then this argument is required. :param provided: This is the interface provided by the adapter and implemented by the factory. If the factory implements a single interface, then this argument is optional and the factory-implemented interface will be used. :param name: The adapter name. :param info: An object that can be converted to a string to provide information about the registration. A `IRegistered` event is generated with an `IAdapterRegistration`. """ def unregisterAdapter(factory=None, required=None, provided=None, name=''): """Unregister an adapter factory :returns: A boolean is returned indicating whether the registry was changed. If the given component is None and there is no component registered, or if the given component is not None and is not registered, then the function returns False, otherwise it returns True. :param factory: This is the object used to compute the adapter. The factory can be None, in which case any factory registered to implement the given provided interface for the given required specifications with the given name is unregistered. :param required: This is a sequence of specifications for objects to be adapted. If the factory is not None and the required arguments is omitted, then the value of the factory's __component_adapts__ attribute will be used. The __component_adapts__ attribute attribute is normally set in class definitions using adapts function, or for callables using the adapter decorator. If the factory is None or doesn't have a __component_adapts__ adapts attribute, then this argument is required. :param provided: This is the interface provided by the adapter and implemented by the factory. If the factory is not None and implements a single interface, then this argument is optional and the factory-implemented interface will be used. :param name: The adapter name. An `IUnregistered` event is generated with an `IAdapterRegistration`. """ def registeredAdapters(): """Return an iterable of `IAdapterRegistration` instances. These registrations describe the current adapter registrations in the object. """ def registerSubscriptionAdapter(factory, required=None, provides=None, name='', info=''): """Register a subscriber factory :param factory: The object used to compute the adapter :param required: This is a sequence of specifications for objects to be adapted. If omitted, then the value of the factory's ``__component_adapts__`` attribute will be used. The ``__component_adapts__`` attribute is normally set using the adapter decorator. If the factory doesn't have a ``__component_adapts__`` adapts attribute, then this argument is required. :param provided: This is the interface provided by the adapter and implemented by the factory. If the factory implements a single interface, then this argument is optional and the factory-implemented interface will be used. :param name: The adapter name. Currently, only the empty string is accepted. Other strings will be accepted in the future when support for named subscribers is added. :param info: An object that can be converted to a string to provide information about the registration. A `IRegistered` event is generated with an `ISubscriptionAdapterRegistration`. """ def unregisterSubscriptionAdapter(factory=None, required=None, provides=None, name=''): """Unregister a subscriber factory. :returns: A boolean is returned indicating whether the registry was changed. If the given component is None and there is no component registered, or if the given component is not None and is not registered, then the function returns False, otherwise it returns True. :param factory: This is the object used to compute the adapter. The factory can be None, in which case any factories registered to implement the given provided interface for the given required specifications with the given name are unregistered. :param required: This is a sequence of specifications for objects to be adapted. If omitted, then the value of the factory's ``__component_adapts__`` attribute will be used. The ``__component_adapts__`` attribute is normally set using the adapter decorator. If the factory doesn't have a ``__component_adapts__`` adapts attribute, then this argument is required. :param provided: This is the interface provided by the adapter and implemented by the factory. If the factory is not None implements a single interface, then this argument is optional and the factory-implemented interface will be used. :param name: The adapter name. Currently, only the empty string is accepted. Other strings will be accepted in the future when support for named subscribers is added. An `IUnregistered` event is generated with an `ISubscriptionAdapterRegistration`. """ def registeredSubscriptionAdapters(): """Return an iterable of `ISubscriptionAdapterRegistration` instances. These registrations describe the current subscription adapter registrations in the object. """ def registerHandler(handler, required=None, name='', info=''): """Register a handler. A handler is a subscriber that doesn't compute an adapter but performs some function when called. :param handler: The object used to handle some event represented by the objects passed to it. :param required: This is a sequence of specifications for objects to be adapted. If omitted, then the value of the factory's ``__component_adapts__`` attribute will be used. The ``__component_adapts__`` attribute is normally set using the adapter decorator. If the factory doesn't have a ``__component_adapts__`` adapts attribute, then this argument is required. :param name: The handler name. Currently, only the empty string is accepted. Other strings will be accepted in the future when support for named handlers is added. :param info: An object that can be converted to a string to provide information about the registration. A `IRegistered` event is generated with an `IHandlerRegistration`. """ def unregisterHandler(handler=None, required=None, name=''): """Unregister a handler. A handler is a subscriber that doesn't compute an adapter but performs some function when called. :returns: A boolean is returned indicating whether the registry was changed. :param handler: This is the object used to handle some event represented by the objects passed to it. The handler can be None, in which case any handlers registered for the given required specifications with the given are unregistered. :param required: This is a sequence of specifications for objects to be adapted. If omitted, then the value of the factory's ``__component_adapts__`` attribute will be used. The ``__component_adapts__`` attribute is normally set using the adapter decorator. If the factory doesn't have a ``__component_adapts__`` adapts attribute, then this argument is required. :param name: The handler name. Currently, only the empty string is accepted. Other strings will be accepted in the future when support for named handlers is added. An `IUnregistered` event is generated with an `IHandlerRegistration`. """ def registeredHandlers(): """Return an iterable of `IHandlerRegistration` instances. These registrations describe the current handler registrations in the object. """ class IComponents(IComponentLookup, IComponentRegistry): """Component registration and access """ # end formerly in zope.component zope.interface-6.4/src/zope/interface/registry.py000066400000000000000000000623451462121350100222320ustar00rootroot00000000000000############################################################################## # # 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. # ############################################################################## """Basic components support """ from collections import defaultdict try: from zope.event import notify except ImportError: # pragma: no cover def notify(*arg, **kw): pass from zope.interface.adapter import AdapterRegistry from zope.interface.declarations import implementedBy from zope.interface.declarations import implementer from zope.interface.declarations import implementer_only from zope.interface.declarations import providedBy from zope.interface.interface import Interface from zope.interface.interfaces import ComponentLookupError from zope.interface.interfaces import IAdapterRegistration from zope.interface.interfaces import IComponents from zope.interface.interfaces import IHandlerRegistration from zope.interface.interfaces import ISpecification from zope.interface.interfaces import ISubscriptionAdapterRegistration from zope.interface.interfaces import IUtilityRegistration from zope.interface.interfaces import Registered from zope.interface.interfaces import Unregistered __all__ = [ # Components is public API, but # the *Registration classes are just implementations # of public interfaces. 'Components', ] class _UnhashableComponentCounter: # defaultdict(int)-like object for unhashable components def __init__(self, otherdict): # [(component, count)] self._data = [item for item in otherdict.items()] def __getitem__(self, key): for component, count in self._data: if component == key: return count return 0 def __setitem__(self, component, count): for i, data in enumerate(self._data): if data[0] == component: self._data[i] = component, count return self._data.append((component, count)) def __delitem__(self, component): for i, data in enumerate(self._data): if data[0] == component: del self._data[i] return raise KeyError(component) # pragma: no cover def _defaultdict_int(): return defaultdict(int) class _UtilityRegistrations: def __init__(self, utilities, utility_registrations): # {provided -> {component: count}} self._cache = defaultdict(_defaultdict_int) self._utilities = utilities self._utility_registrations = utility_registrations self.__populate_cache() def __populate_cache(self): for ((p, _), data) in iter(self._utility_registrations.items()): component = data[0] self.__cache_utility(p, component) def __cache_utility(self, provided, component): try: self._cache[provided][component] += 1 except TypeError: # The component is not hashable, and we have a dict. Switch to a strategy # that doesn't use hashing. prov = self._cache[provided] = _UnhashableComponentCounter(self._cache[provided]) prov[component] += 1 def __uncache_utility(self, provided, component): provided = self._cache[provided] # It seems like this line could raise a TypeError if component isn't # hashable and we haven't yet switched to _UnhashableComponentCounter. However, # we can't actually get in that situation. In order to get here, we would # have had to cache the utility already which would have switched # the datastructure if needed. count = provided[component] count -= 1 if count == 0: del provided[component] else: provided[component] = count return count > 0 def _is_utility_subscribed(self, provided, component): try: return self._cache[provided][component] > 0 except TypeError: # Not hashable and we're still using a dict return False def registerUtility(self, provided, name, component, info, factory): subscribed = self._is_utility_subscribed(provided, component) self._utility_registrations[(provided, name)] = component, info, factory self._utilities.register((), provided, name, component) if not subscribed: self._utilities.subscribe((), provided, component) self.__cache_utility(provided, component) def unregisterUtility(self, provided, name, component): del self._utility_registrations[(provided, name)] self._utilities.unregister((), provided, name) subscribed = self.__uncache_utility(provided, component) if not subscribed: self._utilities.unsubscribe((), provided, component) @implementer(IComponents) class Components: _v_utility_registrations_cache = None def __init__(self, name='', bases=()): # __init__ is used for test cleanup as well as initialization. # XXX add a separate API for test cleanup. assert isinstance(name, str) self.__name__ = name self._init_registries() self._init_registrations() self.__bases__ = tuple(bases) self._v_utility_registrations_cache = None def __repr__(self): return "<{} {}>".format(self.__class__.__name__, self.__name__) def __reduce__(self): # Mimic what a persistent.Persistent object does and elide # _v_ attributes so that they don't get saved in ZODB. # This allows us to store things that cannot be pickled in such # attributes. reduction = super().__reduce__() # (callable, args, state, listiter, dictiter) # We assume the state is always a dict; the last three items # are technically optional and can be missing or None. filtered_state = {k: v for k, v in reduction[2].items() if not k.startswith('_v_')} reduction = list(reduction) reduction[2] = filtered_state return tuple(reduction) def _init_registries(self): # Subclasses have never been required to call this method # if they override it, merely to fill in these two attributes. self.adapters = AdapterRegistry() self.utilities = AdapterRegistry() def _init_registrations(self): self._utility_registrations = {} self._adapter_registrations = {} self._subscription_registrations = [] self._handler_registrations = [] @property def _utility_registrations_cache(self): # We use a _v_ attribute internally so that data aren't saved in ZODB, # because this object cannot be pickled. cache = self._v_utility_registrations_cache if (cache is None or cache._utilities is not self.utilities or cache._utility_registrations is not self._utility_registrations): cache = self._v_utility_registrations_cache = _UtilityRegistrations( self.utilities, self._utility_registrations) return cache def _getBases(self): # Subclasses might override return self.__dict__.get('__bases__', ()) def _setBases(self, bases): # Subclasses might override self.adapters.__bases__ = tuple([ base.adapters for base in bases]) self.utilities.__bases__ = tuple([ base.utilities for base in bases]) self.__dict__['__bases__'] = tuple(bases) __bases__ = property( lambda self: self._getBases(), lambda self, bases: self._setBases(bases), ) def registerUtility(self, component=None, provided=None, name='', info='', event=True, factory=None): if factory: if component: raise TypeError("Can't specify factory and component.") component = factory() if provided is None: provided = _getUtilityProvided(component) if name == '': name = _getName(component) reg = self._utility_registrations.get((provided, name)) if reg is not None: if reg[:2] == (component, info): # already registered return self.unregisterUtility(reg[0], provided, name) self._utility_registrations_cache.registerUtility( provided, name, component, info, factory) if event: notify(Registered( UtilityRegistration(self, provided, name, component, info, factory) )) def unregisterUtility(self, component=None, provided=None, name='', factory=None): if factory: if component: raise TypeError("Can't specify factory and component.") component = factory() if provided is None: if component is None: raise TypeError("Must specify one of component, factory and " "provided") provided = _getUtilityProvided(component) old = self._utility_registrations.get((provided, name)) if (old is None) or ((component is not None) and (component != old[0])): return False if component is None: component = old[0] # Note that component is now the old thing registered self._utility_registrations_cache.unregisterUtility( provided, name, component) notify(Unregistered( UtilityRegistration(self, provided, name, component, *old[1:]) )) return True def registeredUtilities(self): for ((provided, name), data ) in iter(self._utility_registrations.items()): yield UtilityRegistration(self, provided, name, *data) def queryUtility(self, provided, name='', default=None): return self.utilities.lookup((), provided, name, default) def getUtility(self, provided, name=''): utility = self.utilities.lookup((), provided, name) if utility is None: raise ComponentLookupError(provided, name) return utility def getUtilitiesFor(self, interface): yield from self.utilities.lookupAll((), interface) def getAllUtilitiesRegisteredFor(self, interface): return self.utilities.subscriptions((), interface) def registerAdapter(self, factory, required=None, provided=None, name='', info='', event=True): if provided is None: provided = _getAdapterProvided(factory) required = _getAdapterRequired(factory, required) if name == '': name = _getName(factory) self._adapter_registrations[(required, provided, name) ] = factory, info self.adapters.register(required, provided, name, factory) if event: notify(Registered( AdapterRegistration(self, required, provided, name, factory, info) )) def unregisterAdapter(self, factory=None, required=None, provided=None, name='', ): if provided is None: if factory is None: raise TypeError("Must specify one of factory and provided") provided = _getAdapterProvided(factory) if (required is None) and (factory is None): raise TypeError("Must specify one of factory and required") required = _getAdapterRequired(factory, required) old = self._adapter_registrations.get((required, provided, name)) if (old is None) or ((factory is not None) and (factory != old[0])): return False del self._adapter_registrations[(required, provided, name)] self.adapters.unregister(required, provided, name) notify(Unregistered( AdapterRegistration(self, required, provided, name, *old) )) return True def registeredAdapters(self): for ((required, provided, name), (component, info) ) in iter(self._adapter_registrations.items()): yield AdapterRegistration(self, required, provided, name, component, info) def queryAdapter(self, object, interface, name='', default=None): return self.adapters.queryAdapter(object, interface, name, default) def getAdapter(self, object, interface, name=''): adapter = self.adapters.queryAdapter(object, interface, name) if adapter is None: raise ComponentLookupError(object, interface, name) return adapter def queryMultiAdapter(self, objects, interface, name='', default=None): return self.adapters.queryMultiAdapter( objects, interface, name, default) def getMultiAdapter(self, objects, interface, name=''): adapter = self.adapters.queryMultiAdapter(objects, interface, name) if adapter is None: raise ComponentLookupError(objects, interface, name) return adapter def getAdapters(self, objects, provided): for name, factory in self.adapters.lookupAll( list(map(providedBy, objects)), provided): adapter = factory(*objects) if adapter is not None: yield name, adapter def registerSubscriptionAdapter(self, factory, required=None, provided=None, name='', info='', event=True): if name: raise TypeError("Named subscribers are not yet supported") if provided is None: provided = _getAdapterProvided(factory) required = _getAdapterRequired(factory, required) self._subscription_registrations.append( (required, provided, name, factory, info) ) self.adapters.subscribe(required, provided, factory) if event: notify(Registered( SubscriptionRegistration(self, required, provided, name, factory, info) )) def registeredSubscriptionAdapters(self): for data in self._subscription_registrations: yield SubscriptionRegistration(self, *data) def unregisterSubscriptionAdapter(self, factory=None, required=None, provided=None, name='', ): if name: raise TypeError("Named subscribers are not yet supported") if provided is None: if factory is None: raise TypeError("Must specify one of factory and provided") provided = _getAdapterProvided(factory) if (required is None) and (factory is None): raise TypeError("Must specify one of factory and required") required = _getAdapterRequired(factory, required) if factory is None: new = [(r, p, n, f, i) for (r, p, n, f, i) in self._subscription_registrations if not (r == required and p == provided) ] else: new = [(r, p, n, f, i) for (r, p, n, f, i) in self._subscription_registrations if not (r == required and p == provided and f == factory) ] if len(new) == len(self._subscription_registrations): return False self._subscription_registrations[:] = new self.adapters.unsubscribe(required, provided, factory) notify(Unregistered( SubscriptionRegistration(self, required, provided, name, factory, '') )) return True def subscribers(self, objects, provided): return self.adapters.subscribers(objects, provided) def registerHandler(self, factory, required=None, name='', info='', event=True): if name: raise TypeError("Named handlers are not yet supported") required = _getAdapterRequired(factory, required) self._handler_registrations.append( (required, name, factory, info) ) self.adapters.subscribe(required, None, factory) if event: notify(Registered( HandlerRegistration(self, required, name, factory, info) )) def registeredHandlers(self): for data in self._handler_registrations: yield HandlerRegistration(self, *data) def unregisterHandler(self, factory=None, required=None, name=''): if name: raise TypeError("Named subscribers are not yet supported") if (required is None) and (factory is None): raise TypeError("Must specify one of factory and required") required = _getAdapterRequired(factory, required) if factory is None: new = [(r, n, f, i) for (r, n, f, i) in self._handler_registrations if r != required ] else: new = [(r, n, f, i) for (r, n, f, i) in self._handler_registrations if not (r == required and f == factory) ] if len(new) == len(self._handler_registrations): return False self._handler_registrations[:] = new self.adapters.unsubscribe(required, None, factory) notify(Unregistered( HandlerRegistration(self, required, name, factory, '') )) return True def handle(self, *objects): self.adapters.subscribers(objects, None) def rebuildUtilityRegistryFromLocalCache(self, rebuild=False): """ Emergency maintenance method to rebuild the ``.utilities`` registry from the local copy maintained in this object, or detect the need to do so. Most users will never need to call this, but it can be helpful in the event of suspected corruption. By default, this method only checks for corruption. To make it actually rebuild the registry, pass `True` for *rebuild*. :param bool rebuild: If set to `True` (not the default), this method will actually register and subscribe utilities in the registry as needed to synchronize with the local cache. :return: A dictionary that's meant as diagnostic data. The keys and values may change over time. When called with a false *rebuild*, the keys ``"needed_registered"`` and ``"needed_subscribed"`` will be non-zero if any corruption was detected, but that will not be corrected. .. versionadded:: 5.3.0 """ regs = dict(self._utility_registrations) utils = self.utilities needed_registered = 0 did_not_register = 0 needed_subscribed = 0 did_not_subscribe = 0 # Avoid the expensive change process during this; we'll call # it once at the end if needed. assert 'changed' not in utils.__dict__ utils.changed = lambda _: None if rebuild: register = utils.register subscribe = utils.subscribe else: register = subscribe = lambda *args: None try: for (provided, name), (value, _info, _factory) in regs.items(): if utils.registered((), provided, name) != value: register((), provided, name, value) needed_registered += 1 else: did_not_register += 1 if utils.subscribed((), provided, value) is None: needed_subscribed += 1 subscribe((), provided, value) else: did_not_subscribe += 1 finally: del utils.changed if rebuild and (needed_subscribed or needed_registered): utils.changed(utils) return { 'needed_registered': needed_registered, 'did_not_register': did_not_register, 'needed_subscribed': needed_subscribed, 'did_not_subscribe': did_not_subscribe } def _getName(component): try: return component.__component_name__ except AttributeError: return '' def _getUtilityProvided(component): provided = list(providedBy(component)) if len(provided) == 1: return provided[0] raise TypeError( "The utility doesn't provide a single interface " "and no provided interface was specified.") def _getAdapterProvided(factory): provided = list(implementedBy(factory)) if len(provided) == 1: return provided[0] raise TypeError( "The adapter factory doesn't implement a single interface " "and no provided interface was specified.") def _getAdapterRequired(factory, required): if required is None: try: required = factory.__component_adapts__ except AttributeError: raise TypeError( "The adapter factory doesn't have a __component_adapts__ " "attribute and no required specifications were specified" ) elif ISpecification.providedBy(required): raise TypeError("the required argument should be a list of " "interfaces, not a single interface") result = [] for r in required: if r is None: r = Interface elif not ISpecification.providedBy(r): if isinstance(r, type): r = implementedBy(r) else: raise TypeError("Required specification must be a " "specification or class, not %r" % type(r) ) result.append(r) return tuple(result) @implementer(IUtilityRegistration) class UtilityRegistration: def __init__(self, registry, provided, name, component, doc, factory=None): (self.registry, self.provided, self.name, self.component, self.info, self.factory ) = registry, provided, name, component, doc, factory def __repr__(self): return '{}({!r}, {}, {!r}, {}, {!r}, {!r})'.format( self.__class__.__name__, self.registry, getattr(self.provided, '__name__', None), self.name, getattr(self.component, '__name__', repr(self.component)), self.factory, self.info, ) def __hash__(self): return id(self) def __eq__(self, other): return repr(self) == repr(other) def __ne__(self, other): return repr(self) != repr(other) def __lt__(self, other): return repr(self) < repr(other) def __le__(self, other): return repr(self) <= repr(other) def __gt__(self, other): return repr(self) > repr(other) def __ge__(self, other): return repr(self) >= repr(other) @implementer(IAdapterRegistration) class AdapterRegistration: def __init__(self, registry, required, provided, name, component, doc): (self.registry, self.required, self.provided, self.name, self.factory, self.info ) = registry, required, provided, name, component, doc def __repr__(self): return '{}({!r}, {}, {}, {!r}, {}, {!r})'.format( self.__class__.__name__, self.registry, '[' + ", ".join([r.__name__ for r in self.required]) + ']', getattr(self.provided, '__name__', None), self.name, getattr(self.factory, '__name__', repr(self.factory)), self.info, ) def __hash__(self): return id(self) def __eq__(self, other): return repr(self) == repr(other) def __ne__(self, other): return repr(self) != repr(other) def __lt__(self, other): return repr(self) < repr(other) def __le__(self, other): return repr(self) <= repr(other) def __gt__(self, other): return repr(self) > repr(other) def __ge__(self, other): return repr(self) >= repr(other) @implementer_only(ISubscriptionAdapterRegistration) class SubscriptionRegistration(AdapterRegistration): pass @implementer_only(IHandlerRegistration) class HandlerRegistration(AdapterRegistration): def __init__(self, registry, required, name, handler, doc): (self.registry, self.required, self.name, self.handler, self.info ) = registry, required, name, handler, doc @property def factory(self): return self.handler provided = None def __repr__(self): return '{}({!r}, {}, {!r}, {}, {!r})'.format( self.__class__.__name__, self.registry, '[' + ", ".join([r.__name__ for r in self.required]) + ']', self.name, getattr(self.factory, '__name__', repr(self.factory)), self.info, ) zope.interface-6.4/src/zope/interface/ro.py000066400000000000000000000571351462121350100210030ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2003 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. # ############################################################################## """ Compute a resolution order for an object and its bases. .. versionchanged:: 5.0 The resolution order is now based on the same C3 order that Python uses for classes. In complex instances of multiple inheritance, this may result in a different ordering. In older versions, the ordering wasn't required to be C3 compliant, and for backwards compatibility, it still isn't. If the ordering isn't C3 compliant (if it is *inconsistent*), zope.interface will make a best guess to try to produce a reasonable resolution order. Still (just as before), the results in such cases may be surprising. .. rubric:: Environment Variables Due to the change in 5.0, certain environment variables can be used to control errors and warnings about inconsistent resolution orders. They are listed in priority order, with variables at the bottom generally overriding variables above them. ZOPE_INTERFACE_WARN_BAD_IRO If this is set to "1", then if there is at least one inconsistent resolution order discovered, a warning (:class:`InconsistentResolutionOrderWarning`) will be issued. Use the usual warning mechanisms to control this behaviour. The warning text will contain additional information on debugging. ZOPE_INTERFACE_TRACK_BAD_IRO If this is set to "1", then zope.interface will log information about each inconsistent resolution order discovered, and keep those details in memory in this module for later inspection. ZOPE_INTERFACE_STRICT_IRO If this is set to "1", any attempt to use :func:`ro` that would produce a non-C3 ordering will fail by raising :class:`InconsistentResolutionOrderError`. .. important:: ``ZOPE_INTERFACE_STRICT_IRO`` is intended to become the default in the future. There are two environment variables that are independent. ZOPE_INTERFACE_LOG_CHANGED_IRO If this is set to "1", then if the C3 resolution order is different from the legacy resolution order for any given object, a message explaining the differences will be logged. This is intended to be used for debugging complicated IROs. ZOPE_INTERFACE_USE_LEGACY_IRO If this is set to "1", then the C3 resolution order will *not* be used. The legacy IRO will be used instead. This is a temporary measure and will be removed in the future. It is intended to help during the transition. It implies ``ZOPE_INTERFACE_LOG_CHANGED_IRO``. .. rubric:: Debugging Behaviour Changes in zope.interface 5 Most behaviour changes from zope.interface 4 to 5 are related to inconsistent resolution orders. ``ZOPE_INTERFACE_STRICT_IRO`` is the most effective tool to find such inconsistent resolution orders, and we recommend running your code with this variable set if at all possible. Doing so will ensure that all interface resolution orders are consistent, and if they're not, will immediately point the way to where this is violated. Occasionally, however, this may not be enough. This is because in some cases, a C3 ordering can be found (the resolution order is fully consistent) that is substantially different from the ad-hoc legacy ordering. In such cases, you may find that you get an unexpected value returned when adapting one or more objects to an interface. To debug this, *also* enable ``ZOPE_INTERFACE_LOG_CHANGED_IRO`` and examine the output. The main thing to look for is changes in the relative positions of interfaces for which there are registered adapters. """ __docformat__ = 'restructuredtext' __all__ = [ 'ro', 'InconsistentResolutionOrderError', 'InconsistentResolutionOrderWarning', ] __logger = None def _logger(): global __logger # pylint:disable=global-statement if __logger is None: import logging __logger = logging.getLogger(__name__) return __logger def _legacy_mergeOrderings(orderings): """Merge multiple orderings so that within-ordering order is preserved Orderings are constrained in such a way that if an object appears in two or more orderings, then the suffix that begins with the object must be in both orderings. For example: >>> _mergeOrderings([ ... ['x', 'y', 'z'], ... ['q', 'z'], ... [1, 3, 5], ... ['z'] ... ]) ['x', 'y', 'q', 1, 3, 5, 'z'] """ seen = set() result = [] for ordering in reversed(orderings): for o in reversed(ordering): if o not in seen: seen.add(o) result.insert(0, o) return result def _legacy_flatten(begin): result = [begin] i = 0 for ob in iter(result): i += 1 # The recursive calls can be avoided by inserting the base classes # into the dynamically growing list directly after the currently # considered object; the iterator makes sure this will keep working # in the future, since it cannot rely on the length of the list # by definition. result[i:i] = ob.__bases__ return result def _legacy_ro(ob): return _legacy_mergeOrderings([_legacy_flatten(ob)]) ### # Compare base objects using identity, not equality. This matches what # the CPython MRO algorithm does, and is *much* faster to boot: that, # plus some other small tweaks makes the difference between 25s and 6s # in loading 446 plone/zope interface.py modules (1925 InterfaceClass, # 1200 Implements, 1100 ClassProvides objects) ### class InconsistentResolutionOrderWarning(PendingDeprecationWarning): """ The warning issued when an invalid IRO is requested. """ class InconsistentResolutionOrderError(TypeError): """ The error raised when an invalid IRO is requested in strict mode. """ def __init__(self, c3, base_tree_remaining): self.C = c3.leaf base_tree = c3.base_tree self.base_ros = { base: base_tree[i + 1] for i, base in enumerate(self.C.__bases__) } # Unfortunately, this doesn't necessarily directly match # up to any transformation on C.__bases__, because # if any were fully used up, they were removed already. self.base_tree_remaining = base_tree_remaining TypeError.__init__(self) def __str__(self): import pprint return "{}: For object {!r}.\nBase ROs:\n{}\nConflict Location:\n{}".format( self.__class__.__name__, self.C, pprint.pformat(self.base_ros), pprint.pformat(self.base_tree_remaining), ) class _NamedBool(int): # cannot actually inherit bool def __new__(cls, val, name): inst = super(cls, _NamedBool).__new__(cls, val) inst.__name__ = name return inst class _ClassBoolFromEnv: """ Non-data descriptor that reads a transformed environment variable as a boolean, and caches the result in the class. """ def __get__(self, inst, klass): import os for cls in klass.__mro__: my_name = None for k in dir(klass): if k in cls.__dict__ and cls.__dict__[k] is self: my_name = k break if my_name is not None: break else: # pragma: no cover raise RuntimeError("Unable to find self") env_name = 'ZOPE_INTERFACE_' + my_name val = os.environ.get(env_name, '') == '1' val = _NamedBool(val, my_name) setattr(klass, my_name, val) setattr(klass, 'ORIG_' + my_name, self) return val class _StaticMRO: # A previously resolved MRO, supplied by the caller. # Used in place of calculating it. had_inconsistency = None # We don't know... def __init__(self, C, mro): self.leaf = C self.__mro = tuple(mro) def mro(self): return list(self.__mro) class C3: # Holds the shared state during computation of an MRO. @staticmethod def resolver(C, strict, base_mros): strict = strict if strict is not None else C3.STRICT_IRO factory = C3 if strict: factory = _StrictC3 elif C3.TRACK_BAD_IRO: factory = _TrackingC3 memo = {} base_mros = base_mros or {} for base, mro in base_mros.items(): assert base in C.__bases__ memo[base] = _StaticMRO(base, mro) return factory(C, memo) __mro = None __legacy_ro = None direct_inconsistency = False def __init__(self, C, memo): self.leaf = C self.memo = memo kind = self.__class__ base_resolvers = [] for base in C.__bases__: if base not in memo: resolver = kind(base, memo) memo[base] = resolver base_resolvers.append(memo[base]) self.base_tree = [ [C] ] + [ memo[base].mro() for base in C.__bases__ ] + [ list(C.__bases__) ] self.bases_had_inconsistency = any(base.had_inconsistency for base in base_resolvers) if len(C.__bases__) == 1: self.__mro = [C] + memo[C.__bases__[0]].mro() @property def had_inconsistency(self): return self.direct_inconsistency or self.bases_had_inconsistency @property def legacy_ro(self): if self.__legacy_ro is None: self.__legacy_ro = tuple(_legacy_ro(self.leaf)) return list(self.__legacy_ro) TRACK_BAD_IRO = _ClassBoolFromEnv() STRICT_IRO = _ClassBoolFromEnv() WARN_BAD_IRO = _ClassBoolFromEnv() LOG_CHANGED_IRO = _ClassBoolFromEnv() USE_LEGACY_IRO = _ClassBoolFromEnv() BAD_IROS = () def _warn_iro(self): if not self.WARN_BAD_IRO: # For the initial release, one must opt-in to see the warning. # In the future (2021?) seeing at least the first warning will # be the default return import warnings warnings.warn( "An inconsistent resolution order is being requested. " "(Interfaces should follow the Python class rules known as C3.) " "For backwards compatibility, zope.interface will allow this, " "making the best guess it can to produce as meaningful an order as possible. " "In the future this might be an error. Set the warning filter to error, or set " "the environment variable 'ZOPE_INTERFACE_TRACK_BAD_IRO' to '1' and examine " "ro.C3.BAD_IROS to debug, or set 'ZOPE_INTERFACE_STRICT_IRO' to raise exceptions.", InconsistentResolutionOrderWarning, ) @staticmethod def _can_choose_base(base, base_tree_remaining): # From C3: # nothead = [s for s in nonemptyseqs if cand in s[1:]] for bases in base_tree_remaining: if not bases or bases[0] is base: continue for b in bases: if b is base: return False return True @staticmethod def _nonempty_bases_ignoring(base_tree, ignoring): return list(filter(None, [ [b for b in bases if b is not ignoring] for bases in base_tree ])) def _choose_next_base(self, base_tree_remaining): """ Return the next base. The return value will either fit the C3 constraints or be our best guess about what to do. If we cannot guess, this may raise an exception. """ base = self._find_next_C3_base(base_tree_remaining) if base is not None: return base return self._guess_next_base(base_tree_remaining) def _find_next_C3_base(self, base_tree_remaining): """ Return the next base that fits the constraints, or ``None`` if there isn't one. """ for bases in base_tree_remaining: base = bases[0] if self._can_choose_base(base, base_tree_remaining): return base return None class _UseLegacyRO(Exception): pass def _guess_next_base(self, base_tree_remaining): # Narf. We may have an inconsistent order (we won't know for # sure until we check all the bases). Python cannot create # classes like this: # # class B1: # pass # class B2(B1): # pass # class C(B1, B2): # -> TypeError; this is like saying C(B1, B2, B1). # pass # # However, older versions of zope.interface were fine with this order. # A good example is ``providedBy(IOError())``. Because of the way # ``classImplements`` works, it winds up with ``__bases__`` == # ``[IEnvironmentError, IIOError, IOSError, ]`` # (on Python 3). But ``IEnvironmentError`` is a base of both ``IIOError`` # and ``IOSError``. Previously, we would get a resolution order of # ``[IIOError, IOSError, IEnvironmentError, IStandardError, IException, Interface]`` # but the standard Python algorithm would forbid creating that order entirely. # Unlike Python's MRO, we attempt to resolve the issue. A few # heuristics have been tried. One was: # # Strip off the first (highest priority) base of each direct # base one at a time and seeing if we can come to an agreement # with the other bases. (We're trying for a partial ordering # here.) This often resolves cases (such as the IOSError case # above), and frequently produces the same ordering as the # legacy MRO did. If we looked at all the highest priority # bases and couldn't find any partial ordering, then we strip # them *all* out and begin the C3 step again. We take care not # to promote a common root over all others. # # If we only did the first part, stripped off the first # element of the first item, we could resolve simple cases. # But it tended to fail badly. If we did the whole thing, it # could be extremely painful from a performance perspective # for deep/wide things like Zope's OFS.SimpleItem.Item. Plus, # anytime you get ExtensionClass.Base into the mix, you're # likely to wind up in trouble, because it messes with the MRO # of classes. Sigh. # # So now, we fall back to the old linearization (fast to compute). self._warn_iro() self.direct_inconsistency = InconsistentResolutionOrderError(self, base_tree_remaining) raise self._UseLegacyRO def _merge(self): # Returns a merged *list*. result = self.__mro = [] base_tree_remaining = self.base_tree base = None while 1: # Take last picked base out of the base tree wherever it is. # This differs slightly from the standard Python MRO and is needed # because we have no other step that prevents duplicates # from coming in (e.g., in the inconsistent fallback path) base_tree_remaining = self._nonempty_bases_ignoring(base_tree_remaining, base) if not base_tree_remaining: return result try: base = self._choose_next_base(base_tree_remaining) except self._UseLegacyRO: self.__mro = self.legacy_ro return self.legacy_ro result.append(base) def mro(self): if self.__mro is None: self.__mro = tuple(self._merge()) return list(self.__mro) class _StrictC3(C3): __slots__ = () def _guess_next_base(self, base_tree_remaining): raise InconsistentResolutionOrderError(self, base_tree_remaining) class _TrackingC3(C3): __slots__ = () def _guess_next_base(self, base_tree_remaining): import traceback bad_iros = C3.BAD_IROS if self.leaf not in bad_iros: if bad_iros == (): import weakref # This is a race condition, but it doesn't matter much. bad_iros = C3.BAD_IROS = weakref.WeakKeyDictionary() bad_iros[self.leaf] = t = ( InconsistentResolutionOrderError(self, base_tree_remaining), traceback.format_stack() ) _logger().warning("Tracking inconsistent IRO: %s", t[0]) return C3._guess_next_base(self, base_tree_remaining) class _ROComparison: # Exists to compute and print a pretty string comparison # for differing ROs. # Since we're used in a logging context, and may actually never be printed, # this is a class so we can defer computing the diff until asked. # Components we use to build up the comparison report class Item: prefix = ' ' def __init__(self, item): self.item = item def __str__(self): return "{}{}".format( self.prefix, self.item, ) class Deleted(Item): prefix = '- ' class Inserted(Item): prefix = '+ ' Empty = str class ReplacedBy: # pragma: no cover prefix = '- ' suffix = '' def __init__(self, chunk, total_count): self.chunk = chunk self.total_count = total_count def __iter__(self): lines = [ self.prefix + str(item) + self.suffix for item in self.chunk ] while len(lines) < self.total_count: lines.append('') return iter(lines) class Replacing(ReplacedBy): prefix = "+ " suffix = '' _c3_report = None _legacy_report = None def __init__(self, c3, c3_ro, legacy_ro): self.c3 = c3 self.c3_ro = c3_ro self.legacy_ro = legacy_ro def __move(self, from_, to_, chunk, operation): for x in chunk: to_.append(operation(x)) from_.append(self.Empty()) def _generate_report(self): if self._c3_report is None: import difflib # The opcodes we get describe how to turn 'a' into 'b'. So # the old one (legacy) needs to be first ('a') matcher = difflib.SequenceMatcher(None, self.legacy_ro, self.c3_ro) # The reports are equal length sequences. We're going for a # side-by-side diff. self._c3_report = c3_report = [] self._legacy_report = legacy_report = [] for opcode, leg1, leg2, c31, c32 in matcher.get_opcodes(): c3_chunk = self.c3_ro[c31:c32] legacy_chunk = self.legacy_ro[leg1:leg2] if opcode == 'equal': # Guaranteed same length c3_report.extend(self.Item(x) for x in c3_chunk) legacy_report.extend(self.Item(x) for x in legacy_chunk) if opcode == 'delete': # Guaranteed same length assert not c3_chunk self.__move(c3_report, legacy_report, legacy_chunk, self.Deleted) if opcode == 'insert': # Guaranteed same length assert not legacy_chunk self.__move(legacy_report, c3_report, c3_chunk, self.Inserted) if opcode == 'replace': # pragma: no cover (How do you make it output this?) # Either side could be longer. chunk_size = max(len(c3_chunk), len(legacy_chunk)) c3_report.extend(self.Replacing(c3_chunk, chunk_size)) legacy_report.extend(self.ReplacedBy(legacy_chunk, chunk_size)) return self._c3_report, self._legacy_report @property def _inconsistent_label(self): inconsistent = [] if self.c3.direct_inconsistency: inconsistent.append('direct') if self.c3.bases_had_inconsistency: inconsistent.append('bases') return '+'.join(inconsistent) if inconsistent else 'no' def __str__(self): c3_report, legacy_report = self._generate_report() assert len(c3_report) == len(legacy_report) left_lines = [str(x) for x in legacy_report] right_lines = [str(x) for x in c3_report] # We have the same number of lines in the report; this is not # necessarily the same as the number of items in either RO. assert len(left_lines) == len(right_lines) padding = ' ' * 2 max_left = max(len(x) for x in left_lines) max_right = max(len(x) for x in right_lines) left_title = 'Legacy RO (len={})'.format(len(self.legacy_ro)) right_title = 'C3 RO (len={}; inconsistent={})'.format( len(self.c3_ro), self._inconsistent_label, ) lines = [ (padding + left_title.ljust(max_left) + padding + right_title.ljust(max_right)), padding + '=' * (max_left + len(padding) + max_right) ] lines += [ padding + left.ljust(max_left) + padding + right for left, right in zip(left_lines, right_lines) ] return '\n'.join(lines) # Set to `Interface` once it is defined. This is used to # avoid logging false positives about changed ROs. _ROOT = None def ro(C, strict=None, base_mros=None, log_changed_ro=None, use_legacy_ro=None): """ ro(C) -> list Compute the precedence list (mro) according to C3. :return: A fresh `list` object. .. versionchanged:: 5.0.0 Add the *strict*, *log_changed_ro* and *use_legacy_ro* keyword arguments. These are provisional and likely to be removed in the future. They are most useful for testing. """ # The ``base_mros`` argument is for internal optimization and # not documented. resolver = C3.resolver(C, strict, base_mros) mro = resolver.mro() log_changed = log_changed_ro if log_changed_ro is not None else resolver.LOG_CHANGED_IRO use_legacy = use_legacy_ro if use_legacy_ro is not None else resolver.USE_LEGACY_IRO if log_changed or use_legacy: legacy_ro = resolver.legacy_ro assert isinstance(legacy_ro, list) assert isinstance(mro, list) changed = legacy_ro != mro if changed: # Did only Interface move? The fix for issue #8 made that # somewhat common. It's almost certainly not a problem, though, # so allow ignoring it. legacy_without_root = [x for x in legacy_ro if x is not _ROOT] mro_without_root = [x for x in mro if x is not _ROOT] changed = legacy_without_root != mro_without_root if changed: comparison = _ROComparison(resolver, mro, legacy_ro) _logger().warning( "Object %r has different legacy and C3 MROs:\n%s", C, comparison ) if resolver.had_inconsistency and legacy_ro == mro: comparison = _ROComparison(resolver, mro, legacy_ro) _logger().warning( "Object %r had inconsistent IRO and used the legacy RO:\n%s" "\nInconsistency entered at:\n%s", C, comparison, resolver.direct_inconsistency ) if use_legacy: return legacy_ro return mro def is_consistent(C): """ Check if the resolution order for *C*, as computed by :func:`ro`, is consistent according to C3. """ return not C3.resolver(C, False, None).had_inconsistency zope.interface-6.4/src/zope/interface/tests/000077500000000000000000000000001462121350100211405ustar00rootroot00000000000000zope.interface-6.4/src/zope/interface/tests/__init__.py000066400000000000000000000075711462121350100232630ustar00rootroot00000000000000from zope.interface._compat import _should_attempt_c_optimizations class OptimizationTestMixin: """ Helper for testing that C optimizations are used when appropriate. """ def _getTargetClass(self): """ Define this to return the implementation in use, without the 'Py' or 'Fallback' suffix. """ raise NotImplementedError def _getFallbackClass(self): """ Define this to return the fallback Python implementation. """ # Is there an algorithmic way to do this? The C # objects all come from the same module so I don't see how we can # get the Python object from that. raise NotImplementedError def test_optimizations(self): used = self._getTargetClass() fallback = self._getFallbackClass() if _should_attempt_c_optimizations(): self.assertIsNot(used, fallback) else: self.assertIs(used, fallback) class MissingSomeAttrs: """ Helper for tests that raises a specific exception for attributes that are missing. This is usually not an AttributeError, and this object is used to test that those errors are not improperly caught and treated like an AttributeError. """ def __init__(self, exc_kind, **other_attrs): self.__exc_kind = exc_kind d = object.__getattribute__(self, '__dict__') d.update(other_attrs) def __getattribute__(self, name): # Note that we ignore objects found in the class dictionary. d = object.__getattribute__(self, '__dict__') try: return d[name] except KeyError: raise d['_MissingSomeAttrs__exc_kind'](name) EXCEPTION_CLASSES = ( TypeError, RuntimeError, BaseException, ValueError, ) @classmethod def test_raises(cls, unittest, test_func, expected_missing, **other_attrs): """ Loop through various exceptions, calling *test_func* inside a ``assertRaises`` block. :param test_func: A callable of one argument, the instance of this class. :param str expected_missing: The attribute that should fail with the exception. This is used to ensure that we're testing the path we think we are. :param other_attrs: Attributes that should be provided on the test object. Must not contain *expected_missing*. """ assert isinstance(expected_missing, str) assert expected_missing not in other_attrs for exc in cls.EXCEPTION_CLASSES: ob = cls(exc, **other_attrs) with unittest.assertRaises(exc) as ex: test_func(ob) unittest.assertEqual(ex.exception.args[0], expected_missing) # Now test that the AttributeError for that expected_missing is *not* raised. ob = cls(AttributeError, **other_attrs) try: test_func(ob) except AttributeError as e: unittest.assertNotIn(expected_missing, str(e)) except Exception: # pylint:disable=broad-except pass # Be sure cleanup functionality is available; classes that use the adapter hook # need to be sure to subclass ``CleanUp``. # # If zope.component is installed and imported when we run our tests # (import chain: # zope.testrunner->zope.security->zope.location->zope.component.api) # it adds an adapter hook that uses its global site manager. That can cause # leakage from one test to another unless its cleanup hooks are run. The symptoms can # be odd, especially if one test used C objects and the next used the Python # implementation. (For example, you can get strange TypeErrors or find inexplicable # comparisons being done.) try: from zope.testing import cleanup except ImportError: class CleanUp: def cleanUp(self): pass setUp = tearDown = cleanUp else: CleanUp = cleanup.CleanUp zope.interface-6.4/src/zope/interface/tests/advisory_testing.py000066400000000000000000000016021462121350100251060ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2003 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. # ############################################################################## import sys from zope.interface.advice import getFrameInfo my_globals = globals() ClassicClass = None class NewStyleClass: __metaclass__ = type classLevelFrameInfo = getFrameInfo(sys._getframe()) moduleLevelFrameInfo = getFrameInfo(sys._getframe()) zope.interface-6.4/src/zope/interface/tests/dummy.py000066400000000000000000000016201462121350100226440ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2001, 2002 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. # ############################################################################## """ Dummy Module """ from zope.interface import moduleProvides from zope.interface.tests.idummy import IDummyModule moduleProvides(IDummyModule) def bar(baz): # Note: no 'self', because the module provides the interface directly. raise NotImplementedError() zope.interface-6.4/src/zope/interface/tests/idummy.py000066400000000000000000000015721462121350100230230ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2001, 2002 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. # ############################################################################## """ Interface describing API of zope.interface.tests.dummy test module """ from zope.interface import Interface class IDummyModule(Interface): """ Dummy interface for unit tests. """ def bar(baz): """ Just a note. """ zope.interface-6.4/src/zope/interface/tests/m1.py000066400000000000000000000015071462121350100220320ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2004 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. # ############################################################################## """Test module that declares an interface """ from zope.interface import Interface from zope.interface import moduleProvides class I1(Interface): pass class I2(Interface): pass moduleProvides(I1, I2) zope.interface-6.4/src/zope/interface/tests/odd.py000066400000000000000000000057071462121350100222710ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2003 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. # ############################################################################## """Odd meta class that doesn't subclass type. This is used for testing support for ExtensionClass in new interfaces. >>> class A(object): ... __metaclass__ = MetaClass ... a = 1 ... >>> A.__name__ 'A' >>> A.__bases__ == (object,) True >>> class B(object): ... __metaclass__ = MetaClass ... b = 1 ... >>> class C(A, B): pass ... >>> C.__name__ 'C' >>> int(C.__bases__ == (A, B)) 1 >>> a = A() >>> aa = A() >>> a.a 1 >>> aa.a 1 >>> aa.a = 2 >>> a.a 1 >>> aa.a 2 >>> c = C() >>> c.a 1 >>> c.b 1 >>> c.b = 2 >>> c.b 2 >>> C.c = 1 >>> c.c 1 >>> int(C.__class__.__class__ is C.__class__) 1 """ # class OddClass is an odd meta class class MetaMetaClass(type): def __getattribute__(cls, name): if name == '__class__': return cls # Under Python 3.6, __prepare__ gets requested return type.__getattribute__(cls, name) class MetaClass: """Odd classes """ def __init__(self, name, bases, dict): self.__name__ = name self.__bases__ = bases self.__dict__.update(dict) def __call__(self): return OddInstance(self) def __getattr__(self, name): for b in self.__bases__: v = getattr(b, name, self) if v is not self: return v raise AttributeError(name) def __repr__(self): # pragma: no cover return "".format(self.__name__, hex(id(self))) MetaClass = MetaMetaClass('MetaClass', MetaClass.__bases__, {k: v for k, v in MetaClass.__dict__.items() if k not in ('__dict__',)}) class OddInstance: def __init__(self, cls): self.__dict__['__class__'] = cls def __getattribute__(self, name): dict = object.__getattribute__(self, '__dict__') if name == '__dict__': return dict v = dict.get(name, self) if v is not self: return v return getattr(dict['__class__'], name) def __setattr__(self, name, v): self.__dict__[name] = v def __delattr__(self, name): raise NotImplementedError() def __repr__(self): # pragma: no cover return "".format( self.__class__.__name__, hex(id(self))) zope.interface-6.4/src/zope/interface/tests/test_adapter.py000066400000000000000000002331671462121350100242050ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2003 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. # ############################################################################## """Adapter registry tests """ import unittest from zope.interface.tests import OptimizationTestMixin # pylint:disable=inherit-non-class,protected-access,too-many-lines # pylint:disable=attribute-defined-outside-init,blacklisted-name def _makeInterfaces(): from zope.interface import Interface class IB0(Interface): pass class IB1(IB0): pass class IB2(IB0): pass class IB3(IB2, IB1): pass class IB4(IB1, IB2): pass class IF0(Interface): pass class IF1(IF0): pass class IR0(Interface): pass class IR1(IR0): pass return IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 # Custom types to use as part of the AdapterRegistry data structures. # Our custom types do strict type checking to make sure # types propagate through the data tree as expected. class CustomDataTypeBase: _data = None def __getitem__(self, name): return self._data[name] def __setitem__(self, name, value): self._data[name] = value def __delitem__(self, name): del self._data[name] def __len__(self): return len(self._data) def __contains__(self, name): return name in self._data def __eq__(self, other): if other is self: return True # pylint:disable=unidiomatic-typecheck if type(other) != type(self): return False return other._data == self._data def __repr__(self): return repr(self._data) class CustomMapping(CustomDataTypeBase): def __init__(self, other=None): self._data = {} if other: self._data.update(other) self.get = self._data.get self.items = self._data.items class CustomSequence(CustomDataTypeBase): def __init__(self, other=None): self._data = [] if other: self._data.extend(other) self.append = self._data.append class CustomLeafSequence(CustomSequence): pass class CustomProvided(CustomMapping): pass class BaseAdapterRegistryTests(unittest.TestCase): maxDiff = None def _getBaseAdapterRegistry(self): from zope.interface.adapter import BaseAdapterRegistry return BaseAdapterRegistry def _getTargetClass(self): BaseAdapterRegistry = self._getBaseAdapterRegistry() class _CUT(BaseAdapterRegistry): class LookupClass: _changed = _extendors = () def __init__(self, reg): pass def changed(self, orig): self._changed += (orig,) def add_extendor(self, provided): self._extendors += (provided,) def remove_extendor(self, provided): self._extendors = tuple([x for x in self._extendors if x != provided]) for name in BaseAdapterRegistry._delegated: setattr(_CUT.LookupClass, name, object()) return _CUT def _makeOne(self): return self._getTargetClass()() def _getMappingType(self): return dict def _getProvidedType(self): return dict def _getMutableListType(self): return list def _getLeafSequenceType(self): return tuple def test_lookup_delegation(self): CUT = self._getTargetClass() registry = CUT() for name in CUT._delegated: self.assertIs(getattr(registry, name), getattr(registry._v_lookup, name)) def test__generation_on_first_creation(self): registry = self._makeOne() # Bumped to 1 in BaseAdapterRegistry.__init__ self.assertEqual(registry._generation, 1) def test__generation_after_calling_changed(self): registry = self._makeOne() orig = object() registry.changed(orig) # Bumped to 1 in BaseAdapterRegistry.__init__ self.assertEqual(registry._generation, 2) self.assertEqual(registry._v_lookup._changed, (registry, orig,)) def test__generation_after_changing___bases__(self): class _Base: pass registry = self._makeOne() registry.__bases__ = (_Base,) self.assertEqual(registry._generation, 2) def _check_basic_types_of_adapters(self, registry, expected_order=2): self.assertEqual(len(registry._adapters), expected_order) # order 0 and order 1 self.assertIsInstance(registry._adapters, self._getMutableListType()) MT = self._getMappingType() for mapping in registry._adapters: self.assertIsInstance(mapping, MT) self.assertEqual(registry._adapters[0], MT()) self.assertIsInstance(registry._adapters[1], MT) self.assertEqual(len(registry._adapters[expected_order - 1]), 1) def _check_basic_types_of_subscribers(self, registry, expected_order=2): self.assertEqual(len(registry._subscribers), expected_order) # order 0 and order 1 self.assertIsInstance(registry._subscribers, self._getMutableListType()) MT = self._getMappingType() for mapping in registry._subscribers: self.assertIsInstance(mapping, MT) if expected_order: self.assertEqual(registry._subscribers[0], MT()) self.assertIsInstance(registry._subscribers[1], MT) self.assertEqual(len(registry._subscribers[expected_order - 1]), 1) def test_register(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() registry.register([IB0], IR0, '', 'A1') self.assertEqual(registry.registered([IB0], IR0, ''), 'A1') self.assertEqual(registry._generation, 2) self._check_basic_types_of_adapters(registry) MT = self._getMappingType() self.assertEqual(registry._adapters[1], MT({ IB0: MT({ IR0: MT({'': 'A1'}) }) })) PT = self._getProvidedType() self.assertEqual(registry._provided, PT({ IR0: 1 })) registered = list(registry.allRegistrations()) self.assertEqual(registered, [( (IB0,), # required IR0, # provided '', # name 'A1' # value )]) def test_register_multiple_allRegistrations(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() # Use several different depths and several different names registry.register([], IR0, '', 'A1') registry.register([], IR0, 'name1', 'A2') registry.register([IB0], IR0, '', 'A1') registry.register([IB0], IR0, 'name2', 'A2') registry.register([IB0], IR1, '', 'A3') registry.register([IB0], IR1, 'name3', 'A4') registry.register([IB0, IB1], IR0, '', 'A1') registry.register([IB0, IB2], IR0, 'name2', 'A2') registry.register([IB0, IB2], IR1, 'name4', 'A4') registry.register([IB0, IB3], IR1, '', 'A3') def build_adapters(L, MT): return L([ # 0 MT({ IR0: MT({ '': 'A1', 'name1': 'A2' }) }), # 1 MT({ IB0: MT({ IR0: MT({ '': 'A1', 'name2': 'A2' }), IR1: MT({ '': 'A3', 'name3': 'A4' }) }) }), # 3 MT({ IB0: MT({ IB1: MT({ IR0: MT({'': 'A1'}) }), IB2: MT({ IR0: MT({'name2': 'A2'}), IR1: MT({'name4': 'A4'}), }), IB3: MT({ IR1: MT({'': 'A3'}) }) }), }), ]) self.assertEqual(registry._adapters, build_adapters(L=self._getMutableListType(), MT=self._getMappingType())) registered = sorted(registry.allRegistrations()) self.assertEqual(registered, [ ((), IR0, '', 'A1'), ((), IR0, 'name1', 'A2'), ((IB0,), IR0, '', 'A1'), ((IB0,), IR0, 'name2', 'A2'), ((IB0,), IR1, '', 'A3'), ((IB0,), IR1, 'name3', 'A4'), ((IB0, IB1), IR0, '', 'A1'), ((IB0, IB2), IR0, 'name2', 'A2'), ((IB0, IB2), IR1, 'name4', 'A4'), ((IB0, IB3), IR1, '', 'A3') ]) # We can duplicate to another object. registry2 = self._makeOne() for args in registered: registry2.register(*args) self.assertEqual(registry2._adapters, registry._adapters) self.assertEqual(registry2._provided, registry._provided) # We can change the types and rebuild the data structures. registry._mappingType = CustomMapping registry._leafSequenceType = CustomLeafSequence registry._sequenceType = CustomSequence registry._providedType = CustomProvided def addValue(existing, new): existing = existing if existing is not None else CustomLeafSequence() existing.append(new) return existing registry._addValueToLeaf = addValue registry.rebuild() self.assertEqual(registry._adapters, build_adapters( L=CustomSequence, MT=CustomMapping )) def test_register_with_invalid_name(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() with self.assertRaises(ValueError): registry.register([IB0], IR0, object(), 'A1') def test_register_with_value_None_unregisters(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() registry.register([None], IR0, '', 'A1') registry.register([None], IR0, '', None) self.assertEqual(len(registry._adapters), 0) self.assertIsInstance(registry._adapters, self._getMutableListType()) registered = list(registry.allRegistrations()) self.assertEqual(registered, []) def test_register_with_same_value(self): from zope.interface import Interface IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() _value = object() registry.register([None], IR0, '', _value) _before = registry._generation registry.register([None], IR0, '', _value) self.assertEqual(registry._generation, _before) # skipped changed() self._check_basic_types_of_adapters(registry) MT = self._getMappingType() self.assertEqual(registry._adapters[1], MT( { Interface: MT( { IR0: MT({'': _value}) } ) } )) registered = list(registry.allRegistrations()) self.assertEqual(registered, [( (Interface,), # required IR0, # provided '', # name _value # value )]) def test_registered_empty(self): registry = self._makeOne() self.assertEqual(registry.registered([None], None, ''), None) registered = list(registry.allRegistrations()) self.assertEqual(registered, []) def test_registered_non_empty_miss(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() registry.register([IB1], None, '', 'A1') self.assertEqual(registry.registered([IB2], None, ''), None) def test_registered_non_empty_hit(self): registry = self._makeOne() registry.register([None], None, '', 'A1') self.assertEqual(registry.registered([None], None, ''), 'A1') def test_unregister_empty(self): registry = self._makeOne() registry.unregister([None], None, '') # doesn't raise self.assertEqual(registry.registered([None], None, ''), None) self.assertEqual(len(registry._provided), 0) def test_unregister_non_empty_miss_on_required(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() registry.register([IB1], None, '', 'A1') registry.unregister([IB2], None, '') # doesn't raise self.assertEqual(registry.registered([IB1], None, ''), 'A1') self._check_basic_types_of_adapters(registry) MT = self._getMappingType() self.assertEqual(registry._adapters[1], MT( { IB1: MT( { None: MT({'': 'A1'}) } ) } )) PT = self._getProvidedType() self.assertEqual(registry._provided, PT({ None: 1 })) def test_unregister_non_empty_miss_on_name(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() registry.register([IB1], None, '', 'A1') registry.unregister([IB1], None, 'nonesuch') # doesn't raise self.assertEqual(registry.registered([IB1], None, ''), 'A1') self._check_basic_types_of_adapters(registry) MT = self._getMappingType() self.assertEqual(registry._adapters[1], MT( { IB1: MT( { None: MT({'': 'A1'}) } ) } )) PT = self._getProvidedType() self.assertEqual(registry._provided, PT({ None: 1 })) def test_unregister_with_value_not_None_miss(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() orig = object() nomatch = object() registry.register([IB1], None, '', orig) registry.unregister([IB1], None, '', nomatch) #doesn't raise self.assertIs(registry.registered([IB1], None, ''), orig) def test_unregister_hit_clears_empty_subcomponents(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() one = object() another = object() registry.register([IB1, IB2], None, '', one) registry.register([IB1, IB3], None, '', another) self._check_basic_types_of_adapters(registry, expected_order=3) self.assertIn(IB2, registry._adapters[2][IB1]) self.assertIn(IB3, registry._adapters[2][IB1]) MT = self._getMappingType() self.assertEqual(registry._adapters[2], MT( { IB1: MT( { IB2: MT({None: MT({'': one})}), IB3: MT({None: MT({'': another})}) } ) } )) PT = self._getProvidedType() self.assertEqual(registry._provided, PT({ None: 2 })) registry.unregister([IB1, IB3], None, '', another) self.assertIn(IB2, registry._adapters[2][IB1]) self.assertNotIn(IB3, registry._adapters[2][IB1]) self.assertEqual(registry._adapters[2], MT( { IB1: MT( { IB2: MT({None: MT({'': one})}), } ) } )) self.assertEqual(registry._provided, PT({ None: 1 })) def test_unsubscribe_empty(self): registry = self._makeOne() registry.unsubscribe([None], None, '') #doesn't raise self.assertEqual(registry.registered([None], None, ''), None) self._check_basic_types_of_subscribers(registry, expected_order=0) def test_unsubscribe_hit(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() orig = object() registry.subscribe([IB1], None, orig) MT = self._getMappingType() L = self._getLeafSequenceType() PT = self._getProvidedType() self._check_basic_types_of_subscribers(registry) self.assertEqual(registry._subscribers[1], MT({ IB1: MT({ None: MT({ '': L((orig,)) }) }) })) self.assertEqual(registry._provided, PT({})) registry.unsubscribe([IB1], None, orig) #doesn't raise self.assertEqual(len(registry._subscribers), 0) self.assertEqual(registry._provided, PT({})) def assertLeafIdentity(self, leaf1, leaf2): """ Implementations may choose to use new, immutable objects instead of mutating existing subscriber leaf objects, or vice versa. The default implementation uses immutable tuples, so they are never the same. Other implementations may use persistent lists so they should be the same and mutated in place. Subclasses testing this behaviour need to override this method. """ self.assertIsNot(leaf1, leaf2) def test_unsubscribe_after_multiple(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() first = object() second = object() third = object() fourth = object() registry.subscribe([IB1], None, first) registry.subscribe([IB1], None, second) registry.subscribe([IB1], IR0, third) registry.subscribe([IB1], IR0, fourth) self._check_basic_types_of_subscribers(registry, expected_order=2) MT = self._getMappingType() L = self._getLeafSequenceType() PT = self._getProvidedType() self.assertEqual(registry._subscribers[1], MT({ IB1: MT({ None: MT({'': L((first, second))}), IR0: MT({'': L((third, fourth))}), }) })) self.assertEqual(registry._provided, PT({ IR0: 2 })) # The leaf objects may or may not stay the same as they are unsubscribed, # depending on the implementation IR0_leaf_orig = registry._subscribers[1][IB1][IR0][''] Non_leaf_orig = registry._subscribers[1][IB1][None][''] registry.unsubscribe([IB1], None, first) registry.unsubscribe([IB1], IR0, third) self.assertEqual(registry._subscribers[1], MT({ IB1: MT({ None: MT({'': L((second,))}), IR0: MT({'': L((fourth,))}), }) })) self.assertEqual(registry._provided, PT({ IR0: 1 })) IR0_leaf_new = registry._subscribers[1][IB1][IR0][''] Non_leaf_new = registry._subscribers[1][IB1][None][''] self.assertLeafIdentity(IR0_leaf_orig, IR0_leaf_new) self.assertLeafIdentity(Non_leaf_orig, Non_leaf_new) registry.unsubscribe([IB1], None, second) registry.unsubscribe([IB1], IR0, fourth) self.assertEqual(len(registry._subscribers), 0) self.assertEqual(len(registry._provided), 0) def test_subscribe_unsubscribe_identical_objects_provided(self): # https://github.com/zopefoundation/zope.interface/issues/227 IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() first = object() registry.subscribe([IB1], IR0, first) registry.subscribe([IB1], IR0, first) MT = self._getMappingType() L = self._getLeafSequenceType() PT = self._getProvidedType() self.assertEqual(registry._subscribers[1], MT({ IB1: MT({ IR0: MT({'': L((first, first))}), }) })) self.assertEqual(registry._provided, PT({ IR0: 2 })) registry.unsubscribe([IB1], IR0, first) registry.unsubscribe([IB1], IR0, first) self.assertEqual(len(registry._subscribers), 0) self.assertEqual(registry._provided, PT()) def test_subscribe_unsubscribe_nonequal_objects_provided(self): # https://github.com/zopefoundation/zope.interface/issues/227 IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() first = object() second = object() registry.subscribe([IB1], IR0, first) registry.subscribe([IB1], IR0, second) MT = self._getMappingType() L = self._getLeafSequenceType() PT = self._getProvidedType() self.assertEqual(registry._subscribers[1], MT({ IB1: MT({ IR0: MT({'': L((first, second))}), }) })) self.assertEqual(registry._provided, PT({ IR0: 2 })) registry.unsubscribe([IB1], IR0, first) registry.unsubscribe([IB1], IR0, second) self.assertEqual(len(registry._subscribers), 0) self.assertEqual(registry._provided, PT()) def test_subscribed_empty(self): registry = self._makeOne() self.assertIsNone(registry.subscribed([None], None, '')) subscribed = list(registry.allSubscriptions()) self.assertEqual(subscribed, []) def test_subscribed_non_empty_miss(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() registry.subscribe([IB1], IF0, 'A1') # Mismatch required self.assertIsNone(registry.subscribed([IB2], IF0, '')) # Mismatch provided self.assertIsNone(registry.subscribed([IB1], IF1, '')) # Mismatch value self.assertIsNone(registry.subscribed([IB1], IF0, '')) def test_subscribed_non_empty_hit(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() registry.subscribe([IB0], IF0, 'A1') self.assertEqual(registry.subscribed([IB0], IF0, 'A1'), 'A1') def test_unsubscribe_w_None_after_multiple(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() first = object() second = object() registry.subscribe([IB1], None, first) registry.subscribe([IB1], None, second) self._check_basic_types_of_subscribers(registry, expected_order=2) registry.unsubscribe([IB1], None) self.assertEqual(len(registry._subscribers), 0) def test_unsubscribe_non_empty_miss_on_required(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() registry.subscribe([IB1], None, 'A1') self._check_basic_types_of_subscribers(registry, expected_order=2) registry.unsubscribe([IB2], None, '') # doesn't raise self.assertEqual(len(registry._subscribers), 2) MT = self._getMappingType() L = self._getLeafSequenceType() self.assertEqual(registry._subscribers[1], MT({ IB1: MT({ None: MT({'': L(('A1',))}), }) })) def test_unsubscribe_non_empty_miss_on_value(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() registry.subscribe([IB1], None, 'A1') self._check_basic_types_of_subscribers(registry, expected_order=2) registry.unsubscribe([IB1], None, 'A2') # doesn't raise self.assertEqual(len(registry._subscribers), 2) MT = self._getMappingType() L = self._getLeafSequenceType() self.assertEqual(registry._subscribers[1], MT({ IB1: MT({ None: MT({'': L(('A1',))}), }) })) def test_unsubscribe_with_value_not_None_miss(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() orig = object() nomatch = object() registry.subscribe([IB1], None, orig) registry.unsubscribe([IB1], None, nomatch) #doesn't raise self.assertEqual(len(registry._subscribers), 2) def _instance_method_notify_target(self): self.fail("Example method, not intended to be called.") def test_unsubscribe_instance_method(self): # Checking that the values are compared by equality, not identity IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() self.assertEqual(len(registry._subscribers), 0) registry.subscribe([IB1], None, self._instance_method_notify_target) registry.unsubscribe([IB1], None, self._instance_method_notify_target) self.assertEqual(len(registry._subscribers), 0) def test_subscribe_multiple_allRegistrations(self): IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 = _makeInterfaces() # pylint:disable=unused-variable registry = self._makeOne() # Use several different depths and several different values registry.subscribe([], IR0, 'A1') registry.subscribe([], IR0, 'A2') registry.subscribe([IB0], IR0, 'A1') registry.subscribe([IB0], IR0, 'A2') registry.subscribe([IB0], IR1, 'A3') registry.subscribe([IB0], IR1, 'A4') registry.subscribe([IB0, IB1], IR0, 'A1') registry.subscribe([IB0, IB2], IR0, 'A2') registry.subscribe([IB0, IB2], IR1, 'A4') registry.subscribe([IB0, IB3], IR1, 'A3') def build_subscribers(L, F, MT): return L([ # 0 MT({ IR0: MT({ '': F(['A1', 'A2']) }) }), # 1 MT({ IB0: MT({ IR0: MT({ '': F(['A1', 'A2']) }), IR1: MT({ '': F(['A3', 'A4']) }) }) }), # 3 MT({ IB0: MT({ IB1: MT({ IR0: MT({'': F(['A1'])}) }), IB2: MT({ IR0: MT({'': F(['A2'])}), IR1: MT({'': F(['A4'])}), }), IB3: MT({ IR1: MT({'': F(['A3'])}) }) }), }), ]) self.assertEqual(registry._subscribers, build_subscribers( L=self._getMutableListType(), F=self._getLeafSequenceType(), MT=self._getMappingType() )) def build_provided(P): return P({ IR0: 6, IR1: 4, }) self.assertEqual(registry._provided, build_provided(P=self._getProvidedType())) registered = sorted(registry.allSubscriptions()) self.assertEqual(registered, [ ((), IR0, 'A1'), ((), IR0, 'A2'), ((IB0,), IR0, 'A1'), ((IB0,), IR0, 'A2'), ((IB0,), IR1, 'A3'), ((IB0,), IR1, 'A4'), ((IB0, IB1), IR0, 'A1'), ((IB0, IB2), IR0, 'A2'), ((IB0, IB2), IR1, 'A4'), ((IB0, IB3), IR1, 'A3') ]) # We can duplicate this to another object registry2 = self._makeOne() for args in registered: registry2.subscribe(*args) self.assertEqual(registry2._subscribers, registry._subscribers) self.assertEqual(registry2._provided, registry._provided) # We can change the types and rebuild the data structures. registry._mappingType = CustomMapping registry._leafSequenceType = CustomLeafSequence registry._sequenceType = CustomSequence registry._providedType = CustomProvided def addValue(existing, new): existing = existing if existing is not None else CustomLeafSequence() existing.append(new) return existing registry._addValueToLeaf = addValue registry.rebuild() self.assertEqual(registry._subscribers, build_subscribers( L=CustomSequence, F=CustomLeafSequence, MT=CustomMapping )) class CustomTypesBaseAdapterRegistryTests(BaseAdapterRegistryTests): """ This class may be extended by other packages to test their own adapter registries that use custom types. (So be cautious about breaking changes.) One known user is ``zope.component.persistentregistry``. """ def _getMappingType(self): return CustomMapping def _getProvidedType(self): return CustomProvided def _getMutableListType(self): return CustomSequence def _getLeafSequenceType(self): return CustomLeafSequence def _getBaseAdapterRegistry(self): from zope.interface.adapter import BaseAdapterRegistry class CustomAdapterRegistry(BaseAdapterRegistry): _mappingType = self._getMappingType() _sequenceType = self._getMutableListType() _leafSequenceType = self._getLeafSequenceType() _providedType = self._getProvidedType() def _addValueToLeaf(self, existing_leaf_sequence, new_item): if not existing_leaf_sequence: existing_leaf_sequence = self._leafSequenceType() existing_leaf_sequence.append(new_item) return existing_leaf_sequence def _removeValueFromLeaf(self, existing_leaf_sequence, to_remove): without_removed = BaseAdapterRegistry._removeValueFromLeaf( self, existing_leaf_sequence, to_remove) existing_leaf_sequence[:] = without_removed assert to_remove not in existing_leaf_sequence return existing_leaf_sequence return CustomAdapterRegistry def assertLeafIdentity(self, leaf1, leaf2): self.assertIs(leaf1, leaf2) class LookupBaseFallbackTests(unittest.TestCase): def _getFallbackClass(self): from zope.interface.adapter import LookupBaseFallback return LookupBaseFallback _getTargetClass = _getFallbackClass def _makeOne(self, uc_lookup=None, uc_lookupAll=None, uc_subscriptions=None): # pylint:disable=function-redefined if uc_lookup is None: def uc_lookup(self, required, provided, name): pass if uc_lookupAll is None: def uc_lookupAll(self, required, provided): raise NotImplementedError() if uc_subscriptions is None: def uc_subscriptions(self, required, provided): raise NotImplementedError() class Derived(self._getTargetClass()): _uncached_lookup = uc_lookup _uncached_lookupAll = uc_lookupAll _uncached_subscriptions = uc_subscriptions return Derived() def test_lookup_w_invalid_name(self): def _lookup(self, required, provided, name): self.fail("This should never be called") lb = self._makeOne(uc_lookup=_lookup) with self.assertRaises(ValueError): lb.lookup(('A',), 'B', object()) def test_lookup_miss_no_default(self): _called_with = [] def _lookup(self, required, provided, name): _called_with.append((required, provided, name)) lb = self._makeOne(uc_lookup=_lookup) found = lb.lookup(('A',), 'B', 'C') self.assertIsNone(found) self.assertEqual(_called_with, [(('A',), 'B', 'C')]) def test_lookup_miss_w_default(self): _called_with = [] _default = object() def _lookup(self, required, provided, name): _called_with.append((required, provided, name)) lb = self._makeOne(uc_lookup=_lookup) found = lb.lookup(('A',), 'B', 'C', _default) self.assertIs(found, _default) self.assertEqual(_called_with, [(('A',), 'B', 'C')]) def test_lookup_not_cached(self): _called_with = [] a, b, c = object(), object(), object() _results = [a, b, c] def _lookup(self, required, provided, name): _called_with.append((required, provided, name)) return _results.pop(0) lb = self._makeOne(uc_lookup=_lookup) found = lb.lookup(('A',), 'B', 'C') self.assertIs(found, a) self.assertEqual(_called_with, [(('A',), 'B', 'C')]) self.assertEqual(_results, [b, c]) def test_lookup_cached(self): _called_with = [] a, b, c = object(), object(), object() _results = [a, b, c] def _lookup(self, required, provided, name): _called_with.append((required, provided, name)) return _results.pop(0) lb = self._makeOne(uc_lookup=_lookup) found = lb.lookup(('A',), 'B', 'C') found = lb.lookup(('A',), 'B', 'C') self.assertIs(found, a) self.assertEqual(_called_with, [(('A',), 'B', 'C')]) self.assertEqual(_results, [b, c]) def test_lookup_not_cached_multi_required(self): _called_with = [] a, b, c = object(), object(), object() _results = [a, b, c] def _lookup(self, required, provided, name): _called_with.append((required, provided, name)) return _results.pop(0) lb = self._makeOne(uc_lookup=_lookup) found = lb.lookup(('A', 'D'), 'B', 'C') self.assertIs(found, a) self.assertEqual(_called_with, [(('A', 'D'), 'B', 'C')]) self.assertEqual(_results, [b, c]) def test_lookup_cached_multi_required(self): _called_with = [] a, b, c = object(), object(), object() _results = [a, b, c] def _lookup(self, required, provided, name): _called_with.append((required, provided, name)) return _results.pop(0) lb = self._makeOne(uc_lookup=_lookup) found = lb.lookup(('A', 'D'), 'B', 'C') found = lb.lookup(('A', 'D'), 'B', 'C') self.assertIs(found, a) self.assertEqual(_called_with, [(('A', 'D'), 'B', 'C')]) self.assertEqual(_results, [b, c]) def test_lookup_not_cached_after_changed(self): _called_with = [] a, b, c = object(), object(), object() _results = [a, b, c] def _lookup(self, required, provided, name): _called_with.append((required, provided, name)) return _results.pop(0) lb = self._makeOne(uc_lookup=_lookup) found = lb.lookup(('A',), 'B', 'C') lb.changed(lb) found = lb.lookup(('A',), 'B', 'C') self.assertIs(found, b) self.assertEqual(_called_with, [(('A',), 'B', 'C'), (('A',), 'B', 'C')]) self.assertEqual(_results, [c]) def test_lookup1_w_invalid_name(self): def _lookup(self, required, provided, name): self.fail("This should never be called") lb = self._makeOne(uc_lookup=_lookup) with self.assertRaises(ValueError): lb.lookup1('A', 'B', object()) def test_lookup1_miss_no_default(self): _called_with = [] def _lookup(self, required, provided, name): _called_with.append((required, provided, name)) lb = self._makeOne(uc_lookup=_lookup) found = lb.lookup1('A', 'B', 'C') self.assertIsNone(found) self.assertEqual(_called_with, [(('A',), 'B', 'C')]) def test_lookup1_miss_w_default(self): _called_with = [] _default = object() def _lookup(self, required, provided, name): _called_with.append((required, provided, name)) lb = self._makeOne(uc_lookup=_lookup) found = lb.lookup1('A', 'B', 'C', _default) self.assertIs(found, _default) self.assertEqual(_called_with, [(('A',), 'B', 'C')]) def test_lookup1_miss_w_default_negative_cache(self): _called_with = [] _default = object() def _lookup(self, required, provided, name): _called_with.append((required, provided, name)) lb = self._makeOne(uc_lookup=_lookup) found = lb.lookup1('A', 'B', 'C', _default) self.assertIs(found, _default) found = lb.lookup1('A', 'B', 'C', _default) self.assertIs(found, _default) self.assertEqual(_called_with, [(('A',), 'B', 'C')]) def test_lookup1_not_cached(self): _called_with = [] a, b, c = object(), object(), object() _results = [a, b, c] def _lookup(self, required, provided, name): _called_with.append((required, provided, name)) return _results.pop(0) lb = self._makeOne(uc_lookup=_lookup) found = lb.lookup1('A', 'B', 'C') self.assertIs(found, a) self.assertEqual(_called_with, [(('A',), 'B', 'C')]) self.assertEqual(_results, [b, c]) def test_lookup1_cached(self): _called_with = [] a, b, c = object(), object(), object() _results = [a, b, c] def _lookup(self, required, provided, name): _called_with.append((required, provided, name)) return _results.pop(0) lb = self._makeOne(uc_lookup=_lookup) found = lb.lookup1('A', 'B', 'C') found = lb.lookup1('A', 'B', 'C') self.assertIs(found, a) self.assertEqual(_called_with, [(('A',), 'B', 'C')]) self.assertEqual(_results, [b, c]) def test_lookup1_not_cached_after_changed(self): _called_with = [] a, b, c = object(), object(), object() _results = [a, b, c] def _lookup(self, required, provided, name): _called_with.append((required, provided, name)) return _results.pop(0) lb = self._makeOne(uc_lookup=_lookup) found = lb.lookup1('A', 'B', 'C') lb.changed(lb) found = lb.lookup1('A', 'B', 'C') self.assertIs(found, b) self.assertEqual(_called_with, [(('A',), 'B', 'C'), (('A',), 'B', 'C')]) self.assertEqual(_results, [c]) def test_adapter_hook_w_invalid_name(self): req, prv = object(), object() lb = self._makeOne() with self.assertRaises(ValueError): lb.adapter_hook(prv, req, object()) def test_adapter_hook_miss_no_default(self): req, prv = object(), object() lb = self._makeOne() found = lb.adapter_hook(prv, req, '') self.assertIsNone(found) def test_adapter_hook_miss_w_default(self): req, prv, _default = object(), object(), object() lb = self._makeOne() found = lb.adapter_hook(prv, req, '', _default) self.assertIs(found, _default) def test_adapter_hook_hit_factory_returns_None(self): _f_called_with = [] def _factory(context): _f_called_with.append(context) def _lookup(self, required, provided, name): return _factory req, prv, _default = object(), object(), object() lb = self._makeOne(uc_lookup=_lookup) adapted = lb.adapter_hook(prv, req, 'C', _default) self.assertIs(adapted, _default) self.assertEqual(_f_called_with, [req]) def test_adapter_hook_hit_factory_returns_adapter(self): _f_called_with = [] _adapter = object() def _factory(context): _f_called_with.append(context) return _adapter def _lookup(self, required, provided, name): return _factory req, prv, _default = object(), object(), object() lb = self._makeOne(uc_lookup=_lookup) adapted = lb.adapter_hook(prv, req, 'C', _default) self.assertIs(adapted, _adapter) self.assertEqual(_f_called_with, [req]) def test_adapter_hook_super_unwraps(self): _f_called_with = [] def _factory(context): _f_called_with.append(context) return context def _lookup(self, required, provided, name=''): return _factory required = super() provided = object() lb = self._makeOne(uc_lookup=_lookup) adapted = lb.adapter_hook(provided, required) self.assertIs(adapted, self) self.assertEqual(_f_called_with, [self]) def test_queryAdapter(self): _f_called_with = [] _adapter = object() def _factory(context): _f_called_with.append(context) return _adapter def _lookup(self, required, provided, name): return _factory req, prv, _default = object(), object(), object() lb = self._makeOne(uc_lookup=_lookup) adapted = lb.queryAdapter(req, prv, 'C', _default) self.assertIs(adapted, _adapter) self.assertEqual(_f_called_with, [req]) def test_lookupAll_uncached(self): _called_with = [] _results = [object(), object(), object()] def _lookupAll(self, required, provided): _called_with.append((required, provided)) return tuple(_results) lb = self._makeOne(uc_lookupAll=_lookupAll) found = lb.lookupAll('A', 'B') self.assertEqual(found, tuple(_results)) self.assertEqual(_called_with, [(('A',), 'B')]) def test_lookupAll_cached(self): _called_with = [] _results = [object(), object(), object()] def _lookupAll(self, required, provided): _called_with.append((required, provided)) return tuple(_results) lb = self._makeOne(uc_lookupAll=_lookupAll) found = lb.lookupAll('A', 'B') found = lb.lookupAll('A', 'B') self.assertEqual(found, tuple(_results)) self.assertEqual(_called_with, [(('A',), 'B')]) def test_subscriptions_uncached(self): _called_with = [] _results = [object(), object(), object()] def _subscriptions(self, required, provided): _called_with.append((required, provided)) return tuple(_results) lb = self._makeOne(uc_subscriptions=_subscriptions) found = lb.subscriptions('A', 'B') self.assertEqual(found, tuple(_results)) self.assertEqual(_called_with, [(('A',), 'B')]) def test_subscriptions_cached(self): _called_with = [] _results = [object(), object(), object()] def _subscriptions(self, required, provided): _called_with.append((required, provided)) return tuple(_results) lb = self._makeOne(uc_subscriptions=_subscriptions) found = lb.subscriptions('A', 'B') found = lb.subscriptions('A', 'B') self.assertEqual(found, tuple(_results)) self.assertEqual(_called_with, [(('A',), 'B')]) class LookupBaseTests(LookupBaseFallbackTests, OptimizationTestMixin): def _getTargetClass(self): from zope.interface.adapter import LookupBase return LookupBase class VerifyingBaseFallbackTests(unittest.TestCase): def _getFallbackClass(self): from zope.interface.adapter import VerifyingBaseFallback return VerifyingBaseFallback _getTargetClass = _getFallbackClass def _makeOne(self, registry, uc_lookup=None, uc_lookupAll=None, uc_subscriptions=None): # pylint:disable=function-redefined if uc_lookup is None: def uc_lookup(self, required, provided, name): raise NotImplementedError() if uc_lookupAll is None: def uc_lookupAll(self, required, provided): raise NotImplementedError() if uc_subscriptions is None: def uc_subscriptions(self, required, provided): raise NotImplementedError() class Derived(self._getTargetClass()): _uncached_lookup = uc_lookup _uncached_lookupAll = uc_lookupAll _uncached_subscriptions = uc_subscriptions def __init__(self, registry): super().__init__() self._registry = registry derived = Derived(registry) derived.changed(derived) # init. '_verify_ro' / '_verify_generations' return derived def _makeRegistry(self, depth): class WithGeneration: _generation = 1 class Registry: def __init__(self, depth): self.ro = [WithGeneration() for i in range(depth)] return Registry(depth) def test_lookup(self): _called_with = [] a, b, c = object(), object(), object() _results = [a, b, c] def _lookup(self, required, provided, name): _called_with.append((required, provided, name)) return _results.pop(0) reg = self._makeRegistry(3) lb = self._makeOne(reg, uc_lookup=_lookup) found = lb.lookup(('A',), 'B', 'C') found = lb.lookup(('A',), 'B', 'C') self.assertIs(found, a) self.assertEqual(_called_with, [(('A',), 'B', 'C')]) self.assertEqual(_results, [b, c]) reg.ro[1]._generation += 1 found = lb.lookup(('A',), 'B', 'C') self.assertIs(found, b) self.assertEqual(_called_with, [(('A',), 'B', 'C'), (('A',), 'B', 'C')]) self.assertEqual(_results, [c]) def test_lookup1(self): _called_with = [] a, b, c = object(), object(), object() _results = [a, b, c] def _lookup(self, required, provided, name): _called_with.append((required, provided, name)) return _results.pop(0) reg = self._makeRegistry(3) lb = self._makeOne(reg, uc_lookup=_lookup) found = lb.lookup1('A', 'B', 'C') found = lb.lookup1('A', 'B', 'C') self.assertIs(found, a) self.assertEqual(_called_with, [(('A',), 'B', 'C')]) self.assertEqual(_results, [b, c]) reg.ro[1]._generation += 1 found = lb.lookup1('A', 'B', 'C') self.assertIs(found, b) self.assertEqual(_called_with, [(('A',), 'B', 'C'), (('A',), 'B', 'C')]) self.assertEqual(_results, [c]) def test_adapter_hook(self): a, b, _c = [object(), object(), object()] def _factory1(context): return a def _factory2(context): return b def _factory3(context): self.fail("This should never be called") _factories = [_factory1, _factory2, _factory3] def _lookup(self, required, provided, name): return _factories.pop(0) req, prv, _default = object(), object(), object() reg = self._makeRegistry(3) lb = self._makeOne(reg, uc_lookup=_lookup) adapted = lb.adapter_hook(prv, req, 'C', _default) self.assertIs(adapted, a) adapted = lb.adapter_hook(prv, req, 'C', _default) self.assertIs(adapted, a) reg.ro[1]._generation += 1 adapted = lb.adapter_hook(prv, req, 'C', _default) self.assertIs(adapted, b) def test_queryAdapter(self): a, b, _c = [object(), object(), object()] def _factory1(context): return a def _factory2(context): return b def _factory3(context): self.fail("This should never be called") _factories = [_factory1, _factory2, _factory3] def _lookup(self, required, provided, name): return _factories.pop(0) req, prv, _default = object(), object(), object() reg = self._makeRegistry(3) lb = self._makeOne(reg, uc_lookup=_lookup) adapted = lb.queryAdapter(req, prv, 'C', _default) self.assertIs(adapted, a) adapted = lb.queryAdapter(req, prv, 'C', _default) self.assertIs(adapted, a) reg.ro[1]._generation += 1 adapted = lb.adapter_hook(prv, req, 'C', _default) self.assertIs(adapted, b) def test_lookupAll(self): _results_1 = [object(), object(), object()] _results_2 = [object(), object(), object()] _results = [_results_1, _results_2] def _lookupAll(self, required, provided): return tuple(_results.pop(0)) reg = self._makeRegistry(3) lb = self._makeOne(reg, uc_lookupAll=_lookupAll) found = lb.lookupAll('A', 'B') self.assertEqual(found, tuple(_results_1)) found = lb.lookupAll('A', 'B') self.assertEqual(found, tuple(_results_1)) reg.ro[1]._generation += 1 found = lb.lookupAll('A', 'B') self.assertEqual(found, tuple(_results_2)) def test_subscriptions(self): _results_1 = [object(), object(), object()] _results_2 = [object(), object(), object()] _results = [_results_1, _results_2] def _subscriptions(self, required, provided): return tuple(_results.pop(0)) reg = self._makeRegistry(3) lb = self._makeOne(reg, uc_subscriptions=_subscriptions) found = lb.subscriptions('A', 'B') self.assertEqual(found, tuple(_results_1)) found = lb.subscriptions('A', 'B') self.assertEqual(found, tuple(_results_1)) reg.ro[1]._generation += 1 found = lb.subscriptions('A', 'B') self.assertEqual(found, tuple(_results_2)) class VerifyingBaseTests(VerifyingBaseFallbackTests, OptimizationTestMixin): def _getTargetClass(self): from zope.interface.adapter import VerifyingBase return VerifyingBase class AdapterLookupBaseTests(unittest.TestCase): def _getTargetClass(self): from zope.interface.adapter import AdapterLookupBase return AdapterLookupBase def _makeOne(self, registry): return self._getTargetClass()(registry) def _makeSubregistry(self, *provided): class Subregistry: def __init__(self): self._adapters = [] self._subscribers = [] return Subregistry() def _makeRegistry(self, *provided): class Registry: def __init__(self, provided): self._provided = provided self.ro = [] return Registry(provided) def test_ctor_empty_registry(self): registry = self._makeRegistry() alb = self._makeOne(registry) self.assertEqual(alb._extendors, {}) def test_ctor_w_registry_provided(self): from zope.interface import Interface from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry(IFoo, IBar) alb = self._makeOne(registry) self.assertEqual(sorted(alb._extendors.keys()), sorted([IBar, IFoo, Interface])) self.assertEqual(alb._extendors[IFoo], [IFoo, IBar]) self.assertEqual(alb._extendors[IBar], [IBar]) self.assertEqual(sorted(alb._extendors[Interface]), sorted([IFoo, IBar])) def test_changed_empty_required(self): # ALB.changed expects to call a mixed in changed. class Mixin: def changed(self, *other): pass class Derived(self._getTargetClass(), Mixin): pass registry = self._makeRegistry() alb = Derived(registry) alb.changed(alb) def test_changed_w_required(self): # ALB.changed expects to call a mixed in changed. class Mixin: def changed(self, *other): pass class Derived(self._getTargetClass(), Mixin): pass class FauxWeakref: _unsub = None def __init__(self, here): self._here = here def __call__(self): return self if self._here else None def unsubscribe(self, target): self._unsub = target gone = FauxWeakref(False) here = FauxWeakref(True) registry = self._makeRegistry() alb = Derived(registry) alb._required[gone] = 1 alb._required[here] = 1 alb.changed(alb) self.assertEqual(len(alb._required), 0) self.assertEqual(gone._unsub, None) self.assertEqual(here._unsub, alb) def test_init_extendors_after_registry_update(self): from zope.interface import Interface from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry() alb = self._makeOne(registry) registry._provided = [IFoo, IBar] alb.init_extendors() self.assertEqual(sorted(alb._extendors.keys()), sorted([IBar, IFoo, Interface])) self.assertEqual(alb._extendors[IFoo], [IFoo, IBar]) self.assertEqual(alb._extendors[IBar], [IBar]) self.assertEqual(sorted(alb._extendors[Interface]), sorted([IFoo, IBar])) def test_add_extendor(self): from zope.interface import Interface from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry() alb = self._makeOne(registry) alb.add_extendor(IFoo) alb.add_extendor(IBar) self.assertEqual(sorted(alb._extendors.keys()), sorted([IBar, IFoo, Interface])) self.assertEqual(alb._extendors[IFoo], [IFoo, IBar]) self.assertEqual(alb._extendors[IBar], [IBar]) self.assertEqual(sorted(alb._extendors[Interface]), sorted([IFoo, IBar])) def test_remove_extendor(self): from zope.interface import Interface from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry(IFoo, IBar) alb = self._makeOne(registry) alb.remove_extendor(IFoo) self.assertEqual(sorted(alb._extendors.keys()), sorted([IFoo, IBar, Interface])) self.assertEqual(alb._extendors[IFoo], [IBar]) self.assertEqual(alb._extendors[IBar], [IBar]) self.assertEqual(sorted(alb._extendors[Interface]), sorted([IBar])) # test '_subscribe' via its callers, '_uncached_lookup', etc. def test__uncached_lookup_empty_ro(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry() alb = self._makeOne(registry) result = alb._uncached_lookup((IFoo,), IBar) self.assertEqual(result, None) self.assertEqual(len(alb._required), 1) self.assertIn(IFoo.weakref(), alb._required) def test__uncached_lookup_order_miss(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() registry.ro.append(subr) alb = self._makeOne(registry) result = alb._uncached_lookup((IFoo,), IBar) self.assertEqual(result, None) def test__uncached_lookup_extendors_miss(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry() subr = self._makeSubregistry() subr._adapters = [{}, {}] #utilities, single adapters registry.ro.append(subr) alb = self._makeOne(registry) subr._v_lookup = alb result = alb._uncached_lookup((IFoo,), IBar) self.assertEqual(result, None) def test__uncached_lookup_components_miss_wrong_iface(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) IQux = InterfaceClass('IQux') registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() irrelevant = object() subr._adapters = [ #utilities, single adapters {}, {IFoo: {IQux: {'': irrelevant}, }}, ] registry.ro.append(subr) alb = self._makeOne(registry) subr._v_lookup = alb result = alb._uncached_lookup((IFoo,), IBar) self.assertEqual(result, None) def test__uncached_lookup_components_miss_wrong_name(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() wrongname = object() subr._adapters = [ #utilities, single adapters {}, {IFoo: {IBar: {'wrongname': wrongname}, }}, ] registry.ro.append(subr) alb = self._makeOne(registry) subr._v_lookup = alb result = alb._uncached_lookup((IFoo,), IBar) self.assertEqual(result, None) def test__uncached_lookup_simple_hit(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() _expected = object() subr._adapters = [ #utilities, single adapters {}, {IFoo: {IBar: {'': _expected}}}, ] registry.ro.append(subr) alb = self._makeOne(registry) subr._v_lookup = alb result = alb._uncached_lookup((IFoo,), IBar) self.assertIs(result, _expected) def test__uncached_lookup_repeated_hit(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() _expected = object() subr._adapters = [ #utilities, single adapters {}, {IFoo: {IBar: {'': _expected}}}, ] registry.ro.append(subr) alb = self._makeOne(registry) subr._v_lookup = alb result = alb._uncached_lookup((IFoo,), IBar) result2 = alb._uncached_lookup((IFoo,), IBar) self.assertIs(result, _expected) self.assertIs(result2, _expected) def test_queryMultiAdaptor_lookup_miss(self): from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) @implementer(IFoo) class Foo: pass foo = Foo() registry = self._makeRegistry() subr = self._makeSubregistry() subr._adapters = [ #utilities, single adapters {}, {}, ] registry.ro.append(subr) alb = self._makeOne(registry) alb.lookup = alb._uncached_lookup # provided by derived subr._v_lookup = alb _default = object() result = alb.queryMultiAdapter((foo,), IBar, default=_default) self.assertIs(result, _default) def test_queryMultiAdapter_errors_on_attribute_access(self): # Any error on attribute access previously lead to using the _empty singleton as "requires" # argument (See https://github.com/zopefoundation/zope.interface/issues/162) # but after https://github.com/zopefoundation/zope.interface/issues/200 # they get propagated. from zope.interface.interface import InterfaceClass from zope.interface.tests import MissingSomeAttrs IFoo = InterfaceClass('IFoo') registry = self._makeRegistry() alb = self._makeOne(registry) alb.lookup = alb._uncached_lookup def test(ob): return alb.queryMultiAdapter( (ob,), IFoo, ) MissingSomeAttrs.test_raises(self, test, expected_missing='__class__') def test_queryMultiAdaptor_factory_miss(self): from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) @implementer(IFoo) class Foo: pass foo = Foo() registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() _expected = object() _called_with = [] def _factory(context): _called_with.append(context) subr._adapters = [ #utilities, single adapters {}, {IFoo: {IBar: {'': _factory}}}, ] registry.ro.append(subr) alb = self._makeOne(registry) alb.lookup = alb._uncached_lookup # provided by derived subr._v_lookup = alb _default = object() result = alb.queryMultiAdapter((foo,), IBar, default=_default) self.assertIs(result, _default) self.assertEqual(_called_with, [foo]) def test_queryMultiAdaptor_factory_hit(self): from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) @implementer(IFoo) class Foo: pass foo = Foo() registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() _expected = object() _called_with = [] def _factory(context): _called_with.append(context) return _expected subr._adapters = [ #utilities, single adapters {}, {IFoo: {IBar: {'': _factory}}}, ] registry.ro.append(subr) alb = self._makeOne(registry) alb.lookup = alb._uncached_lookup # provided by derived subr._v_lookup = alb _default = object() result = alb.queryMultiAdapter((foo,), IBar, default=_default) self.assertIs(result, _expected) self.assertEqual(_called_with, [foo]) def test_queryMultiAdapter_super_unwraps(self): alb = self._makeOne(self._makeRegistry()) def lookup(*args): return factory def factory(*args): return args alb.lookup = lookup objects = [ super(), 42, "abc", super(), ] result = alb.queryMultiAdapter(objects, None) self.assertEqual(result, ( self, 42, "abc", self, )) def test__uncached_lookupAll_empty_ro(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry() alb = self._makeOne(registry) result = alb._uncached_lookupAll((IFoo,), IBar) self.assertEqual(result, ()) self.assertEqual(len(alb._required), 1) self.assertIn(IFoo.weakref(), alb._required) def test__uncached_lookupAll_order_miss(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() registry.ro.append(subr) alb = self._makeOne(registry) subr._v_lookup = alb result = alb._uncached_lookupAll((IFoo,), IBar) self.assertEqual(result, ()) def test__uncached_lookupAll_extendors_miss(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry() subr = self._makeSubregistry() subr._adapters = [{}, {}] #utilities, single adapters registry.ro.append(subr) alb = self._makeOne(registry) subr._v_lookup = alb result = alb._uncached_lookupAll((IFoo,), IBar) self.assertEqual(result, ()) def test__uncached_lookupAll_components_miss(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) IQux = InterfaceClass('IQux') registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() irrelevant = object() subr._adapters = [ #utilities, single adapters {}, {IFoo: {IQux: {'': irrelevant}}}, ] registry.ro.append(subr) alb = self._makeOne(registry) subr._v_lookup = alb result = alb._uncached_lookupAll((IFoo,), IBar) self.assertEqual(result, ()) def test__uncached_lookupAll_simple_hit(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() _expected = object() _named = object() subr._adapters = [ #utilities, single adapters {}, {IFoo: {IBar: {'': _expected, 'named': _named}}}, ] registry.ro.append(subr) alb = self._makeOne(registry) subr._v_lookup = alb result = alb._uncached_lookupAll((IFoo,), IBar) self.assertEqual(sorted(result), [('', _expected), ('named', _named)]) def test_names(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() _expected = object() _named = object() subr._adapters = [ #utilities, single adapters {}, {IFoo: {IBar: {'': _expected, 'named': _named}}}, ] registry.ro.append(subr) alb = self._makeOne(registry) alb.lookupAll = alb._uncached_lookupAll subr._v_lookup = alb result = alb.names((IFoo,), IBar) self.assertEqual(sorted(result), ['', 'named']) def test__uncached_subscriptions_empty_ro(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry() alb = self._makeOne(registry) result = alb._uncached_subscriptions((IFoo,), IBar) self.assertEqual(result, []) self.assertEqual(len(alb._required), 1) self.assertIn(IFoo.weakref(), alb._required) def test__uncached_subscriptions_order_miss(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() registry.ro.append(subr) alb = self._makeOne(registry) subr._v_lookup = alb result = alb._uncached_subscriptions((IFoo,), IBar) self.assertEqual(result, []) def test__uncached_subscriptions_extendors_miss(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry() subr = self._makeSubregistry() subr._subscribers = [{}, {}] #utilities, single adapters registry.ro.append(subr) alb = self._makeOne(registry) subr._v_lookup = alb result = alb._uncached_subscriptions((IFoo,), IBar) self.assertEqual(result, []) def test__uncached_subscriptions_components_miss_wrong_iface(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) IQux = InterfaceClass('IQux') registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() irrelevant = object() subr._subscribers = [ #utilities, single adapters {}, {IFoo: {IQux: {'': irrelevant}}}, ] registry.ro.append(subr) alb = self._makeOne(registry) subr._v_lookup = alb result = alb._uncached_subscriptions((IFoo,), IBar) self.assertEqual(result, []) def test__uncached_subscriptions_components_miss_wrong_name(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() wrongname = object() subr._subscribers = [ #utilities, single adapters {}, {IFoo: {IBar: {'wrongname': wrongname}}}, ] registry.ro.append(subr) alb = self._makeOne(registry) subr._v_lookup = alb result = alb._uncached_subscriptions((IFoo,), IBar) self.assertEqual(result, []) def test__uncached_subscriptions_simple_hit(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() class Foo: def __lt__(self, other): return True _exp1, _exp2 = Foo(), Foo() subr._subscribers = [ #utilities, single adapters {}, {IFoo: {IBar: {'': (_exp1, _exp2)}}}, ] registry.ro.append(subr) alb = self._makeOne(registry) subr._v_lookup = alb result = alb._uncached_subscriptions((IFoo,), IBar) self.assertEqual(sorted(result), sorted([_exp1, _exp2])) def test_subscribers_wo_provided(self): from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) @implementer(IFoo) class Foo: pass foo = Foo() registry = self._makeRegistry(IFoo, IBar) registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() _called = {} def _factory1(context): _called.setdefault('_factory1', []).append(context) def _factory2(context): _called.setdefault('_factory2', []).append(context) subr._subscribers = [ #utilities, single adapters {}, {IFoo: {None: {'': (_factory1, _factory2)}}}, ] registry.ro.append(subr) alb = self._makeOne(registry) alb.subscriptions = alb._uncached_subscriptions subr._v_lookup = alb result = alb.subscribers((foo,), None) self.assertEqual(result, ()) self.assertEqual(_called, {'_factory1': [foo], '_factory2': [foo]}) def test_subscribers_w_provided(self): from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) @implementer(IFoo) class Foo: pass foo = Foo() registry = self._makeRegistry(IFoo, IBar) registry = self._makeRegistry(IFoo, IBar) subr = self._makeSubregistry() _called = {} _exp1, _exp2 = object(), object() def _factory1(context): _called.setdefault('_factory1', []).append(context) return _exp1 def _factory2(context): _called.setdefault('_factory2', []).append(context) return _exp2 def _side_effect_only(context): _called.setdefault('_side_effect_only', []).append(context) subr._subscribers = [ #utilities, single adapters {}, {IFoo: {IBar: {'': (_factory1, _factory2, _side_effect_only)}}}, ] registry.ro.append(subr) alb = self._makeOne(registry) alb.subscriptions = alb._uncached_subscriptions subr._v_lookup = alb result = alb.subscribers((foo,), IBar) self.assertEqual(result, [_exp1, _exp2]) self.assertEqual(_called, {'_factory1': [foo], '_factory2': [foo], '_side_effect_only': [foo], }) class VerifyingAdapterRegistryTests(unittest.TestCase): # This is also the base for AdapterRegistryTests. That makes the # inheritance seems backwards, but even though they implement the # same interfaces, VAR and AR each only extend BAR; and neither # one will pass the test cases for BAR (it uses a special # LookupClass just for the tests). def _getTargetClass(self): from zope.interface.adapter import VerifyingAdapterRegistry return VerifyingAdapterRegistry def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_verify_object_provides_IAdapterRegistry(self): from zope.interface.interfaces import IAdapterRegistry from zope.interface.verify import verifyObject registry = self._makeOne() verifyObject(IAdapterRegistry, registry) class AdapterRegistryTests(VerifyingAdapterRegistryTests): def _getTargetClass(self): from zope.interface.adapter import AdapterRegistry return AdapterRegistry def test_ctor_no_bases(self): ar = self._makeOne() self.assertEqual(len(ar._v_subregistries), 0) def test_ctor_w_bases(self): base = self._makeOne() sub = self._makeOne([base]) self.assertEqual(len(sub._v_subregistries), 0) self.assertEqual(len(base._v_subregistries), 1) self.assertIn(sub, base._v_subregistries) # test _addSubregistry / _removeSubregistry via only caller, _setBases def test__setBases_removing_existing_subregistry(self): before = self._makeOne() after = self._makeOne() sub = self._makeOne([before]) sub.__bases__ = [after] self.assertEqual(len(before._v_subregistries), 0) self.assertEqual(len(after._v_subregistries), 1) self.assertIn(sub, after._v_subregistries) def test__setBases_wo_stray_entry(self): before = self._makeOne() stray = self._makeOne() after = self._makeOne() sub = self._makeOne([before]) sub.__dict__['__bases__'].append(stray) sub.__bases__ = [after] self.assertEqual(len(before._v_subregistries), 0) self.assertEqual(len(after._v_subregistries), 1) self.assertIn(sub, after._v_subregistries) def test__setBases_w_existing_entry_continuing(self): before = self._makeOne() after = self._makeOne() sub = self._makeOne([before]) sub.__bases__ = [before, after] self.assertEqual(len(before._v_subregistries), 1) self.assertEqual(len(after._v_subregistries), 1) self.assertIn(sub, before._v_subregistries) self.assertIn(sub, after._v_subregistries) def test_changed_w_subregistries(self): base = self._makeOne() class Derived: _changed = None def changed(self, originally_changed): self._changed = originally_changed derived1, derived2 = Derived(), Derived() base._addSubregistry(derived1) base._addSubregistry(derived2) orig = object() base.changed(orig) self.assertIs(derived1._changed, orig) self.assertIs(derived2._changed, orig) class Test_utils(unittest.TestCase): def test__convert_None_to_Interface_w_None(self): from zope.interface.adapter import _convert_None_to_Interface from zope.interface.interface import Interface self.assertIs(_convert_None_to_Interface(None), Interface) def test__convert_None_to_Interface_w_other(self): from zope.interface.adapter import _convert_None_to_Interface other = object() self.assertIs(_convert_None_to_Interface(other), other) def test__normalize_name_str(self): from zope.interface.adapter import _normalize_name STR = b'str' UNICODE = 'str' norm = _normalize_name(STR) self.assertEqual(norm, UNICODE) self.assertIsInstance(norm, type(UNICODE)) def test__normalize_name_unicode(self): from zope.interface.adapter import _normalize_name USTR = 'ustr' self.assertEqual(_normalize_name(USTR), USTR) def test__normalize_name_other(self): from zope.interface.adapter import _normalize_name for other in 1, 1.0, (), [], {}, object(): self.assertRaises(TypeError, _normalize_name, other) # _lookup, _lookupAll, and _subscriptions tested via their callers # (AdapterLookupBase.{lookup,lookupAll,subscriptions}). zope.interface-6.4/src/zope/interface/tests/test_advice.py000066400000000000000000000136301462121350100240070ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2003 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. # ############################################################################## """Tests for advice This module was adapted from 'protocols.tests.advice', part of the Python Enterprise Application Kit (PEAK). Please notify the PEAK authors (pje@telecommunity.com and tsarna@sarna.org) if bugs are found or Zope-specific changes are required, so that the PEAK version of this module can be kept in sync. PEAK is a Python application framework that interoperates with (but does not require) Zope 3 and Twisted. It provides tools for manipulating UML models, object-relational persistence, aspect-oriented programming, and more. Visit the PEAK home page at http://peak.telecommunity.com for more information. """ import sys import unittest class FrameInfoTest(unittest.TestCase): def test_w_module(self): from zope.interface.tests import advisory_testing (kind, module, f_locals, f_globals) = advisory_testing.moduleLevelFrameInfo self.assertEqual(kind, "module") for d in module.__dict__, f_locals, f_globals: self.assertTrue(d is advisory_testing.my_globals) def test_w_class(self): from zope.interface.tests import advisory_testing (kind, module, f_locals, f_globals) = advisory_testing.NewStyleClass.classLevelFrameInfo self.assertEqual(kind, "class") for d in module.__dict__, f_globals: self.assertTrue(d is advisory_testing.my_globals) def test_inside_function_call(self): from zope.interface.advice import getFrameInfo kind, module, f_locals, f_globals = getFrameInfo(sys._getframe()) self.assertEqual(kind, "function call") frame = sys._getframe() self.assertEqual(f_locals, frame.f_locals) self.assertEqual(f_locals, locals()) for d in module.__dict__, f_globals: self.assertTrue(d is globals()) def test_inside_exec(self): from zope.interface.advice import getFrameInfo _globals = {'getFrameInfo': getFrameInfo} _locals = {} exec(_FUNKY_EXEC, _globals, _locals) self.assertEqual(_locals['kind'], "exec") self.assertTrue(_locals['f_locals'] is _locals) self.assertTrue(_locals['module'] is None) self.assertTrue(_locals['f_globals'] is _globals) _FUNKY_EXEC = """\ import sys kind, module, f_locals, f_globals = getFrameInfo(sys._getframe()) """ class Test_isClassAdvisor(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.interface.advice import isClassAdvisor return isClassAdvisor(*args, **kw) def test_w_non_function(self): self.assertEqual(self._callFUT(self), False) def test_w_normal_function(self): def foo(): raise NotImplementedError() self.assertEqual(self._callFUT(foo), False) def test_w_advisor_function(self): def bar(): raise NotImplementedError() bar.previousMetaclass = object() self.assertEqual(self._callFUT(bar), True) class Test_determineMetaclass(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.interface.advice import determineMetaclass return determineMetaclass(*args, **kw) def test_empty_w_explicit_metatype(self): class Meta(type): pass self.assertEqual(self._callFUT((), Meta), Meta) def test_single(self): class Meta(type): pass self.assertEqual(self._callFUT((Meta,)), type) def test_meta_of_class(self): class Metameta(type): pass class Meta(type, metaclass=Metameta): pass self.assertEqual(self._callFUT((Meta, type)), Metameta) def test_multiple_in_hierarchy_py3k(self): class Meta_A(type): pass class Meta_B(Meta_A): pass class A(type, metaclass=Meta_A): pass class B(type, metaclass=Meta_B): pass self.assertEqual(self._callFUT((A, B)), Meta_B) def test_multiple_not_in_hierarchy_py3k(self): class Meta_A(type): pass class Meta_B(type): pass class A(type, metaclass=Meta_A): pass class B(type, metaclass=Meta_B): pass self.assertRaises(TypeError, self._callFUT, (A, B)) class Test_minimalBases(unittest.TestCase): def _callFUT(self, klasses): from zope.interface.advice import minimalBases return minimalBases(klasses) def test_empty(self): self.assertEqual(self._callFUT([]), []) def test_w_newstyle_meta(self): self.assertEqual(self._callFUT([type]), [type]) def test_w_newstyle_class(self): class C: pass self.assertEqual(self._callFUT([C]), [C]) def test_simple_hierarchy_skips_implied(self): class A: pass class B(A): pass class C(B): pass class D: pass self.assertEqual(self._callFUT([A, B, C]), [C]) self.assertEqual(self._callFUT([A, C]), [C]) self.assertEqual(self._callFUT([B, C]), [C]) self.assertEqual(self._callFUT([A, B]), [B]) self.assertEqual(self._callFUT([D, B, D]), [B, D]) def test_repeats_kicked_to_end_of_queue(self): class A: pass class B: pass self.assertEqual(self._callFUT([A, B, A]), [B, A]) zope.interface-6.4/src/zope/interface/tests/test_compile_flags.py000066400000000000000000000024121462121350100253540ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2022 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 # ############################################################################## import struct import unittest import zope.interface # noqa: try to load a C module for side effects class TestFloatingPoint(unittest.TestCase): def test_no_fast_math_optimization(self): # Building with -Ofast enables -ffast-math, which sets certain FPU # flags that can cause breakage elsewhere. A library such as BTrees # has no business changing global FPU flags for the entire process. zero_bits = struct.unpack("!Q", struct.pack("!d", 0.0))[0] next_up = zero_bits + 1 smallest_subnormal = struct.unpack("!d", struct.pack("!Q", next_up))[0] self.assertNotEqual(smallest_subnormal, 0.0) zope.interface-6.4/src/zope/interface/tests/test_declarations.py000066400000000000000000002403341462121350100252270ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2003 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. # ############################################################################## """Test the new API for making and checking interface declarations """ import unittest from zope.interface.tests import MissingSomeAttrs from zope.interface.tests import OptimizationTestMixin from zope.interface.tests.test_interface import \ NameAndModuleComparisonTestsMixin # pylint:disable=inherit-non-class,too-many-lines,protected-access # pylint:disable=blacklisted-name,attribute-defined-outside-init class _Py3ClassAdvice: def _run_generated_code(self, code, globs, locs, fails_under_py3k=True, ): # pylint:disable=exec-used,no-member import warnings with warnings.catch_warnings(record=True) as log: warnings.resetwarnings() try: exec(code, globs, locs) except TypeError: return False else: if fails_under_py3k: self.fail("Didn't raise TypeError") return None class NamedTests(unittest.TestCase): def test_class(self): from zope.interface.declarations import named @named('foo') class Foo: pass self.assertEqual(Foo.__component_name__, 'foo') # pylint:disable=no-member def test_function(self): from zope.interface.declarations import named @named('foo') def doFoo(o): raise NotImplementedError() self.assertEqual(doFoo.__component_name__, 'foo') def test_instance(self): from zope.interface.declarations import named class Foo: pass foo = Foo() named('foo')(foo) self.assertEqual(foo.__component_name__, 'foo') # pylint:disable=no-member class EmptyDeclarationTests(unittest.TestCase): # Tests that should pass for all objects that are empty # declarations. This includes a Declaration explicitly created # that way, and the empty ImmutableDeclaration. def _getEmpty(self): from zope.interface.declarations import Declaration return Declaration() def test___iter___empty(self): decl = self._getEmpty() self.assertEqual(list(decl), []) def test_flattened_empty(self): from zope.interface.interface import Interface decl = self._getEmpty() self.assertEqual(list(decl.flattened()), [Interface]) def test___contains___empty(self): from zope.interface.interface import Interface decl = self._getEmpty() self.assertNotIn(Interface, decl) def test_extends_empty(self): from zope.interface.interface import Interface decl = self._getEmpty() self.assertTrue(decl.extends(Interface)) self.assertTrue(decl.extends(Interface, strict=True)) def test_interfaces_empty(self): decl = self._getEmpty() l = list(decl.interfaces()) self.assertEqual(l, []) def test___sro___(self): from zope.interface.interface import Interface decl = self._getEmpty() self.assertEqual(decl.__sro__, (decl, Interface,)) def test___iro___(self): from zope.interface.interface import Interface decl = self._getEmpty() self.assertEqual(decl.__iro__, (Interface,)) def test_get(self): decl = self._getEmpty() self.assertIsNone(decl.get('attr')) self.assertEqual(decl.get('abc', 'def'), 'def') # It's a positive cache only (when it even exists) # so this added nothing. self.assertFalse(decl._v_attrs) def test_changed_w_existing__v_attrs(self): decl = self._getEmpty() decl._v_attrs = object() decl.changed(decl) self.assertFalse(decl._v_attrs) class DeclarationTests(EmptyDeclarationTests): def _getTargetClass(self): from zope.interface.declarations import Declaration return Declaration def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_ctor_no_bases(self): decl = self._makeOne() self.assertEqual(list(decl.__bases__), []) def test_ctor_w_interface_in_bases(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') decl = self._makeOne(IFoo) self.assertEqual(list(decl.__bases__), [IFoo]) def test_ctor_w_implements_in_bases(self): from zope.interface.declarations import Implements impl = Implements() decl = self._makeOne(impl) self.assertEqual(list(decl.__bases__), [impl]) def test_changed_wo_existing__v_attrs(self): decl = self._makeOne() decl.changed(decl) # doesn't raise self.assertIsNone(decl._v_attrs) def test___contains__w_self(self): decl = self._makeOne() self.assertNotIn(decl, decl) def test___contains__w_unrelated_iface(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') decl = self._makeOne() self.assertNotIn(IFoo, decl) def test___contains__w_base_interface(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') decl = self._makeOne(IFoo) self.assertIn(IFoo, decl) def test___iter___single_base(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') decl = self._makeOne(IFoo) self.assertEqual(list(decl), [IFoo]) def test___iter___multiple_bases(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') decl = self._makeOne(IFoo, IBar) self.assertEqual(list(decl), [IFoo, IBar]) def test___iter___inheritance(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) decl = self._makeOne(IBar) self.assertEqual(list(decl), [IBar]) #IBar.interfaces() omits bases def test___iter___w_nested_sequence_overlap(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') decl = self._makeOne(IBar, (IFoo, IBar)) self.assertEqual(list(decl), [IBar, IFoo]) def test_flattened_single_base(self): from zope.interface.interface import Interface from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') decl = self._makeOne(IFoo) self.assertEqual(list(decl.flattened()), [IFoo, Interface]) def test_flattened_multiple_bases(self): from zope.interface.interface import Interface from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') decl = self._makeOne(IFoo, IBar) self.assertEqual(list(decl.flattened()), [IFoo, IBar, Interface]) def test_flattened_inheritance(self): from zope.interface.interface import Interface from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) decl = self._makeOne(IBar) self.assertEqual(list(decl.flattened()), [IBar, IFoo, Interface]) def test_flattened_w_nested_sequence_overlap(self): from zope.interface.interface import Interface from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') # This is the same as calling ``Declaration(IBar, IFoo, IBar)`` # which doesn't make much sense, but here it is. In older # versions of zope.interface, the __iro__ would have been # IFoo, IBar, Interface, which especially makes no sense. decl = self._makeOne(IBar, (IFoo, IBar)) # Note that decl.__iro__ has IFoo first. self.assertEqual(list(decl.flattened()), [IBar, IFoo, Interface]) def test___sub___unrelated_interface(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') before = self._makeOne(IFoo) after = before - IBar self.assertIsInstance(after, self._getTargetClass()) self.assertEqual(list(after), [IFoo]) def test___sub___related_interface(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') before = self._makeOne(IFoo) after = before - IFoo self.assertEqual(list(after), []) def test___sub___related_interface_by_inheritance(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar', (IFoo,)) before = self._makeOne(IBar) after = before - IBar self.assertEqual(list(after), []) def test___add___unrelated_interface(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') before = self._makeOne(IFoo) after = before + IBar self.assertIsInstance(after, self._getTargetClass()) self.assertEqual(list(after), [IFoo, IBar]) def test___add___related_interface(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') IBaz = InterfaceClass('IBaz') before = self._makeOne(IFoo, IBar) other = self._makeOne(IBar, IBaz) after = before + other self.assertEqual(list(after), [IFoo, IBar, IBaz]) def test___add___overlapping_interface(self): # The derived interfaces end up with higher priority, and # don't produce a C3 resolution order violation. This # example produced a C3 error, and the resulting legacy order # used to be wrong ([IBase, IDerived] instead of # the other way). from zope.interface import Interface from zope.interface import ro from zope.interface.interface import InterfaceClass from zope.interface.tests.test_ro import C3Setting IBase = InterfaceClass('IBase') IDerived = InterfaceClass('IDerived', (IBase,)) with C3Setting(ro.C3.STRICT_IRO, True): base = self._makeOne(IBase) after = base + IDerived self.assertEqual(after.__iro__, (IDerived, IBase, Interface)) self.assertEqual(after.__bases__, (IDerived, IBase)) self.assertEqual(list(after), [IDerived, IBase]) def test___add___overlapping_interface_implementedBy(self): # Like test___add___overlapping_interface, but pulling # in a realistic example. This one previously produced a # C3 error, but the resulting legacy order was (somehow) # correct. from zope.interface import Interface from zope.interface import implementedBy from zope.interface import implementer from zope.interface import ro from zope.interface.tests.test_ro import C3Setting class IBase(Interface): pass class IDerived(IBase): pass @implementer(IBase) class Base: pass with C3Setting(ro.C3.STRICT_IRO, True): after = implementedBy(Base) + IDerived self.assertEqual(after.__sro__, (after, IDerived, IBase, Interface)) self.assertEqual(after.__bases__, (IDerived, IBase)) self.assertEqual(list(after), [IDerived, IBase]) class TestImmutableDeclaration(EmptyDeclarationTests): def _getTargetClass(self): from zope.interface.declarations import _ImmutableDeclaration return _ImmutableDeclaration def _getEmpty(self): from zope.interface.declarations import _empty return _empty def test_pickle(self): import pickle copied = pickle.loads(pickle.dumps(self._getEmpty())) self.assertIs(copied, self._getEmpty()) def test_singleton(self): self.assertIs( self._getTargetClass()(), self._getEmpty() ) def test__bases__(self): self.assertEqual(self._getEmpty().__bases__, ()) def test_change__bases__(self): empty = self._getEmpty() empty.__bases__ = () self.assertEqual(self._getEmpty().__bases__, ()) with self.assertRaises(TypeError): empty.__bases__ = (1,) def test_dependents(self): empty = self._getEmpty() deps = empty.dependents self.assertEqual({}, deps) # Doesn't change the return. deps[1] = 2 self.assertEqual({}, empty.dependents) def test_changed(self): # Does nothing, has no visible side-effects self._getEmpty().changed(None) def test_extends_always_false(self): self.assertFalse(self._getEmpty().extends(self)) self.assertFalse(self._getEmpty().extends(self, strict=True)) self.assertFalse(self._getEmpty().extends(self, strict=False)) def test_get_always_default(self): self.assertIsNone(self._getEmpty().get('name')) self.assertEqual(self._getEmpty().get('name', 42), 42) def test_v_attrs(self): decl = self._getEmpty() self.assertEqual(decl._v_attrs, {}) decl._v_attrs['attr'] = 42 self.assertEqual(decl._v_attrs, {}) self.assertIsNone(decl.get('attr')) attrs = decl._v_attrs = {} attrs['attr'] = 42 self.assertEqual(decl._v_attrs, {}) self.assertIsNone(decl.get('attr')) class TestImplements(NameAndModuleComparisonTestsMixin, unittest.TestCase): def _getTargetClass(self): from zope.interface.declarations import Implements return Implements def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def _makeOneToCompare(self): from zope.interface.declarations import implementedBy class A: pass return implementedBy(A) def test_ctor_no_bases(self): impl = self._makeOne() self.assertEqual(impl.inherit, None) self.assertEqual(impl.declared, ()) self.assertEqual(impl.__name__, '?') self.assertEqual(list(impl.__bases__), []) def test___repr__(self): impl = self._makeOne() impl.__name__ = 'Testing' self.assertEqual(repr(impl), 'classImplements(Testing)') def test___reduce__(self): from zope.interface.declarations import implementedBy impl = self._makeOne() self.assertEqual(impl.__reduce__(), (implementedBy, (None,))) def test_sort(self): from zope.interface.declarations import implementedBy class A: pass class B: pass from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') self.assertEqual(implementedBy(A), implementedBy(A)) self.assertEqual(hash(implementedBy(A)), hash(implementedBy(A))) self.assertTrue(implementedBy(A) < None) self.assertTrue(None > implementedBy(A)) # pylint:disable=misplaced-comparison-constant self.assertTrue(implementedBy(A) < implementedBy(B)) self.assertTrue(implementedBy(A) > IFoo) self.assertTrue(implementedBy(A) <= implementedBy(B)) self.assertTrue(implementedBy(A) >= IFoo) self.assertTrue(implementedBy(A) != IFoo) def test_proxy_equality(self): # https://github.com/zopefoundation/zope.interface/issues/55 class Proxy: def __init__(self, wrapped): self._wrapped = wrapped def __getattr__(self, name): raise NotImplementedError() def __eq__(self, other): return self._wrapped == other def __ne__(self, other): return self._wrapped != other from zope.interface.declarations import implementedBy class A: pass class B: pass implementedByA = implementedBy(A) implementedByB = implementedBy(B) proxy = Proxy(implementedByA) # The order of arguments to the operators matters, # test both self.assertTrue(implementedByA == implementedByA) # pylint:disable=comparison-with-itself self.assertTrue(implementedByA != implementedByB) self.assertTrue(implementedByB != implementedByA) self.assertTrue(proxy == implementedByA) self.assertTrue(implementedByA == proxy) self.assertFalse(proxy != implementedByA) self.assertFalse(implementedByA != proxy) self.assertTrue(proxy != implementedByB) self.assertTrue(implementedByB != proxy) def test_changed_deletes_super_cache(self): impl = self._makeOne() self.assertIsNone(impl._super_cache) self.assertNotIn('_super_cache', impl.__dict__) impl._super_cache = 42 self.assertIn('_super_cache', impl.__dict__) impl.changed(None) self.assertIsNone(impl._super_cache) self.assertNotIn('_super_cache', impl.__dict__) def test_changed_does_not_add_super_cache(self): impl = self._makeOne() self.assertIsNone(impl._super_cache) self.assertNotIn('_super_cache', impl.__dict__) impl.changed(None) self.assertIsNone(impl._super_cache) self.assertNotIn('_super_cache', impl.__dict__) class Test_implementedByFallback(unittest.TestCase): def _getTargetClass(self): # pylint:disable=no-name-in-module from zope.interface.declarations import implementedByFallback return implementedByFallback _getFallbackClass = _getTargetClass def _callFUT(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_dictless_wo_existing_Implements_wo_registrations(self): class Foo: __slots__ = ('__implemented__',) foo = Foo() foo.__implemented__ = None self.assertEqual(list(self._callFUT(foo)), []) def test_dictless_wo_existing_Implements_cant_assign___implemented__(self): class Foo: def _get_impl(self): raise NotImplementedError() def _set_impl(self, val): raise TypeError __implemented__ = property(_get_impl, _set_impl) def __call__(self): # act like a factory raise NotImplementedError() foo = Foo() self.assertRaises(TypeError, self._callFUT, foo) def test_dictless_wo_existing_Implements_w_registrations(self): from zope.interface import declarations class Foo: __slots__ = ('__implemented__',) foo = Foo() foo.__implemented__ = None reg = object() with _MonkeyDict(declarations, 'BuiltinImplementationSpecifications') as specs: specs[foo] = reg self.assertTrue(self._callFUT(foo) is reg) def test_dictless_w_existing_Implements(self): from zope.interface.declarations import Implements impl = Implements() class Foo: __slots__ = ('__implemented__',) foo = Foo() foo.__implemented__ = impl self.assertTrue(self._callFUT(foo) is impl) def test_dictless_w_existing_not_Implements(self): from zope.interface.interface import InterfaceClass class Foo: __slots__ = ('__implemented__',) foo = Foo() IFoo = InterfaceClass('IFoo') foo.__implemented__ = (IFoo,) self.assertEqual(list(self._callFUT(foo)), [IFoo]) def test_w_existing_attr_as_Implements(self): from zope.interface.declarations import Implements impl = Implements() class Foo: __implemented__ = impl self.assertTrue(self._callFUT(Foo) is impl) def test_builtins_added_to_cache(self): from zope.interface import declarations from zope.interface.declarations import Implements with _MonkeyDict(declarations, 'BuiltinImplementationSpecifications') as specs: self.assertEqual(list(self._callFUT(tuple)), []) self.assertEqual(list(self._callFUT(list)), []) self.assertEqual(list(self._callFUT(dict)), []) for typ in (tuple, list, dict): spec = specs[typ] self.assertIsInstance(spec, Implements) self.assertEqual(repr(spec), 'classImplements(%s)' % (typ.__name__,)) def test_builtins_w_existing_cache(self): from zope.interface import declarations t_spec, l_spec, d_spec = object(), object(), object() with _MonkeyDict(declarations, 'BuiltinImplementationSpecifications') as specs: specs[tuple] = t_spec specs[list] = l_spec specs[dict] = d_spec self.assertTrue(self._callFUT(tuple) is t_spec) self.assertTrue(self._callFUT(list) is l_spec) self.assertTrue(self._callFUT(dict) is d_spec) def test_oldstyle_class_no_assertions(self): # TODO: Figure out P3 story class Foo: pass self.assertEqual(list(self._callFUT(Foo)), []) def test_no_assertions(self): # TODO: Figure out P3 story class Foo: pass self.assertEqual(list(self._callFUT(Foo)), []) def test_w_None_no_bases_not_factory(self): class Foo: __implemented__ = None foo = Foo() self.assertRaises(TypeError, self._callFUT, foo) def test_w_None_no_bases_w_factory(self): from zope.interface.declarations import objectSpecificationDescriptor class Foo: __implemented__ = None def __call__(self): raise NotImplementedError() foo = Foo() foo.__name__ = 'foo' spec = self._callFUT(foo) self.assertEqual(spec.__name__, 'zope.interface.tests.test_declarations.foo') self.assertIs(spec.inherit, foo) self.assertIs(foo.__implemented__, spec) self.assertIs(foo.__providedBy__, objectSpecificationDescriptor) # pylint:disable=no-member self.assertNotIn('__provides__', foo.__dict__) def test_w_None_no_bases_w_class(self): from zope.interface.declarations import ClassProvides class Foo: __implemented__ = None spec = self._callFUT(Foo) self.assertEqual(spec.__name__, 'zope.interface.tests.test_declarations.Foo') self.assertIs(spec.inherit, Foo) self.assertIs(Foo.__implemented__, spec) self.assertIsInstance(Foo.__providedBy__, ClassProvides) # pylint:disable=no-member self.assertIsInstance(Foo.__provides__, ClassProvides) # pylint:disable=no-member self.assertEqual(Foo.__provides__, Foo.__providedBy__) # pylint:disable=no-member def test_w_existing_Implements(self): from zope.interface.declarations import Implements impl = Implements() class Foo: __implemented__ = impl self.assertTrue(self._callFUT(Foo) is impl) def test_super_when_base_implements_interface(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass @implementer(IBase) class Base: pass @implementer(IDerived) class Derived(Base): pass self.assertEqual(list(self._callFUT(Derived)), [IDerived, IBase]) sup = super(Derived, Derived) self.assertEqual(list(self._callFUT(sup)), [IBase]) def test_super_when_base_implements_interface_diamond(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass @implementer(IBase) class Base: pass class Child1(Base): pass class Child2(Base): pass @implementer(IDerived) class Derived(Child1, Child2): pass self.assertEqual(list(self._callFUT(Derived)), [IDerived, IBase]) sup = super(Derived, Derived) self.assertEqual(list(self._callFUT(sup)), [IBase]) def test_super_when_parent_implements_interface_diamond(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass class Base: pass class Child1(Base): pass @implementer(IBase) class Child2(Base): pass @implementer(IDerived) class Derived(Child1, Child2): pass self.assertEqual(Derived.__mro__, (Derived, Child1, Child2, Base, object)) self.assertEqual(list(self._callFUT(Derived)), [IDerived, IBase]) sup = super(Derived, Derived) fut = self._callFUT(sup) self.assertEqual(list(fut), [IBase]) self.assertIsNone(fut._dependents) def test_super_when_base_doesnt_implement_interface(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass class Base: pass @implementer(IDerived) class Derived(Base): pass self.assertEqual(list(self._callFUT(Derived)), [IDerived]) sup = super(Derived, Derived) self.assertEqual(list(self._callFUT(sup)), []) def test_super_when_base_is_object(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass @implementer(IDerived) class Derived: pass self.assertEqual(list(self._callFUT(Derived)), [IDerived]) sup = super(Derived, Derived) self.assertEqual(list(self._callFUT(sup)), []) def test_super_multi_level_multi_inheritance(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IM1(Interface): pass class IM2(Interface): pass class IDerived(IBase): pass class IUnrelated(Interface): pass @implementer(IBase) class Base: pass @implementer(IM1) class M1(Base): pass @implementer(IM2) class M2(Base): pass @implementer(IDerived, IUnrelated) class Derived(M1, M2): pass d = Derived sd = super(Derived, Derived) sm1 = super(M1, Derived) sm2 = super(M2, Derived) self.assertEqual(list(self._callFUT(d)), [IDerived, IUnrelated, IM1, IBase, IM2]) self.assertEqual(list(self._callFUT(sd)), [IM1, IBase, IM2]) self.assertEqual(list(self._callFUT(sm1)), [IM2, IBase]) self.assertEqual(list(self._callFUT(sm2)), [IBase]) class Test_implementedBy(Test_implementedByFallback, OptimizationTestMixin): # Repeat tests for C optimizations def _getTargetClass(self): from zope.interface.declarations import implementedBy return implementedBy class _ImplementsTestMixin: FUT_SETS_PROVIDED_BY = True def _callFUT(self, cls, iface): # Declare that *cls* implements *iface*; return *cls* raise NotImplementedError def _check_implementer(self, Foo, orig_spec=None, spec_name=__name__ + '.Foo', inherit="not given"): from zope.interface.declarations import ClassProvides from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') returned = self._callFUT(Foo, IFoo) self.assertIs(returned, Foo) spec = Foo.__implemented__ if orig_spec is not None: self.assertIs(spec, orig_spec) self.assertEqual(spec.__name__, spec_name) inherit = Foo if inherit == "not given" else inherit self.assertIs(spec.inherit, inherit) self.assertIs(Foo.__implemented__, spec) if self.FUT_SETS_PROVIDED_BY: self.assertIsInstance(Foo.__providedBy__, ClassProvides) self.assertIsInstance(Foo.__provides__, ClassProvides) self.assertEqual(Foo.__provides__, Foo.__providedBy__) return Foo, IFoo def test_class(self): class Foo: pass self._check_implementer(Foo) class Test_classImplementsOnly(_ImplementsTestMixin, unittest.TestCase): FUT_SETS_PROVIDED_BY = False def _callFUT(self, cls, iface): from zope.interface.declarations import classImplementsOnly classImplementsOnly(cls, iface) return cls def test_w_existing_Implements(self): from zope.interface.declarations import Implements from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') impl = Implements(IFoo) impl.declared = (IFoo,) class Foo: __implemented__ = impl impl.inherit = Foo self._callFUT(Foo, IBar) # Same spec, now different values self.assertTrue(Foo.__implemented__ is impl) self.assertEqual(impl.inherit, None) self.assertEqual(impl.declared, (IBar,)) def test_class(self): from zope.interface.declarations import Implements from zope.interface.interface import InterfaceClass IBar = InterfaceClass('IBar') old_spec = Implements(IBar) class Foo: __implemented__ = old_spec self._check_implementer(Foo, old_spec, '?', inherit=None) def test_redundant_with_super_still_implements(self): Base, IBase = self._check_implementer( type('Foo', (object,), {}), inherit=None, ) class Child(Base): pass self._callFUT(Child, IBase) self.assertTrue(IBase.implementedBy(Child)) class Test_classImplements(_ImplementsTestMixin, unittest.TestCase): def _callFUT(self, cls, iface): from zope.interface.declarations import classImplements result = classImplements(cls, iface) # pylint:disable=assignment-from-no-return self.assertIsNone(result) return cls def __check_implementer_redundant(self, Base): # If we @implementer exactly what was already present, we write # no declared attributes on the parent (we still set everything, though) Base, IBase = self._check_implementer(Base) class Child(Base): pass returned = self._callFUT(Child, IBase) self.assertIn('__implemented__', returned.__dict__) self.assertNotIn('__providedBy__', returned.__dict__) self.assertIn('__provides__', returned.__dict__) spec = Child.__implemented__ self.assertEqual(spec.declared, ()) self.assertEqual(spec.inherit, Child) self.assertTrue(IBase.providedBy(Child())) def test_redundant_implementer_empty_class_declarations(self): class Foo: pass self.__check_implementer_redundant(Foo) def test_redundant_implementer_Interface(self): from zope.interface import Interface from zope.interface import implementedBy from zope.interface import ro from zope.interface.tests.test_ro import C3Setting class Foo: pass with C3Setting(ro.C3.STRICT_IRO, False): self._callFUT(Foo, Interface) self.assertEqual(list(implementedBy(Foo)), [Interface]) class Baz(Foo): pass self._callFUT(Baz, Interface) self.assertEqual(list(implementedBy(Baz)), [Interface]) def _order_for_two(self, applied_first, applied_second): return (applied_first, applied_second) def test_w_existing_Implements(self): from zope.interface.declarations import Implements from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') impl = Implements(IFoo) impl.declared = (IFoo,) class Foo: __implemented__ = impl impl.inherit = Foo self._callFUT(Foo, IBar) # Same spec, now different values self.assertIs(Foo.__implemented__, impl) self.assertEqual(impl.inherit, Foo) self.assertEqual(impl.declared, self._order_for_two(IFoo, IBar)) def test_w_existing_Implements_w_bases(self): from zope.interface.declarations import Implements from zope.interface.interface import InterfaceClass IRoot = InterfaceClass('IRoot') ISecondRoot = InterfaceClass('ISecondRoot') IExtendsRoot = InterfaceClass('IExtendsRoot', (IRoot,)) impl_root = Implements.named('Root', IRoot) impl_root.declared = (IRoot,) class Root1: __implemented__ = impl_root class Root2: __implemented__ = impl_root impl_extends_root = Implements.named('ExtendsRoot1', IExtendsRoot) impl_extends_root.declared = (IExtendsRoot,) class ExtendsRoot(Root1, Root2): __implemented__ = impl_extends_root impl_extends_root.inherit = ExtendsRoot self._callFUT(ExtendsRoot, ISecondRoot) # Same spec, now different values self.assertIs(ExtendsRoot.__implemented__, impl_extends_root) self.assertEqual(impl_extends_root.inherit, ExtendsRoot) self.assertEqual(impl_extends_root.declared, self._order_for_two(IExtendsRoot, ISecondRoot,)) self.assertEqual(impl_extends_root.__bases__, self._order_for_two(IExtendsRoot, ISecondRoot) + (impl_root,)) class Test_classImplementsFirst(Test_classImplements): def _callFUT(self, cls, iface): from zope.interface.declarations import classImplementsFirst result = classImplementsFirst(cls, iface) # pylint:disable=assignment-from-no-return self.assertIsNone(result) return cls def _order_for_two(self, applied_first, applied_second): return (applied_second, applied_first) class Test__implements_advice(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.interface.declarations import _implements_advice return _implements_advice(*args, **kw) def test_no_existing_implements(self): from zope.interface.declarations import Implements from zope.interface.declarations import classImplements from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') class Foo: __implements_advice_data__ = ((IFoo,), classImplements) self._callFUT(Foo) self.assertNotIn('__implements_advice_data__', Foo.__dict__) self.assertIsInstance(Foo.__implemented__, Implements) # pylint:disable=no-member self.assertEqual(list(Foo.__implemented__), [IFoo]) # pylint:disable=no-member class Test_implementer(Test_classImplements): def _getTargetClass(self): from zope.interface.declarations import implementer return implementer def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def _callFUT(self, cls, *ifaces): decorator = self._makeOne(*ifaces) return decorator(cls) def test_nonclass_cannot_assign_attr(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') decorator = self._makeOne(IFoo) self.assertRaises(TypeError, decorator, object()) def test_nonclass_can_assign_attr(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') class Foo: pass foo = Foo() decorator = self._makeOne(IFoo) returned = decorator(foo) self.assertTrue(returned is foo) spec = foo.__implemented__ # pylint:disable=no-member self.assertEqual(spec.__name__, 'zope.interface.tests.test_declarations.?') self.assertIsNone(spec.inherit,) self.assertIs(foo.__implemented__, spec) # pylint:disable=no-member def test_does_not_leak_on_unique_classes(self): # Make sure nothing is hanging on to the class or Implements # object after they go out of scope. There was briefly a bug # in 5.x that caused SpecificationBase._bases (in C) to not be # traversed or cleared. # https://github.com/zopefoundation/zope.interface/issues/216 import gc from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') begin_count = len(gc.get_objects()) for _ in range(1900): class TestClass: pass self._callFUT(TestClass, IFoo) gc.collect() end_count = len(gc.get_objects()) # How many new objects might still be around? In all currently # tested interpreters, there aren't any, so our counts should # match exactly. When the bug existed, in a steady state, the loop # would grow by two objects each iteration fudge_factor = 0 self.assertLessEqual(end_count, begin_count + fudge_factor) class Test_implementer_only(Test_classImplementsOnly): def _getTargetClass(self): from zope.interface.declarations import implementer_only return implementer_only def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def _callFUT(self, cls, iface): decorator = self._makeOne(iface) return decorator(cls) def test_function(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') decorator = self._makeOne(IFoo) def _function(): raise NotImplementedError() self.assertRaises(ValueError, decorator, _function) def test_method(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass('IFoo') decorator = self._makeOne(IFoo) class Bar: def _method(self): raise NotImplementedError() self.assertRaises(ValueError, decorator, Bar._method) class ProvidesClassTests(unittest.TestCase): def _getTargetClass(self): from zope.interface.declarations import ProvidesClass return ProvidesClass def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_simple_class_one_interface(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") class Foo: pass spec = self._makeOne(Foo, IFoo) self.assertEqual(list(spec), [IFoo]) def test___reduce__(self): from zope.interface.declarations import Provides # the function from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") class Foo: pass spec = self._makeOne(Foo, IFoo) klass, args = spec.__reduce__() self.assertIs(klass, Provides) self.assertEqual(args, (Foo, IFoo)) def test___get___class(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") class Foo: pass spec = self._makeOne(Foo, IFoo) Foo.__provides__ = spec self.assertIs(Foo.__provides__, spec) def test___get___instance(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") class Foo: pass spec = self._makeOne(Foo, IFoo) Foo.__provides__ = spec def _test(): foo = Foo() return foo.__provides__ self.assertRaises(AttributeError, _test) class ProvidesClassStrictTests(ProvidesClassTests): # Tests that require the strict C3 resolution order. def _getTargetClass(self): ProvidesClass = super()._getTargetClass() class StrictProvides(ProvidesClass): def _do_calculate_ro(self, base_mros): return ProvidesClass._do_calculate_ro(self, base_mros=base_mros, strict=True) return StrictProvides def test_overlapping_interfaces_corrected(self): # Giving Provides(cls, IFace), where IFace is already # provided by cls, doesn't produce invalid resolution orders. from zope.interface import Interface from zope.interface import implementedBy from zope.interface import implementer class IBase(Interface): pass @implementer(IBase) class Base: pass spec = self._makeOne(Base, IBase) self.assertEqual(spec.__sro__, ( spec, implementedBy(Base), IBase, implementedBy(object), Interface )) class TestProvidesClassRepr(unittest.TestCase): def _getTargetClass(self): from zope.interface.declarations import ProvidesClass return ProvidesClass def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test__repr__(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") assert IFoo.__name__ == 'IFoo' assert IFoo.__module__ == __name__ assert repr(IFoo) == ''.format(__name__) IBar = InterfaceClass("IBar") inst = self._makeOne(type(self), IFoo, IBar) self.assertEqual( repr(inst), "directlyProvides(TestProvidesClassRepr, IFoo, IBar)" ) def test__repr__module_provides_typical_use(self): # as created through a ``moduleProvides()`` statement # in a module body from zope.interface.tests import dummy provides = dummy.__provides__ # pylint:disable=no-member self.assertEqual( repr(provides), "directlyProvides(sys.modules['zope.interface.tests.dummy'], IDummyModule)" ) def test__repr__module_after_pickle(self): # It doesn't matter, these objects can't be pickled. import pickle from zope.interface.tests import dummy provides = dummy.__provides__ # pylint:disable=no-member for proto in range(pickle.HIGHEST_PROTOCOL + 1): with self.assertRaises(pickle.PicklingError): pickle.dumps(provides, proto) def test__repr__directlyProvides_module(self): import sys from zope.interface.declarations import alsoProvides from zope.interface.declarations import directlyProvides from zope.interface.interface import InterfaceClass from zope.interface.tests import dummy IFoo = InterfaceClass('IFoo') IBar = InterfaceClass('IBar') orig_provides = dummy.__provides__ # pylint:disable=no-member del dummy.__provides__ # pylint:disable=no-member self.addCleanup(setattr, dummy, '__provides__', orig_provides) directlyProvides(dummy, IFoo) provides = dummy.__provides__ # pylint:disable=no-member self.assertEqual( repr(provides), "directlyProvides(sys.modules['zope.interface.tests.dummy'], IFoo)" ) alsoProvides(dummy, IBar) provides = dummy.__provides__ # pylint:disable=no-member self.assertEqual( repr(provides), "directlyProvides(sys.modules['zope.interface.tests.dummy'], IFoo, IBar)" ) # If we make this module also provide IFoo and IBar, then the repr # lists both names. my_module = sys.modules[__name__] assert not hasattr(my_module, '__provides__') directlyProvides(my_module, IFoo, IBar) self.addCleanup(delattr, my_module, '__provides__') self.assertIs(my_module.__provides__, provides) self.assertEqual( repr(provides), "directlyProvides(('zope.interface.tests.dummy', " "'zope.interface.tests.test_declarations'), " "IFoo, IBar)" ) def test__repr__module_provides_cached_shared(self): from zope.interface.declarations import ModuleType from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") inst = self._makeOne(ModuleType, IFoo) inst._v_module_names += ('some.module',) inst._v_module_names += ('another.module',) self.assertEqual( repr(inst), "directlyProvides(('some.module', 'another.module'), IFoo)" ) def test__repr__duplicate_names(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo", __module__='mod1') IFoo2 = InterfaceClass("IFoo", __module__='mod2') IBaz = InterfaceClass("IBaz") inst = self._makeOne(type(self), IFoo, IBaz, IFoo2) self.assertEqual( repr(inst), "directlyProvides(TestProvidesClassRepr, IFoo, IBaz, mod2.IFoo)" ) def test__repr__implementedBy_in_interfaces(self): from zope.interface import Interface from zope.interface import implementedBy class IFoo(Interface): "Does nothing" class Bar: "Does nothing" impl = implementedBy(type(self)) inst = self._makeOne(Bar, IFoo, impl) self.assertEqual( repr(inst), 'directlyProvides(Bar, IFoo, classImplements(TestProvidesClassRepr))' ) def test__repr__empty_interfaces(self): inst = self._makeOne(type(self)) self.assertEqual( repr(inst), 'directlyProvides(TestProvidesClassRepr)', ) def test__repr__non_class(self): class Object: __bases__ = () __str__ = lambda _: self.fail("Should not call str") def __repr__(self): return '' inst = self._makeOne(Object()) self.assertEqual( repr(inst), 'directlyProvides()', ) def test__repr__providedBy_from_class(self): from zope.interface.declarations import implementer from zope.interface.declarations import providedBy from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") @implementer(IFoo) class Foo: pass inst = providedBy(Foo()) self.assertEqual( repr(inst), 'classImplements(Foo, IFoo)' ) def test__repr__providedBy_alsoProvides(self): from zope.interface.declarations import alsoProvides from zope.interface.declarations import implementer from zope.interface.declarations import providedBy from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") IBar = InterfaceClass("IBar") @implementer(IFoo) class Foo: pass foo = Foo() alsoProvides(foo, IBar) inst = providedBy(foo) self.assertEqual( repr(inst), "directlyProvides(Foo, IBar, classImplements(Foo, IFoo))" ) class Test_Provides(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.interface.declarations import Provides return Provides(*args, **kw) def test_no_cached_spec(self): from zope.interface import declarations from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") cache = {} class Foo: pass with _Monkey(declarations, InstanceDeclarations=cache): spec = self._callFUT(Foo, IFoo) self.assertEqual(list(spec), [IFoo]) self.assertTrue(cache[(Foo, IFoo)] is spec) def test_w_cached_spec(self): from zope.interface import declarations from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") prior = object() class Foo: pass cache = {(Foo, IFoo): prior} with _Monkey(declarations, InstanceDeclarations=cache): spec = self._callFUT(Foo, IFoo) self.assertTrue(spec is prior) class Test_directlyProvides(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.interface.declarations import directlyProvides return directlyProvides(*args, **kw) def test_w_normal_object(self): from zope.interface.declarations import ProvidesClass from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") class Foo: pass obj = Foo() self._callFUT(obj, IFoo) self.assertIsInstance(obj.__provides__, ProvidesClass) # pylint:disable=no-member self.assertEqual(list(obj.__provides__), [IFoo]) # pylint:disable=no-member def test_w_class(self): from zope.interface.declarations import ClassProvides from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") class Foo: pass self._callFUT(Foo, IFoo) self.assertIsInstance(Foo.__provides__, ClassProvides) # pylint:disable=no-member self.assertEqual(list(Foo.__provides__), [IFoo]) # pylint:disable=no-member def test_w_classless_object(self): from zope.interface.declarations import ProvidesClass from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") the_dict = {} class Foo: def __getattribute__(self, name): # Emulate object w/o any class if name == '__class__': return None raise NotImplementedError(name) def __setattr__(self, name, value): the_dict[name] = value obj = Foo() self._callFUT(obj, IFoo) self.assertIsInstance(the_dict['__provides__'], ProvidesClass) self.assertEqual(list(the_dict['__provides__']), [IFoo]) class Test_alsoProvides(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.interface.declarations import alsoProvides return alsoProvides(*args, **kw) def test_wo_existing_provides(self): from zope.interface.declarations import ProvidesClass from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") class Foo: pass obj = Foo() self._callFUT(obj, IFoo) self.assertIsInstance(obj.__provides__, ProvidesClass) # pylint:disable=no-member self.assertEqual(list(obj.__provides__), [IFoo]) # pylint:disable=no-member def test_w_existing_provides(self): from zope.interface.declarations import ProvidesClass from zope.interface.declarations import directlyProvides from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") IBar = InterfaceClass("IBar") class Foo: pass obj = Foo() directlyProvides(obj, IFoo) self._callFUT(obj, IBar) self.assertIsInstance(obj.__provides__, ProvidesClass) # pylint:disable=no-member self.assertEqual(list(obj.__provides__), [IFoo, IBar]) # pylint:disable=no-member class Test_noLongerProvides(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.interface.declarations import noLongerProvides return noLongerProvides(*args, **kw) def test_wo_existing_provides(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") class Foo: pass obj = Foo() self._callFUT(obj, IFoo) self.assertEqual(list(obj.__provides__), []) # pylint:disable=no-member def test_w_existing_provides_hit(self): from zope.interface.declarations import directlyProvides from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") class Foo: pass obj = Foo() directlyProvides(obj, IFoo) self._callFUT(obj, IFoo) self.assertEqual(list(obj.__provides__), []) # pylint:disable=no-member def test_w_existing_provides_miss(self): from zope.interface.declarations import directlyProvides from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") IBar = InterfaceClass("IBar") class Foo: pass obj = Foo() directlyProvides(obj, IFoo) self._callFUT(obj, IBar) self.assertEqual(list(obj.__provides__), [IFoo]) # pylint:disable=no-member def test_w_iface_implemented_by_class(self): from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") @implementer(IFoo) class Foo: pass obj = Foo() self.assertRaises(ValueError, self._callFUT, obj, IFoo) class ClassProvidesBaseFallbackTests(unittest.TestCase): def _getTargetClass(self): # pylint:disable=no-name-in-module from zope.interface.declarations import ClassProvidesBaseFallback return ClassProvidesBaseFallback def _makeOne(self, klass, implements): # Don't instantiate directly: the C version can't have attributes # assigned. class Derived(self._getTargetClass()): def __init__(self, k, i): self._cls = k self._implements = i return Derived(klass, implements) def test_w_same_class_via_class(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") class Foo: pass cpbp = Foo.__provides__ = self._makeOne(Foo, IFoo) self.assertTrue(Foo.__provides__ is cpbp) def test_w_same_class_via_instance(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") class Foo: pass foo = Foo() Foo.__provides__ = self._makeOne(Foo, IFoo) self.assertIs(foo.__provides__, IFoo) def test_w_different_class(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") class Foo: pass class Bar(Foo): pass bar = Bar() Foo.__provides__ = self._makeOne(Foo, IFoo) self.assertRaises(AttributeError, getattr, Bar, '__provides__') self.assertRaises(AttributeError, getattr, bar, '__provides__') class ClassProvidesBaseTests(OptimizationTestMixin, ClassProvidesBaseFallbackTests): # Repeat tests for C optimizations def _getTargetClass(self): from zope.interface.declarations import ClassProvidesBase return ClassProvidesBase def _getFallbackClass(self): # pylint:disable=no-name-in-module from zope.interface.declarations import ClassProvidesBaseFallback return ClassProvidesBaseFallback class ClassProvidesTests(unittest.TestCase): def _getTargetClass(self): from zope.interface.declarations import ClassProvides return ClassProvides def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_w_simple_metaclass(self): from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") IBar = InterfaceClass("IBar") @implementer(IFoo) class Foo: pass cp = Foo.__provides__ = self._makeOne(Foo, type(Foo), IBar) self.assertTrue(Foo.__provides__ is cp) self.assertEqual(list(Foo().__provides__), [IFoo]) def test___reduce__(self): from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") IBar = InterfaceClass("IBar") @implementer(IFoo) class Foo: pass cp = Foo.__provides__ = self._makeOne(Foo, type(Foo), IBar) self.assertEqual(cp.__reduce__(), (type(cp), (Foo, type(Foo), IBar))) class ClassProvidesStrictTests(ClassProvidesTests): # Tests that require the strict C3 resolution order. def _getTargetClass(self): ClassProvides = super()._getTargetClass() class StrictClassProvides(ClassProvides): def _do_calculate_ro(self, base_mros): return ClassProvides._do_calculate_ro(self, base_mros=base_mros, strict=True) return StrictClassProvides def test_overlapping_interfaces_corrected(self): # Giving ClassProvides(cls, metaclass, IFace), where IFace is already # provided by metacls, doesn't produce invalid resolution orders. from zope.interface import Interface from zope.interface import implementedBy from zope.interface import implementer class IBase(Interface): pass @implementer(IBase) class metaclass(type): pass cls = metaclass( 'cls', (object,), {} ) spec = self._makeOne(cls, metaclass, IBase) self.assertEqual(spec.__sro__, ( spec, implementedBy(metaclass), IBase, implementedBy(type), implementedBy(object), Interface )) class TestClassProvidesRepr(unittest.TestCase): def _getTargetClass(self): from zope.interface.declarations import ClassProvides return ClassProvides def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test__repr__empty(self): inst = self._makeOne(type(self), type) self.assertEqual( repr(inst), "directlyProvides(TestClassProvidesRepr)" ) def test__repr__providing_one(self): from zope.interface import Interface class IFoo(Interface): "Does nothing" inst = self._makeOne(type(self), type, IFoo) self.assertEqual( repr(inst), "directlyProvides(TestClassProvidesRepr, IFoo)" ) def test__repr__duplicate_names(self): from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo", __module__='mod1') IFoo2 = InterfaceClass("IFoo", __module__='mod2') IBaz = InterfaceClass("IBaz") inst = self._makeOne(type(self), type, IFoo, IBaz, IFoo2) self.assertEqual( repr(inst), "directlyProvides(TestClassProvidesRepr, IFoo, IBaz, mod2.IFoo)" ) def test__repr__implementedBy(self): from zope.interface.declarations import implementedBy from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") @implementer(IFoo) class Foo: pass inst = implementedBy(Foo) self.assertEqual( repr(inst), 'classImplements(Foo, IFoo)' ) def test__repr__implementedBy_generic_callable(self): from zope.interface.declarations import implementedBy # We can't get a __name__ by default, so we get a # module name and a question mark class Callable: def __call__(self): return self inst = implementedBy(Callable()) self.assertEqual( repr(inst), 'classImplements({}.?)'.format(__name__) ) c = Callable() c.__name__ = 'Callable' inst = implementedBy(c) self.assertEqual( repr(inst), 'classImplements(Callable)' ) class Test_directlyProvidedBy(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.interface.declarations import directlyProvidedBy return directlyProvidedBy(*args, **kw) def test_wo_declarations_in_class_or_instance(self): class Foo: pass foo = Foo() self.assertEqual(list(self._callFUT(foo)), []) def test_w_declarations_in_class_but_not_instance(self): from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") @implementer(IFoo) class Foo: pass foo = Foo() self.assertEqual(list(self._callFUT(foo)), []) def test_w_declarations_in_instance_but_not_class(self): from zope.interface.declarations import directlyProvides from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") class Foo: pass foo = Foo() directlyProvides(foo, IFoo) self.assertEqual(list(self._callFUT(foo)), [IFoo]) def test_w_declarations_in_instance_and_class(self): from zope.interface.declarations import directlyProvides from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") IBar = InterfaceClass("IBar") @implementer(IFoo) class Foo: pass foo = Foo() directlyProvides(foo, IBar) self.assertEqual(list(self._callFUT(foo)), [IBar]) class Test_provider(unittest.TestCase): def _getTargetClass(self): from zope.interface.declarations import provider return provider def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_w_class(self): from zope.interface.declarations import ClassProvides from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") @self._makeOne(IFoo) class Foo: pass self.assertIsInstance(Foo.__provides__, ClassProvides) # pylint:disable=no-member self.assertEqual(list(Foo.__provides__), [IFoo]) # pylint:disable=no-member class Test_moduleProvides(unittest.TestCase): # pylint:disable=exec-used def test_called_from_function(self): from zope.interface.declarations import moduleProvides from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") globs = {'__name__': 'zope.interface.tests.foo', 'moduleProvides': moduleProvides, 'IFoo': IFoo} locs = {} CODE = "\n".join([ 'def foo():', ' moduleProvides(IFoo)' ]) exec(CODE, globs, locs) foo = locs['foo'] self.assertRaises(TypeError, foo) def test_called_from_class(self): from zope.interface.declarations import moduleProvides from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") globs = {'__name__': 'zope.interface.tests.foo', 'moduleProvides': moduleProvides, 'IFoo': IFoo} locs = {} CODE = "\n".join([ 'class Foo(object):', ' moduleProvides(IFoo)', ]) with self.assertRaises(TypeError): exec(CODE, globs, locs) def test_called_once_from_module_scope(self): from zope.interface.declarations import moduleProvides from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") globs = {'__name__': 'zope.interface.tests.foo', 'moduleProvides': moduleProvides, 'IFoo': IFoo} CODE = "\n".join([ 'moduleProvides(IFoo)', ]) exec(CODE, globs) spec = globs['__provides__'] self.assertEqual(list(spec), [IFoo]) def test_called_twice_from_module_scope(self): from zope.interface.declarations import moduleProvides from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") globs = {'__name__': 'zope.interface.tests.foo', 'moduleProvides': moduleProvides, 'IFoo': IFoo} CODE = "\n".join([ 'moduleProvides(IFoo)', 'moduleProvides(IFoo)', ]) with self.assertRaises(TypeError): exec(CODE, globs) class Test_getObjectSpecificationFallback(unittest.TestCase): def _getFallbackClass(self): # pylint:disable=no-name-in-module from zope.interface.declarations import getObjectSpecificationFallback return getObjectSpecificationFallback _getTargetClass = _getFallbackClass def _callFUT(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_wo_existing_provides_classless(self): the_dict = {} class Foo: def __getattribute__(self, name): # Emulate object w/o any class if name == '__class__': raise AttributeError(name) try: return the_dict[name] except KeyError: raise AttributeError(name) def __setattr__(self, name, value): raise NotImplementedError() foo = Foo() spec = self._callFUT(foo) self.assertEqual(list(spec), []) def test_existing_provides_is_spec(self): from zope.interface.declarations import directlyProvides from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") def foo(): raise NotImplementedError() directlyProvides(foo, IFoo) spec = self._callFUT(foo) self.assertIs(spec, foo.__provides__) # pylint:disable=no-member def test_existing_provides_is_not_spec(self): def foo(): raise NotImplementedError() foo.__provides__ = object() # not a valid spec spec = self._callFUT(foo) self.assertEqual(list(spec), []) def test_existing_provides(self): from zope.interface.declarations import directlyProvides from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") class Foo: pass foo = Foo() directlyProvides(foo, IFoo) spec = self._callFUT(foo) self.assertEqual(list(spec), [IFoo]) def test_wo_provides_on_class_w_implements(self): from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") @implementer(IFoo) class Foo: pass foo = Foo() spec = self._callFUT(foo) self.assertEqual(list(spec), [IFoo]) def test_wo_provides_on_class_wo_implements(self): class Foo: pass foo = Foo() spec = self._callFUT(foo) self.assertEqual(list(spec), []) def test_catches_only_AttributeError_on_provides(self): MissingSomeAttrs.test_raises(self, self._callFUT, expected_missing='__provides__') def test_catches_only_AttributeError_on_class(self): MissingSomeAttrs.test_raises(self, self._callFUT, expected_missing='__class__', __provides__=None) def test_raises_AttributeError_when_provides_fails_type_check_AttributeError(self): # isinstance(ob.__provides__, SpecificationBase) is not # protected inside any kind of block. class Foo: __provides__ = MissingSomeAttrs(AttributeError) # isinstance() ignores AttributeError on __class__ self._callFUT(Foo()) def test_raises_AttributeError_when_provides_fails_type_check_RuntimeError(self): # isinstance(ob.__provides__, SpecificationBase) is not # protected inside any kind of block. class Foo: __provides__ = MissingSomeAttrs(RuntimeError) with self.assertRaises(RuntimeError) as exc: self._callFUT(Foo()) self.assertEqual('__class__', exc.exception.args[0]) class Test_getObjectSpecification(Test_getObjectSpecificationFallback, OptimizationTestMixin): # Repeat tests for C optimizations def _getTargetClass(self): from zope.interface.declarations import getObjectSpecification return getObjectSpecification class Test_providedByFallback(unittest.TestCase): def _getFallbackClass(self): # pylint:disable=no-name-in-module from zope.interface.declarations import providedByFallback return providedByFallback _getTargetClass = _getFallbackClass def _callFUT(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_wo_providedBy_on_class_wo_implements(self): class Foo: pass foo = Foo() spec = self._callFUT(foo) self.assertEqual(list(spec), []) def test_w_providedBy_valid_spec(self): from zope.interface.declarations import Provides from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") class Foo: pass foo = Foo() foo.__providedBy__ = Provides(Foo, IFoo) spec = self._callFUT(foo) self.assertEqual(list(spec), [IFoo]) def test_w_providedBy_invalid_spec(self): class Foo: pass foo = Foo() foo.__providedBy__ = object() spec = self._callFUT(foo) self.assertEqual(list(spec), []) def test_w_providedBy_invalid_spec_class_w_implements(self): from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") @implementer(IFoo) class Foo: pass foo = Foo() foo.__providedBy__ = object() spec = self._callFUT(foo) self.assertEqual(list(spec), [IFoo]) def test_w_providedBy_invalid_spec_w_provides_no_provides_on_class(self): class Foo: pass foo = Foo() foo.__providedBy__ = object() expected = foo.__provides__ = object() spec = self._callFUT(foo) self.assertTrue(spec is expected) def test_w_providedBy_invalid_spec_w_provides_diff_provides_on_class(self): class Foo: pass foo = Foo() foo.__providedBy__ = object() expected = foo.__provides__ = object() Foo.__provides__ = object() spec = self._callFUT(foo) self.assertTrue(spec is expected) def test_w_providedBy_invalid_spec_w_provides_same_provides_on_class(self): from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") @implementer(IFoo) class Foo: pass foo = Foo() foo.__providedBy__ = object() foo.__provides__ = Foo.__provides__ = object() spec = self._callFUT(foo) self.assertEqual(list(spec), [IFoo]) def test_super_when_base_implements_interface(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass @implementer(IBase) class Base: pass @implementer(IDerived) class Derived(Base): pass derived = Derived() self.assertEqual(list(self._callFUT(derived)), [IDerived, IBase]) sup = super(Derived, derived) fut = self._callFUT(sup) self.assertIsNone(fut._dependents) self.assertEqual(list(fut), [IBase]) def test_super_when_base_doesnt_implement_interface(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass class Base: pass @implementer(IDerived) class Derived(Base): pass derived = Derived() self.assertEqual(list(self._callFUT(derived)), [IDerived]) sup = super(Derived, derived) self.assertEqual(list(self._callFUT(sup)), []) def test_super_when_base_is_object(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass @implementer(IDerived) class Derived: pass derived = Derived() self.assertEqual(list(self._callFUT(derived)), [IDerived]) sup = super(Derived, derived) fut = self._callFUT(sup) self.assertIsNone(fut._dependents) self.assertEqual(list(fut), []) def test_super_when_object_directly_provides(self): from zope.interface import Interface from zope.interface.declarations import directlyProvides from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass @implementer(IBase) class Base: pass class Derived(Base): pass derived = Derived() self.assertEqual(list(self._callFUT(derived)), [IBase]) directlyProvides(derived, IDerived) self.assertEqual(list(self._callFUT(derived)), [IDerived, IBase]) sup = super(Derived, derived) fut = self._callFUT(sup) self.assertIsNone(fut._dependents) self.assertEqual(list(fut), [IBase]) def test_super_multi_level_multi_inheritance(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IM1(Interface): pass class IM2(Interface): pass class IDerived(IBase): pass class IUnrelated(Interface): pass @implementer(IBase) class Base: pass @implementer(IM1) class M1(Base): pass @implementer(IM2) class M2(Base): pass @implementer(IDerived, IUnrelated) class Derived(M1, M2): pass d = Derived() sd = super(Derived, d) sm1 = super(M1, d) sm2 = super(M2, d) self.assertEqual(list(self._callFUT(d)), [IDerived, IUnrelated, IM1, IBase, IM2]) self.assertEqual(list(self._callFUT(sd)), [IM1, IBase, IM2]) self.assertEqual(list(self._callFUT(sm1)), [IM2, IBase]) self.assertEqual(list(self._callFUT(sm2)), [IBase]) def test_catches_only_AttributeError_on_providedBy(self): MissingSomeAttrs.test_raises(self, self._callFUT, expected_missing='__providedBy__', __class__=object) def test_catches_only_AttributeError_on_class(self): # isinstance() tries to get the __class__, which is non-obvious, # so it must be protected too. MissingSomeAttrs.test_raises( self, self._callFUT, expected_missing='__class__') class Test_providedBy(Test_providedByFallback, OptimizationTestMixin): # Repeat tests for C optimizations def _getTargetClass(self): from zope.interface.declarations import providedBy return providedBy class ObjectSpecificationDescriptorFallbackTests(unittest.TestCase): def _getFallbackClass(self): # pylint:disable=no-name-in-module from zope.interface.declarations import \ ObjectSpecificationDescriptorFallback return ObjectSpecificationDescriptorFallback _getTargetClass = _getFallbackClass def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_accessed_via_class(self): from zope.interface.declarations import Provides from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") class Foo: pass Foo.__provides__ = Provides(Foo, IFoo) Foo.__providedBy__ = self._makeOne() self.assertEqual(list(Foo.__providedBy__), [IFoo]) def test_accessed_via_inst_wo_provides(self): from zope.interface.declarations import Provides from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") IBar = InterfaceClass("IBar") @implementer(IFoo) class Foo: pass Foo.__provides__ = Provides(Foo, IBar) Foo.__providedBy__ = self._makeOne() foo = Foo() self.assertEqual(list(foo.__providedBy__), [IFoo]) def test_accessed_via_inst_w_provides(self): from zope.interface.declarations import Provides from zope.interface.declarations import directlyProvides from zope.interface.declarations import implementer from zope.interface.interface import InterfaceClass IFoo = InterfaceClass("IFoo") IBar = InterfaceClass("IBar") IBaz = InterfaceClass("IBaz") @implementer(IFoo) class Foo: pass Foo.__provides__ = Provides(Foo, IBar) Foo.__providedBy__ = self._makeOne() foo = Foo() directlyProvides(foo, IBaz) self.assertEqual(list(foo.__providedBy__), [IBaz, IFoo]) def test_arbitrary_exception_accessing_provides_not_caught(self): class MyException(Exception): pass class Foo: __providedBy__ = self._makeOne() @property def __provides__(self): raise MyException foo = Foo() with self.assertRaises(MyException): getattr(foo, '__providedBy__') def test_AttributeError_accessing_provides_caught(self): class MyException(Exception): pass class Foo: __providedBy__ = self._makeOne() @property def __provides__(self): raise AttributeError foo = Foo() provided = getattr(foo, '__providedBy__') self.assertIsNotNone(provided) def test_None_in__provides__overrides(self): from zope.interface import Interface from zope.interface import implementer class IFoo(Interface): pass @implementer(IFoo) class Foo: @property def __provides__(self): return None Foo.__providedBy__ = self._makeOne() provided = getattr(Foo(), '__providedBy__') self.assertIsNone(provided) class ObjectSpecificationDescriptorTests( ObjectSpecificationDescriptorFallbackTests, OptimizationTestMixin): # Repeat tests for C optimizations def _getTargetClass(self): from zope.interface.declarations import ObjectSpecificationDescriptor return ObjectSpecificationDescriptor # Test _normalizeargs through its callers. class _Monkey: # context-manager for replacing module names in the scope of a test. def __init__(self, module, **kw): self.module = module self.to_restore = {key: getattr(module, key) for key in kw} for key, value in kw.items(): setattr(module, key, value) def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): for key, value in self.to_restore.items(): setattr(self.module, key, value) class _MonkeyDict: # context-manager for restoring a dict w/in a module in the scope of a test. def __init__(self, module, attrname, **kw): self.module = module self.target = getattr(module, attrname) self.to_restore = self.target.copy() self.target.clear() self.target.update(kw) def __enter__(self): return self.target def __exit__(self, exc_type, exc_val, exc_tb): self.target.clear() self.target.update(self.to_restore) zope.interface-6.4/src/zope/interface/tests/test_document.py000066400000000000000000000414141462121350100243730ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2001, 2002 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. # ############################################################################## """Documentation tests. """ import sys import unittest class Test_asStructuredText(unittest.TestCase): def _callFUT(self, iface): from zope.interface.document import asStructuredText return asStructuredText(iface) def test_asStructuredText_no_docstring(self): from zope.interface import Interface EXPECTED = '\n\n'.join([ "INoDocstring", " Attributes:", " Methods:", "" ]) class INoDocstring(Interface): pass self.assertEqual(self._callFUT(INoDocstring), EXPECTED) def test_asStructuredText_empty_with_docstring(self): from zope.interface import Interface EXPECTED = '\n\n'.join([ "IEmpty", " This is an empty interface.", " Attributes:", " Methods:", "" ]) class IEmpty(Interface): """ This is an empty interface. """ self.assertEqual(self._callFUT(IEmpty), EXPECTED) def test_asStructuredText_empty_with_multiline_docstring(self): from zope.interface import Interface # In Python 3.13+, compiler strips indents from docstrings indent = " " * 12 if sys.version_info < (3, 13) else "" EXPECTED = '\n'.join([ "IEmpty", "", " This is an empty interface.", " ", (f"{indent} It can be used to annotate any class or object, " "because it promises"), f"{indent} nothing.", "", " Attributes:", "", " Methods:", "", "" ]) class IEmpty(Interface): """ This is an empty interface. It can be used to annotate any class or object, because it promises nothing. """ self.assertEqual(self._callFUT(IEmpty), EXPECTED) def test_asStructuredText_with_attribute_no_docstring(self): from zope.interface import Attribute from zope.interface import Interface EXPECTED = '\n\n'.join([ "IHasAttribute", " This interface has an attribute.", " Attributes:", " an_attribute -- no documentation", " Methods:", "" ]) class IHasAttribute(Interface): """ This interface has an attribute. """ an_attribute = Attribute('an_attribute') self.assertEqual(self._callFUT(IHasAttribute), EXPECTED) def test_asStructuredText_with_attribute_with_docstring(self): from zope.interface import Attribute from zope.interface import Interface EXPECTED = '\n\n'.join([ "IHasAttribute", " This interface has an attribute.", " Attributes:", " an_attribute -- This attribute is documented.", " Methods:", "" ]) class IHasAttribute(Interface): """ This interface has an attribute. """ an_attribute = Attribute('an_attribute', 'This attribute is documented.') self.assertEqual(self._callFUT(IHasAttribute), EXPECTED) def test_asStructuredText_with_method_no_args_no_docstring(self): from zope.interface import Interface EXPECTED = '\n\n'.join([ "IHasMethod", " This interface has a method.", " Attributes:", " Methods:", " aMethod() -- no documentation", "" ]) class IHasMethod(Interface): """ This interface has a method. """ def aMethod(): pass # pragma: no cover self.assertEqual(self._callFUT(IHasMethod), EXPECTED) def test_asStructuredText_with_method_positional_args_no_docstring(self): from zope.interface import Interface EXPECTED = '\n\n'.join([ "IHasMethod", " This interface has a method.", " Attributes:", " Methods:", " aMethod(first, second) -- no documentation", "" ]) class IHasMethod(Interface): """ This interface has a method. """ def aMethod(first, second): pass # pragma: no cover self.assertEqual(self._callFUT(IHasMethod), EXPECTED) def test_asStructuredText_with_method_starargs_no_docstring(self): from zope.interface import Interface EXPECTED = '\n\n'.join([ "IHasMethod", " This interface has a method.", " Attributes:", " Methods:", " aMethod(first, second, *rest) -- no documentation", "" ]) class IHasMethod(Interface): """ This interface has a method. """ def aMethod(first, second, *rest): pass # pragma: no cover self.assertEqual(self._callFUT(IHasMethod), EXPECTED) def test_asStructuredText_with_method_kwargs_no_docstring(self): from zope.interface import Interface EXPECTED = '\n\n'.join([ "IHasMethod", " This interface has a method.", " Attributes:", " Methods:", " aMethod(first, second, **kw) -- no documentation", "" ]) class IHasMethod(Interface): """ This interface has a method. """ def aMethod(first, second, **kw): pass # pragma: no cover self.assertEqual(self._callFUT(IHasMethod), EXPECTED) def test_asStructuredText_with_method_with_docstring(self): from zope.interface import Interface EXPECTED = '\n\n'.join([ "IHasMethod", " This interface has a method.", " Attributes:", " Methods:", " aMethod() -- This method is documented.", "" ]) class IHasMethod(Interface): """ This interface has a method. """ def aMethod(): """This method is documented. """ self.assertEqual(self._callFUT(IHasMethod), EXPECTED) def test_asStructuredText_derived_ignores_base(self): from zope.interface import Attribute from zope.interface import Interface EXPECTED = '\n\n'.join([ "IDerived", " IDerived doc", " This interface extends:", " o IBase", " Attributes:", " attr1 -- no documentation", " attr2 -- attr2 doc", " Methods:", " method3() -- method3 doc", " method4() -- no documentation", " method5() -- method5 doc", "", ]) class IBase(Interface): def method1(): """docstring""" def method2(): """docstring""" class IDerived(IBase): "IDerived doc" attr1 = Attribute('attr1') attr2 = Attribute('attr2', 'attr2 doc') def method3(): "method3 doc" def method4(): pass # pragma: no cover def method5(): "method5 doc" self.assertEqual(self._callFUT(IDerived), EXPECTED) class Test_asReStructuredText(unittest.TestCase): def _callFUT(self, iface): from zope.interface.document import asReStructuredText return asReStructuredText(iface) def test_asReStructuredText_no_docstring(self): from zope.interface import Interface EXPECTED = '\n\n'.join([ "``INoDocstring``", " Attributes:", " Methods:", "" ]) class INoDocstring(Interface): pass self.assertEqual(self._callFUT(INoDocstring), EXPECTED) def test_asReStructuredText_empty_with_docstring(self): from zope.interface import Interface EXPECTED = '\n\n'.join([ "``IEmpty``", " This is an empty interface.", " Attributes:", " Methods:", "" ]) class IEmpty(Interface): """ This is an empty interface. """ self.assertEqual(self._callFUT(IEmpty), EXPECTED) def test_asReStructuredText_empty_with_multiline_docstring(self): from zope.interface import Interface # In Python 3.13+, compiler strips indents from docstrings indent = " " * 12 if sys.version_info < (3, 13) else "" EXPECTED = '\n'.join([ "``IEmpty``", "", " This is an empty interface.", " ", (f"{indent} It can be used to annotate any class or object, " "because it promises"), f"{indent} nothing.", "", " Attributes:", "", " Methods:", "", "" ]) class IEmpty(Interface): """ This is an empty interface. It can be used to annotate any class or object, because it promises nothing. """ self.assertEqual(self._callFUT(IEmpty), EXPECTED) def test_asReStructuredText_with_attribute_no_docstring(self): from zope.interface import Attribute from zope.interface import Interface EXPECTED = '\n\n'.join([ "``IHasAttribute``", " This interface has an attribute.", " Attributes:", " ``an_attribute`` -- no documentation", " Methods:", "" ]) class IHasAttribute(Interface): """ This interface has an attribute. """ an_attribute = Attribute('an_attribute') self.assertEqual(self._callFUT(IHasAttribute), EXPECTED) def test_asReStructuredText_with_attribute_with_docstring(self): from zope.interface import Attribute from zope.interface import Interface EXPECTED = '\n\n'.join([ "``IHasAttribute``", " This interface has an attribute.", " Attributes:", " ``an_attribute`` -- This attribute is documented.", " Methods:", "" ]) class IHasAttribute(Interface): """ This interface has an attribute. """ an_attribute = Attribute('an_attribute', 'This attribute is documented.') self.assertEqual(self._callFUT(IHasAttribute), EXPECTED) def test_asReStructuredText_with_method_no_args_no_docstring(self): from zope.interface import Interface EXPECTED = '\n\n'.join([ "``IHasMethod``", " This interface has a method.", " Attributes:", " Methods:", " ``aMethod()`` -- no documentation", "" ]) class IHasMethod(Interface): """ This interface has a method. """ def aMethod(): pass # pragma: no cover self.assertEqual(self._callFUT(IHasMethod), EXPECTED) def test_asReStructuredText_with_method_positional_args_no_docstring(self): from zope.interface import Interface EXPECTED = '\n\n'.join([ "``IHasMethod``", " This interface has a method.", " Attributes:", " Methods:", " ``aMethod(first, second)`` -- no documentation", "" ]) class IHasMethod(Interface): """ This interface has a method. """ def aMethod(first, second): pass # pragma: no cover self.assertEqual(self._callFUT(IHasMethod), EXPECTED) def test_asReStructuredText_with_method_starargs_no_docstring(self): from zope.interface import Interface EXPECTED = '\n\n'.join([ "``IHasMethod``", " This interface has a method.", " Attributes:", " Methods:", " ``aMethod(first, second, *rest)`` -- no documentation", "" ]) class IHasMethod(Interface): """ This interface has a method. """ def aMethod(first, second, *rest): pass # pragma: no cover self.assertEqual(self._callFUT(IHasMethod), EXPECTED) def test_asReStructuredText_with_method_kwargs_no_docstring(self): from zope.interface import Interface EXPECTED = '\n\n'.join([ "``IHasMethod``", " This interface has a method.", " Attributes:", " Methods:", " ``aMethod(first, second, **kw)`` -- no documentation", "" ]) class IHasMethod(Interface): """ This interface has a method. """ def aMethod(first, second, **kw): pass # pragma: no cover self.assertEqual(self._callFUT(IHasMethod), EXPECTED) def test_asReStructuredText_with_method_with_docstring(self): from zope.interface import Interface EXPECTED = '\n\n'.join([ "``IHasMethod``", " This interface has a method.", " Attributes:", " Methods:", " ``aMethod()`` -- This method is documented.", "" ]) class IHasMethod(Interface): """ This interface has a method. """ def aMethod(): """This method is documented. """ self.assertEqual(self._callFUT(IHasMethod), EXPECTED) def test_asReStructuredText_derived_ignores_base(self): from zope.interface import Attribute from zope.interface import Interface EXPECTED = '\n\n'.join([ "``IDerived``", " IDerived doc", " This interface extends:", " o ``IBase``", " Attributes:", " ``attr1`` -- no documentation", " ``attr2`` -- attr2 doc", " Methods:", " ``method3()`` -- method3 doc", " ``method4()`` -- no documentation", " ``method5()`` -- method5 doc", "", ]) class IBase(Interface): def method1(): pass # pragma: no cover def method2(): pass # pragma: no cover class IDerived(IBase): "IDerived doc" attr1 = Attribute('attr1') attr2 = Attribute('attr2', 'attr2 doc') def method3(): "method3 doc" def method4(): pass # pragma: no cover def method5(): "method5 doc" self.assertEqual(self._callFUT(IDerived), EXPECTED) class Test__justify_and_indent(unittest.TestCase): def _callFUT(self, text, level, **kw): from zope.interface.document import _justify_and_indent return _justify_and_indent(text, level, **kw) def test_simple_level_0(self): LINES = ['Three blind mice', 'See how they run'] text = '\n'.join(LINES) self.assertEqual(self._callFUT(text, 0), text) def test_simple_level_1(self): LINES = ['Three blind mice', 'See how they run'] text = '\n'.join(LINES) self.assertEqual(self._callFUT(text, 1), '\n'.join([' ' + line for line in LINES])) def test_simple_level_2(self): LINES = ['Three blind mice', 'See how they run'] text = '\n'.join(LINES) self.assertEqual(self._callFUT(text, 1), '\n'.join([' ' + line for line in LINES])) def test_simple_w_CRLF(self): LINES = ['Three blind mice', 'See how they run'] text = '\r\n'.join(LINES) self.assertEqual(self._callFUT(text, 1), '\n'.join([' ' + line for line in LINES])) def test_with_munge(self): TEXT = ("This is a piece of text longer than 15 characters, \n" "and split across multiple lines.") EXPECTED = (" This is a piece\n" " of text longer\n" " than 15 characters,\n" " and split across\n" " multiple lines.\n" " ") self.assertEqual(self._callFUT(TEXT, 1, munge=1, width=15), EXPECTED) zope.interface-6.4/src/zope/interface/tests/test_element.py000066400000000000000000000021401462121350100241770ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2003 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. # ############################################################################## """Test Element meta-class. """ import unittest from zope.interface.interface import Element class TestElement(unittest.TestCase): def test_taggedValues(self): """Test that we can update tagged values of more than one element """ e1 = Element("foo") e2 = Element("bar") e1.setTaggedValue("x", 1) e2.setTaggedValue("x", 2) self.assertEqual(e1.getTaggedValue("x"), 1) self.assertEqual(e2.getTaggedValue("x"), 2) zope.interface-6.4/src/zope/interface/tests/test_exceptions.py000066400000000000000000000144141462121350100247360ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2010 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. # ############################################################################## """ zope.interface.exceptions unit tests """ import unittest def _makeIface(): from zope.interface import Interface class IDummy(Interface): pass return IDummy class DoesNotImplementTests(unittest.TestCase): def _getTargetClass(self): from zope.interface.exceptions import DoesNotImplement return DoesNotImplement def _makeOne(self, *args): iface = _makeIface() return self._getTargetClass()(iface, *args) def test___str__(self): dni = self._makeOne() self.assertEqual( str(dni), "An object has failed to implement interface " "zope.interface.tests.test_exceptions.IDummy: " "Does not declaratively implement the interface." ) def test___str__w_candidate(self): dni = self._makeOne('candidate') self.assertEqual( str(dni), "The object 'candidate' has failed to implement interface " "zope.interface.tests.test_exceptions.IDummy: " "Does not declaratively implement the interface." ) class BrokenImplementationTests(unittest.TestCase): def _getTargetClass(self): from zope.interface.exceptions import BrokenImplementation return BrokenImplementation def _makeOne(self, *args): iface = _makeIface() return self._getTargetClass()(iface, 'missing', *args) def test___str__(self): dni = self._makeOne() self.assertEqual( str(dni), 'An object has failed to implement interface ' 'zope.interface.tests.test_exceptions.IDummy: ' "The 'missing' attribute was not provided.") def test___str__w_candidate(self): dni = self._makeOne('candidate') self.assertEqual( str(dni), 'The object \'candidate\' has failed to implement interface ' 'zope.interface.tests.test_exceptions.IDummy: ' "The 'missing' attribute was not provided.") def broken_function(): """ This is a global function with a simple argument list. It exists to be able to report the same information when formatting signatures. """ class BrokenMethodImplementationTests(unittest.TestCase): def _getTargetClass(self): from zope.interface.exceptions import BrokenMethodImplementation return BrokenMethodImplementation message = 'I said so' def _makeOne(self, *args): return self._getTargetClass()('aMethod', self.message, *args) def test___str__(self): dni = self._makeOne() self.assertEqual( str(dni), "An object has failed to implement interface : " "The contract of 'aMethod' is violated because I said so." ) def test___str__w_candidate_no_implementation(self): dni = self._makeOne('some_function', '', 'candidate') self.assertEqual( str(dni), "The object 'candidate' has failed to implement interface : " "The contract of 'aMethod' is violated because I said so." ) def test___str__w_candidate_w_implementation(self): self.message = 'implementation is wonky' dni = self._makeOne(broken_function, '', 'candidate') self.assertEqual( str(dni), "The object 'candidate' has failed to implement interface : " "The contract of 'aMethod' is violated because " "'broken_function()' is wonky." ) def test___str__w_candidate_w_implementation_not_callable(self): self.message = 'implementation is not callable' dni = self._makeOne(42, '', 'candidate') self.assertEqual( str(dni), "The object 'candidate' has failed to implement interface : " "The contract of 'aMethod' is violated because " "'42' is not callable." ) def test___repr__w_candidate(self): dni = self._makeOne(None, 'candidate') self.assertEqual( repr(dni), "BrokenMethodImplementation('aMethod', 'I said so', None, 'candidate')" ) class MultipleInvalidTests(unittest.TestCase): def _getTargetClass(self): from zope.interface.exceptions import MultipleInvalid return MultipleInvalid def _makeOne(self, excs): iface = _makeIface() return self._getTargetClass()(iface, 'target', excs) def test__str__(self): from zope.interface.exceptions import BrokenMethodImplementation excs = [ BrokenMethodImplementation('aMethod', 'I said so'), Exception("Regular exception") ] dni = self._makeOne(excs) self.assertEqual( str(dni), "The object 'target' has failed to implement interface " "zope.interface.tests.test_exceptions.IDummy:\n" " The contract of 'aMethod' is violated because I said so\n" " Regular exception" ) def test__repr__(self): from zope.interface.exceptions import BrokenMethodImplementation excs = [ BrokenMethodImplementation('aMethod', 'I said so'), # Use multiple arguments to normalize repr; versions of Python # prior to 3.7 add a trailing comma if there's just one. Exception("Regular", "exception") ] dni = self._makeOne(excs) self.assertEqual( repr(dni), "MultipleInvalid(," " 'target'," " (BrokenMethodImplementation('aMethod', 'I said so')," " Exception('Regular', 'exception')))" ) zope.interface-6.4/src/zope/interface/tests/test_interface.py000066400000000000000000002642301462121350100245200ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2001, 2002 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. # ############################################################################## """Test Interface implementation """ # Things we let slide because it's a test # pylint:disable=protected-access,blacklisted-name,attribute-defined-outside-init # pylint:disable=too-many-public-methods,too-many-lines,abstract-method # pylint:disable=redefined-builtin,signature-differs,arguments-differ # Things you get inheriting from Interface # pylint:disable=inherit-non-class,no-self-argument,no-method-argument # Things you get using methods of an Interface 'subclass' # pylint:disable=no-value-for-parameter import unittest from zope.interface.tests import CleanUp from zope.interface.tests import MissingSomeAttrs from zope.interface.tests import OptimizationTestMixin _marker = object() class Test_invariant(unittest.TestCase): def test_w_single(self): from zope.interface.interface import TAGGED_DATA from zope.interface.interface import invariant def _check(*args, **kw): raise NotImplementedError() class Foo: invariant(_check) self.assertEqual(getattr(Foo, TAGGED_DATA, None), {'invariants': [_check]}) def test_w_multiple(self): from zope.interface.interface import TAGGED_DATA from zope.interface.interface import invariant def _check(*args, **kw): raise NotImplementedError() def _another_check(*args, **kw): raise NotImplementedError() class Foo: invariant(_check) invariant(_another_check) self.assertEqual(getattr(Foo, TAGGED_DATA, None), {'invariants': [_check, _another_check]}) class Test_taggedValue(unittest.TestCase): def test_w_single(self): from zope.interface.interface import TAGGED_DATA from zope.interface.interface import taggedValue class Foo: taggedValue('bar', ['baz']) self.assertEqual(getattr(Foo, TAGGED_DATA, None), {'bar': ['baz']}) def test_w_multiple(self): from zope.interface.interface import TAGGED_DATA from zope.interface.interface import taggedValue class Foo: taggedValue('bar', ['baz']) taggedValue('qux', 'spam') self.assertEqual(getattr(Foo, TAGGED_DATA, None), {'bar': ['baz'], 'qux': 'spam'}) def test_w_multiple_overwriting(self): from zope.interface.interface import TAGGED_DATA from zope.interface.interface import taggedValue class Foo: taggedValue('bar', ['baz']) taggedValue('qux', 'spam') taggedValue('bar', 'frob') self.assertEqual(getattr(Foo, TAGGED_DATA, None), {'bar': 'frob', 'qux': 'spam'}) class ElementTests(unittest.TestCase): DEFAULT_NAME = 'AnElement' def _getTargetClass(self): from zope.interface.interface import Element return Element def _makeOne(self, name=None): if name is None: name = self.DEFAULT_NAME return self._getTargetClass()(name) def test_ctor_defaults(self): element = self._makeOne() self.assertEqual(element.__name__, self.DEFAULT_NAME) self.assertEqual(element.getName(), self.DEFAULT_NAME) self.assertEqual(element.__doc__, '') self.assertEqual(element.getDoc(), '') self.assertEqual(list(element.getTaggedValueTags()), []) def test_ctor_no_doc_space_in_name(self): element = self._makeOne('An Element') self.assertEqual(element.__name__, None) self.assertEqual(element.__doc__, 'An Element') def test_getTaggedValue_miss(self): element = self._makeOne() self.assertRaises(KeyError, element.getTaggedValue, 'nonesuch') def test_getDirectTaggedValueTags(self): element = self._makeOne() self.assertEqual([], list(element.getDirectTaggedValueTags())) element.setTaggedValue('foo', 'bar') self.assertEqual(['foo'], list(element.getDirectTaggedValueTags())) def test_queryTaggedValue_miss(self): element = self._makeOne() self.assertEqual(element.queryTaggedValue('nonesuch'), None) def test_queryTaggedValue_miss_w_default(self): element = self._makeOne() self.assertEqual(element.queryTaggedValue('nonesuch', 'bar'), 'bar') def test_getDirectTaggedValue_miss(self): element = self._makeOne() self.assertRaises(KeyError, element.getDirectTaggedValue, 'nonesuch') def test_queryDirectTaggedValue_miss(self): element = self._makeOne() self.assertEqual(element.queryDirectTaggedValue('nonesuch'), None) def test_queryDirectTaggedValue_miss_w_default(self): element = self._makeOne() self.assertEqual(element.queryDirectTaggedValue('nonesuch', 'bar'), 'bar') def test_setTaggedValue(self): element = self._makeOne() element.setTaggedValue('foo', 'bar') self.assertEqual(list(element.getTaggedValueTags()), ['foo']) self.assertEqual(element.getTaggedValue('foo'), 'bar') self.assertEqual(element.queryTaggedValue('foo'), 'bar') def test_verifies(self): from zope.interface.interfaces import IElement from zope.interface.verify import verifyObject element = self._makeOne() verifyObject(IElement, element) class GenericSpecificationBaseTests(unittest.TestCase): # Tests that work with both implementations def _getFallbackClass(self): from zope.interface.interface import SpecificationBasePy return SpecificationBasePy _getTargetClass = _getFallbackClass def _makeOne(self): return self._getTargetClass()() def test_providedBy_miss(self): from zope.interface import interface from zope.interface.declarations import _empty sb = self._makeOne() def _providedBy(obj): return _empty with _Monkey(interface, providedBy=_providedBy): self.assertFalse(sb.providedBy(object())) def test_implementedBy_miss(self): from zope.interface import interface from zope.interface.declarations import _empty sb = self._makeOne() def _implementedBy(obj): return _empty with _Monkey(interface, implementedBy=_implementedBy): self.assertFalse(sb.implementedBy(object())) class SpecificationBaseTests(GenericSpecificationBaseTests, OptimizationTestMixin): # Tests that use the C implementation def _getTargetClass(self): from zope.interface.interface import SpecificationBase return SpecificationBase class SpecificationBasePyTests(GenericSpecificationBaseTests): # Tests that only work with the Python implementation def test___call___miss(self): sb = self._makeOne() sb._implied = {} # not defined by SpecificationBasePy self.assertFalse(sb.isOrExtends(object())) def test___call___hit(self): sb = self._makeOne() testing = object() sb._implied = {testing: {}} # not defined by SpecificationBasePy self.assertTrue(sb(testing)) def test_isOrExtends_miss(self): sb = self._makeOne() sb._implied = {} # not defined by SpecificationBasePy self.assertFalse(sb.isOrExtends(object())) def test_isOrExtends_hit(self): sb = self._makeOne() testing = object() sb._implied = {testing: {}} # not defined by SpecificationBasePy self.assertTrue(sb(testing)) def test_implementedBy_hit(self): from zope.interface import interface sb = self._makeOne() class _Decl: _implied = {sb: {},} def _implementedBy(obj): return _Decl() with _Monkey(interface, implementedBy=_implementedBy): self.assertTrue(sb.implementedBy(object())) def test_providedBy_hit(self): from zope.interface import interface sb = self._makeOne() class _Decl: _implied = {sb: {},} def _providedBy(obj): return _Decl() with _Monkey(interface, providedBy=_providedBy): self.assertTrue(sb.providedBy(object())) class NameAndModuleComparisonTestsMixin(CleanUp): def _makeOneToCompare(self): return self._makeOne('a', 'b') def __check_NotImplemented_comparison(self, name): # Without the correct attributes of __name__ and __module__, # comparison switches to the reverse direction. import operator ib = self._makeOneToCompare() op = getattr(operator, name) meth = getattr(ib, '__%s__' % name) # If either the __name__ or __module__ attribute # is missing from the other object, then we return # NotImplemented. class RaisesErrorOnMissing: Exc = AttributeError def __getattribute__(self, name): try: return object.__getattribute__(self, name) except AttributeError: exc = RaisesErrorOnMissing.Exc raise exc(name) class RaisesErrorOnModule(RaisesErrorOnMissing): def __init__(self): self.__name__ = 'foo' @property def __module__(self): raise AttributeError class RaisesErrorOnName(RaisesErrorOnMissing): def __init__(self): self.__module__ = 'foo' self.assertEqual(RaisesErrorOnModule().__name__, 'foo') self.assertEqual(RaisesErrorOnName().__module__, 'foo') with self.assertRaises(AttributeError): getattr(RaisesErrorOnModule(), '__module__') with self.assertRaises(AttributeError): getattr(RaisesErrorOnName(), '__name__') for cls in RaisesErrorOnModule, RaisesErrorOnName: self.assertIs(meth(cls()), NotImplemented) # If the other object has a comparison function, returning # NotImplemented means Python calls it. class AllowsAnyComparison(RaisesErrorOnMissing): def __eq__(self, other): return True __lt__ = __eq__ __le__ = __eq__ __gt__ = __eq__ __ge__ = __eq__ __ne__ = __eq__ self.assertTrue(op(ib, AllowsAnyComparison())) self.assertIs(meth(AllowsAnyComparison()), NotImplemented) # If it doesn't have the comparison, Python raises a TypeError. class AllowsNoComparison: __eq__ = None __lt__ = __eq__ __le__ = __eq__ __gt__ = __eq__ __ge__ = __eq__ __ne__ = __eq__ self.assertIs(meth(AllowsNoComparison()), NotImplemented) with self.assertRaises(TypeError): op(ib, AllowsNoComparison()) # Errors besides AttributeError are passed class MyException(Exception): pass RaisesErrorOnMissing.Exc = MyException with self.assertRaises(MyException): getattr(RaisesErrorOnModule(), '__module__') with self.assertRaises(MyException): getattr(RaisesErrorOnName(), '__name__') for cls in RaisesErrorOnModule, RaisesErrorOnName: with self.assertRaises(MyException): op(ib, cls()) with self.assertRaises(MyException): meth(cls()) def test__lt__NotImplemented(self): self.__check_NotImplemented_comparison('lt') def test__le__NotImplemented(self): self.__check_NotImplemented_comparison('le') def test__gt__NotImplemented(self): self.__check_NotImplemented_comparison('gt') def test__ge__NotImplemented(self): self.__check_NotImplemented_comparison('ge') class InterfaceBaseTestsMixin(NameAndModuleComparisonTestsMixin): # Tests for both C and Python implementation def _getTargetClass(self): raise NotImplementedError def _getFallbackClass(self): # pylint:disable=no-name-in-module from zope.interface.interface import InterfaceBasePy return InterfaceBasePy def _makeOne(self, object_should_provide=False, name=None, module=None): class IB(self._getTargetClass()): def _call_conform(self, conform): return conform(self) def providedBy(self, obj): return object_should_provide return IB(name, module) def test___call___w___conform___returning_value(self): ib = self._makeOne(False) conformed = object() class _Adapted: def __conform__(self, iface): return conformed self.assertIs(ib(_Adapted()), conformed) def test___call___wo___conform___ob_no_provides_w_alternate(self): ib = self._makeOne(False) __traceback_info__ = ib, self._getTargetClass() adapted = object() alternate = object() self.assertIs(ib(adapted, alternate), alternate) def test___call___w___conform___ob_no_provides_wo_alternate(self): ib = self._makeOne(False) with self.assertRaises(TypeError) as exc: ib(object()) self.assertIn('Could not adapt', str(exc.exception)) def test___call___w_no_conform_catches_only_AttributeError(self): MissingSomeAttrs.test_raises(self, self._makeOne(), expected_missing='__conform__') class InterfaceBaseTests(InterfaceBaseTestsMixin, OptimizationTestMixin, unittest.TestCase): # Tests that work with the C implementation def _getTargetClass(self): from zope.interface.interface import InterfaceBase return InterfaceBase class InterfaceBasePyTests(InterfaceBaseTestsMixin, unittest.TestCase): # Tests that only work with the Python implementation _getTargetClass = InterfaceBaseTestsMixin._getFallbackClass def test___call___w___conform___miss_ob_provides(self): ib = self._makeOne(True) class _Adapted: def __conform__(self, iface): return None adapted = _Adapted() self.assertIs(ib(adapted), adapted) def test___adapt___ob_provides(self): ib = self._makeOne(True) adapted = object() self.assertIs(ib.__adapt__(adapted), adapted) def test___adapt___ob_no_provides_uses_hooks(self): from zope.interface import interface ib = self._makeOne(False) adapted = object() _missed = [] def _hook_miss(iface, obj): _missed.append((iface, obj)) def _hook_hit(iface, obj): return obj with _Monkey(interface, adapter_hooks=[_hook_miss, _hook_hit]): self.assertIs(ib.__adapt__(adapted), adapted) self.assertEqual(_missed, [(ib, adapted)]) class SpecificationTests(unittest.TestCase): def _getTargetClass(self): from zope.interface.interface import Specification return Specification def _makeOne(self, bases=_marker): if bases is _marker: return self._getTargetClass()() return self._getTargetClass()(bases) def test_ctor(self): from zope.interface.interface import Interface spec = self._makeOne() self.assertEqual(spec.__bases__, ()) self.assertEqual(len(spec._implied), 2) self.assertTrue(spec in spec._implied) self.assertTrue(Interface in spec._implied) self.assertEqual(len(spec.dependents), 0) def test_subscribe_first_time(self): spec = self._makeOne() dep = DummyDependent() spec.subscribe(dep) self.assertEqual(len(spec.dependents), 1) self.assertEqual(spec.dependents[dep], 1) def test_subscribe_again(self): spec = self._makeOne() dep = DummyDependent() spec.subscribe(dep) spec.subscribe(dep) self.assertEqual(spec.dependents[dep], 2) def test_unsubscribe_miss(self): spec = self._makeOne() dep = DummyDependent() self.assertRaises(KeyError, spec.unsubscribe, dep) def test_unsubscribe(self): spec = self._makeOne() dep = DummyDependent() spec.subscribe(dep) spec.subscribe(dep) spec.unsubscribe(dep) self.assertEqual(spec.dependents[dep], 1) spec.unsubscribe(dep) self.assertFalse(dep in spec.dependents) def test___setBases_subscribes_bases_and_notifies_dependents(self): from zope.interface.interface import Interface spec = self._makeOne() dep = DummyDependent() spec.subscribe(dep) class I(Interface): pass class J(Interface): pass spec.__bases__ = (I,) self.assertEqual(dep._changed, [spec]) self.assertEqual(I.dependents[spec], 1) spec.__bases__ = (J,) self.assertEqual(I.dependents.get(spec), None) self.assertEqual(J.dependents[spec], 1) def test_changed_clears_volatiles_and_implied(self): from zope.interface.interface import Interface class I(Interface): pass spec = self._makeOne() spec._v_attrs = 'Foo' spec._implied[I] = () spec.changed(spec) self.assertIsNone(spec._v_attrs) self.assertFalse(I in spec._implied) def test_interfaces_skips_already_seen(self): from zope.interface.interface import Interface class IFoo(Interface): pass spec = self._makeOne([IFoo, IFoo]) self.assertEqual(list(spec.interfaces()), [IFoo]) def test_extends_strict_wo_self(self): from zope.interface.interface import Interface class IFoo(Interface): pass spec = self._makeOne(IFoo) self.assertFalse(spec.extends(IFoo, strict=True)) def test_extends_strict_w_self(self): spec = self._makeOne() self.assertFalse(spec.extends(spec, strict=True)) def test_extends_non_strict_w_self(self): spec = self._makeOne() self.assertTrue(spec.extends(spec, strict=False)) def test_get_hit_w__v_attrs(self): spec = self._makeOne() foo = object() spec._v_attrs = {'foo': foo} self.assertTrue(spec.get('foo') is foo) def test_get_hit_from_base_wo__v_attrs(self): from zope.interface.interface import Attribute from zope.interface.interface import Interface class IFoo(Interface): foo = Attribute('foo') class IBar(Interface): bar = Attribute('bar') spec = self._makeOne([IFoo, IBar]) self.assertTrue(spec.get('foo') is IFoo.get('foo')) self.assertTrue(spec.get('bar') is IBar.get('bar')) def test_multiple_inheritance_no_interfaces(self): # If we extend an object that implements interfaces, # plus one that doesn't, we do not interject `Interface` # early in the resolution order. It stays at the end, # like it should. # See https://github.com/zopefoundation/zope.interface/issues/8 from zope.interface.declarations import implementedBy from zope.interface.declarations import implementer from zope.interface.interface import Interface class IDefaultViewName(Interface): pass class Context: pass class RDBModel(Context): pass class IOther(Interface): pass @implementer(IOther) class OtherBase: pass class Model(OtherBase, Context): pass self.assertEqual( implementedBy(Model).__sro__, ( implementedBy(Model), implementedBy(OtherBase), IOther, implementedBy(Context), implementedBy(object), Interface, # This used to be wrong, it used to be 2 too high. ) ) class InterfaceClassTests(unittest.TestCase): def _getTargetClass(self): from zope.interface.interface import InterfaceClass return InterfaceClass def _makeOne(self, name='ITest', bases=(), attrs=None, __doc__=None, __module__=None): return self._getTargetClass()(name, bases, attrs, __doc__, __module__) def test_ctor_defaults(self): klass = self._getTargetClass() inst = klass('ITesting') self.assertEqual(inst.__name__, 'ITesting') self.assertEqual(inst.__doc__, '') self.assertEqual(inst.__bases__, ()) self.assertEqual(inst.getBases(), ()) def test_ctor_bad_bases(self): klass = self._getTargetClass() self.assertRaises(TypeError, klass, 'ITesting', (object(),)) def test_ctor_w_attrs_attrib_methods(self): from zope.interface.interface import Attribute from zope.interface.interface import fromFunction def _bar(): """DOCSTRING""" ATTRS = {'foo': Attribute('Foo', ''), 'bar': fromFunction(_bar), } klass = self._getTargetClass() inst = klass('ITesting', attrs=ATTRS) self.assertEqual(inst.__name__, 'ITesting') self.assertEqual(inst.__doc__, '') self.assertEqual(inst.__bases__, ()) self.assertEqual(inst.names(), ATTRS.keys()) def test_ctor_attrs_w___locals__(self): ATTRS = {'__locals__': {}} klass = self._getTargetClass() inst = klass('ITesting', attrs=ATTRS) self.assertEqual(inst.__name__, 'ITesting') self.assertEqual(inst.__doc__, '') self.assertEqual(inst.__bases__, ()) self.assertEqual(list(inst.names()), []) def test_ctor_attrs_w___annotations__(self): ATTRS = {'__annotations__': {}} klass = self._getTargetClass() inst = klass('ITesting', attrs=ATTRS) self.assertEqual(inst.__name__, 'ITesting') self.assertEqual(inst.__doc__, '') self.assertEqual(inst.__bases__, ()) self.assertEqual(list(inst.names()), []) def test_ctor_attrs_w__decorator_non_return(self): from zope.interface.interface import _decorator_non_return ATTRS = {'dropme': _decorator_non_return} klass = self._getTargetClass() inst = klass('ITesting', attrs=ATTRS) self.assertEqual(inst.__name__, 'ITesting') self.assertEqual(inst.__doc__, '') self.assertEqual(inst.__bases__, ()) self.assertEqual(list(inst.names()), []) def test_ctor_attrs_w_invalid_attr_type(self): from zope.interface.exceptions import InvalidInterface ATTRS = {'invalid': object()} klass = self._getTargetClass() self.assertRaises(InvalidInterface, klass, 'ITesting', attrs=ATTRS) def test_ctor_w_explicit___doc__(self): ATTRS = {'__doc__': 'ATTR'} klass = self._getTargetClass() inst = klass('ITesting', attrs=ATTRS, __doc__='EXPLICIT') self.assertEqual(inst.__doc__, 'EXPLICIT') def test_interfaces(self): iface = self._makeOne() self.assertEqual(list(iface.interfaces()), [iface]) def test_getBases(self): iface = self._makeOne() sub = self._makeOne('ISub', bases=(iface,)) self.assertEqual(sub.getBases(), (iface,)) def test_isEqualOrExtendedBy_identity(self): iface = self._makeOne() self.assertTrue(iface.isEqualOrExtendedBy(iface)) def test_isEqualOrExtendedBy_subiface(self): iface = self._makeOne() sub = self._makeOne('ISub', bases=(iface,)) self.assertTrue(iface.isEqualOrExtendedBy(sub)) self.assertFalse(sub.isEqualOrExtendedBy(iface)) def test_isEqualOrExtendedBy_unrelated(self): one = self._makeOne('One') another = self._makeOne('Another') self.assertFalse(one.isEqualOrExtendedBy(another)) self.assertFalse(another.isEqualOrExtendedBy(one)) def test_names_w_all_False_ignores_bases(self): from zope.interface.interface import Attribute from zope.interface.interface import fromFunction def _bar(): """DOCSTRING""" BASE_ATTRS = {'foo': Attribute('Foo', ''), 'bar': fromFunction(_bar), } DERIVED_ATTRS = {'baz': Attribute('Baz', ''), } base = self._makeOne('IBase', attrs=BASE_ATTRS) derived = self._makeOne('IDerived', bases=(base,), attrs=DERIVED_ATTRS) self.assertEqual(sorted(derived.names(all=False)), ['baz']) def test_names_w_all_True_no_bases(self): from zope.interface.interface import Attribute from zope.interface.interface import fromFunction def _bar(): """DOCSTRING""" ATTRS = {'foo': Attribute('Foo', ''), 'bar': fromFunction(_bar), } one = self._makeOne(attrs=ATTRS) self.assertEqual(sorted(one.names(all=True)), ['bar', 'foo']) def test_names_w_all_True_w_bases_simple(self): from zope.interface.interface import Attribute from zope.interface.interface import fromFunction def _bar(): """DOCSTRING""" BASE_ATTRS = {'foo': Attribute('Foo', ''), 'bar': fromFunction(_bar), } DERIVED_ATTRS = {'baz': Attribute('Baz', ''), } base = self._makeOne('IBase', attrs=BASE_ATTRS) derived = self._makeOne('IDerived', bases=(base,), attrs=DERIVED_ATTRS) self.assertEqual(sorted(derived.names(all=True)), ['bar', 'baz', 'foo']) def test_names_w_all_True_bases_w_same_names(self): from zope.interface.interface import Attribute from zope.interface.interface import fromFunction def _bar(): """DOCSTRING""" def _foo(): """DOCSTRING""" BASE_ATTRS = {'foo': Attribute('Foo', ''), 'bar': fromFunction(_bar), } DERIVED_ATTRS = {'foo': fromFunction(_foo), 'baz': Attribute('Baz', ''), } base = self._makeOne('IBase', attrs=BASE_ATTRS) derived = self._makeOne('IDerived', bases=(base,), attrs=DERIVED_ATTRS) self.assertEqual(sorted(derived.names(all=True)), ['bar', 'baz', 'foo']) def test___iter__(self): from zope.interface.interface import Attribute from zope.interface.interface import fromFunction def _bar(): """DOCSTRING""" def _foo(): """DOCSTRING""" BASE_ATTRS = {'foo': Attribute('Foo', ''), 'bar': fromFunction(_bar), } DERIVED_ATTRS = {'foo': fromFunction(_foo), 'baz': Attribute('Baz', ''), } base = self._makeOne('IBase', attrs=BASE_ATTRS) derived = self._makeOne('IDerived', bases=(base,), attrs=DERIVED_ATTRS) self.assertEqual(sorted(derived), ['bar', 'baz', 'foo']) def test_namesAndDescriptions_w_all_False_ignores_bases(self): from zope.interface.interface import Attribute from zope.interface.interface import fromFunction def _bar(): """DOCSTRING""" BASE_ATTRS = {'foo': Attribute('Foo', ''), 'bar': fromFunction(_bar), } DERIVED_ATTRS = {'baz': Attribute('Baz', ''), } base = self._makeOne('IBase', attrs=BASE_ATTRS) derived = self._makeOne('IDerived', bases=(base,), attrs=DERIVED_ATTRS) self.assertEqual(sorted(derived.namesAndDescriptions(all=False)), [('baz', DERIVED_ATTRS['baz']), ]) def test_namesAndDescriptions_w_all_True_no_bases(self): from zope.interface.interface import Attribute from zope.interface.interface import fromFunction def _bar(): """DOCSTRING""" ATTRS = {'foo': Attribute('Foo', ''), 'bar': fromFunction(_bar), } one = self._makeOne(attrs=ATTRS) self.assertEqual(sorted(one.namesAndDescriptions(all=False)), [('bar', ATTRS['bar']), ('foo', ATTRS['foo']), ]) def test_namesAndDescriptions_w_all_True_simple(self): from zope.interface.interface import Attribute from zope.interface.interface import fromFunction def _bar(): """DOCSTRING""" BASE_ATTRS = {'foo': Attribute('Foo', ''), 'bar': fromFunction(_bar), } DERIVED_ATTRS = {'baz': Attribute('Baz', ''), } base = self._makeOne('IBase', attrs=BASE_ATTRS) derived = self._makeOne('IDerived', bases=(base,), attrs=DERIVED_ATTRS) self.assertEqual(sorted(derived.namesAndDescriptions(all=True)), [('bar', BASE_ATTRS['bar']), ('baz', DERIVED_ATTRS['baz']), ('foo', BASE_ATTRS['foo']), ]) def test_namesAndDescriptions_w_all_True_bases_w_same_names(self): from zope.interface.interface import Attribute from zope.interface.interface import fromFunction def _bar(): """DOCSTRING""" def _foo(): """DOCSTRING""" BASE_ATTRS = {'foo': Attribute('Foo', ''), 'bar': fromFunction(_bar), } DERIVED_ATTRS = {'foo': fromFunction(_foo), 'baz': Attribute('Baz', ''), } base = self._makeOne('IBase', attrs=BASE_ATTRS) derived = self._makeOne('IDerived', bases=(base,), attrs=DERIVED_ATTRS) self.assertEqual(sorted(derived.namesAndDescriptions(all=True)), [('bar', BASE_ATTRS['bar']), ('baz', DERIVED_ATTRS['baz']), ('foo', DERIVED_ATTRS['foo']), ]) def test_getDescriptionFor_miss(self): one = self._makeOne() self.assertRaises(KeyError, one.getDescriptionFor, 'nonesuch') def test_getDescriptionFor_hit(self): from zope.interface.interface import Attribute from zope.interface.interface import fromFunction def _bar(): """DOCSTRING""" ATTRS = {'foo': Attribute('Foo', ''), 'bar': fromFunction(_bar), } one = self._makeOne(attrs=ATTRS) self.assertEqual(one.getDescriptionFor('foo'), ATTRS['foo']) self.assertEqual(one.getDescriptionFor('bar'), ATTRS['bar']) def test___getitem___miss(self): one = self._makeOne() def _test(): return one['nonesuch'] self.assertRaises(KeyError, _test) def test___getitem___hit(self): from zope.interface.interface import Attribute from zope.interface.interface import fromFunction def _bar(): """DOCSTRING""" ATTRS = {'foo': Attribute('Foo', ''), 'bar': fromFunction(_bar), } one = self._makeOne(attrs=ATTRS) self.assertEqual(one['foo'], ATTRS['foo']) self.assertEqual(one['bar'], ATTRS['bar']) def test___contains___miss(self): one = self._makeOne() self.assertFalse('nonesuch' in one) def test___contains___hit(self): from zope.interface.interface import Attribute from zope.interface.interface import fromFunction def _bar(): """DOCSTRING""" ATTRS = {'foo': Attribute('Foo', ''), 'bar': fromFunction(_bar), } one = self._makeOne(attrs=ATTRS) self.assertTrue('foo' in one) self.assertTrue('bar' in one) def test_direct_miss(self): one = self._makeOne() self.assertEqual(one.direct('nonesuch'), None) def test_direct_hit_local_miss_bases(self): from zope.interface.interface import Attribute from zope.interface.interface import fromFunction def _bar(): """DOCSTRING""" def _foo(): """DOCSTRING""" BASE_ATTRS = {'foo': Attribute('Foo', ''), 'bar': fromFunction(_bar), } DERIVED_ATTRS = {'foo': fromFunction(_foo), 'baz': Attribute('Baz', ''), } base = self._makeOne('IBase', attrs=BASE_ATTRS) derived = self._makeOne('IDerived', bases=(base,), attrs=DERIVED_ATTRS) self.assertEqual(derived.direct('foo'), DERIVED_ATTRS['foo']) self.assertEqual(derived.direct('baz'), DERIVED_ATTRS['baz']) self.assertEqual(derived.direct('bar'), None) def test_queryDescriptionFor_miss(self): iface = self._makeOne() self.assertEqual(iface.queryDescriptionFor('nonesuch'), None) def test_queryDescriptionFor_hit(self): from zope.interface import Attribute ATTRS = {'attr': Attribute('Title', 'Description')} iface = self._makeOne(attrs=ATTRS) self.assertEqual(iface.queryDescriptionFor('attr'), ATTRS['attr']) def test_validateInvariants_pass(self): _called_with = [] def _passable(*args, **kw): _called_with.append((args, kw)) return True iface = self._makeOne() obj = object() iface.setTaggedValue('invariants', [_passable]) self.assertEqual(iface.validateInvariants(obj), None) self.assertEqual(_called_with, [((obj,), {})]) def test_validateInvariants_fail_wo_errors_passed(self): from zope.interface.exceptions import Invalid _passable_called_with = [] def _passable(*args, **kw): _passable_called_with.append((args, kw)) return True _fail_called_with = [] def _fail(*args, **kw): _fail_called_with.append((args, kw)) raise Invalid iface = self._makeOne() obj = object() iface.setTaggedValue('invariants', [_passable, _fail]) self.assertRaises(Invalid, iface.validateInvariants, obj) self.assertEqual(_passable_called_with, [((obj,), {})]) self.assertEqual(_fail_called_with, [((obj,), {})]) def test_validateInvariants_fail_w_errors_passed(self): from zope.interface.exceptions import Invalid _errors = [] _fail_called_with = [] def _fail(*args, **kw): _fail_called_with.append((args, kw)) raise Invalid iface = self._makeOne() obj = object() iface.setTaggedValue('invariants', [_fail]) self.assertRaises(Invalid, iface.validateInvariants, obj, _errors) self.assertEqual(_fail_called_with, [((obj,), {})]) self.assertEqual(len(_errors), 1) self.assertTrue(isinstance(_errors[0], Invalid)) def test_validateInvariants_fail_in_base_wo_errors_passed(self): from zope.interface.exceptions import Invalid _passable_called_with = [] def _passable(*args, **kw): _passable_called_with.append((args, kw)) return True _fail_called_with = [] def _fail(*args, **kw): _fail_called_with.append((args, kw)) raise Invalid base = self._makeOne('IBase') derived = self._makeOne('IDerived', (base,)) obj = object() base.setTaggedValue('invariants', [_fail]) derived.setTaggedValue('invariants', [_passable]) self.assertRaises(Invalid, derived.validateInvariants, obj) self.assertEqual(_passable_called_with, [((obj,), {})]) self.assertEqual(_fail_called_with, [((obj,), {})]) def test_validateInvariants_fail_in_base_w_errors_passed(self): from zope.interface.exceptions import Invalid _errors = [] _passable_called_with = [] def _passable(*args, **kw): _passable_called_with.append((args, kw)) return True _fail_called_with = [] def _fail(*args, **kw): _fail_called_with.append((args, kw)) raise Invalid base = self._makeOne('IBase') derived = self._makeOne('IDerived', (base,)) obj = object() base.setTaggedValue('invariants', [_fail]) derived.setTaggedValue('invariants', [_passable]) self.assertRaises(Invalid, derived.validateInvariants, obj, _errors) self.assertEqual(_passable_called_with, [((obj,), {})]) self.assertEqual(_fail_called_with, [((obj,), {})]) self.assertEqual(len(_errors), 1) self.assertTrue(isinstance(_errors[0], Invalid)) def test_validateInvariants_inherited_not_called_multiple_times(self): _passable_called_with = [] def _passable(*args, **kw): _passable_called_with.append((args, kw)) return True obj = object() base = self._makeOne('IBase') base.setTaggedValue('invariants', [_passable]) derived = self._makeOne('IDerived', (base,)) derived.validateInvariants(obj) self.assertEqual(1, len(_passable_called_with)) def test___reduce__(self): iface = self._makeOne('PickleMe') self.assertEqual(iface.__reduce__(), 'PickleMe') def test___hash___normal(self): iface = self._makeOne('HashMe') self.assertEqual(hash(iface), hash(('HashMe', 'zope.interface.tests.test_interface'))) def test___hash___missing_required_attrs(self): class Derived(self._getTargetClass()): def __init__(self): # pylint:disable=super-init-not-called pass # Don't call base class. derived = Derived() with self.assertRaises(AttributeError): hash(derived) def test_comparison_with_None(self): # pylint:disable=singleton-comparison,misplaced-comparison-constant iface = self._makeOne() self.assertTrue(iface < None) self.assertTrue(iface <= None) self.assertFalse(iface == None) self.assertTrue(iface != None) self.assertFalse(iface >= None) self.assertFalse(iface > None) self.assertFalse(None < iface) self.assertFalse(None <= iface) self.assertFalse(None == iface) self.assertTrue(None != iface) self.assertTrue(None >= iface) self.assertTrue(None > iface) def test_comparison_with_same_instance(self): # pylint:disable=comparison-with-itself iface = self._makeOne() self.assertFalse(iface < iface) self.assertTrue(iface <= iface) self.assertTrue(iface == iface) self.assertFalse(iface != iface) self.assertTrue(iface >= iface) self.assertFalse(iface > iface) def test_comparison_with_same_named_instance_in_other_module(self): one = self._makeOne('IName', __module__='zope.interface.tests.one') other = self._makeOne('IName', __module__='zope.interface.tests.other') self.assertTrue(one < other) self.assertFalse(other < one) self.assertTrue(one <= other) self.assertFalse(other <= one) self.assertFalse(one == other) self.assertFalse(other == one) self.assertTrue(one != other) self.assertTrue(other != one) self.assertFalse(one >= other) self.assertTrue(other >= one) self.assertFalse(one > other) self.assertTrue(other > one) def test_assignment_to__class__(self): # https://github.com/zopefoundation/zope.interface/issues/6 class MyException(Exception): pass class MyInterfaceClass(self._getTargetClass()): def __call__(self, target): raise MyException(target) IFoo = self._makeOne('IName') self.assertIsInstance(IFoo, self._getTargetClass()) self.assertIs(type(IFoo), self._getTargetClass()) with self.assertRaises(TypeError): IFoo(1) IFoo.__class__ = MyInterfaceClass self.assertIsInstance(IFoo, MyInterfaceClass) self.assertIs(type(IFoo), MyInterfaceClass) with self.assertRaises(MyException): IFoo(1) def test_assignment_to__class__2(self): # https://github.com/zopefoundation/zope.interface/issues/6 # This is essentially a transcription of the # test presented in the bug report. from zope.interface import Interface class MyInterfaceClass(self._getTargetClass()): def __call__(self, *args): return args IFoo = MyInterfaceClass('IFoo', (Interface,)) self.assertEqual(IFoo(1), (1,)) class IBar(IFoo): pass self.assertEqual(IBar(1), (1,)) class ISpam(Interface): pass with self.assertRaises(TypeError): ISpam() ISpam.__class__ = MyInterfaceClass self.assertEqual(ISpam(1), (1,)) def test__module__is_readonly(self): inst = self._makeOne() with self.assertRaises(AttributeError): inst.__module__ = 'different.module' class InterfaceTests(unittest.TestCase): def test_attributes_link_to_interface(self): from zope.interface import Attribute from zope.interface import Interface class I1(Interface): attr = Attribute("My attr") self.assertTrue(I1['attr'].interface is I1) def test_methods_link_to_interface(self): from zope.interface import Interface class I1(Interface): def method(foo, bar, bingo): "A method" self.assertTrue(I1['method'].interface is I1) def test_classImplements_simple(self): from zope.interface import Interface from zope.interface import implementedBy from zope.interface import providedBy class ICurrent(Interface): def method1(a, b): """docstring""" def method2(a, b): """docstring""" class IOther(Interface): pass class Current: __implemented__ = ICurrent def method1(self, a, b): raise NotImplementedError() def method2(self, a, b): raise NotImplementedError() current = Current() self.assertTrue(ICurrent.implementedBy(Current)) self.assertFalse(IOther.implementedBy(Current)) self.assertEqual(ICurrent, ICurrent) self.assertTrue(ICurrent in implementedBy(Current)) self.assertFalse(IOther in implementedBy(Current)) self.assertTrue(ICurrent in providedBy(current)) self.assertFalse(IOther in providedBy(current)) def test_classImplements_base_not_derived(self): from zope.interface import Interface from zope.interface import implementedBy from zope.interface import providedBy class IBase(Interface): def method(): """docstring""" class IDerived(IBase): pass class Current(): __implemented__ = IBase def method(self): raise NotImplementedError() current = Current() self.assertTrue(IBase.implementedBy(Current)) self.assertFalse(IDerived.implementedBy(Current)) self.assertTrue(IBase in implementedBy(Current)) self.assertFalse(IDerived in implementedBy(Current)) self.assertTrue(IBase in providedBy(current)) self.assertFalse(IDerived in providedBy(current)) def test_classImplements_base_and_derived(self): from zope.interface import Interface from zope.interface import implementedBy from zope.interface import providedBy class IBase(Interface): def method(): """docstring""" class IDerived(IBase): pass class Current: __implemented__ = IDerived def method(self): raise NotImplementedError() current = Current() self.assertTrue(IBase.implementedBy(Current)) self.assertTrue(IDerived.implementedBy(Current)) self.assertFalse(IBase in implementedBy(Current)) self.assertTrue(IBase in implementedBy(Current).flattened()) self.assertTrue(IDerived in implementedBy(Current)) self.assertFalse(IBase in providedBy(current)) self.assertTrue(IBase in providedBy(current).flattened()) self.assertTrue(IDerived in providedBy(current)) def test_classImplements_multiple(self): from zope.interface import Interface from zope.interface import implementedBy from zope.interface import providedBy class ILeft(Interface): def method(): """docstring""" class IRight(ILeft): pass class Left: __implemented__ = ILeft def method(self): raise NotImplementedError() class Right: __implemented__ = IRight class Ambi(Left, Right): pass ambi = Ambi() self.assertTrue(ILeft.implementedBy(Ambi)) self.assertTrue(IRight.implementedBy(Ambi)) self.assertTrue(ILeft in implementedBy(Ambi)) self.assertTrue(IRight in implementedBy(Ambi)) self.assertTrue(ILeft in providedBy(ambi)) self.assertTrue(IRight in providedBy(ambi)) def test_classImplements_multiple_w_explict_implements(self): from zope.interface import Interface from zope.interface import implementedBy from zope.interface import providedBy class ILeft(Interface): def method(): """docstring""" class IRight(ILeft): pass class IOther(Interface): pass class Left(): __implemented__ = ILeft def method(self): raise NotImplementedError() class Right: __implemented__ = IRight class Other: __implemented__ = IOther class Mixed(Left, Right): __implemented__ = Left.__implemented__, Other.__implemented__ mixed = Mixed() self.assertTrue(ILeft.implementedBy(Mixed)) self.assertFalse(IRight.implementedBy(Mixed)) self.assertTrue(IOther.implementedBy(Mixed)) self.assertTrue(ILeft in implementedBy(Mixed)) self.assertFalse(IRight in implementedBy(Mixed)) self.assertTrue(IOther in implementedBy(Mixed)) self.assertTrue(ILeft in providedBy(mixed)) self.assertFalse(IRight in providedBy(mixed)) self.assertTrue(IOther in providedBy(mixed)) def testInterfaceExtendsInterface(self): from zope.interface import Interface new = Interface.__class__ FunInterface = new('FunInterface') BarInterface = new('BarInterface', (FunInterface,)) BobInterface = new('BobInterface') BazInterface = new('BazInterface', (BobInterface, BarInterface,)) self.assertTrue(BazInterface.extends(BobInterface)) self.assertTrue(BazInterface.extends(BarInterface)) self.assertTrue(BazInterface.extends(FunInterface)) self.assertFalse(BobInterface.extends(FunInterface)) self.assertFalse(BobInterface.extends(BarInterface)) self.assertTrue(BarInterface.extends(FunInterface)) self.assertFalse(BarInterface.extends(BazInterface)) def test_verifyClass(self): from zope.interface import Attribute from zope.interface import Interface from zope.interface.verify import verifyClass class ICheckMe(Interface): attr = Attribute('My attr') def method(): "A method" class CheckMe: __implemented__ = ICheckMe attr = 'value' def method(self): raise NotImplementedError() self.assertTrue(verifyClass(ICheckMe, CheckMe)) def test_verifyObject(self): from zope.interface import Attribute from zope.interface import Interface from zope.interface.verify import verifyObject class ICheckMe(Interface): attr = Attribute('My attr') def method(): "A method" class CheckMe: __implemented__ = ICheckMe attr = 'value' def method(self): raise NotImplementedError() check_me = CheckMe() self.assertTrue(verifyObject(ICheckMe, check_me)) def test_interface_object_provides_Interface(self): from zope.interface import Interface class AnInterface(Interface): pass self.assertTrue(Interface.providedBy(AnInterface)) def test_names_simple(self): from zope.interface import Attribute from zope.interface import Interface class ISimple(Interface): attr = Attribute('My attr') def method(): """docstring""" self.assertEqual(sorted(ISimple.names()), ['attr', 'method']) def test_names_derived(self): from zope.interface import Attribute from zope.interface import Interface class IBase(Interface): attr = Attribute('My attr') def method(): """docstring""" class IDerived(IBase): attr2 = Attribute('My attr2') def method(): """docstring""" def method2(): """docstring""" self.assertEqual(sorted(IDerived.names()), ['attr2', 'method', 'method2']) self.assertEqual(sorted(IDerived.names(all=True)), ['attr', 'attr2', 'method', 'method2']) def test_namesAndDescriptions_simple(self): from zope.interface import Attribute from zope.interface import Interface from zope.interface.interface import Method class ISimple(Interface): attr = Attribute('My attr') def method(): "My method" name_values = sorted(ISimple.namesAndDescriptions()) self.assertEqual(len(name_values), 2) self.assertEqual(name_values[0][0], 'attr') self.assertTrue(isinstance(name_values[0][1], Attribute)) self.assertEqual(name_values[0][1].__name__, 'attr') self.assertEqual(name_values[0][1].__doc__, 'My attr') self.assertEqual(name_values[1][0], 'method') self.assertTrue(isinstance(name_values[1][1], Method)) self.assertEqual(name_values[1][1].__name__, 'method') self.assertEqual(name_values[1][1].__doc__, 'My method') def test_namesAndDescriptions_derived(self): from zope.interface import Attribute from zope.interface import Interface from zope.interface.interface import Method class IBase(Interface): attr = Attribute('My attr') def method(): "My method" class IDerived(IBase): attr2 = Attribute('My attr2') def method(): "My method, overridden" def method2(): "My method2" name_values = sorted(IDerived.namesAndDescriptions()) self.assertEqual(len(name_values), 3) self.assertEqual(name_values[0][0], 'attr2') self.assertTrue(isinstance(name_values[0][1], Attribute)) self.assertEqual(name_values[0][1].__name__, 'attr2') self.assertEqual(name_values[0][1].__doc__, 'My attr2') self.assertEqual(name_values[1][0], 'method') self.assertTrue(isinstance(name_values[1][1], Method)) self.assertEqual(name_values[1][1].__name__, 'method') self.assertEqual(name_values[1][1].__doc__, 'My method, overridden') self.assertEqual(name_values[2][0], 'method2') self.assertTrue(isinstance(name_values[2][1], Method)) self.assertEqual(name_values[2][1].__name__, 'method2') self.assertEqual(name_values[2][1].__doc__, 'My method2') name_values = sorted(IDerived.namesAndDescriptions(all=True)) self.assertEqual(len(name_values), 4) self.assertEqual(name_values[0][0], 'attr') self.assertTrue(isinstance(name_values[0][1], Attribute)) self.assertEqual(name_values[0][1].__name__, 'attr') self.assertEqual(name_values[0][1].__doc__, 'My attr') self.assertEqual(name_values[1][0], 'attr2') self.assertTrue(isinstance(name_values[1][1], Attribute)) self.assertEqual(name_values[1][1].__name__, 'attr2') self.assertEqual(name_values[1][1].__doc__, 'My attr2') self.assertEqual(name_values[2][0], 'method') self.assertTrue(isinstance(name_values[2][1], Method)) self.assertEqual(name_values[2][1].__name__, 'method') self.assertEqual(name_values[2][1].__doc__, 'My method, overridden') self.assertEqual(name_values[3][0], 'method2') self.assertTrue(isinstance(name_values[3][1], Method)) self.assertEqual(name_values[3][1].__name__, 'method2') self.assertEqual(name_values[3][1].__doc__, 'My method2') def test_getDescriptionFor_nonesuch_no_default(self): from zope.interface import Interface class IEmpty(Interface): pass self.assertRaises(KeyError, IEmpty.getDescriptionFor, 'nonesuch') def test_getDescriptionFor_simple(self): from zope.interface import Attribute from zope.interface import Interface from zope.interface.interface import Method class ISimple(Interface): attr = Attribute('My attr') def method(): "My method" a_desc = ISimple.getDescriptionFor('attr') self.assertTrue(isinstance(a_desc, Attribute)) self.assertEqual(a_desc.__name__, 'attr') self.assertEqual(a_desc.__doc__, 'My attr') m_desc = ISimple.getDescriptionFor('method') self.assertTrue(isinstance(m_desc, Method)) self.assertEqual(m_desc.__name__, 'method') self.assertEqual(m_desc.__doc__, 'My method') def test_getDescriptionFor_derived(self): from zope.interface import Attribute from zope.interface import Interface from zope.interface.interface import Method class IBase(Interface): attr = Attribute('My attr') def method(): "My method" class IDerived(IBase): attr2 = Attribute('My attr2') def method(): "My method, overridden" def method2(): "My method2" a_desc = IDerived.getDescriptionFor('attr') self.assertTrue(isinstance(a_desc, Attribute)) self.assertEqual(a_desc.__name__, 'attr') self.assertEqual(a_desc.__doc__, 'My attr') m_desc = IDerived.getDescriptionFor('method') self.assertTrue(isinstance(m_desc, Method)) self.assertEqual(m_desc.__name__, 'method') self.assertEqual(m_desc.__doc__, 'My method, overridden') a2_desc = IDerived.getDescriptionFor('attr2') self.assertTrue(isinstance(a2_desc, Attribute)) self.assertEqual(a2_desc.__name__, 'attr2') self.assertEqual(a2_desc.__doc__, 'My attr2') m2_desc = IDerived.getDescriptionFor('method2') self.assertTrue(isinstance(m2_desc, Method)) self.assertEqual(m2_desc.__name__, 'method2') self.assertEqual(m2_desc.__doc__, 'My method2') def test___getitem__nonesuch(self): from zope.interface import Interface class IEmpty(Interface): pass self.assertRaises(KeyError, IEmpty.__getitem__, 'nonesuch') def test___getitem__simple(self): from zope.interface import Attribute from zope.interface import Interface from zope.interface.interface import Method class ISimple(Interface): attr = Attribute('My attr') def method(): "My method" a_desc = ISimple['attr'] self.assertTrue(isinstance(a_desc, Attribute)) self.assertEqual(a_desc.__name__, 'attr') self.assertEqual(a_desc.__doc__, 'My attr') m_desc = ISimple['method'] self.assertTrue(isinstance(m_desc, Method)) self.assertEqual(m_desc.__name__, 'method') self.assertEqual(m_desc.__doc__, 'My method') def test___getitem___derived(self): from zope.interface import Attribute from zope.interface import Interface from zope.interface.interface import Method class IBase(Interface): attr = Attribute('My attr') def method(): "My method" class IDerived(IBase): attr2 = Attribute('My attr2') def method(): "My method, overridden" def method2(): "My method2" a_desc = IDerived['attr'] self.assertTrue(isinstance(a_desc, Attribute)) self.assertEqual(a_desc.__name__, 'attr') self.assertEqual(a_desc.__doc__, 'My attr') m_desc = IDerived['method'] self.assertTrue(isinstance(m_desc, Method)) self.assertEqual(m_desc.__name__, 'method') self.assertEqual(m_desc.__doc__, 'My method, overridden') a2_desc = IDerived['attr2'] self.assertTrue(isinstance(a2_desc, Attribute)) self.assertEqual(a2_desc.__name__, 'attr2') self.assertEqual(a2_desc.__doc__, 'My attr2') m2_desc = IDerived['method2'] self.assertTrue(isinstance(m2_desc, Method)) self.assertEqual(m2_desc.__name__, 'method2') self.assertEqual(m2_desc.__doc__, 'My method2') def test___contains__nonesuch(self): from zope.interface import Interface class IEmpty(Interface): pass self.assertFalse('nonesuch' in IEmpty) def test___contains__simple(self): from zope.interface import Attribute from zope.interface import Interface class ISimple(Interface): attr = Attribute('My attr') def method(): "My method" self.assertTrue('attr' in ISimple) self.assertTrue('method' in ISimple) def test___contains__derived(self): from zope.interface import Attribute from zope.interface import Interface class IBase(Interface): attr = Attribute('My attr') def method(): "My method" class IDerived(IBase): attr2 = Attribute('My attr2') def method(): "My method, overridden" def method2(): "My method2" self.assertTrue('attr' in IDerived) self.assertTrue('method' in IDerived) self.assertTrue('attr2' in IDerived) self.assertTrue('method2' in IDerived) def test___iter__empty(self): from zope.interface import Interface class IEmpty(Interface): pass self.assertEqual(list(IEmpty), []) def test___iter__simple(self): from zope.interface import Attribute from zope.interface import Interface class ISimple(Interface): attr = Attribute('My attr') def method(): "My method" self.assertEqual(sorted(list(ISimple)), ['attr', 'method']) def test___iter__derived(self): from zope.interface import Attribute from zope.interface import Interface class IBase(Interface): attr = Attribute('My attr') def method(): "My method" class IDerived(IBase): attr2 = Attribute('My attr2') def method(): "My method, overridden" def method2(): "My method2" self.assertEqual(sorted(list(IDerived)), ['attr', 'attr2', 'method', 'method2']) def test_function_attributes_become_tagged_values(self): from zope.interface import Interface class ITagMe(Interface): def method(): """docstring""" method.optional = 1 method = ITagMe['method'] self.assertEqual(method.getTaggedValue('optional'), 1) def test___doc___non_element(self): from zope.interface import Interface class IHaveADocString(Interface): "xxx" self.assertEqual(IHaveADocString.__doc__, "xxx") self.assertEqual(list(IHaveADocString), []) def test___doc___as_element(self): from zope.interface import Attribute from zope.interface import Interface class IHaveADocString(Interface): "xxx" __doc__ = Attribute('the doc') self.assertEqual(IHaveADocString.__doc__, "") self.assertEqual(list(IHaveADocString), ['__doc__']) def _errorsEqual(self, has_invariant, error_len, error_msgs, iface): from zope.interface.exceptions import Invalid self.assertRaises(Invalid, iface.validateInvariants, has_invariant) e = [] try: iface.validateInvariants(has_invariant, e) self.fail("validateInvariants should always raise") except Invalid as error: self.assertEqual(error.args[0], e) self.assertEqual(len(e), error_len) msgs = [error.args[0] for error in e] msgs.sort() for msg in msgs: self.assertEqual(msg, error_msgs.pop(0)) def test_invariant_simple(self): from zope.interface import Attribute from zope.interface import Interface from zope.interface import directlyProvides from zope.interface import invariant class IInvariant(Interface): foo = Attribute('foo') bar = Attribute('bar; must eval to Boolean True if foo does') invariant(_ifFooThenBar) class HasInvariant: pass # set up has_invariant = HasInvariant() directlyProvides(has_invariant, IInvariant) # the tests self.assertEqual(IInvariant.getTaggedValue('invariants'), [_ifFooThenBar]) self.assertEqual(IInvariant.validateInvariants(has_invariant), None) has_invariant.bar = 27 self.assertEqual(IInvariant.validateInvariants(has_invariant), None) has_invariant.foo = 42 self.assertEqual(IInvariant.validateInvariants(has_invariant), None) del has_invariant.bar self._errorsEqual(has_invariant, 1, ['If Foo, then Bar!'], IInvariant) def test_invariant_nested(self): from zope.interface import Attribute from zope.interface import Interface from zope.interface import directlyProvides from zope.interface import invariant class IInvariant(Interface): foo = Attribute('foo') bar = Attribute('bar; must eval to Boolean True if foo does') invariant(_ifFooThenBar) class ISubInvariant(IInvariant): invariant(_barGreaterThanFoo) class HasInvariant: pass # nested interfaces with invariants: self.assertEqual(ISubInvariant.getTaggedValue('invariants'), [_barGreaterThanFoo]) has_invariant = HasInvariant() directlyProvides(has_invariant, ISubInvariant) has_invariant.foo = 42 # even though the interface has changed, we should still only have one # error. self._errorsEqual(has_invariant, 1, ['If Foo, then Bar!'], ISubInvariant) # however, if we set foo to 0 (Boolean False) and bar to a negative # number then we'll get the new error has_invariant.foo = 2 has_invariant.bar = 1 self._errorsEqual(has_invariant, 1, ['Please, Boo MUST be greater than Foo!'], ISubInvariant) # and if we set foo to a positive number and boo to 0, we'll # get both errors! has_invariant.foo = 1 has_invariant.bar = 0 self._errorsEqual(has_invariant, 2, ['If Foo, then Bar!', 'Please, Boo MUST be greater than Foo!'], ISubInvariant) # for a happy ending, we'll make the invariants happy has_invariant.foo = 1 has_invariant.bar = 2 self.assertEqual(IInvariant.validateInvariants(has_invariant), None) def test_invariant_mutandis(self): from zope.interface import Attribute from zope.interface import Interface from zope.interface import directlyProvides from zope.interface import invariant class IInvariant(Interface): foo = Attribute('foo') bar = Attribute('bar; must eval to Boolean True if foo does') invariant(_ifFooThenBar) class HasInvariant: pass # now we'll do two invariants on the same interface, # just to make sure that a small # multi-invariant interface is at least minimally tested. has_invariant = HasInvariant() directlyProvides(has_invariant, IInvariant) has_invariant.foo = 42 # if you really need to mutate, then this would be the way to do it. # Probably a bad idea, though. :-) old_invariants = IInvariant.getTaggedValue('invariants') invariants = old_invariants[:] invariants.append(_barGreaterThanFoo) IInvariant.setTaggedValue('invariants', invariants) # even though the interface has changed, we should still only have one # error. self._errorsEqual(has_invariant, 1, ['If Foo, then Bar!'], IInvariant) # however, if we set foo to 0 (Boolean False) and bar to a negative # number then we'll get the new error has_invariant.foo = 2 has_invariant.bar = 1 self._errorsEqual(has_invariant, 1, ['Please, Boo MUST be greater than Foo!'], IInvariant) # and if we set foo to a positive number and boo to 0, we'll # get both errors! has_invariant.foo = 1 has_invariant.bar = 0 self._errorsEqual(has_invariant, 2, ['If Foo, then Bar!', 'Please, Boo MUST be greater than Foo!'], IInvariant) # for another happy ending, we'll make the invariants happy again has_invariant.foo = 1 has_invariant.bar = 2 self.assertEqual(IInvariant.validateInvariants(has_invariant), None) # clean up IInvariant.setTaggedValue('invariants', old_invariants) def test___doc___element(self): from zope.interface import Attribute from zope.interface import Interface class IDocstring(Interface): "xxx" self.assertEqual(IDocstring.__doc__, "xxx") self.assertEqual(list(IDocstring), []) class IDocstringAndAttribute(Interface): "xxx" __doc__ = Attribute('the doc') self.assertEqual(IDocstringAndAttribute.__doc__, "") self.assertEqual(list(IDocstringAndAttribute), ['__doc__']) def test_invariant_as_decorator(self): from zope.interface import Attribute from zope.interface import Interface from zope.interface import implementer from zope.interface import invariant from zope.interface.exceptions import Invalid class IRange(Interface): min = Attribute("Lower bound") max = Attribute("Upper bound") @invariant def range_invariant(ob): if ob.max < ob.min: raise Invalid('max < min') @implementer(IRange) class Range: def __init__(self, min, max): self.min, self.max = min, max IRange.validateInvariants(Range(1, 2)) IRange.validateInvariants(Range(1, 1)) try: IRange.validateInvariants(Range(2, 1)) except Invalid as e: self.assertEqual(str(e), 'max < min') def test_taggedValue(self): from zope.interface import Attribute from zope.interface import Interface from zope.interface import taggedValue class ITagged(Interface): foo = Attribute('foo') bar = Attribute('bar; must eval to Boolean True if foo does') taggedValue('qux', 'Spam') class IDerived(ITagged): taggedValue('qux', 'Spam Spam') taggedValue('foo', 'bar') class IDerived2(IDerived): pass self.assertEqual(ITagged.getTaggedValue('qux'), 'Spam') self.assertRaises(KeyError, ITagged.getTaggedValue, 'foo') self.assertEqual(list(ITagged.getTaggedValueTags()), ['qux']) self.assertEqual(IDerived2.getTaggedValue('qux'), 'Spam Spam') self.assertEqual(IDerived2.getTaggedValue('foo'), 'bar') self.assertEqual(set(IDerived2.getTaggedValueTags()), {'qux', 'foo'}) def _make_taggedValue_tree(self, base): from zope.interface import Attribute from zope.interface import taggedValue O = base class F(O): taggedValue('tag', 'F') tag = Attribute('F') class E(O): taggedValue('tag', 'E') tag = Attribute('E') class D(O): taggedValue('tag', 'D') tag = Attribute('D') class C(D, F): taggedValue('tag', 'C') tag = Attribute('C') class B(D, E): pass class A(B, C): pass return A def test_getTaggedValue_follows__iro__(self): # And not just looks at __bases__. # https://github.com/zopefoundation/zope.interface/issues/190 from zope.interface import Interface # First, confirm that looking at a true class # hierarchy follows the __mro__. class_A = self._make_taggedValue_tree(object) self.assertEqual(class_A.tag.__name__, 'C') # Now check that Interface does, both for attributes... iface_A = self._make_taggedValue_tree(Interface) self.assertEqual(iface_A['tag'].__name__, 'C') # ... and for tagged values. self.assertEqual(iface_A.getTaggedValue('tag'), 'C') self.assertEqual(iface_A.queryTaggedValue('tag'), 'C') # Of course setting something lower overrides it. assert iface_A.__bases__[0].__name__ == 'B' iface_A.__bases__[0].setTaggedValue('tag', 'B') self.assertEqual(iface_A.getTaggedValue('tag'), 'B') def test_getDirectTaggedValue_ignores__iro__(self): # https://github.com/zopefoundation/zope.interface/issues/190 from zope.interface import Interface A = self._make_taggedValue_tree(Interface) self.assertIsNone(A.queryDirectTaggedValue('tag')) self.assertEqual([], list(A.getDirectTaggedValueTags())) with self.assertRaises(KeyError): A.getDirectTaggedValue('tag') A.setTaggedValue('tag', 'A') self.assertEqual(A.queryDirectTaggedValue('tag'), 'A') self.assertEqual(A.getDirectTaggedValue('tag'), 'A') self.assertEqual(['tag'], list(A.getDirectTaggedValueTags())) assert A.__bases__[1].__name__ == 'C' C = A.__bases__[1] self.assertEqual(C.queryDirectTaggedValue('tag'), 'C') self.assertEqual(C.getDirectTaggedValue('tag'), 'C') self.assertEqual(['tag'], list(C.getDirectTaggedValueTags())) def test_description_cache_management(self): # See https://bugs.launchpad.net/zope.interface/+bug/185974 # There was a bug where the cache used by Specification.get() was not # cleared when the bases were changed. from zope.interface import Attribute from zope.interface import Interface class I1(Interface): a = Attribute('a') class I2(I1): pass class I3(I2): pass self.assertTrue(I3.get('a') is I1.get('a')) I2.__bases__ = (Interface,) self.assertTrue(I3.get('a') is None) def test___call___defers_to___conform___(self): from zope.interface import Interface from zope.interface import implementer class I(Interface): pass @implementer(I) class C: def __conform__(self, proto): return 0 self.assertEqual(I(C()), 0) def test___call___object_implements(self): from zope.interface import Interface from zope.interface import implementer class I(Interface): pass @implementer(I) class C: pass c = C() self.assertTrue(I(c) is c) def test___call___miss_wo_alternate(self): from zope.interface import Interface class I(Interface): pass class C: pass c = C() self.assertRaises(TypeError, I, c) def test___call___miss_w_alternate(self): from zope.interface import Interface class I(Interface): pass class C: pass c = C() self.assertTrue(I(c, self) is self) def test___call___w_adapter_hook(self): from zope.interface import Interface from zope.interface.interface import adapter_hooks def _miss(iface, obj): pass def _hit(iface, obj): return self class I(Interface): pass class C: pass c = C() old_adapter_hooks = adapter_hooks[:] adapter_hooks[:] = [_miss, _hit] try: self.assertTrue(I(c) is self) finally: adapter_hooks[:] = old_adapter_hooks def test___call___w_overridden_adapt(self): from zope.interface import Interface from zope.interface import implementer from zope.interface import interfacemethod class I(Interface): @interfacemethod def __adapt__(self, obj): return 42 @implementer(I) class O: pass self.assertEqual(42, I(object())) # __adapt__ can ignore the fact that the object provides # the interface if it chooses. self.assertEqual(42, I(O())) def test___call___w_overridden_adapt_and_conform(self): # Conform is first, taking precedence over __adapt__, # *if* it returns non-None from zope.interface import Interface from zope.interface import implementer from zope.interface import interfacemethod class IAdapt(Interface): @interfacemethod def __adapt__(self, obj): return 42 class ISimple(Interface): """Nothing special.""" @implementer(IAdapt) class Conform24: def __conform__(self, iface): return 24 @implementer(IAdapt) class ConformNone: def __conform__(self, iface): return None self.assertEqual(42, IAdapt(object())) self.assertEqual(24, ISimple(Conform24())) self.assertEqual(24, IAdapt(Conform24())) with self.assertRaises(TypeError): ISimple(ConformNone()) self.assertEqual(42, IAdapt(ConformNone())) def test___call___w_overridden_adapt_call_super(self): import sys from zope.interface import Interface from zope.interface import implementer from zope.interface import interfacemethod class I(Interface): @interfacemethod def __adapt__(self, obj): if not self.providedBy(obj): return 42 return super().__adapt__(obj) @implementer(I) class O: pass self.assertEqual(42, I(object())) o = O() self.assertIs(o, I(o)) def test___adapt___as_method_and_implementation(self): from zope.interface import Interface from zope.interface import interfacemethod class I(Interface): @interfacemethod def __adapt__(self, obj): return 42 def __adapt__(to_adapt): "This is a protocol" self.assertEqual(42, I(object())) self.assertEqual(I['__adapt__'].getSignatureString(), '(to_adapt)') def test___adapt__inheritance_and_type(self): from zope.interface import Interface from zope.interface import interfacemethod class IRoot(Interface): """Root""" class IWithAdapt(IRoot): @interfacemethod def __adapt__(self, obj): return 42 class IOther(IRoot): """Second branch""" class IUnrelated(Interface): """Unrelated""" class IDerivedAdapt(IUnrelated, IWithAdapt, IOther): """Inherits an adapt""" # Order of "inheritance" matters here. class IDerived2Adapt(IDerivedAdapt): """Overrides an inherited custom adapt.""" @interfacemethod def __adapt__(self, obj): return 24 self.assertEqual(42, IDerivedAdapt(object())) for iface in IRoot, IWithAdapt, IOther, IUnrelated, IDerivedAdapt: self.assertEqual(__name__, iface.__module__) for iface in IRoot, IOther, IUnrelated: self.assertEqual(type(IRoot), type(Interface)) # But things that implemented __adapt__ got a new type self.assertNotEqual(type(Interface), type(IWithAdapt)) self.assertEqual(type(IWithAdapt), type(IDerivedAdapt)) self.assertIsInstance(IWithAdapt, type(Interface)) self.assertEqual(24, IDerived2Adapt(object())) self.assertNotEqual(type(IDerived2Adapt), type(IDerivedAdapt)) self.assertIsInstance(IDerived2Adapt, type(IDerivedAdapt)) def test_interfacemethod_is_general(self): from zope.interface import Interface from zope.interface import interfacemethod class I(Interface): @interfacemethod def __call__(self, obj): """Replace an existing method""" return 42 @interfacemethod def this_is_new(self): return 42 self.assertEqual(I(self), 42) self.assertEqual(I.this_is_new(), 42) class AttributeTests(ElementTests): DEFAULT_NAME = 'TestAttribute' def _getTargetClass(self): from zope.interface.interface import Attribute return Attribute def test__repr__w_interface(self): method = self._makeOne() method.interface = type(self) r = repr(method) self.assertTrue(r.startswith(''), r) def test__repr__wo_interface(self): method = self._makeOne() r = repr(method) self.assertTrue(r.startswith(''), r) def test__str__w_interface(self): method = self._makeOne() method.interface = type(self) r = str(method) self.assertEqual(r, __name__ + '.AttributeTests.TestAttribute') def test__str__wo_interface(self): method = self._makeOne() r = str(method) self.assertEqual(r, 'TestAttribute') class MethodTests(AttributeTests): DEFAULT_NAME = 'TestMethod' def _getTargetClass(self): from zope.interface.interface import Method return Method def test_optional_as_property(self): method = self._makeOne() self.assertEqual(method.optional, {}) method.optional = {'foo': 'bar'} self.assertEqual(method.optional, {'foo': 'bar'}) del method.optional self.assertEqual(method.optional, {}) def test___call___raises_BrokenImplementation(self): from zope.interface.exceptions import BrokenImplementation method = self._makeOne() try: method() except BrokenImplementation as e: self.assertEqual(e.interface, None) self.assertEqual(e.name, self.DEFAULT_NAME) else: self.fail('__call__ should raise BrokenImplementation') def test_getSignatureInfo_bare(self): method = self._makeOne() info = method.getSignatureInfo() self.assertEqual(list(info['positional']), []) self.assertEqual(list(info['required']), []) self.assertEqual(info['optional'], {}) self.assertEqual(info['varargs'], None) self.assertEqual(info['kwargs'], None) def test_getSignatureString_bare(self): method = self._makeOne() self.assertEqual(method.getSignatureString(), '()') def test_getSignatureString_w_only_required(self): method = self._makeOne() method.positional = method.required = ['foo'] self.assertEqual(method.getSignatureString(), '(foo)') def test_getSignatureString_w_optional(self): method = self._makeOne() method.positional = method.required = ['foo'] method.optional = {'foo': 'bar'} self.assertEqual(method.getSignatureString(), "(foo='bar')") def test_getSignatureString_w_varargs(self): method = self._makeOne() method.varargs = 'args' self.assertEqual(method.getSignatureString(), "(*args)") def test_getSignatureString_w_kwargs(self): method = self._makeOne() method.kwargs = 'kw' self.assertEqual(method.getSignatureString(), "(**kw)") def test__repr__w_interface(self): method = self._makeOne() method.kwargs = 'kw' method.interface = type(self) r = repr(method) self.assertTrue(r.startswith(''), r) def test__repr__wo_interface(self): method = self._makeOne() method.kwargs = 'kw' r = repr(method) self.assertTrue(r.startswith(''), r) def test__str__w_interface(self): method = self._makeOne() method.kwargs = 'kw' method.interface = type(self) r = str(method) self.assertEqual(r, __name__ + '.MethodTests.TestMethod(**kw)') def test__str__wo_interface(self): method = self._makeOne() method.kwargs = 'kw' r = str(method) self.assertEqual(r, 'TestMethod(**kw)') class Test_fromFunction(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.interface.interface import fromFunction return fromFunction(*args, **kw) def test_bare(self): def _func(): "DOCSTRING" method = self._callFUT(_func) self.assertEqual(method.getName(), '_func') self.assertEqual(method.getDoc(), 'DOCSTRING') self.assertEqual(method.interface, None) self.assertEqual(list(method.getTaggedValueTags()), []) info = method.getSignatureInfo() self.assertEqual(list(info['positional']), []) self.assertEqual(list(info['required']), []) self.assertEqual(info['optional'], {}) self.assertEqual(info['varargs'], None) self.assertEqual(info['kwargs'], None) def test_w_interface(self): from zope.interface.interface import InterfaceClass class IFoo(InterfaceClass): pass def _func(): "DOCSTRING" method = self._callFUT(_func, interface=IFoo) self.assertEqual(method.interface, IFoo) def test_w_name(self): def _func(): "DOCSTRING" method = self._callFUT(_func, name='anotherName') self.assertEqual(method.getName(), 'anotherName') def test_w_only_required(self): def _func(foo): "DOCSTRING" method = self._callFUT(_func) info = method.getSignatureInfo() self.assertEqual(list(info['positional']), ['foo']) self.assertEqual(list(info['required']), ['foo']) self.assertEqual(info['optional'], {}) self.assertEqual(info['varargs'], None) self.assertEqual(info['kwargs'], None) def test_w_optional(self): def _func(foo='bar'): "DOCSTRING" method = self._callFUT(_func) info = method.getSignatureInfo() self.assertEqual(list(info['positional']), ['foo']) self.assertEqual(list(info['required']), []) self.assertEqual(info['optional'], {'foo': 'bar'}) self.assertEqual(info['varargs'], None) self.assertEqual(info['kwargs'], None) def test_w_optional_self(self): # This is a weird case, trying to cover the following code in # FUT:: # # nr = na-len(defaults) # if nr < 0: # defaults=defaults[-nr:] # nr = 0 def _func(self='bar'): "DOCSTRING" method = self._callFUT(_func, imlevel=1) info = method.getSignatureInfo() self.assertEqual(list(info['positional']), []) self.assertEqual(list(info['required']), []) self.assertEqual(info['optional'], {}) self.assertEqual(info['varargs'], None) self.assertEqual(info['kwargs'], None) def test_w_varargs(self): def _func(*args): "DOCSTRING" method = self._callFUT(_func) info = method.getSignatureInfo() self.assertEqual(list(info['positional']), []) self.assertEqual(list(info['required']), []) self.assertEqual(info['optional'], {}) self.assertEqual(info['varargs'], 'args') self.assertEqual(info['kwargs'], None) def test_w_kwargs(self): def _func(**kw): "DOCSTRING" method = self._callFUT(_func) info = method.getSignatureInfo() self.assertEqual(list(info['positional']), []) self.assertEqual(list(info['required']), []) self.assertEqual(info['optional'], {}) self.assertEqual(info['varargs'], None) self.assertEqual(info['kwargs'], 'kw') def test_full_spectrum(self): def _func(foo, bar='baz', *args, **kw): # pylint:disable=keyword-arg-before-vararg "DOCSTRING" method = self._callFUT(_func) info = method.getSignatureInfo() self.assertEqual(list(info['positional']), ['foo', 'bar']) self.assertEqual(list(info['required']), ['foo']) self.assertEqual(info['optional'], {'bar': 'baz'}) self.assertEqual(info['varargs'], 'args') self.assertEqual(info['kwargs'], 'kw') class Test_fromMethod(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.interface.interface import fromMethod return fromMethod(*args, **kw) def test_no_args(self): class Foo: def bar(self): "DOCSTRING" method = self._callFUT(Foo.bar) self.assertEqual(method.getName(), 'bar') self.assertEqual(method.getDoc(), 'DOCSTRING') self.assertEqual(method.interface, None) self.assertEqual(list(method.getTaggedValueTags()), []) info = method.getSignatureInfo() self.assertEqual(list(info['positional']), []) self.assertEqual(list(info['required']), []) self.assertEqual(info['optional'], {}) self.assertEqual(info['varargs'], None) self.assertEqual(info['kwargs'], None) def test_full_spectrum(self): class Foo: def bar(self, foo, bar='baz', *args, **kw): # pylint:disable=keyword-arg-before-vararg "DOCSTRING" method = self._callFUT(Foo.bar) info = method.getSignatureInfo() self.assertEqual(list(info['positional']), ['foo', 'bar']) self.assertEqual(list(info['required']), ['foo']) self.assertEqual(info['optional'], {'bar': 'baz'}) self.assertEqual(info['varargs'], 'args') self.assertEqual(info['kwargs'], 'kw') def test_w_non_method(self): def foo(): "DOCSTRING" method = self._callFUT(foo) self.assertEqual(method.getName(), 'foo') self.assertEqual(method.getDoc(), 'DOCSTRING') self.assertEqual(method.interface, None) self.assertEqual(list(method.getTaggedValueTags()), []) info = method.getSignatureInfo() self.assertEqual(list(info['positional']), []) self.assertEqual(list(info['required']), []) self.assertEqual(info['optional'], {}) self.assertEqual(info['varargs'], None) self.assertEqual(info['kwargs'], None) class DummyDependent: def __init__(self): self._changed = [] def changed(self, originally_changed): self._changed.append(originally_changed) def _barGreaterThanFoo(obj): from zope.interface.exceptions import Invalid foo = getattr(obj, 'foo', None) bar = getattr(obj, 'bar', None) if foo is not None and isinstance(foo, type(bar)): # type checking should be handled elsewhere (like, say, # schema); these invariants should be intra-interface # constraints. This is a hacky way to do it, maybe, but you # get the idea if not bar > foo: raise Invalid('Please, Boo MUST be greater than Foo!') def _ifFooThenBar(obj): from zope.interface.exceptions import Invalid if getattr(obj, 'foo', None) and not getattr(obj, 'bar', None): raise Invalid('If Foo, then Bar!') class _Monkey: # context-manager for replacing module names in the scope of a test. def __init__(self, module, **kw): self.module = module self.to_restore = {key: getattr(module, key) for key in kw} for key, value in kw.items(): setattr(module, key, value) def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): for key, value in self.to_restore.items(): setattr(self.module, key, value) class TestTypeAnnotations(unittest.TestCase): """Test using Interfaces in type annotations.""" def test___or__(self): from typing import Optional from typing import Union from zope.interface import Interface class I1(Interface): pass class I2(Interface): pass class B: a: I1|None b: I1|I2 self.assertEqual( B.__annotations__, {'a': Optional[I1], 'b': Union[I1, I2]}) def test___ror__(self): from typing import Optional from typing import Union from zope.interface import Interface class I1(Interface): pass class A: pass class B: a: None|I1 b: A|I1 self.assertEqual( B.__annotations__, {'a': Optional[I1], 'b': Union[A, I1]}) zope.interface-6.4/src/zope/interface/tests/test_interfaces.py000066400000000000000000000104311462121350100246730ustar00rootroot00000000000000import unittest class _ConformsToIObjectEvent: def _makeOne(self, target=None): if target is None: target = object() return self._getTargetClass()(target) def test_class_conforms_to_IObjectEvent(self): from zope.interface.interfaces import IObjectEvent from zope.interface.verify import verifyClass verifyClass(IObjectEvent, self._getTargetClass()) def test_instance_conforms_to_IObjectEvent(self): from zope.interface.interfaces import IObjectEvent from zope.interface.verify import verifyObject verifyObject(IObjectEvent, self._makeOne()) class _ConformsToIRegistrationEvent(_ConformsToIObjectEvent): def test_class_conforms_to_IRegistrationEvent(self): from zope.interface.interfaces import IRegistrationEvent from zope.interface.verify import verifyClass verifyClass(IRegistrationEvent, self._getTargetClass()) def test_instance_conforms_to_IRegistrationEvent(self): from zope.interface.interfaces import IRegistrationEvent from zope.interface.verify import verifyObject verifyObject(IRegistrationEvent, self._makeOne()) class ObjectEventTests(unittest.TestCase, _ConformsToIObjectEvent): def _getTargetClass(self): from zope.interface.interfaces import ObjectEvent return ObjectEvent def test_ctor(self): target = object() event = self._makeOne(target) self.assertTrue(event.object is target) class RegistrationEventTests(unittest.TestCase, _ConformsToIRegistrationEvent): def _getTargetClass(self): from zope.interface.interfaces import RegistrationEvent return RegistrationEvent def test___repr__(self): target = object() event = self._makeOne(target) r = repr(event) self.assertEqual(r.splitlines(), ['RegistrationEvent event:', repr(target)]) class RegisteredTests(unittest.TestCase, _ConformsToIRegistrationEvent): def _getTargetClass(self): from zope.interface.interfaces import Registered return Registered def test_class_conforms_to_IRegistered(self): from zope.interface.interfaces import IRegistered from zope.interface.verify import verifyClass verifyClass(IRegistered, self._getTargetClass()) def test_instance_conforms_to_IRegistered(self): from zope.interface.interfaces import IRegistered from zope.interface.verify import verifyObject verifyObject(IRegistered, self._makeOne()) class UnregisteredTests(unittest.TestCase, _ConformsToIRegistrationEvent): def _getTargetClass(self): from zope.interface.interfaces import Unregistered return Unregistered def test_class_conforms_to_IUnregistered(self): from zope.interface.interfaces import IUnregistered from zope.interface.verify import verifyClass verifyClass(IUnregistered, self._getTargetClass()) def test_instance_conforms_to_IUnregistered(self): from zope.interface.interfaces import IUnregistered from zope.interface.verify import verifyObject verifyObject(IUnregistered, self._makeOne()) class InterfaceClassTests(unittest.TestCase): def _getTargetClass(self): from zope.interface.interface import InterfaceClass return InterfaceClass def _getTargetInterface(self): from zope.interface.interfaces import IInterface return IInterface def _makeOne(self): from zope.interface.interface import Interface return Interface def test_class_conforms(self): from zope.interface.verify import verifyClass verifyClass(self._getTargetInterface(), self._getTargetClass()) def test_instance_conforms(self): from zope.interface.verify import verifyObject verifyObject(self._getTargetInterface(), self._makeOne()) def test_instance_consistent__iro__(self): from zope.interface import ro self.assertTrue(ro.is_consistent(self._getTargetInterface())) def test_class_consistent__iro__(self): from zope.interface import implementedBy from zope.interface import ro self.assertTrue(ro.is_consistent(implementedBy(self._getTargetClass()))) zope.interface-6.4/src/zope/interface/tests/test_odd_declarations.py000066400000000000000000000166161462121350100260610ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2003 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. # ############################################################################## """Test interface declarations against ExtensionClass-like classes. These tests are to make sure we do something sane in the presence of classic ExtensionClass classes and instances. """ import unittest from zope.interface import Interface from zope.interface import classImplements from zope.interface import classImplementsOnly from zope.interface import directlyProvidedBy from zope.interface import directlyProvides from zope.interface import implementedBy from zope.interface import implementer from zope.interface import providedBy from zope.interface.tests import odd class I1(Interface): pass class I2(Interface): pass class I3(Interface): pass class I31(I3): pass class I4(Interface): pass class I5(Interface): pass class Odd: pass Odd = odd.MetaClass('Odd', Odd.__bases__, {}) class B(Odd): __implemented__ = I2 # TODO: We are going to need more magic to make classProvides work with odd # classes. This will work in the next iteration. For now, we'll use # a different mechanism. # from zope.interface import classProvides class A(Odd): pass classImplements(A, I1) class C(A, B): pass classImplements(C, I31) class Test(unittest.TestCase): def test_ObjectSpecification(self): c = C() directlyProvides(c, I4) self.assertEqual([i.getName() for i in providedBy(c)], ['I4', 'I31', 'I1', 'I2'] ) self.assertEqual([i.getName() for i in providedBy(c).flattened()], ['I4', 'I31', 'I3', 'I1', 'I2', 'Interface'] ) self.assertTrue(I1 in providedBy(c)) self.assertFalse(I3 in providedBy(c)) self.assertTrue(providedBy(c).extends(I3)) self.assertTrue(providedBy(c).extends(I31)) self.assertFalse(providedBy(c).extends(I5)) class COnly(A, B): pass classImplementsOnly(COnly, I31) class D(COnly): pass classImplements(D, I5) classImplements(D, I5) c = D() directlyProvides(c, I4) self.assertEqual([i.getName() for i in providedBy(c)], ['I4', 'I5', 'I31']) self.assertEqual([i.getName() for i in providedBy(c).flattened()], ['I4', 'I5', 'I31', 'I3', 'Interface']) self.assertFalse(I1 in providedBy(c)) self.assertFalse(I3 in providedBy(c)) self.assertTrue(providedBy(c).extends(I3)) self.assertFalse(providedBy(c).extends(I1)) self.assertTrue(providedBy(c).extends(I31)) self.assertTrue(providedBy(c).extends(I5)) class COnly(A, B): __implemented__ = I31 class D(COnly): pass classImplements(D, I5) classImplements(D, I5) c = D() directlyProvides(c, I4) self.assertEqual([i.getName() for i in providedBy(c)], ['I4', 'I5', 'I31']) self.assertEqual([i.getName() for i in providedBy(c).flattened()], ['I4', 'I5', 'I31', 'I3', 'Interface']) self.assertFalse(I1 in providedBy(c)) self.assertFalse(I3 in providedBy(c)) self.assertTrue(providedBy(c).extends(I3)) self.assertFalse(providedBy(c).extends(I1)) self.assertTrue(providedBy(c).extends(I31)) self.assertTrue(providedBy(c).extends(I5)) def test_classImplements(self): @implementer(I3) class A(Odd): pass @implementer(I4) class B(Odd): pass class C(A, B): pass classImplements(C, I1, I2) self.assertEqual([i.getName() for i in implementedBy(C)], ['I1', 'I2', 'I3', 'I4']) classImplements(C, I5) self.assertEqual([i.getName() for i in implementedBy(C)], ['I1', 'I2', 'I5', 'I3', 'I4']) def test_classImplementsOnly(self): @implementer(I3) class A(Odd): pass @implementer(I4) class B(Odd): pass class C(A, B): pass classImplementsOnly(C, I1, I2) self.assertEqual([i.__name__ for i in implementedBy(C)], ['I1', 'I2']) def test_directlyProvides(self): class IA1(Interface): pass class IA2(Interface): pass class IB(Interface): pass class IC(Interface): pass class A(Odd): pass classImplements(A, IA1, IA2) class B(Odd): pass classImplements(B, IB) class C(A, B): pass classImplements(C, IC) ob = C() directlyProvides(ob, I1, I2) self.assertTrue(I1 in providedBy(ob)) self.assertTrue(I2 in providedBy(ob)) self.assertTrue(IA1 in providedBy(ob)) self.assertTrue(IA2 in providedBy(ob)) self.assertTrue(IB in providedBy(ob)) self.assertTrue(IC in providedBy(ob)) directlyProvides(ob, directlyProvidedBy(ob)-I2) self.assertTrue(I1 in providedBy(ob)) self.assertFalse(I2 in providedBy(ob)) self.assertFalse(I2 in providedBy(ob)) directlyProvides(ob, directlyProvidedBy(ob), I2) self.assertTrue(I2 in providedBy(ob)) # see above #def TODO_test_classProvides_fails_for_odd_class(self): # try: # class A(Odd): # classProvides(I1) # except TypeError: # pass # Success # self.assert_(False, # "Shouldn't be able to use directlyProvides on odd class." # ) def test_implementedBy(self): class I2(I1): pass class C1(Odd): pass classImplements(C1, I2) class C2(C1): pass classImplements(C2, I3) self.assertEqual([i.getName() for i in implementedBy(C2)], ['I3', 'I2']) def test_odd_metaclass_that_doesnt_subclass_type(self): # This was originally a doctest in odd.py. # It verifies that the metaclass the rest of these tests use # works as expected. # This is used for testing support for ExtensionClass in new interfaces. class A: a = 1 A = odd.MetaClass('A', A.__bases__, A.__dict__) class B: b = 1 B = odd.MetaClass('B', B.__bases__, B.__dict__) class C(A, B): pass self.assertEqual(C.__bases__, (A, B)) a = A() aa = A() self.assertEqual(a.a, 1) self.assertEqual(aa.a, 1) aa.a = 2 self.assertEqual(a.a, 1) self.assertEqual(aa.a, 2) c = C() self.assertEqual(c.a, 1) self.assertEqual(c.b, 1) c.b = 2 self.assertEqual(c.b, 2) C.c = 1 self.assertEqual(c.c, 1) c.c self.assertIs(C.__class__.__class__, C.__class__) zope.interface-6.4/src/zope/interface/tests/test_registry.py000066400000000000000000003344161462121350100244340ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2001, 2002, 2009 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. # ############################################################################## """Component Registry Tests""" # pylint:disable=protected-access import unittest from zope.interface import Interface from zope.interface.adapter import VerifyingAdapterRegistry from zope.interface.registry import Components class ComponentsTests(unittest.TestCase): def _getTargetClass(self): return Components def _makeOne(self, name='test', *args, **kw): return self._getTargetClass()(name, *args, **kw) def _wrapEvents(self): from zope.interface import registry _events = [] def _notify(*args, **kw): _events.append((args, kw)) _monkey = _Monkey(registry, notify=_notify) return _monkey, _events def test_ctor_no_bases(self): from zope.interface.adapter import AdapterRegistry comp = self._makeOne('testing') self.assertEqual(comp.__name__, 'testing') self.assertEqual(comp.__bases__, ()) self.assertTrue(isinstance(comp.adapters, AdapterRegistry)) self.assertTrue(isinstance(comp.utilities, AdapterRegistry)) self.assertEqual(comp.adapters.__bases__, ()) self.assertEqual(comp.utilities.__bases__, ()) self.assertEqual(comp._utility_registrations, {}) self.assertEqual(comp._adapter_registrations, {}) self.assertEqual(comp._subscription_registrations, []) self.assertEqual(comp._handler_registrations, []) def test_ctor_w_base(self): base = self._makeOne('base') comp = self._makeOne('testing', (base,)) self.assertEqual(comp.__name__, 'testing') self.assertEqual(comp.__bases__, (base,)) self.assertEqual(comp.adapters.__bases__, (base.adapters,)) self.assertEqual(comp.utilities.__bases__, (base.utilities,)) def test___repr__(self): comp = self._makeOne('testing') self.assertEqual(repr(comp), '') # test _init_registries / _init_registrations via only caller, __init__. def test_assign_to___bases__(self): base1 = self._makeOne('base1') base2 = self._makeOne('base2') comp = self._makeOne() comp.__bases__ = (base1, base2) self.assertEqual(comp.__bases__, (base1, base2)) self.assertEqual(comp.adapters.__bases__, (base1.adapters, base2.adapters)) self.assertEqual(comp.utilities.__bases__, (base1.utilities, base2.utilities)) def test_registerUtility_with_component_name(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import named class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') @named('foo') class Foo: pass foo = Foo() _info = 'info' comp = self._makeOne() comp.registerUtility(foo, ifoo, info=_info) self.assertEqual( comp._utility_registrations[ifoo, 'foo'], (foo, _info, None)) def test_registerUtility_both_factory_and_component(self): def _factory(): raise NotImplementedError() _to_reg = object() comp = self._makeOne() self.assertRaises(TypeError, comp.registerUtility, component=_to_reg, factory=_factory) def test_registerUtility_w_component(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Registered from zope.interface.registry import UtilityRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name = 'name' _to_reg = object() comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: comp.registerUtility(_to_reg, ifoo, _name, _info) self.assertTrue(comp.utilities._adapters[0][ifoo][_name] is _to_reg) self.assertEqual(comp._utility_registrations[ifoo, _name], (_to_reg, _info, None)) self.assertEqual(comp.utilities._subscribers[0][ifoo][''], (_to_reg,)) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Registered)) self.assertTrue(isinstance(event.object, UtilityRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertTrue(event.object.name is _name) self.assertTrue(event.object.component is _to_reg) self.assertTrue(event.object.info is _info) self.assertTrue(event.object.factory is None) def test_registerUtility_w_factory(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Registered from zope.interface.registry import UtilityRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name = 'name' _to_reg = object() def _factory(): return _to_reg comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: comp.registerUtility(None, ifoo, _name, _info, factory=_factory) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Registered)) self.assertTrue(isinstance(event.object, UtilityRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertTrue(event.object.name is _name) self.assertTrue(event.object.component is _to_reg) self.assertTrue(event.object.info is _info) self.assertTrue(event.object.factory is _factory) def test_registerUtility_no_provided_available(self): class Foo: pass _info = 'info' _name = 'name' _to_reg = Foo() comp = self._makeOne() self.assertRaises(TypeError, comp.registerUtility, _to_reg, None, _name, _info) def test_registerUtility_wo_provided(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import directlyProvides from zope.interface.interfaces import Registered from zope.interface.registry import UtilityRegistration class IFoo(InterfaceClass): pass class Foo: pass ifoo = IFoo('IFoo') _info = 'info' _name = 'name' _to_reg = Foo() directlyProvides(_to_reg, ifoo) comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: comp.registerUtility(_to_reg, None, _name, _info) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Registered)) self.assertTrue(isinstance(event.object, UtilityRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertTrue(event.object.name is _name) self.assertTrue(event.object.component is _to_reg) self.assertTrue(event.object.info is _info) self.assertTrue(event.object.factory is None) def test_registerUtility_duplicates_existing_reg(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name = 'name' _to_reg = object() comp = self._makeOne() comp.registerUtility(_to_reg, ifoo, _name, _info) _monkey, _events = self._wrapEvents() with _monkey: comp.registerUtility(_to_reg, ifoo, _name, _info) self.assertEqual(len(_events), 0) def test_registerUtility_w_different_info(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info1 = 'info1' _info2 = 'info2' _name = 'name' _to_reg = object() comp = self._makeOne() comp.registerUtility(_to_reg, ifoo, _name, _info1) _monkey, _events = self._wrapEvents() with _monkey: comp.registerUtility(_to_reg, ifoo, _name, _info2) self.assertEqual(len(_events), 2) # unreg, reg self.assertEqual(comp._utility_registrations[(ifoo, _name)], (_to_reg, _info2, None)) # replaced self.assertEqual(comp.utilities._subscribers[0][ifoo][''], (_to_reg,)) def test_registerUtility_w_different_names_same_component(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name1 = 'name1' _name2 = 'name2' _other_reg = object() _to_reg = object() comp = self._makeOne() comp.registerUtility(_other_reg, ifoo, _name1, _info) _monkey, _events = self._wrapEvents() with _monkey: comp.registerUtility(_to_reg, ifoo, _name2, _info) self.assertEqual(len(_events), 1) # reg self.assertEqual(comp._utility_registrations[(ifoo, _name1)], (_other_reg, _info, None)) self.assertEqual(comp._utility_registrations[(ifoo, _name2)], (_to_reg, _info, None)) self.assertEqual(comp.utilities._subscribers[0][ifoo][''], (_other_reg, _to_reg,)) def test_registerUtility_replaces_existing_reg(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Registered from zope.interface.interfaces import Unregistered from zope.interface.registry import UtilityRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name = 'name' _before, _after = object(), object() comp = self._makeOne() comp.registerUtility(_before, ifoo, _name, _info) _monkey, _events = self._wrapEvents() with _monkey: comp.registerUtility(_after, ifoo, _name, _info) self.assertEqual(len(_events), 2) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Unregistered)) self.assertTrue(isinstance(event.object, UtilityRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertTrue(event.object.name is _name) self.assertTrue(event.object.component is _before) self.assertTrue(event.object.info is _info) self.assertTrue(event.object.factory is None) args, kw = _events[1] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Registered)) self.assertTrue(isinstance(event.object, UtilityRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertTrue(event.object.name is _name) self.assertTrue(event.object.component is _after) self.assertTrue(event.object.info is _info) self.assertTrue(event.object.factory is None) def test_registerUtility_w_existing_subscr(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name1 = 'name1' _name2 = 'name2' _to_reg = object() comp = self._makeOne() comp.registerUtility(_to_reg, ifoo, _name1, _info) _monkey, _events = self._wrapEvents() with _monkey: comp.registerUtility(_to_reg, ifoo, _name2, _info) self.assertEqual(comp.utilities._subscribers[0][ifoo][''], (_to_reg,)) def test_registerUtility_wo_event(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name = 'name' _to_reg = object() comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: comp.registerUtility(_to_reg, ifoo, _name, _info, False) self.assertEqual(len(_events), 0) def test_registerUtility_changes_object_identity_after(self): # If a subclass changes the identity of the _utility_registrations, # the cache is updated and the right thing still happens. class CompThatChangesAfter1Reg(self._getTargetClass()): reg_count = 0 def registerUtility(self, *args): self.reg_count += 1 super().registerUtility(*args) if self.reg_count == 1: self._utility_registrations = dict(self._utility_registrations) comp = CompThatChangesAfter1Reg() comp.registerUtility(object(), Interface) self.assertEqual(len(list(comp.registeredUtilities())), 1) class IFoo(Interface): pass comp.registerUtility(object(), IFoo) self.assertEqual(len(list(comp.registeredUtilities())), 2) def test_registerUtility_changes_object_identity_before(self): # If a subclass changes the identity of the _utility_registrations, # the cache is updated and the right thing still happens. class CompThatChangesAfter2Reg(self._getTargetClass()): reg_count = 0 def registerUtility(self, *args): self.reg_count += 1 if self.reg_count == 2: self._utility_registrations = dict(self._utility_registrations) super().registerUtility(*args) comp = CompThatChangesAfter2Reg() comp.registerUtility(object(), Interface) self.assertEqual(len(list(comp.registeredUtilities())), 1) class IFoo(Interface): pass comp.registerUtility(object(), IFoo) self.assertEqual(len(list(comp.registeredUtilities())), 2) class IBar(Interface): pass comp.registerUtility(object(), IBar) self.assertEqual(len(list(comp.registeredUtilities())), 3) def test_unregisterUtility_neither_factory_nor_component_nor_provided(self): comp = self._makeOne() self.assertRaises(TypeError, comp.unregisterUtility, component=None, provided=None, factory=None) def test_unregisterUtility_both_factory_and_component(self): def _factory(): raise NotImplementedError() _to_reg = object() comp = self._makeOne() self.assertRaises(TypeError, comp.unregisterUtility, component=_to_reg, factory=_factory) def test_unregisterUtility_w_component_miss(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _name = 'name' _to_reg = object() comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: unreg = comp.unregisterUtility(_to_reg, ifoo, _name) self.assertFalse(unreg) self.assertFalse(_events) def test_unregisterUtility_w_component(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Unregistered from zope.interface.registry import UtilityRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _name = 'name' _to_reg = object() comp = self._makeOne() comp.registerUtility(_to_reg, ifoo, _name) _monkey, _events = self._wrapEvents() with _monkey: unreg = comp.unregisterUtility(_to_reg, ifoo, _name) self.assertTrue(unreg) self.assertFalse(comp.utilities._adapters) # all erased self.assertFalse((ifoo, _name) in comp._utility_registrations) self.assertFalse(comp.utilities._subscribers) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Unregistered)) self.assertTrue(isinstance(event.object, UtilityRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertTrue(event.object.name is _name) self.assertTrue(event.object.component is _to_reg) self.assertTrue(event.object.factory is None) def test_unregisterUtility_w_factory(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Unregistered from zope.interface.registry import UtilityRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name = 'name' _to_reg = object() def _factory(): return _to_reg comp = self._makeOne() comp.registerUtility(None, ifoo, _name, _info, factory=_factory) _monkey, _events = self._wrapEvents() with _monkey: unreg = comp.unregisterUtility(None, ifoo, _name, factory=_factory) self.assertTrue(unreg) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Unregistered)) self.assertTrue(isinstance(event.object, UtilityRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertTrue(event.object.name is _name) self.assertTrue(event.object.component is _to_reg) self.assertTrue(event.object.factory is _factory) def test_unregisterUtility_wo_explicit_provided(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import directlyProvides from zope.interface.interfaces import Unregistered from zope.interface.registry import UtilityRegistration class IFoo(InterfaceClass): pass class Foo: pass ifoo = IFoo('IFoo') _info = 'info' _name = 'name' _to_reg = Foo() directlyProvides(_to_reg, ifoo) comp = self._makeOne() comp.registerUtility(_to_reg, ifoo, _name, _info) _monkey, _events = self._wrapEvents() with _monkey: unreg = comp.unregisterUtility(_to_reg, None, _name) self.assertTrue(unreg) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Unregistered)) self.assertTrue(isinstance(event.object, UtilityRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertTrue(event.object.name is _name) self.assertTrue(event.object.component is _to_reg) self.assertTrue(event.object.info is _info) self.assertTrue(event.object.factory is None) def test_unregisterUtility_wo_component_or_factory(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import directlyProvides from zope.interface.interfaces import Unregistered from zope.interface.registry import UtilityRegistration class IFoo(InterfaceClass): pass class Foo: pass ifoo = IFoo('IFoo') _info = 'info' _name = 'name' _to_reg = Foo() directlyProvides(_to_reg, ifoo) comp = self._makeOne() comp.registerUtility(_to_reg, ifoo, _name, _info) _monkey, _events = self._wrapEvents() with _monkey: # Just pass the interface / name unreg = comp.unregisterUtility(provided=ifoo, name=_name) self.assertTrue(unreg) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Unregistered)) self.assertTrue(isinstance(event.object, UtilityRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertTrue(event.object.name is _name) self.assertTrue(event.object.component is _to_reg) self.assertTrue(event.object.info is _info) self.assertTrue(event.object.factory is None) def test_unregisterUtility_w_existing_subscr(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name1 = 'name1' _name2 = 'name2' _to_reg = object() comp = self._makeOne() comp.registerUtility(_to_reg, ifoo, _name1, _info) comp.registerUtility(_to_reg, ifoo, _name2, _info) _monkey, _events = self._wrapEvents() with _monkey: comp.unregisterUtility(_to_reg, ifoo, _name2) self.assertEqual(comp.utilities._subscribers[0][ifoo][''], (_to_reg,)) def test_unregisterUtility_w_existing_subscr_non_hashable(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name1 = 'name1' _name2 = 'name2' _to_reg = dict() comp = self._makeOne() comp.registerUtility(_to_reg, ifoo, _name1, _info) comp.registerUtility(_to_reg, ifoo, _name2, _info) _monkey, _events = self._wrapEvents() with _monkey: comp.unregisterUtility(_to_reg, ifoo, _name2) self.assertEqual(comp.utilities._subscribers[0][ifoo][''], (_to_reg,)) def test_unregisterUtility_w_existing_subscr_non_hashable_fresh_cache(self): # We correctly populate the cache of registrations if it has gone away # (for example, the Components was unpickled) from zope.interface.declarations import InterfaceClass from zope.interface.registry import _UtilityRegistrations class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name1 = 'name1' _name2 = 'name2' _to_reg = dict() comp = self._makeOne() comp.registerUtility(_to_reg, ifoo, _name1, _info) comp.registerUtility(_to_reg, ifoo, _name2, _info) _monkey, _events = self._wrapEvents() with _monkey: comp.unregisterUtility(_to_reg, ifoo, _name2) self.assertEqual(comp.utilities._subscribers[0][ifoo][''], (_to_reg,)) def test_unregisterUtility_w_existing_subscr_non_hashable_reinitted(self): # We correctly populate the cache of registrations if the base objects change # out from under us from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name1 = 'name1' _name2 = 'name2' _to_reg = dict() comp = self._makeOne() comp.registerUtility(_to_reg, ifoo, _name1, _info) comp.registerUtility(_to_reg, ifoo, _name2, _info) # zope.component.testing does this comp.__init__('base') comp.registerUtility(_to_reg, ifoo, _name2, _info) _monkey, _events = self._wrapEvents() with _monkey: # Nothing to do, but we don't break either comp.unregisterUtility(_to_reg, ifoo, _name2) self.assertEqual(0, len(comp.utilities._subscribers)) def test_unregisterUtility_w_existing_subscr_other_component(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name1 = 'name1' _name2 = 'name2' _other_reg = object() _to_reg = object() comp = self._makeOne() comp.registerUtility(_other_reg, ifoo, _name1, _info) comp.registerUtility(_to_reg, ifoo, _name2, _info) _monkey, _events = self._wrapEvents() with _monkey: comp.unregisterUtility(_to_reg, ifoo, _name2) self.assertEqual(comp.utilities._subscribers[0][ifoo][''], (_other_reg,)) def test_unregisterUtility_w_existing_subscr_other_component_mixed_hash(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name1 = 'name1' _name2 = 'name2' # First register something hashable _other_reg = object() # Then it transfers to something unhashable _to_reg = dict() comp = self._makeOne() comp.registerUtility(_other_reg, ifoo, _name1, _info) comp.registerUtility(_to_reg, ifoo, _name2, _info) _monkey, _events = self._wrapEvents() with _monkey: comp.unregisterUtility(_to_reg, ifoo, _name2) self.assertEqual(comp.utilities._subscribers[0][ifoo][''], (_other_reg,)) def test_registeredUtilities_empty(self): comp = self._makeOne() self.assertEqual(list(comp.registeredUtilities()), []) def test_registeredUtilities_notempty(self): from zope.interface.declarations import InterfaceClass from zope.interface.registry import UtilityRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name1 = 'name1' _name2 = 'name2' _to_reg = object() comp = self._makeOne() comp.registerUtility(_to_reg, ifoo, _name1, _info) comp.registerUtility(_to_reg, ifoo, _name2, _info) reg = sorted(comp.registeredUtilities(), key=lambda r: r.name) self.assertEqual(len(reg), 2) self.assertTrue(isinstance(reg[0], UtilityRegistration)) self.assertTrue(reg[0].registry is comp) self.assertTrue(reg[0].provided is ifoo) self.assertTrue(reg[0].name is _name1) self.assertTrue(reg[0].component is _to_reg) self.assertTrue(reg[0].info is _info) self.assertTrue(reg[0].factory is None) self.assertTrue(isinstance(reg[1], UtilityRegistration)) self.assertTrue(reg[1].registry is comp) self.assertTrue(reg[1].provided is ifoo) self.assertTrue(reg[1].name is _name2) self.assertTrue(reg[1].component is _to_reg) self.assertTrue(reg[1].info is _info) self.assertTrue(reg[1].factory is None) def test_queryUtility_miss_no_default(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') comp = self._makeOne() self.assertTrue(comp.queryUtility(ifoo) is None) def test_queryUtility_miss_w_default(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') comp = self._makeOne() _default = object() self.assertTrue(comp.queryUtility(ifoo, default=_default) is _default) def test_queryUtility_hit(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _to_reg = object() comp = self._makeOne() comp.registerUtility(_to_reg, ifoo) self.assertTrue(comp.queryUtility(ifoo) is _to_reg) def test_getUtility_miss(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import ComponentLookupError class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') comp = self._makeOne() self.assertRaises(ComponentLookupError, comp.getUtility, ifoo) def test_getUtility_hit(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _to_reg = object() comp = self._makeOne() comp.registerUtility(_to_reg, ifoo) self.assertTrue(comp.getUtility(ifoo) is _to_reg) def test_getUtilitiesFor_miss(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') comp = self._makeOne() self.assertEqual(list(comp.getUtilitiesFor(ifoo)), []) def test_getUtilitiesFor_hit(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _name1 = 'name1' _name2 = 'name2' _to_reg = object() comp = self._makeOne() comp.registerUtility(_to_reg, ifoo, name=_name1) comp.registerUtility(_to_reg, ifoo, name=_name2) self.assertEqual(sorted(comp.getUtilitiesFor(ifoo)), [(_name1, _to_reg), (_name2, _to_reg)]) def test_getAllUtilitiesRegisteredFor_miss(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') comp = self._makeOne() self.assertEqual(list(comp.getAllUtilitiesRegisteredFor(ifoo)), []) def test_getAllUtilitiesRegisteredFor_hit(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _name1 = 'name1' _name2 = 'name2' _to_reg = object() comp = self._makeOne() comp.registerUtility(_to_reg, ifoo, name=_name1) comp.registerUtility(_to_reg, ifoo, name=_name2) self.assertEqual(list(comp.getAllUtilitiesRegisteredFor(ifoo)), [_to_reg]) def test_registerAdapter_with_component_name(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import named class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') @named('foo') class Foo: pass _info = 'info' comp = self._makeOne() comp.registerAdapter(Foo, (ibar,), ifoo, info=_info) self.assertEqual( comp._adapter_registrations[(ibar,), ifoo, 'foo'], (Foo, _info)) def test_registerAdapter_w_explicit_provided_and_required(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Registered from zope.interface.registry import AdapterRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') _info = 'info' _name = 'name' def _factory(context): raise NotImplementedError() comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: comp.registerAdapter(_factory, (ibar,), ifoo, _name, _info) self.assertTrue(comp.adapters._adapters[1][ibar][ifoo][_name] is _factory) self.assertEqual(comp._adapter_registrations[(ibar,), ifoo, _name], (_factory, _info)) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Registered)) self.assertTrue(isinstance(event.object, AdapterRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertEqual(event.object.required, (ibar,)) self.assertTrue(event.object.name is _name) self.assertTrue(event.object.info is _info) self.assertTrue(event.object.factory is _factory) def test_registerAdapter_no_provided_available(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ibar = IFoo('IBar') _info = 'info' _name = 'name' class _Factory: pass comp = self._makeOne() self.assertRaises(TypeError, comp.registerAdapter, _Factory, (ibar,), name=_name, info=_info) def test_registerAdapter_wo_explicit_provided(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer from zope.interface.interfaces import Registered from zope.interface.registry import AdapterRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') _info = 'info' _name = 'name' _to_reg = object() @implementer(ifoo) class _Factory: pass comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: comp.registerAdapter(_Factory, (ibar,), name=_name, info=_info) self.assertTrue(comp.adapters._adapters[1][ibar][ifoo][_name] is _Factory) self.assertEqual(comp._adapter_registrations[(ibar,), ifoo, _name], (_Factory, _info)) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Registered)) self.assertTrue(isinstance(event.object, AdapterRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertEqual(event.object.required, (ibar,)) self.assertTrue(event.object.name is _name) self.assertTrue(event.object.info is _info) self.assertTrue(event.object.factory is _Factory) def test_registerAdapter_no_required_available(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name = 'name' class _Factory: pass comp = self._makeOne() self.assertRaises(TypeError, comp.registerAdapter, _Factory, provided=ifoo, name=_name, info=_info) def test_registerAdapter_w_invalid_required(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') _info = 'info' _name = 'name' class _Factory: pass comp = self._makeOne() self.assertRaises(TypeError, comp.registerAdapter, _Factory, ibar, provided=ifoo, name=_name, info=_info) def test_registerAdapter_w_required_containing_None(self): from zope.interface.declarations import InterfaceClass from zope.interface.interface import Interface from zope.interface.interfaces import Registered from zope.interface.registry import AdapterRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name = 'name' class _Factory: pass comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: comp.registerAdapter(_Factory, [None], provided=ifoo, name=_name, info=_info) self.assertTrue(comp.adapters._adapters[1][Interface][ifoo][_name] is _Factory) self.assertEqual(comp._adapter_registrations[(Interface,), ifoo, _name], (_Factory, _info)) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Registered)) self.assertTrue(isinstance(event.object, AdapterRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertEqual(event.object.required, (Interface,)) self.assertTrue(event.object.name is _name) self.assertTrue(event.object.info is _info) self.assertTrue(event.object.factory is _Factory) def test_registerAdapter_w_required_containing_class(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementedBy from zope.interface.declarations import implementer from zope.interface.interfaces import Registered from zope.interface.registry import AdapterRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') _info = 'info' _name = 'name' class _Factory: pass @implementer(ibar) class _Context: pass _ctx_impl = implementedBy(_Context) comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: comp.registerAdapter(_Factory, [_Context], provided=ifoo, name=_name, info=_info) self.assertTrue(comp.adapters._adapters[1][_ctx_impl][ifoo][_name] is _Factory) self.assertEqual(comp._adapter_registrations[(_ctx_impl,), ifoo, _name], (_Factory, _info)) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Registered)) self.assertTrue(isinstance(event.object, AdapterRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertEqual(event.object.required, (_ctx_impl,)) self.assertTrue(event.object.name is _name) self.assertTrue(event.object.info is _info) self.assertTrue(event.object.factory is _Factory) def test_registerAdapter_w_required_containing_junk(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _name = 'name' class _Factory: pass comp = self._makeOne() self.assertRaises(TypeError, comp.registerAdapter, _Factory, [object()], provided=ifoo, name=_name, info=_info) def test_registerAdapter_wo_explicit_required(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Registered from zope.interface.registry import AdapterRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') _info = 'info' _name = 'name' class _Factory: __component_adapts__ = (ibar,) comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: comp.registerAdapter(_Factory, provided=ifoo, name=_name, info=_info) self.assertTrue(comp.adapters._adapters[1][ibar][ifoo][_name] is _Factory) self.assertEqual(comp._adapter_registrations[(ibar,), ifoo, _name], (_Factory, _info)) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Registered)) self.assertTrue(isinstance(event.object, AdapterRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertEqual(event.object.required, (ibar,)) self.assertTrue(event.object.name is _name) self.assertTrue(event.object.info is _info) self.assertTrue(event.object.factory is _Factory) def test_registerAdapter_wo_event(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') _info = 'info' _name = 'name' def _factory(context): raise NotImplementedError() comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: comp.registerAdapter(_factory, (ibar,), ifoo, _name, _info, event=False) self.assertEqual(len(_events), 0) def test_unregisterAdapter_neither_factory_nor_provided(self): comp = self._makeOne() self.assertRaises(TypeError, comp.unregisterAdapter, factory=None, provided=None) def test_unregisterAdapter_neither_factory_nor_required(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') comp = self._makeOne() self.assertRaises(TypeError, comp.unregisterAdapter, factory=None, provided=ifoo, required=None) def test_unregisterAdapter_miss(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') class _Factory: pass comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: unreg = comp.unregisterAdapter(_Factory, (ibar,), ifoo) self.assertFalse(unreg) def test_unregisterAdapter_hit_w_explicit_provided_and_required(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Unregistered from zope.interface.registry import AdapterRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') class _Factory: pass comp = self._makeOne() comp.registerAdapter(_Factory, (ibar,), ifoo) _monkey, _events = self._wrapEvents() with _monkey: unreg = comp.unregisterAdapter(_Factory, (ibar,), ifoo) self.assertTrue(unreg) self.assertFalse(comp.adapters._adapters) self.assertFalse(comp._adapter_registrations) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Unregistered)) self.assertTrue(isinstance(event.object, AdapterRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertEqual(event.object.required, (ibar,)) self.assertEqual(event.object.name, '') self.assertEqual(event.object.info, '') self.assertTrue(event.object.factory is _Factory) def test_unregisterAdapter_wo_explicit_provided(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer from zope.interface.interfaces import Unregistered from zope.interface.registry import AdapterRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') @implementer(ifoo) class _Factory: pass comp = self._makeOne() comp.registerAdapter(_Factory, (ibar,), ifoo) _monkey, _events = self._wrapEvents() with _monkey: unreg = comp.unregisterAdapter(_Factory, (ibar,)) self.assertTrue(unreg) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Unregistered)) self.assertTrue(isinstance(event.object, AdapterRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertEqual(event.object.required, (ibar,)) self.assertEqual(event.object.name, '') self.assertEqual(event.object.info, '') self.assertTrue(event.object.factory is _Factory) def test_unregisterAdapter_wo_explicit_required(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Unregistered from zope.interface.registry import AdapterRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') class _Factory: __component_adapts__ = (ibar,) comp = self._makeOne() comp.registerAdapter(_Factory, (ibar,), ifoo) _monkey, _events = self._wrapEvents() with _monkey: unreg = comp.unregisterAdapter(_Factory, provided=ifoo) self.assertTrue(unreg) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Unregistered)) self.assertTrue(isinstance(event.object, AdapterRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertEqual(event.object.required, (ibar,)) self.assertEqual(event.object.name, '') self.assertEqual(event.object.info, '') self.assertTrue(event.object.factory is _Factory) def test_registeredAdapters_empty(self): comp = self._makeOne() self.assertEqual(list(comp.registeredAdapters()), []) def test_registeredAdapters_notempty(self): from zope.interface.declarations import InterfaceClass from zope.interface.registry import AdapterRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IFoo') _info = 'info' _name1 = 'name1' _name2 = 'name2' class _Factory: pass comp = self._makeOne() comp.registerAdapter(_Factory, (ibar,), ifoo, _name1, _info) comp.registerAdapter(_Factory, (ibar,), ifoo, _name2, _info) reg = sorted(comp.registeredAdapters(), key=lambda r: r.name) self.assertEqual(len(reg), 2) self.assertTrue(isinstance(reg[0], AdapterRegistration)) self.assertTrue(reg[0].registry is comp) self.assertTrue(reg[0].provided is ifoo) self.assertEqual(reg[0].required, (ibar,)) self.assertTrue(reg[0].name is _name1) self.assertTrue(reg[0].info is _info) self.assertTrue(reg[0].factory is _Factory) self.assertTrue(isinstance(reg[1], AdapterRegistration)) self.assertTrue(reg[1].registry is comp) self.assertTrue(reg[1].provided is ifoo) self.assertEqual(reg[1].required, (ibar,)) self.assertTrue(reg[1].name is _name2) self.assertTrue(reg[1].info is _info) self.assertTrue(reg[1].factory is _Factory) def test_queryAdapter_miss_no_default(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') comp = self._makeOne() _context = object() self.assertTrue(comp.queryAdapter(_context, ifoo) is None) def test_queryAdapter_miss_w_default(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') comp = self._makeOne() _context = object() _default = object() self.assertTrue( comp.queryAdapter(_context, ifoo, default=_default) is _default) def test_queryAdapter_hit(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') class _Factory: def __init__(self, context): self.context = context @implementer(ibar) class _Context: pass _context = _Context() comp = self._makeOne() comp.registerAdapter(_Factory, (ibar,), ifoo) adapter = comp.queryAdapter(_context, ifoo) self.assertTrue(isinstance(adapter, _Factory)) self.assertTrue(adapter.context is _context) def test_getAdapter_miss(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer from zope.interface.interfaces import ComponentLookupError class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') @implementer(ibar) class _Context: pass _context = _Context() comp = self._makeOne() self.assertRaises(ComponentLookupError, comp.getAdapter, _context, ifoo) def test_getAdapter_hit(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') class _Factory: def __init__(self, context): self.context = context @implementer(ibar) class _Context: pass _context = _Context() comp = self._makeOne() comp.registerAdapter(_Factory, (ibar,), ifoo) adapter = comp.getAdapter(_context, ifoo) self.assertIsInstance(adapter, _Factory) self.assertIs(adapter.context, _context) def test_getAdapter_hit_super(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass class IFoo(Interface): pass @implementer(IBase) class Base: pass @implementer(IDerived) class Derived(Base): pass class AdapterBase: def __init__(self, context): self.context = context class AdapterDerived: def __init__(self, context): self.context = context comp = self._makeOne() comp.registerAdapter(AdapterDerived, (IDerived,), IFoo) comp.registerAdapter(AdapterBase, (IBase,), IFoo) self._should_not_change(comp) derived = Derived() adapter = comp.getAdapter(derived, IFoo) self.assertIsInstance(adapter, AdapterDerived) self.assertIs(adapter.context, derived) supe = super(Derived, derived) adapter = comp.getAdapter(supe, IFoo) self.assertIsInstance(adapter, AdapterBase) self.assertIs(adapter.context, derived) def test_getAdapter_hit_super_when_parent_implements_interface_diamond(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass class IFoo(Interface): pass class Base: pass class Child1(Base): pass @implementer(IBase) class Child2(Base): pass @implementer(IDerived) class Derived(Child1, Child2): pass class AdapterBase: def __init__(self, context): self.context = context class AdapterDerived: def __init__(self, context): self.context = context comp = self._makeOne() comp.registerAdapter(AdapterDerived, (IDerived,), IFoo) comp.registerAdapter(AdapterBase, (IBase,), IFoo) self._should_not_change(comp) derived = Derived() adapter = comp.getAdapter(derived, IFoo) self.assertIsInstance(adapter, AdapterDerived) self.assertIs(adapter.context, derived) supe = super(Derived, derived) adapter = comp.getAdapter(supe, IFoo) self.assertIsInstance(adapter, AdapterBase) self.assertIs(adapter.context, derived) def test_queryMultiAdapter_miss(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') ibaz = IFoo('IBaz') @implementer(ibar) class _Context1: pass @implementer(ibaz) class _Context2: pass _context1 = _Context1() _context2 = _Context2() comp = self._makeOne() self.assertEqual(comp.queryMultiAdapter((_context1, _context2), ifoo), None) def test_queryMultiAdapter_miss_w_default(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') ibaz = IFoo('IBaz') @implementer(ibar) class _Context1: pass @implementer(ibaz) class _Context2: pass _context1 = _Context1() _context2 = _Context2() _default = object() comp = self._makeOne() self.assertTrue( comp.queryMultiAdapter((_context1, _context2), ifoo, default=_default) is _default) def test_queryMultiAdapter_hit(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') ibaz = IFoo('IBaz') @implementer(ibar) class _Context1: pass @implementer(ibaz) class _Context2: pass _context1 = _Context1() _context2 = _Context2() class _Factory: def __init__(self, context1, context2): self.context = context1, context2 comp = self._makeOne() comp.registerAdapter(_Factory, (ibar, ibaz), ifoo) adapter = comp.queryMultiAdapter((_context1, _context2), ifoo) self.assertTrue(isinstance(adapter, _Factory)) self.assertEqual(adapter.context, (_context1, _context2)) def test_getMultiAdapter_miss(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer from zope.interface.interfaces import ComponentLookupError class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') ibaz = IFoo('IBaz') @implementer(ibar) class _Context1: pass @implementer(ibaz) class _Context2: pass _context1 = _Context1() _context2 = _Context2() comp = self._makeOne() self.assertRaises(ComponentLookupError, comp.getMultiAdapter, (_context1, _context2), ifoo) def test_getMultiAdapter_hit(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') ibaz = IFoo('IBaz') @implementer(ibar) class _Context1: pass @implementer(ibaz) class _Context2: pass _context1 = _Context1() _context2 = _Context2() class _Factory: def __init__(self, context1, context2): self.context = context1, context2 comp = self._makeOne() comp.registerAdapter(_Factory, (ibar, ibaz), ifoo) adapter = comp.getMultiAdapter((_context1, _context2), ifoo) self.assertTrue(isinstance(adapter, _Factory)) self.assertEqual(adapter.context, (_context1, _context2)) def _should_not_change(self, comp): # Be sure that none of the underlying structures # get told that they have changed during this process # because that invalidates caches. def no_changes(*args): self.fail("Nothing should get changed") comp.changed = no_changes comp.adapters.changed = no_changes comp.adapters._v_lookup.changed = no_changes def test_getMultiAdapter_hit_super(self): from zope.interface import Interface from zope.interface.declarations import implementer class IBase(Interface): pass class IDerived(IBase): pass class IFoo(Interface): pass @implementer(IBase) class Base: pass @implementer(IDerived) class Derived(Base): pass class AdapterBase: def __init__(self, context1, context2): self.context1 = context1 self.context2 = context2 class AdapterDerived(AdapterBase): pass comp = self._makeOne() comp.registerAdapter(AdapterDerived, (IDerived, IDerived), IFoo) comp.registerAdapter(AdapterBase, (IBase, IDerived), IFoo) self._should_not_change(comp) derived = Derived() adapter = comp.getMultiAdapter((derived, derived), IFoo) self.assertIsInstance(adapter, AdapterDerived) self.assertIs(adapter.context1, derived) self.assertIs(adapter.context2, derived) supe = super(Derived, derived) adapter = comp.getMultiAdapter((supe, derived), IFoo) self.assertIsInstance(adapter, AdapterBase) self.assertNotIsInstance(adapter, AdapterDerived) self.assertIs(adapter.context1, derived) self.assertIs(adapter.context2, derived) def test_getAdapters_empty(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') ibaz = IFoo('IBaz') @implementer(ibar) class _Context1: pass @implementer(ibaz) class _Context2: pass _context1 = _Context1() _context2 = _Context2() comp = self._makeOne() self.assertEqual( list(comp.getAdapters((_context1, _context2), ifoo)), []) def test_getAdapters_factory_returns_None(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') ibaz = IFoo('IBaz') @implementer(ibar) class _Context1: pass @implementer(ibaz) class _Context2: pass _context1 = _Context1() _context2 = _Context2() comp = self._makeOne() _called_with = [] def _side_effect_only(context1, context2): _called_with.append((context1, context2)) return None comp.registerAdapter(_side_effect_only, (ibar, ibaz), ifoo) self.assertEqual( list(comp.getAdapters((_context1, _context2), ifoo)), []) self.assertEqual(_called_with, [(_context1, _context2)]) def test_getAdapters_non_empty(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') ibaz = IFoo('IBaz') @implementer(ibar) class _Context1: pass @implementer(ibaz) class _Context2: pass _context1 = _Context1() _context2 = _Context2() class _Factory1: def __init__(self, context1, context2): self.context = context1, context2 class _Factory2: def __init__(self, context1, context2): self.context = context1, context2 _name1 = 'name1' _name2 = 'name2' comp = self._makeOne() comp.registerAdapter(_Factory1, (ibar, ibaz), ifoo, name=_name1) comp.registerAdapter(_Factory2, (ibar, ibaz), ifoo, name=_name2) found = sorted(comp.getAdapters((_context1, _context2), ifoo)) self.assertEqual(len(found), 2) self.assertEqual(found[0][0], _name1) self.assertTrue(isinstance(found[0][1], _Factory1)) self.assertEqual(found[1][0], _name2) self.assertTrue(isinstance(found[1][1], _Factory2)) def test_registerSubscriptionAdapter_w_nonblank_name(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') _name = 'name' _info = 'info' def _factory(context): raise NotImplementedError() comp = self._makeOne() self.assertRaises(TypeError, comp.registerSubscriptionAdapter, _factory, (ibar,), ifoo, _name, _info) def test_registerSubscriptionAdapter_w_explicit_provided_and_required(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Registered from zope.interface.registry import SubscriptionRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') _blank = '' _info = 'info' def _factory(context): raise NotImplementedError() comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: comp.registerSubscriptionAdapter(_factory, (ibar,), ifoo, info=_info) reg = comp.adapters._subscribers[1][ibar][ifoo][_blank] self.assertEqual(len(reg), 1) self.assertTrue(reg[0] is _factory) self.assertEqual(comp._subscription_registrations, [((ibar,), ifoo, _blank, _factory, _info)]) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Registered)) self.assertTrue(isinstance(event.object, SubscriptionRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertEqual(event.object.required, (ibar,)) self.assertEqual(event.object.name, _blank) self.assertTrue(event.object.info is _info) self.assertTrue(event.object.factory is _factory) def test_registerSubscriptionAdapter_wo_explicit_provided(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer from zope.interface.interfaces import Registered from zope.interface.registry import SubscriptionRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') _info = 'info' _blank = '' @implementer(ifoo) class _Factory: pass comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: comp.registerSubscriptionAdapter(_Factory, (ibar,), info=_info) reg = comp.adapters._subscribers[1][ibar][ifoo][_blank] self.assertEqual(len(reg), 1) self.assertTrue(reg[0] is _Factory) self.assertEqual(comp._subscription_registrations, [((ibar,), ifoo, _blank, _Factory, _info)]) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Registered)) self.assertTrue(isinstance(event.object, SubscriptionRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertEqual(event.object.required, (ibar,)) self.assertEqual(event.object.name, _blank) self.assertTrue(event.object.info is _info) self.assertTrue(event.object.factory is _Factory) def test_registerSubscriptionAdapter_wo_explicit_required(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Registered from zope.interface.registry import SubscriptionRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') _info = 'info' _blank = '' class _Factory: __component_adapts__ = (ibar,) comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: comp.registerSubscriptionAdapter( _Factory, provided=ifoo, info=_info) reg = comp.adapters._subscribers[1][ibar][ifoo][_blank] self.assertEqual(len(reg), 1) self.assertTrue(reg[0] is _Factory) self.assertEqual(comp._subscription_registrations, [((ibar,), ifoo, _blank, _Factory, _info)]) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Registered)) self.assertTrue(isinstance(event.object, SubscriptionRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertEqual(event.object.required, (ibar,)) self.assertEqual(event.object.name, _blank) self.assertTrue(event.object.info is _info) self.assertTrue(event.object.factory is _Factory) def test_registerSubscriptionAdapter_wo_event(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') _blank = '' _info = 'info' def _factory(context): raise NotImplementedError() comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: comp.registerSubscriptionAdapter(_factory, (ibar,), ifoo, info=_info, event=False) self.assertEqual(len(_events), 0) def test_registeredSubscriptionAdapters_empty(self): comp = self._makeOne() self.assertEqual(list(comp.registeredSubscriptionAdapters()), []) def test_registeredSubscriptionAdapters_notempty(self): from zope.interface.declarations import InterfaceClass from zope.interface.registry import SubscriptionRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IFoo') _info = 'info' _blank = '' class _Factory: pass comp = self._makeOne() comp.registerSubscriptionAdapter(_Factory, (ibar,), ifoo, info=_info) comp.registerSubscriptionAdapter(_Factory, (ibar,), ifoo, info=_info) reg = list(comp.registeredSubscriptionAdapters()) self.assertEqual(len(reg), 2) self.assertTrue(isinstance(reg[0], SubscriptionRegistration)) self.assertTrue(reg[0].registry is comp) self.assertTrue(reg[0].provided is ifoo) self.assertEqual(reg[0].required, (ibar,)) self.assertEqual(reg[0].name, _blank) self.assertTrue(reg[0].info is _info) self.assertTrue(reg[0].factory is _Factory) self.assertTrue(isinstance(reg[1], SubscriptionRegistration)) self.assertTrue(reg[1].registry is comp) self.assertTrue(reg[1].provided is ifoo) self.assertEqual(reg[1].required, (ibar,)) self.assertEqual(reg[1].name, _blank) self.assertTrue(reg[1].info is _info) self.assertTrue(reg[1].factory is _Factory) def test_unregisterSubscriptionAdapter_w_nonblank_name(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') _nonblank = 'nonblank' comp = self._makeOne() self.assertRaises(TypeError, comp.unregisterSubscriptionAdapter, required=ifoo, provided=ibar, name=_nonblank) def test_unregisterSubscriptionAdapter_neither_factory_nor_provided(self): comp = self._makeOne() self.assertRaises(TypeError, comp.unregisterSubscriptionAdapter, factory=None, provided=None) def test_unregisterSubscriptionAdapter_neither_factory_nor_required(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') comp = self._makeOne() self.assertRaises(TypeError, comp.unregisterSubscriptionAdapter, factory=None, provided=ifoo, required=None) def test_unregisterSubscriptionAdapter_miss(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') class _Factory: pass comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: unreg = comp.unregisterSubscriptionAdapter(_Factory, (ibar,), ifoo) self.assertFalse(unreg) self.assertFalse(_events) def test_unregisterSubscriptionAdapter_hit_wo_factory(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Unregistered from zope.interface.registry import SubscriptionRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') class _Factory: pass comp = self._makeOne() comp.registerSubscriptionAdapter(_Factory, (ibar,), ifoo) _monkey, _events = self._wrapEvents() with _monkey: unreg = comp.unregisterSubscriptionAdapter(None, (ibar,), ifoo) self.assertTrue(unreg) self.assertFalse(comp.adapters._subscribers) self.assertFalse(comp._subscription_registrations) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Unregistered)) self.assertTrue(isinstance(event.object, SubscriptionRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertEqual(event.object.required, (ibar,)) self.assertEqual(event.object.name, '') self.assertEqual(event.object.info, '') self.assertTrue(event.object.factory is None) def test_unregisterSubscriptionAdapter_hit_w_factory(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Unregistered from zope.interface.registry import SubscriptionRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') class _Factory: pass comp = self._makeOne() comp.registerSubscriptionAdapter(_Factory, (ibar,), ifoo) _monkey, _events = self._wrapEvents() with _monkey: unreg = comp.unregisterSubscriptionAdapter(_Factory, (ibar,), ifoo) self.assertTrue(unreg) self.assertFalse(comp.adapters._subscribers) self.assertFalse(comp._subscription_registrations) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Unregistered)) self.assertTrue(isinstance(event.object, SubscriptionRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertEqual(event.object.required, (ibar,)) self.assertEqual(event.object.name, '') self.assertEqual(event.object.info, '') self.assertTrue(event.object.factory is _Factory) def test_unregisterSubscriptionAdapter_wo_explicit_provided(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer from zope.interface.interfaces import Unregistered from zope.interface.registry import SubscriptionRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') @implementer(ifoo) class _Factory: pass comp = self._makeOne() comp.registerSubscriptionAdapter(_Factory, (ibar,), ifoo) _monkey, _events = self._wrapEvents() with _monkey: unreg = comp.unregisterSubscriptionAdapter(_Factory, (ibar,)) self.assertTrue(unreg) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Unregistered)) self.assertTrue(isinstance(event.object, SubscriptionRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertEqual(event.object.required, (ibar,)) self.assertEqual(event.object.name, '') self.assertEqual(event.object.info, '') self.assertTrue(event.object.factory is _Factory) def test_unregisterSubscriptionAdapter_wo_explicit_required(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Unregistered from zope.interface.registry import SubscriptionRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') class _Factory: __component_adapts__ = (ibar,) comp = self._makeOne() comp.registerSubscriptionAdapter(_Factory, (ibar,), ifoo) _monkey, _events = self._wrapEvents() with _monkey: unreg = comp.unregisterSubscriptionAdapter(_Factory, provided=ifoo) self.assertTrue(unreg) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Unregistered)) self.assertTrue(isinstance(event.object, SubscriptionRegistration)) self.assertTrue(event.object.registry is comp) self.assertTrue(event.object.provided is ifoo) self.assertEqual(event.object.required, (ibar,)) self.assertEqual(event.object.name, '') self.assertEqual(event.object.info, '') self.assertTrue(event.object.factory is _Factory) def test_subscribers_empty(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') comp = self._makeOne() @implementer(ibar) class Bar: pass bar = Bar() self.assertEqual(list(comp.subscribers((bar,), ifoo)), []) def test_subscribers_non_empty(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') class _Factory: __component_adapts__ = (ibar,) def __init__(self, context): self._context = context class _Derived(_Factory): pass comp = self._makeOne() comp.registerSubscriptionAdapter(_Factory, (ibar,), ifoo) comp.registerSubscriptionAdapter(_Derived, (ibar,), ifoo) @implementer(ibar) class Bar: pass bar = Bar() subscribers = comp.subscribers((bar,), ifoo) def _klassname(x): return x.__class__.__name__ subscribers = sorted(subscribers, key=_klassname) self.assertEqual(len(subscribers), 2) self.assertTrue(isinstance(subscribers[0], _Derived)) self.assertTrue(isinstance(subscribers[1], _Factory)) def test_registerHandler_w_nonblank_name(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _nonblank = 'nonblank' comp = self._makeOne() def _factory(context): raise NotImplementedError() self.assertRaises(TypeError, comp.registerHandler, _factory, required=ifoo, name=_nonblank) def test_registerHandler_w_explicit_required(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Registered from zope.interface.registry import HandlerRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _blank = '' _info = 'info' def _factory(context): raise NotImplementedError() comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: comp.registerHandler(_factory, (ifoo,), info=_info) reg = comp.adapters._subscribers[1][ifoo][None][_blank] self.assertEqual(len(reg), 1) self.assertTrue(reg[0] is _factory) self.assertEqual(comp._handler_registrations, [((ifoo,), _blank, _factory, _info)]) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Registered)) self.assertTrue(isinstance(event.object, HandlerRegistration)) self.assertTrue(event.object.registry is comp) self.assertEqual(event.object.required, (ifoo,)) self.assertEqual(event.object.name, _blank) self.assertTrue(event.object.info is _info) self.assertTrue(event.object.factory is _factory) def test_registerHandler_wo_explicit_required_no_event(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _info = 'info' _blank = '' class _Factory: __component_adapts__ = (ifoo,) pass comp = self._makeOne() _monkey, _events = self._wrapEvents() with _monkey: comp.registerHandler(_Factory, info=_info, event=False) reg = comp.adapters._subscribers[1][ifoo][None][_blank] self.assertEqual(len(reg), 1) self.assertTrue(reg[0] is _Factory) self.assertEqual(comp._handler_registrations, [((ifoo,), _blank, _Factory, _info)]) self.assertEqual(len(_events), 0) def test_registeredHandlers_empty(self): comp = self._makeOne() self.assertFalse(list(comp.registeredHandlers())) def test_registeredHandlers_non_empty(self): from zope.interface.declarations import InterfaceClass from zope.interface.registry import HandlerRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') def _factory1(context): raise NotImplementedError() def _factory2(context): raise NotImplementedError() comp = self._makeOne() comp.registerHandler(_factory1, (ifoo,)) comp.registerHandler(_factory2, (ifoo,)) def _factory_name(x): return x.factory.__code__.co_name subscribers = sorted(comp.registeredHandlers(), key=_factory_name) self.assertEqual(len(subscribers), 2) self.assertTrue(isinstance(subscribers[0], HandlerRegistration)) self.assertEqual(subscribers[0].required, (ifoo,)) self.assertEqual(subscribers[0].name, '') self.assertEqual(subscribers[0].factory, _factory1) self.assertEqual(subscribers[0].info, '') self.assertTrue(isinstance(subscribers[1], HandlerRegistration)) self.assertEqual(subscribers[1].required, (ifoo,)) self.assertEqual(subscribers[1].name, '') self.assertEqual(subscribers[1].factory, _factory2) self.assertEqual(subscribers[1].info, '') def test_unregisterHandler_w_nonblank_name(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _nonblank = 'nonblank' comp = self._makeOne() self.assertRaises(TypeError, comp.unregisterHandler, required=(ifoo,), name=_nonblank) def test_unregisterHandler_neither_factory_nor_required(self): comp = self._makeOne() self.assertRaises(TypeError, comp.unregisterHandler) def test_unregisterHandler_miss(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') comp = self._makeOne() unreg = comp.unregisterHandler(required=(ifoo,)) self.assertFalse(unreg) def test_unregisterHandler_hit_w_factory_and_explicit_provided(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Unregistered from zope.interface.registry import HandlerRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') comp = self._makeOne() def _factory(context): raise NotImplementedError() comp = self._makeOne() comp.registerHandler(_factory, (ifoo,)) _monkey, _events = self._wrapEvents() with _monkey: unreg = comp.unregisterHandler(_factory, (ifoo,)) self.assertTrue(unreg) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Unregistered)) self.assertTrue(isinstance(event.object, HandlerRegistration)) self.assertTrue(event.object.registry is comp) self.assertEqual(event.object.required, (ifoo,)) self.assertEqual(event.object.name, '') self.assertTrue(event.object.factory is _factory) def test_unregisterHandler_hit_w_only_explicit_provided(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Unregistered from zope.interface.registry import HandlerRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') comp = self._makeOne() def _factory(context): raise NotImplementedError() comp = self._makeOne() comp.registerHandler(_factory, (ifoo,)) _monkey, _events = self._wrapEvents() with _monkey: unreg = comp.unregisterHandler(required=(ifoo,)) self.assertTrue(unreg) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Unregistered)) self.assertTrue(isinstance(event.object, HandlerRegistration)) self.assertTrue(event.object.registry is comp) self.assertEqual(event.object.required, (ifoo,)) self.assertEqual(event.object.name, '') self.assertTrue(event.object.factory is None) def test_unregisterHandler_wo_explicit_required(self): from zope.interface.declarations import InterfaceClass from zope.interface.interfaces import Unregistered from zope.interface.registry import HandlerRegistration class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') class _Factory: __component_adapts__ = (ifoo,) comp = self._makeOne() comp.registerHandler(_Factory) _monkey, _events = self._wrapEvents() with _monkey: unreg = comp.unregisterHandler(_Factory) self.assertTrue(unreg) self.assertEqual(len(_events), 1) args, kw = _events[0] event, = args self.assertEqual(kw, {}) self.assertTrue(isinstance(event, Unregistered)) self.assertTrue(isinstance(event.object, HandlerRegistration)) self.assertTrue(event.object.registry is comp) self.assertEqual(event.object.required, (ifoo,)) self.assertEqual(event.object.name, '') self.assertEqual(event.object.info, '') self.assertTrue(event.object.factory is _Factory) def test_handle_empty(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') comp = self._makeOne() @implementer(ifoo) class Bar: pass bar = Bar() comp.handle((bar,)) # doesn't raise def test_handle_non_empty(self): from zope.interface.declarations import InterfaceClass from zope.interface.declarations import implementer class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') _called_1 = [] def _factory_1(context): _called_1.append(context) _called_2 = [] def _factory_2(context): _called_2.append(context) comp = self._makeOne() comp.registerHandler(_factory_1, (ifoo,)) comp.registerHandler(_factory_2, (ifoo,)) @implementer(ifoo) class Bar: pass bar = Bar() comp.handle(bar) self.assertEqual(_called_1, [bar]) self.assertEqual(_called_2, [bar]) def test_register_unregister_identical_objects_provided(self, identical=True): # https://github.com/zopefoundation/zope.interface/issues/227 class IFoo(Interface): pass comp = self._makeOne() first = object() second = first if identical else object() comp.registerUtility(first, provided=IFoo) comp.registerUtility(second, provided=IFoo, name='bar') self.assertEqual(len(comp.utilities._subscribers), 1) self.assertEqual(comp.utilities._subscribers, [{ IFoo: {'': (first, ) if identical else (first, second)} }]) self.assertEqual(comp.utilities._provided, { IFoo: 3 if identical else 4 }) res = comp.unregisterUtility(first, provided=IFoo) self.assertTrue(res) res = comp.unregisterUtility(second, provided=IFoo, name='bar') self.assertTrue(res) self.assertEqual(comp.utilities._provided, {}) self.assertEqual(len(comp.utilities._subscribers), 0) def test_register_unregister_nonequal_objects_provided(self): self.test_register_unregister_identical_objects_provided(identical=False) def test_rebuildUtilityRegistryFromLocalCache(self): class IFoo(Interface): "Does nothing" class UtilityImplementingFoo: "Does nothing" comps = self._makeOne() for i in range(30): comps.registerUtility(UtilityImplementingFoo(), IFoo, name='{}'.format(i)) orig_generation = comps.utilities._generation orig_adapters = comps.utilities._adapters self.assertEqual(len(orig_adapters), 1) self.assertEqual(len(orig_adapters[0]), 1) self.assertEqual(len(orig_adapters[0][IFoo]), 30) orig_subscribers = comps.utilities._subscribers self.assertEqual(len(orig_subscribers), 1) self.assertEqual(len(orig_subscribers[0]), 1) self.assertEqual(len(orig_subscribers[0][IFoo]), 1) self.assertEqual(len(orig_subscribers[0][IFoo]['']), 30) # Blow a bunch of them away, creating artificial corruption new_adapters = comps.utilities._adapters = type(orig_adapters)() new_adapters.append({}) d = new_adapters[0][IFoo] = {} for name in range(10): name = str(str(name)) d[name] = orig_adapters[0][IFoo][name] self.assertNotEqual(orig_adapters, new_adapters) new_subscribers = comps.utilities._subscribers = type(orig_subscribers)() new_subscribers.append({}) d = new_subscribers[0][IFoo] = {} d[''] = () for name in range(5, 12): # 12 - 5 = 7 name = str(str(name)) comp = orig_adapters[0][IFoo][name] d[''] += (comp,) # We can preflight (by default) and nothing changes rebuild_results_preflight = comps.rebuildUtilityRegistryFromLocalCache() self.assertEqual(comps.utilities._generation, orig_generation) self.assertEqual(rebuild_results_preflight, { 'did_not_register': 10, 'needed_registered': 20, 'did_not_subscribe': 7, 'needed_subscribed': 23, }) # Now for real rebuild_results = comps.rebuildUtilityRegistryFromLocalCache(rebuild=True) # The generation only got incremented once self.assertEqual(comps.utilities._generation, orig_generation + 1) # The result was the same self.assertEqual(rebuild_results_preflight, rebuild_results) self.assertEqual(new_adapters, orig_adapters) self.assertEqual( len(new_subscribers[0][IFoo]['']), len(orig_subscribers[0][IFoo][''])) for orig_subscriber in orig_subscribers[0][IFoo]['']: self.assertIn(orig_subscriber, new_subscribers[0][IFoo]['']) # Preflighting, rebuilding again produce no changes. preflight_after = comps.rebuildUtilityRegistryFromLocalCache() self.assertEqual(preflight_after, { 'did_not_register': 30, 'needed_registered': 0, 'did_not_subscribe': 30, 'needed_subscribed': 0, }) rebuild_after = comps.rebuildUtilityRegistryFromLocalCache(rebuild=True) self.assertEqual(rebuild_after, preflight_after) self.assertEqual(comps.utilities._generation, orig_generation + 1) class UnhashableComponentsTests(ComponentsTests): def _getTargetClass(self): # Mimic what pyramid does to create an unhashable # registry class Components(super(UnhashableComponentsTests, self)._getTargetClass(), dict): pass return Components # Test _getUtilityProvided, _getAdapterProvided, _getAdapterRequired via their # callers (Component.registerUtility, Component.registerAdapter). class UtilityRegistrationTests(unittest.TestCase): def _getTargetClass(self): from zope.interface.registry import UtilityRegistration return UtilityRegistration def _makeOne(self, component=None, factory=None): from zope.interface.declarations import InterfaceClass class InterfaceClassSubclass(InterfaceClass): pass ifoo = InterfaceClassSubclass('IFoo') class _Registry: def __repr__(self): return '_REGISTRY' registry = _Registry() name = 'name' doc = 'DOCSTRING' klass = self._getTargetClass() return (klass(registry, ifoo, name, component, doc, factory), registry, name, ) def test_class_conforms_to_IUtilityRegistration(self): from zope.interface.interfaces import IUtilityRegistration from zope.interface.verify import verifyClass verifyClass(IUtilityRegistration, self._getTargetClass()) def test_instance_conforms_to_IUtilityRegistration(self): from zope.interface.interfaces import IUtilityRegistration from zope.interface.verify import verifyObject ur, _, _ = self._makeOne() verifyObject(IUtilityRegistration, ur) def test___repr__(self): class _Component: __name__ = 'TEST' _component = _Component() ur, _registry, _name = self._makeOne(_component) self.assertEqual(repr(ur), "UtilityRegistration(_REGISTRY, IFoo, %r, TEST, None, 'DOCSTRING')" % (_name)) def test___repr___provided_wo_name(self): class _Component: def __repr__(self): return 'TEST' _component = _Component() ur, _registry, _name = self._makeOne(_component) ur.provided = object() self.assertEqual(repr(ur), "UtilityRegistration(_REGISTRY, None, %r, TEST, None, 'DOCSTRING')" % (_name)) def test___repr___component_wo_name(self): class _Component: def __repr__(self): return 'TEST' _component = _Component() ur, _registry, _name = self._makeOne(_component) ur.provided = object() self.assertEqual(repr(ur), "UtilityRegistration(_REGISTRY, None, %r, TEST, None, 'DOCSTRING')" % (_name)) def test___hash__(self): _component = object() ur, _registry, _name = self._makeOne(_component) self.assertEqual(ur.__hash__(), id(ur)) def test___eq___identity(self): _component = object() ur, _registry, _name = self._makeOne(_component) self.assertTrue(ur == ur) def test___eq___hit(self): _component = object() ur, _registry, _name = self._makeOne(_component) ur2, _, _ = self._makeOne(_component) self.assertTrue(ur == ur2) def test___eq___miss(self): _component = object() _component2 = object() ur, _registry, _name = self._makeOne(_component) ur2, _, _ = self._makeOne(_component2) self.assertFalse(ur == ur2) def test___ne___identity(self): _component = object() ur, _registry, _name = self._makeOne(_component) self.assertFalse(ur != ur) def test___ne___hit(self): _component = object() ur, _registry, _name = self._makeOne(_component) ur2, _, _ = self._makeOne(_component) self.assertFalse(ur != ur2) def test___ne___miss(self): _component = object() _component2 = object() ur, _registry, _name = self._makeOne(_component) ur2, _, _ = self._makeOne(_component2) self.assertTrue(ur != ur2) def test___lt___identity(self): _component = object() ur, _registry, _name = self._makeOne(_component) self.assertFalse(ur < ur) def test___lt___hit(self): _component = object() ur, _registry, _name = self._makeOne(_component) ur2, _, _ = self._makeOne(_component) self.assertFalse(ur < ur2) def test___lt___miss(self): _component = object() _component2 = object() ur, _registry, _name = self._makeOne(_component) ur2, _, _ = self._makeOne(_component2) ur2.name = _name + '2' self.assertTrue(ur < ur2) def test___le___identity(self): _component = object() ur, _registry, _name = self._makeOne(_component) self.assertTrue(ur <= ur) def test___le___hit(self): _component = object() ur, _registry, _name = self._makeOne(_component) ur2, _, _ = self._makeOne(_component) self.assertTrue(ur <= ur2) def test___le___miss(self): _component = object() _component2 = object() ur, _registry, _name = self._makeOne(_component) ur2, _, _ = self._makeOne(_component2) ur2.name = _name + '2' self.assertTrue(ur <= ur2) def test___gt___identity(self): _component = object() ur, _registry, _name = self._makeOne(_component) self.assertFalse(ur > ur) def test___gt___hit(self): _component = object() _component2 = object() ur, _registry, _name = self._makeOne(_component) ur2, _, _ = self._makeOne(_component2) ur2.name = _name + '2' self.assertTrue(ur2 > ur) def test___gt___miss(self): _component = object() ur, _registry, _name = self._makeOne(_component) ur2, _, _ = self._makeOne(_component) self.assertFalse(ur2 > ur) def test___ge___identity(self): _component = object() ur, _registry, _name = self._makeOne(_component) self.assertTrue(ur >= ur) def test___ge___miss(self): _component = object() _component2 = object() ur, _registry, _name = self._makeOne(_component) ur2, _, _ = self._makeOne(_component2) ur2.name = _name + '2' self.assertFalse(ur >= ur2) def test___ge___hit(self): _component = object() ur, _registry, _name = self._makeOne(_component) ur2, _, _ = self._makeOne(_component) ur2.name = _name + '2' self.assertTrue(ur2 >= ur) class AdapterRegistrationTests(unittest.TestCase): def _getTargetClass(self): from zope.interface.registry import AdapterRegistration return AdapterRegistration def _makeOne(self, component=None): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') class _Registry: def __repr__(self): return '_REGISTRY' registry = _Registry() name = 'name' doc = 'DOCSTRING' klass = self._getTargetClass() return (klass(registry, (ibar,), ifoo, name, component, doc), registry, name, ) def test_class_conforms_to_IAdapterRegistration(self): from zope.interface.interfaces import IAdapterRegistration from zope.interface.verify import verifyClass verifyClass(IAdapterRegistration, self._getTargetClass()) def test_instance_conforms_to_IAdapterRegistration(self): from zope.interface.interfaces import IAdapterRegistration from zope.interface.verify import verifyObject ar, _, _ = self._makeOne() verifyObject(IAdapterRegistration, ar) def test___repr__(self): class _Component: __name__ = 'TEST' _component = _Component() ar, _registry, _name = self._makeOne(_component) self.assertEqual(repr(ar), ("AdapterRegistration(_REGISTRY, [IBar], IFoo, %r, TEST, " + "'DOCSTRING')") % (_name)) def test___repr___provided_wo_name(self): class _Component: def __repr__(self): return 'TEST' _component = _Component() ar, _registry, _name = self._makeOne(_component) ar.provided = object() self.assertEqual(repr(ar), ("AdapterRegistration(_REGISTRY, [IBar], None, %r, TEST, " + "'DOCSTRING')") % (_name)) def test___repr___component_wo_name(self): class _Component: def __repr__(self): return 'TEST' _component = _Component() ar, _registry, _name = self._makeOne(_component) ar.provided = object() self.assertEqual(repr(ar), ("AdapterRegistration(_REGISTRY, [IBar], None, %r, TEST, " + "'DOCSTRING')") % (_name)) def test___hash__(self): _component = object() ar, _registry, _name = self._makeOne(_component) self.assertEqual(ar.__hash__(), id(ar)) def test___eq___identity(self): _component = object() ar, _registry, _name = self._makeOne(_component) self.assertTrue(ar == ar) def test___eq___hit(self): _component = object() ar, _registry, _name = self._makeOne(_component) ar2, _, _ = self._makeOne(_component) self.assertTrue(ar == ar2) def test___eq___miss(self): _component = object() _component2 = object() ar, _registry, _name = self._makeOne(_component) ar2, _, _ = self._makeOne(_component2) self.assertFalse(ar == ar2) def test___ne___identity(self): _component = object() ar, _registry, _name = self._makeOne(_component) self.assertFalse(ar != ar) def test___ne___miss(self): _component = object() ar, _registry, _name = self._makeOne(_component) ar2, _, _ = self._makeOne(_component) self.assertFalse(ar != ar2) def test___ne___hit_component(self): _component = object() _component2 = object() ar, _registry, _name = self._makeOne(_component) ar2, _, _ = self._makeOne(_component2) self.assertTrue(ar != ar2) def test___ne___hit_provided(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ibaz = IFoo('IBaz') _component = object() ar, _registry, _name = self._makeOne(_component) ar2, _, _ = self._makeOne(_component) ar2.provided = ibaz self.assertTrue(ar != ar2) def test___ne___hit_required(self): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ibaz = IFoo('IBaz') _component = object() _component2 = object() ar, _registry, _name = self._makeOne(_component) ar2, _, _ = self._makeOne(_component2) ar2.required = (ibaz,) self.assertTrue(ar != ar2) def test___lt___identity(self): _component = object() ar, _registry, _name = self._makeOne(_component) self.assertFalse(ar < ar) def test___lt___hit(self): _component = object() ar, _registry, _name = self._makeOne(_component) ar2, _, _ = self._makeOne(_component) self.assertFalse(ar < ar2) def test___lt___miss(self): _component = object() _component2 = object() ar, _registry, _name = self._makeOne(_component) ar2, _, _ = self._makeOne(_component2) ar2.name = _name + '2' self.assertTrue(ar < ar2) def test___le___identity(self): _component = object() ar, _registry, _name = self._makeOne(_component) self.assertTrue(ar <= ar) def test___le___hit(self): _component = object() ar, _registry, _name = self._makeOne(_component) ar2, _, _ = self._makeOne(_component) self.assertTrue(ar <= ar2) def test___le___miss(self): _component = object() _component2 = object() ar, _registry, _name = self._makeOne(_component) ar2, _, _ = self._makeOne(_component2) ar2.name = _name + '2' self.assertTrue(ar <= ar2) def test___gt___identity(self): _component = object() ar, _registry, _name = self._makeOne(_component) self.assertFalse(ar > ar) def test___gt___hit(self): _component = object() _component2 = object() ar, _registry, _name = self._makeOne(_component) ar2, _, _ = self._makeOne(_component2) ar2.name = _name + '2' self.assertTrue(ar2 > ar) def test___gt___miss(self): _component = object() ar, _registry, _name = self._makeOne(_component) ar2, _, _ = self._makeOne(_component) self.assertFalse(ar2 > ar) def test___ge___identity(self): _component = object() ar, _registry, _name = self._makeOne(_component) self.assertTrue(ar >= ar) def test___ge___miss(self): _component = object() _component2 = object() ar, _registry, _name = self._makeOne(_component) ar2, _, _ = self._makeOne(_component2) ar2.name = _name + '2' self.assertFalse(ar >= ar2) def test___ge___hit(self): _component = object() ar, _registry, _name = self._makeOne(_component) ar2, _, _ = self._makeOne(_component) ar2.name = _name + '2' self.assertTrue(ar2 >= ar) class SubscriptionRegistrationTests(unittest.TestCase): def _getTargetClass(self): from zope.interface.registry import SubscriptionRegistration return SubscriptionRegistration def _makeOne(self, component=None): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') ibar = IFoo('IBar') class _Registry: def __repr__(self): # pragma: no cover return '_REGISTRY' registry = _Registry() name = 'name' doc = 'DOCSTRING' klass = self._getTargetClass() return (klass(registry, (ibar,), ifoo, name, component, doc), registry, name, ) def test_class_conforms_to_ISubscriptionAdapterRegistration(self): from zope.interface.interfaces import ISubscriptionAdapterRegistration from zope.interface.verify import verifyClass verifyClass(ISubscriptionAdapterRegistration, self._getTargetClass()) def test_instance_conforms_to_ISubscriptionAdapterRegistration(self): from zope.interface.interfaces import ISubscriptionAdapterRegistration from zope.interface.verify import verifyObject sar, _, _ = self._makeOne() verifyObject(ISubscriptionAdapterRegistration, sar) class HandlerRegistrationTests(unittest.TestCase): def _getTargetClass(self): from zope.interface.registry import HandlerRegistration return HandlerRegistration def _makeOne(self, component=None): from zope.interface.declarations import InterfaceClass class IFoo(InterfaceClass): pass ifoo = IFoo('IFoo') class _Registry: def __repr__(self): return '_REGISTRY' registry = _Registry() name = 'name' doc = 'DOCSTRING' klass = self._getTargetClass() return (klass(registry, (ifoo,), name, component, doc), registry, name, ) def test_class_conforms_to_IHandlerRegistration(self): from zope.interface.interfaces import IHandlerRegistration from zope.interface.verify import verifyClass verifyClass(IHandlerRegistration, self._getTargetClass()) def test_instance_conforms_to_IHandlerRegistration(self): from zope.interface.interfaces import IHandlerRegistration from zope.interface.verify import verifyObject hr, _, _ = self._makeOne() verifyObject(IHandlerRegistration, hr) def test_properties(self): def _factory(context): raise NotImplementedError() hr, _, _ = self._makeOne(_factory) self.assertTrue(hr.handler is _factory) self.assertTrue(hr.factory is hr.handler) self.assertTrue(hr.provided is None) def test___repr___factory_w_name(self): class _Factory: __name__ = 'TEST' hr, _registry, _name = self._makeOne(_Factory()) self.assertEqual(repr(hr), ("HandlerRegistration(_REGISTRY, [IFoo], %r, TEST, " + "'DOCSTRING')") % (_name)) def test___repr___factory_wo_name(self): class _Factory: def __repr__(self): return 'TEST' hr, _registry, _name = self._makeOne(_Factory()) self.assertEqual(repr(hr), ("HandlerRegistration(_REGISTRY, [IFoo], %r, TEST, " + "'DOCSTRING')") % (_name)) class PersistentAdapterRegistry(VerifyingAdapterRegistry): def __getstate__(self): state = self.__dict__.copy() for k in list(state): if k in self._delegated or k.startswith('_v'): state.pop(k) state.pop('ro', None) return state def __setstate__(self, state): bases = state.pop('__bases__', ()) self.__dict__.update(state) self._createLookup() self.__bases__ = bases self._v_lookup.changed(self) class PersistentComponents(Components): # Mimic zope.component.persistentregistry.PersistentComponents: # we should be picklalable, but not persistent.Persistent ourself. def _init_registries(self): self.adapters = PersistentAdapterRegistry() self.utilities = PersistentAdapterRegistry() class PersistentDictComponents(PersistentComponents, dict): # Like Pyramid's Registry, we subclass Components and dict pass class PersistentComponentsDict(dict, PersistentComponents): # Like the above, but inheritance is flipped def __init__(self, name): dict.__init__(self) PersistentComponents.__init__(self, name) class TestPersistentComponents(unittest.TestCase): def _makeOne(self): return PersistentComponents('test') def _check_equality_after_pickle(self, made): pass def test_pickles_empty(self): import pickle comp = self._makeOne() pickle.dumps(comp) comp2 = pickle.loads(pickle.dumps(comp)) self.assertEqual(comp2.__name__, 'test') def test_pickles_with_utility_registration(self): import pickle comp = self._makeOne() utility = object() comp.registerUtility( utility, Interface) self.assertIs(utility, comp.getUtility(Interface)) comp2 = pickle.loads(pickle.dumps(comp)) self.assertEqual(comp2.__name__, 'test') # The utility is still registered self.assertIsNotNone(comp2.getUtility(Interface)) # We can register another one comp2.registerUtility( utility, Interface) self.assertIs(utility, comp2.getUtility(Interface)) self._check_equality_after_pickle(comp2) class TestPersistentDictComponents(TestPersistentComponents): def _getTargetClass(self): return PersistentDictComponents def _makeOne(self): comp = self._getTargetClass()(name='test') comp['key'] = 42 return comp def _check_equality_after_pickle(self, made): self.assertIn('key', made) self.assertEqual(made['key'], 42) class TestPersistentComponentsDict(TestPersistentDictComponents): def _getTargetClass(self): return PersistentComponentsDict class _Monkey: # context-manager for replacing module names in the scope of a test. def __init__(self, module, **kw): self.module = module self.to_restore = {key: getattr(module, key) for key in kw} for key, value in kw.items(): setattr(module, key, value) def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): for key, value in self.to_restore.items(): setattr(self.module, key, value) zope.interface-6.4/src/zope/interface/tests/test_ro.py000066400000000000000000000334541462121350100232020ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2014 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. # ############################################################################## """Resolution ordering utility tests""" import unittest # pylint:disable=blacklisted-name,protected-access,attribute-defined-outside-init class Test__mergeOrderings(unittest.TestCase): def _callFUT(self, orderings): from zope.interface.ro import _legacy_mergeOrderings return _legacy_mergeOrderings(orderings) def test_empty(self): self.assertEqual(self._callFUT([]), []) def test_single(self): self.assertEqual(self._callFUT(['a', 'b', 'c']), ['a', 'b', 'c']) def test_w_duplicates(self): self.assertEqual(self._callFUT([['a'], ['b', 'a']]), ['b', 'a']) def test_suffix_across_multiple_duplicates(self): O1 = ['x', 'y', 'z'] O2 = ['q', 'z'] O3 = [1, 3, 5] O4 = ['z'] self.assertEqual(self._callFUT([O1, O2, O3, O4]), ['x', 'y', 'q', 1, 3, 5, 'z']) class Test__flatten(unittest.TestCase): def _callFUT(self, ob): from zope.interface.ro import _legacy_flatten return _legacy_flatten(ob) def test_w_empty_bases(self): class Foo: pass foo = Foo() foo.__bases__ = () self.assertEqual(self._callFUT(foo), [foo]) def test_w_single_base(self): class Foo: pass self.assertEqual(self._callFUT(Foo), [Foo, object]) def test_w_bases(self): class Foo: pass class Bar(Foo): pass self.assertEqual(self._callFUT(Bar), [Bar, Foo, object]) def test_w_diamond(self): class Foo: pass class Bar(Foo): pass class Baz(Foo): pass class Qux(Bar, Baz): pass self.assertEqual(self._callFUT(Qux), [Qux, Bar, Foo, object, Baz, Foo, object]) class Test_ro(unittest.TestCase): maxDiff = None def _callFUT(self, ob, **kwargs): from zope.interface.ro import _legacy_ro return _legacy_ro(ob, **kwargs) def test_w_empty_bases(self): class Foo: pass foo = Foo() foo.__bases__ = () self.assertEqual(self._callFUT(foo), [foo]) def test_w_single_base(self): class Foo: pass self.assertEqual(self._callFUT(Foo), [Foo, object]) def test_w_bases(self): class Foo: pass class Bar(Foo): pass self.assertEqual(self._callFUT(Bar), [Bar, Foo, object]) def test_w_diamond(self): class Foo: pass class Bar(Foo): pass class Baz(Foo): pass class Qux(Bar, Baz): pass self.assertEqual(self._callFUT(Qux), [Qux, Bar, Baz, Foo, object]) def _make_IOErr(self): # This can't be done in the standard C3 ordering. class Foo: def __init__(self, name, *bases): self.__name__ = name self.__bases__ = bases def __repr__(self): # pragma: no cover return self.__name__ # Mimic what classImplements(IOError, IIOError) # does. IEx = Foo('IEx') IStdErr = Foo('IStdErr', IEx) IEnvErr = Foo('IEnvErr', IStdErr) IIOErr = Foo('IIOErr', IEnvErr) IOSErr = Foo('IOSErr', IEnvErr) IOErr = Foo('IOErr', IEnvErr, IIOErr, IOSErr) return IOErr, [IOErr, IIOErr, IOSErr, IEnvErr, IStdErr, IEx] def test_non_orderable(self): IOErr, bases = self._make_IOErr() self.assertEqual(self._callFUT(IOErr), bases) def test_mixed_inheritance_and_implementation(self): # https://github.com/zopefoundation/zope.interface/issues/8 # This test should fail, but doesn't, as described in that issue. # pylint:disable=inherit-non-class from zope.interface import Interface from zope.interface import implementedBy from zope.interface import implementer from zope.interface import providedBy class IFoo(Interface): pass @implementer(IFoo) class ImplementsFoo: pass class ExtendsFoo(ImplementsFoo): pass class ImplementsNothing: pass class ExtendsFooImplementsNothing(ExtendsFoo, ImplementsNothing): pass self.assertEqual( self._callFUT(providedBy(ExtendsFooImplementsNothing())), [implementedBy(ExtendsFooImplementsNothing), implementedBy(ExtendsFoo), implementedBy(ImplementsFoo), IFoo, Interface, implementedBy(ImplementsNothing), implementedBy(object)]) class C3Setting: def __init__(self, setting, value): self._setting = setting self._value = value def __enter__(self): from zope.interface import ro setattr(ro.C3, self._setting.__name__, self._value) def __exit__(self, t, v, tb): from zope.interface import ro setattr(ro.C3, self._setting.__name__, self._setting) class Test_c3_ro(Test_ro): def setUp(self): Test_ro.setUp(self) from zope.testing.loggingsupport import InstalledHandler self.log_handler = handler = InstalledHandler('zope.interface.ro') self.addCleanup(handler.uninstall) def _callFUT(self, ob, **kwargs): from zope.interface.ro import ro return ro(ob, **kwargs) def _make_complex_diamond(self, base): # https://github.com/zopefoundation/zope.interface/issues/21 O = base class F(O): pass class E(O): pass class D(O): pass class C(D, F): pass class B(D, E): pass class A(B, C): pass if hasattr(A, 'mro'): self.assertEqual(A.mro(), self._callFUT(A)) return A def test_complex_diamond_object(self): self._make_complex_diamond(object) def test_complex_diamond_interface(self): from zope.interface import Interface IA = self._make_complex_diamond(Interface) self.assertEqual( [x.__name__ for x in IA.__iro__], ['A', 'B', 'C', 'D', 'E', 'F', 'Interface'] ) def test_complex_diamond_use_legacy_argument(self): from zope.interface import Interface A = self._make_complex_diamond(Interface) legacy_A_iro = self._callFUT(A, use_legacy_ro=True) self.assertNotEqual(A.__iro__, legacy_A_iro) # And logging happened as a side-effect. self._check_handler_complex_diamond() def test_complex_diamond_compare_legacy_argument(self): from zope.interface import Interface A = self._make_complex_diamond(Interface) computed_A_iro = self._callFUT(A, log_changed_ro=True) # It matches, of course, but we did log a warning. self.assertEqual(tuple(computed_A_iro), A.__iro__) self._check_handler_complex_diamond() def _check_handler_complex_diamond(self): handler = self.log_handler self.assertEqual(1, len(handler.records)) record = handler.records[0] self.assertEqual('\n'.join(l.rstrip() for l in record.getMessage().splitlines()), """\ Object has different legacy and C3 MROs: Legacy RO (len=7) C3 RO (len=7; inconsistent=no) ================================================================== zope.interface.tests.test_ro.A zope.interface.tests.test_ro.A zope.interface.tests.test_ro.B zope.interface.tests.test_ro.B - zope.interface.tests.test_ro.E zope.interface.tests.test_ro.C zope.interface.tests.test_ro.C zope.interface.tests.test_ro.D zope.interface.tests.test_ro.D + zope.interface.tests.test_ro.E zope.interface.tests.test_ro.F zope.interface.tests.test_ro.F zope.interface.Interface zope.interface.Interface""") def test_ExtendedPathIndex_implement_thing_implementedby_super(self): # See https://github.com/zopefoundation/zope.interface/pull/182#issuecomment-598754056 from zope.interface import ro # pylint:disable=inherit-non-class class _Based: __bases__ = () def __init__(self, name, bases=(), attrs=None): self.__name__ = name self.__bases__ = bases def __repr__(self): return self.__name__ Interface = _Based('Interface', (), {}) class IPluggableIndex(Interface): pass class ILimitedResultIndex(IPluggableIndex): pass class IQueryIndex(IPluggableIndex): pass class IPathIndex(Interface): pass # A parent class who implements two distinct interfaces whose # only common ancestor is Interface. An easy case. # @implementer(IPathIndex, IQueryIndex) # class PathIndex(object): # pass obj = _Based('object') PathIndex = _Based('PathIndex', (IPathIndex, IQueryIndex, obj)) # Child class that tries to put an interface the parent declares # later ahead of the parent. # @implementer(ILimitedResultIndex, IQueryIndex) # class ExtendedPathIndex(PathIndex): # pass ExtendedPathIndex = _Based('ExtendedPathIndex', (ILimitedResultIndex, IQueryIndex, PathIndex)) # We were able to resolve it, and in exactly the same way as # the legacy RO did, even though it is inconsistent. result = self._callFUT(ExtendedPathIndex, log_changed_ro=True, strict=False) self.assertEqual(result, [ ExtendedPathIndex, ILimitedResultIndex, PathIndex, IPathIndex, IQueryIndex, IPluggableIndex, Interface, obj]) record, = self.log_handler.records self.assertIn('used the legacy', record.getMessage()) with self.assertRaises(ro.InconsistentResolutionOrderError): self._callFUT(ExtendedPathIndex, strict=True) def test_OSError_IOError(self): from zope.interface import providedBy from zope.interface.common import interfaces self.assertEqual( list(providedBy(OSError()).flattened()), [ interfaces.IOSError, interfaces.IIOError, interfaces.IEnvironmentError, interfaces.IStandardError, interfaces.IException, interfaces.Interface, ]) def test_non_orderable(self): import warnings from zope.interface import ro try: # If we've already warned, we must reset that state. del ro.__warningregistry__ except AttributeError: pass with warnings.catch_warnings(): warnings.simplefilter('error') with C3Setting(ro.C3.WARN_BAD_IRO, True), C3Setting(ro.C3.STRICT_IRO, False): with self.assertRaises(ro.InconsistentResolutionOrderWarning): super().test_non_orderable() IOErr, _ = self._make_IOErr() with self.assertRaises(ro.InconsistentResolutionOrderError): self._callFUT(IOErr, strict=True) with C3Setting(ro.C3.TRACK_BAD_IRO, True), C3Setting(ro.C3.STRICT_IRO, False): with warnings.catch_warnings(): warnings.simplefilter('ignore') self._callFUT(IOErr) self.assertIn(IOErr, ro.C3.BAD_IROS) iro = self._callFUT(IOErr, strict=False) legacy_iro = self._callFUT(IOErr, use_legacy_ro=True, strict=False) self.assertEqual(iro, legacy_iro) class TestC3(unittest.TestCase): def _makeOne(self, C, strict=False, base_mros=None): from zope.interface.ro import C3 return C3.resolver(C, strict, base_mros) def test_base_mros_given(self): c3 = self._makeOne(type(self), base_mros={unittest.TestCase: unittest.TestCase.__mro__}) memo = c3.memo self.assertIn(unittest.TestCase, memo) # We used the StaticMRO class self.assertIsNone(memo[unittest.TestCase].had_inconsistency) def test_one_base_optimization(self): c3 = self._makeOne(type(self)) # Even though we didn't call .mro() yet, the MRO has been # computed. self.assertIsNotNone(c3._C3__mro) # pylint:disable=no-member c3._merge = None self.assertEqual(c3.mro(), list(type(self).__mro__)) class Test_ROComparison(unittest.TestCase): class MockC3: direct_inconsistency = False bases_had_inconsistency = False def _makeOne(self, c3=None, c3_ro=(), legacy_ro=()): from zope.interface.ro import _ROComparison return _ROComparison(c3 or self.MockC3(), c3_ro, legacy_ro) def test_inconsistent_label(self): comp = self._makeOne() self.assertEqual('no', comp._inconsistent_label) comp.c3.direct_inconsistency = True self.assertEqual("direct", comp._inconsistent_label) comp.c3.bases_had_inconsistency = True self.assertEqual("direct+bases", comp._inconsistent_label) comp.c3.direct_inconsistency = False self.assertEqual('bases', comp._inconsistent_label) zope.interface-6.4/src/zope/interface/tests/test_sorting.py000066400000000000000000000036161462121350100242440ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2001, 2002 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. # ############################################################################## """Test interface sorting """ import unittest from zope.interface import Interface class I1(Interface): pass class I2(I1): pass class I3(I1): pass class I4(Interface): pass class I5(I4): pass class I6(I2): pass class Test(unittest.TestCase): def test(self): l = [I1, I3, I5, I6, I4, I2] l.sort() self.assertEqual(l, [I1, I2, I3, I4, I5, I6]) def test_w_None(self): l = [I1, None, I3, I5, I6, I4, I2] l.sort() self.assertEqual(l, [I1, I2, I3, I4, I5, I6, None]) def test_w_equal_names(self): # interfaces with equal names but different modules should sort by # module name from zope.interface.tests.m1 import I1 as m1_I1 l = [I1, m1_I1] l.sort() self.assertEqual(l, [m1_I1, I1]) def test_I1_I2(self): self.assertLess(I1.__name__, I2.__name__) self.assertEqual(I1.__module__, I2.__module__) self.assertEqual(I1.__module__, __name__) self.assertLess(I1, I2) def _makeI1(self): class I1(Interface): pass return I1 def test_nested(self): nested_I1 = self._makeI1() self.assertEqual(I1, nested_I1) self.assertEqual(nested_I1, I1) self.assertEqual(hash(I1), hash(nested_I1)) zope.interface-6.4/src/zope/interface/tests/test_verify.py000066400000000000000000000453671462121350100240740ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2001, 2002 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. # ############################################################################## """ zope.interface.verify unit tests """ import unittest # pylint:disable=inherit-non-class,no-method-argument,no-self-argument class Test_verifyClass(unittest.TestCase): verifier = None def setUp(self): self.verifier = self._get_FUT() @classmethod def _get_FUT(cls): from zope.interface.verify import verifyClass return verifyClass _adjust_object_before_verify = lambda self, x: x def _callFUT(self, iface, klass, **kwargs): return self.verifier(iface, self._adjust_object_before_verify(klass), **kwargs) def test_class_doesnt_implement(self): from zope.interface import Interface from zope.interface.exceptions import DoesNotImplement class ICurrent(Interface): pass class Current: pass self.assertRaises(DoesNotImplement, self._callFUT, ICurrent, Current) def test_class_doesnt_implement_but_classImplements_later(self): from zope.interface import Interface from zope.interface import classImplements class ICurrent(Interface): pass class Current: pass classImplements(Current, ICurrent) self._callFUT(ICurrent, Current) def test_class_doesnt_have_required_method_simple(self): from zope.interface import Interface from zope.interface import implementer from zope.interface.exceptions import BrokenImplementation class ICurrent(Interface): def method(): """docstring""" @implementer(ICurrent) class Current: pass self.assertRaises(BrokenImplementation, self._callFUT, ICurrent, Current) def test_class_has_required_method_simple(self): from zope.interface import Interface from zope.interface import implementer class ICurrent(Interface): def method(): """docstring""" @implementer(ICurrent) class Current: def method(self): raise NotImplementedError() self._callFUT(ICurrent, Current) def test_class_doesnt_have_required_method_derived(self): from zope.interface import Interface from zope.interface import implementer from zope.interface.exceptions import BrokenImplementation class IBase(Interface): def method(): """docstring""" class IDerived(IBase): pass @implementer(IDerived) class Current: pass self.assertRaises(BrokenImplementation, self._callFUT, IDerived, Current) def test_class_has_required_method_derived(self): from zope.interface import Interface from zope.interface import implementer class IBase(Interface): def method(): """docstring""" class IDerived(IBase): pass @implementer(IDerived) class Current: def method(self): raise NotImplementedError() self._callFUT(IDerived, Current) def test_method_takes_wrong_arg_names_but_OK(self): # We no longer require names to match. from zope.interface import Interface from zope.interface import implementer class ICurrent(Interface): def method(a): """docstring""" @implementer(ICurrent) class Current: def method(self, b): raise NotImplementedError() self._callFUT(ICurrent, Current) def test_method_takes_not_enough_args(self): from zope.interface import Interface from zope.interface import implementer from zope.interface.exceptions import BrokenMethodImplementation class ICurrent(Interface): def method(a): """docstring""" @implementer(ICurrent) class Current: def method(self): raise NotImplementedError() self.assertRaises(BrokenMethodImplementation, self._callFUT, ICurrent, Current) def test_method_doesnt_take_required_starargs(self): from zope.interface import Interface from zope.interface import implementer from zope.interface.exceptions import BrokenMethodImplementation class ICurrent(Interface): def method(*args): """docstring""" @implementer(ICurrent) class Current: def method(self): raise NotImplementedError() self.assertRaises(BrokenMethodImplementation, self._callFUT, ICurrent, Current) def test_method_doesnt_take_required_only_kwargs(self): from zope.interface import Interface from zope.interface import implementer from zope.interface.exceptions import BrokenMethodImplementation class ICurrent(Interface): def method(**kw): """docstring""" @implementer(ICurrent) class Current: def method(self): raise NotImplementedError() self.assertRaises(BrokenMethodImplementation, self._callFUT, ICurrent, Current) def test_method_takes_extra_arg(self): from zope.interface import Interface from zope.interface import implementer from zope.interface.exceptions import BrokenMethodImplementation class ICurrent(Interface): def method(a): """docstring""" @implementer(ICurrent) class Current: def method(self, a, b): raise NotImplementedError() self.assertRaises(BrokenMethodImplementation, self._callFUT, ICurrent, Current) def test_method_takes_extra_arg_with_default(self): from zope.interface import Interface from zope.interface import implementer class ICurrent(Interface): def method(a): """docstring""" @implementer(ICurrent) class Current: def method(self, a, b=None): raise NotImplementedError() self._callFUT(ICurrent, Current) def test_method_takes_only_positional_args(self): from zope.interface import Interface from zope.interface import implementer class ICurrent(Interface): def method(a): """docstring""" @implementer(ICurrent) class Current: def method(self, *args): raise NotImplementedError() self._callFUT(ICurrent, Current) def test_method_takes_only_kwargs(self): from zope.interface import Interface from zope.interface import implementer from zope.interface.exceptions import BrokenMethodImplementation class ICurrent(Interface): def method(a): """docstring""" @implementer(ICurrent) class Current: def method(self, **kw): raise NotImplementedError() self.assertRaises(BrokenMethodImplementation, self._callFUT, ICurrent, Current) def test_method_takes_extra_starargs(self): from zope.interface import Interface from zope.interface import implementer class ICurrent(Interface): def method(a): """docstring""" @implementer(ICurrent) class Current: def method(self, a, *args): raise NotImplementedError() self._callFUT(ICurrent, Current) def test_method_takes_extra_starargs_and_kwargs(self): from zope.interface import Interface from zope.interface import implementer class ICurrent(Interface): def method(a): """docstring""" @implementer(ICurrent) class Current: def method(self, a, *args, **kw): raise NotImplementedError() self._callFUT(ICurrent, Current) def test_method_doesnt_take_required_positional_and_starargs(self): from zope.interface import Interface from zope.interface import implementer from zope.interface.exceptions import BrokenMethodImplementation class ICurrent(Interface): def method(a, *args): """docstring""" @implementer(ICurrent) class Current: def method(self, a): raise NotImplementedError() self.assertRaises(BrokenMethodImplementation, self._callFUT, ICurrent, Current) def test_method_takes_required_positional_and_starargs(self): from zope.interface import Interface from zope.interface import implementer class ICurrent(Interface): def method(a, *args): """docstring""" @implementer(ICurrent) class Current: def method(self, a, *args): raise NotImplementedError() self._callFUT(ICurrent, Current) def test_method_takes_only_starargs(self): from zope.interface import Interface from zope.interface import implementer class ICurrent(Interface): def method(a, *args): """docstring""" @implementer(ICurrent) class Current: def method(self, *args): raise NotImplementedError() self._callFUT(ICurrent, Current) def test_method_takes_required_kwargs(self): from zope.interface import Interface from zope.interface import implementer class ICurrent(Interface): def method(**kwargs): """docstring""" @implementer(ICurrent) class Current: def method(self, **kw): raise NotImplementedError() self._callFUT(ICurrent, Current) def test_method_takes_positional_plus_required_starargs(self): from zope.interface import Interface from zope.interface import implementer from zope.interface.exceptions import BrokenMethodImplementation class ICurrent(Interface): def method(*args): """docstring""" @implementer(ICurrent) class Current: def method(self, a, *args): raise NotImplementedError() self.assertRaises(BrokenMethodImplementation, self._callFUT, ICurrent, Current) def test_method_doesnt_take_required_kwargs(self): from zope.interface import Interface from zope.interface import implementer from zope.interface.exceptions import BrokenMethodImplementation class ICurrent(Interface): def method(**kwargs): """docstring""" @implementer(ICurrent) class Current: def method(self, a): raise NotImplementedError() self.assertRaises(BrokenMethodImplementation, self._callFUT, ICurrent, Current) def test_class_has_method_for_iface_attr(self): from zope.interface import Attribute from zope.interface import Interface from zope.interface import implementer class ICurrent(Interface): attr = Attribute("The foo Attribute") @implementer(ICurrent) class Current: def attr(self): raise NotImplementedError() self._callFUT(ICurrent, Current) def test_class_has_nonmethod_for_method(self): from zope.interface import Interface from zope.interface import implementer from zope.interface.exceptions import BrokenMethodImplementation class ICurrent(Interface): def method(): """docstring""" @implementer(ICurrent) class Current: method = 1 self.assertRaises(BrokenMethodImplementation, self._callFUT, ICurrent, Current) def test_class_has_attribute_for_attribute(self): from zope.interface import Attribute from zope.interface import Interface from zope.interface import implementer class ICurrent(Interface): attr = Attribute("The foo Attribute") @implementer(ICurrent) class Current: attr = 1 self._callFUT(ICurrent, Current) def test_class_misses_attribute_for_attribute(self): # This check *passes* for verifyClass from zope.interface import Attribute from zope.interface import Interface from zope.interface import implementer class ICurrent(Interface): attr = Attribute("The foo Attribute") @implementer(ICurrent) class Current: pass self._callFUT(ICurrent, Current) def test_w_callable_non_func_method(self): from zope.interface import Interface from zope.interface import implementer from zope.interface.interface import Method class QuasiMethod(Method): def __call__(self, *args, **kw): raise NotImplementedError() class QuasiCallable: def __call__(self, *args, **kw): raise NotImplementedError() class ICurrent(Interface): attr = QuasiMethod('This is callable') @implementer(ICurrent) class Current: attr = QuasiCallable() self._callFUT(ICurrent, Current) def test_w_decorated_method(self): from zope.interface import Interface from zope.interface import implementer def decorator(func): # this is, in fact, zope.proxy.non_overridable return property(lambda self: func.__get__(self)) class ICurrent(Interface): def method(a): """docstring""" @implementer(ICurrent) class Current: @decorator def method(self, a): raise NotImplementedError() self._callFUT(ICurrent, Current) def test_dict_IFullMapping(self): # A dict should be an IFullMapping, but this exposes two # issues. First, on CPython, methods of builtin types are # "method_descriptor" objects, and are harder to introspect. # Second, on PyPy, the signatures can be just plain wrong, # specifying as required arguments that are actually optional. # See https://github.com/zopefoundation/zope.interface/issues/118 from zope.interface.common.mapping import IFullMapping self._callFUT(IFullMapping, dict, tentative=True) def test_list_ISequence(self): # As for test_dict_IFullMapping from zope.interface.common.sequence import ISequence self._callFUT(ISequence, list, tentative=True) def test_tuple_IReadSequence(self): # As for test_dict_IFullMapping from zope.interface.common.sequence import IReadSequence self._callFUT(IReadSequence, tuple, tentative=True) def test_multiple_invalid(self): from zope.interface import Interface from zope.interface import classImplements from zope.interface.exceptions import BrokenImplementation from zope.interface.exceptions import DoesNotImplement from zope.interface.exceptions import MultipleInvalid class ISeveralMethods(Interface): def meth1(arg1): "Method 1" def meth2(arg1): "Method 2" class SeveralMethods: pass with self.assertRaises(MultipleInvalid) as exc: self._callFUT(ISeveralMethods, SeveralMethods) ex = exc.exception self.assertEqual(3, len(ex.exceptions)) self.assertIsInstance(ex.exceptions[0], DoesNotImplement) self.assertIsInstance(ex.exceptions[1], BrokenImplementation) self.assertIsInstance(ex.exceptions[2], BrokenImplementation) # If everything else is correct, only the single error is raised without # the wrapper. classImplements(SeveralMethods, ISeveralMethods) SeveralMethods.meth1 = lambda self, arg1: "Hi" with self.assertRaises(BrokenImplementation): self._callFUT(ISeveralMethods, SeveralMethods) class Test_verifyObject(Test_verifyClass): @classmethod def _get_FUT(cls): from zope.interface.verify import verifyObject return verifyObject def _adjust_object_before_verify(self, target): if isinstance(target, (type, type(OldSkool))): target = target() return target def test_class_misses_attribute_for_attribute(self): # This check *fails* for verifyObject from zope.interface import Attribute from zope.interface import Interface from zope.interface import implementer from zope.interface.exceptions import BrokenImplementation class ICurrent(Interface): attr = Attribute("The foo Attribute") @implementer(ICurrent) class Current: pass self.assertRaises(BrokenImplementation, self._callFUT, ICurrent, Current) def test_module_hit(self): from zope.interface.tests import dummy from zope.interface.tests.idummy import IDummyModule self._callFUT(IDummyModule, dummy) def test_module_miss(self): from zope.interface import Interface from zope.interface.exceptions import DoesNotImplement from zope.interface.tests import dummy # same name, different object class IDummyModule(Interface): pass self.assertRaises(DoesNotImplement, self._callFUT, IDummyModule, dummy) def test_staticmethod_hit_on_class(self): from zope.interface import Interface from zope.interface import provider from zope.interface.verify import verifyObject class IFoo(Interface): def bar(a, b): "The bar method" @provider(IFoo) class Foo: @staticmethod def bar(a, b): raise AssertionError("We're never actually called") # Don't use self._callFUT, we don't want to instantiate the # class. verifyObject(IFoo, Foo) class OldSkool: pass zope.interface-6.4/src/zope/interface/verify.py000066400000000000000000000160721462121350100216620ustar00rootroot00000000000000############################################################################## # # Copyright (c) 2001, 2002 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. # ############################################################################## """Verify interface implementations """ import inspect import sys from types import FunctionType from types import MethodType from zope.interface.exceptions import BrokenImplementation from zope.interface.exceptions import BrokenMethodImplementation from zope.interface.exceptions import DoesNotImplement from zope.interface.exceptions import Invalid from zope.interface.exceptions import MultipleInvalid from zope.interface.interface import Method from zope.interface.interface import fromFunction from zope.interface.interface import fromMethod __all__ = [ 'verifyObject', 'verifyClass', ] # This will be monkey-patched when running under Zope 2, so leave this # here: MethodTypes = (MethodType, ) def _verify(iface, candidate, tentative=False, vtype=None): """ Verify that *candidate* might correctly provide *iface*. This involves: - Making sure the candidate claims that it provides the interface using ``iface.providedBy`` (unless *tentative* is `True`, in which case this step is skipped). This means that the candidate's class declares that it `implements ` the interface, or the candidate itself declares that it `provides ` the interface - Making sure the candidate defines all the necessary methods - Making sure the methods have the correct signature (to the extent possible) - Making sure the candidate defines all the necessary attributes :return bool: Returns a true value if everything that could be checked passed. :raises zope.interface.Invalid: If any of the previous conditions does not hold. .. versionchanged:: 5.0 If multiple methods or attributes are invalid, all such errors are collected and reported. Previously, only the first error was reported. As a special case, if only one such error is present, it is raised alone, like before. """ if vtype == 'c': tester = iface.implementedBy else: tester = iface.providedBy excs = [] if not tentative and not tester(candidate): excs.append(DoesNotImplement(iface, candidate)) for name, desc in iface.namesAndDescriptions(all=True): try: _verify_element(iface, name, desc, candidate, vtype) except Invalid as e: excs.append(e) if excs: if len(excs) == 1: raise excs[0] raise MultipleInvalid(iface, candidate, excs) return True def _verify_element(iface, name, desc, candidate, vtype): # Here the `desc` is either an `Attribute` or `Method` instance try: attr = getattr(candidate, name) except AttributeError: if (not isinstance(desc, Method)) and vtype == 'c': # We can't verify non-methods on classes, since the # class may provide attrs in it's __init__. return # TODO: This should use ``raise...from`` raise BrokenImplementation(iface, desc, candidate) if not isinstance(desc, Method): # If it's not a method, there's nothing else we can test return if inspect.ismethoddescriptor(attr) or inspect.isbuiltin(attr): # The first case is what you get for things like ``dict.pop`` # on CPython (e.g., ``verifyClass(IFullMapping, dict))``). The # second case is what you get for things like ``dict().pop`` on # CPython (e.g., ``verifyObject(IFullMapping, dict()))``. # In neither case can we get a signature, so there's nothing # to verify. Even the inspect module gives up and raises # ValueError: no signature found. The ``__text_signature__`` attribute # isn't typically populated either. # # Note that on PyPy 2 or 3 (up through 7.3 at least), these are # not true for things like ``dict.pop`` (but might be true for C extensions?) return if isinstance(attr, FunctionType): if isinstance(candidate, type) and vtype == 'c': # This is an "unbound method". # Only unwrap this if we're verifying implementedBy; # otherwise we can unwrap @staticmethod on classes that directly # provide an interface. meth = fromFunction(attr, iface, name=name, imlevel=1) else: # Nope, just a normal function meth = fromFunction(attr, iface, name=name) elif (isinstance(attr, MethodTypes) and type(attr.__func__) is FunctionType): meth = fromMethod(attr, iface, name) elif isinstance(attr, property) and vtype == 'c': # Without an instance we cannot be sure it's not a # callable. # TODO: This should probably check inspect.isdatadescriptor(), # a more general form than ``property`` return else: if not callable(attr): raise BrokenMethodImplementation(desc, "implementation is not a method", attr, iface, candidate) # sigh, it's callable, but we don't know how to introspect it, so # we have to give it a pass. return # Make sure that the required and implemented method signatures are # the same. mess = _incompat(desc.getSignatureInfo(), meth.getSignatureInfo()) if mess: raise BrokenMethodImplementation(desc, mess, attr, iface, candidate) def verifyClass(iface, candidate, tentative=False): """ Verify that the *candidate* might correctly provide *iface*. """ return _verify(iface, candidate, tentative, vtype='c') def verifyObject(iface, candidate, tentative=False): return _verify(iface, candidate, tentative, vtype='o') verifyObject.__doc__ = _verify.__doc__ _MSG_TOO_MANY = 'implementation requires too many arguments' def _incompat(required, implemented): #if (required['positional'] != # implemented['positional'][:len(required['positional'])] # and implemented['kwargs'] is None): # return 'imlementation has different argument names' if len(implemented['required']) > len(required['required']): return _MSG_TOO_MANY if ((len(implemented['positional']) < len(required['positional'])) and not implemented['varargs']): return "implementation doesn't allow enough arguments" if required['kwargs'] and not implemented['kwargs']: return "implementation doesn't support keyword arguments" if required['varargs'] and not implemented['varargs']: return "implementation doesn't support variable arguments" zope.interface-6.4/tox.ini000066400000000000000000000041241462121350100156060ustar00rootroot00000000000000# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/c-code [tox] minversion = 4.0 envlist = lint py37,py37-pure py38,py38-pure py39,py39-pure py310,py310-pure py311,py311-pure py312,py312-pure py313,py313-pure pypy3 docs coverage [testenv] usedevelop = true pip_pre = py313: true deps = py37: urllib3 < 2 Sphinx setenv = pure: PURE_PYTHON=1 !pure-!pypy3: PURE_PYTHON=0 ZOPE_INTERFACE_STRICT_IRO=1 py312: VIRTUALENV_PIP=23.1.2 py312: PIP_REQUIRE_VIRTUALENV=0 commands = coverage run -p -m unittest discover -s src {posargs} sphinx-build -b doctest -d {envdir}/.cache/doctrees docs {envdir}/.cache/doctest extras = test docs [testenv:coverage] basepython = python3 allowlist_externals = mkdir deps = coverage py37: urllib3 < 2 setenv = PURE_PYTHON=1 commands = mkdir -p {toxinidir}/parts/htmlcov coverage combine coverage html -i coverage report -i -m --fail-under=99 depends = py37,py37-pure,py38,py38-pure,py39,py39-pure,py310,py310-pure,py311,py311-pure,pypy,pypy3,docs parallel_show_output = true [testenv:release-check] description = ensure that the distribution is ready to release basepython = python3 skip_install = true deps = twine build check-manifest check-python-versions >= 0.20.0 wheel commands_pre = commands = check-manifest check-python-versions --only setup.py,tox.ini,.github/workflows/tests.yml python -m build --sdist --no-isolation twine check dist/* [testenv:lint] basepython = python3 skip_install = true deps = isort flake8 commands = isort --check-only --diff {toxinidir}/src {toxinidir}/setup.py flake8 src setup.py [testenv:isort-apply] basepython = python3 skip_install = true commands_pre = deps = isort commands = isort {toxinidir}/src {toxinidir}/setup.py [] [testenv:docs] basepython = python3 skip_install = false 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