-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Alternate concept for VirtualMux #187
Comments
S = TypeVar("S")
class VirtualMux(Generic[S]):
def __init__(self):
self._signal_map: dict[Signal, set[Pin]] = {}
def __call__(self, signal: S, trigger_update: bool=False) -> None:
self.multiplex(signal, trigger_update)
def multiplex(self, signal: S, trigger_update: bool=False) -> None:
print(self._signal_map[signal])
MuxOneSigDef = Union[
Annotated[Literal["sig1"], ("x0",)],
Annotated[Literal["sig2"], ("x1",)],
Annotated[Literal["sig3"], ("x0", "x1")],
]
class MuxOne(VirtualMux[MuxOneSigDef]):
pass |
Possible way to extend this idea to "Tree" mux definitions. Unravelling this in code to build the signal map would be a bit of a mess, but I think it captures all the required info. There is one weird thing. I'm stealing the For each instance of
MuxC = Annotated[Literal["c0", "c1", "c2"], ("x4", "x5")]
MuxB = Annotated[Literal["b0", "b1", "", "b3"], ("x2", "x3"), (None,)] # None corresponds to an unused signal, marked by ""
MuxA = Annotated[Literal["a0", "", "a2", ""], ("x0", "x1"), (MuxB, MuxC)] # MuxB & MuxC connect to the entries marked by ""
MuxTreeDef = Union[MuxA, MuxB, MuxC]
class TreeDefinedMux[MuxTreeDef]:
pass |
Need to check if this has an impact on that tree definition idea, because it is relying on ordering
|
def _map_signals(self) -> SignalMap:
"""
Default implementation of the signal mapping
We need to construct a dictionary mapping signals to a set of pins.
In the case that self.map_list is set, the is pretty trival.
If the mux is defined with self.map_tree we have more work to
do, which is recursively delegated to _map_tree
Avoid subclassing. Consider creating helper functions to build
map_tree or map_list.
"""
if hasattr(self, "map_tree"):
return self._map_tree(self.map_tree, self.pin_list, fixed_pins=frozenset())
elif hasattr(self, "map_list"):
return {sig: frozenset(pins) for sig, *pins in self.map_list}
elif (self.__orig_bases__ != VirtualMux.__orig_bases__):
# the user has provided map_list using annotations
# if the type annotations have not been supplied, then self.__orig_bases__ == VirtualMux.__orig_bases__
# if they have been overridden, then self.__orig_bases__ != VirtualMux.__orig_bases__
# works in 3.8 and 3.12
# this will only work if VirtualMux was subclassed
# if creating an instance using VirtualMux[type]() then __orig_bases__ does not exist
# there seems to be some
# we are expecting exactly 1 value, unpack it
cls, = self.__orig_bases__
assert get_origin(cls) == VirtualMux
lst, = get_args(cls)
assert get_origin(lst) == Union
signals = get_args(lst)
sigmap = {}
for s in signals:
if not hasattr(s, "__metadata__"):
raise ValueError("VirtualMux Subclass must define the pins for each signal")
# s.__metadata__ is our pin list
pins = s.__metadata__
# get_args gives Literal
literal, = get_args(s)
# get_args gives members of Literal
signame, = get_args(literal)
sigmap[signame] = frozenset(pins)
return sigmap
else:
raise ValueError(
"VirtualMux subclass must define either map_tree or map_list or provide a type to VirtualMux"
) This nearly gets there. It'd be nice if we could do something like MuxOne = VirtualMux[MuxOneSigDef]
@dataclass
class JigMuxGroup(MuxGroup):
mux_one: MuxOne = field(default_factory=MuxOne) I think that would get rid of my weird feelings about the way we subclass a thing to do basically nothing. But I get that this whole idea is bastardising the crap out of this system anyway. The |
def __class_getitem__(cls, *args, **kwargs):
# without calling getitem the class doesn't work as a generic
getitm = super().__class_getitem__(*args, **kwargs) # normally returns a generic
# create a proxy to force the __init_subclass__ hook
class Hack(getitm):
...
return Hack # now the actual class can be initialised Forgive me for I have sinned. |
Looks like they changed how Annotated behaves with |
Made a PR for people to look at and play around with. |
This came about while thinking of other ways to create a VirtualMux that will be type hint and IDE friendly. Originally a comment on #184 but I think it is better to have an issue separate from that so it doesn't get lost.
Each signal would be a method on the resulting VirtualMux subclass. This would be a big change for existing scripts. But once the jig refactor PR lands, we could introduce something like this and scripts could choose which style to use. For new scripts, the IDE autocomplete benefits of this approach would be nice to have. (There might be a way to type-hint the current VirtualMux, but I haven't figured it out)
Implementation - Maybe? very not tested... Most likely bugs, but I think the concept is O.K. I think we would end up with signal returning a class instead of a function, but this maps out the general idea.
The text was updated successfully, but these errors were encountered: