ATLAS Offline Software
Loading...
Searching...
No Matches
ConfigurableMeta.py
Go to the documentation of this file.
1# Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2
3# File: AthenaCommon/python/ConfigurableMeta.py
4# Author: Wim Lavrijsen (WLavrijsen@lbl.gov)
5
6import weakref
7from AthenaCommon import Logging, PropertyProxy
8
9
10
11__version__ = '1.2.0'
12__author__ = 'Wim Lavrijsen (WLavrijsen@lbl.gov)'
13
14__all__ = [ 'ConfigurableMeta' ]
15
16
17
18log = Logging.logging.getLogger( 'ConfigurableMeta' )
19
20
21
23 """The setting of Gaudi component properties needs to be deferred and
24 history of who set what where needs to be collected. This is done
25 by using PropertyProxy descriptors rather than the default ones."""
26
27 def __new__( self, name, bases, dct ):
28 # enforce use of classmethod for getType() and setDefaults()
29 if 'getType' in dct and not isinstance( dct[ 'getType' ], classmethod ):
30 dct[ 'getType' ] = classmethod( dct[ 'getType' ] )
31
32 if 'setDefaults' in dct and not isinstance( dct[ 'setDefaults' ], classmethod ):
33 dct[ 'setDefaults' ] = classmethod( dct[ 'setDefaults' ] )
34
35 # collect what are properties (basically, any public name; C++ variables
36 # shouldn't start with an '_' because of portability constraints, hence
37 # it is safe to assume that any such vars are python private ones)
38 newclass = type.__new__( self, name, bases, dct )
39
40 if 'AthenaCommon' not in newclass.__module__:
41 # check for required methods and the right number of arguments
42 # meths = { 'getServices' : 1, # retrieve list of services to configure
43 meths = { 'getDlls' : 1, # provide list of Dlls to load
44 'getGaudiType' : 1, # return string describing component class
45 'getHandle' : 1, # provide access to C++ side component instance
46 'getType' : 1 } # return the type of the actual C++ component
47
48 for meth, nArgs in meths.items():
49 try:
50 f = getattr( newclass, meth )
51 except AttributeError:
52 raise NotImplementedError("%s is missing in class %s" % (meth,str(newclass)))
53
54 # in addition, verify the number of arguments w/o defaults
55 nargcount = f.__code__.co_argcount
56 dflts = f.__defaults__
57 ndefaults = dflts and len(dflts) or 0
58 if not nargcount - ndefaults <= nArgs <= nargcount:
59 raise TypeError("%s.%s requires exactly %d arguments" % (newclass,meth,nArgs))
60
61 # cache references of instances by name for duplicate removal
62 newclass.configurables = weakref.WeakValueDictionary()
63
64 # loop over slots, which are all assumed to be properties, create proxies, defaults
65 properties = {}
66 slots = dct.get( '__slots__' )
67 if slots:
68 props = [ x for x in slots if x[0] != '_' ]
69 propDict = dct.get('_propertyDocDct')
70 for prop in props:
71 docString = propDict and propDict.get(prop)
72 if type(slots) is dict:
73 default = slots[prop]
74 else:
75 default = None
76 proxy = PropertyProxy.PropertyProxyFactory(
77 getattr( newclass, prop ), docString, default )
78
79 properties[ prop ] = proxy
80 setattr( newclass, prop, proxy )
81
82 # complete set of properties includes those from base classes
83 for base in bases:
84 try:
85 bprops = base._properties.copy()
86 bprops.update( properties )
87 properties = bprops
88 except AttributeError:
89 pass
90
91 newclass._properties = properties
92
93 return newclass
94
95 def __call__( cls, *args, **kwargs ):
96 """To Gaudi, any object with the same type/name is the same object. Hence,
97 this is mimicked in the configuration: instantiating a new Configurable
98 of a type with the same name will return the same instance."""
99
100 # Get the instance: `singleton' logic needs to be in __new__, not here,
101 # for compatibililty with pickling.)
102 cfg = cls.__new__( cls, *args, **kwargs )
103
104 # Initialize the object, if not done already.
105 if cfg and ( not hasattr(cfg, '_fInitOk') or not cfg._fInitOk ):
106 cls.__init__( cfg, *args, **kwargs )
107
108 return cfg
this metaclass installs PropertyProxy descriptors for Gaudi properties