Initialization¶
As mentioned in the introduction, every SCORE application starts with an initialization step. This page will describe a few of the details of the initialization process, so make sure you have read and understood the basics described in the introduction to the initialization before proceeding.
The Entry Point¶
The main function responsible for initializing your application is
score.init.init()
. It accepts a nested dictionary, which is expected
to contain the configuration of each module. The easiest way of creating
such a nested configuration is through the function
score.init.parse_config_file()
:
from score.init import init, parse_config_file
score = init(parse_config_file('/path/to/file.conf'))
There is another function, which does the above calls in a single step:
score.init.init_from_file()
from score.init import init_from_file
score = init_from_file('/path/to/file.conf')
The Module List¶
The first configuration, that is read from the initial configuration, is that
of score.init
itself: The module needs to know, which other modules it
should initialize. It will extract the key conf['score.init']['modules']
and construct a list of modules.
The next step is to create a dependency map to define the order, in which these modules need to be initialized. Every module is assigned an alias in this step, which is defined as part of the module name after the last period. If the module is called transportation.aviation, for example, its alias will be aviation by default.
The modules will then be initialized through a call to their init function. The confdict passed as first parameter to this function is retrieved from the initial configuration file under the alias of the module. The transportation.aviation module will be initialized like the following, for example:
from transportation.aviation import init
init(conf['aviation'])
Aliasing Modules¶
Module aliases must be unique within a score initialization. But it is possible to provide an explicit alias for each module. If you want the transportation.aviation module to be initialized as fleet, for example, you could write your configuration file like this:
[score.init]
modules = transportation.aviation:fleet
This makes it possible to initialize the same module multiple times, allowing you to access the live database, as well as a legacy database, for example.
Since modules have dependencies, you might want to pass a dependency under a different name if you change its alias. Here is an example initializing the coconut module using the aviation module:
[score.init]
modules =
transportation.aviation:fleet
coconut(swallow=fleet)
Configuration Helpers¶
Passing configuration values as strings is not always the most convenient (or the most readable) method. There are scenarios where python code is the best way of configuring modules. The http router is a good example, which uses decorators to define routes:
from score.http import RouterConfiguration
router = RouterConfiguration()
@router.route('home', '/')
def home(ctx):
return 'Hello World'
The RouterConfiguration
in this scenario is called configuration
helper and helps keep the configuration readable. That configuration helper is
referenced in the confdict of the score.http
module in a way, that can
be interpreted by func:score.init.parse_object and will be used during the
initialisation to compile the actual router.
The important bit here is that this helper object is in no way to be regarded
as property of any module, meaning that modules making use of such
configuration objects must not alter these objects during their initialization.
This definition allows reusing these objects across multiple module
instantiations; i.e. it is possible to initialize the score.http
module
twice using the same configuration helper, and neither instance will interfere
with the other.
Finalization¶
Every module creates its own score.init.ConfiguredModule
class
during the initialization and updates the return values of other initialized
modules. The score.css
module, for example, will register some routes at
the score.http
module, in order to serve css assets.
Since some of the ConfiguredModules were modified after the initial call to
the module’s init
, the finalization step will give them a chance to process
these changes before starting the actual application logic.
For this finalization step, all ConfiguredModule objects may implement a
method called _finalize
, accepting any number module names. This
module-list-as-function-arguments declares this modules dependencies for this
step only.
In the following example, the coconut module requires a swallow module
during the call to its init
, but will wait until the knights and africa
modules are finalized, before finalizing itself. Note that the finalization
process also states, that the africa module is not strictly required, but if
it was configured, it will be finalized before this one:
from score.init import ConfiguredModule
def init(confdict, swallow, knights=None):
# TODO: do some real initialization here
return ConfiguredCoconutModule(swallow, knights)
class ConfiguredCoconutModule(ConfiguredModule):
def __init__(self, swallow, knights):
import coconut
super().__init__(coconut)
self.swallow = swallow
self.knights = knights
def _finalize(self, knights, africa=None):
assert self.knights == knights
knights.notify_topic(self, africa)
If a module has a dynamic list of dependencies for its _finalize
function,
it can provide them in one of two ways as a separate member called
_finalize_dependencies
. It should either be
- a list of required dependencies (i.e. module aliases), or
- a dict mapping module aliases to booleans, indicating if the dependency is mandatory or not.
The above example could be rewritten to make use of this alternate approach:
from score.init import ConfiguredModule
def init(confdict, swallow, knights=None):
# TODO: do some real initialization here
return ConfiguredCoconutModule(swallow, knights)
class ConfiguredCoconutModule(ConfiguredModule):
def __init__(self, swallow, knights):
import coconut
super().__init__(coconut)
self.swallow = swallow
self.knights = knights
self._finalize_dependencies = {
'knights': True,
'africa': False,
}
def _finalize(self, **kwargs):
assert self.knights == kwargs['knights']
self.knights.notify_topic(self, kwargs.get('africa'))