Skip to content

Commit c347ff7

Browse files
committed
Add pretty_print.py and delegators.py, with tests.
1 parent c01e967 commit c347ff7

34 files changed

+1546
-2
lines changed

doc-source/api/delegators.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
=====================================
2+
:mod:`domdf_python_tools.delegators`
3+
=====================================
4+
5+
.. automodule:: domdf_python_tools.delegators

doc-source/api/pretty_print.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
=======================================
2+
:mod:`domdf_python_tools.pretty_print`
3+
=======================================
4+
5+
.. automodule:: domdf_python_tools.pretty_print

doc-source/index.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,6 @@ domdf_python_tools
110110
.. end shields
111111
112112
113-
|
114-
115113
Installation
116114
-------------
117115

domdf_python_tools/delegators.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#!/usr/bin/env python
2+
# cython: language_level=3
3+
#
4+
# delegators.py
5+
"""
6+
Decorators for functions that delegate parts of their functionality
7+
to other functions.
8+
9+
.. versionadded:: 1.0.0
10+
"""
11+
#
12+
# Copyright © 2020 Dominic Davis-Foster <[email protected]>
13+
#
14+
# This program is free software; you can redistribute it and/or modify
15+
# it under the terms of the GNU Lesser General Public License as published by
16+
# the Free Software Foundation; either version 3 of the License, or
17+
# (at your option) any later version.
18+
#
19+
# This program is distributed in the hope that it will be useful,
20+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+
# GNU Lesser General Public License for more details.
23+
#
24+
# You should have received a copy of the GNU Lesser General Public License
25+
# along with this program; if not, write to the Free Software
26+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
27+
# MA 02110-1301, USA.
28+
#
29+
# delegate_kwargs based on https://github.com/fastai/fastcore
30+
# | Licensed under the Apache License, Version 2.0 (the "License"); you may
31+
# | not use this file except in compliance with the License. You may obtain
32+
# | a copy of the License at
33+
# |
34+
# | http://www.apache.org/licenses/LICENSE-2.0
35+
# |
36+
# | Unless required by applicable law or agreed to in writing, software
37+
# | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
38+
# | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
39+
# | License for the specific language governing permissions and limitations
40+
# | under the License.
41+
#
42+
43+
# stdlib
44+
import inspect
45+
import typing
46+
from typing import Callable
47+
48+
__all__ = ["delegate_kwargs", "delegates"]
49+
50+
_C = typing.TypeVar("_C", bound="Callable")
51+
52+
53+
def delegate_kwargs(to: Callable, *except_):
54+
r"""
55+
Decorator to replace ``**kwargs`` in function signatures with the
56+
parameter names from the delegated function.
57+
58+
:param to: The function \*\*kwargs is passed on to.
59+
:param \*except\_: Parameter names not to delegate.
60+
61+
:raises ValueError: if a non-default argument follows a default argument.
62+
"""
63+
64+
# TODO: return annotation
65+
66+
def _f(f: Callable):
67+
to_f, from_f = to, f
68+
69+
to_sig = inspect.signature(to_f)
70+
from_sig = inspect.signature(from_f)
71+
to_annotations = typing.get_type_hints(to_f)
72+
from_annotations = typing.get_type_hints(from_f)
73+
to_params = {k: v for k, v in to_sig.parameters.items() if k not in except_}
74+
from_params = dict(from_sig.parameters)
75+
76+
if from_params.pop("kwargs", False):
77+
if "kwargs" in from_annotations:
78+
del from_annotations["kwargs"]
79+
80+
for param in from_params:
81+
if param in to_params:
82+
del to_params[param]
83+
84+
f.__signature__ = from_sig.replace(
85+
parameters=[*from_params.values(), *to_params.values()]
86+
) # type: ignore
87+
f.__annotations__ = {**to_annotations, **from_annotations}
88+
89+
return f
90+
91+
return _f
92+
93+
94+
def delegates(to: Callable) -> Callable[[_C], _C]:
95+
r"""
96+
Decorator to replace ``*args, **kwargs`` function signatures
97+
with the signature of the delegated function.
98+
99+
:param to: The function the arguments are passed on to.
100+
"""
101+
102+
def copy_annotations(f):
103+
if hasattr(to, "__annotations__"):
104+
if hasattr(f, "__annotations__"):
105+
return_annotation = f.__annotations__.get("return", inspect.Parameter.empty)
106+
f.__annotations__.update(to.__annotations__)
107+
if return_annotation is not inspect.Parameter.empty:
108+
f.__annotations__["return"] = return_annotation
109+
else:
110+
f.__annotations__ = to.__annotations__
111+
112+
def _f(f: _C) -> _C:
113+
to_sig = inspect.signature(to)
114+
from_sig = inspect.signature(f)
115+
from_params = dict(from_sig.parameters)
116+
117+
if tuple(from_params.keys()) == ("args", "kwargs"):
118+
f.__signature__ = to_sig # type: ignore
119+
copy_annotations(f)
120+
121+
elif tuple(from_params.keys()) == ("self", "args", "kwargs"):
122+
f.__signature__ = from_sig.replace( # type: ignore
123+
parameters=[
124+
from_params["self"],
125+
*to_sig.parameters.values()
126+
]
127+
)
128+
129+
copy_annotations(f)
130+
131+
return f
132+
133+
return _f

0 commit comments

Comments
 (0)