pax_global_header00006660000000000000000000000064145552674100014523gustar00rootroot0000000000000052 comment=0e71df17fb3066cb8bc2429d3fd0dbb3c6ca231f overrides-7.7.0/000077500000000000000000000000001455526741000135405ustar00rootroot00000000000000overrides-7.7.0/.github/000077500000000000000000000000001455526741000151005ustar00rootroot00000000000000overrides-7.7.0/.github/workflows/000077500000000000000000000000001455526741000171355ustar00rootroot00000000000000overrides-7.7.0/.github/workflows/ci.yml000066400000000000000000000013401455526741000202510ustar00rootroot00000000000000name: CI on: push: branches: [master, main] pull_request: jobs: build: runs-on: ubuntu-latest strategy: matrix: python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v2 - name: Setup Python uses: actions/setup-python@v2 with: python-version: ${{ matrix.python }} - name: Install requirements run: | pip install -U pip pip install -r requirements-dev.txt pip install . - name: Run pytest run: pytest tests - name: Run mypy run: mypy overrides - name: Run mypy static tests (Python=3.11) if: matrix.python == '3.11' run: ./check_mypy.sh overrides-7.7.0/.gitignore000066400000000000000000000013611455526741000155310ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # C extensions *.so .venv .DS_Store tmp/ # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ # Idea .idea/ overrides.iml overrides-7.7.0/LICENSE000066400000000000000000000261361455526741000145550ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. overrides-7.7.0/MANIFEST.in000066400000000000000000000001611455526741000152740ustar00rootroot00000000000000include LICENSE include README.rst include tox.ini include overrides/py.typed recursive-include tests *.py *.pyi overrides-7.7.0/README.rst000066400000000000000000000117611455526741000152350ustar00rootroot00000000000000overrides ========= .. image:: https://img.shields.io/pypi/v/overrides.svg :target: https://pypi.python.org/pypi/overrides .. image:: http://pepy.tech/badge/overrides :target: http://pepy.tech/project/overrides A decorator ``@override`` that verifies that a method that should override an inherited method actually does it. Copies the docstring of the inherited method to the overridden method. Since signature validation and docstring inheritance are performed on class creation and not on class instantiation, this library significantly improves the safety and experience of creating class hierarchies in Python without significantly impacting performance. See https://stackoverflow.com/q/1167617 for the initial inspiration for this library. Motivation ---------- Python has no standard mechanism by which to guarantee that (1) a method that previously overrode an inherited method continues to do so, and (2) a method that previously did not override an inherited will not override now. This opens the door for subtle problems as class hierarchies evolve over time. For example, 1. A method that is added to a superclass is shadowed by an existing method with the same name in a subclass. 2. A method of a superclass that is overridden by a subclass is renamed in the superclass but not in the subclass. 3. A method of a superclass that is overridden by a subclass is removed in the superclass but not in the subclass. 4. A method of a superclass that is overridden by a subclass but the signature of the overridden method is incompatible with that of the inherited one. These can be only checked by explicitly marking method override in the code. Python also has no standard mechanism by which to inherit docstrings in overridden methods. Because most standard linters (e.g., flake8) have rules that require all public methods to have a docstring, this inevitably leads to a proliferation of ``See parent class for usage`` docstrings on overridden methods, or, worse, to a disabling of these rules altogether. In addition, mediocre or missing docstrings degrade the quality of tooltips and completions that can be provided by an editor. Installation ------------ Compatible with Python 3.6+. .. code-block:: bash $ pip install overrides Usage ----- Use ``@override`` to indicate that a subclass method should override a superclass method. .. code-block:: python from overrides import override class SuperClass: def foo(self): """This docstring will be inherited by any method that overrides this!""" return 1 def bar(self, x) -> str: return x class SubClass(SuperClass): @override def foo(self): return 2 @override def bar(self, y) -> int: # Raises, because the signature is not compatible. return y @override def zoo(self): # Raises, because does not exist in the super class. return "foobarzoo" Use ``EnforceOverrides`` to require subclass methods that shadow superclass methods to be decorated with ``@override``. .. code-block:: python from overrides import EnforceOverrides class SuperClass(EnforceOverrides): def foo(self): return 1 class SubClass(SuperClass): def foo(self): # Raises, because @override is missing. return 2 Use ``@final`` to indicate that a superclass method cannot be overriden. With Python 3.11 and above ``@final`` is directly `typing.final `_. .. code-block:: python from overrides import EnforceOverrides, final, override class SuperClass(EnforceOverrides): @final def foo(self): return 1 class SubClass(SuperClass): @override def foo(self): # Raises, because overriding a final method is forbidden. return 2 Note that ``@classmethod`` and ``@staticmethod`` must be declared before ``@override``. .. code-block:: python from overrides import override class SuperClass: @staticmethod def foo(x): return 1 class SubClass(SuperClass): @staticmethod @override def foo(x): return 2 Flags of control ---------------- .. code-block:: python # To prevent all signature checks do: @override(check_signature=False) def some_method(self, now_this_can_be_funny_and_wrong: str, what_ever: int) -> "Dictirux": pass # To do the check only at runtime and solve some forward reference problems @override(check_at_runtime=True) def some_other_method(self, ..) -> "SomethingDefinedLater": pass a.some_other_method() # Kaboom if not SomethingDefinedLater Contributors ------------ This project exists only through the work of all the people who contribute. mkorpela, drorasaf, ngoodman90, TylerYep, leeopop, donpatrice, jayvdb, joelgrus, lisyarus, soulmerge, rkr-at-dbx, ashwin153, brentyi, jobh, tjsmart, bersbersbers, LysanderGG, mgorny. overrides-7.7.0/check_mypy.sh000077500000000000000000000001621455526741000162310ustar00rootroot00000000000000#!/bin/bash -x set -e mypy mypy_fails | grep "Found 3 errors in 2 files (checked 2 source files)" mypy mypy_passesoverrides-7.7.0/conftest.py000066400000000000000000000004311455526741000157350ustar00rootroot00000000000000import sys from typing import Optional import py def pytest_ignore_collect(path: py.path.local, config: "Config") -> Optional[bool]: if sys.version_info[0] == 3 and sys.version_info[1] < 8: if str(path).endswith("__py38.py"): return True return None overrides-7.7.0/mypy_fails/000077500000000000000000000000001455526741000157145ustar00rootroot00000000000000overrides-7.7.0/mypy_fails/wrong_signature.py000066400000000000000000000003121455526741000214770ustar00rootroot00000000000000from overrides import override class Parent: def metodi(self, x: int) -> str: return f"{x}" class Child(Parent): @override def metodi(self, x: str) -> int: return int(x) overrides-7.7.0/mypy_fails/wrong_signature_forward_ref.py000066400000000000000000000002641455526741000240650ustar00rootroot00000000000000from overrides import override class Parent: def metoda(self) -> None: pass class Child(Parent): @override def metoda(self) -> "Child": return self overrides-7.7.0/mypy_passes/000077500000000000000000000000001455526741000161145ustar00rootroot00000000000000overrides-7.7.0/mypy_passes/passing_override.py000066400000000000000000000004511455526741000220310ustar00rootroot00000000000000from typing import Tuple from overrides import override class Parent: def execute(self, x: int, y: str, z: bool, *args, **kwargs) -> Tuple[str, int]: return y, x class Child(Parent): @override def execute(self, *args, **kwargs) -> Tuple[str, int]: return "moi", 1 overrides-7.7.0/mypy_passes/passing_rewrite.py000066400000000000000000000004411455526741000216720ustar00rootroot00000000000000""""Bug.""" from typing import Callable, TypeVar, ParamSpec P = ParamSpec("P") T = TypeVar("T") try: from overrides import override except ModuleNotFoundError: def override(func: Callable[P, T]) -> Callable[P, T]: # type: ignore[no-redef] return func print(override) overrides-7.7.0/overrides/000077500000000000000000000000001455526741000155425ustar00rootroot00000000000000overrides-7.7.0/overrides/__init__.py000066400000000000000000000005151455526741000176540ustar00rootroot00000000000000from overrides.enforce import EnforceOverrides import sys if sys.version_info < (3, 11): from overrides.final import final else: from typing import final from overrides.overrides import __VERSION__, overrides, override __all__ = [ "__VERSION__", "override", "overrides", "final", "EnforceOverrides", ] overrides-7.7.0/overrides/enforce.py000066400000000000000000000044551455526741000175450ustar00rootroot00000000000000from abc import ABCMeta class EnforceOverridesMeta(ABCMeta): def __new__(mcls, name, bases, namespace, **kwargs): # Ignore any methods defined on the metaclass when enforcing overrides. for method in dir(mcls): if not method.startswith("__") and method != "mro": value = getattr(mcls, method) if not isinstance(value, (bool, str, int, float, tuple, list, dict)): setattr(getattr(mcls, method), "__ignored__", True) cls = super().__new__(mcls, name, bases, namespace, **kwargs) for name, value in namespace.items(): mcls._check_if_overrides_final_method(name, bases) if not name.startswith("__"): value = mcls._handle_special_value(value) mcls._check_if_overrides_without_overrides_decorator(name, value, bases) return cls @staticmethod def _check_if_overrides_without_overrides_decorator(name, value, bases): is_override = getattr(value, "__override__", False) for base in bases: base_class_method = getattr(base, name, False) if ( not base_class_method or not callable(base_class_method) or getattr(base_class_method, "__ignored__", False) ): continue if not is_override: raise TypeError( f"Method {name} overrides method from {base} but does not have @override decorator" ) @staticmethod def _check_if_overrides_final_method(name, bases): for base in bases: base_class_method = getattr(base, name, False) # `__final__` is added by `@final` decorator if getattr(base_class_method, "__final__", False): raise TypeError( f"Method {name} is finalized in {base}, it cannot be overridden" ) @staticmethod def _handle_special_value(value): if isinstance(value, classmethod) or isinstance(value, staticmethod): value = value.__get__(None, dict) elif isinstance(value, property): value = value.fget return value class EnforceOverrides(metaclass=EnforceOverridesMeta): "Use this as the parent class for your custom classes" pass overrides-7.7.0/overrides/final.py000066400000000000000000000027471455526741000172170ustar00rootroot00000000000000# # Copyright 2016 Keunhong Lee # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # from types import FunctionType from typing import Callable, TypeVar, Union _WrappedMethod = TypeVar("_WrappedMethod", bound=Union[FunctionType, Callable]) def final(method: _WrappedMethod) -> _WrappedMethod: """Decorator to indicate that the decorated method is finalized and cannot be overridden. The decorator code is executed while loading class. Using this method should have minimal runtime performance implications. Currently, only methods with @override are checked. How to use: from overrides import final class SuperClass(object): @final def method(self): return 2 class SubClass(SuperClass): @override def method(self): #causes an error return 1 :raises AssertionError: if there exists a match in sub classes for the method name :return: method """ setattr(method, "__final__", True) return method overrides-7.7.0/overrides/overrides.py000066400000000000000000000165211455526741000201230ustar00rootroot00000000000000# # Copyright 2019 Mikko Korpela # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import dis import functools import inspect import sys from types import FrameType, FunctionType from typing import Callable, List, Optional, Tuple, TypeVar, Union, overload __VERSION__ = "7.7.0" from overrides.signature import ensure_signature_is_compatible _WrappedMethod = TypeVar("_WrappedMethod", bound=Union[FunctionType, Callable]) _DecoratorMethod = Callable[[_WrappedMethod], _WrappedMethod] @overload def overrides( method: None = None, *, check_signature: bool = True, check_at_runtime: bool = False, ) -> _DecoratorMethod: ... @overload def overrides( method: _WrappedMethod, *, check_signature: bool = True, check_at_runtime: bool = False, ) -> _WrappedMethod: ... def overrides( method: Optional[_WrappedMethod] = None, *, check_signature: bool = True, check_at_runtime: bool = False, ) -> Union[_DecoratorMethod, _WrappedMethod]: """Decorator to indicate that the decorated method overrides a method in superclass. The decorator code is executed while loading class. Using this method should have minimal runtime performance implications. How to use: from overrides import overrides class SuperClass(object): def method(self): return 2 class SubClass(SuperClass): @overrides def method(self): return 1 :param check_signature: Whether or not to check the signature of the overridden method. :param check_at_runtime: Whether or not to check the overridden method at runtime. :raises AssertionError: if no match in super classes for the method name :return: method with possibly added (if the method doesn't have one) docstring from super class """ if method is not None: return _overrides(method, check_signature, check_at_runtime) else: return functools.partial( overrides, check_signature=check_signature, check_at_runtime=check_at_runtime, ) @overload def override( method: None = None, *, check_signature: bool = True, check_at_runtime: bool = False, ) -> _DecoratorMethod: ... @overload def override( method: _WrappedMethod, *, check_signature: bool = True, check_at_runtime: bool = False, ) -> _WrappedMethod: ... def override( method: Optional[_WrappedMethod] = None, *, check_signature: bool = True, check_at_runtime: bool = False, ) -> Union[_DecoratorMethod, _WrappedMethod]: """Decorator to indicate that the decorated method overrides a method in superclass. The decorator code is executed while loading class. Using this method should have minimal runtime performance implications. How to use: from overrides import override class SuperClass(object): def method(self): return 2 class SubClass(SuperClass): @override def method(self): return 1 :param check_signature: Whether or not to check the signature of the overridden method. :param check_at_runtime: Whether or not to check the overridden method at runtime. :raises AssertionError: if no match in super classes for the method name :return: method with possibly added (if the method doesn't have one) docstring from super class """ if method is not None: return _overrides(method, check_signature, check_at_runtime) else: return functools.partial( overrides, check_signature=check_signature, check_at_runtime=check_at_runtime, ) def _overrides( method: _WrappedMethod, check_signature: bool, check_at_runtime: bool, ) -> _WrappedMethod: setattr(method, "__override__", True) global_vars = getattr(method, "__globals__", None) if global_vars is None: global_vars = vars(sys.modules[method.__module__]) for super_class in _get_base_classes(sys._getframe(3), global_vars): if hasattr(super_class, method.__name__): if check_at_runtime: @functools.wraps(method) def wrapper(*args, **kwargs): _validate_method(method, super_class, check_signature) return method(*args, **kwargs) return wrapper # type: ignore else: _validate_method(method, super_class, check_signature) return method raise TypeError(f"{method.__qualname__}: No super class method found") def _validate_method(method, super_class, check_signature): super_method = getattr(super_class, method.__name__) is_static = isinstance( inspect.getattr_static(super_class, method.__name__), staticmethod ) if getattr(super_method, "__final__", False): raise TypeError(f"{method.__name__}: is finalized in {super_class}") if not method.__doc__: method.__doc__ = super_method.__doc__ if ( check_signature and not method.__name__.startswith("__") and not isinstance(super_method, property) ): ensure_signature_is_compatible(super_method, method, is_static) def _get_base_classes(frame, namespace): return [ _get_base_class(class_name_components, namespace) for class_name_components in _get_base_class_names(frame) ] def _get_base_class_names(frame: FrameType) -> List[List[str]]: """Get baseclass names from the code object""" current_item: List[str] = [] items: List[List[str]] = [] add_last_step = True for instruction in dis.get_instructions(frame.f_code): if instruction.offset > frame.f_lasti: break if instruction.opcode not in dis.hasname: continue if not add_last_step: items = [] add_last_step = True # Combine LOAD_NAME and LOAD_GLOBAL as they have similar functionality if instruction.opname in ["LOAD_NAME", "LOAD_GLOBAL"]: if current_item: items.append(current_item) current_item = [instruction.argval] elif instruction.opname == "LOAD_ATTR" and current_item: current_item.append(instruction.argval) # Reset on other instructions else: if current_item: items.append(current_item) current_item = [] add_last_step = False if current_item: items.append(current_item) return items def _get_base_class(components, namespace): try: obj = namespace[components[0]] except KeyError: if isinstance(namespace["__builtins__"], dict): obj = namespace["__builtins__"][components[0]] else: obj = getattr(namespace["__builtins__"], components[0]) for component in components[1:]: if hasattr(obj, component): obj = getattr(obj, component) return obj overrides-7.7.0/overrides/py.typed000066400000000000000000000000001455526741000172270ustar00rootroot00000000000000overrides-7.7.0/overrides/signature.py000066400000000000000000000270111455526741000201160ustar00rootroot00000000000000import inspect from inspect import Parameter from types import FunctionType from typing import Callable, Dict, Optional, Tuple, Type, TypeVar, Union, get_type_hints from .typing_utils import get_args, issubtype _WrappedMethod = TypeVar("_WrappedMethod", bound=Union[FunctionType, Callable]) _WrappedMethod2 = TypeVar("_WrappedMethod2", bound=Union[FunctionType, Callable]) def _contains_unbound_typevar(t: Type) -> bool: """Recursively check if `t` or any types contained by `t` is a `TypeVar`. Examples where we return `True`: `T`, `Optional[T]`, `Tuple[Optional[T], ...]`, ... Examples where we return `False`: `int`, `Optional[str]`, ... :param t: Type to evaluate. :return: `True` if the input type contains an unbound `TypeVar`, `False` otherwise. """ # Check self if isinstance(t, TypeVar): return True # Check children for arg in get_args(t): if _contains_unbound_typevar(arg): return True return False def _issubtype(left, right): if _contains_unbound_typevar(left): return True if right is None: return True if _contains_unbound_typevar(right): return True try: return issubtype(left, right) except TypeError: # Ignore all broken cases return True def _get_type_hints(callable) -> Optional[Dict]: try: return get_type_hints(callable) except (NameError, TypeError): return None def _is_same_module(callable1: _WrappedMethod, callable2: _WrappedMethod2) -> bool: mod1 = callable1.__module__.split(".")[0] # "__module__" attribute may be missing in CPython or it can be None # in PyPy: https://github.com/mkorpela/overrides/issues/118 mod2 = getattr(callable2, "__module__", None) if mod2 is None: return False mod2 = mod2.split(".")[0] return mod1 == mod2 def ensure_signature_is_compatible( super_callable: _WrappedMethod, sub_callable: _WrappedMethod2, is_static: bool = False, ) -> None: """Ensure that the signature of `sub_callable` is compatible with the signature of `super_callable`. Guarantees that any call to `super_callable` will work on `sub_callable` by checking the following criteria: 1. The return type of `sub_callable` is a subtype of the return type of `super_callable`. 2. All parameters of `super_callable` are present in `sub_callable`, unless `sub_callable` declares `*args` or `**kwargs`. 3. All positional parameters of `super_callable` appear in the same order in `sub_callable`. 4. All parameters of `super_callable` are a subtype of the corresponding parameters of `sub_callable`. 5. All required parameters of `sub_callable` are present in `super_callable`, unless `super_callable` declares `*args` or `**kwargs`. :param super_callable: Function to check compatibility with. :param sub_callable: Function to check compatibility of. :param is_static: True if staticmethod and should check first argument. """ super_callable = _unbound_func(super_callable) sub_callable = _unbound_func(sub_callable) try: super_sig = inspect.signature(super_callable) except ValueError: return super_type_hints = _get_type_hints(super_callable) sub_sig = inspect.signature(sub_callable) sub_type_hints = _get_type_hints(sub_callable) method_name = sub_callable.__qualname__ same_main_module = _is_same_module(sub_callable, super_callable) if super_type_hints is not None and sub_type_hints is not None: ensure_return_type_compatibility(super_type_hints, sub_type_hints, method_name) ensure_all_kwargs_defined_in_sub( super_sig, sub_sig, super_type_hints, sub_type_hints, is_static, method_name ) ensure_all_positional_args_defined_in_sub( super_sig, sub_sig, super_type_hints, sub_type_hints, is_static, same_main_module, method_name, ) ensure_no_extra_args_in_sub(super_sig, sub_sig, is_static, method_name) def _unbound_func(callable: _WrappedMethod) -> _WrappedMethod: if hasattr(callable, "__self__") and hasattr(callable, "__func__"): return callable.__func__ # type: ignore return callable def ensure_all_kwargs_defined_in_sub( super_sig: inspect.Signature, sub_sig: inspect.Signature, super_type_hints: Dict, sub_type_hints: Dict, check_first_parameter: bool, method_name: str, ): sub_has_var_kwargs = any( p.kind == Parameter.VAR_KEYWORD for p in sub_sig.parameters.values() ) for super_index, (name, super_param) in enumerate(super_sig.parameters.items()): if super_index == 0 and not check_first_parameter: continue if super_param.kind == Parameter.VAR_POSITIONAL: continue if super_param.kind == Parameter.POSITIONAL_ONLY: continue if not is_param_defined_in_sub( name, True, sub_has_var_kwargs, sub_sig, super_param ): raise TypeError(f"{method_name}: `{name}` is not present.") elif name in sub_sig.parameters and super_param.kind != Parameter.VAR_KEYWORD: sub_index = list(sub_sig.parameters.keys()).index(name) sub_param = sub_sig.parameters[name] if super_param.kind != sub_param.kind and not ( super_param.kind == Parameter.KEYWORD_ONLY and sub_param.kind == Parameter.POSITIONAL_OR_KEYWORD ): raise TypeError(f"{method_name}: `{name}` is not `{super_param.kind}`") elif super_index > sub_index and super_param.kind != Parameter.KEYWORD_ONLY: raise TypeError( f"{method_name}: `{name}` is not parameter at index `{super_index}`" ) elif ( name in super_type_hints and name in sub_type_hints and not _issubtype(super_type_hints[name], sub_type_hints[name]) ): raise TypeError( f"`{method_name}: {name} must be a supertype of `{super_param.annotation}` but is `{sub_param.annotation}`" ) def ensure_all_positional_args_defined_in_sub( super_sig: inspect.Signature, sub_sig: inspect.Signature, super_type_hints: Dict, sub_type_hints: Dict, check_first_parameter: bool, is_same_main_module: bool, method_name: str, ): sub_parameter_values = [ v for v in sub_sig.parameters.values() if v.kind not in (Parameter.KEYWORD_ONLY, Parameter.VAR_KEYWORD) ] super_parameter_values = [ v for v in super_sig.parameters.values() if v.kind not in (Parameter.KEYWORD_ONLY, Parameter.VAR_KEYWORD) ] sub_has_var_args = any( p.kind == Parameter.VAR_POSITIONAL for p in sub_parameter_values ) super_has_var_args = any( p.kind == Parameter.VAR_POSITIONAL for p in super_parameter_values ) if not sub_has_var_args and len(sub_parameter_values) < len(super_parameter_values): raise TypeError(f"{method_name}: parameter list too short") super_shift = 0 for index, sub_param in enumerate(sub_parameter_values): if index == 0 and not check_first_parameter: continue if index + super_shift >= len(super_parameter_values): if sub_param.kind == Parameter.VAR_POSITIONAL: continue if ( sub_param.kind == Parameter.POSITIONAL_ONLY and sub_param.default != Parameter.empty ): continue if sub_param.kind == Parameter.POSITIONAL_OR_KEYWORD: continue # Assume use as keyword raise TypeError( f"{method_name}: `{sub_param.name}` positionally required in subclass but not in supertype" ) if sub_param.kind == Parameter.VAR_POSITIONAL: return super_param = super_parameter_values[index + super_shift] if super_param.kind == Parameter.VAR_POSITIONAL: super_shift -= 1 if super_param.kind == Parameter.VAR_POSITIONAL: if not sub_has_var_args: raise TypeError(f"{method_name}: `{super_param.name}` must be present") continue if ( super_param.kind != sub_param.kind and not ( super_param.kind == Parameter.POSITIONAL_ONLY and sub_param.kind == Parameter.POSITIONAL_OR_KEYWORD ) and not (sub_param.kind == Parameter.POSITIONAL_ONLY and super_has_var_args) ): raise TypeError( f"{method_name}: `{sub_param.name}` is not `{super_param.kind}` and is `{sub_param.kind}`" ) elif ( super_param.name in super_type_hints or is_same_main_module ) and not _issubtype( super_type_hints.get(super_param.name, None), sub_type_hints.get(sub_param.name, None), ): raise TypeError( f"`{method_name}: {sub_param.name} overriding must be a supertype of `{super_param.annotation}` but is `{sub_param.annotation}`" ) def is_param_defined_in_sub( name: str, sub_has_var_args: bool, sub_has_var_kwargs: bool, sub_sig: inspect.Signature, super_param: inspect.Parameter, ) -> bool: return ( name in sub_sig.parameters or (super_param.kind == Parameter.VAR_POSITIONAL and sub_has_var_args) or (super_param.kind == Parameter.VAR_KEYWORD and sub_has_var_kwargs) or (super_param.kind == Parameter.POSITIONAL_ONLY and sub_has_var_args) or ( super_param.kind == Parameter.POSITIONAL_OR_KEYWORD and sub_has_var_args and sub_has_var_kwargs ) or (super_param.kind == Parameter.KEYWORD_ONLY and sub_has_var_kwargs) ) def ensure_no_extra_args_in_sub( super_sig: inspect.Signature, sub_sig: inspect.Signature, check_first_parameter: bool, method_name: str, ) -> None: super_params = super_sig.parameters.values() super_var_args = any(p.kind == Parameter.VAR_POSITIONAL for p in super_params) super_var_kwargs = any(p.kind == Parameter.VAR_KEYWORD for p in super_params) for sub_index, (name, sub_param) in enumerate(sub_sig.parameters.items()): if ( sub_param.kind == Parameter.POSITIONAL_ONLY and len(super_params) > sub_index and list(super_params)[sub_index].kind == Parameter.POSITIONAL_ONLY ): continue if ( name not in super_sig.parameters and sub_param.default == Parameter.empty and sub_param.kind != Parameter.VAR_POSITIONAL and sub_param.kind != Parameter.VAR_KEYWORD and not (sub_param.kind == Parameter.KEYWORD_ONLY and super_var_kwargs) and not (sub_param.kind == Parameter.POSITIONAL_ONLY and super_var_args) and not ( sub_param.kind == Parameter.POSITIONAL_OR_KEYWORD and super_var_args ) and (sub_index > 0 or check_first_parameter) ): raise TypeError(f"{method_name}: `{name}` is not a valid parameter.") def ensure_return_type_compatibility( super_type_hints: Dict, sub_type_hints: Dict, method_name: str ): super_return = super_type_hints.get("return", None) sub_return = sub_type_hints.get("return", None) if not _issubtype(sub_return, super_return) and super_return is not None: raise TypeError( f"{method_name}: return type `{sub_return}` is not a `{super_return}`." ) overrides-7.7.0/overrides/typing_utils.py000066400000000000000000000336401455526741000206540ustar00rootroot00000000000000""" Backport Python3.8+ typing utils & issubtype & more ![Python 3.6](https://github.com/bojiang/typing_utils/workflows/Python%203.6/badge.svg) ![Python 3.7](https://github.com/bojiang/typing_utils/workflows/Python%203.7/badge.svg) ![Python 3.8](https://github.com/bojiang/typing_utils/workflows/Python%203.8/badge.svg) ## Install ``` bash pip install typing_utils ``` """ import collections.abc import io import itertools import types import typing if hasattr(typing, "ForwardRef"): # python3.8 ForwardRef = getattr(typing, "ForwardRef") elif hasattr(typing, "_ForwardRef"): # python3.6 ForwardRef = getattr(typing, "_ForwardRef") else: raise NotImplementedError() if hasattr(typing, "Literal"): Literal = getattr(typing, "Literal") else: Literal = None if hasattr(typing, "_TypedDictMeta"): _TypedDictMeta = getattr(typing, "_TypedDictMeta") else: _TypedDictMeta = None if hasattr(types, "UnionType"): UnionType = getattr(types, "UnionType") else: UnionType = None unknown = None BUILTINS_MAPPING = { typing.List: list, typing.Set: set, typing.Dict: dict, typing.Tuple: tuple, typing.ByteString: bytes, # https://docs.python.org/3/library/typing.html#typing.ByteString typing.Callable: collections.abc.Callable, typing.Sequence: collections.abc.Sequence, type(None): None, } STATIC_SUBTYPE_MAPPING: typing.Dict[type, typing.Type] = { io.TextIOWrapper: typing.TextIO, io.TextIOBase: typing.TextIO, io.StringIO: typing.TextIO, io.BufferedReader: typing.BinaryIO, io.BufferedWriter: typing.BinaryIO, io.BytesIO: typing.BinaryIO, } if UnionType: def is_union(element: object) -> bool: return element is typing.Union or element is UnionType else: def is_union(element: object) -> bool: return element is typing.Union def optional_all(elements) -> typing.Optional[bool]: if all(elements): return True if all(e is False for e in elements): return False return unknown def optional_any(elements) -> typing.Optional[bool]: if any(elements): return True if any(e is None for e in elements): return unknown return False def _hashable(value): """Determine whether `value` can be hashed.""" try: hash(value) except TypeError: return False return True get_type_hints = typing.get_type_hints GenericClass = type(typing.List) UnionClass = type(typing.Union) Type = typing.Union[None, type, "typing.TypeVar"] OriginType = typing.Union[None, type] TypeArgs = typing.Union[type, typing.AbstractSet[type], typing.Sequence[type]] def _normalize_aliases(type_: Type) -> Type: if isinstance(type_, typing.TypeVar): return type_ assert _hashable(type_), "_normalize_aliases should only be called on element types" if type_ in BUILTINS_MAPPING: return BUILTINS_MAPPING[type_] # type: ignore return type_ def get_origin(type_): """Get the unsubscripted version of a type. This supports generic types, Callable, Tuple, Union, Literal, Final and ClassVar. Return None for unsupported types. Examples: ```python from typing_utils import get_origin get_origin(Literal[42]) is Literal get_origin(int) is None get_origin(ClassVar[int]) is ClassVar get_origin(Generic) is Generic get_origin(Generic[T]) is Generic get_origin(Union[T, int]) is Union get_origin(List[Tuple[T, T]][int]) == list ``` """ if hasattr(typing, "get_origin"): # python 3.8+ _getter = getattr(typing, "get_origin") ori = _getter(type_) elif hasattr(typing.List, "_special"): # python 3.7 if isinstance(type_, GenericClass) and not type_._special: ori = type_.__origin__ elif hasattr(type_, "_special") and type_._special: ori = type_ elif type_ is typing.Generic: ori = typing.Generic else: ori = None else: # python 3.6 if isinstance(type_, GenericClass): ori = type_.__origin__ if ori is None: ori = type_ elif isinstance(type_, UnionClass): ori = type_.__origin__ elif type_ is typing.Generic: ori = typing.Generic else: ori = None if ori is None and _TypedDictMeta and isinstance(type_, _TypedDictMeta): ori = dict return _normalize_aliases(ori) def get_args(type_) -> typing.Tuple: """Get type arguments with all substitutions performed. For unions, basic simplifications used by Union constructor are performed. Examples: ```python from typing_utils import get_args get_args(Dict[str, int]) == (str, int) get_args(int) == () get_args(Union[int, Union[T, int], str][int]) == (int, str) get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) get_args(Callable[[], T][int]) == ([], int) ``` """ if hasattr(typing, "get_args"): # python 3.8+ _getter = getattr(typing, "get_args") res = _getter(type_) elif hasattr(typing.List, "_special"): # python 3.7 if ( isinstance(type_, GenericClass) and not type_._special # type: ignore ): # backport for python 3.8 res = type_.__args__ # type: ignore if get_origin(type_) is collections.abc.Callable and res[0] is not Ellipsis: res = (list(res[:-1]), res[-1]) else: res = () else: # python 3.6 if isinstance(type_, (GenericClass, UnionClass)): # backport for python 3.8 res = type_.__args__ # type: ignore if get_origin(type_) is collections.abc.Callable and res[0] is not Ellipsis: res = (list(res[:-1]), res[-1]) else: res = () if _TypedDictMeta and isinstance(type_, _TypedDictMeta): return str, typing.Any return () if res is None else res def eval_forward_ref(ref, forward_refs=None): """ eval forward_refs in all cPython versions """ localns = forward_refs or {} if hasattr(typing, "_eval_type"): # python3.8 & python 3.9 _eval_type = getattr(typing, "_eval_type") return _eval_type(ref, globals(), localns) if hasattr(ref, "_eval_type"): # python3.6 _eval_type = getattr(ref, "_eval_type") return _eval_type(globals(), localns) raise NotImplementedError() class NormalizedType(typing.NamedTuple): """ Normalized type, made it possible to compare, hash between types. """ origin: Type args: typing.Union[tuple, frozenset] = tuple() def __eq__(self, other): if isinstance(other, NormalizedType): if self.origin != other.origin: return False if isinstance(self.args, frozenset) and isinstance(other.args, frozenset): return self.args <= other.args and other.args <= self.args return self.origin == other.origin and self.args == other.args if not self.args: return self.origin == other return False def __hash__(self) -> int: if not self.args: return hash(self.origin) return hash((self.origin, self.args)) def __repr__(self): if not self.args: return f"{self.origin}" return f"{self.origin}[{self.args}])" def _normalize_args(tps: TypeArgs): if isinstance(tps, str): return tps if isinstance(tps, collections.abc.Sequence): return tuple(_normalize_args(type_) for type_ in tps) if isinstance(tps, collections.abc.Set): return frozenset(_normalize_args(type_) for type_ in tps) return normalize(tps) def normalize(type_: Type) -> NormalizedType: """ convert types to NormalizedType instances. """ args = get_args(type_) origin = get_origin(type_) if not origin: return NormalizedType(_normalize_aliases(type_)) origin = _normalize_aliases(origin) if is_union(origin): # sort args when the origin is Union args = _normalize_args(frozenset(args)) else: args = _normalize_args(args) return NormalizedType(origin, args) def _is_origin_subtype(left: OriginType, right: OriginType) -> bool: if left is right: return True if ( left is not None and left in STATIC_SUBTYPE_MAPPING and right == STATIC_SUBTYPE_MAPPING[left] ): return True if hasattr(left, "mro"): for parent in left.mro(): # type: ignore if parent == right: return True if isinstance(left, type) and isinstance(right, type): return issubclass(left, right) return left == right NormalizedTypeArgs = typing.Union[ typing.Tuple[typing.Any, ...], typing.FrozenSet[NormalizedType], NormalizedType, ] def _is_origin_subtype_args( left: "NormalizedTypeArgs", right: "NormalizedTypeArgs", forward_refs: typing.Optional[typing.Mapping[str, type]], ) -> typing.Optional[bool]: if isinstance(left, frozenset): if not isinstance(right, frozenset): return False excluded = left - right if not excluded: # Union[str, int] <> Union[int, str] return True # Union[list, int] <> Union[typing.Sequence, int] return all( any(_is_normal_subtype(e, r, forward_refs) for r in right) for e in excluded ) if isinstance(left, collections.abc.Sequence) and not isinstance( left, NormalizedType ): if not isinstance(right, collections.abc.Sequence) or isinstance( right, NormalizedType ): return False if ( left and left[-1].origin is not Ellipsis and right and right[-1].origin is Ellipsis ): # Tuple[type, type] <> Tuple[type, ...] return all(_is_origin_subtype_args(l, right[0], forward_refs) for l in left) if len(left) != len(right): return False return all( l is not None and r is not None and _is_origin_subtype_args(l, r, forward_refs) for l, r in itertools.zip_longest(left, right) ) assert isinstance(left, NormalizedType) assert isinstance(right, NormalizedType) return _is_normal_subtype(left, right, forward_refs) def _is_normal_subtype( left: NormalizedType, right: NormalizedType, forward_refs: typing.Optional[typing.Mapping[str, type]], ) -> typing.Optional[bool]: if isinstance(left.origin, ForwardRef): left = normalize(eval_forward_ref(left.origin, forward_refs=forward_refs)) if isinstance(right.origin, ForwardRef): right = normalize(eval_forward_ref(right.origin, forward_refs=forward_refs)) # Any if right.origin is typing.Any: return True # Union if is_union(right.origin) and is_union(left.origin): return _is_origin_subtype_args(left.args, right.args, forward_refs) if is_union(right.origin): return optional_any( _is_normal_subtype(left, a, forward_refs) for a in right.args ) if is_union(left.origin): return optional_all( _is_normal_subtype(a, right, forward_refs) for a in left.args ) # Literal if right.origin is Literal: if left.origin is not Literal: return False return set(left.args).issubset(set(right.args)) # TypeVar if isinstance(left.origin, typing.TypeVar) and isinstance( right.origin, typing.TypeVar ): if left.origin is right.origin: return True left_bound = getattr(left.origin, "__bound__", None) right_bound = getattr(right.origin, "__bound__", None) if right_bound is None or left_bound is None: return unknown return _is_normal_subtype( normalize(left_bound), normalize(right_bound), forward_refs ) if isinstance(right.origin, typing.TypeVar): return unknown if isinstance(left.origin, typing.TypeVar): left_bound = getattr(left.origin, "__bound__", None) if left_bound is None: return unknown return _is_normal_subtype(normalize(left_bound), right, forward_refs) if not left.args and not right.args: return _is_origin_subtype(left.origin, right.origin) if not right.args: return _is_origin_subtype(left.origin, right.origin) if _is_origin_subtype(left.origin, right.origin): return _is_origin_subtype_args(left.args, right.args, forward_refs) return False def issubtype( left: Type, right: Type, forward_refs: typing.Optional[dict] = None, ) -> typing.Optional[bool]: """Check that the left argument is a subtype of the right. For unions, check if the type arguments of the left is a subset of the right. Also works for nested types including ForwardRefs. Examples: ```python from typing_utils import issubtype issubtype(typing.List, typing.Any) == True issubtype(list, list) == True issubtype(list, typing.List) == True issubtype(list, typing.Sequence) == True issubtype(typing.List[int], list) == True issubtype(typing.List[typing.List], list) == True issubtype(list, typing.List[int]) == False issubtype(list, typing.Union[typing.Tuple, typing.Set]) == False issubtype(typing.List[typing.List], typing.List[typing.Sequence]) == True JSON = typing.Union[ int, float, bool, str, None, typing.Sequence["JSON"], typing.Mapping[str, "JSON"] ] issubtype(str, JSON, forward_refs={'JSON': JSON}) == True issubtype(typing.Dict[str, str], JSON, forward_refs={'JSON': JSON}) == True issubtype(typing.Dict[str, bytes], JSON, forward_refs={'JSON': JSON}) == False ``` """ return _is_normal_subtype(normalize(left), normalize(right), forward_refs) __all__ = [ "issubtype", "get_origin", "get_args", "get_type_hints", ] overrides-7.7.0/release.sh000077500000000000000000000001051455526741000155130ustar00rootroot00000000000000python setup.py sdist bdist_wheel twine upload -r overrides dist/*.* overrides-7.7.0/requirements-dev.txt000066400000000000000000000001341455526741000175760ustar00rootroot00000000000000black>=20.8b1 flake8>=3.9.1 isort>=5.8.0 mypy>=0.812 pytest>=6.2.3 pip>=21.0.1 wheel>=0.36.2overrides-7.7.0/setup.py000066400000000000000000000023631455526741000152560ustar00rootroot00000000000000#!/usr/bin/env python from distutils.core import setup from setuptools import find_packages # type: ignore from os.path import abspath, join, dirname name = "Mikko Korpela" # I might be just a bit too much afraid of those bots. address = name.lower().replace(" ", ".") + chr(64) + "gmail.com" desc = "A decorator to automatically detect mismatch when overriding a method." CURDIR = dirname(abspath(__file__)) with open(join(CURDIR, "README.rst")) as f: LONG_DESCRIPTION = f.read() setup( name="overrides", version="7.7.0", description=desc, long_description=LONG_DESCRIPTION, author=name, author_email=address, url="https://github.com/mkorpela/overrides", packages=find_packages(), package_data={ "overrides": ["*.pyi", "py.typed"], }, include_package_data=True, install_requires=['typing;python_version<"3.5"'], python_requires=">=3.6", license="Apache License, Version 2.0", keywords=["override", "inheritence", "OOP"], classifiers=[ "Intended Audience :: Developers", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", ], ) overrides-7.7.0/tests/000077500000000000000000000000001455526741000147025ustar00rootroot00000000000000overrides-7.7.0/tests/test_decorator_params.py000066400000000000000000000010521455526741000216360ustar00rootroot00000000000000from typing import Callable, Type from overrides import override class SuperClass: def method(self) -> int: return 2 def my_decorator(name: str) -> Callable: def func(cls: Type) -> Type: return cls return func class MyClass: def __init__(self, name: str): self.my_name: str = name def test_my_func() -> None: my_object = MyClass("Name accessed in decorator") @my_decorator(my_object.my_name) class SubClass(SuperClass): @override def method(self) -> int: return 1 overrides-7.7.0/tests/test_enforce__py38.py000066400000000000000000000310561455526741000207630ustar00rootroot00000000000000import unittest from typing import Optional, TypeVar, Union from overrides import EnforceOverrides, final, override from overrides.signature import ensure_signature_is_compatible class Enforcing(EnforceOverrides): classVariableIsOk = "OK?" @final def finality(self): return "final" @final def __and__(self, other): return True def nonfinal1(self, param: int) -> str: return "super1" def nonfinal2(self): return "super2" @property def nonfinal_property(self): return "super_property" @staticmethod def nonfinal_staticmethod(): return "super_staticmethod" @classmethod def nonfinal_classmethod(cls): return "super_classmethod" class EnforceTests(unittest.TestCase): def test_enforcing_when_all_ok(self): class Subclazz(Enforcing): classVariableIsOk = "OK!" @override def nonfinal1(self, param: int) -> str: return "2" sc = Subclazz() self.assertEqual(sc.finality(), "final") self.assertEqual(sc.nonfinal1(1), "2") self.assertEqual(sc.nonfinal2(), "super2") self.assertEqual(sc.classVariableIsOk, "OK!") def test_enforcing_when_finality_broken(self): with self.assertRaises(TypeError): class BrokesFinality(Enforcing): def finality(self): return "NEVER HERE" def test_trying_to_override_final_magic_method(self): with self.assertRaises(TypeError): class FinalMagicOverrides(Enforcing): def __and__(self, other): return False def test_enforcing_when_none_explicit_override(self): with self.assertRaises(TypeError): class Overrider(Enforcing): def nonfinal2(self): return "NEVER HERE EITHER" def test_enforcing_when_property_overriden(self): class PropertyOverrider(Enforcing): @property @override def nonfinal_property(self): return "subclass_property" self.assertNotEqual( PropertyOverrider.nonfinal_property, Enforcing.nonfinal_property ) def test_enforcing_when_incompatible(self): with self.assertRaises(TypeError): class Incompatible(Enforcing): @override def nonfinal1(self, param: str): pass def test_enforcing_when_staticmethod_overriden(self): class StaticMethodOverrider(Enforcing): @staticmethod @override def nonfinal_staticmethod(): return "subclass_staticmethod" self.assertNotEqual( StaticMethodOverrider.nonfinal_staticmethod(), Enforcing.nonfinal_staticmethod(), ) def test_enforcing_when_classmethod_overriden(self): class ClassMethodOverrider(Enforcing): @classmethod @override def nonfinal_classmethod(cls): return "subclass_classmethod" self.assertNotEqual( ClassMethodOverrider.nonfinal_classmethod(), Enforcing.nonfinal_classmethod(), ) def test_enforcing_when_metaclass_method_overridden(self): class MetaClassMethodOverrider(Enforcing): def register(self): pass with self.assertRaises(TypeError): class SubClass(MetaClassMethodOverrider): def register(self): pass def test_ensure_compatible_when_compatible(self): def sup(self, a, /, b: str, c: int, *, d, e, **kwargs) -> object: pass def sub( self, a, b: object, c, d, f: str = "foo", *args, g: str = "bar", e, **kwargs ) -> str: pass ensure_signature_is_compatible(sup, sub) def test_ensure_compatible_when_type_hints_are_strings(self): def sup(self, x: "str") -> "object": pass def sub(self, x: "object") -> "str": pass ensure_signature_is_compatible(sup, sub) def test_ensure_compatible_when_return_types_are_incompatible(self): def sup(self, x) -> int: pass def sub(self, x) -> str: pass with self.assertRaises(TypeError): ensure_signature_is_compatible(sup, sub) def test_ensure_compatible_when_parameter_positions_are_incompatible(self): def sup(self, x, y): pass def sub(self, y, x): pass def sub2(self, z, x): pass def sub3(self, y, z): pass with self.assertRaises(TypeError): ensure_signature_is_compatible(sup, sub) with self.assertRaises(TypeError): ensure_signature_is_compatible(sup, sub2) with self.assertRaises(TypeError): ensure_signature_is_compatible(sup, sub3) def test_ensure_compatible_when_parameter_types_are_incompatible(self): def sup(self, x: object): pass def sub(self, y: str): pass with self.assertRaises(TypeError): ensure_signature_is_compatible(sup, sub) def test_ensure_compatible_when_parameter_kinds_are_incompatible(self): def sup(self, x): pass def sub(self, *, x): pass with self.assertRaises(TypeError): ensure_signature_is_compatible(sup, sub) def test_ensure_compatible_when_parameter_lists_are_incompatible(self): def sup(self, x): pass def sub(self, x, y): pass with self.assertRaises(TypeError): ensure_signature_is_compatible(sup, sub) def test_ensure_compatible_when_missing_arg(self): def sup(self): pass def sub(self, x): pass with self.assertRaises(TypeError): ensure_signature_is_compatible(sup, sub) def test_ensure_compatible_when_additional_arg(self): def sup(self, x): pass def sub(self): pass with self.assertRaises(TypeError): ensure_signature_is_compatible(sup, sub) def test_union_compatible(self): def sup(self, x: int): pass def sub(self, x: Union[int, str]): pass ensure_signature_is_compatible(sup, sub) with self.assertRaises(TypeError): ensure_signature_is_compatible(sub, sup) def test_generic_sub(self): def better_typed_method(self, x: int, y: Optional[str], z: float = 3.0): pass def generic_method(self, *args, **kwargs): pass ensure_signature_is_compatible(better_typed_method, generic_method) with self.assertRaises(TypeError): ensure_signature_is_compatible(generic_method, better_typed_method) def test_if_super_has_args_then_sub_must_have(self): def sub1(self, x=2, y=3, z=4, /): pass def subbest(self, x=1, /, *burgs): pass def supah(self, *args): pass # supah() => subbest() # supah(2) => subbest(2) # supah(2,3) => subbest(2,3) # supah(*args) => subbest(*args) ensure_signature_is_compatible(supah, subbest) # sub1(1,2,3) => subbest(1,2,3) # sub1() => subbest() ensure_signature_is_compatible(sub1, subbest) with self.assertRaises(TypeError): # supah() => sub1() ok # supah(2) => sub1(2) ok # supah(1,2,3,4) => sub1() takes from 0 to 3 positional arguments but 4 were given ensure_signature_is_compatible(supah, sub1) with self.assertRaises(TypeError): # subbest() => sub1() ok # subbest(1,2,3) => sub1(1,2,3) ok # subbest(1,2,3,4) => sub1() takes from 0 to 3 positional arguments but 4 were given ensure_signature_is_compatible(subbest, sub1) def test_if_super_has_kwargs_then_sub_must_have(self): def sub1(self, *, x=3, y=3, z=4): pass def sus(self, *, x=3, **kwargs): pass def superb(self, **kwargs): pass # superb() => sus() # superb(foo=1) => sus(foo=1) # superb(x=4) => sus(x=4) # superb(x=4, foo=1) => sus(x=4, foo=1) # superb(**kwargs) => sus(**kwargs) ensure_signature_is_compatible(superb, sus) ensure_signature_is_compatible(sub1, sus) with self.assertRaises(TypeError): ensure_signature_is_compatible(superb, sub1) with self.assertRaises(TypeError): ensure_signature_is_compatible(sus, sub1) def test_allowed_extra_args_in_overrider(self): def superb(self): pass def optional_arg(self, arg=1): pass def optional_positional_arg(self, arg2=2, /): pass def optional_kw_only_arg(self, *, arg3=3): pass # superb() => optional_arg() ensure_signature_is_compatible(superb, optional_arg) # superb() => optional_positional_arg() ensure_signature_is_compatible(superb, optional_positional_arg) # superb() => optional_kw_only_arg() ensure_signature_is_compatible(superb, optional_kw_only_arg) def test_signatures_when_self_mismach(self): class A: def foo(self, x: int): pass class B: def bar(me, x: int): pass class C: def zoo(self, x: str): pass ensure_signature_is_compatible(A().foo, B().bar) with self.assertRaises(TypeError): ensure_signature_is_compatible(A().foo, C().zoo) ensure_signature_is_compatible(A.foo, B().bar) with self.assertRaises(TypeError): ensure_signature_is_compatible(A.foo, C().zoo) ensure_signature_is_compatible(A.foo, B.bar) with self.assertRaises(TypeError): ensure_signature_is_compatible(A.foo, C.zoo) ensure_signature_is_compatible(A().foo, B.bar) with self.assertRaises(TypeError): ensure_signature_is_compatible(A().foo, C.zoo) def test_signature_with_static_method(self): class A: @staticmethod def foo(x: int): pass class B: @staticmethod def bar(x: int): pass class C: @staticmethod def zoo(x: str): pass ensure_signature_is_compatible(A().foo, B().bar, True) with self.assertRaises(TypeError): ensure_signature_is_compatible(A().foo, C().zoo, True) ensure_signature_is_compatible(A.foo, B().bar, True) with self.assertRaises(TypeError): ensure_signature_is_compatible(A.foo, C().zoo, True) ensure_signature_is_compatible(A.foo, B.bar, True) with self.assertRaises(TypeError): ensure_signature_is_compatible(A.foo, C.zoo, True) ensure_signature_is_compatible(A().foo, B.bar, True) with self.assertRaises(TypeError): ensure_signature_is_compatible(A().foo, C.zoo, True) def test_typevar_in_signature(self): T = TypeVar("T") K = TypeVar("K") def typevarred(t: T) -> K: pass def typed(t: str) -> int: pass def untyped(t): pass def return_typed(t) -> int: pass ensure_signature_is_compatible(typevarred, untyped, True) ensure_signature_is_compatible(typevarred, typed, True) ensure_signature_is_compatible(untyped, typevarred, True) ensure_signature_is_compatible(typed, typevarred, True) ensure_signature_is_compatible(untyped, return_typed, True) with self.assertRaises(TypeError): ensure_signature_is_compatible(untyped, typed, True) with self.assertRaises(TypeError): ensure_signature_is_compatible(typed, untyped, True) def test_nested_typevar_in_signature(self): T = TypeVar("T") K = TypeVar("K") def typevarred(t: Optional[T]) -> Optional[K]: pass def typed(t: Optional[str]) -> Optional[int]: pass def untyped(t): pass def return_typed(t) -> Optional[int]: pass ensure_signature_is_compatible(typevarred, untyped, True) ensure_signature_is_compatible(typevarred, typed, True) ensure_signature_is_compatible(untyped, typevarred, True) ensure_signature_is_compatible(typed, typevarred, True) ensure_signature_is_compatible(untyped, return_typed, True) overrides-7.7.0/tests/test_final.py000066400000000000000000000101011455526741000173750ustar00rootroot00000000000000import unittest from overrides import override, final import test_somefinalpackage class SuperClass(object): def some_method(self): """Super Class Docs""" return "super" @final def some_finalized_method(self): return "super_final" @final class SomeFinalClass: pass class SubClass(SuperClass): @override def some_method(self): return "sub" @final def another_finalized(self): return "sub_final" class Sub2(test_somefinalpackage.SomeClass, SuperClass): @override def somewhat_fun_method(self): return "foo" @override def some_method(self): pass class FinalTests(unittest.TestCase): def test_final_passes_simple(self): sub = SubClass() self.assertEqual(sub.some_method(), "sub") self.assertEqual(sub.some_method.__doc__, "Super Class Docs") self.assertEqual(sub.some_finalized_method(), "super_final") def test_final_passes_for_superclass_in_another_package(self): sub2 = Sub2() self.assertEqual(sub2.somewhat_fun_method(), "foo") self.assertEqual(sub2.somewhat_fun_method.__doc__, "LULZ") self.assertEqual(sub2.some_finalized_method(), "super_final") self.assertEqual(sub2.somewhat_finalized_method(), "some_final") def test_final_fails_simple(self): try: class SubClassFail(SuperClass): @override def some_method(self): return "subfail" @override def some_finalized_method(self): pass raise RuntimeError("Should not go here") except TypeError: pass def test_final_fails_inner_class(self): try: class SubClassFail(SuperClass): @override class SomeFinalClass: pass raise RuntimeError("Should not go here") except TypeError: pass def test_final_fails_another_package(self): try: class Sub2Fail(test_somefinalpackage.SomeClass, SuperClass): @override def somewhat_fun_method(self): return "foo" @override def some_method(self): pass @override def some_finalized_method(self): pass raise RuntimeError("Should not go here") except TypeError: pass def test_final_fails_deep(self): try: class Sub3Fail(test_somefinalpackage.SomeClass, SubClass): @override def somewhat_fun_method(self): return "foo" @override def some_method(self): pass @override def some_finalized_method(self): pass raise RuntimeError("Should not go here") except TypeError: pass def test_final_fails_in_middle(self): try: class Sub4Fail(test_somefinalpackage.SomeClass, SubClass): @override def somewhat_fun_method(self): return "foo" @override def some_method(self): pass @override def another_finalized(self): pass raise RuntimeError("Should not go here") except TypeError: pass def test_final_fails_from_another_package(self): try: class Sub5Fail(test_somefinalpackage.SomeClass, SubClass): @override def somewhat_fun_method(self): return "foo" @override def some_method(self): pass @override def some_finalized_method(self): pass raise RuntimeError("Should not go here") except TypeError: pass if __name__ == "__main__": unittest.main() overrides-7.7.0/tests/test_issubtype_with_typeddict__py38.py000066400000000000000000000006161455526741000244730ustar00rootroot00000000000000from typing import Dict, Any, TypedDict from overrides.typing_utils import issubtype class MyTypedDict(TypedDict): foo: float bar: float class Typed2(TypedDict): zoo: float class Typed3(MyTypedDict, Typed2): pass def test_typeddict_and_dict(): assert issubtype(Typed3, Typed2) assert issubtype(Typed3, MyTypedDict) assert issubtype(MyTypedDict, Dict[str, Any]) overrides-7.7.0/tests/test_named_and_positional__py38.py000066400000000000000000000054221455526741000235070ustar00rootroot00000000000000from abc import abstractmethod, ABC from typing import Literal from overrides import override, EnforceOverrides class A(EnforceOverrides): def methoda(self, x=0): print(x) def methodb(self, x: int, /, y: str) -> str: return y * x class Other: def foo(self): pass def test_should_pass(): class B(A): @override def methoda(self, y=1, **kwargs): print(y) super().methoda(**kwargs) def test_should_also_pass(): class B(A): @override def methoda(self, z=1, x=1, **kwargs): pass class Abs(ABC): @abstractmethod def method(self, str: Literal["max", "min"]): pass def test_literal_passes(): class B(Abs): @override def method(self, str: Literal["max", "min"]): return class C(Abs): @override def method(self, str: Literal["max", "min", "half"]): return def test_literal_failure(): try: class D(Abs): @override def method(self, str: Literal["a", "b", "c"]): pass raise AssertionError("Should not go here") except TypeError: pass def test_literal_failure_not_accepting_all(): try: class D(Abs): @override def method(self, str: Literal["min"]): pass raise AssertionError("Should not go here") except TypeError: pass def test_can_not_override_with_positional_only(): try: class C(A): @override def methoda(self, x=0, /): pass raise AssertionError("Should not go here") except TypeError: pass def test_can_override_positional_only(): class PositionalOnly1(A): @override def methodb(self, x: int, /, y: str) -> str: return "OK" def test_can_override_positional_only_with_new_name(): class PositionalOnly2(A): @override def methodb(self, new_name_is_ok: int, /, y: str) -> str: return "OK2" def test_can_not_override_positional_only_with_new_type(): try: class PositionalOnly3(A): @override def methodb(self, x: str, /, y: str) -> str: return "NOPE" raise AssertionError("Should not go here") except TypeError: pass def test_can_not_override_with_keyword_only(): try: class C2(A): @override def methoda(self, *, x=0): pass raise AssertionError("Should not go here") except TypeError: pass def test_multiple_inheritance(): class Multi(A, Other): @override def methoda(self, y=2, **kwargs): pass @override def foo(self) -> int: pass overrides-7.7.0/tests/test_new_union__py3_10.py000066400000000000000000000011711455526741000215460ustar00rootroot00000000000000from __future__ import annotations import sys import pytest from overrides import override class A: def f(self) -> int | str: return 1 def test_should_allow_reducing_type(): class B(A): @override def f(self) -> int: return 1 assert B().f() == 1 @pytest.mark.skipif(sys.version_info < (3, 10), reason="requires Python3.10 or higher") def test_should_not_allow_increasing_type(): with pytest.raises(TypeError): class C(A): @override def f(self) -> int | str | list[str]: return [] assert False, "should not go here" overrides-7.7.0/tests/test_overrides.py000066400000000000000000000112211455526741000203120ustar00rootroot00000000000000import sys import unittest from contextlib import contextmanager from typing import Generic, TypeVar import test_somepackage from overrides import override TObject = TypeVar("TObject", bound="Foo") @contextmanager def no_error(): yield class SubClassOfGeneric(Generic[TObject]): def some_method(self): """Generic sub class.""" pass class SubSubClassOfGeneric(SubClassOfGeneric["SubSubClassOfGeneric"]): @override def some_method(self): return 17 class SuperClass(object): @staticmethod def this_is_static(x, y, z): pass def some_method(self): """Super Class Docs""" return "super" class SomeClass: """Super Inner Class Docs""" def check(self): return 0 class SubClass(SuperClass): @override def some_method(self): return "sub" class Subber(SuperClass): @override def some_method(self): """Subber""" return 1 class Nest: class First: class Second: def foo(self): pass class Sub2(test_somepackage.SomeClass, SuperClass): @override def somewhat_fun_method(self): return "foo" @override def some_method(self): pass class SubclassOfInt(int): @override def __str__(self): return "subclass of int" class CheckAtRuntime(SuperClass): @override(check_at_runtime=True) def some_method(self, x): pass class StaticMethodOverridePass(SuperClass): @staticmethod @override def this_is_static(x, y, z, *args): pass class InnerClassOverride(SuperClass): @override class SomeClass: def check(self): return 1 class ChildOfNested(Nest.First.Second): @override def foo(self): pass class OverridesTests(unittest.TestCase): def test_overrides_passes_for_same_package_superclass(self): sub = SubClass() self.assertEqual(sub.some_method(), "sub") self.assertEqual(sub.some_method.__doc__, "Super Class Docs") def test_override_inner_class(self): sup = SuperClass.SomeClass() sub = InnerClassOverride.SomeClass() self.assertEqual(sup.check(), 0) self.assertEqual(sub.check(), 1) self.assertEqual(sup.__doc__, sub.__doc__) def test_overrides_does_not_override_method_doc(self): sub = Subber() self.assertEqual(sub.some_method(), 1) self.assertEqual(sub.some_method.__doc__, "Subber") def test_overrides_passes_for_superclass_in_another_package(self): sub2 = Sub2() self.assertEqual(sub2.somewhat_fun_method(), "foo") self.assertEqual(sub2.somewhat_fun_method.__doc__, "LULZ") def test_assertion_error_is_thrown_when_method_not_in_superclass(self): try: class ShouldFail(SuperClass): @override def somo_method(self): pass raise RuntimeError("Should not go here") except TypeError: pass def test_static_method(self): try: class StaticMethodOverrideFail(SuperClass): @staticmethod @override def this_is_static(k, y, z): pass raise RuntimeError("Should not go here") except TypeError: pass def test_can_override_builtin(self): x = SubclassOfInt(10) self.assertEqual(str(x), "subclass of int") def test_overrides_method_from_generic_subclass(self): genericsub = SubSubClassOfGeneric() self.assertEqual(genericsub.some_method(), 17) self.assertEqual(genericsub.some_method.__doc__, "Generic sub class.") def test_overrides_check_at_runtime(self): with self.assertRaises(TypeError): CheckAtRuntime().some_method(1) def test_overrides_builtin_method_correct_signature(self): class SubclassOfInt(int): @override def bit_length(self): return super().bit_length() x = SubclassOfInt(1) self.assertEqual(x.bit_length(), 1) def test_overrides_builtin_method_incorrect_signature(self): if sys.version_info >= (3, 7): expected_error = self.assertRaises(TypeError) else: # In 3.6 inspecting signature's isn't possible for builtin # methods so this this passes the signature check. expected_error = no_error() with expected_error: class SubclassOfInt(int): @override def bit_length(self, _): "This will fail, bit_length takes in no arguments" if __name__ == "__main__": unittest.main() overrides-7.7.0/tests/test_signatures.py000066400000000000000000000046221455526741000205030ustar00rootroot00000000000000from typing import Any, Type, Union from overrides import override class SuperbClass: @classmethod def class_method(cls): pass @staticmethod def static_method(param: str) -> int: if param == "foo": return 2 return 1 def normal_method(self, x: int, y: str = "hello", *args, **kwargs) -> bool: return x == 1 or y == "bar" or len(args) == 3 or "zoo" in kwargs def self_typed_method(self: "SuperbClass") -> "SuperbClass": return self @classmethod def self_typed_class_method(cls: "Type[SuperbClass]") -> None: return None def foo(self, x) -> None: return None class ForwardReferencer(SuperbClass): @override def foo(self, x: "ForwardReferencer") -> "ForwardReferencer": pass class ClassMethodOverrider(SuperbClass): @classmethod @override def class_method(cls): pass class StaticMethodOverrider(SuperbClass): @staticmethod @override def static_method(param: Union[str, bool]) -> int: return 3 if param == "bar" else 2 class NormalMethodOverrider(SuperbClass): @override def normal_method(self, x: int, y: str = "zoo", *args, **kwargs) -> bool: return x % 3 == 1 or y in kwargs or x == len(args) class OverridesWithSignatureIgnore(SuperbClass): @override(check_signature=False) def normal_method(self, x: int) -> bool: return x % 2 == 1 class SelfTypedOverride(SuperbClass): @override(check_at_runtime=True) def self_typed_method(self: "SelfTypedOverride") -> "SelfTypedOverride": return self @classmethod @override(check_at_runtime=True) def self_typed_class_method(cls: "Type[SelfTypedOverride]") -> None: return None class A: def foo(self: int): pass class B(A): @override def foo(self: str): pass class UntypedBaseClass: def do_something(self, arg1, arg2=None): pass def test_typed_subclass_in_same_package_is_an_error(): try: class TypedSubclass(UntypedBaseClass): @override def do_something(self, arg1: str, arg2: Any = None) -> Any: pass raise AssertionError("Should not go here") except TypeError: pass def test_that_this_file_is_ok(): pass def test_self_typed_overrides(): SelfTypedOverride().self_typed_method() SelfTypedOverride().self_typed_class_method() overrides-7.7.0/tests/test_somefinalpackage.py000066400000000000000000000003231455526741000216020ustar00rootroot00000000000000from overrides import final class SomeClass(object): def somewhat_fun_method(self): """LULZ""" return "LOL" @final def somewhat_finalized_method(self): return "some_final" overrides-7.7.0/tests/test_somepackage.py000066400000000000000000000001441455526741000205710ustar00rootroot00000000000000class SomeClass(object): def somewhat_fun_method(self): """LULZ""" return "LOL" overrides-7.7.0/tests/test_special_cases.py000066400000000000000000000017011455526741000211100ustar00rootroot00000000000000from abc import ABC, abstractmethod from collections.abc import MutableMapping from concurrent.futures import Future from typing import Callable, Any from overrides import override class MyInterface(ABC): @abstractmethod def run(self) -> "Future[str]": pass class MyInterface2(ABC): @abstractmethod def run(self, callback: Callable[[str], None]): pass def test_future_is_fine(): class FutureWorks(MyInterface): @override def run(self) -> "Future[str]": pass def test_callable_is_fine(): class CallableWorks(MyInterface2): @override def run(self, callback: Callable[[str], None]): pass def test_overriding_untyped_from_other_package_is_fine(): class Params(MutableMapping): DEFAULT = object() @override def pop( self, key: str, default: Any = DEFAULT, keep_as_dict: bool = False ) -> Any: pass