Skip to content

Commit

Permalink
Give modules as separate parameter to ModuleManager
Browse files Browse the repository at this point in the history
  • Loading branch information
JakobGM committed May 2, 2018
1 parent f3ead90 commit 091f9a5
Show file tree
Hide file tree
Showing 15 changed files with 338 additions and 222 deletions.
3 changes: 2 additions & 1 deletion astrality/astrality.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,14 @@ def exit_handler(signal=None, frame=None) -> None:
signal.signal(signal.SIGTERM, exit_handler)

try:
config, global_context = user_configuration()
config, modules, global_context = user_configuration()

# Delay further actions if configuration says so
time.sleep(config['config/astrality']['startup_delay'])

module_manager = ModuleManager(
config=config,
modules=modules,
context=global_context,
)
module_manager.finish_tasks()
Expand Down
15 changes: 4 additions & 11 deletions astrality/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def infer_runtime_variables_from_config(

def user_configuration(
config_directory: Optional[Path] = None,
) -> Tuple[ApplicationConfig, Context]:
) -> Tuple[ApplicationConfig, Dict, Context]:
"""
Return dictionary containing the users configuration.
Expand Down Expand Up @@ -206,7 +206,6 @@ def user_configuration(
context=global_context,
prepend='module/',
)
config.update(modules_config)

# Some metadata used by other classes, stored in '_runtime' key
runtime = infer_runtime_variables_from_config(
Expand All @@ -222,7 +221,7 @@ def user_configuration(
ASTRALITY_DEFAULT_GLOBAL_SETTINGS['config/astrality'].copy()
config['config/astrality'].update(user_settings)

return config, global_context
return config, modules_config, global_context


def create_config_directory(path: Optional[Path] = None, empty=False) -> Path:
Expand Down Expand Up @@ -365,18 +364,12 @@ def modules(self, context: Context) -> Dict[Any, Any]:
if hasattr(self, '_modules'):
return self._modules

modules = filter_config_file(
self._modules = filter_config_file(
config_file=self.modules_file,
context=context,
enabled_module_name=self.enabled_module,
prepend=self.prepend,
)

self._modules = {
f'module/{module_name}': module_config
for module_name, module_config
in modules.items()
}
return self._modules

def context(self, context: Context = Context()) -> Context:
Expand Down Expand Up @@ -417,7 +410,7 @@ def config(self, context: Context = Context()) -> Dict:

def __contains__(self, module_name: str) -> bool:
"""Return True if this source contains enabled module_name."""
return 'module/' + module_name in self._config
return module_name in self._config

@classmethod
def represented_by(cls, module_name: str) -> bool:
Expand Down
72 changes: 33 additions & 39 deletions astrality/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ class Module:

def __init__(
self,
module_config: ModuleConfig,
name: str,
module_config: ModuleConfigDict,
module_directory: Path,
replacer: Callable[[str], str] = lambda string: string,
context_store: Context = Context(),
Expand All @@ -111,11 +112,7 @@ def __init__(
'on_startup': {'run': ['echo weekday is {event}']},
}
"""
# Can only initialize one module at a time
assert len(module_config) == 1

section: str = next(iter(module_config.keys()))
self.name: str = section[7:]
self.name = name

# The source directory for the module, determining how to interpret
# relative paths in the module config
Expand All @@ -125,7 +122,7 @@ def __init__(
self.replace = replacer

# Extract configuration content
module_config_content: ModuleConfigDict = module_config[section]
module_config_content: ModuleConfigDict = module_config

# Use static event_listener if no event_listener is specified
self.event_listener: EventListener = \
Expand Down Expand Up @@ -317,32 +314,19 @@ def replace_placeholders(match: Match) -> str:
)

@staticmethod
def valid_class_section(
section: ModuleConfig,
def valid_module(
name: str,
config: ModuleConfigDict,
requires_timeout: Union[int, float],
requires_working_directory: Path,
) -> bool:
"""Check if the given dict represents a valid enabled module."""
if not len(section) == 1:
raise RuntimeError(
'Tried to check module section with dict '
'which does not have exactly one item.',
)

try:
module_name = next(iter(section.keys()))
valid_module_name = \
module_name.split('/')[0].lower() == 'module' # type: ignore
enabled = section[module_name].get('enabled', True)
if not (valid_module_name and enabled):
return False

except KeyError:
if not config.get('enabled', True):
return False

# The module is enabled, now check if all requirements are satisfied
requires: List[RequirementDict] = cast_to_list(
section[module_name].get(
config.get(
'requires',
{},
),
Expand All @@ -360,7 +344,7 @@ def valid_class_section(
return True
else:
logger.warning(
f'[{module_name}] ' +
f'[module/{name}] ' +
", ".join([
repr(requirement)
for requirement
Expand All @@ -381,6 +365,7 @@ class ModuleManager:
def __init__(
self,
config: ApplicationConfig,
modules: Dict[str, ModuleConfigDict] = {},
context: Context = Context(),
) -> None:
"""Initialize a ModuleManager object from `astrality.yml` dict."""
Expand Down Expand Up @@ -417,17 +402,22 @@ def __init__(
)
module_directory = external_module_source.directory

for section, options in module_configs.items():
module_config = {section: options}
for module_name, module_config in module_configs.items():
if module_name \
not in self.global_modules_config.enabled_modules:
continue

if not Module.valid_class_section(
section=module_config,
requires_timeout=self.global_modules_config.requires_timeout, # noqa
if not Module.valid_module(
name=module_name,
config=module_config,
requires_timeout=self.global_modules_config.
requires_timeout, # noqa
requires_working_directory=module_directory,
) or section not in self.global_modules_config.enabled_modules:
):
continue

module = Module(
name=module_name,
module_config=module_config,
module_directory=module_directory,
replacer=self.interpolate_string,
Expand All @@ -437,18 +427,21 @@ def __init__(
self.modules[module.name] = module

# Insert modules defined in `astrality.yml`
for section, options in config.items():
module_config = {section: options}

for module_name, module_config in modules.items():
# Check if this module should be included
if not Module.valid_class_section(
section=module_config,
if module_name not in self.global_modules_config.enabled_modules:
continue

if not Module.valid_module(
name=module_name,
config=module_config,
requires_timeout=self.global_modules_config.requires_timeout,
requires_working_directory=self.config_directory,
) or section not in self.global_modules_config.enabled_modules:
):
continue

module = Module(
name=module_name,
module_config=module_config,
module_directory=self.config_directory,
replacer=self.interpolate_string,
Expand Down Expand Up @@ -652,14 +645,15 @@ def on_application_config_modified(self):
return

# Hot reloading is enabled, get the new configuration dict
new_application_config, new_context = user_configuration(
new_application_config, new_modules, new_context = user_configuration(
config_directory=self.config_directory,
)

try:
# Reinstantiate this object
new_module_manager = ModuleManager(
config=new_application_config,
modules=new_modules,
context=new_context,
)

Expand Down
2 changes: 1 addition & 1 deletion astrality/tests/config/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,6 @@ def test_dict_from_config_file(self, dir_with_compilable_files):
}

def test_get_user_configuration(self, dir_with_compilable_files):
user_conf, _ = user_configuration(dir_with_compilable_files)
user_conf, *_ = user_configuration(dir_with_compilable_files)
assert user_conf['config/key1'] == 'test_value'
assert user_conf['config/key2'] == 'test'
25 changes: 13 additions & 12 deletions astrality/tests/config/test_modules_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ def test_retrieval_of_external_module_config(test_config_directory):
modules_directory=test_config_directory / 'test_modules',
)

assert external_module_source.config({}) == {
'module/burma::burma': {
assert external_module_source.modules({}) == {
'burma::burma': {
'enabled': True,
'safe': False,
},
Expand Down Expand Up @@ -188,7 +188,7 @@ def test_recursive_module_directory(
modules_directory=test_config_directory / 'test_modules',
)
assert directory_module.modules({}) == {
'module/recursive/directory::bulgaria': {
'recursive/directory::bulgaria': {
'on_startup': {
'run': "echo 'Greetings from Bulgaria!'",
},
Expand All @@ -204,8 +204,8 @@ def test_getting_config_dict_from_directory_module(
enabling_statement=enabling_statement,
modules_directory=test_config_directory / 'freezed_modules',
)
assert directory_module.config({}) == {
'module/north_america::USA': {
assert directory_module.modules({}) == {
'north_america::USA': {
'on_startup': {
'run': 'echo Greetings from the USA!',
},
Expand All @@ -229,8 +229,8 @@ def test_enabling_just_one_module_in_diretory(
enabling_statement=enabling_statement,
modules_directory=test_config_directory / 'freezed_modules',
)
assert directory_module.config({}) == {
'module/south_america::brazil': {
assert directory_module.modules({}) == {
'south_america::brazil': {
'on_startup': {
'run': 'echo Greetings from Brazil!',
},
Expand Down Expand Up @@ -418,13 +418,14 @@ def test_automatical_retrival_of_github_module(self, tmpdir):
},
modules_directory=modules_directory,
)
assert github_module_source.config({}) == {
'module/github::jakobgm/test-module.astrality::botswana': {

assert github_module_source.modules({}) == {
'github::jakobgm/test-module.astrality::botswana': {
'on_startup': {
'run': "echo 'Greetings from Botswana!'",
},
},
'module/github::jakobgm/test-module.astrality::ghana': {
'github::jakobgm/test-module.astrality::ghana': {
'on_startup': {
'run': "echo 'Greetings from Ghana!'",
},
Expand Down Expand Up @@ -455,7 +456,7 @@ def test_use_of_autoupdating_github_source(self, tmpdir):
)

# The repository is lazely cloned, so we need to get the config
config = github_module_source.config({})
github_module_source.modules({})

repo_dir = modules_directory / 'jakobgm' / 'test-module.astrality'
assert repo_dir.is_dir()
Expand All @@ -481,7 +482,7 @@ def test_use_of_autoupdating_github_source(self, tmpdir):
},
modules_directory=modules_directory,
)
config = github_module_source.config({})
github_module_source.modules({})

# The autoupdating should update the module to origin/master
# containing the README.rst file
Expand Down
10 changes: 9 additions & 1 deletion astrality/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ def context():
"""Return the context object for the example configuration."""
this_test_file = os.path.abspath(__file__)
conf_path = Path(this_test_file).parents[1] / 'config'
return user_configuration(conf_path)[2]

@pytest.fixture(scope='session', autouse=True)
def modules():
"""Return the modules object for the example configuration."""
this_test_file = os.path.abspath(__file__)
conf_path = Path(this_test_file).parents[1] / 'config'
return user_configuration(conf_path)[1]


Expand Down Expand Up @@ -112,7 +119,8 @@ def _module_factory(
) -> Module:
"""Return module with specified action blocks and config."""
module = Module(
module_config={f'module/{name}': {}},
name=name,
module_config={},
module_directory=module_directory,
replacer=replacer,
context_store=context_store,
Expand Down

0 comments on commit 091f9a5

Please sign in to comment.