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 This work is funded through NLnet under Grant 2019-02-012
8 The reasons why this technique is being deployed is because SimpleHandshake
9 needs to be dynamically replaced at the end-users' choice, without having
10 to duplicate dozens of classes using multiple-inheritanc "Mix-in" techniques.
12 It is however extremely unusual, and has been explicitly limited to this *one*
13 module. DO NOT try to use this technique elsewhere, it is extremely hard to
14 understand (meta-class programming).
18 from abc
import ABCMeta
20 from nmutil
.singlepipe
import SimpleHandshake
21 from nmutil
.singlepipe
import MaskCancellable
25 # with many thanks to jsbueno on stackexchange for this one
26 # https://stackoverflow.com/questions/57273070/
28 # http://lists.libre-riscv.org/pipermail/libre-riscv-dev/2019-July/002259.html
32 recursing
= threading
.local()
33 recursing
.check
= False
34 mlock
= threading
.Lock()
36 def __call__(cls
, *args
, **kw
):
38 if mcls
.recursing
.check
:
39 return super().__call
__(*args
, **kw
)
41 base
= spec
.pipekls
# pick up the dynamic class from PipelineSpec, HERE
43 if (cls
, base
) not in mcls
.registry
:
44 print ("__call__", args
, kw
, cls
, base
,
45 base
.__bases
__, cls
.__bases
__)
46 mcls
.registry
[cls
, base
] = type(
48 (cls
, base
) + cls
.__bases
__[1:],
51 real_cls
= mcls
.registry
[cls
, base
]
54 mcls
.recursing
.check
= True
55 instance
= real_cls
.__class
__.__call
__(real_cls
, *args
, **kw
)
56 mcls
.recursing
.check
= False
60 # Inherit from this class instead of SimpleHandshake (or other ControlBase
61 # derivative), and the metaclass will instead *replace* DynamicPipe -
62 # *at runtime* - with the class that is specified *as a parameter*
65 # as explained in the list posting and in the stackexchange post, this is
66 # needed to avoid a MASSIVE suite of duplicated multiple-inheritance classes
67 # that "Mix in" SimpleHandshake (or other).
69 # unfortunately, composition does not work in this instance
70 # (make an *instance* of SimpleHandshake or other class and pass it in)
71 # due to the multiple level inheritance, and in several places
72 # the inheriting class needs to do some setup that the deriving class
73 # needs in order to function correctly.
75 class DynamicPipe(metaclass
=Meta
):
76 def __init__(self
, *args
):
77 print ("DynamicPipe init", super(), args
)
78 super().__init
__(self
, *args
)
81 # bad hack: the DynamicPipe metaclass ends up creating an __init__ signature
82 # for the dynamically-derived class. luckily, SimpleHandshake only needs
83 # "self" as the 1st argument (it is its own "Stage"). anything else
84 # could hypothetically be passed through the pspec.
85 class SimpleHandshakeRedir(SimpleHandshake
):
86 def __init__(self
, mod
, *args
):
87 print ("redir", mod
, args
)
89 if args
and args
[0].stage
:
91 SimpleHandshake
.__init
__(self
, stage
)
94 class MaskCancellableRedir(MaskCancellable
):
95 def __init__(self
, mod
, *args
):
97 maskwid
= args
[0].maskwid
100 print ("redir mask", mod
, args
, maskwid
)
101 MaskCancellable
.__init
__(self
, stage
, maskwid
)