├── .editorconfig
├── .github
└── workflows
│ ├── pre-commit.yml
│ └── tests.yml
├── .gitignore
├── .manylinux-install.sh
├── .manylinux.sh
├── .meta.toml
├── .pre-commit-config.yaml
├── .readthedocs.yaml
├── CHANGES.rst
├── CONTRIBUTING.md
├── COPYRIGHT.txt
├── LICENSE.txt
├── MANIFEST.in
├── README.rst
├── benchmarks
├── .gitignore
└── micro.py
├── build.cmd
├── buildout.cfg
├── docs
├── Makefile
├── README.rst
├── README.ru.rst
├── _static
│ └── .gitignore
├── adapter.rst
├── adapter.ru.rst
├── api
│ ├── adapters.rst
│ ├── common.rst
│ ├── components.rst
│ ├── declarations.rst
│ ├── index.rst
│ ├── ro.rst
│ └── specifications.rst
├── changes.rst
├── conf.py
├── foodforthought.rst
├── hacking.rst
├── human.rst
├── human.ru.rst
├── index.rst
├── make.bat
├── requirements.txt
└── verify.rst
├── pyproject.toml
├── setup.cfg
├── setup.py
├── src
└── zope
│ ├── __init__.py
│ └── interface
│ ├── __init__.py
│ ├── _compat.py
│ ├── _flatten.py
│ ├── _zope_interface_coptimizations.c
│ ├── adapter.py
│ ├── advice.py
│ ├── common
│ ├── __init__.py
│ ├── builtins.py
│ ├── collections.py
│ ├── idatetime.py
│ ├── interfaces.py
│ ├── io.py
│ ├── mapping.py
│ ├── numbers.py
│ ├── sequence.py
│ └── tests
│ │ ├── __init__.py
│ │ ├── basemapping.py
│ │ ├── test_builtins.py
│ │ ├── test_collections.py
│ │ ├── test_idatetime.py
│ │ ├── test_import_interfaces.py
│ │ ├── test_io.py
│ │ └── test_numbers.py
│ ├── declarations.py
│ ├── document.py
│ ├── exceptions.py
│ ├── interface.py
│ ├── interfaces.py
│ ├── registry.py
│ ├── ro.py
│ ├── tests
│ ├── __init__.py
│ ├── advisory_testing.py
│ ├── dummy.py
│ ├── idummy.py
│ ├── m1.py
│ ├── odd.py
│ ├── test_adapter.py
│ ├── test_advice.py
│ ├── test_compile_flags.py
│ ├── test_declarations.py
│ ├── test_document.py
│ ├── test_element.py
│ ├── test_exceptions.py
│ ├── test_interface.py
│ ├── test_interfaces.py
│ ├── test_odd_declarations.py
│ ├── test_registry.py
│ ├── test_ro.py
│ ├── test_sorting.py
│ └── test_verify.py
│ └── verify.py
└── tox.ini
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Generated from:
2 | # https://github.com/zopefoundation/meta/tree/master/config/c-code
3 | #
4 | # EditorConfig Configuration file, for more details see:
5 | # http://EditorConfig.org
6 | # EditorConfig is a convention description, that could be interpreted
7 | # by multiple editors to enforce common coding conventions for specific
8 | # file types
9 |
10 | # top-most EditorConfig file:
11 | # Will ignore other EditorConfig files in Home directory or upper tree level.
12 | root = true
13 |
14 |
15 | [*] # For All Files
16 | # Unix-style newlines with a newline ending every file
17 | end_of_line = lf
18 | insert_final_newline = true
19 | trim_trailing_whitespace = true
20 | # Set default charset
21 | charset = utf-8
22 | # Indent style default
23 | indent_style = space
24 | # Max Line Length - a hard line wrap, should be disabled
25 | max_line_length = off
26 |
27 | [*.{py,cfg,ini}]
28 | # 4 space indentation
29 | indent_size = 4
30 |
31 | [*.{yml,zpt,pt,dtml,zcml}]
32 | # 2 space indentation
33 | indent_size = 2
34 |
35 | [{Makefile,.gitmodules}]
36 | # Tab indentation (no size specified, but view as 4 spaces)
37 | indent_style = tab
38 | indent_size = unset
39 | tab_width = unset
40 |
--------------------------------------------------------------------------------
/.github/workflows/pre-commit.yml:
--------------------------------------------------------------------------------
1 | # Generated from:
2 | # https://github.com/zopefoundation/meta/tree/master/config/c-code
3 | name: pre-commit
4 |
5 | on:
6 | pull_request:
7 | push:
8 | branches:
9 | - master
10 | # Allow to run this workflow manually from the Actions tab
11 | workflow_dispatch:
12 |
13 | env:
14 | FORCE_COLOR: 1
15 |
16 | jobs:
17 | pre-commit:
18 | permissions:
19 | contents: read
20 | pull-requests: write
21 | name: linting
22 | runs-on: ubuntu-latest
23 | steps:
24 | - uses: actions/checkout@v4
25 | - uses: actions/setup-python@v5
26 | with:
27 | python-version: 3.x
28 | - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd #v3.0.1
29 | with:
30 | extra_args: --all-files --show-diff-on-failure
31 | env:
32 | PRE_COMMIT_COLOR: always
33 | - uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 #v1.1.0
34 | if: always()
35 | with:
36 | msg: Apply pre-commit code formatting
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated from:
2 | # https://github.com/zopefoundation/meta/tree/master/config/c-code
3 | *.dll
4 | *.egg-info/
5 | *.profraw
6 | *.pyc
7 | *.pyo
8 | *.so
9 | .coverage
10 | .coverage.*
11 | .eggs/
12 | .installed.cfg
13 | .mr.developer.cfg
14 | .tox/
15 | .vscode/
16 | __pycache__/
17 | bin/
18 | build/
19 | coverage.xml
20 | develop-eggs/
21 | develop/
22 | dist/
23 | docs/_build
24 | eggs/
25 | etc/
26 | lib/
27 | lib64
28 | log/
29 | parts/
30 | pyvenv.cfg
31 | testing.log
32 | var/
33 |
--------------------------------------------------------------------------------
/.manylinux-install.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Generated from:
3 | # https://github.com/zopefoundation/meta/tree/master/config/c-code
4 |
5 | set -e -x
6 |
7 | # Running inside docker
8 | # Set a cache directory for pip. This was
9 | # mounted to be the same as it is outside docker so it
10 | # can be persisted.
11 | export XDG_CACHE_HOME="/cache"
12 | # XXX: This works for macOS, where everything bind-mounted
13 | # is seen as owned by root in the container. But when the host is Linux
14 | # the actual UIDs come through to the container, triggering
15 | # pip to disable the cache when it detects that the owner doesn't match.
16 | # The below is an attempt to fix that, taken from bcrypt. It seems to work on
17 | # Github Actions.
18 | if [ -n "$GITHUB_ACTIONS" ]; then
19 | echo Adjusting pip cache permissions
20 | mkdir -p $XDG_CACHE_HOME/pip
21 | chown -R $(whoami) $XDG_CACHE_HOME
22 | fi
23 | ls -ld /cache
24 | ls -ld /cache/pip
25 |
26 | # We need some libraries because we build wheels from scratch:
27 | yum -y install libffi-devel
28 |
29 | tox_env_map() {
30 | case $1 in
31 | *"cp39"*) echo 'py39';;
32 | *"cp310"*) echo 'py310';;
33 | *"cp311"*) echo 'py311';;
34 | *"cp312"*) echo 'py312';;
35 | *"cp313"*) echo 'py313';;
36 | *"cp314"*) echo 'py314';;
37 | *) echo 'py';;
38 | esac
39 | }
40 |
41 | # Compile wheels
42 | for PYBIN in /opt/python/*/bin; do
43 | if \
44 | [[ "${PYBIN}" == *"cp39/"* ]] || \
45 | [[ "${PYBIN}" == *"cp310/"* ]] || \
46 | [[ "${PYBIN}" == *"cp311/"* ]] || \
47 | [[ "${PYBIN}" == *"cp312/"* ]] || \
48 | [[ "${PYBIN}" == *"cp313/"* ]] || \
49 | [[ "${PYBIN}" == *"cp314/"* ]] ; then
50 | if [[ "${PYBIN}" == *"cp314/"* ]] ; then
51 | "${PYBIN}/pip" install --pre -e /io/
52 | "${PYBIN}/pip" wheel /io/ --pre -w wheelhouse/
53 | else
54 | "${PYBIN}/pip" install -e /io/
55 | "${PYBIN}/pip" wheel /io/ -w wheelhouse/
56 | fi
57 | if [ `uname -m` == 'aarch64' ]; then
58 | cd /io/
59 | ${PYBIN}/pip install tox
60 | TOXENV=$(tox_env_map "${PYBIN}")
61 | ${PYBIN}/tox -e ${TOXENV}
62 | cd ..
63 | fi
64 | rm -rf /io/build /io/*.egg-info
65 | fi
66 | done
67 |
68 | # Bundle external shared libraries into the wheels
69 | for whl in wheelhouse/zope.interface*.whl; do
70 | auditwheel repair "$whl" -w /io/wheelhouse/
71 | done
72 |
--------------------------------------------------------------------------------
/.manylinux.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Generated from:
3 | # https://github.com/zopefoundation/meta/tree/master/config/c-code
4 |
5 | set -e -x
6 |
7 | # Mount the current directory as /io
8 | # Mount the pip cache directory as /cache
9 | # `pip cache` requires pip 20.1
10 | echo Setting up caching
11 | python --version
12 | python -mpip --version
13 | LCACHE="$(dirname `python -mpip cache dir`)"
14 | echo Sharing pip cache at $LCACHE $(ls -ld $LCACHE)
15 |
16 | docker run --rm -e GITHUB_ACTIONS -v "$(pwd)":/io -v "$LCACHE:/cache" $DOCKER_IMAGE $PRE_CMD /io/.manylinux-install.sh
17 |
--------------------------------------------------------------------------------
/.meta.toml:
--------------------------------------------------------------------------------
1 | # Generated from:
2 | # https://github.com/zopefoundation/meta/tree/master/config/c-code
3 | [meta]
4 | template = "c-code"
5 | commit-id = "3c1c588c"
6 |
7 | [python]
8 | with-pypy = true
9 | with-sphinx-doctests = true
10 | with-windows = true
11 | with-future-python = true
12 | with-docs = true
13 | with-macos = false
14 |
15 | [tox]
16 | use-flake8 = true
17 | testenv-commands = [
18 | "pip install -U -e .[test]",
19 | "coverage run -p -m unittest discover -s src {posargs}",
20 | ]
21 | testenv-setenv = [
22 | "ZOPE_INTERFACE_STRICT_IRO=1",
23 | ]
24 | coverage-command = "coverage combine"
25 | coverage-additional = [
26 | "depends = py38,py38-pure,py39,py39-pure,py310,py310-pure,py311,py311-pure,py312,py312-pure,py313,py313-pure,pypy3,docs",
27 | "parallel_show_output = true",
28 | ]
29 |
30 | [flake8]
31 | additional-config = [
32 | "ignore =",
33 | " # module level import not at top of file: we are avoiding cycles",
34 | " E402,",
35 | " # import not used: we are publishing APIs, at least from __init__.py",
36 | " F401,",
37 | " # line break after binary operator",
38 | " W504",
39 | ]
40 |
41 | [coverage]
42 | fail-under = 98
43 |
44 | [manifest]
45 | additional-rules = [
46 | "include *.yaml",
47 | "include *.cmd",
48 | "include *.sh",
49 | "recursive-include benchmarks *.py",
50 | "recursive-include docs *.bat",
51 | "recursive-include docs *.py",
52 | "recursive-include docs *.rst",
53 | "recursive-include docs Makefile",
54 | ]
55 |
56 | [check-manifest]
57 | additional-ignores = [
58 | "docs/_build/doctest/output.txt",
59 | "docs/_build/html/_sources/*",
60 | "docs/_build/html/_sources/api/*",
61 | "docs/_build/html/_static/scripts/*",
62 | ]
63 |
64 | [github-actions]
65 | additional-install = [
66 | "- name: Install zope.interface",
67 | " # ``python -m unittest discover`` only works with editable",
68 | " # installs, so we have to duplicate some work and can't",
69 | " # install the built wheel. (zope.testrunner",
70 | " # works fine with non-editable installs.)",
71 | " run: |",
72 | " pip install -U wheel",
73 | " pip install -U --no-binary :all: coverage[toml]",
74 | " # Unzip into src/ so that testrunner can find the .so files",
75 | " # when we ask it to load tests from that directory. This",
76 | " # might also save some build time?",
77 | " unzip -n dist/zope.interface-*whl -d src",
78 | " pip install -U -e .[test]",
79 | "- name: Run tests with C extensions",
80 | " if: ${{ !startsWith(matrix.python-version, 'pypy') }}",
81 | " run: |",
82 | " coverage run -p -m unittest discover -s src",
83 | "- name: Run tests without C extensions",
84 | " run: |",
85 | " coverage run -p -m unittest discover -s src",
86 | " env:",
87 | " PURE_PYTHON: 1",
88 | "- name: Report Coverage",
89 | " run: |",
90 | " coverage combine",
91 | " coverage report --ignore-errors --show-missing",
92 | ]
93 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | # Generated from:
2 | # https://github.com/zopefoundation/meta/tree/master/config/c-code
3 | minimum_pre_commit_version: '3.6'
4 | repos:
5 | - repo: https://github.com/pycqa/isort
6 | rev: "6.0.0"
7 | hooks:
8 | - id: isort
9 | - repo: https://github.com/hhatto/autopep8
10 | rev: "v2.3.2"
11 | hooks:
12 | - id: autopep8
13 | args: [--in-place, --aggressive, --aggressive]
14 | - repo: https://github.com/asottile/pyupgrade
15 | rev: v3.19.1
16 | hooks:
17 | - id: pyupgrade
18 | args: [--py39-plus]
19 | - repo: https://github.com/isidentical/teyit
20 | rev: 0.4.3
21 | hooks:
22 | - id: teyit
23 | - repo: https://github.com/PyCQA/flake8
24 | rev: "7.1.1"
25 | hooks:
26 | - id: flake8
27 | additional_dependencies:
28 | - flake8-debugger == 4.1.2
29 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # Generated from:
2 | # https://github.com/zopefoundation/meta/tree/master/config/c-code
3 | # Read the Docs configuration file
4 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
5 |
6 | # Required
7 | version: 2
8 |
9 | # Set the version of Python and other tools you might need
10 | build:
11 | os: ubuntu-22.04
12 | tools:
13 | python: "3.11"
14 |
15 | # Build documentation in the docs/ directory with Sphinx
16 | sphinx:
17 | configuration: docs/conf.py
18 |
19 | # We recommend specifying your dependencies to enable reproducible builds:
20 | # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
21 | python:
22 | install:
23 | - requirements: docs/requirements.txt
24 | - method: pip
25 | path: .
26 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 |
5 | # Contributing to zopefoundation projects
6 |
7 | The projects under the zopefoundation GitHub organization are open source and
8 | welcome contributions in different forms:
9 |
10 | * bug reports
11 | * code improvements and bug fixes
12 | * documentation improvements
13 | * pull request reviews
14 |
15 | For any changes in the repository besides trivial typo fixes you are required
16 | to sign the contributor agreement. See
17 | https://www.zope.dev/developer/becoming-a-committer.html for details.
18 |
19 | Please visit our [Developer
20 | Guidelines](https://www.zope.dev/developer/guidelines.html) if you'd like to
21 | contribute code changes and our [guidelines for reporting
22 | bugs](https://www.zope.dev/developer/reporting-bugs.html) if you want to file a
23 | bug report.
24 |
--------------------------------------------------------------------------------
/COPYRIGHT.txt:
--------------------------------------------------------------------------------
1 | Zope Foundation and Contributors
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Zope Public License (ZPL) Version 2.1
2 |
3 | A copyright notice accompanies this license document that identifies the
4 | copyright holders.
5 |
6 | This license has been certified as open source. It has also been designated as
7 | GPL compatible by the Free Software Foundation (FSF).
8 |
9 | Redistribution and use in source and binary forms, with or without
10 | modification, are permitted provided that the following conditions are met:
11 |
12 | 1. Redistributions in source code must retain the accompanying copyright
13 | notice, this list of conditions, and the following disclaimer.
14 |
15 | 2. Redistributions in binary form must reproduce the accompanying copyright
16 | notice, this list of conditions, and the following disclaimer in the
17 | documentation and/or other materials provided with the distribution.
18 |
19 | 3. Names of the copyright holders must not be used to endorse or promote
20 | products derived from this software without prior written permission from the
21 | copyright holders.
22 |
23 | 4. The right to distribute this software or to use it for any purpose does not
24 | give you the right to use Servicemarks (sm) or Trademarks (tm) of the
25 | copyright
26 | holders. Use of them is covered by separate agreement with the copyright
27 | holders.
28 |
29 | 5. If any files are modified, you must cause the modified files to carry
30 | prominent notices stating that you changed the files and the date of any
31 | change.
32 |
33 | Disclaimer
34 |
35 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
36 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
38 | EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
39 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
40 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
41 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
42 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
43 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
44 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | # Generated from:
2 | # https://github.com/zopefoundation/meta/tree/master/config/c-code
3 | include *.md
4 | include *.rst
5 | include *.txt
6 | include buildout.cfg
7 | include tox.ini
8 | include .pre-commit-config.yaml
9 |
10 | recursive-include docs *.py
11 | recursive-include docs *.rst
12 | recursive-include docs *.txt
13 | recursive-include docs Makefile
14 |
15 | recursive-include src *.py
16 | include *.yaml
17 | include *.cmd
18 | include *.sh
19 | recursive-include benchmarks *.py
20 | recursive-include docs *.bat
21 | recursive-include docs *.py
22 | recursive-include docs *.rst
23 | recursive-include docs Makefile
24 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | ====================
2 | ``zope.interface``
3 | ====================
4 |
5 | .. image:: https://img.shields.io/pypi/v/zope.interface.svg
6 | :target: https://pypi.python.org/pypi/zope.interface/
7 | :alt: Latest Version
8 |
9 | .. image:: https://img.shields.io/pypi/pyversions/zope.interface.svg
10 | :target: https://pypi.org/project/zope.interface/
11 | :alt: Supported Python versions
12 |
13 | .. image:: https://github.com/zopefoundation/zope.interface/actions/workflows/tests.yml/badge.svg
14 | :target: https://github.com/zopefoundation/zope.interface/actions/workflows/tests.yml
15 |
16 | .. image:: https://readthedocs.org/projects/zopeinterface/badge/?version=latest
17 | :target: https://zopeinterface.readthedocs.io/en/latest/
18 | :alt: Documentation Status
19 |
20 | This package is intended to be independently reusable in any Python
21 | project. It is maintained by the `Zope Toolkit project
22 | `_.
23 |
24 | This package provides an implementation of "object interfaces" for Python.
25 | Interfaces are a mechanism for labeling objects as conforming to a given
26 | API or contract. So, this package can be considered as implementation of
27 | the `Design By Contract`_ methodology support in Python.
28 |
29 | .. _Design By Contract: http://en.wikipedia.org/wiki/Design_by_contract
30 |
31 | For detailed documentation, please see https://zopeinterface.readthedocs.io/en/latest/
32 |
--------------------------------------------------------------------------------
/benchmarks/.gitignore:
--------------------------------------------------------------------------------
1 | *.json
2 |
--------------------------------------------------------------------------------
/benchmarks/micro.py:
--------------------------------------------------------------------------------
1 | import pyperf
2 |
3 | from zope.interface import Interface
4 | from zope.interface import classImplements
5 | from zope.interface import implementedBy
6 | from zope.interface.interface import InterfaceClass
7 | from zope.interface.registry import Components
8 |
9 |
10 | # Long, mostly similar names are a worst case for equality
11 | # comparisons.
12 | ifaces = [
13 | InterfaceClass('I' + ('0' * 20) + str(i), (Interface,), {})
14 | for i in range(100)
15 | ]
16 |
17 |
18 | class IWideInheritance(*ifaces):
19 | """
20 | Inherits from 100 unrelated interfaces.
21 | """
22 |
23 |
24 | class WideInheritance:
25 | pass
26 |
27 |
28 | classImplements(WideInheritance, IWideInheritance)
29 |
30 |
31 | def make_deep_inheritance():
32 | children = []
33 | base = Interface
34 | for iface in ifaces:
35 | child = InterfaceClass('IDerived' + base.__name__, (iface, base,), {})
36 | base = child
37 | children.append(child)
38 | return children
39 |
40 |
41 | deep_ifaces = make_deep_inheritance()
42 |
43 |
44 | class DeepestInheritance:
45 | pass
46 |
47 |
48 | classImplements(DeepestInheritance, deep_ifaces[-1])
49 |
50 |
51 | class ImplementsNothing:
52 | pass
53 |
54 |
55 | class HasConformReturnNone:
56 | def __conform__(self, iface):
57 | return None
58 |
59 |
60 | class HasConformReturnObject:
61 | def __conform__(self, iface):
62 | return self
63 |
64 |
65 | def make_implementer(iface):
66 | c = type('Implementer' + iface.__name__, (object,), {})
67 | classImplements(c, iface)
68 | return c
69 |
70 |
71 | implementers = [
72 | make_implementer(iface)
73 | for iface in ifaces
74 | ]
75 |
76 | providers = [
77 | implementer()
78 | for implementer in implementers
79 | ]
80 |
81 | INNER = 100
82 |
83 |
84 | def bench_in(loops, o):
85 | t0 = pyperf.perf_counter()
86 | for _ in range(loops):
87 | for _ in range(INNER):
88 | o.__contains__(Interface)
89 |
90 | return pyperf.perf_counter() - t0
91 |
92 |
93 | def bench_sort(loops, objs):
94 | import random
95 | rand = random.Random(8675309)
96 |
97 | shuffled = list(objs)
98 | rand.shuffle(shuffled)
99 |
100 | t0 = pyperf.perf_counter()
101 | for _ in range(loops):
102 | for _ in range(INNER):
103 | sorted(shuffled)
104 |
105 | return pyperf.perf_counter() - t0
106 |
107 |
108 | def bench_query_adapter(loops, components, objs=providers):
109 | components_queryAdapter = components.queryAdapter
110 | # One time through to prime the caches
111 | for iface in ifaces:
112 | for provider in providers:
113 | components_queryAdapter(provider, iface)
114 |
115 | t0 = pyperf.perf_counter()
116 | for _ in range(loops):
117 | for iface in ifaces:
118 | for provider in objs:
119 | components_queryAdapter(provider, iface)
120 | return pyperf.perf_counter() - t0
121 |
122 |
123 | def bench_getattr(loops, name, get=getattr):
124 | t0 = pyperf.perf_counter()
125 | for _ in range(loops):
126 | for _ in range(INNER):
127 | get(Interface, name) # 1
128 | get(Interface, name) # 2
129 | get(Interface, name) # 3
130 | get(Interface, name) # 4
131 | get(Interface, name) # 5
132 | get(Interface, name) # 6
133 | get(Interface, name) # 7
134 | get(Interface, name) # 8
135 | get(Interface, name) # 9
136 | get(Interface, name) # 10
137 | return pyperf.perf_counter() - t0
138 |
139 |
140 | def bench_iface_call_no_conform_no_alternate_not_provided(loops):
141 | inst = ImplementsNothing()
142 | t0 = pyperf.perf_counter()
143 | for _ in range(loops):
144 | for _ in range(INNER):
145 | for iface in ifaces:
146 | try:
147 | iface(inst)
148 | except TypeError:
149 | pass
150 | else:
151 | raise TypeError("Should have failed")
152 | return pyperf.perf_counter() - t0
153 |
154 |
155 | def bench_iface_call_no_conform_w_alternate_not_provided(loops):
156 | inst = ImplementsNothing()
157 | t0 = pyperf.perf_counter()
158 | for _ in range(loops):
159 | for _ in range(INNER):
160 | for iface in ifaces:
161 | iface(inst, 42)
162 | return pyperf.perf_counter() - t0
163 |
164 |
165 | def bench_iface_call_w_conform_return_none_not_provided(loops):
166 | inst = HasConformReturnNone()
167 | t0 = pyperf.perf_counter()
168 | for _ in range(loops):
169 | for _ in range(INNER):
170 | for iface in ifaces:
171 | iface(inst, 42)
172 | return pyperf.perf_counter() - t0
173 |
174 |
175 | def bench_iface_call_w_conform_return_non_none_not_provided(loops):
176 | inst = HasConformReturnObject()
177 | t0 = pyperf.perf_counter()
178 | for _ in range(loops):
179 | for _ in range(INNER):
180 | for iface in ifaces:
181 | iface(inst)
182 | return pyperf.perf_counter() - t0
183 |
184 |
185 | def _bench_iface_call_simple(loops, inst):
186 | t0 = pyperf.perf_counter()
187 | for _ in range(loops):
188 | for _ in range(INNER):
189 | for iface in ifaces:
190 | iface(inst)
191 | return pyperf.perf_counter() - t0
192 |
193 |
194 | def bench_iface_call_no_conform_provided_wide(loops):
195 | return _bench_iface_call_simple(loops, WideInheritance())
196 |
197 |
198 | def bench_iface_call_no_conform_provided_deep(loops):
199 | return _bench_iface_call_simple(loops, DeepestInheritance())
200 |
201 |
202 | runner = pyperf.Runner()
203 |
204 | runner.bench_time_func(
205 | 'call interface (provides; deep)',
206 | bench_iface_call_no_conform_provided_deep,
207 | inner_loops=INNER * len(ifaces)
208 | )
209 |
210 | runner.bench_time_func(
211 | 'call interface (provides; wide)',
212 | bench_iface_call_no_conform_provided_wide,
213 | inner_loops=INNER * len(ifaces)
214 | )
215 |
216 | runner.bench_time_func(
217 | 'call interface (no alternate, no conform, not provided)',
218 | bench_iface_call_no_conform_no_alternate_not_provided,
219 | inner_loops=INNER * len(ifaces)
220 | )
221 |
222 | runner.bench_time_func(
223 | 'call interface (alternate, no conform, not provided)',
224 | bench_iface_call_no_conform_w_alternate_not_provided,
225 | inner_loops=INNER * len(ifaces)
226 | )
227 |
228 | runner.bench_time_func(
229 | 'call interface (no alternate, valid conform, not provided)',
230 | bench_iface_call_w_conform_return_non_none_not_provided,
231 | inner_loops=INNER * len(ifaces)
232 | )
233 |
234 | runner.bench_time_func(
235 | 'call interface (alternate, invalid conform, not provided)',
236 | bench_iface_call_w_conform_return_none_not_provided,
237 | inner_loops=INNER * len(ifaces)
238 | )
239 |
240 | runner.bench_time_func(
241 | 'read __module__', # stored in C, accessed through __getattribute__
242 | bench_getattr,
243 | '__module__',
244 | inner_loops=INNER * 10
245 | )
246 |
247 | runner.bench_time_func(
248 | 'read __name__', # stored in C, accessed through PyMemberDef
249 | bench_getattr,
250 | '__name__',
251 | inner_loops=INNER * 10
252 | )
253 |
254 | runner.bench_time_func(
255 | 'read __doc__', # stored in Python instance dictionary directly
256 | bench_getattr,
257 | '__doc__',
258 | inner_loops=INNER * 10
259 | )
260 |
261 | runner.bench_time_func(
262 | 'read providedBy', # from the class, wrapped into a method object.
263 | bench_getattr,
264 | 'providedBy',
265 | inner_loops=INNER * 10
266 | )
267 |
268 | runner.bench_time_func(
269 | 'query adapter (no registrations)',
270 | bench_query_adapter,
271 | Components(),
272 | inner_loops=1
273 | )
274 |
275 |
276 | def populate_components():
277 | def factory(o):
278 | return 42
279 |
280 | pop_components = Components()
281 | for iface in ifaces:
282 | for other_iface in ifaces:
283 | pop_components.registerAdapter(
284 | factory, (iface,), other_iface, event=False)
285 |
286 | return pop_components
287 |
288 |
289 | runner.bench_time_func(
290 | 'query adapter (all trivial registrations)',
291 | bench_query_adapter,
292 | populate_components(),
293 | inner_loops=1
294 | )
295 |
296 | runner.bench_time_func(
297 | 'query adapter (all trivial registrations, wide inheritance)',
298 | bench_query_adapter,
299 | populate_components(),
300 | [WideInheritance()],
301 | inner_loops=1
302 | )
303 |
304 | runner.bench_time_func(
305 | 'query adapter (all trivial registrations, deep inheritance)',
306 | bench_query_adapter,
307 | populate_components(),
308 | [DeepestInheritance()],
309 | inner_loops=1
310 | )
311 |
312 | runner.bench_time_func(
313 | 'sort interfaces',
314 | bench_sort,
315 | ifaces,
316 | inner_loops=INNER,
317 | )
318 |
319 | runner.bench_time_func(
320 | 'sort implementedBy',
321 | bench_sort,
322 | [implementedBy(p) for p in implementers],
323 | inner_loops=INNER,
324 | )
325 |
326 | runner.bench_time_func(
327 | 'sort mixed',
328 | bench_sort,
329 | [implementedBy(p) for p in implementers] + ifaces,
330 | inner_loops=INNER,
331 | )
332 |
333 | runner.bench_time_func(
334 | 'contains (empty dict)',
335 | bench_in,
336 | {},
337 | inner_loops=INNER
338 | )
339 |
340 | runner.bench_time_func(
341 | 'contains (populated dict: interfaces)',
342 | bench_in,
343 | {k: k for k in ifaces},
344 | inner_loops=INNER
345 | )
346 |
347 | runner.bench_time_func(
348 | 'contains (populated list: interfaces)',
349 | bench_in,
350 | ifaces,
351 | inner_loops=INNER
352 | )
353 |
354 | runner.bench_time_func(
355 | 'contains (populated dict: implementedBy)',
356 | bench_in,
357 | {implementedBy(p): 1 for p in implementers},
358 | inner_loops=INNER
359 | )
360 |
361 | runner.bench_time_func(
362 | 'contains (populated list: implementedBy)',
363 | bench_in,
364 | [implementedBy(p) for p in implementers],
365 | inner_loops=INNER
366 | )
367 |
--------------------------------------------------------------------------------
/build.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 | :: To build extensions for 64 bit Python 3, we need to configure environment
3 | :: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of:
4 | :: MS Windows SDK for Windows 7 and .NET Framework 4
5 | ::
6 | :: More details at:
7 | :: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows
8 |
9 | IF "%DISTUTILS_USE_SDK%"=="1" (
10 | ECHO Configuring environment to build with MSVC on a 64bit architecture
11 | ECHO Using Windows SDK 7.1
12 | "C:\Program Files\Microsoft SDKs\Windows\v7.1\Setup\WindowsSdkVer.exe" -q -version:v7.1
13 | CALL "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 /release
14 | SET MSSdk=1
15 | REM Need the following to allow tox to see the SDK compiler
16 | SET TOX_TESTENV_PASSENV=DISTUTILS_USE_SDK MSSdk INCLUDE LIB
17 | ) ELSE (
18 | ECHO Using default MSVC build environment
19 | )
20 |
21 | CALL %*
22 |
--------------------------------------------------------------------------------
/buildout.cfg:
--------------------------------------------------------------------------------
1 | [buildout]
2 | develop = .
3 | parts = test python
4 |
5 | [test]
6 | recipe = zc.recipe.testrunner
7 | eggs = zope.interface
8 | zope.event
9 |
10 | [python]
11 | recipe = zc.recipe.egg
12 | eggs = zope.interface
13 | zope.event
14 | interpreter = python
15 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # Internal variables.
11 | PAPEROPT_a4 = -D latex_paper_size=a4
12 | PAPEROPT_letter = -D latex_paper_size=letter
13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
14 | # the i18n builder cannot share the environment and doctrees with the others
15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
16 |
17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
18 |
19 | help:
20 | @echo "Please use \`make ' where is one of"
21 | @echo " html to make standalone HTML files"
22 | @echo " dirhtml to make HTML files named index.html in directories"
23 | @echo " singlehtml to make a single large HTML file"
24 | @echo " pickle to make pickle files"
25 | @echo " json to make JSON files"
26 | @echo " htmlhelp to make HTML files and a HTML help project"
27 | @echo " qthelp to make HTML files and a qthelp project"
28 | @echo " devhelp to make HTML files and a Devhelp project"
29 | @echo " epub to make an epub"
30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
31 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
32 | @echo " text to make text files"
33 | @echo " man to make manual pages"
34 | @echo " texinfo to make Texinfo files"
35 | @echo " info to make Texinfo files and run them through makeinfo"
36 | @echo " gettext to make PO message catalogs"
37 | @echo " changes to make an overview of all changed/added/deprecated items"
38 | @echo " linkcheck to check all external links for integrity"
39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
40 |
41 | clean:
42 | -rm -rf $(BUILDDIR)/*
43 |
44 | html:
45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
46 | @echo
47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
48 |
49 | dirhtml:
50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
51 | @echo
52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
53 |
54 | singlehtml:
55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
56 | @echo
57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
58 |
59 | pickle:
60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
61 | @echo
62 | @echo "Build finished; now you can process the pickle files."
63 |
64 | json:
65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
66 | @echo
67 | @echo "Build finished; now you can process the JSON files."
68 |
69 | htmlhelp:
70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
71 | @echo
72 | @echo "Build finished; now you can run HTML Help Workshop with the" \
73 | ".hhp project file in $(BUILDDIR)/htmlhelp."
74 |
75 | qthelp:
76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
77 | @echo
78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/zopeinterface.qhcp"
81 | @echo "To view the help file:"
82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/zopeinterface.qhc"
83 |
84 | devhelp:
85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
86 | @echo
87 | @echo "Build finished."
88 | @echo "To view the help file:"
89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/zopeinterface"
90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/zopeinterface"
91 | @echo "# devhelp"
92 |
93 | epub:
94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
95 | @echo
96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
97 |
98 | latex:
99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
100 | @echo
101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
103 | "(use \`make latexpdf' here to do that automatically)."
104 |
105 | latexpdf:
106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
107 | @echo "Running LaTeX files through pdflatex..."
108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
110 |
111 | text:
112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
113 | @echo
114 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
115 |
116 | man:
117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
118 | @echo
119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
120 |
121 | texinfo:
122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
123 | @echo
124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
125 | @echo "Run \`make' in that directory to run these through makeinfo" \
126 | "(use \`make info' here to do that automatically)."
127 |
128 | info:
129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
130 | @echo "Running Texinfo files through makeinfo..."
131 | make -C $(BUILDDIR)/texinfo info
132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
133 |
134 | gettext:
135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
136 | @echo
137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
138 |
139 | changes:
140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
141 | @echo
142 | @echo "The overview file is in $(BUILDDIR)/changes."
143 |
144 | linkcheck:
145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
146 | @echo
147 | @echo "Link check complete; look for any errors in the above output " \
148 | "or in $(BUILDDIR)/linkcheck/output.txt."
149 |
150 | doctest:
151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
152 | @echo "Testing of doctests in the sources finished, look at the " \
153 | "results in $(BUILDDIR)/doctest/output.txt."
154 |
--------------------------------------------------------------------------------
/docs/_static/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zopefoundation/zope.interface/f418c310af667c619314cdc635aeaca2b1c7a4c3/docs/_static/.gitignore
--------------------------------------------------------------------------------
/docs/api/adapters.rst:
--------------------------------------------------------------------------------
1 | ==================
2 | Adapter Registry
3 | ==================
4 |
5 | Usage of the adapter registry is documented in :ref:`adapter-registry`.
6 |
7 |
8 | The adapter registry's API is defined by
9 | :class:`zope.interface.interfaces.IAdapterRegistry`:
10 |
11 | .. autointerface:: zope.interface.interfaces.IAdapterRegistry
12 | :members:
13 | :member-order: bysource
14 |
15 |
16 | The concrete implementations of ``IAdapterRegistry`` provided by this
17 | package allows for some customization opportunities.
18 |
19 | .. autoclass:: zope.interface.adapter.BaseAdapterRegistry
20 | :members:
21 | :private-members:
22 |
23 | .. autoclass:: zope.interface.adapter.AdapterRegistry
24 | :members:
25 |
26 | .. autoclass:: zope.interface.adapter.VerifyingAdapterRegistry
27 | :members:
28 |
--------------------------------------------------------------------------------
/docs/api/common.rst:
--------------------------------------------------------------------------------
1 | ====================================
2 | Python Standard Library Interfaces
3 | ====================================
4 |
5 | The ``zope.interface.common`` package provides interfaces for objects
6 | distributed as part of the Python standard library. Importing these
7 | modules (usually) has the effect of making the standard library objects
8 | implement the correct interface.
9 |
10 | zope.interface.common.interface
11 | ===============================
12 |
13 | .. automodule:: zope.interface.common.interfaces
14 |
15 | zope.interface.common.idatetime
16 | ===============================
17 |
18 | .. automodule:: zope.interface.common.idatetime
19 |
20 | zope.interface.common.collections
21 | =================================
22 |
23 | .. automodule:: zope.interface.common.collections
24 |
25 | zope.interface.common.numbers
26 | =============================
27 |
28 | .. automodule:: zope.interface.common.numbers
29 |
30 | zope.interface.common.builtins
31 | ==============================
32 |
33 | .. automodule:: zope.interface.common.builtins
34 |
35 | zope.interface.common.io
36 | ========================
37 |
38 | .. automodule:: zope.interface.common.io
39 |
40 | .. Deprecated or discouraged modules below this
41 |
42 | zope.interface.common.mapping
43 | =============================
44 |
45 | .. automodule:: zope.interface.common.mapping
46 |
47 | zope.interface.common.sequence
48 | ==============================
49 |
50 | .. automodule:: zope.interface.common.sequence
51 |
--------------------------------------------------------------------------------
/docs/api/components.rst:
--------------------------------------------------------------------------------
1 | ======================
2 | Component Registries
3 | ======================
4 |
5 | The component registry's API is defined by
6 | ``zope.interface.interfaces.IComponents``:
7 |
8 | .. autointerface:: zope.interface.interfaces.IComponentLookup
9 | :members:
10 | :member-order: bysource
11 |
12 |
13 | .. autointerface:: zope.interface.interfaces.IComponentRegistry
14 | :members:
15 | :member-order: bysource
16 |
17 | .. autointerface:: zope.interface.interfaces.IComponents
18 | :members:
19 | :member-order: bysource
20 |
21 | One default implementation of `~zope.interface.interfaces.IComponents` is provided.
22 |
23 | .. autoclass:: zope.interface.registry.Components
24 |
25 | Events
26 | ======
27 |
28 | Adding and removing components from the component registry create
29 | registration and unregistration events. Like most things, they are
30 | defined by an interface and a default implementation is provided.
31 |
32 | Registration
33 | ------------
34 |
35 | .. autointerface:: zope.interface.interfaces.IObjectEvent
36 | .. autointerface:: zope.interface.interfaces.IRegistrationEvent
37 |
38 | .. autointerface:: zope.interface.interfaces.IRegistered
39 | .. autoclass:: zope.interface.interfaces.Registered
40 |
41 | .. autointerface:: zope.interface.interfaces.IUnregistered
42 | .. autoclass:: zope.interface.interfaces.Unregistered
43 |
44 |
45 | Details
46 | -------
47 |
48 | These are all types of ``IObjectEvent``, meaning they have an object
49 | that provides specific details about the event. Component registries
50 | create detail objects for four types of components they manage.
51 |
52 | All four share a common base interface.
53 |
54 | .. autointerface:: zope.interface.interfaces.IRegistration
55 |
56 | * Utilities
57 |
58 | .. autointerface:: zope.interface.interfaces.IUtilityRegistration
59 | .. autoclass:: zope.interface.registry.UtilityRegistration
60 |
61 | * Handlers
62 |
63 | .. autointerface:: zope.interface.interfaces.IHandlerRegistration
64 | .. autoclass:: zope.interface.registry.HandlerRegistration
65 |
66 | * Adapters
67 |
68 | For ease of implementation, a shared base class is used for these
69 | events. It should not be referenced by clients, but it is documented
70 | to show the common attributes.
71 |
72 | .. autointerface:: zope.interface.interfaces._IBaseAdapterRegistration
73 |
74 | .. autointerface:: zope.interface.interfaces.IAdapterRegistration
75 | .. autoclass:: zope.interface.registry.AdapterRegistration
76 |
77 | .. autointerface:: zope.interface.interfaces.ISubscriptionAdapterRegistration
78 | .. autoclass:: zope.interface.registry.SubscriptionRegistration
79 |
80 | Exceptions
81 | ==========
82 |
83 | .. autoclass:: zope.interface.interfaces.ComponentLookupError
84 | .. autoclass:: zope.interface.interfaces.Invalid
85 |
--------------------------------------------------------------------------------
/docs/api/index.rst:
--------------------------------------------------------------------------------
1 | ===============
2 | API Reference
3 | ===============
4 |
5 | Contents:
6 |
7 | .. toctree::
8 | :maxdepth: 2
9 |
10 | specifications
11 | declarations
12 | adapters
13 | components
14 | common
15 | ro
16 |
--------------------------------------------------------------------------------
/docs/api/ro.rst:
--------------------------------------------------------------------------------
1 | ===========================================
2 | Computing The Resolution Order (Priority)
3 | ===========================================
4 |
5 | Just as Python classes have a method resolution order that determines
6 | which implementation of a method gets used when inheritance is used,
7 | interfaces have a resolution order that determines their ordering when
8 | searching for adapters.
9 |
10 | That order is computed by ``zope.interface.ro.ro``. This is an
11 | internal module not generally needed by a user of ``zope.interface``,
12 | but its documentation can be helpful to understand how orders are
13 | computed.
14 |
15 | ``zope.interface.ro``
16 | =====================
17 |
18 | .. automodule:: zope.interface.ro
19 | :member-order: alphabetical
20 |
--------------------------------------------------------------------------------
/docs/changes.rst:
--------------------------------------------------------------------------------
1 | .. include:: ../CHANGES.rst
2 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | #
2 | # zope.interface documentation build configuration file, created by
3 | # sphinx-quickstart on Mon Mar 26 16:31:31 2012.
4 | #
5 | # This file is execfile()d with the current directory set to its containing dir
6 | #
7 | # Note that not all possible configuration values are present in this
8 | # autogenerated file.
9 | #
10 | import datetime
11 | import os
12 | import sys
13 |
14 | import pkg_resources
15 |
16 |
17 | # All configuration values have a default; values that are commented out
18 | # serve to show the default.
19 |
20 | year = datetime.datetime.now().year
21 | sys.path.append(os.path.abspath('../src'))
22 | rqmt = pkg_resources.require('zope.interface')[0]
23 | # Import and document pure-python versions of things; they tend to have better
24 | # docstrings and signatures.
25 | os.environ['PURE_PYTHON'] = '1'
26 | # If extensions (or modules to document with autodoc) are in another directory,
27 | # add these directories to sys.path here. If the directory is relative to the
28 | # documentation root, use os.path.abspath to make it absolute, like shown here.
29 | # sys.path.insert(0, os.path.abspath('.'))
30 |
31 | # -- General configuration -----------------------------------------------
32 |
33 | # If your documentation needs a minimal Sphinx version, state it here.
34 | # needs_sphinx = '1.0'
35 |
36 | # Add any Sphinx extension module names here as strings. They can be extensions
37 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
38 | extensions = [
39 | 'sphinx.ext.autodoc',
40 | 'sphinx.ext.doctest',
41 | 'sphinx.ext.intersphinx',
42 | 'sphinx.ext.todo',
43 | 'sphinx.ext.ifconfig',
44 | 'sphinx.ext.viewcode',
45 | 'repoze.sphinx.autointerface',
46 | ]
47 |
48 | # Add any paths that contain templates here, relative to this directory.
49 | templates_path = ['_templates']
50 |
51 | # The suffix of source filenames.
52 | source_suffix = '.rst'
53 |
54 | # The encoding of source files.
55 | # source_encoding = 'utf-8-sig'
56 |
57 | # The master toctree document.
58 | master_doc = 'index'
59 |
60 | # General information about the project.
61 | project = 'zope.interface'
62 | copyright = f'2012-{year}, Zope Foundation contributors'
63 |
64 | # The version info for the project you're documenting, acts as replacement for
65 | # |version| and |release|, also used in various other places throughout the
66 | # built documents.
67 | #
68 | # The short X.Y version.
69 | version = '%s.%s' % tuple(rqmt.version.split('.')[:2])
70 | # The full version, including alpha/beta/rc tags.
71 | release = rqmt.version
72 |
73 | # The language for content autogenerated by Sphinx. Refer to documentation
74 | # for a list of supported languages.
75 | # language = None
76 |
77 | # There are two options for replacing |today|: either, you set today to some
78 | # non-false value, then it is used:
79 | # today = ''
80 | # Else, today_fmt is used as the format for a strftime call.
81 | # today_fmt = '%B %d, %Y'
82 |
83 | # List of patterns, relative to source directory, that match files and
84 | # directories to ignore when looking for source files.
85 | exclude_patterns = ['_build']
86 |
87 | # The reST default role (used for this markup: `text`) to use for all
88 | # documents.
89 | default_role = 'obj'
90 |
91 | # If true, '()' will be appended to :func: etc. cross-reference text.
92 | # add_function_parentheses = True
93 |
94 | # If true, the current module name will be prepended to all description
95 | # unit titles (such as .. function::).
96 | # add_module_names = True
97 |
98 | # If true, sectionauthor and moduleauthor directives will be shown in the
99 | # output. They are ignored by default.
100 | # show_authors = False
101 |
102 | # The name of the Pygments (syntax highlighting) style to use.
103 | pygments_style = 'sphinx'
104 |
105 | # A list of ignored prefixes for module index sorting.
106 | # modindex_common_prefix = []
107 |
108 |
109 | # -- Options for HTML output ---------------------------------------------
110 |
111 | # The theme to use for HTML and HTML Help pages. See the documentation for
112 | # a list of builtin themes.
113 | html_theme = 'furo'
114 |
115 | # Theme options are theme-specific and customize the look and feel of a theme
116 | # further. For a list of options available for each theme, see the
117 | # documentation.
118 | # html_theme_options = {}
119 |
120 | # Add any paths that contain custom themes here, relative to this directory.
121 | # html_theme_path = []
122 |
123 | # The name for this set of Sphinx documents. If None, it defaults to
124 | # " v documentation".
125 | # html_title = None
126 |
127 | # A shorter title for the navigation bar. Default is the same as html_title.
128 | # html_short_title = None
129 |
130 | # The name of an image file (relative to this directory) to place at the top
131 | # of the sidebar.
132 | # html_logo = None
133 |
134 | # The name of an image file (within the static path) to use as favicon of the
135 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
136 | # pixels large.
137 | # html_favicon = None
138 |
139 | # Add any paths that contain custom static files (such as style sheets) here,
140 | # relative to this directory. They are copied after the builtin static files,
141 | # so a file named "default.css" will overwrite the builtin "default.css".
142 | html_static_path = ['_static']
143 |
144 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
145 | # using the given strftime format.
146 | # html_last_updated_fmt = '%b %d, %Y'
147 |
148 | # If true, SmartyPants will be used to convert quotes and dashes to
149 | # typographically correct entities.
150 | # html_use_smartypants = True
151 |
152 | # Custom sidebar templates, maps document names to template names.
153 | # html_sidebars = {}
154 |
155 | # Additional templates that should be rendered to pages, maps page names to
156 | # template names.
157 | # html_additional_pages = {}
158 |
159 | # If false, no module index is generated.
160 | # html_domain_indices = True
161 |
162 | # If false, no index is generated.
163 | # html_use_index = True
164 |
165 | # If true, the index is split into individual pages for each letter.
166 | # html_split_index = False
167 |
168 | # If true, links to the reST sources are added to the pages.
169 | # html_show_sourcelink = True
170 |
171 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
172 | # html_show_sphinx = True
173 |
174 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
175 | # html_show_copyright = True
176 |
177 | # If true, an OpenSearch description file will be output, and all pages will
178 | # contain a tag referring to it. The value of this option must be the
179 | # base URL from which the finished HTML is served.
180 | # html_use_opensearch = ''
181 |
182 | # This is the file name suffix for HTML files (e.g. ".xhtml").
183 | # html_file_suffix = None
184 |
185 | # Output file base name for HTML help builder.
186 | htmlhelp_basename = 'zopeinterfacedoc'
187 |
188 |
189 | # -- Options for LaTeX output --------------------------------------------
190 |
191 | latex_elements = {
192 | # The paper size ('letterpaper' or 'a4paper').
193 | # 'papersize': 'letterpaper',
194 |
195 | # The font size ('10pt', '11pt' or '12pt').
196 | # 'pointsize': '10pt',
197 |
198 | # Additional stuff for the LaTeX preamble.
199 | # 'preamble': '',
200 | }
201 |
202 | # pdflatex can't handle Cyrillic out of the box, but xetext/lualatex should be
203 | # able to cope
204 | latex_engine = 'lualatex'
205 |
206 | # Grouping the document tree into LaTeX files. List of tuples
207 | # (source start file, target name, title, author, documentclass [howto/manual])
208 | latex_documents = [
209 | ('index', 'zopeinterface.tex', 'zope.interface Documentation',
210 | 'Zope Foundation contributors', 'manual'),
211 | ]
212 |
213 | # The name of an image file (relative to this directory) to place at the top of
214 | # the title page.
215 | # latex_logo = None
216 |
217 | # For "manual" documents, if this is true, then toplevel headings are parts,
218 | # not chapters.
219 | # latex_use_parts = False
220 |
221 | # If true, show page references after internal links.
222 | # latex_show_pagerefs = False
223 |
224 | # If true, show URL addresses after external links.
225 | # latex_show_urls = False
226 |
227 | # Documents to append as an appendix to all manuals.
228 | # latex_appendices = []
229 |
230 | # If false, no module index is generated.
231 | # latex_domain_indices = True
232 |
233 |
234 | # -- Options for manual page output --------------------------------------
235 |
236 | # One entry per manual page. List of tuples
237 | # (source start file, name, description, authors, manual section).
238 | man_pages = [
239 | ('index', 'zopeinterface', 'zope.interface Documentation',
240 | ['Zope Foundation contributors'], 1)
241 | ]
242 |
243 | # If true, show URL addresses after external links.
244 | # man_show_urls = False
245 |
246 |
247 | # -- Options for Texinfo output ------------------------------------------
248 |
249 | # Grouping the document tree into Texinfo files. List of tuples
250 | # (source start file, target name, title, author,
251 | # dir menu entry, description, category)
252 | texinfo_documents = [
253 | ('index',
254 | 'zopeinterface',
255 | 'zope.interface Documentation',
256 | 'Zope Foundation contributors',
257 | 'zopeinterface',
258 | 'One line description of project.',
259 | 'Miscellaneous'),
260 | ]
261 |
262 | # Documents to append as an appendix to all manuals.
263 | # texinfo_appendices = []
264 |
265 | # If false, no module index is generated.
266 | # texinfo_domain_indices = True
267 |
268 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
269 | # texinfo_show_urls = 'footnote'
270 |
271 |
272 | # Example configuration for intersphinx: refer to the Python standard library.
273 | intersphinx_mapping = {
274 | 'python': ('https://docs.python.org/', None),
275 | 'persistent': ('https://persistent.readthedocs.io/en/latest/', None),
276 | 'btrees': ('https://btrees.readthedocs.io/en/latest/', None),
277 | }
278 |
279 | # Sphinx 1.8+ prefers this to `autodoc_default_flags`. It's documented that
280 | # either True or None mean the same thing as just setting the flag, but
281 | # only None works in 1.8 (True works in 2.0)
282 | autodoc_default_options = {
283 | 'members': None,
284 | 'show-inheritance': None,
285 | }
286 | autodoc_member_order = 'bysource'
287 | autoclass_content = 'both'
288 |
--------------------------------------------------------------------------------
/docs/foodforthought.rst:
--------------------------------------------------------------------------------
1 | ================================
2 | Food-based subscription examples
3 | ================================
4 |
5 |
6 | This file gives more subscription examples using a cooking-based example:
7 |
8 | .. doctest::
9 |
10 | >>> from zope.interface.adapter import AdapterRegistry
11 | >>> registry = AdapterRegistry()
12 |
13 | >>> import zope.interface
14 | >>> class IAnimal(zope.interface.Interface):
15 | ... pass
16 | >>> class IPoultry(IAnimal):
17 | ... pass
18 | >>> class IChicken(IPoultry):
19 | ... pass
20 | >>> class ISeafood(IAnimal):
21 | ... pass
22 |
23 | Adapting to some other interface for which there is no
24 | subscription adapter returns an empty sequence:
25 |
26 | .. doctest::
27 |
28 | >>> class IRecipe(zope.interface.Interface):
29 | ... pass
30 | >>> class ISausages(IRecipe):
31 | ... pass
32 | >>> class INoodles(IRecipe):
33 | ... pass
34 | >>> class IKFC(IRecipe):
35 | ... pass
36 |
37 | >>> list(registry.subscriptions([IPoultry], IRecipe))
38 | []
39 |
40 | unless we define a subscription:
41 |
42 | .. doctest::
43 |
44 | >>> registry.subscribe([IAnimal], ISausages, 'sausages')
45 | >>> list(registry.subscriptions([IPoultry], ISausages))
46 | ['sausages']
47 |
48 | And define another subscription adapter:
49 |
50 | .. doctest::
51 |
52 | >>> registry.subscribe([IPoultry], INoodles, 'noodles')
53 | >>> meals = list(registry.subscriptions([IPoultry], IRecipe))
54 | >>> meals.sort()
55 | >>> meals
56 | ['noodles', 'sausages']
57 |
58 | >>> registry.subscribe([IChicken], IKFC, 'kfc')
59 | >>> meals = list(registry.subscriptions([IChicken], IRecipe))
60 | >>> meals.sort()
61 | >>> meals
62 | ['kfc', 'noodles', 'sausages']
63 |
64 | And the answer for poultry hasn't changed:
65 |
66 | .. doctest::
67 |
68 | >>> meals = list(registry.subscriptions([IPoultry], IRecipe))
69 | >>> meals.sort()
70 | >>> meals
71 | ['noodles', 'sausages']
72 |
--------------------------------------------------------------------------------
/docs/human.rst:
--------------------------------------------------------------------------------
1 | ==========================
2 | Using the Adapter Registry
3 | ==========================
4 |
5 | This is a small demonstration of the :mod:`zope.interface` package including its
6 | adapter registry. It is intended to provide a concrete but narrow example on
7 | how to use interfaces and adapters outside of Zope 3.
8 |
9 | First we have to import the interface package:
10 |
11 | .. doctest::
12 |
13 | >>> import zope.interface
14 |
15 | We now develop an interface for our object, which is a simple file in this
16 | case. For now we simply support one attribute, the body, which contains the
17 | actual file contents:
18 |
19 | .. doctest::
20 |
21 | >>> class IFile(zope.interface.Interface):
22 | ...
23 | ... body = zope.interface.Attribute('Contents of the file.')
24 | ...
25 |
26 | For statistical reasons we often want to know the size of a file. However, it
27 | would be clumsy to implement the size directly in the file object, since the
28 | size really represents meta-data. Thus we create another interface that
29 | provides the size of something:
30 |
31 | .. doctest::
32 |
33 | >>> class ISize(zope.interface.Interface):
34 | ...
35 | ... def getSize():
36 | ... 'Return the size of an object.'
37 | ...
38 |
39 | Now we need to implement the file interface. It is essential that the object states
40 | that it implements the ``IFile`` interface. We also provide a default body
41 | value (just to make things simpler for this example):
42 |
43 | .. doctest::
44 |
45 | >>> @zope.interface.implementer(IFile)
46 | ... class File(object):
47 | ...
48 | ... body = 'foo bar'
49 | ...
50 |
51 | Next we implement an adapter that can provide the ``ISize`` interface given any
52 | object providing ``IFile``. By convention we use ``__used_for__`` to specify the
53 | interface that we expect the adapted object to provide, in our case
54 | ``IFile``. However, this attribute is not used for anything. If you have
55 | multiple interfaces for which an adapter is used, just specify the interfaces
56 | via a tuple.
57 |
58 | Again by convention, the constructor of an adapter takes one argument, the
59 | context. The context in this case is an instance of ``File`` (providing ``IFile``)
60 | that is used to extract the size from. Also by convention the context is
61 | stored in an attribute named ``context`` on the adapter. The `twisted`_ community
62 | refers to the context as the ``original`` object. However, you may feel free to
63 | use a specific argument name, such as ``file``:
64 |
65 | .. doctest::
66 |
67 | >>> @zope.interface.implementer(ISize)
68 | ... class FileSize(object):
69 | ...
70 | ... __used_for__ = IFile
71 | ...
72 | ... def __init__(self, context):
73 | ... self.context = context
74 | ...
75 | ... def getSize(self):
76 | ... return len(self.context.body)
77 | ...
78 |
79 | Now that we have written our adapter, we have to register it with an adapter
80 | registry, so that it can be looked up when needed. There is no such thing as a
81 | global registry; thus we have to instantiate one for our example manually:
82 |
83 | .. doctest::
84 |
85 | >>> from zope.interface.adapter import AdapterRegistry
86 | >>> registry = AdapterRegistry()
87 |
88 |
89 | The registry keeps a map of what adapters implement based on another
90 | interface the object already provides. Therefore, we next have to register an
91 | adapter that adapts from ``IFile`` to ``ISize``. The first argument to
92 | the registry's ``register()`` method is a list of original interfaces.In our
93 | cause we have only one original interface, ``IFile``. A list makes sense, since
94 | the interface package has the concept of multi-adapters, which are adapters
95 | that require multiple objects to adapt to a new interface. In these
96 | situations, your adapter constructor will require an argument for each
97 | specified interface.
98 |
99 | The second argument is the interface the adapter provides, in our case
100 | ``ISize``. The third argument is the name of the adapter. Since we do not care
101 | about names, we simply leave it as an empty string. Names are commonly useful,
102 | if you have adapters for the same set of interfaces, but they are useful in
103 | different situations. The last argument is simply the adapter class:
104 |
105 | .. doctest::
106 |
107 | >>> registry.register([IFile], ISize, '', FileSize)
108 |
109 | You can now use the the registry to lookup the adapter:
110 |
111 | .. doctest::
112 |
113 | >>> registry.lookup1(IFile, ISize, '')
114 |
115 |
116 | Let's get a little bit more practical. Let's create a ``File`` instance and
117 | create the adapter using a registry lookup. Then we see whether the adapter
118 | returns the correct size by calling ``getSize()``:
119 |
120 | .. doctest::
121 |
122 | >>> file = File()
123 | >>> size = registry.lookup1(IFile, ISize, '')(file)
124 | >>> size.getSize()
125 | 7
126 |
127 | However, this is not very practical, since I have to manually pass in the
128 | arguments to the lookup method. There is some syntactic candy that will allow
129 | us to get an adapter instance by simply calling ``ISize(file)``. To make use of
130 | this functionality, we need to add our registry to the ``adapter_hooks`` list,
131 | which is a member of the adapters module. This list stores a collection of
132 | callables that are automatically invoked when ``IFoo(obj)`` is called; their
133 | purpose is to locate adapters that implement an interface for a certain
134 | context instance.
135 |
136 | You are required to implement your own adapter hook; this example covers one
137 | of the simplest hooks that use the registry, but you could implement one that
138 | used an adapter cache or persistent adapters, for instance. The helper hook is
139 | required to expect as first argument the desired output interface (for us
140 | ``ISize``) and as the second argument the context of the adapter (here
141 | ``file``). The function returns an adapter, i.e. a ``FileSize`` instance:
142 |
143 | .. doctest::
144 |
145 | >>> def hook(provided, object):
146 | ... adapter = registry.lookup1(zope.interface.providedBy(object),
147 | ... provided, '')
148 | ... return adapter(object)
149 | ...
150 |
151 | We now just add the hook to an ``adapter_hooks`` list:
152 |
153 | .. doctest::
154 |
155 | >>> from zope.interface.interface import adapter_hooks
156 | >>> adapter_hooks.append(hook)
157 |
158 | Once the hook is registered, you can use the desired syntax:
159 |
160 | .. doctest::
161 |
162 | >>> size = ISize(file)
163 | >>> size.getSize()
164 | 7
165 |
166 | Now we have to clean up after ourselves, so that others after us have a clean
167 | ``adapter_hooks`` list:
168 |
169 | .. doctest::
170 |
171 | >>> adapter_hooks.remove(hook)
172 |
173 | That's it. I have intentionally left out a discussion of named adapters and
174 | multi-adapters, since this text is intended as a practical and simple
175 | introduction to Zope 3 interfaces and adapters. You might want to read the
176 | ``adapter.txt`` in the ``zope.interface`` package for a more formal, referential
177 | and complete treatment of the package. Warning: People have reported that
178 | ``adapter.txt`` makes their brain feel soft!
179 |
180 | .. _twisted: https://twistedmatrix.com/
181 |
--------------------------------------------------------------------------------
/docs/human.ru.rst:
--------------------------------------------------------------------------------
1 | ===============================
2 | Использование реестра адаптеров
3 | ===============================
4 |
5 | Данный документ содержит небольшую демонстрацию пакета ``zope.interface`` и его
6 | реестра адаптеров. Документ рассчитывался как конкретный, но более узкий пример
7 | того как использовать интерфейсы и адаптеры вне Zope 3.
8 |
9 | Сначала нам необходимо импортировать пакет для работы с интерфейсами::
10 |
11 | >>> import zope.interface
12 |
13 | Теперь мы разработаем интерфейс для нашего объекта - простого файла. Наш файл
14 | будет содержать всего один атрибут - body, в котором фактически будет сохранено
15 | содержимое файла::
16 |
17 | >>> class IFile(zope.interface.Interface):
18 | ...
19 | ... body = zope.interface.Attribute(u'Содержимое файла.')
20 | ...
21 |
22 | Для статистики нам часто необходимо знать размер файла. Но было бы несколько
23 | топорно реализовывать определение размера прямо для объекта файла, т.к. размер
24 | больше относится к мета-данным. Таким образом мы создаем еще один интерфейс для
25 | представления размера какого-либо объекта::
26 |
27 | >>> class ISize(zope.interface.Interface):
28 | ...
29 | ... def getSize():
30 | ... 'Return the size of an object.'
31 | ...
32 |
33 | Теперь мы должны создать класс реализующий наш файл. Необходимо что бы наш
34 | объект хранил информацию о том, что он реализует интерфейс `IFile`. Мы также
35 | создаем атрибут с содержимым файла по умолчанию (для упрощения нашего
36 | примера)::
37 |
38 | >>> class File(object):
39 | ...
40 | ... zope.interface.implements(IFile)
41 | ... body = 'foo bar'
42 | ...
43 |
44 | Дальше мы создаем адаптер, который будет предоставлять интерфейс `ISize`
45 | получая любой объект предоставляющий интерфейс `IFile`. По соглашению мы
46 | используем атрибут `__used_for__` для указания интерфейса который как мы
47 | ожидаем предоставляет адаптируемый объект, `IFile` в нашем случае. На самом
48 | деле этот атрибут используется только для документирования. В случае если
49 | адаптер используется для нескольких интерфейсов можно указать их все в виде
50 | кортежа.
51 |
52 | Опять же по соглашению конструктор адаптера получает один аргумент - context
53 | (контекст). В нашем случае контекст - это экземпляр `IFile` (объект,
54 | предоставляющий `IFile`) который используется для получения из него размера.
55 | Так же по соглашению контекст сохраняется а адаптере в атрибуте с именем
56 | `context`. Twisted комьюнити ссылается на контекст как на объект `original`.
57 | Таким образом можно также дать аргументу любое подходящее имя, например
58 | `file`::
59 |
60 | >>> class FileSize(object):
61 | ...
62 | ... zope.interface.implements(ISize)
63 | ... __used_for__ = IFile
64 | ...
65 | ... def __init__(self, context):
66 | ... self.context = context
67 | ...
68 | ... def getSize(self):
69 | ... return len(self.context.body)
70 | ...
71 |
72 | Теперь когда мы написали наш адаптер мы должны зарегистрировать его в реестре
73 | адаптеров, что бы его можно было запросить когда он понадобится. Здесь нет
74 | какого-либо глобального реестра адаптеров, таким образом мы должны
75 | самостоятельно создать для нашего примера реестр::
76 |
77 | >>> from zope.interface.adapter import AdapterRegistry
78 | >>> registry = AdapterRegistry()
79 |
80 | Реестр содержит отображение того, что адаптер реализует на основе другого
81 | интерфейса который предоставляет объект. Поэтому дальше мы регистрируем адаптер
82 | который адаптирует интерфейс `IFile` к интерфейсу `ISize`. Первый аргумент к
83 | методу `register()` реестра - это список адаптируемых интерфейсов. В нашем
84 | случае мы имеем только один адаптируемый интерфейс - `IFile`. Список
85 | интерфейсов имеет смысл для использования концепции мульти-адаптеров, которые
86 | требуют нескольких оригинальных объектов для адаптации к новому интерфейсу. В
87 | этой ситуации конструктор адаптера будет требовать новый аргумент для каждого
88 | оригинального интерфейса.
89 |
90 | Второй аргумент метода `register()` - это интерфейс который предоставляет
91 | адаптер, в нашем случае `ISize`. Третий аргумент - имя адаптера. Сейчас нам не
92 | важно имя адаптера и мы передаем его как пустую строку. Обычно имена полезны
93 | если используются адаптеры для одинакового набора интерфейсов, но в различных
94 | ситуациях. Последний аргумент - это класс адаптера::
95 |
96 | >>> registry.register([IFile], ISize, '', FileSize)
97 |
98 | Теперь мы можем использовать реестр для запроса адаптера::
99 |
100 | >>> registry.lookup1(IFile, ISize, '')
101 |
102 |
103 | Попробуем более практичный пример. Создадим экземпляр `File` и создадим адаптер
104 | использующий запрос реестра. Затем мы увидим возвращает ли адаптер корректный
105 | размер при вызове `getSize()`::
106 |
107 | >>> file = File()
108 | >>> size = registry.lookup1(IFile, ISize, '')(file)
109 | >>> size.getSize()
110 | 7
111 |
112 | На самом деле это не очень практично, т.к. нам нужно самим передавать все
113 | аргументы методу запроса. Существует некоторый синтаксический леденец который
114 | позволяет нам получить экземпляр адаптера просто вызвав `ISize(file)`. Что бы
115 | использовать эту функциональность нам понадобится добавить наш реестр к списку
116 | adapter_hooks, который находится в модуле с адаптерами. Этот список хранит
117 | коллекцию вызываемых объектов которые вызываются автоматически когда вызывается
118 | IFoo(obj); их предназначение - найти адаптеры которые реализуют интерфейс для
119 | определенного экземпляра контекста.
120 |
121 | Необходимо реализовать свою собственную функцию для поиска адаптера; данный
122 | пример описывает одну из простейших функций для использования с реестром, но
123 | также можно реализовать поисковые функции которые, например, используют
124 | кэширование, или адаптеры сохраняемые в базе. Функция поиска должна принимать
125 | желаемый на выходе интерфейс (в нашем случае `ISize`) как первый аргумент и
126 | контекст для адаптации (`file`) как второй. Функция должна вернуть адаптер,
127 | т.е. экземпляр `FileSize`::
128 |
129 | >>> def hook(provided, object):
130 | ... adapter = registry.lookup1(zope.interface.providedBy(object),
131 | ... provided, '')
132 | ... return adapter(object)
133 | ...
134 |
135 | Теперь мы просто добавляем нашу функцию к списку `adapter_hooks`::
136 |
137 | >>> from zope.interface.interface import adapter_hooks
138 | >>> adapter_hooks.append(hook)
139 |
140 | Как только функция зарегистрирована мы можем использовать желаемый синтаксис::
141 |
142 | >>> size = ISize(file)
143 | >>> size.getSize()
144 | 7
145 |
146 | После нам нужно прибраться за собой, что бы другие получили чистый список
147 | `adaper_hooks` после нас::
148 |
149 | >>> adapter_hooks.remove(hook)
150 |
151 | Это все. Здесь намеренно отложена дискуссия об именованных и мульти-адаптерах,
152 | т.к. данный текст рассчитан как практическое и простое введение в интерфейсы и
153 | адаптеры Zope 3. Для более подробной информации имеет смысл прочитать
154 | `adapter.txt` из пакета `zope.interface`, что бы получить более формальное,
155 | справочное и полное трактование пакета. Внимание: многие жаловались, что
156 | `adapter.txt` приводит их мозг к расплавленному состоянию!
157 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | zope.interface documentation
2 | ============================
3 |
4 | .. toctree::
5 | :maxdepth: 2
6 |
7 | README
8 | adapter
9 | human
10 | verify
11 | foodforthought
12 | api/index
13 | hacking
14 | changes
15 |
16 | По-русски
17 | =========
18 |
19 | .. toctree::
20 | :maxdepth: 2
21 |
22 | README.ru
23 | adapter.ru
24 | human.ru
25 |
26 |
27 |
28 | Indices and tables
29 | ==================
30 |
31 | * :ref:`genindex`
32 | * :ref:`modindex`
33 | * :ref:`search`
34 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=_build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
10 | set I18NSPHINXOPTS=%SPHINXOPTS% .
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
31 | echo. text to make text files
32 | echo. man to make manual pages
33 | echo. texinfo to make Texinfo files
34 | echo. gettext to make PO message catalogs
35 | echo. changes to make an overview over all changed/added/deprecated items
36 | echo. linkcheck to check all external links for integrity
37 | echo. doctest to run all doctests embedded in the documentation if enabled
38 | goto end
39 | )
40 |
41 | if "%1" == "clean" (
42 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
43 | del /q /s %BUILDDIR%\*
44 | goto end
45 | )
46 |
47 | if "%1" == "html" (
48 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
49 | if errorlevel 1 exit /b 1
50 | echo.
51 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
52 | goto end
53 | )
54 |
55 | if "%1" == "dirhtml" (
56 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
57 | if errorlevel 1 exit /b 1
58 | echo.
59 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
60 | goto end
61 | )
62 |
63 | if "%1" == "singlehtml" (
64 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
65 | if errorlevel 1 exit /b 1
66 | echo.
67 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
68 | goto end
69 | )
70 |
71 | if "%1" == "pickle" (
72 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
73 | if errorlevel 1 exit /b 1
74 | echo.
75 | echo.Build finished; now you can process the pickle files.
76 | goto end
77 | )
78 |
79 | if "%1" == "json" (
80 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
81 | if errorlevel 1 exit /b 1
82 | echo.
83 | echo.Build finished; now you can process the JSON files.
84 | goto end
85 | )
86 |
87 | if "%1" == "htmlhelp" (
88 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
89 | if errorlevel 1 exit /b 1
90 | echo.
91 | echo.Build finished; now you can run HTML Help Workshop with the ^
92 | .hhp project file in %BUILDDIR%/htmlhelp.
93 | goto end
94 | )
95 |
96 | if "%1" == "qthelp" (
97 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
98 | if errorlevel 1 exit /b 1
99 | echo.
100 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
101 | .qhcp project file in %BUILDDIR%/qthelp, like this:
102 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\zopeinterface.qhcp
103 | echo.To view the help file:
104 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\zopeinterface.ghc
105 | goto end
106 | )
107 |
108 | if "%1" == "devhelp" (
109 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
110 | if errorlevel 1 exit /b 1
111 | echo.
112 | echo.Build finished.
113 | goto end
114 | )
115 |
116 | if "%1" == "epub" (
117 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
118 | if errorlevel 1 exit /b 1
119 | echo.
120 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
121 | goto end
122 | )
123 |
124 | if "%1" == "latex" (
125 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
126 | if errorlevel 1 exit /b 1
127 | echo.
128 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
129 | goto end
130 | )
131 |
132 | if "%1" == "text" (
133 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
134 | if errorlevel 1 exit /b 1
135 | echo.
136 | echo.Build finished. The text files are in %BUILDDIR%/text.
137 | goto end
138 | )
139 |
140 | if "%1" == "man" (
141 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
142 | if errorlevel 1 exit /b 1
143 | echo.
144 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
145 | goto end
146 | )
147 |
148 | if "%1" == "texinfo" (
149 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
150 | if errorlevel 1 exit /b 1
151 | echo.
152 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
153 | goto end
154 | )
155 |
156 | if "%1" == "gettext" (
157 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
158 | if errorlevel 1 exit /b 1
159 | echo.
160 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
161 | goto end
162 | )
163 |
164 | if "%1" == "changes" (
165 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
166 | if errorlevel 1 exit /b 1
167 | echo.
168 | echo.The overview file is in %BUILDDIR%/changes.
169 | goto end
170 | )
171 |
172 | if "%1" == "linkcheck" (
173 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
174 | if errorlevel 1 exit /b 1
175 | echo.
176 | echo.Link check complete; look for any errors in the above output ^
177 | or in %BUILDDIR%/linkcheck/output.txt.
178 | goto end
179 | )
180 |
181 | if "%1" == "doctest" (
182 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
183 | if errorlevel 1 exit /b 1
184 | echo.
185 | echo.Testing of doctests in the sources finished, look at the ^
186 | results in %BUILDDIR%/doctest/output.txt.
187 | goto end
188 | )
189 |
190 | :end
191 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | Sphinx
2 | furo
3 | repoze.sphinx.autointerface
4 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | #
2 | # Generated from:
3 | # https://github.com/zopefoundation/meta/tree/master/config/c-code
4 |
5 | [build-system]
6 | requires = [
7 | "setuptools <= 75.6.0",
8 | ]
9 | build-backend = "setuptools.build_meta"
10 |
11 | [tool.coverage.run]
12 | branch = true
13 | source = ["zope.interface"]
14 | relative_files = true
15 |
16 | [tool.coverage.report]
17 | fail_under = 98
18 | precision = 2
19 | ignore_errors = true
20 | show_missing = true
21 | exclude_lines = [
22 | "pragma: no cover",
23 | "pragma: nocover",
24 | "except ImportError:",
25 | "raise NotImplementedError",
26 | "if __name__ == '__main__':",
27 | "self.fail",
28 | "raise AssertionError",
29 | "raise unittest.Skip",
30 | ]
31 |
32 | [tool.coverage.html]
33 | directory = "parts/htmlcov"
34 |
35 | [tool.coverage.paths]
36 | source = [
37 | "src/",
38 | ".tox/*/lib/python*/site-packages/",
39 | ".tox/pypy*/site-packages/",
40 | ]
41 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | # Generated from:
2 | # https://github.com/zopefoundation/meta/tree/master/config/c-code
3 |
4 | [zest.releaser]
5 | create-wheel = no
6 |
7 | [flake8]
8 | doctests = 1
9 | ignore =
10 | # module level import not at top of file: we are avoiding cycles
11 | E402,
12 | # import not used: we are publishing APIs, at least from __init__.py
13 | F401,
14 | # line break after binary operator
15 | W504
16 |
17 | [check-manifest]
18 | ignore =
19 | .editorconfig
20 | .meta.toml
21 | docs/_build/html/_sources/*
22 | docs/_build/doctest/*
23 | docs/_build/doctest/output.txt
24 | docs/_build/html/_sources/*
25 | docs/_build/html/_sources/api/*
26 | docs/_build/html/_static/scripts/*
27 |
28 | [isort]
29 | force_single_line = True
30 | combine_as_imports = True
31 | sections = FUTURE,STDLIB,THIRDPARTY,ZOPE,FIRSTPARTY,LOCALFOLDER
32 | known_third_party = docutils, pkg_resources, pytz
33 | known_zope =
34 | known_first_party =
35 | default_section = ZOPE
36 | line_length = 79
37 | lines_after_imports = 2
38 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2004-2007 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | # This package is developed by the Zope Toolkit project, documented here:
15 | # http://docs.zope.org/zopetoolkit
16 | # When developing and releasing this package, please follow the documented
17 | # Zope Toolkit policies as described by this documentation.
18 | ##############################################################################
19 | """Setup for zope.interface package
20 | """
21 |
22 | import os
23 | import sys
24 | from distutils.errors import CCompilerError
25 | from distutils.errors import DistutilsExecError
26 | from distutils.errors import DistutilsPlatformError
27 |
28 | from setuptools import Extension
29 | from setuptools import find_packages
30 | from setuptools import setup
31 | from setuptools.command.build_ext import build_ext
32 |
33 |
34 | version = '7.3.dev0'
35 |
36 |
37 | class optional_build_ext(build_ext):
38 | """This class subclasses build_ext and allows
39 | the building of C extensions to fail.
40 | """
41 |
42 | def run(self):
43 | try:
44 | build_ext.run(self)
45 | except DistutilsPlatformError as e:
46 | self._unavailable(e)
47 |
48 | def build_extension(self, ext):
49 | try:
50 | build_ext.build_extension(self, ext)
51 | except (CCompilerError, DistutilsExecError, OSError) as e:
52 | self._unavailable(e)
53 |
54 | def _unavailable(self, e):
55 | print('*' * 80)
56 | print("""WARNING:
57 |
58 | An optional code optimization (C extension) could not be compiled.
59 |
60 | Optimizations for this package will not be available!""")
61 | print("")
62 | print(e)
63 | print('*' * 80)
64 |
65 |
66 | codeoptimization_c = os.path.join('src', 'zope', 'interface',
67 | '_zope_interface_coptimizations.c')
68 | codeoptimization = [
69 | Extension(
70 | "zope.interface._zope_interface_coptimizations",
71 | [os.path.normcase(codeoptimization_c)]
72 | ),
73 | ]
74 |
75 | is_jython = 'java' in sys.platform
76 | is_pypy = hasattr(sys, 'pypy_version_info')
77 |
78 | # Jython cannot build the C optimizations. Nor, as of 7.3, can PyPy (
79 | # it doesn't have PySuper_Type) Everywhere else, defer the decision to
80 | # runtime.
81 | if is_jython or is_pypy:
82 | ext_modules = []
83 | else:
84 | ext_modules = codeoptimization
85 | tests_require = [
86 | # The test dependencies should NOT have direct or transitive
87 | # dependencies on zope.interface.
88 | 'coverage[toml]',
89 | 'zope.event',
90 | 'zope.testing',
91 | ]
92 | testing_extras = tests_require
93 |
94 |
95 | def read(*rnames):
96 | with open(os.path.join(os.path.dirname(__file__), *rnames)) as f:
97 | return f.read()
98 |
99 |
100 | long_description = (
101 | read('README.rst') + '\n' + read('CHANGES.rst')
102 | )
103 |
104 | setup(
105 | name='zope.interface',
106 | version=version,
107 | url='https://github.com/zopefoundation/zope.interface',
108 | license='ZPL-2.1',
109 | description='Interfaces for Python',
110 | author='Zope Foundation and Contributors',
111 | author_email='zope-dev@zope.dev',
112 | long_description=long_description,
113 | long_description_content_type='text/x-rst',
114 | classifiers=[
115 | "Development Status :: 5 - Production/Stable",
116 | "Intended Audience :: Developers",
117 | "License :: OSI Approved :: Zope Public License",
118 | "Operating System :: OS Independent",
119 | "Programming Language :: Python",
120 | "Programming Language :: Python :: 3",
121 | "Programming Language :: Python :: 3.9",
122 | "Programming Language :: Python :: 3.10",
123 | "Programming Language :: Python :: 3.11",
124 | "Programming Language :: Python :: 3.12",
125 | "Programming Language :: Python :: 3.13",
126 | "Programming Language :: Python :: Implementation :: CPython",
127 | "Programming Language :: Python :: Implementation :: PyPy",
128 | "Framework :: Zope :: 3",
129 | "Topic :: Software Development :: Libraries :: Python Modules",
130 | ],
131 | packages=find_packages('src'),
132 | package_dir={'': 'src'},
133 | namespace_packages=["zope"],
134 | cmdclass={
135 | 'build_ext': optional_build_ext,
136 | },
137 | include_package_data=True,
138 | zip_safe=False,
139 | install_requires=['setuptools'],
140 | python_requires='>=3.9',
141 | extras_require={
142 | 'docs': ['Sphinx',
143 | 'repoze.sphinx.autointerface',
144 | 'furo'],
145 | 'test': tests_require,
146 | 'testing': testing_extras,
147 | },
148 | ext_modules=ext_modules,
149 | keywords=['interface', 'components', 'plugins'],
150 | )
151 |
--------------------------------------------------------------------------------
/src/zope/__init__.py:
--------------------------------------------------------------------------------
1 | __import__('pkg_resources').declare_namespace(__name__)
2 |
--------------------------------------------------------------------------------
/src/zope/interface/__init__.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2001, 2002 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """Interfaces
15 |
16 | This package implements the Python "scarecrow" proposal.
17 |
18 | The package exports two objects, `Interface` and `Attribute` directly. It also
19 | exports several helper methods. Interface is used to create an interface with
20 | a class statement, as in:
21 |
22 | class IMyInterface(Interface):
23 | '''Interface documentation
24 | '''
25 |
26 | def meth(arg1, arg2):
27 | '''Documentation for meth
28 | '''
29 |
30 | # Note that there is no self argument
31 |
32 | To find out what you can do with interfaces, see the interface
33 | interface, `IInterface` in the `interfaces` module.
34 |
35 | The package has several public modules:
36 |
37 | o `declarations` provides utilities to declare interfaces on objects. It
38 | also provides a wide range of helpful utilities that aid in managing
39 | declared interfaces. Most of its public names are however imported here.
40 |
41 | o `document` has a utility for documenting an interface as structured text.
42 |
43 | o `exceptions` has the interface-defined exceptions
44 |
45 | o `interfaces` contains a list of all public interfaces for this package.
46 |
47 | o `verify` has utilities for verifying implementations of interfaces.
48 |
49 | See the module doc strings for more information.
50 | """
51 | __docformat__ = 'restructuredtext'
52 | # pylint:disable=wrong-import-position,unused-import
53 | from zope.interface.interface import Interface
54 | from zope.interface.interface import _wire
55 |
56 |
57 | # Need to actually get the interface elements to implement the right interfaces
58 | _wire()
59 | del _wire
60 |
61 | from zope.interface.declarations import Declaration # isort: skip
62 | # The following are to make spec pickles cleaner
63 | from zope.interface.declarations import Provides
64 | from zope.interface.declarations import alsoProvides
65 | from zope.interface.declarations import classImplements
66 | from zope.interface.declarations import classImplementsFirst
67 | from zope.interface.declarations import classImplementsOnly
68 | from zope.interface.declarations import directlyProvidedBy
69 | from zope.interface.declarations import directlyProvides
70 | from zope.interface.declarations import implementedBy
71 | from zope.interface.declarations import implementer
72 | from zope.interface.declarations import implementer_only
73 | from zope.interface.declarations import moduleProvides
74 | from zope.interface.declarations import named
75 | from zope.interface.declarations import noLongerProvides
76 | from zope.interface.declarations import providedBy
77 | from zope.interface.declarations import provider
78 | from zope.interface.exceptions import Invalid
79 | from zope.interface.interface import Attribute
80 | from zope.interface.interface import interfacemethod
81 | from zope.interface.interface import invariant
82 | from zope.interface.interface import taggedValue
83 | from zope.interface.interfaces import IInterfaceDeclaration
84 |
85 |
86 | moduleProvides(IInterfaceDeclaration)
87 |
88 | __all__ = ('Interface', 'Attribute') + tuple(IInterfaceDeclaration)
89 |
90 | assert all(k in globals() for k in __all__)
91 |
--------------------------------------------------------------------------------
/src/zope/interface/_compat.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2006 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """
15 | Support functions for dealing with differences in platforms, including Python
16 | versions and implementations.
17 |
18 | This file should have no imports from the rest of zope.interface because it is
19 | used during early bootstrapping.
20 | """
21 | import os
22 | import sys
23 |
24 |
25 | PY313_OR_OLDER = sys.version_info < (3, 14)
26 |
27 |
28 | def _normalize_name(name):
29 | if isinstance(name, bytes):
30 | name = str(name, 'ascii')
31 | if isinstance(name, str):
32 | return name
33 | raise TypeError("name must be a string or ASCII-only bytes")
34 |
35 |
36 | PYPY = hasattr(sys, 'pypy_version_info')
37 |
38 |
39 | def _c_optimizations_required():
40 | """
41 | Return a true value if the C optimizations are required.
42 |
43 | This uses the ``PURE_PYTHON`` variable as documented in `_use_c_impl`.
44 | """
45 | pure_env = os.environ.get('PURE_PYTHON')
46 | require_c = pure_env == "0"
47 | return require_c
48 |
49 |
50 | def _c_optimizations_available():
51 | """
52 | Return the C optimization module, if available, otherwise
53 | a false value.
54 |
55 | If the optimizations are required but not available, this
56 | raises the ImportError.
57 |
58 | This does not say whether they should be used or not.
59 | """
60 | catch = () if _c_optimizations_required() else (ImportError,)
61 | try:
62 | from zope.interface import _zope_interface_coptimizations as c_opt
63 | return c_opt
64 | except catch: # pragma: no cover (only Jython doesn't build extensions)
65 | return False
66 |
67 |
68 | def _c_optimizations_ignored():
69 | """
70 | The opposite of `_c_optimizations_required`.
71 | """
72 | pure_env = os.environ.get('PURE_PYTHON')
73 | return pure_env is not None and pure_env != "0"
74 |
75 |
76 | def _should_attempt_c_optimizations():
77 | """
78 | Return a true value if we should attempt to use the C optimizations.
79 |
80 | This takes into account whether we're on PyPy and the value of the
81 | ``PURE_PYTHON`` environment variable, as defined in `_use_c_impl`.
82 | """
83 | is_pypy = hasattr(sys, 'pypy_version_info')
84 |
85 | if _c_optimizations_required():
86 | return True
87 | if is_pypy:
88 | return False
89 | return not _c_optimizations_ignored()
90 |
91 |
92 | def _use_c_impl(py_impl, name=None, globs=None):
93 | """
94 | Decorator. Given an object implemented in Python, with a name like
95 | ``Foo``, import the corresponding C implementation from
96 | ``zope.interface._zope_interface_coptimizations`` with the name
97 | ``Foo`` and use it instead.
98 |
99 | If the ``PURE_PYTHON`` environment variable is set to any value
100 | other than ``"0"``, or we're on PyPy, ignore the C implementation
101 | and return the Python version. If the C implementation cannot be
102 | imported, return the Python version. If ``PURE_PYTHON`` is set to
103 | 0, *require* the C implementation (let the ImportError propagate);
104 | note that PyPy can import the C implementation in this case (and all
105 | tests pass).
106 |
107 | In all cases, the Python version is kept available. in the module
108 | globals with the name ``FooPy`` and the name ``FooFallback`` (both
109 | conventions have been used; the C implementation of some functions
110 | looks for the ``Fallback`` version, as do some of the Sphinx
111 | documents).
112 |
113 | Example::
114 |
115 | @_use_c_impl
116 | class Foo(object):
117 | ...
118 | """
119 | name = name or py_impl.__name__
120 | globs = globs or sys._getframe(1).f_globals
121 |
122 | def find_impl():
123 | if not _should_attempt_c_optimizations():
124 | return py_impl
125 |
126 | c_opt = _c_optimizations_available()
127 | if not c_opt: # pragma: no cover (Jython doesn't build extensions)
128 | return py_impl
129 |
130 | __traceback_info__ = c_opt
131 | return getattr(c_opt, name)
132 |
133 | c_impl = find_impl()
134 | # Always make available by the FooPy name and FooFallback
135 | # name (for testing and documentation)
136 | globs[name + 'Py'] = py_impl
137 | globs[name + 'Fallback'] = py_impl
138 |
139 | return c_impl
140 |
--------------------------------------------------------------------------------
/src/zope/interface/_flatten.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2002 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """Adapter-style interface registry
15 |
16 | See Adapter class.
17 | """
18 | from zope.interface import Declaration
19 |
20 |
21 | def _flatten(implements, include_None=0):
22 |
23 | try:
24 | r = implements.flattened()
25 | except AttributeError:
26 | if implements is None:
27 | r = ()
28 | else:
29 | r = Declaration(implements).flattened()
30 |
31 | if not include_None:
32 | return r
33 |
34 | r = list(r)
35 | r.append(None)
36 | return r
37 |
--------------------------------------------------------------------------------
/src/zope/interface/advice.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2003 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """Class advice.
15 |
16 | This module was adapted from 'protocols.advice', part of the Python
17 | Enterprise Application Kit (PEAK). Please notify the PEAK authors
18 | (pje@telecommunity.com and tsarna@sarna.org) if bugs are found or
19 | Zope-specific changes are required, so that the PEAK version of this module
20 | can be kept in sync.
21 |
22 | PEAK is a Python application framework that interoperates with (but does
23 | not require) Zope 3 and Twisted. It provides tools for manipulating UML
24 | models, object-relational persistence, aspect-oriented programming, and more.
25 | Visit the PEAK home page at http://peak.telecommunity.com for more information.
26 | """
27 |
28 | from types import FunctionType
29 |
30 |
31 | __all__ = [
32 | 'determineMetaclass',
33 | 'getFrameInfo',
34 | 'isClassAdvisor',
35 | 'minimalBases',
36 | ]
37 |
38 | import sys
39 |
40 |
41 | def getFrameInfo(frame):
42 | """Return (kind,module,locals,globals) for a frame
43 |
44 | 'kind' is one of "exec", "module", "class", "function call", or "unknown".
45 | """
46 |
47 | f_locals = frame.f_locals
48 | f_globals = frame.f_globals
49 |
50 | sameNamespace = f_locals is f_globals
51 | hasModule = '__module__' in f_locals
52 | hasName = '__name__' in f_globals
53 |
54 | sameName = hasModule and hasName
55 | sameName = sameName and f_globals['__name__'] == f_locals['__module__']
56 |
57 | module = hasName and sys.modules.get(f_globals['__name__']) or None
58 |
59 | namespaceIsModule = module and module.__dict__ is f_globals
60 |
61 | if not namespaceIsModule:
62 | # some kind of funky exec
63 | kind = "exec"
64 | elif sameNamespace and not hasModule:
65 | kind = "module"
66 | elif sameName and not sameNamespace:
67 | kind = "class"
68 | elif not sameNamespace:
69 | kind = "function call"
70 | else: # pragma: no cover
71 | # How can you have f_locals is f_globals, and have '__module__'
72 | # set? # This is probably module-level code, but with a
73 | # '__module__' variable.
74 | kind = "unknown"
75 | return kind, module, f_locals, f_globals
76 |
77 |
78 | def isClassAdvisor(ob):
79 | """True if 'ob' is a class advisor function"""
80 | return isinstance(ob, FunctionType) and hasattr(ob, 'previousMetaclass')
81 |
82 |
83 | def determineMetaclass(bases, explicit_mc=None):
84 | """Determine metaclass from 1+ bases and optional explicit __metaclass__"""
85 |
86 | meta = [getattr(b, '__class__', type(b)) for b in bases]
87 |
88 | if explicit_mc is not None:
89 | # The explicit metaclass needs to be verified for compatibility
90 | # as well, and allowed to resolve the incompatible bases, if any
91 | meta.append(explicit_mc)
92 |
93 | if len(meta) == 1:
94 | # easy case
95 | return meta[0]
96 |
97 | candidates = minimalBases(meta) # minimal set of metaclasses
98 |
99 | if len(candidates) > 1:
100 | # We could auto-combine, but for now we won't...
101 | raise TypeError("Incompatible metatypes", bases)
102 |
103 | # Just one, return it
104 | return candidates[0]
105 |
106 |
107 | def minimalBases(classes):
108 | """Reduce a list of base classes to its ordered minimum equivalent"""
109 | candidates = []
110 |
111 | for m in classes:
112 | for n in classes:
113 | if issubclass(n, m) and m is not n:
114 | break
115 | else:
116 | # m has no subclasses in 'classes'
117 | if m in candidates:
118 | candidates.remove(m) # ensure that we're later in the list
119 | candidates.append(m)
120 |
121 | return candidates
122 |
--------------------------------------------------------------------------------
/src/zope/interface/common/builtins.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | # Copyright (c) 2020 Zope Foundation and Contributors.
3 | # All Rights Reserved.
4 | #
5 | # This software is subject to the provisions of the Zope Public License,
6 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
7 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 | # FOR A PARTICULAR PURPOSE.
11 | ##############################################################################
12 | """
13 | Interface definitions for builtin types.
14 |
15 | After this module is imported, the standard library types will declare
16 | that they implement the appropriate interface.
17 |
18 | .. versionadded:: 5.0.0
19 | """
20 |
21 | from zope.interface import classImplements
22 | from zope.interface._compat import PY313_OR_OLDER
23 | from zope.interface.common import collections
24 | from zope.interface.common import io
25 | from zope.interface.common import numbers
26 |
27 |
28 | __all__ = [
29 | 'IList',
30 | 'ITuple',
31 | 'ITextString',
32 | 'IByteString',
33 | 'INativeString',
34 | 'IBool',
35 | 'IDict',
36 | 'IFile',
37 | ]
38 |
39 |
40 | # pylint:disable=no-self-argument
41 | class IList(collections.IMutableSequence):
42 | """
43 | Interface for :class:`list`
44 | """
45 | extra_classes = (list,)
46 |
47 | def sort(key=None, reverse=False):
48 | """
49 | Sort the list in place and return None.
50 |
51 | *key* and *reverse* must be passed by name only.
52 | """
53 |
54 |
55 | class ITuple(collections.ISequence):
56 | """
57 | Interface for :class:`tuple`
58 | """
59 | extra_classes = (tuple,)
60 |
61 |
62 | class ITextString(collections.ISequence):
63 | """
64 | Interface for text ("unicode") strings.
65 |
66 | This is :class:`str`
67 | """
68 | extra_classes = (str,)
69 |
70 |
71 | if PY313_OR_OLDER:
72 | class IByteString(collections.IByteString):
73 | """
74 | Interface for immutable byte strings.
75 |
76 | On all Python versions this is :class:`bytes`.
77 |
78 | Unlike :class:`zope.interface.common.collections.IByteString`
79 | (the parent of this interface) this does *not* include
80 | :class:`bytearray`.
81 | """
82 | extra_classes = (bytes,)
83 |
84 |
85 | class INativeString(ITextString):
86 | """
87 | Interface for native strings.
88 |
89 | On all Python versions, this is :class:`str`. Tt extends
90 | :class:`ITextString`.
91 | """
92 |
93 |
94 | # We're not extending ABCInterface so extra_classes won't work
95 | classImplements(str, INativeString)
96 |
97 |
98 | class IBool(numbers.IIntegral):
99 | """
100 | Interface for :class:`bool`
101 | """
102 | extra_classes = (bool,)
103 |
104 |
105 | class IDict(collections.IMutableMapping):
106 | """
107 | Interface for :class:`dict`
108 | """
109 | extra_classes = (dict,)
110 |
111 |
112 | class IFile(io.IIOBase):
113 | """
114 | Interface for :class:`file`.
115 |
116 | It is recommended to use the interfaces from
117 | :mod:`zope.interface.common.io` instead of this interface.
118 |
119 | On Python 3, there is no single implementation of this interface;
120 | depending on the arguments, the :func:`open` builtin can return
121 | many different classes that implement different interfaces from
122 | :mod:`zope.interface.common.io`.
123 | """
124 | extra_classes = ()
125 |
--------------------------------------------------------------------------------
/src/zope/interface/common/collections.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | # Copyright (c) 2020 Zope Foundation and Contributors.
3 | # All Rights Reserved.
4 | #
5 | # This software is subject to the provisions of the Zope Public License,
6 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
7 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 | # FOR A PARTICULAR PURPOSE.
11 | ##############################################################################
12 | """
13 | Interface definitions paralleling the abstract base classes defined in
14 | :mod:`collections.abc`.
15 |
16 | After this module is imported, the standard library types will declare that
17 | they implement the appropriate interface. While most standard library types
18 | will properly implement that interface (that is, ``verifyObject(ISequence,
19 | list()))`` will pass, for example), a few might not:
20 |
21 | - `memoryview` doesn't feature all the defined methods of
22 | ``ISequence`` such as ``count``; it is still declared to provide
23 | ``ISequence`` though.
24 |
25 | - `collections.deque.pop` doesn't accept the ``index`` argument of
26 | `collections.abc.MutableSequence.pop`
27 |
28 | - `range.index` does not accept the ``start`` and ``stop`` arguments.
29 |
30 | .. versionadded:: 5.0.0
31 | """
32 |
33 | import sys
34 | from abc import ABCMeta
35 | from collections import OrderedDict
36 | from collections import UserDict
37 | from collections import UserList
38 | from collections import UserString
39 | from collections import abc
40 |
41 | from zope.interface._compat import PY313_OR_OLDER
42 | from zope.interface.common import ABCInterface
43 | from zope.interface.common import optional
44 |
45 |
46 | # pylint:disable=inherit-non-class,
47 | # pylint:disable=no-self-argument,no-method-argument
48 | # pylint:disable=unexpected-special-method-signature
49 | # pylint:disable=no-value-for-parameter
50 |
51 |
52 | def _new_in_ver(name, ver,
53 | bases_if_missing=(ABCMeta,),
54 | register_if_missing=()):
55 | if ver:
56 | return getattr(abc, name)
57 |
58 | # TODO: It's a shame to have to repeat the bases when
59 | # the ABC is missing. Can we DRY that?
60 | missing = ABCMeta(name, bases_if_missing, {
61 | '__doc__': "The ABC %s is not defined in this version of Python." % (
62 | name
63 | ),
64 | })
65 |
66 | for c in register_if_missing:
67 | missing.register(c)
68 |
69 | return missing
70 |
71 |
72 | __all__ = [
73 | 'IAsyncGenerator',
74 | 'IAsyncIterable',
75 | 'IAsyncIterator',
76 | 'IAwaitable',
77 | 'ICollection',
78 | 'IContainer',
79 | 'ICoroutine',
80 | 'IGenerator',
81 | 'IHashable',
82 | 'IItemsView',
83 | 'IIterable',
84 | 'IIterator',
85 | 'IKeysView',
86 | 'IMapping',
87 | 'IMappingView',
88 | 'IMutableMapping',
89 | 'IMutableSequence',
90 | 'IMutableSet',
91 | 'IReversible',
92 | 'ISequence',
93 | 'ISet',
94 | 'ISized',
95 | 'IValuesView',
96 | ]
97 |
98 |
99 | class IContainer(ABCInterface):
100 | abc = abc.Container
101 |
102 | @optional
103 | def __contains__(other):
104 | """
105 | Optional method. If not provided, the interpreter will use
106 | ``__iter__`` or the old ``__getitem__`` protocol
107 | to implement ``in``.
108 | """
109 |
110 |
111 | class IHashable(ABCInterface):
112 | abc = abc.Hashable
113 |
114 |
115 | class IIterable(ABCInterface):
116 | abc = abc.Iterable
117 |
118 | @optional
119 | def __iter__():
120 | """
121 | Optional method. If not provided, the interpreter will
122 | implement `iter` using the old ``__getitem__`` protocol.
123 | """
124 |
125 |
126 | class IIterator(IIterable):
127 | abc = abc.Iterator
128 |
129 |
130 | class IReversible(IIterable):
131 | abc = _new_in_ver('Reversible', True, (IIterable.getABC(),))
132 |
133 | @optional
134 | def __reversed__():
135 | """
136 | Optional method. If this isn't present, the interpreter
137 | will use ``__len__`` and ``__getitem__`` to implement the
138 | `reversed` builtin.
139 | """
140 |
141 |
142 | class IGenerator(IIterator):
143 | # New in Python 3.5
144 | abc = _new_in_ver('Generator', True, (IIterator.getABC(),))
145 |
146 |
147 | class ISized(ABCInterface):
148 | abc = abc.Sized
149 |
150 |
151 | # ICallable is not defined because there's no standard signature.
152 |
153 |
154 | class ICollection(ISized,
155 | IIterable,
156 | IContainer):
157 | abc = _new_in_ver(
158 | 'Collection',
159 | True,
160 | (ISized.getABC(), IIterable.getABC(), IContainer.getABC())
161 | )
162 |
163 |
164 | class ISequence(IReversible,
165 | ICollection):
166 | abc = abc.Sequence
167 | extra_classes = (UserString,)
168 | # On Python 2, basestring was registered as an ISequence, and
169 | # its subclass str is an IByteString. If we also register str as
170 | # an ISequence, that tends to lead to inconsistent resolution order.
171 | ignored_classes = ()
172 |
173 | @optional
174 | def __reversed__():
175 | """
176 | Optional method. If this isn't present, the interpreter
177 | will use ``__len__`` and ``__getitem__`` to implement the
178 | `reversed` builtin.
179 | """
180 |
181 | @optional
182 | def __iter__():
183 | """
184 | Optional method. If not provided, the interpreter will
185 | implement `iter` using the old ``__getitem__`` protocol.
186 | """
187 |
188 |
189 | class IMutableSequence(ISequence):
190 | abc = abc.MutableSequence
191 | extra_classes = (UserList,)
192 |
193 |
194 | if PY313_OR_OLDER:
195 | class IByteString(ISequence):
196 | """
197 | This unifies `bytes` and `bytearray`.
198 | """
199 | abc = _new_in_ver(
200 | 'ByteString', True, (ISequence.getABC(),), (bytes, bytearray),
201 | )
202 |
203 |
204 | class ISet(ICollection):
205 | abc = abc.Set
206 |
207 |
208 | class IMutableSet(ISet):
209 | abc = abc.MutableSet
210 |
211 |
212 | class IMapping(ICollection):
213 | abc = abc.Mapping
214 | extra_classes = (dict,)
215 | # OrderedDict is a subclass of dict. On CPython 2,
216 | # it winds up registered as a IMutableMapping, which
217 | # produces an inconsistent IRO if we also try to register it
218 | # here.
219 | ignored_classes = (OrderedDict,)
220 |
221 |
222 | class IMutableMapping(IMapping):
223 | abc = abc.MutableMapping
224 | extra_classes = (dict, UserDict,)
225 | ignored_classes = (OrderedDict,)
226 |
227 |
228 | class IMappingView(ISized):
229 | abc = abc.MappingView
230 |
231 |
232 | class IItemsView(IMappingView, ISet):
233 | abc = abc.ItemsView
234 |
235 |
236 | class IKeysView(IMappingView, ISet):
237 | abc = abc.KeysView
238 |
239 |
240 | class IValuesView(IMappingView, ICollection):
241 | abc = abc.ValuesView
242 |
243 | @optional
244 | def __contains__(other):
245 | """
246 | Optional method. If not provided, the interpreter will use
247 | ``__iter__`` or the old ``__len__`` and ``__getitem__`` protocol
248 | to implement ``in``.
249 | """
250 |
251 |
252 | class IAwaitable(ABCInterface):
253 | abc = _new_in_ver('Awaitable', True)
254 |
255 |
256 | class ICoroutine(IAwaitable):
257 | abc = _new_in_ver('Coroutine', True)
258 |
259 |
260 | class IAsyncIterable(ABCInterface):
261 | abc = _new_in_ver('AsyncIterable', True)
262 |
263 |
264 | class IAsyncIterator(IAsyncIterable):
265 | abc = _new_in_ver('AsyncIterator', True)
266 |
267 |
268 | class IAsyncGenerator(IAsyncIterator):
269 | abc = _new_in_ver('AsyncGenerator', True)
270 |
--------------------------------------------------------------------------------
/src/zope/interface/common/interfaces.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2003 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """Interfaces for standard python exceptions
15 | """
16 | from zope.interface import Interface
17 | from zope.interface import classImplements
18 |
19 |
20 | class IException(Interface):
21 | "Interface for `Exception`"
22 |
23 |
24 | classImplements(Exception, IException) # noqa E305
25 |
26 |
27 | class IStandardError(IException):
28 | "Interface for `StandardError` (no longer existing.)"
29 |
30 |
31 | class IWarning(IException):
32 | "Interface for `Warning`"
33 |
34 |
35 | classImplements(Warning, IWarning) # noqa E305
36 |
37 |
38 | class ISyntaxError(IStandardError):
39 | "Interface for `SyntaxError`"
40 |
41 |
42 | classImplements(SyntaxError, ISyntaxError) # noqa E305
43 |
44 |
45 | class ILookupError(IStandardError):
46 | "Interface for `LookupError`"
47 |
48 |
49 | classImplements(LookupError, ILookupError) # noqa E305
50 |
51 |
52 | class IValueError(IStandardError):
53 | "Interface for `ValueError`"
54 |
55 |
56 | classImplements(ValueError, IValueError) # noqa E305
57 |
58 |
59 | class IRuntimeError(IStandardError):
60 | "Interface for `RuntimeError`"
61 |
62 |
63 | classImplements(RuntimeError, IRuntimeError) # noqa E305
64 |
65 |
66 | class IArithmeticError(IStandardError):
67 | "Interface for `ArithmeticError`"
68 |
69 |
70 | classImplements(ArithmeticError, IArithmeticError) # noqa E305
71 |
72 |
73 | class IAssertionError(IStandardError):
74 | "Interface for `AssertionError`"
75 |
76 |
77 | classImplements(AssertionError, IAssertionError) # noqa E305
78 |
79 |
80 | class IAttributeError(IStandardError):
81 | "Interface for `AttributeError`"
82 |
83 |
84 | classImplements(AttributeError, IAttributeError) # noqa E305
85 |
86 |
87 | class IDeprecationWarning(IWarning):
88 | "Interface for `DeprecationWarning`"
89 |
90 |
91 | classImplements(DeprecationWarning, IDeprecationWarning) # noqa E305
92 |
93 |
94 | class IEOFError(IStandardError):
95 | "Interface for `EOFError`"
96 |
97 |
98 | classImplements(EOFError, IEOFError) # noqa E305
99 |
100 |
101 | class IEnvironmentError(IStandardError):
102 | "Interface for `EnvironmentError`"
103 |
104 |
105 | classImplements(EnvironmentError, IEnvironmentError) # noqa E305
106 |
107 |
108 | class IFloatingPointError(IArithmeticError):
109 | "Interface for `FloatingPointError`"
110 |
111 |
112 | classImplements(FloatingPointError, IFloatingPointError) # noqa E305
113 |
114 |
115 | class IIOError(IEnvironmentError):
116 | "Interface for `IOError`"
117 |
118 |
119 | classImplements(IOError, IIOError) # noqa E305
120 |
121 |
122 | class IImportError(IStandardError):
123 | "Interface for `ImportError`"
124 |
125 |
126 | classImplements(ImportError, IImportError) # noqa E305
127 |
128 |
129 | class IIndentationError(ISyntaxError):
130 | "Interface for `IndentationError`"
131 |
132 |
133 | classImplements(IndentationError, IIndentationError) # noqa E305
134 |
135 |
136 | class IIndexError(ILookupError):
137 | "Interface for `IndexError`"
138 |
139 |
140 | classImplements(IndexError, IIndexError) # noqa E305
141 |
142 |
143 | class IKeyError(ILookupError):
144 | "Interface for `KeyError`"
145 |
146 |
147 | classImplements(KeyError, IKeyError) # noqa E305
148 |
149 |
150 | class IKeyboardInterrupt(IStandardError):
151 | "Interface for `KeyboardInterrupt`"
152 |
153 |
154 | classImplements(KeyboardInterrupt, IKeyboardInterrupt) # noqa E305
155 |
156 |
157 | class IMemoryError(IStandardError):
158 | "Interface for `MemoryError`"
159 |
160 |
161 | classImplements(MemoryError, IMemoryError) # noqa E305
162 |
163 |
164 | class INameError(IStandardError):
165 | "Interface for `NameError`"
166 |
167 |
168 | classImplements(NameError, INameError) # noqa E305
169 |
170 |
171 | class INotImplementedError(IRuntimeError):
172 | "Interface for `NotImplementedError`"
173 |
174 |
175 | classImplements(NotImplementedError, INotImplementedError) # noqa E305
176 |
177 |
178 | class IOSError(IEnvironmentError):
179 | "Interface for `OSError`"
180 |
181 |
182 | classImplements(OSError, IOSError) # noqa E305
183 |
184 |
185 | class IOverflowError(IArithmeticError):
186 | "Interface for `ArithmeticError`"
187 |
188 |
189 | classImplements(OverflowError, IOverflowError) # noqa E305
190 |
191 |
192 | class IOverflowWarning(IWarning):
193 | """Deprecated, no standard class implements this.
194 |
195 | This was the interface for ``OverflowWarning`` prior to Python 2.5,
196 | but that class was removed for all versions after that.
197 | """
198 |
199 |
200 | class IReferenceError(IStandardError):
201 | "Interface for `ReferenceError`"
202 |
203 |
204 | classImplements(ReferenceError, IReferenceError) # noqa E305
205 |
206 |
207 | class IRuntimeWarning(IWarning):
208 | "Interface for `RuntimeWarning`"
209 |
210 |
211 | classImplements(RuntimeWarning, IRuntimeWarning) # noqa E305
212 |
213 |
214 | class IStopIteration(IException):
215 | "Interface for `StopIteration`"
216 |
217 |
218 | classImplements(StopIteration, IStopIteration) # noqa E305
219 |
220 |
221 | class ISyntaxWarning(IWarning):
222 | "Interface for `SyntaxWarning`"
223 |
224 |
225 | classImplements(SyntaxWarning, ISyntaxWarning) # noqa E305
226 |
227 |
228 | class ISystemError(IStandardError):
229 | "Interface for `SystemError`"
230 |
231 |
232 | classImplements(SystemError, ISystemError) # noqa E305
233 |
234 |
235 | class ISystemExit(IException):
236 | "Interface for `SystemExit`"
237 |
238 |
239 | classImplements(SystemExit, ISystemExit) # noqa E305
240 |
241 |
242 | class ITabError(IIndentationError):
243 | "Interface for `TabError`"
244 |
245 |
246 | classImplements(TabError, ITabError) # noqa E305
247 |
248 |
249 | class ITypeError(IStandardError):
250 | "Interface for `TypeError`"
251 |
252 |
253 | classImplements(TypeError, ITypeError) # noqa E305
254 |
255 |
256 | class IUnboundLocalError(INameError):
257 | "Interface for `UnboundLocalError`"
258 |
259 |
260 | classImplements(UnboundLocalError, IUnboundLocalError) # noqa E305
261 |
262 |
263 | class IUnicodeError(IValueError):
264 | "Interface for `UnicodeError`"
265 |
266 |
267 | classImplements(UnicodeError, IUnicodeError) # noqa E305
268 |
269 |
270 | class IUserWarning(IWarning):
271 | "Interface for `UserWarning`"
272 |
273 |
274 | classImplements(UserWarning, IUserWarning) # noqa E305
275 |
276 |
277 | class IZeroDivisionError(IArithmeticError):
278 | "Interface for `ZeroDivisionError`"
279 |
280 |
281 | classImplements(ZeroDivisionError, IZeroDivisionError) # noqa E305
282 |
--------------------------------------------------------------------------------
/src/zope/interface/common/io.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | # Copyright (c) 2020 Zope Foundation and Contributors.
3 | # All Rights Reserved.
4 | #
5 | # This software is subject to the provisions of the Zope Public License,
6 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
7 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 | # FOR A PARTICULAR PURPOSE.
11 | ##############################################################################
12 | """
13 | Interface definitions paralleling the abstract base classes defined in
14 | :mod:`io`.
15 |
16 | After this module is imported, the standard library types will declare
17 | that they implement the appropriate interface.
18 |
19 | .. versionadded:: 5.0.0
20 | """
21 |
22 | import io as abc
23 |
24 | from zope.interface.common import ABCInterface
25 |
26 |
27 | # pylint:disable=inherit-non-class,
28 | # pylint:disable=no-member
29 |
30 | class IIOBase(ABCInterface):
31 | abc = abc.IOBase
32 |
33 |
34 | class IRawIOBase(IIOBase):
35 | abc = abc.RawIOBase
36 |
37 |
38 | class IBufferedIOBase(IIOBase):
39 | abc = abc.BufferedIOBase
40 | extra_classes = ()
41 |
42 |
43 | class ITextIOBase(IIOBase):
44 | abc = abc.TextIOBase
45 |
--------------------------------------------------------------------------------
/src/zope/interface/common/mapping.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2001, 2002 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """
15 | Mapping Interfaces.
16 |
17 | Importing this module does *not* mark any standard classes as
18 | implementing any of these interfaces.
19 |
20 | While this module is not deprecated, new code should generally use
21 | :mod:`zope.interface.common.collections`, specifically
22 | :class:`~zope.interface.common.collections.IMapping` and
23 | :class:`~zope.interface.common.collections.IMutableMapping`. This
24 | module is occasionally useful for its extremely fine grained breakdown
25 | of interfaces.
26 |
27 | The standard library :class:`dict` and :class:`collections.UserDict`
28 | implement ``IMutableMapping``, but *do not* implement any of the
29 | interfaces in this module.
30 | """
31 | from zope.interface import Interface
32 | from zope.interface.common import collections
33 |
34 |
35 | class IItemMapping(Interface):
36 | """Simplest readable mapping object
37 | """
38 |
39 | def __getitem__(key):
40 | """Get a value for a key
41 |
42 | A `KeyError` is raised if there is no value for the key.
43 | """
44 |
45 |
46 | class IReadMapping(collections.IContainer, IItemMapping):
47 | """
48 | Basic mapping interface.
49 |
50 | .. versionchanged:: 5.0.0
51 | Extend ``IContainer``
52 | """
53 |
54 | def get(key, default=None):
55 | """Get a value for a key
56 |
57 | The default is returned if there is no value for the key.
58 | """
59 |
60 | def __contains__(key):
61 | """Tell if a key exists in the mapping."""
62 | # Optional in IContainer, required by this interface.
63 |
64 |
65 | class IWriteMapping(Interface):
66 | """Mapping methods for changing data"""
67 |
68 | def __delitem__(key):
69 | """Delete a value from the mapping using the key."""
70 |
71 | def __setitem__(key, value):
72 | """Set a new item in the mapping."""
73 |
74 |
75 | class IEnumerableMapping(collections.ISized, IReadMapping):
76 | """
77 | Mapping objects whose items can be enumerated.
78 |
79 | .. versionchanged:: 5.0.0
80 | Extend ``ISized``
81 | """
82 |
83 | def keys():
84 | """Return the keys of the mapping object.
85 | """
86 |
87 | def __iter__():
88 | """Return an iterator for the keys of the mapping object.
89 | """
90 |
91 | def values():
92 | """Return the values of the mapping object.
93 | """
94 |
95 | def items():
96 | """Return the items of the mapping object.
97 | """
98 |
99 |
100 | class IMapping(IWriteMapping, IEnumerableMapping):
101 | ''' Simple mapping interface '''
102 |
103 |
104 | class IIterableMapping(IEnumerableMapping):
105 | """A mapping that has distinct methods for iterating
106 | without copying.
107 |
108 | """
109 |
110 |
111 | class IClonableMapping(Interface):
112 | """Something that can produce a copy of itself.
113 |
114 | This is available in `dict`.
115 | """
116 |
117 | def copy():
118 | "return copy of dict"
119 |
120 |
121 | class IExtendedReadMapping(IIterableMapping):
122 | """
123 | Something with a particular method equivalent to ``__contains__``.
124 |
125 | On Python 2, `dict` provided the ``has_key`` method, but it was removed
126 | in Python 3.
127 | """
128 |
129 |
130 | class IExtendedWriteMapping(IWriteMapping):
131 | """Additional mutation methods.
132 |
133 | These are all provided by `dict`.
134 | """
135 |
136 | def clear():
137 | "delete all items"
138 |
139 | def update(d):
140 | " Update D from E: for k in E.keys(): D[k] = E[k]"
141 |
142 | def setdefault(key, default=None):
143 | "D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D"
144 |
145 | def pop(k, default=None):
146 | """
147 | pop(k[,default]) -> value
148 |
149 | Remove specified key and return the corresponding value.
150 |
151 | If key is not found, *default* is returned if given, otherwise
152 | `KeyError` is raised. Note that *default* must not be passed by
153 | name.
154 | """
155 |
156 | def popitem():
157 | """remove and return some (key, value) pair as a
158 | 2-tuple; but raise KeyError if mapping is empty"""
159 |
160 |
161 | class IFullMapping(
162 | collections.IMutableMapping,
163 | IExtendedReadMapping,
164 | IExtendedWriteMapping,
165 | IClonableMapping,
166 | IMapping,
167 | ):
168 | """
169 | Full mapping interface.
170 |
171 | Most uses of this interface should instead use
172 | :class:`~zope.interface.commons.collections.IMutableMapping` (one of the
173 | bases of this interface). The required methods are the same.
174 |
175 | .. versionchanged:: 5.0.0
176 | Extend ``IMutableMapping``
177 | """
178 |
--------------------------------------------------------------------------------
/src/zope/interface/common/numbers.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | # Copyright (c) 2020 Zope Foundation and Contributors.
3 | # All Rights Reserved.
4 | #
5 | # This software is subject to the provisions of the Zope Public License,
6 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
7 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 | # FOR A PARTICULAR PURPOSE.
11 | ##############################################################################
12 | """
13 | Interface definitions paralleling the abstract base classes defined in
14 | :mod:`numbers`.
15 |
16 | After this module is imported, the standard library types will declare
17 | that they implement the appropriate interface.
18 |
19 | .. versionadded:: 5.0.0
20 | """
21 |
22 | import numbers as abc
23 |
24 | from zope.interface.common import ABCInterface
25 | from zope.interface.common import optional
26 |
27 |
28 | # pylint:disable=inherit-non-class,
29 | # pylint:disable=no-self-argument,no-method-argument
30 | # pylint:disable=unexpected-special-method-signature
31 | # pylint:disable=no-value-for-parameter
32 |
33 |
34 | class INumber(ABCInterface):
35 | abc = abc.Number
36 |
37 |
38 | class IComplex(INumber):
39 | abc = abc.Complex
40 |
41 | @optional
42 | def __complex__():
43 | """
44 | Rarely implemented, even in builtin types.
45 | """
46 |
47 |
48 | class IReal(IComplex):
49 | abc = abc.Real
50 |
51 | @optional
52 | def __complex__():
53 | """
54 | Rarely implemented, even in builtin types.
55 | """
56 |
57 | __floor__ = __ceil__ = __complex__
58 |
59 |
60 | class IRational(IReal):
61 | abc = abc.Rational
62 |
63 |
64 | class IIntegral(IRational):
65 | abc = abc.Integral
66 |
--------------------------------------------------------------------------------
/src/zope/interface/common/sequence.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2001, 2002 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """
15 | Sequence Interfaces
16 |
17 | Importing this module does *not* mark any standard classes as
18 | implementing any of these interfaces.
19 |
20 | While this module is not deprecated, new code should generally use
21 | :mod:`zope.interface.common.collections`, specifically
22 | :class:`~zope.interface.common.collections.ISequence` and
23 | :class:`~zope.interface.common.collections.IMutableSequence`. This
24 | module is occasionally useful for its fine-grained breakdown of interfaces.
25 |
26 | The standard library :class:`list`, :class:`tuple` and
27 | :class:`collections.UserList`, among others, implement ``ISequence``
28 | or ``IMutableSequence`` but *do not* implement any of the interfaces
29 | in this module.
30 | """
31 |
32 | __docformat__ = 'restructuredtext'
33 | from zope.interface import Interface
34 | from zope.interface.common import collections
35 |
36 |
37 | class IMinimalSequence(collections.IIterable):
38 | """Most basic sequence interface.
39 |
40 | All sequences are iterable. This requires at least one of the
41 | following:
42 |
43 | - a `__getitem__()` method that takes a single argument; integer
44 | values starting at 0 must be supported, and `IndexError` should
45 | be raised for the first index for which there is no value, or
46 |
47 | - an `__iter__()` method that returns an iterator as defined in
48 | the Python documentation (http://docs.python.org/lib/typeiter.html).
49 |
50 | """
51 |
52 | def __getitem__(index):
53 | """``x.__getitem__(index) <==> x[index]``
54 |
55 | Declaring this interface does not specify whether `__getitem__`
56 | supports slice objects."""
57 |
58 |
59 | class IFiniteSequence(collections.ISized, IMinimalSequence):
60 | """
61 | A sequence of bound size.
62 |
63 | .. versionchanged:: 5.0.0
64 | Extend ``ISized``
65 | """
66 |
67 |
68 | class IReadSequence(collections.IContainer, IFiniteSequence):
69 | """
70 | read interface shared by tuple and list
71 |
72 | This interface is similar to
73 | :class:`~zope.interface.common.collections.ISequence`, but
74 | requires that all instances be totally ordered. Most users
75 | should prefer ``ISequence``.
76 |
77 | .. versionchanged:: 5.0.0
78 | Extend ``IContainer``
79 | """
80 |
81 | def __contains__(item):
82 | """``x.__contains__(item) <==> item in x``"""
83 | # Optional in IContainer, required here.
84 |
85 | def __lt__(other):
86 | """``x.__lt__(other) <==> x < other``"""
87 |
88 | def __le__(other):
89 | """``x.__le__(other) <==> x <= other``"""
90 |
91 | def __eq__(other):
92 | """``x.__eq__(other) <==> x == other``"""
93 |
94 | def __ne__(other):
95 | """``x.__ne__(other) <==> x != other``"""
96 |
97 | def __gt__(other):
98 | """``x.__gt__(other) <==> x > other``"""
99 |
100 | def __ge__(other):
101 | """``x.__ge__(other) <==> x >= other``"""
102 |
103 | def __add__(other):
104 | """``x.__add__(other) <==> x + other``"""
105 |
106 | def __mul__(n):
107 | """``x.__mul__(n) <==> x * n``"""
108 |
109 | def __rmul__(n):
110 | """``x.__rmul__(n) <==> n * x``"""
111 |
112 |
113 | class IExtendedReadSequence(IReadSequence):
114 | """Full read interface for lists"""
115 |
116 | def count(item):
117 | """Return number of occurrences of value"""
118 |
119 | def index(item, *args):
120 | """index(value, [start, [stop]]) -> int
121 |
122 | Return first index of *value*
123 | """
124 |
125 |
126 | class IUniqueMemberWriteSequence(Interface):
127 | """The write contract for a sequence that may enforce unique members"""
128 |
129 | def __setitem__(index, item):
130 | """``x.__setitem__(index, item) <==> x[index] = item``
131 |
132 | Declaring this interface does not specify whether `__setitem__`
133 | supports slice objects.
134 | """
135 |
136 | def __delitem__(index):
137 | """``x.__delitem__(index) <==> del x[index]``
138 |
139 | Declaring this interface does not specify whether `__delitem__`
140 | supports slice objects.
141 | """
142 |
143 | def __iadd__(y):
144 | """``x.__iadd__(y) <==> x += y``"""
145 |
146 | def append(item):
147 | """Append item to end"""
148 |
149 | def insert(index, item):
150 | """Insert item before index"""
151 |
152 | def pop(index=-1):
153 | """Remove and return item at index (default last)"""
154 |
155 | def remove(item):
156 | """Remove first occurrence of value"""
157 |
158 | def reverse():
159 | """Reverse *IN PLACE*"""
160 |
161 | def sort(cmpfunc=None):
162 | """Stable sort *IN PLACE*; `cmpfunc(x, y)` -> -1, 0, 1"""
163 |
164 | def extend(iterable):
165 | """Extend list by appending elements from the iterable"""
166 |
167 |
168 | class IWriteSequence(IUniqueMemberWriteSequence):
169 | """Full write contract for sequences"""
170 |
171 | def __imul__(n):
172 | """``x.__imul__(n) <==> x *= n``"""
173 |
174 |
175 | class ISequence(IReadSequence, IWriteSequence):
176 | """
177 | Full sequence contract.
178 |
179 | New code should prefer
180 | :class:`~zope.interface.common.collections.IMutableSequence`.
181 |
182 | Compared to that interface, which is implemented by :class:`list`
183 | (:class:`~zope.interface.common.builtins.IList`), among others,
184 | this interface is missing the following methods:
185 |
186 | - clear
187 |
188 | - count
189 |
190 | - index
191 |
192 | This interface adds the following methods:
193 |
194 | - sort
195 | """
196 |
--------------------------------------------------------------------------------
/src/zope/interface/common/tests/__init__.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | # Copyright (c) 2020 Zope Foundation and Contributors.
3 | # All Rights Reserved.
4 | #
5 | # This software is subject to the provisions of the Zope Public License,
6 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
7 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 | # FOR A PARTICULAR PURPOSE.
11 | ##############################################################################
12 |
13 | import unittest
14 |
15 | from zope.interface.common import ABCInterface
16 | from zope.interface.common import ABCInterfaceClass
17 | from zope.interface.verify import verifyClass
18 | from zope.interface.verify import verifyObject
19 |
20 |
21 | def iter_abc_interfaces(predicate=lambda iface: True):
22 | # Iterate ``(iface, classes)``, where ``iface`` is a descendent of
23 | # the ABCInterfaceClass passing the *predicate* and ``classes`` is
24 | # an iterable of classes registered to conform to that interface.
25 | #
26 | # Note that some builtin classes are registered for two distinct parts of
27 | # the ABC/interface tree. For example, bytearray is both ByteString and
28 | # MutableSequence.
29 | seen = set()
30 | stack = list(
31 | ABCInterface.dependents
32 | ) # subclasses, but also implementedBy objects
33 |
34 | while stack:
35 | iface = stack.pop(0)
36 | if iface in seen or not isinstance(iface, ABCInterfaceClass):
37 | continue
38 | seen.add(iface)
39 | stack.extend(list(iface.dependents))
40 | if not predicate(iface):
41 | continue
42 |
43 | registered = set(iface.getRegisteredConformers())
44 | registered -= set(iface._ABCInterfaceClass__ignored_classes)
45 | if registered:
46 | yield iface, registered
47 |
48 |
49 | def add_abc_interface_tests(cls, module):
50 | def predicate(iface):
51 | return iface.__module__ == module
52 | add_verify_tests(cls, iter_abc_interfaces(predicate))
53 |
54 |
55 | def add_verify_tests(cls, iface_classes_iter):
56 | cls.maxDiff = None
57 | for iface, registered_classes in iface_classes_iter:
58 | for stdlib_class in registered_classes:
59 | def test(self, stdlib_class=stdlib_class, iface=iface):
60 | if (
61 | stdlib_class in self.UNVERIFIABLE or
62 | stdlib_class.__name__ in self.UNVERIFIABLE
63 | ):
64 | self.skipTest("Unable to verify %s" % stdlib_class)
65 |
66 | self.assertTrue(self.verify(iface, stdlib_class))
67 |
68 | suffix = "{}_{}_{}_{}".format(
69 | stdlib_class.__module__.replace('.', '_'),
70 | stdlib_class.__name__,
71 | iface.__module__.replace('.', '_'),
72 | iface.__name__
73 | )
74 | name = 'test_auto_' + suffix
75 | test.__name__ = name
76 | assert not hasattr(cls, name), (name, list(cls.__dict__))
77 | setattr(cls, name, test)
78 |
79 | def test_ro(self, stdlib_class=stdlib_class, iface=iface):
80 | from zope.interface import Interface
81 | from zope.interface import implementedBy
82 | from zope.interface import ro
83 | self.assertEqual(
84 | tuple(ro.ro(iface, strict=True)),
85 | iface.__sro__)
86 | implements = implementedBy(stdlib_class)
87 | sro = implements.__sro__
88 | self.assertIs(sro[-1], Interface)
89 |
90 | if stdlib_class not in self.UNVERIFIABLE_RO:
91 | # Check that we got the strict C3 resolution order, unless
92 | # we know we cannot. Note that 'Interface' is virtual base
93 | # that doesn't necessarily appear at the same place in the
94 | # calculated SRO as in the final SRO.
95 | strict = stdlib_class not in self.NON_STRICT_RO
96 | isro = ro.ro(implements, strict=strict)
97 | isro.remove(Interface)
98 | isro.append(Interface)
99 |
100 | self.assertEqual(tuple(isro), sro)
101 |
102 | name = 'test_auto_ro_' + suffix
103 | test_ro.__name__ = name
104 | assert not hasattr(cls, name)
105 | setattr(cls, name, test_ro)
106 |
107 |
108 | class VerifyClassMixin(unittest.TestCase):
109 | verifier = staticmethod(verifyClass)
110 | UNVERIFIABLE = ()
111 | NON_STRICT_RO = ()
112 | UNVERIFIABLE_RO = ()
113 |
114 | def _adjust_object_before_verify(self, iface, x):
115 | return x
116 |
117 | def verify(self, iface, klass, **kwargs):
118 | return self.verifier(iface,
119 | self._adjust_object_before_verify(iface, klass),
120 | **kwargs)
121 |
122 |
123 | class VerifyObjectMixin(VerifyClassMixin):
124 | verifier = staticmethod(verifyObject)
125 | CONSTRUCTORS = {
126 | }
127 |
128 | def _adjust_object_before_verify(self, iface, x):
129 | constructor = self.CONSTRUCTORS.get(x)
130 | if not constructor:
131 | constructor = self.CONSTRUCTORS.get(iface)
132 | if not constructor:
133 | constructor = self.CONSTRUCTORS.get(x.__name__)
134 | if not constructor:
135 | constructor = x
136 | if constructor is unittest.SkipTest:
137 | self.skipTest("Cannot create " + str(x))
138 |
139 | try:
140 | result = constructor()
141 | except Exception as e: # pragma: no cover
142 | raise TypeError(
143 | f'Failed to create instance of {constructor}') from e
144 | if hasattr(result, 'close'):
145 | self.addCleanup(result.close)
146 | return result
147 |
--------------------------------------------------------------------------------
/src/zope/interface/common/tests/basemapping.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2001, 2002 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """Base Mapping tests
15 | """
16 | from operator import __getitem__
17 |
18 |
19 | def testIReadMapping(self, inst, state, absent):
20 | for key in state:
21 | self.assertEqual(inst[key], state[key])
22 | self.assertEqual(inst.get(key, None), state[key])
23 | self.assertIn(key, inst)
24 |
25 | for key in absent:
26 | self.assertEqual(inst.get(key, None), None)
27 | self.assertEqual(inst.get(key), None)
28 | self.assertEqual(inst.get(key, self), self)
29 | self.assertRaises(KeyError, __getitem__, inst, key)
30 |
31 |
32 | def test_keys(self, inst, state):
33 | # Return the keys of the mapping object
34 | inst_keys = sorted(inst.keys())
35 | state_keys = sorted(state.keys())
36 | self.assertEqual(inst_keys, state_keys)
37 |
38 |
39 | def test_iter(self, inst, state):
40 | # Return the keys of the mapping object
41 | inst_keys = sorted(inst)
42 | state_keys = sorted(state.keys())
43 | self.assertEqual(inst_keys, state_keys)
44 |
45 |
46 | def test_values(self, inst, state):
47 | # Return the values of the mapping object
48 | inst_values = sorted(inst.values())
49 | state_values = sorted(state.values())
50 | self.assertEqual(inst_values, state_values)
51 |
52 |
53 | def test_items(self, inst, state):
54 | # Return the items of the mapping object
55 | inst_items = sorted(inst.items())
56 | state_items = sorted(state.items())
57 | self.assertEqual(inst_items, state_items)
58 |
59 |
60 | def test___len__(self, inst, state):
61 | # Return the number of items
62 | self.assertEqual(len(inst), len(state))
63 |
64 |
65 | def testIEnumerableMapping(self, inst, state):
66 | test_keys(self, inst, state)
67 | test_items(self, inst, state)
68 | test_values(self, inst, state)
69 | test___len__(self, inst, state)
70 |
71 |
72 | class BaseTestIReadMapping:
73 |
74 | def testIReadMapping(self):
75 | inst = self._IReadMapping__sample()
76 | state = self._IReadMapping__stateDict()
77 | absent = self._IReadMapping__absentKeys()
78 | testIReadMapping(self, inst, state, absent)
79 |
80 |
81 | class BaseTestIEnumerableMapping(BaseTestIReadMapping):
82 | # Mapping objects whose items can be enumerated
83 |
84 | def test_keys(self):
85 | # Return the keys of the mapping object
86 | inst = self._IEnumerableMapping__sample()
87 | state = self._IEnumerableMapping__stateDict()
88 | test_keys(self, inst, state)
89 |
90 | def test_values(self):
91 | # Return the values of the mapping object
92 | inst = self._IEnumerableMapping__sample()
93 | state = self._IEnumerableMapping__stateDict()
94 | test_values(self, inst, state)
95 |
96 | def test_items(self):
97 | # Return the items of the mapping object
98 | inst = self._IEnumerableMapping__sample()
99 | state = self._IEnumerableMapping__stateDict()
100 | test_items(self, inst, state)
101 |
102 | def test___len__(self):
103 | # Return the number of items
104 | inst = self._IEnumerableMapping__sample()
105 | state = self._IEnumerableMapping__stateDict()
106 | test___len__(self, inst, state)
107 |
108 | def _IReadMapping__stateDict(self):
109 | return self._IEnumerableMapping__stateDict()
110 |
111 | def _IReadMapping__sample(self):
112 | return self._IEnumerableMapping__sample()
113 |
114 | def _IReadMapping__absentKeys(self):
115 | return self._IEnumerableMapping__absentKeys()
116 |
--------------------------------------------------------------------------------
/src/zope/interface/common/tests/test_builtins.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | # Copyright (c) 2020 Zope Foundation and Contributors.
3 | # All Rights Reserved.
4 | #
5 | # This software is subject to the provisions of the Zope Public License,
6 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
7 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 | # FOR A PARTICULAR PURPOSE.
11 | ##############################################################################
12 |
13 | import unittest
14 |
15 | from zope.interface._compat import PY313_OR_OLDER
16 | from zope.interface.common import builtins
17 |
18 | from . import VerifyClassMixin
19 | from . import VerifyObjectMixin
20 | from . import add_verify_tests
21 |
22 |
23 | class TestVerifyClass(VerifyClassMixin,
24 | unittest.TestCase):
25 | pass
26 |
27 |
28 | VERIFY_TESTS = [
29 | (builtins.IList, (list,)),
30 | (builtins.ITuple, (tuple,)),
31 | (builtins.ITextString, (str,)),
32 | (builtins.INativeString, (str,)),
33 | (builtins.IBool, (bool,)),
34 | (builtins.IDict, (dict,)),
35 | (builtins.IFile, ()),
36 |
37 | ]
38 | if PY313_OR_OLDER:
39 | VERIFY_TESTS.append(
40 | (builtins.IByteString, (bytes,))
41 | )
42 |
43 | add_verify_tests(TestVerifyClass, tuple(VERIFY_TESTS))
44 |
45 |
46 | class TestVerifyObject(VerifyObjectMixin,
47 | TestVerifyClass):
48 | CONSTRUCTORS = {
49 | builtins.IFile: lambda: open(__file__)
50 | }
51 |
--------------------------------------------------------------------------------
/src/zope/interface/common/tests/test_collections.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | # Copyright (c) 2020 Zope Foundation and Contributors.
3 | # All Rights Reserved.
4 | #
5 | # This software is subject to the provisions of the Zope Public License,
6 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
7 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 | # FOR A PARTICULAR PURPOSE.
11 | ##############################################################################
12 |
13 |
14 | import array
15 | import sys
16 | import unittest
17 | from collections import OrderedDict
18 | from collections import abc
19 | from collections import deque
20 | from types import MappingProxyType
21 |
22 | from zope.interface import Invalid
23 | from zope.interface._compat import PYPY
24 | # Note that importing z.i.c.collections does work on import.
25 | from zope.interface.common import collections
26 |
27 | from . import VerifyClassMixin
28 | from . import VerifyObjectMixin
29 | from . import add_abc_interface_tests
30 |
31 |
32 | class TestVerifyClass(VerifyClassMixin, unittest.TestCase):
33 |
34 | # Here we test some known builtin classes that are defined to implement
35 | # various collection interfaces as a quick sanity test.
36 | def test_frozenset(self):
37 | self.assertIsInstance(frozenset(), abc.Set)
38 | self.assertTrue(self.verify(collections.ISet, frozenset))
39 |
40 | def test_list(self):
41 | self.assertIsInstance(list(), abc.MutableSequence)
42 | self.assertTrue(self.verify(collections.IMutableSequence, list))
43 |
44 | # Here we test some derived classes.
45 | def test_UserList(self):
46 | self.assertTrue(self.verify(collections.IMutableSequence,
47 | collections.UserList))
48 |
49 | def test_UserDict(self):
50 | self.assertTrue(self.verify(collections.IMutableMapping,
51 | collections.UserDict))
52 |
53 | def test_UserString(self):
54 | self.assertTrue(self.verify(collections.ISequence,
55 | collections.UserString))
56 |
57 | # Now we go through the registry, which should have several things, mostly
58 | # builtins, but if we've imported other libraries already, it could
59 | # contain things from outside of there too. We aren't concerned about
60 | # third-party code here, just standard library types. We start with a
61 | # blacklist of things to exclude, but if that gets out of hand we can
62 | # figure out a better whitelisting.
63 | UNVERIFIABLE = {
64 | # This is declared to be an ISequence, but is missing lots of methods,
65 | # including some that aren't part of a language protocol, such as
66 | # ``index`` and ``count``.
67 | memoryview,
68 | # 'pkg_resources._vendor.pyparsing.ParseResults' is registered as a
69 | # MutableMapping but is missing methods like ``popitem`` and
70 | # ``setdefault``. It's imported due to namespace packages.
71 | 'ParseResults',
72 | # sqlite3.Row claims ISequence but also misses ``index`` and
73 | # ``count``. It's imported because...? Coverage imports it, but why
74 | # do we have it without coverage?
75 | 'Row',
76 | # In Python 3.10 ``array.array`` appears as ``IMutableSequence`` but it
77 | # does not provide a ``clear()`` method and it cannot be instantiated
78 | # using ``array.array()``.
79 | array.array,
80 | }
81 |
82 | if PYPY:
83 | UNVERIFIABLE.update({
84 | # collections.deque.pop() doesn't support the index= argument to
85 | # MutableSequence.pop(). We can't verify this on CPython because
86 | # we can't get the signature, but on PyPy we /can/ get the
87 | # signature, and of course it doesn't match.
88 | deque,
89 | # Likewise for index
90 | range,
91 | })
92 | UNVERIFIABLE_RO = {
93 | # ``array.array`` fails the ``test_auto_ro_*`` tests with and
94 | # without strict RO but only on Windows (AppVeyor) on Python 3.10.0
95 | # (in older versions ``array.array`` does not appear as
96 | # ``IMutableSequence``).
97 | array.array,
98 | }
99 |
100 |
101 | add_abc_interface_tests(TestVerifyClass, collections.ISet.__module__)
102 |
103 |
104 | def _get_FrameLocalsProxy():
105 | return type(sys._getframe().f_locals)
106 |
107 |
108 | class TestVerifyObject(VerifyObjectMixin,
109 | TestVerifyClass):
110 | CONSTRUCTORS = {
111 | collections.IValuesView: {}.values,
112 | collections.IItemsView: {}.items,
113 | collections.IKeysView: {}.keys,
114 | memoryview: lambda: memoryview(b'abc'),
115 | range: lambda: range(10),
116 | MappingProxyType: lambda: MappingProxyType({}),
117 | collections.UserString: lambda: collections.UserString('abc'),
118 | type(iter(bytearray())): lambda: iter(bytearray()),
119 | type(iter(b'abc')): lambda: iter(b'abc'),
120 | 'coroutine': unittest.SkipTest,
121 | type(iter({}.keys())): lambda: iter({}.keys()),
122 | type(iter({}.items())): lambda: iter({}.items()),
123 | type(iter({}.values())): lambda: iter({}.values()),
124 | type(i for i in range(1)): lambda: (i for i in range(3)),
125 | type(iter([])): lambda: iter([]),
126 | type(reversed([])): lambda: reversed([]),
127 | 'longrange_iterator': unittest.SkipTest,
128 | 'range_iterator': lambda: iter(range(3)),
129 | 'rangeiterator': lambda: iter(range(3)),
130 | type(iter(set())): lambda: iter(set()),
131 | type(iter('')): lambda: iter(''),
132 | 'async_generator': unittest.SkipTest,
133 | type(iter(tuple())): lambda: iter(tuple()),
134 | }
135 | if sys.version_info >= (3, 13):
136 | def FrameLocalsProxy_constructor():
137 | return _get_FrameLocalsProxy()(sys._getframe())
138 | FrameLocalsProxy = _get_FrameLocalsProxy()
139 | CONSTRUCTORS[FrameLocalsProxy] = FrameLocalsProxy_constructor
140 |
141 | UNVERIFIABLE_RO = {
142 | # ``array.array`` fails the ``test_auto_ro_*`` tests with and
143 | # without strict RO but only on Windows (AppVeyor) on Python 3.10.0
144 | # (in older versions ``array.array`` does not appear as
145 | # ``IMutableSequence``).
146 | array.array,
147 | }
148 |
--------------------------------------------------------------------------------
/src/zope/interface/common/tests/test_idatetime.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2003 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """Test for datetime interfaces
15 | """
16 |
17 | import unittest
18 | from datetime import date
19 | from datetime import datetime
20 | from datetime import time
21 | from datetime import timedelta
22 | from datetime import tzinfo
23 |
24 | from zope.interface.common.idatetime import IDate
25 | from zope.interface.common.idatetime import IDateClass
26 | from zope.interface.common.idatetime import IDateTime
27 | from zope.interface.common.idatetime import IDateTimeClass
28 | from zope.interface.common.idatetime import ITime
29 | from zope.interface.common.idatetime import ITimeClass
30 | from zope.interface.common.idatetime import ITimeDelta
31 | from zope.interface.common.idatetime import ITimeDeltaClass
32 | from zope.interface.common.idatetime import ITZInfo
33 | from zope.interface.verify import verifyClass
34 | from zope.interface.verify import verifyObject
35 |
36 |
37 | class TestDateTimeInterfaces(unittest.TestCase):
38 |
39 | def test_interfaces(self):
40 | verifyObject(ITimeDelta, timedelta(minutes=20))
41 | verifyObject(IDate, date(2000, 1, 2))
42 | verifyObject(IDateTime, datetime(2000, 1, 2, 10, 20))
43 | verifyObject(ITime, time(20, 30, 15, 1234))
44 | verifyObject(ITZInfo, tzinfo())
45 | verifyClass(ITimeDeltaClass, timedelta)
46 | verifyClass(IDateClass, date)
47 | verifyClass(IDateTimeClass, datetime)
48 | verifyClass(ITimeClass, time)
49 |
--------------------------------------------------------------------------------
/src/zope/interface/common/tests/test_import_interfaces.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2006 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | import unittest
15 |
16 |
17 | class TestInterfaceImport(unittest.TestCase):
18 |
19 | def test_import(self):
20 | import zope.interface.common.interfaces as x
21 | self.assertIsNotNone(x)
22 |
--------------------------------------------------------------------------------
/src/zope/interface/common/tests/test_io.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | # Copyright (c) 2020 Zope Foundation and Contributors.
3 | # All Rights Reserved.
4 | #
5 | # This software is subject to the provisions of the Zope Public License,
6 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
7 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 | # FOR A PARTICULAR PURPOSE.
11 | ##############################################################################
12 |
13 |
14 | import io as abc
15 | import unittest
16 |
17 | # Note that importing z.i.c.io does work on import.
18 | from zope.interface.common import io
19 |
20 | from . import VerifyClassMixin
21 | from . import VerifyObjectMixin
22 | from . import add_abc_interface_tests
23 |
24 |
25 | class TestVerifyClass(VerifyClassMixin,
26 | unittest.TestCase):
27 | pass
28 |
29 |
30 | add_abc_interface_tests(TestVerifyClass, io.IIOBase.__module__)
31 |
32 |
33 | class TestVerifyObject(VerifyObjectMixin,
34 | TestVerifyClass):
35 | CONSTRUCTORS = {
36 | abc.BufferedWriter: lambda: abc.BufferedWriter(abc.StringIO()),
37 | abc.BufferedReader: lambda: abc.BufferedReader(abc.StringIO()),
38 | abc.TextIOWrapper: lambda: abc.TextIOWrapper(abc.BytesIO()),
39 | abc.BufferedRandom: lambda: abc.BufferedRandom(abc.BytesIO()),
40 | abc.BufferedRWPair: lambda: abc.BufferedRWPair(
41 | abc.BytesIO(), abc.BytesIO()
42 | ),
43 | abc.FileIO: lambda: abc.FileIO(__file__),
44 | '_WindowsConsoleIO': unittest.SkipTest,
45 | 'WinConsoleIO': unittest.SkipTest, # breaks on PyPy-3.10
46 | }
47 |
--------------------------------------------------------------------------------
/src/zope/interface/common/tests/test_numbers.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | # Copyright (c) 2020 Zope Foundation and Contributors.
3 | # All Rights Reserved.
4 | #
5 | # This software is subject to the provisions of the Zope Public License,
6 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
7 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 | # FOR A PARTICULAR PURPOSE.
11 | ##############################################################################
12 |
13 |
14 | import numbers as abc
15 | import unittest
16 |
17 | # Note that importing z.i.c.numbers does work on import.
18 | from zope.interface.common import numbers
19 |
20 | from . import VerifyClassMixin
21 | from . import VerifyObjectMixin
22 | from . import add_abc_interface_tests
23 |
24 |
25 | class TestVerifyClass(VerifyClassMixin,
26 | unittest.TestCase):
27 |
28 | def test_int(self):
29 | self.assertIsInstance(int(), abc.Integral)
30 | self.assertTrue(self.verify(numbers.IIntegral, int))
31 |
32 | def test_float(self):
33 | self.assertIsInstance(float(), abc.Real)
34 | self.assertTrue(self.verify(numbers.IReal, float))
35 |
36 |
37 | add_abc_interface_tests(TestVerifyClass, numbers.INumber.__module__)
38 |
39 |
40 | class TestVerifyObject(VerifyObjectMixin,
41 | TestVerifyClass):
42 | pass
43 |
--------------------------------------------------------------------------------
/src/zope/interface/document.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2001, 2002 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """ Pretty-Print an Interface object as structured text (Yum)
15 |
16 | This module provides a function, asStructuredText, for rendering an
17 | interface as structured text.
18 | """
19 | import zope.interface
20 |
21 |
22 | __all__ = [
23 | 'asReStructuredText',
24 | 'asStructuredText',
25 | ]
26 |
27 |
28 | def asStructuredText(iface, munge=0, rst=False):
29 | """ Output structured text format. Note, this will whack any existing
30 | 'structured' format of the text.
31 |
32 | If `rst=True`, then the output will quote all code as inline literals in
33 | accordance with 'reStructuredText' markup principles.
34 | """
35 |
36 | if rst:
37 | def inline_literal(s):
38 | return f"``{s}``"
39 | else:
40 | def inline_literal(s):
41 | return s
42 |
43 | r = [inline_literal(iface.getName())]
44 | outp = r.append
45 | level = 1
46 |
47 | if iface.getDoc():
48 | outp(_justify_and_indent(_trim_doc_string(iface.getDoc()), level))
49 |
50 | bases = [base
51 | for base in iface.__bases__
52 | if base is not zope.interface.Interface
53 | ]
54 | if bases:
55 | outp(_justify_and_indent("This interface extends:", level, munge))
56 | level += 1
57 | for b in bases:
58 | item = "o %s" % inline_literal(b.getName())
59 | outp(_justify_and_indent(_trim_doc_string(item), level, munge))
60 | level -= 1
61 |
62 | namesAndDescriptions = sorted(iface.namesAndDescriptions())
63 |
64 | outp(_justify_and_indent("Attributes:", level, munge))
65 | level += 1
66 | for name, desc in namesAndDescriptions:
67 | if not hasattr(desc, 'getSignatureString'): # ugh...
68 | item = "{} -- {}".format(
69 | inline_literal(desc.getName()),
70 | desc.getDoc() or 'no documentation'
71 | )
72 | outp(_justify_and_indent(_trim_doc_string(item), level, munge))
73 | level -= 1
74 |
75 | outp(_justify_and_indent("Methods:", level, munge))
76 | level += 1
77 | for name, desc in namesAndDescriptions:
78 | if hasattr(desc, 'getSignatureString'): # ugh...
79 | _call = f"{desc.getName()}{desc.getSignatureString()}"
80 | item = "{} -- {}".format(
81 | inline_literal(_call),
82 | desc.getDoc() or 'no documentation'
83 | )
84 | outp(_justify_and_indent(_trim_doc_string(item), level, munge))
85 |
86 | return "\n\n".join(r) + "\n\n"
87 |
88 |
89 | def asReStructuredText(iface, munge=0):
90 | """ Output reStructuredText format.
91 |
92 | Note, this will whack any existing 'structured' format of the text."""
93 | return asStructuredText(iface, munge=munge, rst=True)
94 |
95 |
96 | def _trim_doc_string(text):
97 | """ Trims a doc string to make it format
98 | correctly with structured text. """
99 |
100 | lines = text.replace('\r\n', '\n').split('\n')
101 | nlines = [lines.pop(0)]
102 | if lines:
103 | min_indent = min([len(line) - len(line.lstrip())
104 | for line in lines])
105 | for line in lines:
106 | nlines.append(line[min_indent:])
107 |
108 | return '\n'.join(nlines)
109 |
110 |
111 | def _justify_and_indent(text, level, munge=0, width=72):
112 | """ indent and justify text, rejustify (munge) if specified """
113 |
114 | indent = " " * level
115 |
116 | if munge:
117 | lines = []
118 | line = indent
119 | text = text.split()
120 |
121 | for word in text:
122 | line = ' '.join([line, word])
123 | if len(line) > width:
124 | lines.append(line)
125 | line = indent
126 | else:
127 | lines.append(line)
128 |
129 | return '\n'.join(lines)
130 |
131 | else:
132 | return indent + \
133 | text.strip().replace("\r\n", "\n") .replace("\n", "\n" + indent)
134 |
--------------------------------------------------------------------------------
/src/zope/interface/exceptions.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2002 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """Interface-specific exceptions
15 | """
16 |
17 | __all__ = [
18 | # Invalid tree
19 | 'Invalid',
20 | 'DoesNotImplement',
21 | 'BrokenImplementation',
22 | 'BrokenMethodImplementation',
23 | 'MultipleInvalid',
24 | # Other
25 | 'BadImplements',
26 | 'InvalidInterface',
27 | ]
28 |
29 |
30 | class Invalid(Exception):
31 | """A specification is violated
32 | """
33 |
34 |
35 | class _TargetInvalid(Invalid):
36 | # Internal use. Subclass this when you're describing
37 | # a particular target object that's invalid according
38 | # to a specific interface.
39 | #
40 | # For backwards compatibility, the *target* and *interface* are
41 | # optional, and the signatures are inconsistent in their ordering.
42 | #
43 | # We deal with the inconsistency in ordering by defining the index
44 | # of the two values in ``self.args``. *target* uses a marker object to
45 | # distinguish "not given" from "given, but None", because the latter
46 | # can be a value that gets passed to validation. For this reason, it must
47 | # always be the last argument (we detect absence by the ``IndexError``).
48 |
49 | _IX_INTERFACE = 0
50 | _IX_TARGET = 1
51 | # The exception to catch when indexing self.args indicating that
52 | # an argument was not given. If all arguments are expected,
53 | # a subclass should set this to ().
54 | _NOT_GIVEN_CATCH = IndexError
55 | _NOT_GIVEN = ''
56 |
57 | def _get_arg_or_default(self, ix, default=None):
58 | try:
59 | return self.args[ix] # pylint:disable=unsubscriptable-object
60 | except self._NOT_GIVEN_CATCH:
61 | return default
62 |
63 | @property
64 | def interface(self):
65 | return self._get_arg_or_default(self._IX_INTERFACE)
66 |
67 | @property
68 | def target(self):
69 | return self._get_arg_or_default(self._IX_TARGET, self._NOT_GIVEN)
70 |
71 | ###
72 | # str
73 | #
74 | # The ``__str__`` of self is implemented by concatenating (%s), in order,
75 | # these properties (none of which should have leading or trailing
76 | # whitespace):
77 | #
78 | # - self._str_subject
79 | # Begin the message, including a description of the target.
80 | # - self._str_description
81 | # Provide a general description of the type of error, including
82 | # the interface name if possible and relevant.
83 | # - self._str_conjunction
84 | # Join the description to the details. Defaults to ": ".
85 | # - self._str_details
86 | # Provide details about how this particular instance of the error.
87 | # - self._str_trailer
88 | # End the message. Usually just a period.
89 | ###
90 |
91 | @property
92 | def _str_subject(self):
93 | target = self.target
94 | if target is self._NOT_GIVEN:
95 | return "An object"
96 | return f"The object {target!r}"
97 |
98 | @property
99 | def _str_description(self):
100 | return "has failed to implement interface %s" % (
101 | self.interface or ''
102 | )
103 |
104 | _str_conjunction = ": "
105 | _str_details = ""
106 | _str_trailer = '.'
107 |
108 | def __str__(self):
109 | return "{} {}{}{}{}".format(
110 | self._str_subject,
111 | self._str_description,
112 | self._str_conjunction,
113 | self._str_details,
114 | self._str_trailer
115 | )
116 |
117 |
118 | class DoesNotImplement(_TargetInvalid):
119 | """
120 | DoesNotImplement(interface[, target])
121 |
122 | The *target* (optional) does not implement the *interface*.
123 |
124 | .. versionchanged:: 5.0.0
125 | Add the *target* argument and attribute, and change the resulting
126 | string value of this object accordingly.
127 | """
128 |
129 | _str_details = "Does not declaratively implement the interface"
130 |
131 |
132 | class BrokenImplementation(_TargetInvalid):
133 | """
134 | BrokenImplementation(interface, name[, target])
135 |
136 | The *target* (optional) is missing the attribute *name*.
137 |
138 | .. versionchanged:: 5.0.0
139 | Add the *target* argument and attribute, and change the resulting
140 | string value of this object accordingly.
141 |
142 | The *name* can either be a simple string or a ``Attribute`` object.
143 | """
144 |
145 | _IX_NAME = _TargetInvalid._IX_INTERFACE + 1
146 | _IX_TARGET = _IX_NAME + 1
147 |
148 | @property
149 | def name(self):
150 | return self.args[1] # pylint:disable=unsubscriptable-object
151 |
152 | @property
153 | def _str_details(self):
154 | return "The %s attribute was not provided" % (
155 | repr(self.name) if isinstance(self.name, str) else self.name
156 | )
157 |
158 |
159 | class BrokenMethodImplementation(_TargetInvalid):
160 | """
161 | BrokenMethodImplementation(
162 | method, message[, implementation, interface, target]
163 | )
164 |
165 | The *target* (optional) has a *method* in *implementation* that violates
166 | its contract in a way described by *mess*.
167 |
168 | .. versionchanged:: 5.0.0
169 | Add the *interface* and *target* argument and attribute,
170 | and change the resulting string value of this object accordingly.
171 |
172 | The *method* can either be a simple string or a ``Method`` object.
173 |
174 | .. versionchanged:: 5.0.0
175 | If *implementation* is given, then the *message* will have the
176 | string "implementation" replaced with an short but informative
177 | representation of *implementation*.
178 |
179 | """
180 |
181 | _IX_IMPL = 2
182 | _IX_INTERFACE = _IX_IMPL + 1
183 | _IX_TARGET = _IX_INTERFACE + 1
184 |
185 | @property
186 | def method(self):
187 | return self.args[0] # pylint:disable=unsubscriptable-object
188 |
189 | @property
190 | def mess(self):
191 | return self.args[1] # pylint:disable=unsubscriptable-object
192 |
193 | @staticmethod
194 | def __implementation_str(impl):
195 | # It could be a callable or some arbitrary object, we don't
196 | # know yet.
197 | import inspect # Inspect is a heavy-weight dependency, lots of imports
198 | try:
199 | sig = inspect.signature
200 | formatsig = str
201 | except AttributeError:
202 | sig = inspect.getargspec
203 | formatsig = inspect.formatargspec
204 |
205 | try:
206 | sig = sig(impl)
207 | except (ValueError, TypeError):
208 | # Unable to introspect. Darn.
209 | # This could be a non-callable, or a particular builtin,
210 | # or a bound method that doesn't even accept 'self', e.g.,
211 | # ``Class.method = lambda: None; Class().method``
212 | return repr(impl)
213 |
214 | try:
215 | name = impl.__qualname__
216 | except AttributeError:
217 | name = impl.__name__
218 |
219 | return name + formatsig(sig)
220 |
221 | @property
222 | def _str_details(self):
223 | impl = self._get_arg_or_default(self._IX_IMPL, self._NOT_GIVEN)
224 | message = self.mess
225 | if impl is not self._NOT_GIVEN and 'implementation' in message:
226 | message = message.replace("implementation", '%r')
227 | message = message % (self.__implementation_str(impl),)
228 |
229 | return 'The contract of {} is violated because {}'.format(
230 | repr(self.method) if isinstance(self.method, str) else self.method,
231 | message,
232 | )
233 |
234 |
235 | class MultipleInvalid(_TargetInvalid):
236 | """
237 | The *target* has failed to implement the *interface* in
238 | multiple ways.
239 |
240 | The failures are described by *exceptions*, a collection of
241 | other `Invalid` instances.
242 |
243 | .. versionadded:: 5.0
244 | """
245 |
246 | _NOT_GIVEN_CATCH = ()
247 |
248 | def __init__(self, interface, target, exceptions):
249 | super().__init__(interface, target, tuple(exceptions))
250 |
251 | @property
252 | def exceptions(self):
253 | return self.args[2] # pylint:disable=unsubscriptable-object
254 |
255 | @property
256 | def _str_details(self):
257 | # It would be nice to use tabs here, but that
258 | # is hard to represent in doctests.
259 | return '\n ' + '\n '.join(
260 | x._str_details.strip() if isinstance(x, _TargetInvalid) else str(x)
261 | for x in self.exceptions
262 | )
263 |
264 | _str_conjunction = ':' # no trailing space, messes up doctests
265 | _str_trailer = ''
266 |
267 |
268 | class InvalidInterface(Exception):
269 | """The interface has invalid contents
270 | """
271 |
272 |
273 | class BadImplements(TypeError):
274 | """An implementation assertion is invalid
275 |
276 | because it doesn't contain an interface or a sequence of valid
277 | implementation assertions.
278 | """
279 |
--------------------------------------------------------------------------------
/src/zope/interface/tests/__init__.py:
--------------------------------------------------------------------------------
1 | from zope.interface._compat import _should_attempt_c_optimizations
2 |
3 |
4 | class OptimizationTestMixin:
5 | """Mixin testing that C optimizations are used when appropriate.
6 | """
7 |
8 | def _getTargetClass(self):
9 | """Return the implementation in use, without 'Py' or 'Fallback' suffix.
10 | """
11 | raise NotImplementedError
12 |
13 | def _getFallbackClass(self):
14 | """Return the fallback Python implementation.
15 | """
16 | # Is there an algorithmic way to do this? The C
17 | # objects all come from the same module so I don't see how we can
18 | # get the Python object from that.
19 | raise NotImplementedError
20 |
21 | def test_optimizations(self):
22 | used = self._getTargetClass()
23 | fallback = self._getFallbackClass()
24 |
25 | if _should_attempt_c_optimizations():
26 | self.assertIsNot(used, fallback)
27 | else:
28 | self.assertIs(used, fallback)
29 |
30 |
31 | class SubclassableMixin:
32 |
33 | def _getTargetClass(self):
34 | """Return the implementation in use without 'Py' or 'Fallback' suffix.
35 | """
36 | raise NotImplementedError
37 |
38 | def test_can_subclass(self):
39 | klass = self._getTargetClass()
40 |
41 | class Derived(klass): # no raise
42 | pass
43 |
44 |
45 | class MissingSomeAttrs:
46 | """
47 | Helper for tests that raises a specific exception
48 | for attributes that are missing. This is usually not
49 | an AttributeError, and this object is used to test that
50 | those errors are not improperly caught and treated like
51 | an AttributeError.
52 | """
53 |
54 | def __init__(self, exc_kind, **other_attrs):
55 | self.__exc_kind = exc_kind
56 | d = object.__getattribute__(self, '__dict__')
57 | d.update(other_attrs)
58 |
59 | def __getattribute__(self, name):
60 | # Note that we ignore objects found in the class dictionary.
61 | d = object.__getattribute__(self, '__dict__')
62 | try:
63 | return d[name]
64 | except KeyError:
65 | raise d['_MissingSomeAttrs__exc_kind'](name)
66 |
67 | EXCEPTION_CLASSES = (
68 | TypeError,
69 | RuntimeError,
70 | BaseException,
71 | ValueError,
72 | )
73 |
74 | @classmethod
75 | def test_raises(cls, unittest, test_func, expected_missing, **other_attrs):
76 | """
77 | Loop through various exceptions, calling *test_func* inside a
78 | ``assertRaises`` block.
79 |
80 | :param test_func: A callable of one argument, the instance of this
81 | class.
82 | :param str expected_missing: The attribute that should fail with the
83 | exception. This is used to ensure that we're testing the path we
84 | think we are.
85 | :param other_attrs: Attributes that should be provided on the test
86 | object. Must not contain *expected_missing*.
87 | """
88 | assert isinstance(expected_missing, str)
89 | assert expected_missing not in other_attrs
90 | for exc in cls.EXCEPTION_CLASSES:
91 | ob = cls(exc, **other_attrs)
92 | with unittest.assertRaises(exc) as ex:
93 | test_func(ob)
94 |
95 | unittest.assertEqual(ex.exception.args[0], expected_missing)
96 |
97 | # Now test that the AttributeError for that expected_missing is *not*
98 | # raised.
99 | ob = cls(AttributeError, **other_attrs)
100 | try:
101 | test_func(ob)
102 | except AttributeError as e:
103 | unittest.assertNotIn(expected_missing, str(e))
104 | except Exception: # pylint:disable=broad-except
105 | pass
106 |
107 | # Be sure cleanup functionality is available; classes that use the adapter hook
108 | # need to be sure to subclass ``CleanUp``.
109 | #
110 | # If zope.component is installed and imported when we run our tests
111 | # (import chain:
112 | # zope.testrunner->zope.security->zope.location->zope.component.api)
113 | # it adds an adapter hook that uses its global site manager. That can cause
114 | # leakage from one test to another unless its cleanup hooks are run. The
115 | # symptoms can be odd, especially if one test used C objects and the next used
116 | # the Python implementation. (For example, you can get strange TypeErrors or
117 | # find inexplicable comparisons being done.)
118 |
119 |
120 | try:
121 | from zope.testing import cleanup
122 | except ImportError:
123 |
124 | class CleanUp:
125 | def cleanUp(self):
126 | pass
127 |
128 | setUp = tearDown = cleanUp
129 | else:
130 | CleanUp = cleanup.CleanUp
131 |
--------------------------------------------------------------------------------
/src/zope/interface/tests/advisory_testing.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2003 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | import sys
15 |
16 | from zope.interface.advice import getFrameInfo
17 |
18 |
19 | my_globals = globals()
20 |
21 | ClassicClass = None
22 |
23 |
24 | class NewStyleClass:
25 | __metaclass__ = type
26 | classLevelFrameInfo = getFrameInfo(sys._getframe())
27 |
28 |
29 | moduleLevelFrameInfo = getFrameInfo(sys._getframe())
30 |
--------------------------------------------------------------------------------
/src/zope/interface/tests/dummy.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2001, 2002 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """ Dummy Module
15 | """
16 | from zope.interface import moduleProvides
17 | from zope.interface.tests.idummy import IDummyModule
18 |
19 |
20 | moduleProvides(IDummyModule)
21 |
22 |
23 | def bar(baz):
24 | # Note: no 'self', because the module provides the interface directly.
25 | raise NotImplementedError()
26 |
--------------------------------------------------------------------------------
/src/zope/interface/tests/idummy.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2001, 2002 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """ Interface describing API of zope.interface.tests.dummy test module
15 | """
16 | from zope.interface import Interface
17 |
18 |
19 | class IDummyModule(Interface):
20 | """ Dummy interface for unit tests.
21 | """
22 | def bar(baz):
23 | """ Just a note.
24 | """
25 |
--------------------------------------------------------------------------------
/src/zope/interface/tests/m1.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2004 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """Test module that declares an interface
15 | """
16 | from zope.interface import Interface
17 | from zope.interface import moduleProvides
18 |
19 |
20 | class I1(Interface):
21 | pass
22 |
23 |
24 | class I2(Interface):
25 | pass
26 |
27 |
28 | moduleProvides(I1, I2)
29 |
--------------------------------------------------------------------------------
/src/zope/interface/tests/odd.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2003 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """Odd meta class that doesn't subclass type.
15 |
16 | This is used for testing support for ExtensionClass in new interfaces.
17 |
18 | >>> class A(object):
19 | ... __metaclass__ = MetaClass
20 | ... a = 1
21 | ...
22 | >>> A.__name__
23 | 'A'
24 | >>> A.__bases__ == (object,)
25 | True
26 | >>> class B(object):
27 | ... __metaclass__ = MetaClass
28 | ... b = 1
29 | ...
30 | >>> class C(A, B): pass
31 | ...
32 | >>> C.__name__
33 | 'C'
34 | >>> int(C.__bases__ == (A, B))
35 | 1
36 | >>> a = A()
37 | >>> aa = A()
38 | >>> a.a
39 | 1
40 | >>> aa.a
41 | 1
42 | >>> aa.a = 2
43 | >>> a.a
44 | 1
45 | >>> aa.a
46 | 2
47 | >>> c = C()
48 | >>> c.a
49 | 1
50 | >>> c.b
51 | 1
52 | >>> c.b = 2
53 | >>> c.b
54 | 2
55 | >>> C.c = 1
56 | >>> c.c
57 | 1
58 |
59 | >>> int(C.__class__.__class__ is C.__class__)
60 | 1
61 | """
62 |
63 | # class OddClass is an odd meta class
64 |
65 |
66 | class MetaMetaClass(type):
67 |
68 | def __getattribute__(cls, name):
69 | if name == '__class__':
70 | return cls
71 | # Under Python 3.6, __prepare__ gets requested
72 | return type.__getattribute__(cls, name)
73 |
74 |
75 | class MetaClass:
76 | """Odd classes
77 | """
78 |
79 | def __init__(self, name, bases, dict):
80 | self.__name__ = name
81 | self.__bases__ = bases
82 | self.__dict__.update(dict)
83 |
84 | def __call__(self):
85 | return OddInstance(self)
86 |
87 | def __getattr__(self, name):
88 | for b in self.__bases__:
89 | v = getattr(b, name, self)
90 | if v is not self:
91 | return v
92 | raise AttributeError(name)
93 |
94 | def __repr__(self): # pragma: no cover
95 | return f""
96 |
97 |
98 | MetaClass = MetaMetaClass(
99 | 'MetaClass',
100 | MetaClass.__bases__,
101 | {
102 | k: v for k, v in MetaClass.__dict__.items()
103 | if k not in ('__dict__',)
104 | }
105 | )
106 |
107 |
108 | class OddInstance:
109 |
110 | def __init__(self, cls):
111 | self.__dict__['__class__'] = cls
112 |
113 | def __getattribute__(self, name):
114 | dict = object.__getattribute__(self, '__dict__')
115 | if name == '__dict__':
116 | return dict
117 | v = dict.get(name, self)
118 | if v is not self:
119 | return v
120 | return getattr(dict['__class__'], name)
121 |
122 | def __setattr__(self, name, v):
123 | self.__dict__[name] = v
124 |
125 | def __delattr__(self, name):
126 | raise NotImplementedError()
127 |
128 | def __repr__(self): # pragma: no cover
129 | return "".format(
130 | self.__class__.__name__, hex(id(self)))
131 |
--------------------------------------------------------------------------------
/src/zope/interface/tests/test_advice.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2003 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """Tests for advice
15 |
16 | This module was adapted from 'protocols.tests.advice', part of the Python
17 | Enterprise Application Kit (PEAK). Please notify the PEAK authors
18 | (pje@telecommunity.com and tsarna@sarna.org) if bugs are found or
19 | Zope-specific changes are required, so that the PEAK version of this module
20 | can be kept in sync.
21 |
22 | PEAK is a Python application framework that interoperates with (but does
23 | not require) Zope 3 and Twisted. It provides tools for manipulating UML
24 | models, object-relational persistence, aspect-oriented programming, and more.
25 | Visit the PEAK home page at http://peak.telecommunity.com for more information.
26 | """
27 |
28 | import sys
29 | import unittest
30 |
31 |
32 | class FrameInfoTest(unittest.TestCase):
33 |
34 | def test_w_module(self):
35 | from zope.interface.tests import advisory_testing
36 | (kind, module,
37 | f_locals, f_globals) = advisory_testing.moduleLevelFrameInfo
38 | self.assertEqual(kind, "module")
39 | for d in module.__dict__, f_locals, f_globals:
40 | self.assertIs(d, advisory_testing.my_globals)
41 |
42 | def test_w_class(self):
43 | from zope.interface.tests import advisory_testing
44 | (kind,
45 | module,
46 | f_locals,
47 | f_globals) = advisory_testing.NewStyleClass.classLevelFrameInfo
48 | self.assertEqual(kind, "class")
49 |
50 | for d in module.__dict__, f_globals:
51 | self.assertIs(d, advisory_testing.my_globals)
52 |
53 | def test_inside_function_call(self):
54 | from zope.interface.advice import getFrameInfo
55 | kind, module, f_locals, f_globals = getFrameInfo(sys._getframe())
56 | self.assertEqual(kind, "function call")
57 |
58 | frame = sys._getframe()
59 | self.assertEqual(f_locals, frame.f_locals)
60 | self.assertEqual(f_locals, locals())
61 |
62 | for d in module.__dict__, f_globals:
63 | self.assertIs(d, globals())
64 |
65 | def test_inside_exec(self):
66 | from zope.interface.advice import getFrameInfo
67 | _globals = {'getFrameInfo': getFrameInfo}
68 | _locals = {}
69 | exec(_FUNKY_EXEC, _globals, _locals)
70 | self.assertEqual(_locals['kind'], "exec")
71 | self.assertIs(_locals['f_locals'], _locals)
72 | self.assertIsNone(_locals['module'])
73 | self.assertIs(_locals['f_globals'], _globals)
74 |
75 |
76 | _FUNKY_EXEC = """\
77 | import sys
78 | kind, module, f_locals, f_globals = getFrameInfo(sys._getframe())
79 | """
80 |
81 |
82 | class Test_isClassAdvisor(unittest.TestCase):
83 |
84 | def _callFUT(self, *args, **kw):
85 | from zope.interface.advice import isClassAdvisor
86 | return isClassAdvisor(*args, **kw)
87 |
88 | def test_w_non_function(self):
89 | self.assertEqual(self._callFUT(self), False)
90 |
91 | def test_w_normal_function(self):
92 |
93 | def foo():
94 | raise NotImplementedError()
95 |
96 | self.assertEqual(self._callFUT(foo), False)
97 |
98 | def test_w_advisor_function(self):
99 |
100 | def bar():
101 | raise NotImplementedError()
102 |
103 | bar.previousMetaclass = object()
104 | self.assertEqual(self._callFUT(bar), True)
105 |
106 |
107 | class Test_determineMetaclass(unittest.TestCase):
108 |
109 | def _callFUT(self, *args, **kw):
110 | from zope.interface.advice import determineMetaclass
111 | return determineMetaclass(*args, **kw)
112 |
113 | def test_empty_w_explicit_metatype(self):
114 |
115 | class Meta(type):
116 | pass
117 |
118 | self.assertEqual(self._callFUT((), Meta), Meta)
119 |
120 | def test_single(self):
121 |
122 | class Meta(type):
123 | pass
124 |
125 | self.assertEqual(self._callFUT((Meta,)), type)
126 |
127 | def test_meta_of_class(self):
128 |
129 | class Metameta(type):
130 | pass
131 |
132 | class Meta(type, metaclass=Metameta):
133 | pass
134 |
135 | self.assertEqual(self._callFUT((Meta, type)), Metameta)
136 |
137 | def test_multiple_in_hierarchy_py3k(self):
138 |
139 | class Meta_A(type):
140 | pass
141 |
142 | class Meta_B(Meta_A):
143 | pass
144 |
145 | class A(type, metaclass=Meta_A):
146 | pass
147 |
148 | class B(type, metaclass=Meta_B):
149 | pass
150 |
151 | self.assertEqual(self._callFUT((A, B)), Meta_B)
152 |
153 | def test_multiple_not_in_hierarchy_py3k(self):
154 |
155 | class Meta_A(type):
156 | pass
157 |
158 | class Meta_B(type):
159 | pass
160 |
161 | class A(type, metaclass=Meta_A):
162 | pass
163 |
164 | class B(type, metaclass=Meta_B):
165 | pass
166 |
167 | self.assertRaises(TypeError, self._callFUT, (A, B))
168 |
169 |
170 | class Test_minimalBases(unittest.TestCase):
171 |
172 | def _callFUT(self, klasses):
173 | from zope.interface.advice import minimalBases
174 | return minimalBases(klasses)
175 |
176 | def test_empty(self):
177 | self.assertEqual(self._callFUT([]), [])
178 |
179 | def test_w_newstyle_meta(self):
180 | self.assertEqual(self._callFUT([type]), [type])
181 |
182 | def test_w_newstyle_class(self):
183 |
184 | class C:
185 | pass
186 |
187 | self.assertEqual(self._callFUT([C]), [C])
188 |
189 | def test_simple_hierarchy_skips_implied(self):
190 |
191 | class A:
192 | pass
193 |
194 | class B(A):
195 | pass
196 |
197 | class C(B):
198 | pass
199 |
200 | class D:
201 | pass
202 |
203 | self.assertEqual(self._callFUT([A, B, C]), [C])
204 | self.assertEqual(self._callFUT([A, C]), [C])
205 | self.assertEqual(self._callFUT([B, C]), [C])
206 | self.assertEqual(self._callFUT([A, B]), [B])
207 | self.assertEqual(self._callFUT([D, B, D]), [B, D])
208 |
209 | def test_repeats_kicked_to_end_of_queue(self):
210 |
211 | class A:
212 | pass
213 |
214 | class B:
215 | pass
216 |
217 | self.assertEqual(self._callFUT([A, B, A]), [B, A])
218 |
--------------------------------------------------------------------------------
/src/zope/interface/tests/test_compile_flags.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2022 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE
12 | #
13 | ##############################################################################
14 | import struct
15 | import unittest
16 |
17 | import zope.interface # noqa: try to load a C module for side effects
18 |
19 |
20 | class TestFloatingPoint(unittest.TestCase):
21 |
22 | def test_no_fast_math_optimization(self):
23 | # Building with -Ofast enables -ffast-math, which sets certain FPU
24 | # flags that can cause breakage elsewhere. A library such as BTrees
25 | # has no business changing global FPU flags for the entire process.
26 | zero_bits = struct.unpack("!Q", struct.pack("!d", 0.0))[0]
27 | next_up = zero_bits + 1
28 | smallest_subnormal = struct.unpack("!d", struct.pack("!Q", next_up))[0]
29 | self.assertNotEqual(smallest_subnormal, 0.0)
30 |
--------------------------------------------------------------------------------
/src/zope/interface/tests/test_element.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2003 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """Test Element meta-class.
15 | """
16 |
17 | import unittest
18 |
19 | from zope.interface.interface import Element
20 |
21 |
22 | class TestElement(unittest.TestCase):
23 |
24 | def test_taggedValues(self):
25 | """Test that we can update tagged values of more than one element
26 | """
27 |
28 | e1 = Element("foo")
29 | e2 = Element("bar")
30 | e1.setTaggedValue("x", 1)
31 | e2.setTaggedValue("x", 2)
32 | self.assertEqual(e1.getTaggedValue("x"), 1)
33 | self.assertEqual(e2.getTaggedValue("x"), 2)
34 |
--------------------------------------------------------------------------------
/src/zope/interface/tests/test_exceptions.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2010 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """ zope.interface.exceptions unit tests
15 | """
16 | import unittest
17 |
18 |
19 | def _makeIface():
20 | from zope.interface import Interface
21 |
22 | class IDummy(Interface):
23 | pass
24 |
25 | return IDummy
26 |
27 |
28 | class DoesNotImplementTests(unittest.TestCase):
29 |
30 | def _getTargetClass(self):
31 | from zope.interface.exceptions import DoesNotImplement
32 | return DoesNotImplement
33 |
34 | def _makeOne(self, *args):
35 | iface = _makeIface()
36 | return self._getTargetClass()(iface, *args)
37 |
38 | def test___str__(self):
39 | dni = self._makeOne()
40 | self.assertEqual(
41 | str(dni),
42 | "An object has failed to implement interface "
43 | "zope.interface.tests.test_exceptions.IDummy: "
44 | "Does not declaratively implement the interface."
45 | )
46 |
47 | def test___str__w_candidate(self):
48 | dni = self._makeOne('candidate')
49 | self.assertEqual(
50 | str(dni),
51 | "The object 'candidate' has failed to implement interface "
52 | "zope.interface.tests.test_exceptions.IDummy: "
53 | "Does not declaratively implement the interface."
54 | )
55 |
56 |
57 | class BrokenImplementationTests(unittest.TestCase):
58 |
59 | def _getTargetClass(self):
60 | from zope.interface.exceptions import BrokenImplementation
61 | return BrokenImplementation
62 |
63 | def _makeOne(self, *args):
64 | iface = _makeIface()
65 | return self._getTargetClass()(iface, 'missing', *args)
66 |
67 | def test___str__(self):
68 | dni = self._makeOne()
69 | self.assertEqual(
70 | str(dni),
71 | 'An object has failed to implement interface '
72 | 'zope.interface.tests.test_exceptions.IDummy: '
73 | "The 'missing' attribute was not provided.")
74 |
75 | def test___str__w_candidate(self):
76 | dni = self._makeOne('candidate')
77 | self.assertEqual(
78 | str(dni),
79 | 'The object \'candidate\' has failed to implement interface '
80 | 'zope.interface.tests.test_exceptions.IDummy: '
81 | "The 'missing' attribute was not provided.")
82 |
83 |
84 | def broken_function():
85 | """
86 | This is a global function with a simple argument list.
87 |
88 | It exists to be able to report the same information when
89 | formatting signatures.
90 | """
91 |
92 |
93 | class BrokenMethodImplementationTests(unittest.TestCase):
94 |
95 | def _getTargetClass(self):
96 | from zope.interface.exceptions import BrokenMethodImplementation
97 | return BrokenMethodImplementation
98 |
99 | message = 'I said so'
100 |
101 | def _makeOne(self, *args):
102 | return self._getTargetClass()('aMethod', self.message, *args)
103 |
104 | def test___str__(self):
105 | dni = self._makeOne()
106 | self.assertEqual(
107 | str(dni),
108 | "An object has failed to implement interface : "
109 | "The contract of 'aMethod' is violated because I said so."
110 | )
111 |
112 | def test___str__w_candidate_no_implementation(self):
113 | dni = self._makeOne('some_function', '', 'candidate')
114 | self.assertEqual(
115 | str(dni),
116 | "The object 'candidate' has failed to implement interface : "
117 | "The contract of 'aMethod' is violated because I said so."
118 | )
119 |
120 | def test___str__w_candidate_w_implementation(self):
121 | self.message = 'implementation is wonky'
122 | dni = self._makeOne(broken_function, '', 'candidate')
123 | self.assertEqual(
124 | str(dni),
125 | "The object 'candidate' has failed to implement interface : "
126 | "The contract of 'aMethod' is violated because "
127 | "'broken_function()' is wonky."
128 | )
129 |
130 | def test___str__w_candidate_w_implementation_not_callable(self):
131 | self.message = 'implementation is not callable'
132 | dni = self._makeOne(42, '', 'candidate')
133 | self.assertEqual(
134 | str(dni),
135 | "The object 'candidate' has failed to implement interface : "
136 | "The contract of 'aMethod' is violated because "
137 | "'42' is not callable."
138 | )
139 |
140 | def test___repr__w_candidate(self):
141 | dni = self._makeOne(None, 'candidate')
142 | self.assertEqual(
143 | repr(dni),
144 | "BrokenMethodImplementation("
145 | "'aMethod', 'I said so', None, 'candidate')"
146 | )
147 |
148 |
149 | class MultipleInvalidTests(unittest.TestCase):
150 |
151 | def _getTargetClass(self):
152 | from zope.interface.exceptions import MultipleInvalid
153 | return MultipleInvalid
154 |
155 | def _makeOne(self, excs):
156 | iface = _makeIface()
157 | return self._getTargetClass()(iface, 'target', excs)
158 |
159 | def test__str__(self):
160 | from zope.interface.exceptions import BrokenMethodImplementation
161 | excs = [
162 | BrokenMethodImplementation('aMethod', 'I said so'),
163 | Exception("Regular exception")
164 | ]
165 | dni = self._makeOne(excs)
166 | self.assertEqual(
167 | str(dni),
168 | "The object 'target' has failed to implement interface "
169 | "zope.interface.tests.test_exceptions.IDummy:\n"
170 | " The contract of 'aMethod' is violated because I said so\n"
171 | " Regular exception"
172 | )
173 |
174 | def test__repr__(self):
175 | from zope.interface.exceptions import BrokenMethodImplementation
176 | excs = [
177 | BrokenMethodImplementation('aMethod', 'I said so'),
178 | # Use multiple arguments to normalize repr; versions of Python
179 | # prior to 3.7 add a trailing comma if there's just one.
180 | Exception("Regular", "exception")
181 | ]
182 | dni = self._makeOne(excs)
183 | self.assertEqual(
184 | repr(dni),
185 | "MultipleInvalid("
186 | ","
187 | " 'target',"
188 | " (BrokenMethodImplementation('aMethod', 'I said so'),"
189 | " Exception('Regular', 'exception')))"
190 | )
191 |
--------------------------------------------------------------------------------
/src/zope/interface/tests/test_interfaces.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 |
4 | class _ConformsToIObjectEvent:
5 |
6 | def _makeOne(self, target=None):
7 | if target is None:
8 | target = object()
9 | return self._getTargetClass()(target)
10 |
11 | def test_class_conforms_to_IObjectEvent(self):
12 | from zope.interface.interfaces import IObjectEvent
13 | from zope.interface.verify import verifyClass
14 | verifyClass(IObjectEvent, self._getTargetClass())
15 |
16 | def test_instance_conforms_to_IObjectEvent(self):
17 | from zope.interface.interfaces import IObjectEvent
18 | from zope.interface.verify import verifyObject
19 | verifyObject(IObjectEvent, self._makeOne())
20 |
21 |
22 | class _ConformsToIRegistrationEvent(_ConformsToIObjectEvent):
23 |
24 | def test_class_conforms_to_IRegistrationEvent(self):
25 | from zope.interface.interfaces import IRegistrationEvent
26 | from zope.interface.verify import verifyClass
27 | verifyClass(IRegistrationEvent, self._getTargetClass())
28 |
29 | def test_instance_conforms_to_IRegistrationEvent(self):
30 | from zope.interface.interfaces import IRegistrationEvent
31 | from zope.interface.verify import verifyObject
32 | verifyObject(IRegistrationEvent, self._makeOne())
33 |
34 |
35 | class ObjectEventTests(unittest.TestCase, _ConformsToIObjectEvent):
36 |
37 | def _getTargetClass(self):
38 | from zope.interface.interfaces import ObjectEvent
39 | return ObjectEvent
40 |
41 | def test_ctor(self):
42 | target = object()
43 | event = self._makeOne(target)
44 | self.assertIs(event.object, target)
45 |
46 |
47 | class RegistrationEventTests(unittest.TestCase,
48 | _ConformsToIRegistrationEvent):
49 |
50 | def _getTargetClass(self):
51 | from zope.interface.interfaces import RegistrationEvent
52 | return RegistrationEvent
53 |
54 | def test___repr__(self):
55 | target = object()
56 | event = self._makeOne(target)
57 | r = repr(event)
58 | self.assertEqual(r.splitlines(),
59 | ['RegistrationEvent event:', repr(target)])
60 |
61 |
62 | class RegisteredTests(unittest.TestCase,
63 | _ConformsToIRegistrationEvent):
64 |
65 | def _getTargetClass(self):
66 | from zope.interface.interfaces import Registered
67 | return Registered
68 |
69 | def test_class_conforms_to_IRegistered(self):
70 | from zope.interface.interfaces import IRegistered
71 | from zope.interface.verify import verifyClass
72 | verifyClass(IRegistered, self._getTargetClass())
73 |
74 | def test_instance_conforms_to_IRegistered(self):
75 | from zope.interface.interfaces import IRegistered
76 | from zope.interface.verify import verifyObject
77 | verifyObject(IRegistered, self._makeOne())
78 |
79 |
80 | class UnregisteredTests(unittest.TestCase,
81 | _ConformsToIRegistrationEvent):
82 |
83 | def _getTargetClass(self):
84 | from zope.interface.interfaces import Unregistered
85 | return Unregistered
86 |
87 | def test_class_conforms_to_IUnregistered(self):
88 | from zope.interface.interfaces import IUnregistered
89 | from zope.interface.verify import verifyClass
90 | verifyClass(IUnregistered, self._getTargetClass())
91 |
92 | def test_instance_conforms_to_IUnregistered(self):
93 | from zope.interface.interfaces import IUnregistered
94 | from zope.interface.verify import verifyObject
95 | verifyObject(IUnregistered, self._makeOne())
96 |
97 |
98 | class InterfaceClassTests(unittest.TestCase):
99 |
100 | def _getTargetClass(self):
101 | from zope.interface.interface import InterfaceClass
102 | return InterfaceClass
103 |
104 | def _getTargetInterface(self):
105 | from zope.interface.interfaces import IInterface
106 | return IInterface
107 |
108 | def _makeOne(self):
109 | from zope.interface.interface import Interface
110 | return Interface
111 |
112 | def test_class_conforms(self):
113 | from zope.interface.verify import verifyClass
114 | verifyClass(self._getTargetInterface(), self._getTargetClass())
115 |
116 | def test_instance_conforms(self):
117 | from zope.interface.verify import verifyObject
118 | verifyObject(self._getTargetInterface(), self._makeOne())
119 |
120 | def test_instance_consistent__iro__(self):
121 | from zope.interface import ro
122 | self.assertTrue(ro.is_consistent(self._getTargetInterface()))
123 |
124 | def test_class_consistent__iro__(self):
125 | from zope.interface import implementedBy
126 | from zope.interface import ro
127 |
128 | self.assertTrue(
129 | ro.is_consistent(implementedBy(self._getTargetClass()))
130 | )
131 |
--------------------------------------------------------------------------------
/src/zope/interface/tests/test_odd_declarations.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2003 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """Test interface declarations against ExtensionClass-like classes.
15 |
16 | These tests are to make sure we do something sane in the presence of
17 | classic ExtensionClass classes and instances.
18 | """
19 | import unittest
20 |
21 | from zope.interface import Interface
22 | from zope.interface import classImplements
23 | from zope.interface import classImplementsOnly
24 | from zope.interface import directlyProvidedBy
25 | from zope.interface import directlyProvides
26 | from zope.interface import implementedBy
27 | from zope.interface import implementer
28 | from zope.interface import providedBy
29 | from zope.interface.tests import odd
30 |
31 |
32 | class I1(Interface):
33 | pass
34 |
35 |
36 | class I2(Interface):
37 | pass
38 |
39 |
40 | class I3(Interface):
41 | pass
42 |
43 |
44 | class I31(I3):
45 | pass
46 |
47 |
48 | class I4(Interface):
49 | pass
50 |
51 |
52 | class I5(Interface):
53 | pass
54 |
55 |
56 | class Odd:
57 | pass
58 |
59 |
60 | Odd = odd.MetaClass('Odd', Odd.__bases__, {})
61 |
62 |
63 | class B(Odd):
64 | __implemented__ = I2
65 |
66 |
67 | # TODO: We are going to need more magic to make classProvides work with odd
68 | # classes. This will work in the next iteration. For now, we'll use
69 | # a different mechanism.
70 |
71 | # from zope.interface import classProvides
72 | class A(Odd):
73 | pass
74 |
75 |
76 | classImplements(A, I1)
77 |
78 |
79 | class C(A, B):
80 | pass
81 |
82 |
83 | classImplements(C, I31)
84 |
85 |
86 | class Test(unittest.TestCase):
87 |
88 | def test_ObjectSpecification(self):
89 | c = C()
90 | directlyProvides(c, I4)
91 | self.assertEqual([i.getName() for i in providedBy(c)],
92 | ['I4', 'I31', 'I1', 'I2']
93 | )
94 | self.assertEqual([i.getName() for i in providedBy(c).flattened()],
95 | ['I4', 'I31', 'I3', 'I1', 'I2', 'Interface']
96 | )
97 | self.assertIn(I1, providedBy(c))
98 | self.assertNotIn(I3, providedBy(c))
99 | self.assertTrue(providedBy(c).extends(I3))
100 | self.assertTrue(providedBy(c).extends(I31))
101 | self.assertFalse(providedBy(c).extends(I5))
102 |
103 | class COnly(A, B):
104 | pass
105 | classImplementsOnly(COnly, I31)
106 |
107 | class D(COnly):
108 | pass
109 | classImplements(D, I5)
110 |
111 | classImplements(D, I5)
112 |
113 | c = D()
114 | directlyProvides(c, I4)
115 | self.assertEqual([i.getName() for i in providedBy(c)],
116 | ['I4', 'I5', 'I31'])
117 | self.assertEqual([i.getName() for i in providedBy(c).flattened()],
118 | ['I4', 'I5', 'I31', 'I3', 'Interface'])
119 | self.assertNotIn(I1, providedBy(c))
120 | self.assertNotIn(I3, providedBy(c))
121 | self.assertTrue(providedBy(c).extends(I3))
122 | self.assertFalse(providedBy(c).extends(I1))
123 | self.assertTrue(providedBy(c).extends(I31))
124 | self.assertTrue(providedBy(c).extends(I5))
125 |
126 | class COnly(A, B):
127 | __implemented__ = I31
128 |
129 | class D(COnly):
130 | pass
131 |
132 | classImplements(D, I5)
133 | classImplements(D, I5)
134 |
135 | c = D()
136 | directlyProvides(c, I4)
137 | self.assertEqual([i.getName() for i in providedBy(c)],
138 | ['I4', 'I5', 'I31'])
139 | self.assertEqual([i.getName() for i in providedBy(c).flattened()],
140 | ['I4', 'I5', 'I31', 'I3', 'Interface'])
141 | self.assertNotIn(I1, providedBy(c))
142 | self.assertNotIn(I3, providedBy(c))
143 | self.assertTrue(providedBy(c).extends(I3))
144 | self.assertFalse(providedBy(c).extends(I1))
145 | self.assertTrue(providedBy(c).extends(I31))
146 | self.assertTrue(providedBy(c).extends(I5))
147 |
148 | def test_classImplements(self):
149 |
150 | @implementer(I3)
151 | class A(Odd):
152 | pass
153 |
154 | @implementer(I4)
155 | class B(Odd):
156 | pass
157 |
158 | class C(A, B):
159 | pass
160 |
161 | classImplements(C, I1, I2)
162 | self.assertEqual([i.getName() for i in implementedBy(C)],
163 | ['I1', 'I2', 'I3', 'I4'])
164 |
165 | classImplements(C, I5)
166 | self.assertEqual([i.getName() for i in implementedBy(C)],
167 | ['I1', 'I2', 'I5', 'I3', 'I4'])
168 |
169 | def test_classImplementsOnly(self):
170 |
171 | @implementer(I3)
172 | class A(Odd):
173 | pass
174 |
175 | @implementer(I4)
176 | class B(Odd):
177 | pass
178 |
179 | class C(A, B):
180 | pass
181 |
182 | classImplementsOnly(C, I1, I2)
183 | self.assertEqual([i.__name__ for i in implementedBy(C)],
184 | ['I1', 'I2'])
185 |
186 | def test_directlyProvides(self):
187 |
188 | class IA1(Interface):
189 | pass
190 |
191 | class IA2(Interface):
192 | pass
193 |
194 | class IB(Interface):
195 | pass
196 |
197 | class IC(Interface):
198 | pass
199 |
200 | class A(Odd):
201 | pass
202 | classImplements(A, IA1, IA2)
203 |
204 | class B(Odd):
205 | pass
206 | classImplements(B, IB)
207 |
208 | class C(A, B):
209 | pass
210 | classImplements(C, IC)
211 |
212 | ob = C()
213 | directlyProvides(ob, I1, I2)
214 | self.assertIn(I1, providedBy(ob))
215 | self.assertIn(I2, providedBy(ob))
216 | self.assertIn(IA1, providedBy(ob))
217 | self.assertIn(IA2, providedBy(ob))
218 | self.assertIn(IB, providedBy(ob))
219 | self.assertIn(IC, providedBy(ob))
220 |
221 | directlyProvides(ob, directlyProvidedBy(ob) - I2)
222 | self.assertIn(I1, providedBy(ob))
223 | self.assertNotIn(I2, providedBy(ob))
224 | self.assertNotIn(I2, providedBy(ob))
225 | directlyProvides(ob, directlyProvidedBy(ob), I2)
226 | self.assertIn(I2, providedBy(ob))
227 |
228 | # see above
229 | # def TODO_test_classProvides_fails_for_odd_class(self):
230 | # try:
231 | # class A(Odd):
232 | # classProvides(I1)
233 | # except TypeError:
234 | # pass # Success
235 | # self.assert_(
236 | # False,
237 | # "Shouldn't be able to use directlyProvides on odd class."
238 | # )
239 |
240 | def test_implementedBy(self):
241 |
242 | class I2(I1):
243 | pass
244 |
245 | class C1(Odd):
246 | pass
247 |
248 | classImplements(C1, I2)
249 |
250 | class C2(C1):
251 | pass
252 |
253 | classImplements(C2, I3)
254 |
255 | self.assertEqual([i.getName() for i in implementedBy(C2)],
256 | ['I3', 'I2'])
257 |
258 | def test_odd_metaclass_that_doesnt_subclass_type(self):
259 | # This was originally a doctest in odd.py.
260 | # It verifies that the metaclass the rest of these tests use
261 | # works as expected.
262 |
263 | # This is used for testing support for ExtensionClass in new
264 | # interfaces.
265 |
266 | class A:
267 | a = 1
268 |
269 | A = odd.MetaClass('A', A.__bases__, A.__dict__)
270 |
271 | class B:
272 | b = 1
273 |
274 | B = odd.MetaClass('B', B.__bases__, B.__dict__)
275 |
276 | class C(A, B):
277 | pass
278 |
279 | self.assertEqual(C.__bases__, (A, B))
280 |
281 | a = A()
282 | aa = A()
283 | self.assertEqual(a.a, 1)
284 | self.assertEqual(aa.a, 1)
285 |
286 | aa.a = 2
287 | self.assertEqual(a.a, 1)
288 | self.assertEqual(aa.a, 2)
289 |
290 | c = C()
291 | self.assertEqual(c.a, 1)
292 | self.assertEqual(c.b, 1)
293 |
294 | c.b = 2
295 | self.assertEqual(c.b, 2)
296 |
297 | C.c = 1
298 | self.assertEqual(c.c, 1)
299 | c.c
300 |
301 | self.assertIs(C.__class__.__class__, C.__class__)
302 |
--------------------------------------------------------------------------------
/src/zope/interface/tests/test_sorting.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2001, 2002 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """Test interface sorting
15 | """
16 |
17 | import unittest
18 |
19 | from zope.interface import Interface
20 |
21 |
22 | class I1(Interface):
23 | pass
24 |
25 |
26 | class I2(I1):
27 | pass
28 |
29 |
30 | class I3(I1):
31 | pass
32 |
33 |
34 | class I4(Interface):
35 | pass
36 |
37 |
38 | class I5(I4):
39 | pass
40 |
41 |
42 | class I6(I2):
43 | pass
44 |
45 |
46 | class Test(unittest.TestCase):
47 |
48 | def test(self):
49 | iface_list = [I1, I3, I5, I6, I4, I2]
50 | iface_list.sort()
51 | self.assertEqual(iface_list, [I1, I2, I3, I4, I5, I6])
52 |
53 | def test_w_None(self):
54 | iface_list = [I1, None, I3, I5, I6, I4, I2]
55 | iface_list.sort()
56 | self.assertEqual(iface_list, [I1, I2, I3, I4, I5, I6, None])
57 |
58 | def test_w_equal_names(self):
59 | # interfaces with equal names but different modules should sort by
60 | # module name
61 | from zope.interface.tests.m1 import I1 as m1_I1
62 | iface_list = [I1, m1_I1]
63 | iface_list.sort()
64 | self.assertEqual(iface_list, [m1_I1, I1])
65 |
66 | def test_I1_I2(self):
67 | self.assertLess(I1.__name__, I2.__name__)
68 | self.assertEqual(I1.__module__, I2.__module__)
69 | self.assertEqual(I1.__module__, __name__)
70 | self.assertLess(I1, I2)
71 |
72 | def _makeI1(self):
73 |
74 | class I1(Interface):
75 | pass
76 |
77 | return I1
78 |
79 | def test_nested(self):
80 | nested_I1 = self._makeI1()
81 | self.assertEqual(I1, nested_I1)
82 | self.assertEqual(nested_I1, I1)
83 | self.assertEqual(hash(I1), hash(nested_I1))
84 |
--------------------------------------------------------------------------------
/src/zope/interface/verify.py:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | #
3 | # Copyright (c) 2001, 2002 Zope Foundation and Contributors.
4 | # All Rights Reserved.
5 | #
6 | # This software is subject to the provisions of the Zope Public License,
7 | # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 | # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 | # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 | # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 | # FOR A PARTICULAR PURPOSE.
12 | #
13 | ##############################################################################
14 | """Verify interface implementations
15 | """
16 | import inspect
17 | import sys
18 | from types import FunctionType
19 | from types import MethodType
20 |
21 | from zope.interface.exceptions import BrokenImplementation
22 | from zope.interface.exceptions import BrokenMethodImplementation
23 | from zope.interface.exceptions import DoesNotImplement
24 | from zope.interface.exceptions import Invalid
25 | from zope.interface.exceptions import MultipleInvalid
26 | from zope.interface.interface import Method
27 | from zope.interface.interface import fromFunction
28 | from zope.interface.interface import fromMethod
29 |
30 |
31 | __all__ = [
32 | 'verifyObject',
33 | 'verifyClass',
34 | ]
35 |
36 | # This will be monkey-patched when running under Zope 2, so leave this
37 | # here:
38 | MethodTypes = (MethodType, )
39 |
40 |
41 | def _verify(iface, candidate, tentative=False, vtype=None):
42 | """
43 | Verify that *candidate* might correctly provide *iface*.
44 |
45 | This involves:
46 |
47 | - Making sure the candidate claims that it provides the
48 | interface using ``iface.providedBy`` (unless *tentative* is `True`, in
49 | which case this step is skipped). This means that the candidate's class
50 | declares that it `implements ` the
51 | interface, or the candidate itself declares that it `provides
52 | `
53 | the interface
54 |
55 | - Making sure the candidate defines all the necessary methods
56 |
57 | - Making sure the methods have the correct signature (to the
58 | extent possible)
59 |
60 | - Making sure the candidate defines all the necessary attributes
61 |
62 | :return bool: Returns a true value if everything that could be
63 | checked passed.
64 | :raises zope.interface.Invalid: If any of the previous
65 | conditions does not hold.
66 |
67 | .. versionchanged:: 5.0
68 | If multiple methods or attributes are invalid, all such errors
69 | are collected and reported. Previously, only the first error was
70 | reported. As a special case, if only one such error is present, it is
71 | raised alone, like before.
72 | """
73 |
74 | if vtype == 'c':
75 | tester = iface.implementedBy
76 | else:
77 | tester = iface.providedBy
78 |
79 | excs = []
80 | if not tentative and not tester(candidate):
81 | excs.append(DoesNotImplement(iface, candidate))
82 |
83 | for name, desc in iface.namesAndDescriptions(all=True):
84 | try:
85 | _verify_element(iface, name, desc, candidate, vtype)
86 | except Invalid as e:
87 | excs.append(e)
88 |
89 | if excs:
90 | if len(excs) == 1:
91 | raise excs[0]
92 | raise MultipleInvalid(iface, candidate, excs)
93 |
94 | return True
95 |
96 |
97 | def _verify_element(iface, name, desc, candidate, vtype):
98 | # Here the `desc` is either an `Attribute` or `Method` instance
99 | try:
100 | attr = getattr(candidate, name)
101 | except AttributeError:
102 |
103 | if (not isinstance(desc, Method)) and vtype == 'c':
104 | # We can't verify non-methods on classes, since the
105 | # class may provide attrs in it's __init__.
106 | return
107 |
108 | # TODO: This should use ``raise...from``
109 | raise BrokenImplementation(iface, desc, candidate)
110 |
111 | if not isinstance(desc, Method):
112 | # If it's not a method, there's nothing else we can test
113 | return
114 |
115 | if inspect.ismethoddescriptor(attr) or inspect.isbuiltin(attr):
116 | # The first case is what you get for things like ``dict.pop``
117 | # on CPython (e.g., ``verifyClass(IFullMapping, dict))``). The
118 | # second case is what you get for things like ``dict().pop`` on
119 | # CPython (e.g., ``verifyObject(IFullMapping, dict()))``.
120 | # In neither case can we get a signature, so there's nothing
121 | # to verify. Even the inspect module gives up and raises
122 | # ValueError: no signature found. The ``__text_signature__`` attribute
123 | # isn't typically populated either.
124 | #
125 | # Note that on PyPy 2 or 3 (up through 7.3 at least), these are not
126 | # true for things like ``dict.pop`` (but might be true for C
127 | # extensions?)
128 | return
129 |
130 | if isinstance(attr, FunctionType):
131 |
132 | if isinstance(candidate, type) and vtype == 'c':
133 | # This is an "unbound method".
134 | # Only unwrap this if we're verifying implementedBy;
135 | # otherwise we can unwrap @staticmethod on classes that directly
136 | # provide an interface.
137 | meth = fromFunction(attr, iface, name=name, imlevel=1)
138 | else:
139 | # Nope, just a normal function
140 | meth = fromFunction(attr, iface, name=name)
141 |
142 | elif (
143 | isinstance(attr, MethodTypes) and
144 | type(attr.__func__) is FunctionType
145 | ):
146 | meth = fromMethod(attr, iface, name)
147 |
148 | elif isinstance(attr, property) and vtype == 'c':
149 | # Without an instance we cannot be sure it's not a
150 | # callable.
151 | # TODO: This should probably check inspect.isdatadescriptor(),
152 | # a more general form than ``property``
153 | return
154 |
155 | else:
156 | if not callable(attr):
157 | raise BrokenMethodImplementation(
158 | desc,
159 | "implementation is not a method",
160 | attr,
161 | iface,
162 | candidate
163 | )
164 | # sigh, it's callable, but we don't know how to introspect it, so
165 | # we have to give it a pass.
166 | return
167 |
168 | # Make sure that the required and implemented method signatures are
169 | # the same.
170 | mess = _incompat(desc.getSignatureInfo(), meth.getSignatureInfo())
171 | if mess:
172 | raise BrokenMethodImplementation(desc, mess, attr, iface, candidate)
173 |
174 |
175 | def verifyClass(iface, candidate, tentative=False):
176 | """
177 | Verify that the *candidate* might correctly provide *iface*.
178 | """
179 | return _verify(iface, candidate, tentative, vtype='c')
180 |
181 |
182 | def verifyObject(iface, candidate, tentative=False):
183 | return _verify(iface, candidate, tentative, vtype='o')
184 |
185 |
186 | verifyObject.__doc__ = _verify.__doc__
187 |
188 | _MSG_TOO_MANY = 'implementation requires too many arguments'
189 |
190 |
191 | def _incompat(required, implemented):
192 | # if (required['positional'] !=
193 | # implemented['positional'][:len(required['positional'])]
194 | # and implemented['kwargs'] is None):
195 | # return 'imlementation has different argument names'
196 | if len(implemented['required']) > len(required['required']):
197 | return _MSG_TOO_MANY
198 |
199 | if (
200 | (len(implemented['positional']) < len(required['positional'])) and
201 | not implemented['varargs']
202 | ):
203 | return "implementation doesn't allow enough arguments"
204 |
205 | if required['kwargs'] and not implemented['kwargs']:
206 | return "implementation doesn't support keyword arguments"
207 |
208 | if required['varargs'] and not implemented['varargs']:
209 | return "implementation doesn't support variable arguments"
210 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | # Generated from:
2 | # https://github.com/zopefoundation/meta/tree/master/config/c-code
3 | [tox]
4 | minversion = 4.0
5 | envlist =
6 | release-check
7 | lint
8 | py39,py39-pure
9 | py310,py310-pure
10 | py311,py311-pure
11 | py312,py312-pure
12 | py313,py313-pure
13 | py314,py314-pure
14 | pypy3
15 | docs
16 | coverage
17 |
18 | [testenv]
19 | pip_pre = py314: true
20 | deps =
21 | setuptools <= 75.6.0
22 | Sphinx
23 | setenv =
24 | pure: PURE_PYTHON=1
25 | !pure-!pypy3: PURE_PYTHON=0
26 | ZOPE_INTERFACE_STRICT_IRO=1
27 | commands =
28 | pip install -U -e .[test]
29 | coverage run -p -m unittest discover -s src {posargs}
30 | sphinx-build -b doctest -d {envdir}/.cache/doctrees docs {envdir}/.cache/doctest
31 | extras =
32 | test
33 | docs
34 |
35 | [testenv:setuptools-latest]
36 | basepython = python3
37 | deps =
38 | git+https://github.com/pypa/setuptools.git\#egg=setuptools
39 |
40 | [testenv:coverage]
41 | basepython = python3
42 | allowlist_externals =
43 | mkdir
44 | deps =
45 | coverage[toml]
46 | setenv =
47 | PURE_PYTHON=1
48 | commands =
49 | mkdir -p {toxinidir}/parts/htmlcov
50 | coverage combine
51 | coverage html
52 | coverage report
53 | depends = py38,py38-pure,py39,py39-pure,py310,py310-pure,py311,py311-pure,py312,py312-pure,py313,py313-pure,pypy3,docs
54 | parallel_show_output = true
55 |
56 | [testenv:release-check]
57 | description = ensure that the distribution is ready to release
58 | basepython = python3
59 | skip_install = true
60 | deps =
61 | setuptools <= 75.6.0
62 | twine
63 | build
64 | check-manifest
65 | check-python-versions >= 0.20.0
66 | wheel
67 | commands_pre =
68 | commands =
69 | check-manifest
70 | check-python-versions --only setup.py,tox.ini,.github/workflows/tests.yml
71 | python -m build --sdist --no-isolation
72 | twine check dist/*
73 |
74 | [testenv:lint]
75 | description = This env runs all linters configured in .pre-commit-config.yaml
76 | basepython = python3
77 | skip_install = true
78 | deps =
79 | pre-commit
80 | commands_pre =
81 | commands =
82 | pre-commit run --all-files --show-diff-on-failure
83 |
84 | [testenv:docs]
85 | basepython = python3
86 | skip_install = false
87 | commands_pre =
88 | commands =
89 | sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html
90 | sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest
91 |
--------------------------------------------------------------------------------