Skip to content

Commit 4243faa

Browse files
committed
All tests passing
1 parent 3f9dfa8 commit 4243faa

File tree

5 files changed

+194
-112
lines changed

5 files changed

+194
-112
lines changed

pycardano/serialization.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from datetime import datetime
1111
from decimal import Decimal
1212
from functools import wraps
13-
from inspect import isclass
13+
from inspect import getfullargspec, isclass
1414
from typing import (
1515
Any,
1616
Callable,
@@ -385,12 +385,15 @@ def to_validated_primitive(self) -> Primitive:
385385
return self.to_primitive()
386386

387387
@classmethod
388-
def from_primitive(cls: Type[CBORBase], value: Any) -> CBORBase:
388+
def from_primitive(
389+
cls: Type[CBORBase], value: Any, type_args: Optional[tuple] = None
390+
) -> CBORBase:
389391
"""Turn a CBOR primitive to its original class type.
390392
391393
Args:
392394
cls (CBORBase): The original class type.
393395
value (:const:`Primitive`): A CBOR primitive.
396+
type_args (Optional[tuple]): Type arguments for the class.
394397
395398
Returns:
396399
CBORBase: A CBOR serializable object.
@@ -543,7 +546,11 @@ def _restore_typed_primitive(
543546
if t is Any or (t in PRIMITIVE_TYPES and isinstance(v, t)):
544547
return v
545548
elif isclass(t) and issubclass(t, CBORSerializable):
546-
return t.from_primitive(v)
549+
if "type_args" in getfullargspec(t.from_primitive).args:
550+
args = typing.get_args(t)
551+
return t.from_primitive(v, type_args=args)
552+
else:
553+
return t.from_primitive(v)
547554
elif hasattr(t, "__origin__") and (t.__origin__ is list):
548555
t_args = t.__args__
549556
if len(t_args) != 1:
@@ -1001,28 +1008,41 @@ def to_shallow_primitive(self) -> Union[CBORTag, List[T]]:
10011008
return list(self)
10021009

10031010
@classmethod
1004-
def from_primitive(cls: Type[OrderedSet[T]], value: Any) -> OrderedSet[T]:
1011+
def from_primitive(
1012+
cls: OrderedSet[T], value: Any, type_args: Optional[tuple] = None
1013+
) -> OrderedSet[T]:
1014+
assert (
1015+
type_args is None or len(type_args) == 1
1016+
), "OrderedSet should have exactly one type argument"
1017+
# Retrieve the type arguments from the class
1018+
type_arg = type_args[0] if type_args else None
1019+
10051020
if isinstance(value, CBORTag) and value.tag == 258:
1021+
if isclass(type_arg) and issubclass(type_arg, CBORSerializable):
1022+
value.value = [type_arg.from_primitive(v) for v in value.value]
10061023
return cls(value.value, use_tag=True)
1024+
10071025
if isinstance(value, (list, tuple, set)):
1026+
if isclass(type_arg) and issubclass(type_arg, CBORSerializable):
1027+
value = [type_arg.from_primitive(v) for v in value]
10081028
return cls(list(value), use_tag=False)
1029+
10091030
raise ValueError(f"Cannot deserialize {value} to {cls.__name__}")
10101031

10111032

10121033
class NonEmptyOrderedSet(OrderedSet[T]):
10131034
def __init__(self, iterable: Optional[List[T]] = None, use_tag: bool = True):
10141035
super().__init__(iterable, use_tag)
10151036

1016-
def to_shallow_primitive(self) -> Union[CBORTag, List[T]]:
1037+
def validate(self):
10171038
if not self:
10181039
raise ValueError("NonEmptyOrderedSet cannot be empty")
1019-
return super().to_shallow_primitive()
10201040

10211041
@classmethod
10221042
def from_primitive(
1023-
cls: Type[NonEmptyOrderedSet[T]], value: Any
1043+
cls: NonEmptyOrderedSet[T], value: Any, type_args: Optional[tuple] = None
10241044
) -> NonEmptyOrderedSet[T]:
1025-
result = cast(NonEmptyOrderedSet[T], super().from_primitive(value))
1045+
result = cast(NonEmptyOrderedSet[T], super().from_primitive(value, type_args))
10261046
if not result:
10271047
raise ValueError("NonEmptyOrderedSet cannot be empty")
10281048
return result

pycardano/transaction.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ class Withdrawals(DictCBORSerializable):
518518

519519
@dataclass(repr=False)
520520
class TransactionBody(MapCBORSerializable):
521-
inputs: OrderedSet[TransactionInput] = field(
521+
inputs: Union[List[TransactionInput], OrderedSet[TransactionInput]] = field(
522522
default_factory=OrderedSet,
523523
metadata={"key": 0},
524524
)
@@ -562,15 +562,19 @@ class TransactionBody(MapCBORSerializable):
562562
default=None, metadata={"key": 11, "optional": True}
563563
)
564564

565-
collateral: Optional[Union[List[TransactionInput], NonEmptyOrderedSet[TransactionInput]]] = field(
565+
collateral: Optional[
566+
Union[List[TransactionInput], NonEmptyOrderedSet[TransactionInput]]
567+
] = field(
566568
default=None,
567569
metadata={
568570
"key": 13,
569571
"optional": True,
570572
},
571573
)
572574

573-
required_signers: Optional[Union[List[VerificationKeyHash], NonEmptyOrderedSet[VerificationKeyHash]]] = field(
575+
required_signers: Optional[
576+
Union[List[VerificationKeyHash], NonEmptyOrderedSet[VerificationKeyHash]]
577+
] = field(
574578
default=None,
575579
metadata={
576580
"key": 14,
@@ -590,7 +594,9 @@ class TransactionBody(MapCBORSerializable):
590594
default=None, metadata={"key": 17, "optional": True}
591595
)
592596

593-
reference_inputs: Optional[Union[List[TransactionInput], NonEmptyOrderedSet[TransactionInput]]] = field(
597+
reference_inputs: Optional[
598+
Union[List[TransactionInput], NonEmptyOrderedSet[TransactionInput]]
599+
] = field(
594600
default=None,
595601
metadata={
596602
"key": 18,

pycardano/witness.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,19 @@ def from_primitive(
4949

5050
@dataclass(repr=False)
5151
class TransactionWitnessSet(MapCBORSerializable):
52-
vkey_witnesses: Optional[Union[List[VerificationKeyWitness], NonEmptyOrderedSet[VerificationKeyWitness]]] = field(
52+
vkey_witnesses: Optional[
53+
Union[List[VerificationKeyWitness], NonEmptyOrderedSet[VerificationKeyWitness]]
54+
] = field(
5355
default=None,
5456
metadata={
5557
"key": 0,
5658
"optional": True,
5759
},
5860
)
5961

60-
native_scripts: Optional[Union[List[NativeScript], NonEmptyOrderedSet[NativeScript]]] = field(
62+
native_scripts: Optional[
63+
Union[List[NativeScript], NonEmptyOrderedSet[NativeScript]]
64+
] = field(
6165
default=None,
6266
metadata={
6367
"key": 1,
@@ -70,7 +74,9 @@ class TransactionWitnessSet(MapCBORSerializable):
7074
default=None, metadata={"optional": True, "key": 2}
7175
)
7276

73-
plutus_v1_script: Optional[Union[List[PlutusV1Script], NonEmptyOrderedSet[PlutusV1Script]]] = field(
77+
plutus_v1_script: Optional[
78+
Union[List[PlutusV1Script], NonEmptyOrderedSet[PlutusV1Script]]
79+
] = field(
7480
default=None,
7581
metadata={
7682
"key": 3,
@@ -88,15 +94,19 @@ class TransactionWitnessSet(MapCBORSerializable):
8894
metadata={"optional": True, "key": 5},
8995
)
9096

91-
plutus_v2_script: Optional[Union[List[PlutusV2Script], NonEmptyOrderedSet[PlutusV2Script]]] = field(
97+
plutus_v2_script: Optional[
98+
Union[List[PlutusV2Script], NonEmptyOrderedSet[PlutusV2Script]]
99+
] = field(
92100
default=None,
93101
metadata={
94102
"key": 6,
95103
"optional": True,
96104
},
97105
)
98106

99-
plutus_v3_script: Optional[Union[List[PlutusV3Script], NonEmptyOrderedSet[PlutusV3Script]]] = field(
107+
plutus_v3_script: Optional[
108+
Union[List[PlutusV3Script], NonEmptyOrderedSet[PlutusV3Script]]
109+
] = field(
100110
default=None,
101111
metadata={
102112
"key": 7,

test/pycardano/test_serialization.py

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
11
from collections import defaultdict, deque
22
from dataclasses import dataclass, field
33
from test.pycardano.util import check_two_way_cbor
4-
from typing import Any, Deque, Dict, List, Optional, Set, Tuple, Union
4+
from typing import (
5+
Any,
6+
Deque,
7+
Dict,
8+
List,
9+
Optional,
10+
Set,
11+
Tuple,
12+
Type,
13+
Union,
14+
get_args,
15+
get_origin,
16+
)
517

618
import cbor2
719
import pytest
820
from cbor2 import CBORTag
921

1022
from pycardano import (
23+
CBORBase,
1124
Datum,
1225
MultiAsset,
1326
RawPlutusData,
@@ -605,17 +618,32 @@ def test_ordered_set_with_complex_types():
605618
)
606619
witness = VerificationKeyWitness(vkey, sig)
607620

608-
# Create OrderedSet[VerificationKeyWitness]
609-
s = OrderedSet[VerificationKeyWitness]([witness])
610-
assert len(s) == 1
611-
assert witness in s
621+
witness_set = TransactionWitnessSet(
622+
vkey_witnesses=NonEmptyOrderedSet[VerificationKeyWitness]([witness])
623+
)
624+
625+
# # Deserialize an OrderedSet[int]
626+
# data = [1, 2, 3]
627+
# ordered_set = OrderedSet[int].from_primitive(data)
628+
# print(ordered_set) # Output: OrderedSet([1, 2, 3])
629+
#
630+
# # Deserialize an OrderedSet[MyCBORClass]
631+
# class MyCBORClass(ArrayCBORSerializable):
632+
# a: int
633+
#
634+
# @dataclass
635+
# class MyCBORClass2(ArrayCBORSerializable):
636+
# a: OrderedSet[MyCBORClass]
637+
#
638+
#
639+
# data = [{(1,), (2,)}]
640+
# ordered_set = MyCBORClass2.from_primitive(data)
641+
# print(ordered_set) # Output: OrderedSet([MyCBORClass(), MyCBORClass()])
612642

613643
# Test serialization/deserialization
614-
primitive = s.to_primitive()
615-
restored = OrderedSet[VerificationKeyWitness].from_primitive(primitive)
616-
assert restored == s
617-
assert restored[0].vkey == witness.vkey
618-
assert restored[0].signature == witness.signature
644+
primitive = witness_set.to_primitive()
645+
restored = TransactionWitnessSet.from_primitive(primitive)
646+
assert restored == witness_set
619647

620648

621649
def test_non_empty_ordered_set():
@@ -624,11 +652,11 @@ def test_non_empty_ordered_set():
624652
assert list(s) == [1, 2, 3]
625653

626654
# Test validation of non-empty constraint
627-
with pytest.raises(SerializeException, match="cannot be empty"):
655+
with pytest.raises(ValueError, match="NonEmptyOrderedSet cannot be empty"):
628656
s = NonEmptyOrderedSet()
629657
s.to_validated_primitive()
630658

631-
with pytest.raises(SerializeException, match="cannot be empty"):
659+
with pytest.raises(ValueError, match="NonEmptyOrderedSet cannot be empty"):
632660
s = NonEmptyOrderedSet([])
633661
s.to_validated_primitive()
634662

@@ -674,14 +702,16 @@ def test_non_empty_ordered_set_with_complex_types():
674702

675703
# Test serialization/deserialization
676704
primitive = s.to_primitive()
677-
restored = NonEmptyOrderedSet[VerificationKeyWitness].from_primitive(primitive)
705+
restored = NonEmptyOrderedSet[VerificationKeyWitness].from_primitive(
706+
primitive, type_args=(VerificationKeyWitness,)
707+
)
678708
assert restored == s
679709
assert restored[0].vkey == witness.vkey
680710
assert restored[0].signature == witness.signature
681711

682712
# Test empty set validation
683713
s = NonEmptyOrderedSet[VerificationKeyWitness]()
684-
with pytest.raises(SerializeException, match="cannot be empty"):
714+
with pytest.raises(ValueError, match="NonEmptyOrderedSet cannot be empty"):
685715
s.to_validated_primitive()
686716

687717

@@ -710,7 +740,7 @@ def test_transaction_witness_set_with_ordered_sets():
710740

711741
# Test empty list conversion
712742
witness_set = TransactionWitnessSet(vkey_witnesses=[])
713-
with pytest.raises(SerializeException, match="cannot be empty"):
743+
with pytest.raises(ValueError, match="NonEmptyOrderedSet cannot be empty"):
714744
witness_set.to_validated_primitive()
715745

716746
# Test None value

0 commit comments

Comments
 (0)