diff --git a/CHANGELOG.md b/CHANGELOG.md index 08b81b0a00ff8a926e28c9faa5c0635d1fce91dc..db97fa89a2f0f0dbb000747a9a4df65557030ec4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,3 +2,4 @@ * fixed #49 * fixed #48 +* added get_size diff --git a/docs/instrumentation/memory.rst b/docs/instrumentation/memory.rst index 6bca500f6723b4d5b208c7fe0f5e294673aae10d..3a0e8f88d6d08a27ff6fb6dc010da610bf50bb29 100644 --- a/docs/instrumentation/memory.rst +++ b/docs/instrumentation/memory.rst @@ -8,6 +8,8 @@ be recomputed later. Problem is, that they need a trigger to do it. Memory pressure management from Satella solves that problem. +.. autofunction:: satella.instrumentation.memory.get_size + Defining severity levels ------------------------ diff --git a/satella/__init__.py b/satella/__init__.py index d809dae6d452ea3dc940523aabcf02ae8fecd277..fd902b6a6a76c5aadfb6a74ae335ceffa9dfb548 100644 --- a/satella/__init__.py +++ b/satella/__init__.py @@ -1 +1 @@ -__version__ = '2.17.19a3' +__version__ = '2.17.19' diff --git a/satella/instrumentation/memory/__init__.py b/satella/instrumentation/memory/__init__.py index 8027fa49e76a3c00fd30c7b006c6ad27d0a27d9c..7d01e97028724fb5a316b91635b891c88df3d661 100644 --- a/satella/instrumentation/memory/__init__.py +++ b/satella/instrumentation/memory/__init__.py @@ -3,8 +3,9 @@ from .conditions import Any, All, GlobalRelativeValue, GlobalAbsoluteValue, Loca from .default import install_force_gc_collect from .memthread import MemoryPressureManager from .dump_frames_on import dump_memory_on, install_dump_memory_on +from .get_object_size import get_size __all__ = ['Any', 'All', 'MemoryPressureManager', 'GlobalAbsoluteValue', 'GB', 'GlobalRelativeValue', 'LocalRelativeValue', 'LocalAbsoluteValue', 'MB', 'KB', 'CustomCondition', 'Not', 'install_force_gc_collect', - 'dump_memory_on', 'install_dump_memory_on'] + 'dump_memory_on', 'install_dump_memory_on', 'get_size'] diff --git a/satella/instrumentation/memory/get_object_size.py b/satella/instrumentation/memory/get_object_size.py new file mode 100644 index 0000000000000000000000000000000000000000..fae6a17950ac91cded03c2ca9731ad5fa669cd06 --- /dev/null +++ b/satella/instrumentation/memory/get_object_size.py @@ -0,0 +1,29 @@ +import sys + + +# shamelessly stolen from +# https://goshippo.com/blog/measure-real-size-any-python-object/ +def get_size(obj, seen=None) -> int: + """ + Recursively finds size of objects + + :param obj: object to measure + :return: size in bytes of the object and all of it's subcomponents + """ + size = sys.getsizeof(obj) + if seen is None: + seen = set() + obj_id = id(obj) + if obj_id in seen: + return 0 + # Important mark as seen *before* entering recursion to gracefully handle + # self-referential objects + seen.add(obj_id) + if isinstance(obj, dict): + size += sum(get_size(v, seen) for v in obj.values()) + size += sum(get_size(k, seen) for k in obj.keys()) + elif hasattr(obj, '__dict__'): + size += get_size(obj.__dict__, seen) + elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes, bytearray)): + size += sum(get_size(i, seen) for i in obj) + return size diff --git a/tests/test_instrumentation/test_memory.py b/tests/test_instrumentation/test_memory.py index c7fcc8d0b7b4ff826964d699cc0b273e1444e855..3b73a9a81127810afb1500e64b915e394276ba41 100644 --- a/tests/test_instrumentation/test_memory.py +++ b/tests/test_instrumentation/test_memory.py @@ -5,7 +5,7 @@ import sys from satella.instrumentation import install_dump_frames_on from satella.instrumentation.memory import MemoryPressureManager, CustomCondition, All, Any, \ - dump_memory_on + dump_memory_on, get_size import time import unittest logger = logging.getLogger(__name__) @@ -27,6 +27,10 @@ class TestMemory(unittest.TestCase): install_dump_frames_on(signal.SIGUSR1) os.kill(os.getpid(), signal.SIGUSR1) + def test_get_size(self): + a = 'a' * 1024 + self.assertGreaterEqual(get_size(a), 1024) + def test_dump_memory(self): dump_memory_on()