From 3a30c7010ab126c43d07fd92cf4a40d5fdab7631 Mon Sep 17 00:00:00 2001 From: Ricardo Robles Date: Sun, 16 Nov 2025 17:30:20 +0100 Subject: [PATCH 1/3] PEP 8108: Introduce coroutinedispatch class for method overloading in functools --- peps/pep-8108.rst | 220 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 peps/pep-8108.rst diff --git a/peps/pep-8108.rst b/peps/pep-8108.rst new file mode 100644 index 00000000000..55694dec068 --- /dev/null +++ b/peps/pep-8108.rst @@ -0,0 +1,220 @@ +PEP: 8108 +Title: Adding coroutinedispatch to functools for Method Overloading +Author: Ricardo Robles +Status: Draft +Type: Standards Track +Created: 16-Nov-2025 + +Abstract +======== + +This PEP proposes the addition of a `coroutinedispatch` class to the `functools` module in Python's standard library. The `coroutinedispatch` class enables method overloading based on argument types, supporting both synchronous and asynchronous methods. This feature simplifies the implementation of polymorphic behavior in Python, making it easier to write clean and maintainable code. + +Motivation +========== + +Python currently lacks built-in support for method overloading based on argument types. While developers can achieve similar functionality using conditional statements or external libraries, these approaches often result in verbose and less readable code. Adding a `coroutinedispatch` class to the standard library would: + +- Provide a standardized and Pythonic way to implement method overloading. +- Enhance code readability and maintainability. +- Support both synchronous and asynchronous methods, addressing a common limitation in existing third-party solutions. + +Rationale +========= + +The `coroutinedispatch` class leverages Python's type hints and introspection capabilities to match method implementations based on argument types. This design aligns with Python's dynamic nature while encouraging the use of type annotations. By integrating this functionality into the `functools` module, we ensure consistency with other utility features for functional programming. + +Specification +============= + +The `coroutinedispatch` class will be implemented as follows: + +- **Registration of Methods**: Developers can register multiple implementations of a method using the `@coroutinedispatch` decorator. The decorator inspects the argument types of the decorated function and stores the implementation accordingly. +- **Type Matching**: When the method is called, the `coroutinedispatch` class matches the provided arguments against the registered implementations. If no exact match is found, a `TypeError` is raised. +- **Support for Async Methods**: The `coroutinedispatch` class distinguishes between synchronous and asynchronous contexts, allowing developers to define separate implementations for each. + +Example Usage +------------- + +.. code-block:: python + + import asyncio + from functools import coroutinedispatch + + class Example: + @coroutinedispatch + def process(self, x: int) -> str: + return f"Processing integer: {x}" + + @process.register + async def _(self, x: int) -> str: + await asyncio.sleep(0.1) + return f"Processing integer asynchronously: {x}" + + @process.register + def _(self, x: str) -> str: + return f"Processing string: {x}" + + @process.register + async def _(self, x: str) -> str: + await asyncio.sleep(0.1) + return f"Processing string asynchronously: {x}" + + example = Example() + print(example.process(42)) # Processing integer: 42 + print(example.process("hello")) # Processing string: hello + + async def main(): + print(await example.process(42)) # Processing integer asynchronously: 42 + print(await example.process("hello")) # Processing string asynchronously: hello + + asyncio.run(main()) + +Backwards Compatibility +======================= + +This proposal introduces a new class to the `functools` module and does not modify any existing functionality. As such, it is fully backwards compatible. + +Security Implications +===================== + +The `coroutinedispatch` class relies on Python's type hints and introspection capabilities. It does not introduce any new security risks beyond those inherent to dynamic type checking. + +How to Teach This +================= + +The `coroutinedispatch` class should be documented in the Python standard library reference, with examples demonstrating its usage. Tutorials and guides on functional programming in Python can include sections on `coroutinedispatch` to showcase its benefits. + +Reference Implementation +========================= + +A reference implementation of the `coroutinedispatch` class is provided below: + +.. code-block:: python + + import inspect + import asyncio + from typing import Any, Callable, get_type_hints, get_origin + from functools import lru_cache + + + class coroutinedispatch: + def __init__(self, func: Callable): + self._sync_methods = {} + self._async_methods = {} + self._name = func.__name__ + + arg_types = self.get_arg_types(func) + + if inspect.iscoroutinefunction(func): + self._async_methods[arg_types] = func + else: + self._sync_methods[arg_types] = func + + def get_arg_types(self, func: Callable) -> tuple: + try: + hints = get_type_hints(func) + sig = inspect.signature(func) + params = list(sig.parameters.values()) + + if params and params[0].name in ("self", "cls"): + params = params[1:] + + return tuple(hints.get(p.name, Any) for p in params) + except Exception: + return () + + @lru_cache(maxsize=128) + def match_types(self, provided_args: tuple, expected_types: tuple) -> bool: + if len(provided_args) != len(expected_types): + return False + + for arg, expected in zip(provided_args, expected_types): + if expected is Any: + continue + + origin = get_origin(expected) + if origin is not None: + expected = origin + + if not isinstance(arg, expected): + return False + + return True + + @lru_cache(maxsize=128) + def _find_matching_method(self, is_async: bool, *args: Any) -> Callable: + """Find the method that matches the argument types.""" + methods = self._async_methods if is_async else self._sync_methods + + # Search for exact match + for arg_types, method in methods.items(): + if self.match_types(args, arg_types): + return method + + # If no match, raise descriptive error + arg_type_names = tuple(type(arg).__name__ for arg in args) + context = "async" if is_async else "sync" + available = list(methods.keys()) + + raise TypeError( + f"No matching {context} method '{self._name}' found for " + f"arguments: {arg_type_names}. Available: {available}" + ) + + def register(self, func: Callable) -> Callable: + """Register a new method overload.""" + arg_types = self.get_arg_types(func) + + if inspect.iscoroutinefunction(func): + self._async_methods[arg_types] = func + else: + self._sync_methods[arg_types] = func + + return self + + def __call__(self, *args: Any, **kwargs: Any) -> Any: + try: + asyncio.get_event_loop() + is_async_context = True + except RuntimeError: + is_async_context = False + pass + + # Exclude 'self' from arguments if present + check_args = args[1:] if args and hasattr(args[0], self._name) else args + + try: + method = self._find_matching_method(is_async_context, *check_args) + result = method(*args, **kwargs) + + return result + except TypeError: + # If no async/sync method, try the other + try: + is_async_context = not is_async_context + method = self._find_matching_method(is_async_context, *check_args) + result = method(*args, **kwargs) + + return result + except TypeError: + raise + + def __get__(self, obj, objtype=None): + """Support for bound methods""" + if obj is None: + return self + + import functools + + return functools.partial(self.__call__, obj) + +Acknowledgements +================ + +Thanks to the Python community for their feedback and contributions to this proposal. + +Copyright +========= + +This document has been placed in the public domain. \ No newline at end of file From eed47e1d4e12b5e44f14d1668b31f890e30474ed Mon Sep 17 00:00:00 2001 From: Ricardo Robles Date: Sun, 16 Nov 2025 17:33:54 +0100 Subject: [PATCH 2/3] PEP 8108: Add newline at end of file for consistency --- peps/pep-8108.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-8108.rst b/peps/pep-8108.rst index 55694dec068..193d3fe96c0 100644 --- a/peps/pep-8108.rst +++ b/peps/pep-8108.rst @@ -217,4 +217,4 @@ Thanks to the Python community for their feedback and contributions to this prop Copyright ========= -This document has been placed in the public domain. \ No newline at end of file +This document has been placed in the public domain. From 329eb7a4828a81e4a3c768dcdc035b9bb46d4169 Mon Sep 17 00:00:00 2001 From: Ricardo Robles Date: Sun, 16 Nov 2025 17:37:09 +0100 Subject: [PATCH 3/3] PEP 8108: Consistent use of backticks for `coroutinedispatch` in documentation --- peps/pep-8108.rst | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/peps/pep-8108.rst b/peps/pep-8108.rst index 193d3fe96c0..6d7c24e85fc 100644 --- a/peps/pep-8108.rst +++ b/peps/pep-8108.rst @@ -8,12 +8,12 @@ Created: 16-Nov-2025 Abstract ======== -This PEP proposes the addition of a `coroutinedispatch` class to the `functools` module in Python's standard library. The `coroutinedispatch` class enables method overloading based on argument types, supporting both synchronous and asynchronous methods. This feature simplifies the implementation of polymorphic behavior in Python, making it easier to write clean and maintainable code. +This PEP proposes the addition of a ``coroutinedispatch`` class to the ``functools`` module in Python's standard library. The ``coroutinedispatch`` class enables method overloading based on argument types, supporting both synchronous and asynchronous methods. This feature simplifies the implementation of polymorphic behavior in Python, making it easier to write clean and maintainable code. Motivation ========== -Python currently lacks built-in support for method overloading based on argument types. While developers can achieve similar functionality using conditional statements or external libraries, these approaches often result in verbose and less readable code. Adding a `coroutinedispatch` class to the standard library would: +Python currently lacks built-in support for method overloading based on argument types. While developers can achieve similar functionality using conditional statements or external libraries, these approaches often result in verbose and less readable code. Adding a ``coroutinedispatch`` class to the standard library would: - Provide a standardized and Pythonic way to implement method overloading. - Enhance code readability and maintainability. @@ -22,16 +22,16 @@ Python currently lacks built-in support for method overloading based on argument Rationale ========= -The `coroutinedispatch` class leverages Python's type hints and introspection capabilities to match method implementations based on argument types. This design aligns with Python's dynamic nature while encouraging the use of type annotations. By integrating this functionality into the `functools` module, we ensure consistency with other utility features for functional programming. +The ``coroutinedispatch`` class leverages Python's type hints and introspection capabilities to match method implementations based on argument types. This design aligns with Python's dynamic nature while encouraging the use of type annotations. By integrating this functionality into the ``functools`` module, we ensure consistency with other utility features for functional programming. Specification ============= -The `coroutinedispatch` class will be implemented as follows: +The ``coroutinedispatch`` class will be implemented as follows: -- **Registration of Methods**: Developers can register multiple implementations of a method using the `@coroutinedispatch` decorator. The decorator inspects the argument types of the decorated function and stores the implementation accordingly. -- **Type Matching**: When the method is called, the `coroutinedispatch` class matches the provided arguments against the registered implementations. If no exact match is found, a `TypeError` is raised. -- **Support for Async Methods**: The `coroutinedispatch` class distinguishes between synchronous and asynchronous contexts, allowing developers to define separate implementations for each. +- **Registration of Methods**: Developers can register multiple implementations of a method using the ``@coroutinedispatch`` decorator. The decorator inspects the argument types of the decorated function and stores the implementation accordingly. +- **Type Matching**: When the method is called, the ``coroutinedispatch`` class matches the provided arguments against the registered implementations. If no exact match is found, a ``TypeError`` is raised. +- **Support for Async Methods**: The ``coroutinedispatch`` class distinguishes between synchronous and asynchronous contexts, allowing developers to define separate implementations for each. Example Usage ------------- @@ -73,22 +73,22 @@ Example Usage Backwards Compatibility ======================= -This proposal introduces a new class to the `functools` module and does not modify any existing functionality. As such, it is fully backwards compatible. +This proposal introduces a new class to the ``functools`` module and does not modify any existing functionality. As such, it is fully backwards compatible. Security Implications ===================== -The `coroutinedispatch` class relies on Python's type hints and introspection capabilities. It does not introduce any new security risks beyond those inherent to dynamic type checking. +The ``coroutinedispatch`` class relies on Python's type hints and introspection capabilities. It does not introduce any new security risks beyond those inherent to dynamic type checking. How to Teach This ================= -The `coroutinedispatch` class should be documented in the Python standard library reference, with examples demonstrating its usage. Tutorials and guides on functional programming in Python can include sections on `coroutinedispatch` to showcase its benefits. +The ``coroutinedispatch`` class should be documented in the Python standard library reference, with examples demonstrating its usage. Tutorials and guides on functional programming in Python can include sections on ``coroutinedispatch`` to showcase its benefits. Reference Implementation ========================= -A reference implementation of the `coroutinedispatch` class is provided below: +A reference implementation of the ``coroutinedispatch`` class is provided below: .. code-block:: python