-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
/
decorators.py
189 lines (142 loc) · 6.4 KB
/
decorators.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#-----------------------------------------------------------------------------
# Copyright (c) 2012 - 2023, Anaconda, Inc., and Bokeh Contributors.
# All rights reserved.
#
# The full license is in the file LICENSE.txt, distributed with this software.
#-----------------------------------------------------------------------------
""" Provide decorators help with define Bokeh validation checks.
"""
#-----------------------------------------------------------------------------
# Boilerplate
#-----------------------------------------------------------------------------
from __future__ import annotations
import logging # isort:skip
log = logging.getLogger(__name__)
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
# Standard library imports
from typing import (
TYPE_CHECKING,
Any,
Callable,
Union,
cast,
)
# Bokeh imports
from .check import ValidationIssue, Validator, ValidatorType
from .issue import Error, Issue, Warning
if TYPE_CHECKING:
from typing_extensions import TypeAlias
#-----------------------------------------------------------------------------
# Globals and constants
#-----------------------------------------------------------------------------
__all__ = (
"error",
"warning",
)
#-----------------------------------------------------------------------------
# Private API
#-----------------------------------------------------------------------------
ValidationFunction: TypeAlias = Callable[..., Union[str, None]]
ValidationDecorator: TypeAlias = Callable[[ValidationFunction], Validator]
def _validator(code_or_name: int | str | Issue, validator_type: ValidatorType) -> ValidationDecorator:
""" Internal shared implementation to handle both error and warning
validation checks.
Args:
code code_or_name (int, str or Issue) : a defined error code or custom message
validator_type (str) : either "error" or "warning"
Returns:
validation decorator
"""
issues: type[Error] | type[Warning] = \
Error if validator_type == "error" else Warning
def decorator(func: ValidationFunction) -> Validator:
def _wrapper(*args: Any, **kwargs: Any) -> list[ValidationIssue]:
extra = func(*args, **kwargs)
if extra is None:
return []
issue: Issue
name: str
if isinstance(code_or_name, str):
issue = issues.get_by_name("EXT")
name = f"{issue.name}:{code_or_name}"
elif isinstance(code_or_name, int):
try:
issue = issues.get_by_code(code_or_name)
name = issue.name
except KeyError:
raise ValueError(f"unknown {validator_type} code {code_or_name}")
else:
issue = code_or_name
name = issue.name
code = issue.code
text = issue.description
return [ValidationIssue(code, name, text, extra)]
wrapper = cast(Validator, _wrapper)
wrapper.validator_type = validator_type
return wrapper
return decorator
#-----------------------------------------------------------------------------
# General API
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Dev API
#-----------------------------------------------------------------------------
def error(code_or_name: int | str | Issue) -> ValidationDecorator:
""" Decorator to mark a validator method for a Bokeh error condition
Args:
code_or_name (int, str or Issue) : a code from ``bokeh.validation.errors`` or a string label for a custom check
Returns:
callable : decorator for Bokeh model methods
The function that is decorated should have a name that starts with
``_check``, and return a string message in case a bad condition is
detected, and ``None`` if no bad condition is detected.
Examples:
The first example uses a numeric code for a standard error provided in
``bokeh.validation.errors``. This usage is primarily of interest to Bokeh
core developers.
.. code-block:: python
from bokeh.validation.errors import REQUIRED_RANGES
@error(REQUIRED_RANGES)
def _check_no_glyph_renderers(self):
if bad_condition: return "message"
The second example shows how a custom warning check can be implemented by
passing an arbitrary string label to the decorator. This usage is primarily
of interest to anyone extending Bokeh with their own custom models.
.. code-block:: python
@error("MY_CUSTOM_WARNING")
def _check_my_custom_warning(self):
if bad_condition: return "message"
"""
return _validator(code_or_name, "error")
def warning(code_or_name: int | str | Issue) -> ValidationDecorator:
""" Decorator to mark a validator method for a Bokeh error condition
Args:
code_or_name (int, str or Issue) : a code from ``bokeh.validation.errors`` or a string label for a custom check
Returns:
callable : decorator for Bokeh model methods
The function that is decorated should have a name that starts with
``_check``, and return a string message in case a bad condition is
detected, and ``None`` if no bad condition is detected.
Examples:
The first example uses a numeric code for a standard warning provided in
``bokeh.validation.warnings``. This usage is primarily of interest to Bokeh
core developers.
.. code-block:: python
from bokeh.validation.warnings import MISSING_RENDERERS
@warning(MISSING_RENDERERS)
def _check_no_glyph_renderers(self):
if bad_condition: return "message"
The second example shows how a custom warning check can be implemented by
passing an arbitrary string label to the decorator. This usage is primarily
of interest to anyone extending Bokeh with their own custom models.
.. code-block:: python
@warning("MY_CUSTOM_WARNING")
def _check_my_custom_warning(self):
if bad_condition: return "message"
"""
return _validator(code_or_name, "warning")
#-----------------------------------------------------------------------------
# Code
#-----------------------------------------------------------------------------