Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/galaxy/util/object_wrapper.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author | shellac |
---|---|
date | Mon, 22 Mar 2021 18:12:50 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4f3585e2f14b |
---|---|
1 """ | |
2 Classes for wrapping Objects and Sanitizing string output. | |
3 """ | |
4 | |
5 import copyreg | |
6 import inspect | |
7 import logging | |
8 import string | |
9 from collections import UserDict | |
10 from collections.abc import Callable | |
11 from numbers import Number | |
12 from types import ( | |
13 BuiltinFunctionType, | |
14 BuiltinMethodType, | |
15 CodeType, | |
16 FrameType, | |
17 FunctionType, | |
18 GeneratorType, | |
19 GetSetDescriptorType, | |
20 MemberDescriptorType, | |
21 MethodType, | |
22 ModuleType, | |
23 TracebackType, | |
24 ) | |
25 | |
26 NoneType = type(None) | |
27 NotImplementedType = type(NotImplemented) | |
28 EllipsisType = type(Ellipsis) | |
29 XRangeType = range | |
30 SliceType = slice | |
31 | |
32 # Python 2 version was: | |
33 # from types import BufferType, DictProxyType | |
34 # Py3 doesn't have these concepts, just treat them like SliceType that | |
35 # so they are __WRAP_NO_SUBCLASS__. | |
36 BufferType = SliceType | |
37 DictProxyType = SliceType | |
38 | |
39 from galaxy.util import sanitize_lists_to_string as _sanitize_lists_to_string | |
40 | |
41 log = logging.getLogger(__name__) | |
42 | |
43 # Define different behaviors for different types, see also: https://docs.python.org/2/library/types.html | |
44 | |
45 # Known Callable types | |
46 __CALLABLE_TYPES__ = (FunctionType, MethodType, GeneratorType, CodeType, BuiltinFunctionType, BuiltinMethodType, ) | |
47 | |
48 # Always wrap these types without attempting to subclass | |
49 __WRAP_NO_SUBCLASS__ = (ModuleType, XRangeType, SliceType, BufferType, TracebackType, FrameType, DictProxyType, | |
50 GetSetDescriptorType, MemberDescriptorType) + __CALLABLE_TYPES__ | |
51 | |
52 # Don't wrap or sanitize. | |
53 __DONT_SANITIZE_TYPES__ = (Number, bool, NoneType, NotImplementedType, EllipsisType, bytearray, ) | |
54 | |
55 # Wrap contents, but not the container | |
56 __WRAP_SEQUENCES__ = (tuple, list, ) | |
57 __WRAP_SETS__ = (set, frozenset, ) | |
58 __WRAP_MAPPINGS__ = (dict, UserDict, ) | |
59 | |
60 | |
61 # Define the set of characters that are not sanitized, and define a set of mappings for those that are. | |
62 # characters that are valid | |
63 VALID_CHARACTERS = set(string.ascii_letters + string.digits + " -=_.()/+*^,:?!@") | |
64 | |
65 # characters that are allowed but need to be escaped | |
66 CHARACTER_MAP = {'>': '__gt__', | |
67 '<': '__lt__', | |
68 "'": '__sq__', | |
69 '"': '__dq__', | |
70 '[': '__ob__', | |
71 ']': '__cb__', | |
72 '{': '__oc__', | |
73 '}': '__cc__', | |
74 '\n': '__cn__', | |
75 '\r': '__cr__', | |
76 '\t': '__tc__', | |
77 '#': '__pd__'} | |
78 | |
79 INVALID_CHARACTER = "X" | |
80 | |
81 | |
82 def coerce(x, y): | |
83 # __coerce__ doesn't do anything under Python anyway. | |
84 return x | |
85 | |
86 | |
87 def cmp(x, y): | |
88 # Builtin in Python 2, but not Python 3. | |
89 return (x > y) - (x < y) | |
90 | |
91 | |
92 def sanitize_lists_to_string(values, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP, invalid_character=INVALID_CHARACTER): | |
93 return _sanitize_lists_to_string(values, valid_characters=valid_characters, character_map=character_map, invalid_character=invalid_character) | |
94 | |
95 | |
96 def wrap_with_safe_string(value, no_wrap_classes=None): | |
97 """ | |
98 Recursively wrap values that should be wrapped. | |
99 """ | |
100 | |
101 def __do_wrap(value): | |
102 if isinstance(value, SafeStringWrapper): | |
103 # Only ever wrap one-layer | |
104 return value | |
105 if isinstance(value, Callable): | |
106 safe_class = CallableSafeStringWrapper | |
107 else: | |
108 safe_class = SafeStringWrapper | |
109 if isinstance(value, no_wrap_classes): | |
110 return value | |
111 if isinstance(value, __WRAP_NO_SUBCLASS__): | |
112 return safe_class(value, safe_string_wrapper_function=__do_wrap) | |
113 for this_type in __WRAP_SEQUENCES__ + __WRAP_SETS__: | |
114 if isinstance(value, this_type): | |
115 return this_type(list(map(__do_wrap, value))) | |
116 for this_type in __WRAP_MAPPINGS__: | |
117 if isinstance(value, this_type): | |
118 # Wrap both key and value | |
119 return this_type((__do_wrap(x[0]), __do_wrap(x[1])) for x in value.items()) | |
120 # Create a dynamic class that joins SafeStringWrapper with the object being wrapped. | |
121 # This allows e.g. isinstance to continue to work. | |
122 try: | |
123 wrapped_class_name = value.__name__ | |
124 wrapped_class = value | |
125 except Exception: | |
126 wrapped_class_name = value.__class__.__name__ | |
127 wrapped_class = value.__class__ | |
128 value_mod = inspect.getmodule(value) | |
129 if value_mod: | |
130 wrapped_class_name = f"{value_mod.__name__}.{wrapped_class_name}" | |
131 wrapped_class_name = "SafeStringWrapper({}:{})".format(wrapped_class_name, ",".join(sorted(map(str, no_wrap_classes)))) | |
132 do_wrap_func_name = f"__do_wrap_{wrapped_class_name}" | |
133 do_wrap_func = __do_wrap | |
134 global_dict = globals() | |
135 if wrapped_class_name in global_dict: | |
136 # Check to see if we have created a wrapper for this class yet, if so, reuse | |
137 wrapped_class = global_dict.get(wrapped_class_name) | |
138 do_wrap_func = global_dict.get(do_wrap_func_name, __do_wrap) | |
139 else: | |
140 try: | |
141 wrapped_class = type(wrapped_class_name, (safe_class, wrapped_class, ), {}) | |
142 except TypeError as e: | |
143 # Fail-safe for when a class cannot be dynamically subclassed. | |
144 log.warning(f"Unable to create dynamic subclass {wrapped_class_name} for {type(value)}, {value}: {e}") | |
145 wrapped_class = type(wrapped_class_name, (safe_class, ), {}) | |
146 if wrapped_class not in (SafeStringWrapper, CallableSafeStringWrapper): | |
147 # Save this wrapper for reuse and pickling/copying | |
148 global_dict[wrapped_class_name] = wrapped_class | |
149 do_wrap_func.__name__ = do_wrap_func_name | |
150 global_dict[do_wrap_func_name] = do_wrap_func | |
151 | |
152 def pickle_safe_object(safe_object): | |
153 return (wrapped_class, (safe_object.unsanitized, do_wrap_func, )) | |
154 # Set pickle and copy properties | |
155 copyreg.pickle(wrapped_class, pickle_safe_object, do_wrap_func) | |
156 return wrapped_class(value, safe_string_wrapper_function=do_wrap_func) | |
157 | |
158 # Determine classes not to wrap | |
159 if no_wrap_classes: | |
160 if not isinstance(no_wrap_classes, (tuple, list)): | |
161 no_wrap_classes = [no_wrap_classes] | |
162 no_wrap_classes = list(no_wrap_classes) + list(__DONT_SANITIZE_TYPES__) + [SafeStringWrapper] | |
163 else: | |
164 no_wrap_classes = list(__DONT_SANITIZE_TYPES__) + [SafeStringWrapper] | |
165 no_wrap_classes = tuple(set(sorted(no_wrap_classes, key=str))) | |
166 return __do_wrap(value) | |
167 | |
168 | |
169 # N.B. refer to e.g. https://docs.python.org/reference/datamodel.html for information on Python's Data Model. | |
170 | |
171 | |
172 class SafeStringWrapper: | |
173 """ | |
174 Class that wraps and sanitizes any provided value's attributes | |
175 that will attempt to be cast into a string. | |
176 | |
177 Attempts to mimic behavior of original class, including operands. | |
178 | |
179 To ensure proper handling of e.g. subclass checks, the *wrap_with_safe_string()* | |
180 method should be used. | |
181 | |
182 This wrapping occurs in a recursive/parasitic fashion, as all called attributes of | |
183 the originally wrapped object will also be wrapped and sanitized, unless the attribute | |
184 is of a type found in __DONT_SANITIZE_TYPES__ + __DONT_WRAP_TYPES__, where e.g. ~(strings | |
185 will still be sanitized, but not wrapped), and e.g. integers will have neither. | |
186 """ | |
187 __UNSANITIZED_ATTRIBUTE_NAME__ = 'unsanitized' | |
188 __NO_WRAP_NAMES__ = ['__safe_string_wrapper_function__', '__class__', __UNSANITIZED_ATTRIBUTE_NAME__] | |
189 | |
190 def __new__(cls, *arg, **kwd): | |
191 # We need to define a __new__ since, we are subclassing from e.g. immutable str, which internally sets data | |
192 # that will be used when other + this (this + other is handled by __add__) | |
193 try: | |
194 sanitized_value = sanitize_lists_to_string(arg[0], valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP) | |
195 return super().__new__(cls, sanitized_value) | |
196 except TypeError: | |
197 # Class to be wrapped takes no parameters. | |
198 # This is pefectly normal for mutable types. | |
199 return super().__new__(cls) | |
200 | |
201 def __init__(self, value, safe_string_wrapper_function=wrap_with_safe_string): | |
202 self.unsanitized = value | |
203 self.__safe_string_wrapper_function__ = safe_string_wrapper_function | |
204 | |
205 def __str__(self): | |
206 return sanitize_lists_to_string(self.unsanitized, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP) | |
207 | |
208 def __repr__(self): | |
209 return "{} object at {:x} on: {}".format(sanitize_lists_to_string(self.__class__.__name__, valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP), id(self), sanitize_lists_to_string(repr(self.unsanitized), valid_characters=VALID_CHARACTERS, character_map=CHARACTER_MAP)) | |
210 | |
211 def __lt__(self, other): | |
212 while isinstance(other, SafeStringWrapper): | |
213 other = other.unsanitized | |
214 return self.unsanitized < other | |
215 | |
216 def __le__(self, other): | |
217 while isinstance(other, SafeStringWrapper): | |
218 other = other.unsanitized | |
219 return self.unsanitized <= other | |
220 | |
221 def __eq__(self, other): | |
222 while isinstance(other, SafeStringWrapper): | |
223 other = other.unsanitized | |
224 return self.unsanitized == other | |
225 | |
226 def __ne__(self, other): | |
227 while isinstance(other, SafeStringWrapper): | |
228 other = other.unsanitized | |
229 return self.unsanitized != other | |
230 | |
231 def __gt__(self, other): | |
232 while isinstance(other, SafeStringWrapper): | |
233 other = other.unsanitized | |
234 return self.unsanitized > other | |
235 | |
236 def __ge__(self, other): | |
237 while isinstance(other, SafeStringWrapper): | |
238 other = other.unsanitized | |
239 return self.unsanitized >= other | |
240 | |
241 def __cmp__(self, other): | |
242 while isinstance(other, SafeStringWrapper): | |
243 other = other.unsanitized | |
244 return cmp(self.unsanitized, other) | |
245 | |
246 # Do not implement __rcmp__, python 2.2 < 2.6 | |
247 | |
248 def __hash__(self): | |
249 return hash(self.unsanitized) | |
250 | |
251 def __bool__(self): | |
252 return bool(self.unsanitized) | |
253 __nonzero__ = __bool__ | |
254 | |
255 # Do not implement __unicode__, we will rely on __str__ | |
256 | |
257 def __getattr__(self, name): | |
258 if name in SafeStringWrapper.__NO_WRAP_NAMES__: | |
259 # FIXME: is this ever reached? | |
260 return object.__getattribute__(self, name) | |
261 return self.__safe_string_wrapper_function__(getattr(self.unsanitized, name)) | |
262 | |
263 def __setattr__(self, name, value): | |
264 if name in SafeStringWrapper.__NO_WRAP_NAMES__: | |
265 return object.__setattr__(self, name, value) | |
266 return setattr(self.unsanitized, name, value) | |
267 | |
268 def __delattr__(self, name): | |
269 if name in SafeStringWrapper.__NO_WRAP_NAMES__: | |
270 return object.__delattr__(self, name) | |
271 return delattr(self.unsanitized, name) | |
272 | |
273 def __getattribute__(self, name): | |
274 if name in SafeStringWrapper.__NO_WRAP_NAMES__: | |
275 return object.__getattribute__(self, name) | |
276 return self.__safe_string_wrapper_function__(getattr(object.__getattribute__(self, 'unsanitized'), name)) | |
277 | |
278 # Skip Descriptors | |
279 | |
280 # Skip __slots__ | |
281 | |
282 # Don't need to define a metaclass, we'll use the helper function to handle with subclassing for e.g. isinstance() | |
283 | |
284 # Revisit: | |
285 # __instancecheck__ | |
286 # __subclasscheck__ | |
287 # We are using a helper class to create dynamic subclasses to handle class checks | |
288 | |
289 # We address __call__ as needed based upon unsanitized, through the use of a CallableSafeStringWrapper class | |
290 | |
291 def __len__(self): | |
292 original_value = self.unsanitized | |
293 while isinstance(original_value, SafeStringWrapper): | |
294 original_value = self.unsanitized | |
295 return len(self.unsanitized) | |
296 | |
297 def __getitem__(self, key): | |
298 return self.__safe_string_wrapper_function__(self.unsanitized[key]) | |
299 | |
300 def __setitem__(self, key, value): | |
301 while isinstance(value, SafeStringWrapper): | |
302 value = value.unsanitized | |
303 self.unsanitized[key] = value | |
304 | |
305 def __delitem__(self, key): | |
306 del self.unsanitized[key] | |
307 | |
308 def __iter__(self): | |
309 return iter(map(self.__safe_string_wrapper_function__, iter(self.unsanitized))) | |
310 | |
311 # Do not implement __reversed__ | |
312 | |
313 def __contains__(self, item): | |
314 # FIXME: Do we need to consider if item is/isn't or does/doesn't contain SafeStringWrapper? | |
315 # When considering e.g. nested lists/dicts/etc, this gets complicated | |
316 while isinstance(item, SafeStringWrapper): | |
317 item = item.unsanitized | |
318 return item in self.unsanitized | |
319 | |
320 # Not sure that we need these slice methods, but will provide anyway | |
321 def __getslice__(self, i, j): | |
322 return self.__safe_string_wrapper_function__(self.unsanitized[i:j]) | |
323 | |
324 def __setslice__(self, i, j, value): | |
325 self.unsanitized[i:j] = value | |
326 | |
327 def __delslice__(self, i, j): | |
328 del self.unsanitized[i:j] | |
329 | |
330 def __add__(self, other): | |
331 while isinstance(other, SafeStringWrapper): | |
332 other = other.unsanitized | |
333 return self.__safe_string_wrapper_function__(self.unsanitized + other) | |
334 | |
335 def __sub__(self, other): | |
336 while isinstance(other, SafeStringWrapper): | |
337 other = other.unsanitized | |
338 return self.__safe_string_wrapper_function__(self.unsanitized - other) | |
339 | |
340 def __mul__(self, other): | |
341 while isinstance(other, SafeStringWrapper): | |
342 other = other.unsanitized | |
343 return self.__safe_string_wrapper_function__(self.unsanitized * other) | |
344 | |
345 def __floordiv__(self, other): | |
346 while isinstance(other, SafeStringWrapper): | |
347 other = other.unsanitized | |
348 return self.__safe_string_wrapper_function__(self.unsanitized // other) | |
349 | |
350 def __mod__(self, other): | |
351 while isinstance(other, SafeStringWrapper): | |
352 other = other.unsanitized | |
353 return self.__safe_string_wrapper_function__(self.unsanitized % other) | |
354 | |
355 def __divmod__(self, other): | |
356 while isinstance(other, SafeStringWrapper): | |
357 other = other.unsanitized | |
358 return self.__safe_string_wrapper_function__(divmod(self.unsanitized, other)) | |
359 | |
360 def __pow__(self, *other): | |
361 while isinstance(other, SafeStringWrapper): | |
362 other = other.unsanitized | |
363 return self.__safe_string_wrapper_function__(pow(self.unsanitized, *other)) | |
364 | |
365 def __lshift__(self, other): | |
366 while isinstance(other, SafeStringWrapper): | |
367 other = other.unsanitized | |
368 return self.__safe_string_wrapper_function__(self.unsanitized << other) | |
369 | |
370 def __rshift__(self, other): | |
371 while isinstance(other, SafeStringWrapper): | |
372 other = other.unsanitized | |
373 return self.__safe_string_wrapper_function__(self.unsanitized >> other) | |
374 | |
375 def __and__(self, other): | |
376 while isinstance(other, SafeStringWrapper): | |
377 other = other.unsanitized | |
378 return self.__safe_string_wrapper_function__(self.unsanitized & other) | |
379 | |
380 def __xor__(self, other): | |
381 while isinstance(other, SafeStringWrapper): | |
382 other = other.unsanitized | |
383 return self.__safe_string_wrapper_function__(self.unsanitized ^ other) | |
384 | |
385 def __or__(self, other): | |
386 while isinstance(other, SafeStringWrapper): | |
387 other = other.unsanitized | |
388 return self.__safe_string_wrapper_function__(self.unsanitized | other) | |
389 | |
390 def __div__(self, other): | |
391 while isinstance(other, SafeStringWrapper): | |
392 other = other.unsanitized | |
393 return self.__safe_string_wrapper_function__(self.unsanitized / other) | |
394 | |
395 def __truediv__(self, other): | |
396 while isinstance(other, SafeStringWrapper): | |
397 other = other.unsanitized | |
398 return self.__safe_string_wrapper_function__(self.unsanitized / other) | |
399 | |
400 # The only reflected operand that we will define is __rpow__, due to coercion rules complications as per docs | |
401 def __rpow__(self, other): | |
402 while isinstance(other, SafeStringWrapper): | |
403 other = other.unsanitized | |
404 return self.__safe_string_wrapper_function__(pow(other, self.unsanitized)) | |
405 | |
406 # Do not implement in-place operands | |
407 | |
408 def __neg__(self): | |
409 return self.__safe_string_wrapper_function__(-self.unsanitized) | |
410 | |
411 def __pos__(self): | |
412 return self.__safe_string_wrapper_function__(+self.unsanitized) | |
413 | |
414 def __abs__(self): | |
415 return self.__safe_string_wrapper_function__(abs(self.unsanitized)) | |
416 | |
417 def __invert__(self): | |
418 return self.__safe_string_wrapper_function__(~self.unsanitized) | |
419 | |
420 def __complex__(self): | |
421 return self.__safe_string_wrapper_function__(complex(self.unsanitized)) | |
422 | |
423 def __int__(self): | |
424 return int(self.unsanitized) | |
425 | |
426 def __float__(self): | |
427 return float(self.unsanitized) | |
428 | |
429 def __oct__(self): | |
430 return oct(self.unsanitized) | |
431 | |
432 def __hex__(self): | |
433 return hex(self.unsanitized) | |
434 | |
435 def __index__(self): | |
436 return self.unsanitized.index() | |
437 | |
438 def __coerce__(self, other): | |
439 while isinstance(other, SafeStringWrapper): | |
440 other = other.unsanitized | |
441 return coerce(self.unsanitized, other) | |
442 | |
443 def __enter__(self): | |
444 return self.unsanitized.__enter__() | |
445 | |
446 def __exit__(self, *args): | |
447 return self.unsanitized.__exit__(*args) | |
448 | |
449 | |
450 class CallableSafeStringWrapper(SafeStringWrapper): | |
451 | |
452 def __call__(self, *args, **kwds): | |
453 return self.__safe_string_wrapper_function__(self.unsanitized(*args, **kwds)) | |
454 | |
455 | |
456 # Enable pickling/deepcopy | |
457 def pickle_SafeStringWrapper(safe_object): | |
458 args = (safe_object.unsanitized, ) | |
459 cls = SafeStringWrapper | |
460 if isinstance(safe_object, CallableSafeStringWrapper): | |
461 cls = CallableSafeStringWrapper | |
462 return (cls, args) | |
463 | |
464 | |
465 copyreg.pickle(SafeStringWrapper, pickle_SafeStringWrapper, wrap_with_safe_string) | |
466 copyreg.pickle(CallableSafeStringWrapper, pickle_SafeStringWrapper, wrap_with_safe_string) |