1 # SPDX-License-Identifier: LGPL-2.1-or-later
2 # See Notices.txt for copyright information
4 """ Meta-class that allows a dynamic runtime parameter-selectable "mixin"
6 The reasons why this technique is being deployed is because SimpleHandshake
7 needs to be dynamically replaced at the end-users' choice, without having
8 to duplicate dozens of classes using multiple-inheritanc "Mix-in" techniques.
10 It is however extremely unusual, and has been explicitly limited to this *one*
11 module. DO NOT try to use this technique elsewhere, it is extremely hard to
12 understand (meta-class programming).
16 from abc
import ABCMeta
18 from nmutil
.singlepipe
import SimpleHandshake
19 from nmutil
.singlepipe
import MaskCancellable
23 # with many thanks to jsbueno on stackexchange for this one
24 # https://stackoverflow.com/questions/57273070/
26 # http://lists.libre-riscv.org/pipermail/libre-riscv-dev/2019-July/002259.html
30 recursing
= threading
.local()
31 recursing
.check
= False
32 mlock
= threading
.Lock()
34 def __call__(cls
, *args
, **kw
):
36 if mcls
.recursing
.check
:
37 return super().__call
__(*args
, **kw
)
39 base
= spec
.pipekls
# pick up the dynamic class from PipelineSpec, HERE
41 if (cls
, base
) not in mcls
.registry
:
42 print ("__call__", args
, kw
, cls
, base
,
43 base
.__bases
__, cls
.__bases
__)
44 mcls
.registry
[cls
, base
] = type(
46 (cls
, base
) + cls
.__bases
__[1:],
49 real_cls
= mcls
.registry
[cls
, base
]
52 mcls
.recursing
.check
= True
53 instance
= real_cls
.__class
__.__call
__(real_cls
, *args
, **kw
)
54 mcls
.recursing
.check
= False
58 # Inherit from this class instead of SimpleHandshake (or other ControlBase
59 # derivative), and the metaclass will instead *replace* DynamicPipe -
60 # *at runtime* - with the class that is specified *as a parameter*
63 # as explained in the list posting and in the stackexchange post, this is
64 # needed to avoid a MASSIVE suite of duplicated multiple-inheritance classes
65 # that "Mix in" SimpleHandshake (or other).
67 # unfortunately, composition does not work in this instance
68 # (make an *instance* of SimpleHandshake or other class and pass it in)
69 # due to the multiple level inheritance, and in several places
70 # the inheriting class needs to do some setup that the deriving class
71 # needs in order to function correctly.
73 class DynamicPipe(metaclass
=Meta
):
74 def __init__(self
, *args
):
75 print ("DynamicPipe init", super(), args
)
76 super().__init
__(self
, *args
)
79 # bad hack: the DynamicPipe metaclass ends up creating an __init__ signature
80 # for the dynamically-derived class. luckily, SimpleHandshake only needs
81 # "self" as the 1st argument (it is its own "Stage"). anything else
82 # could hypothetically be passed through the pspec.
83 class SimpleHandshakeRedir(SimpleHandshake
):
84 def __init__(self
, mod
, *args
):
85 print ("redir", mod
, args
)
87 if args
and args
[0].stage
:
89 SimpleHandshake
.__init
__(self
, stage
)
92 class MaskCancellableRedir(MaskCancellable
):
93 def __init__(self
, mod
, *args
):
95 maskwid
= args
[0].maskwid
98 print ("redir mask", mod
, args
, maskwid
)
99 MaskCancellable
.__init
__(self
, stage
, maskwid
)