safe.py 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import collections
  2. import json
  3. from django.utils.encoding import force_str
  4. from sentry.utils.strings import truncatechars
  5. SENTRY_MAX_VARIABLE_SIZE = 512
  6. def get_path(data, *path, **kwargs):
  7. """
  8. Safely resolves data from a recursive data structure. A value is only
  9. returned if the full path exists, otherwise ``None`` is returned.
  10. If the ``default`` argument is specified, it is returned instead of ``None``.
  11. If the ``filter`` argument is specified and the value is a list, it is
  12. filtered with the given callback. Alternatively, pass ``True`` as filter to
  13. only filter ``None`` values.
  14. """
  15. default = kwargs.pop("default", None)
  16. f = kwargs.pop("filter", None)
  17. for k in kwargs:
  18. raise TypeError("set_path() got an undefined keyword argument '%s'" % k)
  19. for p in path:
  20. if isinstance(data, collections.abc.Mapping) and p in data:
  21. data = data[p]
  22. elif isinstance(data, (list, tuple)) and -len(data) <= p < len(data):
  23. data = data[p]
  24. else:
  25. return default
  26. if f and data and isinstance(data, (list, tuple)):
  27. data = list(filter((lambda x: x is not None) if f is True else f, data))
  28. return data if data is not None else default
  29. def trim(
  30. value,
  31. max_size=SENTRY_MAX_VARIABLE_SIZE,
  32. max_depth=6,
  33. object_hook=None,
  34. _depth=0,
  35. _size=0,
  36. **kwargs
  37. ):
  38. """
  39. Truncates a value to ```MAX_VARIABLE_SIZE```.
  40. The method of truncation depends on the type of value.
  41. """
  42. options = {
  43. "max_depth": max_depth,
  44. "max_size": max_size,
  45. "object_hook": object_hook,
  46. "_depth": _depth + 1,
  47. }
  48. if _depth > max_depth:
  49. if not isinstance(value, str):
  50. value = json.dumps(value)
  51. return trim(value, _size=_size, max_size=max_size)
  52. elif isinstance(value, dict):
  53. result = {}
  54. _size += 2
  55. for k in sorted(value.keys()):
  56. v = value[k]
  57. trim_v = trim(v, _size=_size, **options)
  58. result[k] = trim_v
  59. _size += len(force_str(trim_v)) + 1
  60. if _size >= max_size:
  61. break
  62. elif isinstance(value, (list, tuple)):
  63. result = []
  64. _size += 2
  65. for v in value:
  66. trim_v = trim(v, _size=_size, **options)
  67. result.append(trim_v)
  68. _size += len(force_str(trim_v))
  69. if _size >= max_size:
  70. break
  71. if isinstance(value, tuple):
  72. result = tuple(result)
  73. elif isinstance(value, str):
  74. result = truncatechars(value, max_size - _size)
  75. else:
  76. result = value
  77. if object_hook is None:
  78. return result
  79. return object_hook(result)