-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adjustments to the Serializable class
- Added a `transform` method for type conversions and more. - Include NBT and Serializable into the Sphinx documentation. - Added more explicit error messages to serializable tests. - Added the possibility to specify the error message/arguments in the tests. - Added pytest to the docs requirements to list the test function in the docs.
- Loading branch information
1 parent
56a896f
commit bbab587
Showing
18 changed files
with
414 additions
and
359 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
- Changed the way `Serializable` classes are handled: | ||
|
||
Here is how a basic `Serializable` class looks like: | ||
```python | ||
@final | ||
@dataclass | ||
class ToyClass(Serializable): | ||
"""Toy class for testing demonstrating the use of gen_serializable_test on `Serializable`.""" | ||
|
||
a: int | ||
b: str | int | ||
|
||
@override | ||
def serialize_to(self, buf: Buffer): | ||
"""Write the object to a buffer.""" | ||
self.b = cast(str, self.b) # Handled by the transform method | ||
buf.write_varint(self.a) | ||
buf.write_utf(self.b) | ||
|
||
@classmethod | ||
@override | ||
def deserialize(cls, buf: Buffer) -> ToyClass: | ||
"""Deserialize the object from a buffer.""" | ||
a = buf.read_varint() | ||
if a == 0: | ||
raise ZeroDivisionError("a must be non-zero") | ||
b = buf.read_utf() | ||
return cls(a, b) | ||
|
||
@override | ||
def validate(self) -> None: | ||
"""Validate the object's attributes.""" | ||
if self.a == 0: | ||
raise ZeroDivisionError("a must be non-zero") | ||
if (isinstance(self.b, int) and math.log10(self.b) > 10) or (isinstance(self.b, str) and len(self.b) > 10): | ||
raise ValueError("b must be less than 10 characters") | ||
|
||
@override | ||
def transform(self) -> None: | ||
"""Apply a transformation to the payload of the object.""" | ||
if isinstance(self.b, int): | ||
self.b = str(self.b) | ||
``` | ||
|
||
|
||
The `Serializable` class implement the following methods: | ||
- `serialize_to(buf: Buffer) -> None`: Serializes the object to a buffer. | ||
- `deserialize(buf: Buffer) -> Serializable`: Deserializes the object from a buffer. | ||
|
||
And the following optional methods: | ||
- `validate() -> None`: Validates the object's attributes, raising an exception if they are invalid. | ||
- `transform() -> None`: Transforms the the object's attributes, this method is meant to convert types like you would in a classic `__init__`. | ||
You can rely on this `validate` having been executed. | ||
|
||
- Added a test generator for `Serializable` classes: | ||
|
||
The `gen_serializable_test` function generates tests for `Serializable` classes. It takes the following arguments: | ||
|
||
- `context`: The dictionary containing the context in which the generated test class will be placed (e.g. `globals()`). | ||
> Dictionary updates must reflect in the context. This is the case for `globals()` but implementation-specific for `locals()`. | ||
- `cls`: The `Serializable` class to generate tests for. | ||
- `fields`: A list of fields where the test values will be placed. | ||
|
||
> In the example above, the `ToyClass` class has two fields: `a` and `b`. | ||
- `test_data`: A list of tuples containing either: | ||
- `((field1_value, field2_value, ...), expected_bytes)`: The values of the fields and the expected serialized bytes. This needs to work both ways, i.e. `cls(field1_value, field2_value, ...) == cls.deserialize(expected_bytes).` | ||
- `((field1_value, field2_value, ...), exception)`: The values of the fields and the expected exception when validating the object. | ||
- `(exception, bytes)`: The expected exception when deserializing the bytes and the bytes to deserialize. | ||
|
||
The `gen_serializable_test` function generates a test class with the following tests: | ||
```python | ||
gen_serializable_test( | ||
context=globals(), | ||
cls=ToyClass, | ||
fields=[("a", int), ("b", str)], | ||
test_data=[ | ||
((1, "hello"), b"\x01\x05hello"), | ||
((2, "world"), b"\x02\x05world"), | ||
((3, 1234567890), b"\x03\x0a1234567890"), | ||
((0, "hello"), ZeroDivisionError("a must be non-zero")), # With an error message | ||
((1, "hello world"), ValueError), # No error message | ||
((1, 12345678900), ValueError), | ||
(ZeroDivisionError, b"\x00"), | ||
(ZeroDivisionError, b"\x01\x05hello"), | ||
(IOError, b"\x01"), | ||
], | ||
) | ||
``` | ||
|
||
The generated test class will have the following tests: | ||
|
||
```python | ||
class TestGenToyClass: | ||
def test_serialization(self): | ||
# 2 subtests for the cases 1 and 2 | ||
|
||
def test_deserialization(self): | ||
# 2 subtests for the cases 1 and 2 | ||
|
||
def test_validation(self): | ||
# 2 subtests for the cases 3 and 4 | ||
|
||
def test_exceptions(self): | ||
# 2 subtests for the cases 5 and 6 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
.. api/types documentation master file | ||
======================= | ||
API Types Documentation | ||
======================= | ||
|
||
Welcome to the API Types documentation! This documentation provides information about the various types used in the API. | ||
|
||
.. toctree:: | ||
:maxdepth: 2 | ||
|
||
nbt.rst |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
NBT Format | ||
========== | ||
|
||
.. automodule:: mcproto.types.nbt | ||
:members: | ||
:show-inheritance: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ Content | |
api/packets.rst | ||
api/protocol.rst | ||
api/internal.rst | ||
api/types/index.rst | ||
|
||
|
||
Indices and tables | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.