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