objsize


Nameobjsize JSON
Version 0.7.0 PyPI version JSON
download
home_page
SummaryTraversal over Python's objects subtree and calculate the total size of the subtree in bytes (deep size).
upload_time2023-07-03 22:47:09
maintainer
docs_urlNone
author
requires_python>=3.7
licenseBSD-3-Clause
keywords object-size recursive deep traversal object size debug deep-object-size
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # objsize

[![Coverage Status](https://coveralls.io/repos/github/liran-funaro/objsize/badge.svg?branch=master)](https://coveralls.io/github/liran-funaro/objsize?branch=master) [![Downloads](https://static.pepy.tech/badge/objsize)](https://pepy.tech/project/objsize)

The `objsize` Python package allows for the exploration and
measurement of an object’s complete memory usage in bytes, including its
child objects. This process, often referred to as deep size calculation,
is achieved through Python’s internal Garbage Collection (GC) mechanism.

The `objsize` package is designed to ignore shared objects, such as
`None`, types, modules, classes, functions, and lambdas, because they
are shared across many instances. One of the key performance features of
`objsize` is that it avoids recursive calls, ensuring a faster and
safer execution.

## Key Features

* Traverse objects’ subtree
* Calculates the size of objects, including nested objects (deep size), in bytes
* Exclude non-exclusive objects
* Exclude specified objects subtree
* Provides flexibility by allowing users to define custom handlers for:
  - Object’s size calculation
  - Object’s referents (i.e., its children)
  - Object filter (skip specific objects)

## Documentation

| [`objsize`](https://liran-funaro.github.io/objsize/library/objsize.html#module-objsize)   | Traversal over Python's objects subtree and calculating the total size of the subtree (deep size).   |
|-------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|

# Install

```bash
pip install objsize==0.7.0
```

# Basic Usage

Calculate the size of the object including all its members in bytes.

```pycon
>>> import objsize
>>> objsize.get_deep_size(dict(arg1='hello', arg2='world'))
340
```

It is possible to calculate the deep size of multiple objects by passing
multiple arguments:

```pycon
>>> objsize.get_deep_size(['hello', 'world'], dict(arg1='hello', arg2='world'), {'hello', 'world'})
628
```

# Complex Data

`objsize` can calculate the size of an object’s entire subtree in
bytes regardless of the type of objects in it, and its depth.

Here is a complex data structure, for example, that include a self
reference:

```python
my_data = list(range(3)), list(range(3, 6))

class MyClass:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.d = {'x': x, 'y': y, 'self': self}

    def __repr__(self):
        return f"{self.__class__.__name__}()"

my_obj = MyClass(*my_data)
```

We can calculate `my_obj` deep size, including its stored data.

```pycon
>>> objsize.get_deep_size(my_obj)
724
```

We might want to ignore non-exclusive objects such as the ones stored in
`my_data`.

```pycon
>>> objsize.get_deep_size(my_obj, exclude=[my_data])
384
```

Or simply let `objsize` detect that automatically:

```pycon
>>> objsize.get_exclusive_deep_size(my_obj)
384
```

# Non Shared Functions or Classes

`objsize` filters functions, lambdas, and classes by default since
they are usually shared among many objects. For example:

```pycon
>>> method_dict = {"identity": lambda x: x, "double": lambda x: x*2}
>>> objsize.get_deep_size(method_dict)
232
```

Some objects, however, as illustrated in the above example, have unique
functions not shared by other objects. Due to this, it may be useful to
count their sizes. You can achieve this by providing an alternative
filter function.

```pycon
>>> objsize.get_deep_size(method_dict, filter_func=objsize.shared_object_filter)
986
```

Notes:

* The default filter function is
  [`objsize.traverse.shared_object_or_function_filter()`](https://liran-funaro.github.io/objsize/library/objsize.traverse.html#objsize.traverse.shared_object_or_function_filter).
* When using [`objsize.traverse.shared_object_filter()`](https://liran-funaro.github.io/objsize/library/objsize.traverse.html#objsize.traverse.shared_object_filter), shared functions and
  lambdas are also counted, but builtin functions are still excluded.

# Special Cases

Some objects handle their data in a way that prevents Python’s GC from
detecting it. The user can supply a special way to calculate the actual
size of these objects.

## Case 1: [`torch`](https://pytorch.org/docs/stable/torch.html#module-torch)

Using a simple calculation of the object size won’t work for
[`torch.Tensor`](https://pytorch.org/docs/stable/tensors.html#torch.Tensor).

```pycon
>>> import torch
>>> objsize.get_deep_size(torch.rand(200))
72
```

So the user can define its own size calculation handler for such cases:

```python
import objsize
import sys
import torch

def get_size_of_torch(o):
    # `objsize.safe_is_instance` catches `ReferenceError` caused by `weakref` objects
    if objsize.safe_is_instance(o, torch.Tensor):
        return sys.getsizeof(o) + (o.element_size() * o.nelement())
    else:
        return sys.getsizeof(o)
```

Then use it as follows:

```pycon
>>> objsize.get_deep_size(
...   torch.rand(200),
...   get_size_func=get_size_of_torch
... )
872
```

The above approach may neglect the object’s internal structure. The user
can help `objsize` to find the object’s hidden storage by supplying it
with its own referent and filter functions:

```python
import objsize
import gc
import torch

def get_referents_torch(*objs):
    # Yield all native referents
    yield from gc.get_referents(*objs)
    for o in objs:
        # If the object is a torch tensor, then also yield its storage
        if type(o) == torch.Tensor:
            yield o.untyped_storage()

# `torch.dtype` is a common object like Python's types.
MySharedObjects = (*objsize.SharedObjectOrFunctionType, torch.dtype)

def filter_func(o):
    return not objsize.safe_is_instance(o, MySharedObjects)
```

Then use these as follows:

```pycon
>>> objsize.get_deep_size(
...   torch.rand(200),
...   get_referents_func=get_referents_torch,
...   filter_func=filter_func
... )
928
```

## Case 2: [`weakref`](https://docs.python.org/3/library/weakref.html#module-weakref)

Using a simple calculation of the object size won’t work for
`weakref.proxy`.

```pycon
>>> from collections import UserList
>>> o = UserList([0]*100)
>>> objsize.get_deep_size(o)
1032
>>> import weakref
>>> o_ref = weakref.proxy(o)
>>> objsize.get_deep_size(o_ref)
72
```

To mitigate this, you can provide a method that attempts to fetch the
proxy’s referents:

```python
import weakref
import gc

def get_weakref_referents(*objs):
    yield from gc.get_referents(*objs)
    for o in objs:
        if type(o) in weakref.ProxyTypes:
            try:
                yield o.__repr__.__self__
            except ReferenceError:
                pass
```

Then use it as follows:

```pycon
>>> objsize.get_deep_size(o_ref, get_referents_func=get_weakref_referents)
1104
```

After the referenced object will be collected, then the size of the
proxy object will be reduced.

```pycon
>>> del o
>>> gc.collect()
>>> # Wait for the object to be collected
>>> objsize.get_deep_size(o_ref, get_referents_func=get_weakref_referents)
72
```

# Object Size Settings

To avoid repeating the input settings when handling the special cases
above, you can use the [`ObjSizeSettings`](https://liran-funaro.github.io/objsize/library/objsize.traverse.html#objsize.traverse.ObjSizeSettings) class.

```pycon
>>> torch_objsize = objsize.ObjSizeSettings(
...   get_referents_func=get_referents_torch,
...   filter_func=filter_func,
... )
>>> torch_objsize.get_deep_size(torch.rand(200))
928
>>> torch_objsize.get_deep_size(torch.rand(300))
1328
```

See [`ObjSizeSettings`](https://liran-funaro.github.io/objsize/library/objsize.traverse.html#objsize.traverse.ObjSizeSettings) for the
list of configurable parameters.

# Traversal

A user can implement its own function over the entire subtree using the
traversal method, which traverses all the objects in the subtree.

```pycon
>>> for o in objsize.traverse_bfs(my_obj):
...     print(o)
...
MyClass()
{'x': [0, 1, 2], 'y': [3, 4, 5], 'd': {'x': [0, 1, 2], 'y': [3, 4, 5], 'self': MyClass()}}
[0, 1, 2]
[3, 4, 5]
{'x': [0, 1, 2], 'y': [3, 4, 5], 'self': MyClass()}
2
1
0
5
4
3
```

Similar to before, non-exclusive objects can be ignored.

```pycon
>>> for o in objsize.traverse_exclusive_bfs(my_obj):
...     print(o)
...
MyClass()
{'x': [0, 1, 2], 'y': [3, 4, 5], 'd': {'x': [0, 1, 2], 'y': [3, 4, 5], 'self': MyClass()}}
{'x': [0, 1, 2], 'y': [3, 4, 5], 'self': MyClass()}
```

# Alternative

[Pympler](https://pythonhosted.org/Pympler/) also supports
determining an object deep size via `pympler.asizeof()`. There are two
main differences between `objsize` and `pympler`.

1. `objsize` has additional features:
   * Traversing the object subtree: iterating all the object’s
     descendants one by one.
   * Excluding non-exclusive objects. That is, objects that are also
     referenced from somewhere else in the program. This is true for
     calculating the object’s deep size and for traversing its
     descendants.
2. `objsize` has a simple and robust implementation with significantly
   fewer lines of code, compared to `pympler`. The Pympler
   implementation uses recursion, and thus have to use a maximal depth
   argument to avoid reaching Python’s max depth. `objsize`, however,
   uses BFS which is more efficient and simple to follow. Moreover, the
   Pympler implementation carefully takes care of any object type.
   `objsize` archives the same goal with a simple and generic
   implementation, which has fewer lines of code.

# License: BSD-3

Copyright (c) 2006-2023, Liran Funaro.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the
   names of its contributors may be used to endorse or promote products
   derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS”
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "objsize",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "object-size,recursive,deep,traversal,object,size,debug,deep-object-size",
    "author": "",
    "author_email": "Liran Funaro <liran.funaro@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/88/de/0a0a81c4ce02ab3c0dc672619472f3988c2b398b7785e3176c2ae96c2a7d/objsize-0.7.0.tar.gz",
    "platform": null,
    "description": "# objsize\n\n[![Coverage Status](https://coveralls.io/repos/github/liran-funaro/objsize/badge.svg?branch=master)](https://coveralls.io/github/liran-funaro/objsize?branch=master) [![Downloads](https://static.pepy.tech/badge/objsize)](https://pepy.tech/project/objsize)\n\nThe `objsize` Python package allows for the exploration and\nmeasurement of an object\u2019s complete memory usage in bytes, including its\nchild objects. This process, often referred to as deep size calculation,\nis achieved through Python\u2019s internal Garbage Collection (GC) mechanism.\n\nThe `objsize` package is designed to ignore shared objects, such as\n`None`, types, modules, classes, functions, and lambdas, because they\nare shared across many instances. One of the key performance features of\n`objsize` is that it avoids recursive calls, ensuring a faster and\nsafer execution.\n\n## Key Features\n\n* Traverse objects\u2019 subtree\n* Calculates the size of objects, including nested objects (deep size), in bytes\n* Exclude non-exclusive objects\n* Exclude specified objects subtree\n* Provides flexibility by allowing users to define custom handlers for:\n  - Object\u2019s size calculation\n  - Object\u2019s referents (i.e., its children)\n  - Object filter (skip specific objects)\n\n## Documentation\n\n| [`objsize`](https://liran-funaro.github.io/objsize/library/objsize.html#module-objsize)   | Traversal over Python's objects subtree and calculating the total size of the subtree (deep size).   |\n|-------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|\n\n# Install\n\n```bash\npip install objsize==0.7.0\n```\n\n# Basic Usage\n\nCalculate the size of the object including all its members in bytes.\n\n```pycon\n>>> import objsize\n>>> objsize.get_deep_size(dict(arg1='hello', arg2='world'))\n340\n```\n\nIt is possible to calculate the deep size of multiple objects by passing\nmultiple arguments:\n\n```pycon\n>>> objsize.get_deep_size(['hello', 'world'], dict(arg1='hello', arg2='world'), {'hello', 'world'})\n628\n```\n\n# Complex Data\n\n`objsize` can calculate the size of an object\u2019s entire subtree in\nbytes regardless of the type of objects in it, and its depth.\n\nHere is a complex data structure, for example, that include a self\nreference:\n\n```python\nmy_data = list(range(3)), list(range(3, 6))\n\nclass MyClass:\n    def __init__(self, x, y):\n        self.x = x\n        self.y = y\n        self.d = {'x': x, 'y': y, 'self': self}\n\n    def __repr__(self):\n        return f\"{self.__class__.__name__}()\"\n\nmy_obj = MyClass(*my_data)\n```\n\nWe can calculate `my_obj` deep size, including its stored data.\n\n```pycon\n>>> objsize.get_deep_size(my_obj)\n724\n```\n\nWe might want to ignore non-exclusive objects such as the ones stored in\n`my_data`.\n\n```pycon\n>>> objsize.get_deep_size(my_obj, exclude=[my_data])\n384\n```\n\nOr simply let `objsize` detect that automatically:\n\n```pycon\n>>> objsize.get_exclusive_deep_size(my_obj)\n384\n```\n\n# Non Shared Functions or Classes\n\n`objsize` filters functions, lambdas, and classes by default since\nthey are usually shared among many objects. For example:\n\n```pycon\n>>> method_dict = {\"identity\": lambda x: x, \"double\": lambda x: x*2}\n>>> objsize.get_deep_size(method_dict)\n232\n```\n\nSome objects, however, as illustrated in the above example, have unique\nfunctions not shared by other objects. Due to this, it may be useful to\ncount their sizes. You can achieve this by providing an alternative\nfilter function.\n\n```pycon\n>>> objsize.get_deep_size(method_dict, filter_func=objsize.shared_object_filter)\n986\n```\n\nNotes:\n\n* The default filter function is\n  [`objsize.traverse.shared_object_or_function_filter()`](https://liran-funaro.github.io/objsize/library/objsize.traverse.html#objsize.traverse.shared_object_or_function_filter).\n* When using [`objsize.traverse.shared_object_filter()`](https://liran-funaro.github.io/objsize/library/objsize.traverse.html#objsize.traverse.shared_object_filter), shared functions and\n  lambdas are also counted, but builtin functions are still excluded.\n\n# Special Cases\n\nSome objects handle their data in a way that prevents Python\u2019s GC from\ndetecting it. The user can supply a special way to calculate the actual\nsize of these objects.\n\n## Case 1: [`torch`](https://pytorch.org/docs/stable/torch.html#module-torch)\n\nUsing a simple calculation of the object size won\u2019t work for\n[`torch.Tensor`](https://pytorch.org/docs/stable/tensors.html#torch.Tensor).\n\n```pycon\n>>> import torch\n>>> objsize.get_deep_size(torch.rand(200))\n72\n```\n\nSo the user can define its own size calculation handler for such cases:\n\n```python\nimport objsize\nimport sys\nimport torch\n\ndef get_size_of_torch(o):\n    # `objsize.safe_is_instance` catches `ReferenceError` caused by `weakref` objects\n    if objsize.safe_is_instance(o, torch.Tensor):\n        return sys.getsizeof(o) + (o.element_size() * o.nelement())\n    else:\n        return sys.getsizeof(o)\n```\n\nThen use it as follows:\n\n```pycon\n>>> objsize.get_deep_size(\n...   torch.rand(200),\n...   get_size_func=get_size_of_torch\n... )\n872\n```\n\nThe above approach may neglect the object\u2019s internal structure. The user\ncan help `objsize` to find the object\u2019s hidden storage by supplying it\nwith its own referent and filter functions:\n\n```python\nimport objsize\nimport gc\nimport torch\n\ndef get_referents_torch(*objs):\n    # Yield all native referents\n    yield from gc.get_referents(*objs)\n    for o in objs:\n        # If the object is a torch tensor, then also yield its storage\n        if type(o) == torch.Tensor:\n            yield o.untyped_storage()\n\n# `torch.dtype` is a common object like Python's types.\nMySharedObjects = (*objsize.SharedObjectOrFunctionType, torch.dtype)\n\ndef filter_func(o):\n    return not objsize.safe_is_instance(o, MySharedObjects)\n```\n\nThen use these as follows:\n\n```pycon\n>>> objsize.get_deep_size(\n...   torch.rand(200),\n...   get_referents_func=get_referents_torch,\n...   filter_func=filter_func\n... )\n928\n```\n\n## Case 2: [`weakref`](https://docs.python.org/3/library/weakref.html#module-weakref)\n\nUsing a simple calculation of the object size won\u2019t work for\n`weakref.proxy`.\n\n```pycon\n>>> from collections import UserList\n>>> o = UserList([0]*100)\n>>> objsize.get_deep_size(o)\n1032\n>>> import weakref\n>>> o_ref = weakref.proxy(o)\n>>> objsize.get_deep_size(o_ref)\n72\n```\n\nTo mitigate this, you can provide a method that attempts to fetch the\nproxy\u2019s referents:\n\n```python\nimport weakref\nimport gc\n\ndef get_weakref_referents(*objs):\n    yield from gc.get_referents(*objs)\n    for o in objs:\n        if type(o) in weakref.ProxyTypes:\n            try:\n                yield o.__repr__.__self__\n            except ReferenceError:\n                pass\n```\n\nThen use it as follows:\n\n```pycon\n>>> objsize.get_deep_size(o_ref, get_referents_func=get_weakref_referents)\n1104\n```\n\nAfter the referenced object will be collected, then the size of the\nproxy object will be reduced.\n\n```pycon\n>>> del o\n>>> gc.collect()\n>>> # Wait for the object to be collected\n>>> objsize.get_deep_size(o_ref, get_referents_func=get_weakref_referents)\n72\n```\n\n# Object Size Settings\n\nTo avoid repeating the input settings when handling the special cases\nabove, you can use the [`ObjSizeSettings`](https://liran-funaro.github.io/objsize/library/objsize.traverse.html#objsize.traverse.ObjSizeSettings) class.\n\n```pycon\n>>> torch_objsize = objsize.ObjSizeSettings(\n...   get_referents_func=get_referents_torch,\n...   filter_func=filter_func,\n... )\n>>> torch_objsize.get_deep_size(torch.rand(200))\n928\n>>> torch_objsize.get_deep_size(torch.rand(300))\n1328\n```\n\nSee [`ObjSizeSettings`](https://liran-funaro.github.io/objsize/library/objsize.traverse.html#objsize.traverse.ObjSizeSettings) for the\nlist of configurable parameters.\n\n# Traversal\n\nA user can implement its own function over the entire subtree using the\ntraversal method, which traverses all the objects in the subtree.\n\n```pycon\n>>> for o in objsize.traverse_bfs(my_obj):\n...     print(o)\n...\nMyClass()\n{'x': [0, 1, 2], 'y': [3, 4, 5], 'd': {'x': [0, 1, 2], 'y': [3, 4, 5], 'self': MyClass()}}\n[0, 1, 2]\n[3, 4, 5]\n{'x': [0, 1, 2], 'y': [3, 4, 5], 'self': MyClass()}\n2\n1\n0\n5\n4\n3\n```\n\nSimilar to before, non-exclusive objects can be ignored.\n\n```pycon\n>>> for o in objsize.traverse_exclusive_bfs(my_obj):\n...     print(o)\n...\nMyClass()\n{'x': [0, 1, 2], 'y': [3, 4, 5], 'd': {'x': [0, 1, 2], 'y': [3, 4, 5], 'self': MyClass()}}\n{'x': [0, 1, 2], 'y': [3, 4, 5], 'self': MyClass()}\n```\n\n# Alternative\n\n[Pympler](https://pythonhosted.org/Pympler/) also supports\ndetermining an object deep size via `pympler.asizeof()`. There are two\nmain differences between `objsize` and `pympler`.\n\n1. `objsize` has additional features:\n   * Traversing the object subtree: iterating all the object\u2019s\n     descendants one by one.\n   * Excluding non-exclusive objects. That is, objects that are also\n     referenced from somewhere else in the program. This is true for\n     calculating the object\u2019s deep size and for traversing its\n     descendants.\n2. `objsize` has a simple and robust implementation with significantly\n   fewer lines of code, compared to `pympler`. The Pympler\n   implementation uses recursion, and thus have to use a maximal depth\n   argument to avoid reaching Python\u2019s max depth. `objsize`, however,\n   uses BFS which is more efficient and simple to follow. Moreover, the\n   Pympler implementation carefully takes care of any object type.\n   `objsize` archives the same goal with a simple and generic\n   implementation, which has fewer lines of code.\n\n# License: BSD-3\n\nCopyright (c) 2006-2023, Liran Funaro.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright\n   notice, this list of conditions and the following disclaimer in the\n   documentation and/or other materials provided with the distribution.\n3. Neither the name of the copyright holder nor the\n   names of its contributors may be used to endorse or promote products\n   derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \u201cAS IS\u201d\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n",
    "bugtrack_url": null,
    "license": "BSD-3-Clause",
    "summary": "Traversal over Python's objects subtree and calculate the total size of the subtree in bytes (deep size).",
    "version": "0.7.0",
    "project_urls": {
        "Homepage": "https://github.com/liran-funaro/objsize"
    },
    "split_keywords": [
        "object-size",
        "recursive",
        "deep",
        "traversal",
        "object",
        "size",
        "debug",
        "deep-object-size"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "982600ba2cd7d79935ecefa384020828f3a96c3c9c9b66faf9d93aa16eb75985",
                "md5": "2e6f1311a214ec2544d07c7b81e7123b",
                "sha256": "a8b03ce87477c649a99e6b1920f4eeb8b9ba3f8bc2a94d0e5c06ef68adc334a7"
            },
            "downloads": -1,
            "filename": "objsize-0.7.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "2e6f1311a214ec2544d07c7b81e7123b",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 11231,
            "upload_time": "2023-07-03T22:47:07",
            "upload_time_iso_8601": "2023-07-03T22:47:07.618156Z",
            "url": "https://files.pythonhosted.org/packages/98/26/00ba2cd7d79935ecefa384020828f3a96c3c9c9b66faf9d93aa16eb75985/objsize-0.7.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "88de0a0a81c4ce02ab3c0dc672619472f3988c2b398b7785e3176c2ae96c2a7d",
                "md5": "4852a4de79f4972b8641fda424bb5656",
                "sha256": "d66bbb2a4341803caba84894b5753f9b065ebe1cbf50fd186ae438dfc1ca4729"
            },
            "downloads": -1,
            "filename": "objsize-0.7.0.tar.gz",
            "has_sig": false,
            "md5_digest": "4852a4de79f4972b8641fda424bb5656",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 16062,
            "upload_time": "2023-07-03T22:47:09",
            "upload_time_iso_8601": "2023-07-03T22:47:09.995463Z",
            "url": "https://files.pythonhosted.org/packages/88/de/0a0a81c4ce02ab3c0dc672619472f3988c2b398b7785e3176c2ae96c2a7d/objsize-0.7.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-07-03 22:47:09",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "liran-funaro",
    "github_project": "objsize",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "objsize"
}
        
Elapsed time: 0.08399s