Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: return the correct size for custom container objects #547

Merged
merged 2 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/safeds/data/image/containers/_image.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import sys
import io
import warnings
from pathlib import Path
Expand Down Expand Up @@ -108,6 +109,16 @@ def __eq__(self, other: object) -> bool:
and torch.all(torch.eq(self._image_tensor, other._set_device(self.device)._image_tensor)).item()
)

def __sizeof__(self) -> int:
"""
Return the complete size of this object.

Returns
-------
Size of this object in bytes.
"""
return sys.getsizeof(self._image_tensor) + self._image_tensor.element_size() * self._image_tensor.nelement()

def _repr_jpeg_(self) -> bytes | None:
"""
Return a JPEG image as bytes.
Expand Down
11 changes: 11 additions & 0 deletions src/safeds/data/tabular/containers/_column.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import sys
import io
from collections.abc import Sequence
from numbers import Number
Expand Down Expand Up @@ -247,6 +248,16 @@ def __repr__(self) -> str:
"""
return f"Column({self._name!r}, {list(self._data)!r})"

def __sizeof__(self) -> int:
"""
Return the complete size of this object.

Returns
-------
Size of this object in bytes.
"""
return sys.getsizeof(self._data) + sys.getsizeof(self._name) + sys.getsizeof(self._type)

def __str__(self) -> str:
"""
Return a user-friendly string representation of this column.
Expand Down
11 changes: 11 additions & 0 deletions src/safeds/data/tabular/containers/_row.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import sys
import functools
from collections.abc import Callable, Mapping
from typing import TYPE_CHECKING, Any
Expand Down Expand Up @@ -269,6 +270,16 @@ def __repr__(self) -> str:
"""
return f"Row({self!s})"

def __sizeof__(self) -> int:
"""
Return the complete size of this object.

Returns
-------
Size of this object in bytes.
"""
return sys.getsizeof(self._data) + sys.getsizeof(self._schema)

def __str__(self) -> str:
"""
Return a user-friendly string representation of this row.
Expand Down
11 changes: 11 additions & 0 deletions src/safeds/data/tabular/containers/_table.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import sys
import functools
import io
import warnings
Expand Down Expand Up @@ -475,6 +476,16 @@ def __repr__(self) -> str:
tmp.columns = self.column_names
return tmp.__repr__()

def __sizeof__(self) -> int:
"""
Return the complete size of this object.

Returns
-------
Size of this object in bytes.
"""
return sys.getsizeof(self._data) + sys.getsizeof(self._schema)

def __str__(self) -> str:
tmp = self._data.reset_index(drop=True)
tmp.columns = self.column_names
Expand Down
11 changes: 11 additions & 0 deletions src/safeds/data/tabular/containers/_tagged_table.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import sys
from typing import TYPE_CHECKING

from safeds.data.tabular.containers import Column, Row, Table
Expand Down Expand Up @@ -164,6 +165,16 @@ def __init__(
self._features: Table = _data.keep_only_columns(feature_names)
self._target: Column = _data.get_column(target_name)

def __sizeof__(self) -> int:
"""
Return the complete size of this object.

Returns
-------
Size of this object in bytes.
"""
return Table.__sizeof__(self) + sys.getsizeof(self._features) + sys.getsizeof(self._target)

# ------------------------------------------------------------------------------------------------------------------
# Properties
# ------------------------------------------------------------------------------------------------------------------
Expand Down
11 changes: 11 additions & 0 deletions src/safeds/data/tabular/containers/_time_series.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import sys
from typing import TYPE_CHECKING

from safeds.data.tabular.containers import Column, Row, Table, TaggedTable
Expand Down Expand Up @@ -186,6 +187,16 @@ def __init__(
raise UnknownColumnNameError([time_name])
self._time: Column = _data.get_column(time_name)

def __sizeof__(self) -> int:
"""
Return the complete size of this object.

Returns
-------
Size of this object in bytes.
"""
return TaggedTable.__sizeof__(self) + sys.getsizeof(self._time)

# ------------------------------------------------------------------------------------------------------------------
# Properties
# ------------------------------------------------------------------------------------------------------------------
Expand Down
11 changes: 11 additions & 0 deletions src/safeds/data/tabular/typing/_schema.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import sys
from dataclasses import dataclass
from typing import TYPE_CHECKING

Expand Down Expand Up @@ -100,6 +101,16 @@ def __repr__(self) -> str:
"""
return f"Schema({self!s})"

def __sizeof__(self) -> int:
"""
Return the complete size of this object.

Returns
-------
Size of this object in bytes.
"""
return sum(map(sys.getsizeof, self._schema.keys())) + sum(map(sys.getsizeof, self._schema.values())) + sys.getsizeof(self._schema)

def __str__(self) -> str:
"""
Return a user-friendly string representation of the schema.
Expand Down
14 changes: 14 additions & 0 deletions tests/safeds/data/image/containers/test_image.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import sys
import typing
from pathlib import Path
from tempfile import NamedTemporaryFile
Expand Down Expand Up @@ -900,3 +901,16 @@ def test_should_return_edges_of_image(
image_edges = image.find_edges()
assert image_edges == snapshot_png
_assert_width_height_channel(image, image_edges)


@pytest.mark.parametrize("device", _test_devices(), ids=_test_devices_ids())
class TestSizeof:
@pytest.mark.parametrize(
"resource_path",
_test_images_all(),
ids=_test_images_all_ids(),
)
def test_should_size_be_greater_than_normal_object(self, resource_path: str | Path, device: Device) -> None:
_skip_if_device_not_available(device)
image = Image.from_file(resolve_resource_path(resource_path), device)
assert sys.getsizeof(image) >= image.width * image.height * image.channel
21 changes: 21 additions & 0 deletions tests/safeds/data/tabular/containers/_column/test_sizeof.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import sys

import pytest
from safeds.data.tabular.containers import Column


@pytest.mark.parametrize(
"column",
[
Column("a", []),
Column("a", [0]),
Column("a", [0, "1"]),
],
ids=[
"empty",
"one row",
"multiple rows",
],
)
def test_should_size_be_greater_than_normal_object(column: Column) -> None:
assert sys.getsizeof(column) > sys.getsizeof(object())
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import sys

import pytest
from safeds.data.tabular.containers import Table, TimeSeries


@pytest.mark.parametrize(
"time_series",
[

TimeSeries(
{
"time": [0, 1, 2],
"feature_1": [3, 9, 6],
"feature_2": [6, 12, 9],
"target": [1, 3, 2],
},
"target",
"time",
["feature_1", "feature_2"],
),

TimeSeries(
{
"time": [0, 1, 2],
"feature_1": [3, 9, 6],
"feature_2": [6, 12, 9],
"other": [3, 9, 12],
"target": [1, 3, 2],
},
"target",
"time",
["feature_1", "feature_2"],
),
],
ids=["normal", "table_with_column_as_non_feature"],
)
def test_should_size_be_greater_than_normal_object(time_series: TimeSeries) -> None:
assert sys.getsizeof(time_series) > sys.getsizeof(object())
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import sys

import pytest
from safeds.data.tabular.containers import Table, TaggedTable


@pytest.mark.parametrize(
"tagged_table",
[

TaggedTable(
{
"feature_1": [3, 9, 6],
"feature_2": [6, 12, 9],
"target": [1, 3, 2],
},
"target",
["feature_1", "feature_2"],
),

TaggedTable(
{
"feature_1": [3, 9, 6],
"feature_2": [6, 12, 9],
"other": [3, 9, 12],
"target": [1, 3, 2],
},
"target",
["feature_1", "feature_2"],
),
],
ids=["normal", "table_with_column_as_non_feature"],
)
def test_should_size_be_greater_than_normal_object(tagged_table: TaggedTable) -> None:
assert sys.getsizeof(tagged_table) > sys.getsizeof(object())
21 changes: 21 additions & 0 deletions tests/safeds/data/tabular/containers/_table/test_sizeof.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import sys

import pytest
from safeds.data.tabular.containers import Table


@pytest.mark.parametrize(
"table",
[
Table(),
Table({"col1": [0]}),
Table({"col1": [0, "1"], "col2": ["a", "b"]}),
],
ids=[
"empty table",
"table with one row",
"table with multiple rows",
],
)
def test_should_size_be_greater_than_normal_object(table: Table) -> None:
assert sys.getsizeof(table) > sys.getsizeof(object())
19 changes: 19 additions & 0 deletions tests/safeds/data/tabular/containers/test_row.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import re
import sys
from collections.abc import Callable
from typing import Any

Expand Down Expand Up @@ -552,3 +553,21 @@ def test_should_sort_columns(self, row: Row, comparator: Callable[[tuple, tuple]
def test_should_sort_table_out_of_place(self, row: Row) -> None:
sorted_row = row.sort_columns()
assert sorted_row != row


class TestSizeof:
@pytest.mark.parametrize(
"row",
[
Row(),
Row({"col1": 0}),
Row({"col1": 0, "col2": "a"}),
],
ids=[
"empty",
"single column",
"multiple columns",
],
)
def test_should_size_be_greater_than_normal_object(self, row: Row) -> None:
assert sys.getsizeof(row) > sys.getsizeof(object())
19 changes: 19 additions & 0 deletions tests/safeds/data/tabular/typing/test_schema.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import sys
from typing import TYPE_CHECKING

import pandas as pd
Expand Down Expand Up @@ -495,3 +496,21 @@ class TestReprMarkdown:
)
def test_should_create_a_string_representation(self, schema: Schema, expected: str) -> None:
assert schema._repr_markdown_() == expected


class TestSizeof:
@pytest.mark.parametrize(
"schema",
[
Schema({}),
Schema({"A": Integer()}),
Schema({"A": Integer(), "B": String()}),
],
ids=[
"empty",
"single column",
"multiple columns",
],
)
def test_should_size_be_greater_than_normal_object(self, schema: Schema) -> None:
assert sys.getsizeof(schema) > sys.getsizeof(object())