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
21 # with many thanks to jsbueno on stackexchange for this one
22 # https://stackoverflow.com/questions/57273070/
24 # http://lists.libre-riscv.org/pipermail/libre-riscv-dev/2019-July/002259.html
28 recursing
= threading
.local()
29 recursing
.check
= False
30 mlock
= threading
.Lock()
32 def __call__(cls
, *args
, **kw
):
34 if mcls
.recursing
.check
:
35 return super().__call
__(*args
, **kw
)
37 base
= spec
.pipekls
# pick up the dynamic class from PipelineSpec, HERE
39 if (cls
, base
) not in mcls
.registry
:
40 print ("__call__", args
, kw
, cls
, base
,
41 base
.__bases
__, cls
.__bases
__)
42 mcls
.registry
[cls
, base
] = type(
44 (cls
, base
) + cls
.__bases
__[1:],
47 real_cls
= mcls
.registry
[cls
, base
]
50 mcls
.recursing
.check
= True
51 instance
= real_cls
.__class
__.__call
__(real_cls
, *args
, **kw
)
52 mcls
.recursing
.check
= False
56 # Inherit from this class instead of SimpleHandshake (or other ControlBase
57 # derivative), and the metaclass will instead *replace* DynamicPipe -
58 # *at runtime* - with the class that is specified *as a parameter*
61 # as explained in the list posting and in the stackexchange post, this is
62 # needed to avoid a MASSIVE suite of duplicated multiple-inheritance classes
63 # that "Mix in" SimpleHandshake (or other).
65 # unfortunately, composition does not work in this instance
66 # (make an *instance* of SimpleHandshake or other class and pass it in)
67 # due to the multiple level inheritance, and in several places
68 # the inheriting class needs to do some setup that the deriving class
69 # needs in order to function correctly.
71 class DynamicPipe(metaclass
=Meta
):
72 def __init__(self
, *args
):
73 print ("DynamicPipe init", super(), args
)
74 super().__init
__(self
, *args
)
77 # bad hack: the DynamicPipe metaclass ends up creating an __init__ signature
78 # for the dynamically-derived class. luckily, SimpleHandshake only needs
79 # "self" as the 1st argument (it is its own "Stage"). anything else
80 # could hypothetically be passed through the pspec.
81 class SimpleHandshakeRedir(SimpleHandshake
):
82 def __init__(self
, mod
, *args
):
83 print ("redir", mod
, args
)
85 if args
and args
[0].stage
:
87 SimpleHandshake
.__init
__(self
, stage
)