1717class RegressionTestMeta (type ):
1818
1919 class MetaNamespace (namespaces .LocalNamespace ):
20- '''Custom namespace to control the cls attribute assignment.'''
20+ '''Custom namespace to control the cls attribute assignment.
21+
22+ Regular Python class attributes can be overriden by either
23+ parameters or variables respecting the order of execution.
24+ A variable or a parameter may not be declared more than once in the
25+ same class body. Overriding a variable with a parameter or the other
26+ way around has an undefined behaviour. A variable's value may be
27+ updated multiple times within the same class body. A parameter's
28+ value may not be updated more than once within the same class body.
29+ '''
30+
2131 def __setitem__ (self , key , value ):
22- if isinstance (value , variables .VarDirective ):
32+ if isinstance (value , variables .TestVar ):
2333 # Insert the attribute in the variable namespace
2434 self ['_rfm_local_var_space' ][key ] = value
35+
36+ # Override the regular class attribute (if present)
37+ self ._namespace .pop (key , None )
38+
2539 elif isinstance (value , parameters .TestParam ):
2640 # Insert the attribute in the parameter namespace
2741 self ['_rfm_local_param_space' ][key ] = value
42+
43+ # Override the regular class attribute (if present)
44+ self ._namespace .pop (key , None )
45+
46+ elif key in self ['_rfm_local_param_space' ]:
47+ raise ValueError (
48+ f'cannot override parameter { key !r} '
49+ )
2850 else :
29- super ().__setitem__ (key , value )
51+ # Insert the items manually to overide the namespace clash
52+ # check from the base namespace.
53+ self ._namespace [key ] = value
3054
3155 def __getitem__ (self , key ):
3256 '''Expose and control access to the local namespaces.
@@ -40,22 +64,31 @@ def __getitem__(self, key):
4064 except KeyError as err :
4165 try :
4266 # Handle variable access
43- var = self ['_rfm_local_var_space' ][key ]
44- if var .is_defined ():
45- return var .default_value
46- else :
47- raise ValueError (
48- f'variable { key !r} is not assigned a value'
49- )
67+ v = self ['_rfm_local_var_space' ][key ]
68+ v .__set_name__ (self , key )
69+ return v
5070
5171 except KeyError :
5272 # Handle parameter access
5373 if key in self ['_rfm_local_param_space' ]:
5474 raise ValueError (
5575 'accessing a test parameter from the class '
5676 'body is disallowed'
57- )
77+ ) from None
5878 else :
79+ # As the last resource, look if key is a variable in
80+ # any of the base classes. If so, make its value
81+ # available in the current class' namespace.
82+ for b in self ['_rfm_bases' ]:
83+ if key in b ._rfm_var_space :
84+ v = b ._rfm_var_space [key ]
85+ v .__set_name__ (self , key )
86+
87+ # Store a deep-copy of the variable's
88+ # value and return.
89+ self ._namespace [key ] = v .default_value
90+ return self ._namespace [key ]
91+
5992 # If 'key' is neither a variable nor a parameter,
6093 # raise the exception from the base __getitem__.
6194 raise err from None
@@ -64,6 +97,11 @@ def __getitem__(self, key):
6497 def __prepare__ (metacls , name , bases , ** kwargs ):
6598 namespace = super ().__prepare__ (name , bases , ** kwargs )
6699
100+ # Keep reference to the bases inside the namespace
101+ namespace ['_rfm_bases' ] = [
102+ b for b in bases if hasattr (b , '_rfm_var_space' )
103+ ]
104+
67105 # Regression test parameter space defined at the class level
68106 local_param_space = namespaces .LocalNamespace ()
69107 namespace ['_rfm_local_param_space' ] = local_param_space
@@ -78,7 +116,7 @@ def __prepare__(metacls, name, bases, **kwargs):
78116
79117 # Directives to add/modify a regression test variable
80118 namespace ['variable' ] = variables .TestVar
81- namespace ['required' ] = variables .UndefineVar ()
119+ namespace ['required' ] = variables .Undefined
82120 return metacls .MetaNamespace (namespace )
83121
84122 def __new__ (metacls , name , bases , namespace , ** kwargs ):
@@ -172,7 +210,7 @@ def __call__(cls, *args, **kwargs):
172210 obj .__init__ (* args , ** kwargs )
173211 return obj
174212
175- def __getattribute__ (cls , name ):
213+ def __getattr__ (cls , name ):
176214 ''' Attribute lookup method for the MetaNamespace.
177215
178216 This metaclass implements a custom namespace, where built-in `variable`
@@ -183,15 +221,14 @@ def __getattribute__(cls, name):
183221 requested class attribute.
184222 '''
185223 try :
186- return super (). __getattribute__ ( name )
187- except AttributeError :
224+ return cls . _rfm_var_space . vars [ name ]
225+ except KeyError :
188226 try :
189- return cls ._rfm_local_var_space [name ]
227+ return cls ._rfm_param_space . params [name ]
190228 except KeyError :
191- try :
192- return cls ._rfm_local_param_space [name ]
193- except KeyError :
194- return super ().__getattr__ (name )
229+ raise AttributeError (
230+ f'class { cls .__qualname__ !r} has no attribute { name !r} '
231+ ) from None
195232
196233 @property
197234 def param_space (cls ):
0 commit comments