Skip to content

Commit 51d234c

Browse files
committed
1. add utils.get_size to get size of objects recursively.
1 parent 83421af commit 51d234c

File tree

3 files changed

+70
-3
lines changed

3 files changed

+70
-3
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
### 1.2.1 (2025-03-03)
2+
1. add `utils.get_size` to get size of objects recursively.
3+
2.
4+
5+
16
### 1.2.0 (2025-02-16)
27
1. add alias for AsyncQueueListener: `functools.async_logger`
38

morebuiltins/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "1.1.9"
1+
__version__ = "1.2.1"
22
__all__ = [
33
"morebuiltins.utils",
44
"morebuiltins.date",

morebuiltins/utils.py

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import asyncio
22
import atexit
33
import base64
4+
import collections.abc
45
import gzip
56
import hashlib
67
import inspect
@@ -12,6 +13,7 @@
1213
import sys
1314
import tempfile
1415
import traceback
16+
import types
1517
from collections import UserDict
1618
from enum import IntEnum
1719
from functools import wraps
@@ -32,7 +34,6 @@
3234
Sequence,
3335
Tuple,
3436
Type,
35-
TypedDict,
3637
Union,
3738
)
3839

@@ -68,6 +69,9 @@
6869
"PathLock",
6970
"i2b",
7071
"b2i",
72+
"get_hash_int",
73+
"iter_weights",
74+
"get_size",
7175
]
7276

7377

@@ -743,7 +747,7 @@ def iter_weights(
743747
yield item
744748

745749

746-
def default_dict(typeddict_class: dict, **kwargs):
750+
def default_dict(typeddict_class: Type[dict], **kwargs):
747751
"""Initializes a dictionary with default zero values based on a subclass of TypedDict.
748752
749753
>>> class Demo(dict):
@@ -1704,6 +1708,64 @@ async def __aexit__(self, *_):
17041708
self.lock.release()
17051709

17061710

1711+
def get_size(obj, seen=None, iterate_unsafe=False) -> int:
1712+
"""Recursively get size of objects.
1713+
1714+
Args:
1715+
obj: object of any type
1716+
seen (set): set of ids of objects already seen
1717+
iterate_unsafe (bool, optional): whether to iterate through generators/iterators. Defaults to False.
1718+
1719+
Returns:
1720+
int: size of object in bytes
1721+
1722+
Examples:
1723+
>>> get_size("") > 0
1724+
True
1725+
>>> get_size([]) > 0
1726+
True
1727+
>>> def gen():
1728+
... for i in range(10):
1729+
... yield i
1730+
>>> g = gen()
1731+
>>> get_size(g) > 0
1732+
True
1733+
>>> next(g)
1734+
0
1735+
>>> get_size(g, iterate_unsafe=True) > 0
1736+
True
1737+
>>> try:
1738+
... next(g)
1739+
... except StopIteration:
1740+
... "StopIteration"
1741+
'StopIteration'
1742+
"""
1743+
size = sys.getsizeof(obj)
1744+
if seen is None:
1745+
seen = set()
1746+
obj_id = id(obj)
1747+
if obj_id in seen:
1748+
return 0
1749+
seen.add(obj_id)
1750+
if isinstance(obj, (str, bytes, bytearray)):
1751+
pass
1752+
elif isinstance(obj, dict):
1753+
size += sum([get_size(v, seen, iterate_unsafe) for v in obj.values()])
1754+
size += sum([get_size(k, seen, iterate_unsafe) for k in obj.keys()])
1755+
elif hasattr(obj, "__dict__"):
1756+
size += get_size(obj.__dict__, seen, iterate_unsafe)
1757+
elif isinstance(obj, types.GeneratorType) or isinstance(
1758+
obj, collections.abc.Iterator
1759+
):
1760+
if iterate_unsafe:
1761+
# Warning: this will consume the generator/iterator
1762+
size += sum([get_size(i, seen, iterate_unsafe) for i in obj])
1763+
elif hasattr(obj, "__iter__"):
1764+
# Safe to iterate through containers like lists, tuples, sets, etc.
1765+
size += sum([get_size(i, seen, iterate_unsafe) for i in obj])
1766+
return size
1767+
1768+
17071769
if __name__ == "__main__":
17081770
__name__ = "morebuiltins.utils"
17091771
import doctest

0 commit comments

Comments
 (0)