From de42905639e7c01d20960c2bdadc920df7b776a4 Mon Sep 17 00:00:00 2001 From: Zac Brannelly <101668878@student.swin.edu.au> Date: Wed, 30 Oct 2019 16:18:33 +1100 Subject: [PATCH 1/8] ENHANCE: Create singleton instance of config and access via decorator --- surround/__init__.py | 2 +- surround/config.py | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/surround/__init__.py b/surround/__init__.py index 1cc198ff..dfac8ce2 100644 --- a/surround/__init__.py +++ b/surround/__init__.py @@ -4,7 +4,7 @@ from .state import State from .stage import Validator, Filter, Estimator from .visualiser import Visualiser -from .config import Config +from .config import Config, has_config from .assembler import Assembler from .runners import Runner, RunMode diff --git a/surround/config.py b/surround/config.py index b961bbb6..85b987c0 100644 --- a/surround/config.py +++ b/surround/config.py @@ -1,5 +1,6 @@ import ast import os +import functools from pathlib import Path from collections.abc import Mapping @@ -51,6 +52,19 @@ class Config(Mapping): SURRROUND_PREDICT_DEBUG=False """ + __instance = None + + @staticmethod + def instance(): + """ + Static method which returns the a singleton instance of Config. + """ + + if not Config.__instance: + Config.__instance = Config(auto_load=True) + + return Config.__instance + def __init__(self, project_root=None, package_path=None, auto_load=False): """ Constructor of the Config class, loads the default YAML file into storage. @@ -373,3 +387,16 @@ def __len__(self): """ return len(self._storage) + +def has_config(func): + """ + Decorator that injects the singleton config instance into the arguments of the + function. + """ + + @functools.wraps(func) + def wrapper(*args, **kwargs): + kwargs["config"] = Config.instance() + return func(*args, **kwargs) + + return wrapper From ab3a69dd66e61e7e76d9b7c864701c777e04e404 Mon Sep 17 00:00:00 2001 From: Zac Brannelly <101668878@student.swin.edu.au> Date: Wed, 30 Oct 2019 16:18:51 +1100 Subject: [PATCH 2/8] ENHANCE: Access config via decorator in generated project --- templates/new/batch_main.py.txt | 6 +++--- templates/new/web_main.py.txt | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/templates/new/batch_main.py.txt b/templates/new/batch_main.py.txt index 8c800212..defa8066 100644 --- a/templates/new/batch_main.py.txt +++ b/templates/new/batch_main.py.txt @@ -5,7 +5,7 @@ Runners and assemblies are defined in here. import os import argparse -from surround import Surround, Assembler, Config +from surround import Surround, Assembler, has_config from .stages import Baseline, InputValidator, ReportGenerator from .file_system_runner import FileSystemRunner @@ -20,8 +20,8 @@ ASSEMBLIES = [ .set_visualiser(ReportGenerator()) ] -def main(): - config = Config(auto_load=True) +@has_config +def main(config=None): default_runner = config.get_path('runner.default') default_assembler = config.get_path('assembler.default') diff --git a/templates/new/web_main.py.txt b/templates/new/web_main.py.txt index 7de7cb84..b9e45137 100644 --- a/templates/new/web_main.py.txt +++ b/templates/new/web_main.py.txt @@ -5,7 +5,7 @@ Runners and ASSEMBLIES are defined in here. import os import argparse -from surround import Surround, Assembler, Config +from surround import Surround, Assembler, has_config from .stages import Baseline, InputValidator, ReportGenerator from .file_system_runner import FileSystemRunner from .web_runner import WebRunner @@ -22,8 +22,8 @@ ASSEMBLIES = [ .set_visualiser(ReportGenerator()) ] -def main(): - config = Config(auto_load=True) +@has_config +def main(config): default_runner = config.get_path('runner.default') default_assembler = config.get_path('assembler.default') From cba57d588de4f1287c50f91228c8b4b283cf129a Mon Sep 17 00:00:00 2001 From: Zac Brannelly <101668878@student.swin.edu.au> Date: Wed, 30 Oct 2019 16:56:48 +1100 Subject: [PATCH 3/8] FIX: Ensure assembler is using the singleton instance of Config --- surround/assembler.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/surround/assembler.py b/surround/assembler.py index 72ddd58a..2d7b901f 100644 --- a/surround/assembler.py +++ b/surround/assembler.py @@ -7,7 +7,7 @@ from abc import ABC from datetime import datetime -from .config import Config +from .config import has_config from .stage import Filter, Estimator, Validator from .visualiser import Visualiser @@ -57,7 +57,8 @@ class Assembler(ABC): """ # pylint: disable=too-many-instance-attributes - def __init__(self, assembler_name=""): + @has_config + def __init__(self, assembler_name="", config=None): """ Constructor for an Assembler pipeline: @@ -66,7 +67,7 @@ def __init__(self, assembler_name=""): """ self.assembler_name = assembler_name - self.config = Config(auto_load=True) + self.config = config self.stages = None self.estimator = None self.validator = None From aca7f7206fe9937f76f284cbe0eea686f5ad7b52 Mon Sep 17 00:00:00 2001 From: Zac Brannelly <101668878@student.swin.edu.au> Date: Wed, 30 Oct 2019 16:57:28 +1100 Subject: [PATCH 4/8] ENHANCE: Allow overriding the name has_config uses for config arg --- surround/config.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/surround/config.py b/surround/config.py index 85b987c0..16898f37 100644 --- a/surround/config.py +++ b/surround/config.py @@ -388,15 +388,31 @@ def __len__(self): return len(self._storage) -def has_config(func): +def has_config(func=None, name="config"): """ - Decorator that injects the singleton config instance into the arguments of the - function. + Decorator that injects the singleton config instance into the arguments of the function. + e.g. + ``` + @has_config + def some_func(config): + value = config.get_path("some.config") + ... + + @has_config(name="global_config") + def other_func(global_config, local_config): + value = config.get_path("some.config") + ``` """ @functools.wraps(func) - def wrapper(*args, **kwargs): - kwargs["config"] = Config.instance() + def function_wrapper(*args, **kwargs): + kwargs[name] = Config.instance() return func(*args, **kwargs) - return wrapper + if func: + return function_wrapper + + def recursive_wrapper(func): + return has_config(func, name) + + return recursive_wrapper From a4a986048be84d64aff131602590184930dad484 Mon Sep 17 00:00:00 2001 From: Zac Brannelly <101668878@student.swin.edu.au> Date: Wed, 30 Oct 2019 17:05:58 +1100 Subject: [PATCH 5/8] FIX: Import Config class in Assembler --- surround/assembler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/surround/assembler.py b/surround/assembler.py index 2d7b901f..f3d87313 100644 --- a/surround/assembler.py +++ b/surround/assembler.py @@ -7,7 +7,7 @@ from abc import ABC from datetime import datetime -from .config import has_config +from .config import Config, has_config from .stage import Filter, Estimator, Validator from .visualiser import Visualiser From f6acb14b4fd357eec558f0efa4da4c0128621e87 Mon Sep 17 00:00:00 2001 From: Zac Brannelly <101668878@student.swin.edu.au> Date: Fri, 15 Nov 2019 11:27:19 +1100 Subject: [PATCH 6/8] FIX: Error in web main template --- templates/new/web_main.py.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/new/web_main.py.txt b/templates/new/web_main.py.txt index b9e45137..ef0feec9 100644 --- a/templates/new/web_main.py.txt +++ b/templates/new/web_main.py.txt @@ -23,7 +23,7 @@ ASSEMBLIES = [ ] @has_config -def main(config): +def main(config=None): default_runner = config.get_path('runner.default') default_assembler = config.get_path('assembler.default') From bb93acfce4567a635777c1d76477695817d1bcf5 Mon Sep 17 00:00:00 2001 From: Zac Brannelly <101668878@student.swin.edu.au> Date: Tue, 19 Nov 2019 15:19:05 +1100 Subject: [PATCH 7/8] ENHANCE: Override global config using decorator --- surround/config.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/surround/config.py b/surround/config.py index 16898f37..cb99e43f 100644 --- a/surround/config.py +++ b/surround/config.py @@ -388,7 +388,7 @@ def __len__(self): return len(self._storage) -def has_config(func=None, name="config"): +def has_config(func=None, name="config", filename=None): """ Decorator that injects the singleton config instance into the arguments of the function. e.g. @@ -399,20 +399,28 @@ def some_func(config): ... @has_config(name="global_config") - def other_func(global_config, local_config): + def other_func(global_config, new_config): value = config.get_path("some.config") + + @has_config(filename="override.yaml") + def some_func(config): + value = config.get_path("override.value") ``` """ @functools.wraps(func) def function_wrapper(*args, **kwargs): - kwargs[name] = Config.instance() + config = Config.instance() + if filename: + path = os.path.join(config["package_path"], filename) + config.read_config_files([path]) + kwargs[name] = config return func(*args, **kwargs) if func: return function_wrapper def recursive_wrapper(func): - return has_config(func, name) + return has_config(func, name, filename) return recursive_wrapper From 454256d690c694c5eaf833fbe3b06819383b19c9 Mon Sep 17 00:00:00 2001 From: Zac Brannelly <101668878@student.swin.edu.au> Date: Tue, 19 Nov 2019 15:26:06 +1100 Subject: [PATCH 8/8] FIX: Avoid pylint error --- surround/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/surround/config.py b/surround/config.py index cb99e43f..04ebd328 100644 --- a/surround/config.py +++ b/surround/config.py @@ -412,7 +412,7 @@ def some_func(config): def function_wrapper(*args, **kwargs): config = Config.instance() if filename: - path = os.path.join(config["package_path"], filename) + path = os.path.join(config.get_path("package_path"), filename) config.read_config_files([path]) kwargs[name] = config return func(*args, **kwargs)