diff --git a/README.md b/README.md
index b851ca9..25493bb 100644
--- a/README.md
+++ b/README.md
@@ -2,13 +2,15 @@
-| | |
-| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Technology | [![Python](https://img.shields.io/badge/Python-3776AB.svg?style=flat&logo=Python&logoColor=white)](https://www.python.org/) [![Ape](https://img.shields.io/badge/Built%20with-Ape-blue.svg)](https://github.com/ApeWorX/ape) [![GitHub Actions](https://img.shields.io/badge/GitHub%20Actions-2088FF.svg?style=flat&logo=GitHub-Actions&logoColor=white)](https://github.com/features/actions) [![Pytest](https://img.shields.io/badge/Pytest-0A9EDC.svg?style=flat&logo=Pytest&logoColor=white)](abcd) |
-| CI/CD | [![Tests](https://github.com/alienrobotninja/bee-py/actions/workflows/tests.yml/badge.svg)](https://github.com/alienrobotninja/bee-py/actions/workflows/tests.yml) [![Labeler](https://github.com/alienrobotninja/bee-py/actions/workflows/labeler.yml/badge.svg)](https://github.com/alienrobotninja/bee-py/actions/workflows/labeler.yml) |
-| Docs | [![Read the Docs](https://img.shields.io/readthedocs/bee-py/latest.svg?label=Read%20the%20Docs)](https://bee-py.readthedocs.io/) |
-| Package | [![PyPI - Version](https://img.shields.io/pypi/v/bee-py.svg)](https://pypi.org/project/bee-py/) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/bee-py)](https://pypi.org/project/bee-py/) [![PyPI - License](https://img.shields.io/pypi/l/bee-py)](https://pypi.org/project/bee-py/) |
-| Meta | [![GitHub license](https://img.shields.io/github/license/alienrobotninja/bee-py?style=flat&color=1573D5)](https://github.com/alienrobotninja/bee-py/blob/main/LICENSE) [![GitHub last commit](https://img.shields.io/github/last-commit/alienrobotninja/bee-py?style=flat&color=1573D5)](https://github.com/alienrobotninja/bee-py/commits/main) [![GitHub commit activity](https://img.shields.io/github/commit-activity/m/alienrobotninja/bee-py?style=flat&color=1573D5)](https://github.com/alienrobotninja/bee-py/graphs/commit-activity) [![GitHub top language](https://img.shields.io/github/languages/top/alienrobotninja/bee-py?style=flat&color=1573D5)](https://github.com/alienrobotninja/bee-py) |
+| Feature | Value |
+| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Technology | [![Python](https://img.shields.io/badge/Python-3776AB.svg?style=flat&logo=Python&logoColor=white)](https://www.python.org/) [![Ape](https://img.shields.io/badge/Built%20with-Ape-blue.svg)](https://github.com/ApeWorX/ape) [![GitHub Actions](https://img.shields.io/badge/GitHub%20Actions-2088FF.svg?style=flat&logo=GitHub-Actions&logoColor=white)](https://github.com/features/actions) [![Pytest](https://img.shields.io/badge/Pytest-0A9EDC.svg?style=flat&logo=Pytest&logoColor=white)](https://github.com/alienrobotninja/bee-py/actions/workflows/tests.yml/badge.svg) |
+| Linting | [![Code style: black](https://img.shields.io/badge/Code%20Style-black-000000.svg)](https://github.com/psf/black) ![Style Guide](https://img.shields.io/badge/Style%20Guide-Flake8-blue) ![Imports Sorting](https://img.shields.io/badge/Imports%20Sorted-isort-yellow) |
+| Type Checking | [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/) |
+| CI/CD | [![Tests](https://github.com/alienrobotninja/bee-py/actions/workflows/tests.yml/badge.svg)](https://github.com/alienrobotninja/bee-py/actions/workflows/tests.yml) [![Labeler](https://github.com/alienrobotninja/bee-py/actions/workflows/labeler.yml/badge.svg)](https://github.com/alienrobotninja/bee-py/actions/workflows/labeler.yml) [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) |
+| Docs | [![Read the Docs](https://img.shields.io/readthedocs/bee-py/latest.svg?label=Read%20the%20Docs)](https://bee-py.readthedocs.io/) |
+| Package | [![PyPI - Version](https://img.shields.io/pypi/v/bee-py.svg)](https://pypi.org/project/bee-py/) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/bee-py)](https://pypi.org/project/bee-py/) [![PyPI - License](https://img.shields.io/pypi/l/bee-py)](https://pypi.org/project/bee-py/) |
+| Meta | [![GitHub license](https://img.shields.io/github/license/alienrobotninja/bee-py?style=flat&color=1573D5)](https://github.com/alienrobotninja/bee-py/blob/main/LICENSE) [![GitHub last commit](https://img.shields.io/github/last-commit/alienrobotninja/bee-py?style=flat&color=1573D5)](https://github.com/alienrobotninja/bee-py/commits/main) [![GitHub commit activity](https://img.shields.io/github/commit-activity/m/alienrobotninja/bee-py?style=flat&color=1573D5)](https://github.com/alienrobotninja/bee-py/graphs/commit-activity) [![GitHub top language](https://img.shields.io/github/languages/top/alienrobotninja/bee-py?style=flat&color=1573D5)](https://github.com/alienrobotninja/bee-py) |
diff --git a/src/bee_py/feed/feed.py b/src/bee_py/feed/feed.py
index a35a0d3..51af915 100644
--- a/src/bee_py/feed/feed.py
+++ b/src/bee_py/feed/feed.py
@@ -1,37 +1,56 @@
from typing import NewType, Union
-# from bee_py.chunk.serialize import serialize_bytes
+from pydantic import BaseModel
+
+from bee_py.chunk.serialize import serialize_bytes
+from bee_py.chunk.signer import sign
+from bee_py.chunk.soc import download_single_owner_chunk, upload_single_owner_chunk_data
+from bee_py.feed.identifiers import make_feed_identifier
+from bee_py.feed.type import FeedType
+from bee_py.modules.chunk import *
+from bee_py.types.type import ( # Reference,
+ BatchId,
+ BeeRequestOptions,
+ FeedReader,
+ FeedType,
+ FeedUpdateOptions,
+ FeedWriter,
+ JsonFeedOptions,
+ UploadOptions,
+)
+from bee_py.utils.collection import assert_collection, make_collection
+from bee_py.utils.hash import keccak_hash
+from bee_py.utils.hex import bytes_to_hex, hex_to_bytes, make_hex_string
+from bee_py.utils.reference import make_bytes_reference
TIMESTAMP_PAYLOAD_SIZE = 8
TIMESTAMP_PAYLOAD_SIZE_HEX = 16
+REFERENCE_PAYLOAD_OFFSET = TIMESTAMP_PAYLOAD_SIZE
IndexBytes = NewType("IndexBytes", bytes)
-class Epoch:
- def __init__(self, time: int, level: int):
- self.time = time
- self.level = level
-
-
-class Index:
- def __init__(self, value: Union[int, Epoch, bytes, str]):
- self._validate(value)
- self._value = value
-
- def _validate(self, value: Union[int, Epoch, bytes, str]):
- if not (
- isinstance(value, (int, Epoch))
- or (isinstance(value, bytes) and len(value) == TIMESTAMP_PAYLOAD_SIZE)
- or (
- isinstance(value, str)
- and len(value) == TIMESTAMP_PAYLOAD_SIZE_HEX
- and all(c in "0123456789abcdefABCDEF" for c in value)
- )
- ):
- msg = "Index must be an int, Epoch, 8-byte bytes, or a hex string of length 16"
- raise ValueError(msg)
-
- def __str__(self):
- return str(self._value)
+class Epoch(BaseModel):
+ """
+ Epoch model.
+
+ :param time: The time of the epoch.
+ :type time: int
+ :param level: The level of the epoch.
+ :type level: int
+ """
+
+ time: int
+ level: int
+
+
+class Index(BaseModel):
+ """
+ Index model.
+
+ :param index: The index can be a number, an epoch, index bytes or a string.
+ :type index: Union[int, Epoch, bytes, str]
+ """
+
+ index: Union[int, Epoch, bytes, str]
diff --git a/src/bee_py/types/type.py b/src/bee_py/types/type.py
index a546bfa..77dadd7 100644
--- a/src/bee_py/types/type.py
+++ b/src/bee_py/types/type.py
@@ -5,7 +5,7 @@
from ape.managers.accounts import AccountAPI
from ape.types import AddressType
-from pydantic import BaseModel, Field
+from pydantic import BaseModel, Field, validator
from requests import PreparedRequest, Response
from typing_extensions import TypeAlias
@@ -331,18 +331,27 @@ def __str__(self):
return f"ReferenceResponse(reference={self.reference})"
-class Topic:
- def __init__(self, value: str):
- self._validate(value)
- self._value = value
+class Topic(BaseModel):
+ """
+ Represents a topic.
+
+ Attributes:
+ value: The value of the topic.
+ """
+
+ value: str
- def _validate(self, value: str):
- if len(value) != self.TOPIC_HEX_LENGTH or not all(c in "0123456789abcdefABCDEF" for c in value):
- msg = f"Topic must be a hex string of length {self.TOPIC_HEX_LENGTH}"
+ TOPIC_HEX_LENGTH = 32 # define the length of the topic hex string
+
+ @validator("value")
+ def validate_value(cls, v):
+ if len(v) != cls.TOPIC_HEX_LENGTH or not all(c in "0123456789abcdefABCDEF" for c in v):
+ msg = f"Topic must be a hex string of length {cls.TOPIC_HEX_LENGTH}"
raise ValueError(msg)
+ return v
def __str__(self):
- return self._value
+ return self.value
ReferenceOrENS = Union[Reference, str]
@@ -363,17 +372,17 @@ def assert_address(value: Any):
raise ValueError(msg)
-class BeeGenericResponse:
- """Represents a generic response from the Bee API.
+class BeeGenericResponse(BaseModel):
+ """
+ Represents a generic response from the Bee API.
Attributes:
message: The human-readable message associated with the response.
code: The numerical code associated with the response.
"""
- def __init__(self, message: str, code: int):
- self.message = message
- self.code = code
+ message: str
+ code: int
class PeerBalance(BaseModel):
@@ -656,25 +665,21 @@ class FeedType(Enum):
EPOCH = "epoch"
-class FeedUpdateOptions:
+class FeedUpdateOptions(UploadOptions, BaseModel):
"""
Options for updating a feed.
+
+ :param at: The start date as a Unix timestamp.
+ :type at: Optional[int]
+ :param type: The type of the feed (default: 'sequence').
+ :type type: Optional[FeedType]
+ :param index: Fetch a specific previous feed's update (default fetches the latest update).
+ :type index: Optional[str]
"""
- def __init__(self, at: Optional[int] = None, _type: Optional[FeedType] = "sequence", index: Optional[str] = None):
- """
- Constructor for FeedUpdateOptions.
-
- :param at: The start date as a Unix timestamp.
- :type at: Optional[int]
- :param type: The type of the feed (default: 'sequence').
- :_type type: Optional[FeedType]
- :param index: Fetch a specific previous feed's update (default fetches the latest update).
- :type index: Optional[str]
- """
- self.at = at
- self.type = _type
- self.index = index
+ at: Optional[int] = None
+ _type: Optional[FeedType] = "sequence"
+ index: Optional[str] = None
class FeedReader(BaseModel):
@@ -687,8 +692,18 @@ def download(self, options: Optional[FeedUpdateOptions] = None):
pass
-class FeedUploadOptions(UploadOptions, FeedUpdateOptions):
- pass
+class FeedUploadOptions(BaseModel):
+ """
+ Options for uploading a feed.
+
+ :param upload_options: Options for the upload.
+ :type upload_options: UploadOptions
+ :param feed_update_options: Options for updating the feed.
+ :type feed_update_options: FeedUpdateOptions
+ """
+
+ upload_options: UploadOptions
+ feed_update_options: FeedUpdateOptions
class FeedWriter(FeedReader):
diff --git a/tests/integration/feed/test_json.py b/src/bee_py/utils/reference.py
similarity index 100%
rename from tests/integration/feed/test_json.py
rename to src/bee_py/utils/reference.py
diff --git a/tests/conftest.py b/tests/conftest.py
index 4ed87ae..4591a5c 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -251,14 +251,6 @@ def _method(soc_hash):
return _method
-@pytest.fixture
-def test_create_file(tmp_path):
- d = tmp_path / "sub"
- d.mkdir()
- p = d / "hello.txt"
- p.write_bytes(b"a" * 32)
-
-
@pytest.fixture(scope="session")
def create_fake_file(tmp_path_factory):
# Create a temporary file in the tmp_path directory
diff --git a/tests/unit/feed/test_json.py b/tests/unit/feed/test_json.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/unit/utils/test_type.py b/tests/unit/utils/test_type.py
new file mode 100644
index 0000000..2ab2746
--- /dev/null
+++ b/tests/unit/utils/test_type.py
@@ -0,0 +1,27 @@
+import pytest
+
+
+def is_integer(value):
+ return isinstance(value, int)
+
+
+@pytest.mark.parametrize(
+ "value, expected",
+ [
+ # Wrong values
+ (lambda: {}, False),
+ (5.000000000000001, False),
+ ("False", False),
+ ("True", False),
+ (float("inf"), False),
+ (float("nan"), False),
+ ([1], False),
+ # Correct values
+ (5, True),
+ (0, True),
+ (10, True),
+ (-1, True),
+ ],
+)
+def test_is_integer(value, expected):
+ assert is_integer(value) == expected