added covariant and contravariant from PR #1

This commit is contained in:
Tim Rid 2021-03-13 17:49:03 +01:00
parent 667ee5c7d3
commit d0a31458a8
4 changed files with 45 additions and 11 deletions

View file

@ -84,8 +84,8 @@ def stream_iseof(stream: t.BinaryIO) -> bool: ...
# ===============================================================================
# abstract constructs
# ===============================================================================
ParsedType = t.TypeVar("ParsedType")
BuildTypes = t.TypeVar("BuildTypes")
ParsedType = t.TypeVar("ParsedType", covariant=True)
BuildTypes = t.TypeVar("BuildTypes", contravariant=True)
class Construct(t.Generic[ParsedType, BuildTypes]):
name: t.Optional[str]
@ -151,8 +151,8 @@ ValueType = t.TypeVar("ValueType")
ConstantOrContextLambda = t.Union[ValueType, t.Callable[[Context], t.Any]]
ConstantOrContextLambda2 = t.Union[ValueType, t.Callable[[Context], ValueType]]
SubconParsedType = t.TypeVar("SubconParsedType")
SubconBuildTypes = t.TypeVar("SubconBuildTypes")
SubconParsedType = t.TypeVar("SubconParsedType", covariant=True)
SubconBuildTypes = t.TypeVar("SubconBuildTypes", contravariant=True)
class Subconstruct(
t.Generic[SubconParsedType, SubconBuildTypes, ParsedType, BuildTypes],
@ -547,11 +547,11 @@ class Rebuild(Subconstruct[SubconParsedType, SubconBuildTypes, ParsedType, Build
) -> Rebuild[SubconParsedType, SubconBuildTypes, SubconParsedType, None]: ...
class Default(Subconstruct[SubconParsedType, SubconBuildTypes, ParsedType, BuildTypes]):
value: ConstantOrContextLambda2[SubconBuildTypes]
value: ConstantOrContextLambda[SubconBuildTypes]
def __new__(
cls,
subcon: Construct[SubconParsedType, SubconBuildTypes],
value: ConstantOrContextLambda2[SubconBuildTypes],
value: ConstantOrContextLambda[SubconBuildTypes],
) -> Default[
SubconParsedType,
SubconBuildTypes,

View file

@ -3,10 +3,10 @@ import enum
import textwrap
import typing as t
ParsedType = t.TypeVar("ParsedType")
BuildTypes = t.TypeVar("BuildTypes")
SubconParsedType = t.TypeVar("SubconParsedType")
SubconBuildTypes = t.TypeVar("SubconBuildTypes")
ParsedType = t.TypeVar("ParsedType", covariant=True)
BuildTypes = t.TypeVar("BuildTypes", contravariant=True)
SubconParsedType = t.TypeVar("SubconParsedType", covariant=True)
SubconBuildTypes = t.TypeVar("SubconBuildTypes", contravariant=True)
ListType = t.TypeVar("ListType")
ValueType = t.TypeVar("ValueType")

View file

@ -51,7 +51,7 @@ class TContainerBase(_TContainerBase):
def TStructField(
subcon: Construct[ParsedType, BuildTypes],
subcon: Construct[ParsedType, t.Any],
doc: t.Optional[str] = None,
parsed: t.Optional[t.Callable[[t.Any, Context], None]] = None,
) -> ParsedType:

View file

@ -2227,3 +2227,37 @@ def test_struct_issue_771() -> None:
assert spec.sizeof(**info) == 10
def test_buildtypes_contravariance() -> None:
if t.TYPE_CHECKING:
class HexString(Adapter[bytes, bytes, str, str]): ...
else:
class HexString(Adapter):
def _decode(self, obj, context, path):
return obj.hex()
def _encode(self, obj, context, path):
return bytes.fromhex(obj)
HexStringBytes = HexString(Bytes(2))
assert HexStringBytes.build('1234') == b'\x12\x34'
assert HexStringBytes.parse(b'\x56\x78') == '5678'
# this fails if BuildTypes is not contravariant,
# as GreedyBytes has a BuildType of Union[bytes, int]
HexStringGreedyBytes = HexString(GreedyBytes)
assert HexStringGreedyBytes.build('9abc') == b'\x9a\xbc'
assert HexStringGreedyBytes.parse(b'\xcd\xef') == 'cdef'
def test_parsetype_covariance() -> None:
T = t.TypeVar('T')
if t.TYPE_CHECKING:
class ReversedList(SymmetricAdapter[t.List[T], t.List[T], t.List[T], t.List[T]]): ...
else:
class ReversedList(SymmetricAdapter):
def _decode(self, obj, context, path):
return list(reversed(obj))
assert ReversedList(Array(4, Byte)).build([1, 2, 3, 4]) == b'\x04\x03\x02\x01'
assert ReversedList(Array(4, Byte)).parse(b'\x01\x02\x03\x04') == [4, 3, 2, 1]