comparison env/lib/python3.7/site-packages/galaxy/util/object_wrapper.py @ 2:6af9afd405e9 draft

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