forked from slackapi/python-slack-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path__init__.py
More file actions
116 lines (95 loc) · 3.55 KB
/
Copy path__init__.py
File metadata and controls
116 lines (95 loc) · 3.55 KB
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
from abc import ABCMeta, abstractmethod
from functools import wraps
from typing import Callable, Iterable, List, Set, Union
from ...errors import SlackObjectFormationError
class BaseObject:
def __str__(self):
return f"<slack.{self.__class__.__name__}>"
class JsonObject(BaseObject, metaclass=ABCMeta):
@property
@abstractmethod
def attributes(self) -> Set[str]:
"""
Provide a set of attributes of this object that will make up its JSON structure
"""
return set()
def validate_json(self) -> None:
"""
Raises:
SlackObjectFormationError if the object was not valid
"""
for attribute in (func for func in dir(self) if not func.startswith("__")):
method = getattr(self, attribute)
if callable(method) and hasattr(method, "validator"):
method()
def get_non_null_attributes(self) -> dict:
"""
Construct a dictionary out of non-null keys (from attributes property)
present on this object
"""
return {
key: getattr(self, key, None)
for key in sorted(self.attributes)
if getattr(self, key, None) is not None
}
def to_dict(self, *args) -> dict:
"""
Extract this object as a JSON-compatible, Slack-API-valid dictionary
Args:
*args: Any specific formatting args (rare; generally not required)
Raises:
SlackObjectFormationError if the object was not valid
"""
self.validate_json()
return self.get_non_null_attributes()
def __repr__(self):
json = self.to_dict()
if json:
return f"<slack.{self.__class__.__name__}: {json}>"
else:
return self.__str__()
class JsonValidator:
def __init__(self, message: str):
"""
Decorate a method on a class to mark it as a JSON validator. Validation
functions should return true if valid, false if not.
Args:
message: Message to be attached to the thrown SlackObjectFormationError
"""
self.message = message
def __call__(self, func: Callable) -> Callable[..., None]:
@wraps(func)
def wrapped_f(*args, **kwargs):
if not func(*args, **kwargs):
raise SlackObjectFormationError(self.message)
wrapped_f.validator = True
return wrapped_f
class EnumValidator(JsonValidator):
def __init__(self, attribute: str, enum: Iterable[str]):
super().__init__(
f"{attribute} attribute must be one of the following values: "
f"{', '.join(enum)}"
)
def extract_json(
item_or_items: Union[JsonObject, List[JsonObject], str], *format_args
) -> Union[dict, List[dict], str]:
"""
Given a sequence (or single item), attempt to call the to_dict() method on each
item and return a plain list. If item is not the expected type, return it
unmodified, in case it's already a plain dict or some other user created class.
Args:
item_or_items: item(s) to go through
format_args: Any formatting specifiers to pass into the object's to_dict
method
"""
try:
return [
elem.to_dict(*format_args) if isinstance(elem, JsonObject) else elem
for elem in item_or_items
]
except TypeError: # not iterable, so try returning it as a single item
return (
item_or_items.to_dict(*format_args)
if isinstance(item_or_items, JsonObject)
else item_or_items
)