diff --git a/.github/workflows/conda_env/environment_linux.yml b/.github/workflows/conda_env/environment_linux.yml index 01251956..fdd11175 100644 --- a/.github/workflows/conda_env/environment_linux.yml +++ b/.github/workflows/conda_env/environment_linux.yml @@ -6,6 +6,6 @@ dependencies: - pytest-timeout - psutil - mpi4py -- dask=2022.01.0 +- dask=2022.04.0 - dakota - coverage<=6.2 diff --git a/.github/workflows/conda_env/environment_macos.yml b/.github/workflows/conda_env/environment_macos.yml index 998ad716..0ae296d3 100644 --- a/.github/workflows/conda_env/environment_macos.yml +++ b/.github/workflows/conda_env/environment_macos.yml @@ -5,5 +5,5 @@ dependencies: - pytest-cov - pytest-timeout - psutil -- dask=2022.01.0 +- dask=2022.04.0 - coverage<=6.2 diff --git a/.github/workflows/conda_env/environment_static_analysis.yml b/.github/workflows/conda_env/environment_static_analysis.yml index 3e3fdc4a..d0ddba41 100644 --- a/.github/workflows/conda_env/environment_static_analysis.yml +++ b/.github/workflows/conda_env/environment_static_analysis.yml @@ -7,5 +7,5 @@ dependencies: - pylint=2.11.1 - bandit=1.7.2 - codespell=2.1.0 -- dask=2021.01.0 +- dask=2022.04.0 - pytest diff --git a/doc/user_guides/advanced_guide.rst b/doc/user_guides/advanced_guide.rst index 2772aa14..bc42e7c0 100644 --- a/doc/user_guides/advanced_guide.rst +++ b/doc/user_guides/advanced_guide.rst @@ -216,6 +216,17 @@ This section contains some useful tips on testing, debugging and documenting you * Document the component code such that another person can understand how it works. It helps if the structure remains the same as the example component. * Write a description of what the component does, the inputs it uses, outputs it produces, and what scenarios and modes it can be used in in the component documentation section. +* Protected attributes: + + * The following Component attributes are used internally within IPS and are protected so you can not assigned to them: + + * ``component_id`` + * ``services`` + * ``config`` + * ``start_time`` + * ``method_name`` + * ``args`` + ----------------- Writing Drivers diff --git a/ipsframework/component.py b/ipsframework/component.py index b6c90762..3a1cf393 100644 --- a/ipsframework/component.py +++ b/ipsframework/component.py @@ -25,14 +25,14 @@ def __init__(self, services, config): """ Set up config values and reference to services. """ - self.component_id = None - self.invocation_q = None - self.services = weakref.proxy(services) - self.config = config - self.start_time = 0.0 - self.sys_exit = None - self.method_name = None - self.args = None + self.__component_id = None + self.__invocation_q = None + self.__services = weakref.proxy(services) + self.__config = config + self.__start_time = 0.0 + self.__sys_exit = None + self.__method_name = None + self.__args = None for i in config.keys(): try: setattr(self, i, config[i]) @@ -44,7 +44,7 @@ def __copy__(self): cls = self.__class__ result = cls.__new__(cls) for k, v in self.__dict__.items(): - if k in ["invocation_q", "sys_exit", "services"]: + if k in ["_Component__invocation_q", "_Component__sys_exit", "_Component__services"]: setattr(result, k, None) else: setattr(result, k, copy(v)) @@ -54,9 +54,9 @@ def __initialize__(self, component_id, invocation_q, start_time=0.0): """ Establish connection to *invocation_q*. """ - self.component_id = component_id - self.invocation_q = invocation_q - self.start_time = start_time + self.__component_id = component_id + self.__invocation_q = invocation_q + self.__start_time = start_time # setattr(sys, 'exit', sys.exit) def __my_exit__(self, arg=0): @@ -77,7 +77,7 @@ def __run__(self): tmp = sys.exit sys.exit = self.__my_exit__ - self.sys_exit = tmp + self.__sys_exit = tmp try: redirect = self.services.sim_conf['OUT_REDIRECT'] except KeyError: @@ -119,15 +119,15 @@ def __run__(self): self.services._init_event_service() while True: - msg = self.invocation_q.get() + msg = self.__invocation_q.get() self.services.log('Received Message ') sender_id = msg.sender_id call_id = msg.call_id - self.method_name = msg.target_method - self.args = msg.args + self.__method_name = msg.target_method + self.__args = msg.args keywords = msg.keywords formatted_args = ['%.3f' % (x) if isinstance(x, float) - else str(x) for x in self.args] + else str(x) for x in self.__args] if keywords: formatted_args += [" %s=" % k + str(v) for (k, v) in keywords.items()] @@ -149,6 +149,30 @@ def __run__(self): Message.SUCCESS, retval) self.services.fwk_in_q.put(response_msg) + @property + def component_id(self): + return self.__component_id + + @property + def services(self): + return self.__services + + @property + def config(self): + return self.__config + + @property + def start_time(self): + return self.__start_time + + @property + def method_name(self): + return self.__method_name + + @property + def args(self): + return self.__args + def init(self, timestamp=0.0, **keywords): """ Produce some default debugging information before the rest of the code @@ -191,7 +215,7 @@ def terminate(self, status): self.services.cleanup() if status == Message.SUCCESS: self.services.debug('Calling self.sys_exit(0)') - self.sys_exit(0) + self.__sys_exit(0) else: self.services.debug('Calling self.sys_exit(1)') - self.sys_exit(1) + self.__sys_exit(1) diff --git a/ipsframework/portalBridge.py b/ipsframework/portalBridge.py index 15ed3a34..d3ddae8d 100644 --- a/ipsframework/portalBridge.py +++ b/ipsframework/portalBridge.py @@ -84,7 +84,6 @@ def __init__(self, services, config): self.host = '' self.curTime = time.localtime() self.startTime = self.curTime - self.services = services self.sim_map = {} self.portal_url = None self.done = False diff --git a/tests/components/workers/bad_workers.py b/tests/components/workers/bad_workers.py index 185ae84b..e4d37af7 100644 --- a/tests/components/workers/bad_workers.py +++ b/tests/components/workers/bad_workers.py @@ -35,3 +35,8 @@ def step(self, timestamp=0.0, **keywords): class exception_worker(Component): def step(self, timestamp=0.0, **keywords): raise RuntimeError('Runtime error') + + +class assign_protected_attribute(Component): + def step(self, timestamp=0.0, **keywords): + self.args = 0 diff --git a/tests/new/test_bad_components.py b/tests/new/test_bad_components.py index 0a4bb87e..8aaa13e5 100644 --- a/tests/new/test_bad_components.py +++ b/tests/new/test_bad_components.py @@ -164,3 +164,31 @@ def test_bad_task_pool2(tmpdir): assert "WORKER__bad_task_pool_worker2_2 ERROR Uncaught Exception in component method.\n" in lines assert "DRIVER__driver_1 ERROR Uncaught Exception in component method.\n" in lines + + +def test_assign_protected_attribute(tmpdir): + platform_file, config_file = write_basic_config_and_platform_files(tmpdir, worker='assign_protected_attribute') + + framework = Framework(config_file_list=[str(config_file)], + log_file_name=str(tmpdir.join('ips.log')), + platform_file_name=str(platform_file), + debug=None, + verbose_debug=None, + cmd_nodes=0, + cmd_ppn=0) + + framework.run() + + # check output log file + with open(str(tmpdir.join('sim.log')), 'r') as f: + lines = f.readlines() + + # python 3.10 includes the attribute name in the error message + assert "AttributeError: can't set attribute\n" in lines or "AttributeError: can't set attribute 'args'\n" in lines + assert "Exception: can't set attribute\n" in lines or "Exception: can't set attribute 'args'\n" in lines + + # remove timestamp + lines = [line[24:] for line in lines] + + assert "WORKER__assign_protected_attribute_2 ERROR Uncaught Exception in component method.\n" in lines + assert "DRIVER__driver_1 ERROR Uncaught Exception in component method.\n" in lines