From 21b35465d01a9e3df1671c16d2297ab6a7c99c85 Mon Sep 17 00:00:00 2001 From: lcontami Date: Mon, 10 Jul 2017 11:04:17 +0200 Subject: [PATCH 1/8] first file --- ecpy/measure/hooks/addtask_hook.py | 82 ++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 ecpy/measure/hooks/addtask_hook.py diff --git a/ecpy/measure/hooks/addtask_hook.py b/ecpy/measure/hooks/addtask_hook.py new file mode 100644 index 00000000..d36cf95f --- /dev/null +++ b/ecpy/measure/hooks/addtask_hook.py @@ -0,0 +1,82 @@ +from .base_hooks import BasePostExecutionHook + + +class AddTasksHook(BasePostExecutionHook): + """Post-execusion hook to add a hierarchy of tasks. + + """ + root_task = Typed(RootTask) # dans Atom; à regarder + + def check(self, workbench, **kwargs): + ''' + check that the post-hook task can be executed + appelé dans measure.py l.454 + ''' + # pour ajouter une tâche; où est-ce que je mets ça ? dans un init ? + root_task.childen.append(task) + test, traceback = root_task.check() + + + def run(self, workbench, engine): + ''' + execute the post-hook task + l.390 dans processor + remarque à Madar: c'est pas clair dans la doc que le processor va nous passer + workbench et engine + ''' + + meas = self.measure + meas_id = meas.id + + # infos = ExecutionInfos(id=meas_id+'.posttask', + # task=self.root_task, + # build_deps=deps.get_build_dependencies().dependencies, + # runtime_deps=deps.get_runtime_dependencies('main'), + # observed_entries=meas.collect_monitored_entries(), + # checks=not meas.forced_enqueued, + # ) + # regarder comment adapter ça... appelé l.308 dans engine + + # Ask the engine to perform the task + execution_result = self.engine.perform(infos) + + + def pause(self): + ''' + pause the task + ''' + self.engine.pause() + + + def resume(self): + ''' + resume the task + ''' + self.engine.resume() + + + def stop(self, force=False): + ''' + stop the task + ''' + self.engine.stop(force) + + + def list_runtimes(self, workbench): + ''' + returns the run_time dependencies + ''' + # mesure collect_runtimes collects the pre/post hook dependencies + # rq: on peut avoir un même tache dans monitor + post hook car elles ne vont pas être exécutées en même temps + + + # note pour Lauriane + # dans le processeur (pour start, stop) ou dans l'objet Measure (pour check), + # il est prévu que ces méthodes soient appelées au bon moment. + # c'est pour ça qu'il faut les définir ici dans le cas spécifique + # de notre hook (qui utilise notamment l'engine) + + # parent de BasePostHook est BaseToolDeclaration (dans base_tool) + # on voit que sur la mesure on a un get_state, set_state, mais pas sur le hook ! + # voir comment c'est appelé dans le workspace, et adapter éventuellement le workspace pour + # pouvoir faire ça pour hooks \ No newline at end of file From b5ff7154c8bb74676ad255ade874b0bf6d785062 Mon Sep 17 00:00:00 2001 From: lcontami Date: Tue, 24 Apr 2018 14:02:55 +0200 Subject: [PATCH 2/8] Work in progress on adding a post_hook that allows to run a list of tasks --- docs/source/dev_guide/atom_enaml.rst | 2 + exopy/app/dependencies/plugin.py | 2 +- exopy/measurement/manifest.enaml | 21 +++++++++- .../measurement/workspace/tools_edition.enaml | 38 +++++++++---------- exopy/tasks/tasks/base_views.enaml | 13 ++++++- 5 files changed, 52 insertions(+), 24 deletions(-) diff --git a/docs/source/dev_guide/atom_enaml.rst b/docs/source/dev_guide/atom_enaml.rst index 1cc07547..8c4261fa 100644 --- a/docs/source/dev_guide/atom_enaml.rst +++ b/docs/source/dev_guide/atom_enaml.rst @@ -26,3 +26,5 @@ Enaml .. todo:: Need to describe the syntax for overriding declarative functions. + +See the package documentation at http://enaml.readthedocs.io \ No newline at end of file diff --git a/exopy/app/dependencies/plugin.py b/exopy/app/dependencies/plugin.py index d488ba33..36c7d3a7 100644 --- a/exopy/app/dependencies/plugin.py +++ b/exopy/app/dependencies/plugin.py @@ -284,7 +284,7 @@ def validate_dependencies(self, kind, dependencies): return not container.errors, container.errors def collect_dependencies(self, kind, dependencies, owner=None): - """Collect that a set of dependencies. + """Collect a set of dependencies. For runtime dependencies if permissions are necessary to use a dependence they are requested and should released when they are no diff --git a/exopy/measurement/manifest.enaml b/exopy/measurement/manifest.enaml index 3aa3917c..13fb7212 100644 --- a/exopy/measurement/manifest.enaml +++ b/exopy/measurement/manifest.enaml @@ -26,7 +26,7 @@ from ..instruments.api import InstrUser from .engines.process_engine import ProcessEngine from .editors.api import Editor -from .hooks.api import PreExecutionHook +from .hooks.api import PreExecutionHook, PostExecutionHook logger = logging.getLogger(__name__) @@ -216,6 +216,25 @@ enamldef MeasureManifest(PluginManifest): manifest: from .hooks.internal_checks import InternalChecksHook return InternalChecksHook(declaration=self) + Extension: + id = 'post-execution' + point = manifest.id + '.post-execution' + PostExecutionHook: + id = 'exopy.addtask_hook' + description = ('Run an additional task at the end of a measure,' + 'even if it is stopped.') + + # appelé dans plugin, l 266 + new => (workbench, default=False): + from .hooks.addtask_hook import AddTasksHook + return AddTasksHook(declaration=self) + + make_view => (workbench, hook): + with enaml.imports(): + from .hooks.addtask_view import AddTasksView + return AddTasksView(declaration=self, hook=hook, + workbench=workbench) + Extension: id = 'preferences' point = 'exopy.app.preferences.plugin' diff --git a/exopy/measurement/workspace/tools_edition.enaml b/exopy/measurement/workspace/tools_edition.enaml index 70a9c281..524c959a 100644 --- a/exopy/measurement/workspace/tools_edition.enaml +++ b/exopy/measurement/workspace/tools_edition.enaml @@ -122,8 +122,10 @@ enamldef ToolsEditor(DestroyableContainer): main: attr _names = ids_to_unique_names(getattr(measurement, _kind), reverse=True) constraints << [hbox(tools, *(list(inc.objects) + - [vbox(add, remove, up, down, spacer)])) - ] + [vbox(add, remove, up, down, spacer)])), + add.right == contents_right, + tools.bottom == contents_bottom, + tools.top == contents_top] func update_items(): """Update the list of tools. @@ -132,6 +134,7 @@ enamldef ToolsEditor(DestroyableContainer): main: self._names = ids_to_unique_names(getattr(measurement, _kind), reverse=True) tools.items = list(_names) + if not _names: tools.selected_item = None @@ -181,13 +184,13 @@ enamldef ToolsEditor(DestroyableContainer): main: Include: inc: objects << (make_view(tools.selected_item) if tools.selected_item - else []) + else [Container()]) destroy_old = False PushButton: add: text = 'Add' enabled << not all([id in tools.items - for id in getattr(measurement.plugin, _kind)]) + for id in getattr(measurement.plugin, _kind)]) clicked :: selector = ToolSelector(measurement=measurement, kind=kind) res = selector.exec_() @@ -263,27 +266,22 @@ enamldef ToolsEditorDockItem(DockItem): main: Page: title = 'Pre-execution' name = 'exopy.measurement.workspace.tools.pre_hooks' - Container: - constraints << [hbox(pre_ed, spacer)] - ToolsEditor: pre_ed: - kind = 'pre-hook' - measurement << main.measurement - mandatory_tools = ['exopy.internal_checks'] + ToolsEditor: pre_ed: + kind = 'pre-hook' + measurement << main.measurement + mandatory_tools = ['exopy.internal_checks'] Page: title = 'Monitors' name = 'exopy.measurement.workspace.tools.monitors' - Container: - constraints << [hbox(mon_ed, spacer)] - ToolsEditor: mon_ed: - kind = 'monitor' - measurement << main.measurement + ToolsEditor: mon_ed: + kind = 'monitor' + measurement << main.measurement Page: title = 'Post-execution' name = 'exopy.measurement.workspace.tools.post_hooks' - Container: - constraints << [hbox(post_ed, spacer)] - ToolsEditor: post_ed: - kind = 'post-hook' - measurement << main.measurement + ToolsEditor: post_ed: + kind = 'post-hook' + measurement << main.measurement + diff --git a/exopy/tasks/tasks/base_views.enaml b/exopy/tasks/tasks/base_views.enaml index b957a022..292b74b5 100644 --- a/exopy/tasks/tasks/base_views.enaml +++ b/exopy/tasks/tasks/base_views.enaml @@ -18,7 +18,7 @@ from atom.api import Event from enaml.widgets.api import (GroupBox, Stack, StackItem, FileDialogEx, Label, Field, ToolButton, CheckBox) from enaml.core.api import d_, d_func -from enaml.layout.api import hbox, vbox, align +from enaml.layout.api import hbox, vbox, align, spacer from ...app.icons.api import get_icon from ...utils.enaml_destroy_hook import add_destroy_hook @@ -79,6 +79,9 @@ enamldef RootTaskView(BaseTaskView): main: #: Reference to the core plugin of the application. attr core + #: Option to display or not the RootTask path + attr show_path = True + root = main refresh => (): @@ -179,14 +182,19 @@ enamldef RootTaskView(BaseTaskView): main: view.root = None self.root = None - constraints = [vbox(hbox(p_lab, p_val, p_exp, prof), editor), + constraints = [vbox(hbox(p_lab, p_val, p_exp, prof), editor, spacer), align('v_center', p_lab, p_val)] Label: p_lab: text = 'Root path' + visible = show_path + Field: p_val: text := task.default_path + visible = show_path + ToolButton: p_exp: + visible = show_path icon = get_icon(core.workbench, 'folder-open') clicked :: curr_path = task.default_path @@ -198,6 +206,7 @@ enamldef RootTaskView(BaseTaskView): main: task.default_path = path CheckBox: prof: text = 'Profile' + visible = show_path checked := task.should_profile tool_tip = 'Profile the execution of the task and dump the result.' From 422554d46526d8c7ed267bbc7a8cc557f361b5c1 Mon Sep 17 00:00:00 2001 From: lcontami Date: Mon, 10 Jul 2017 16:42:29 +0200 Subject: [PATCH 3/8] Added documentation for the instrument pools # Conflicts: # docs/source/user_guide/measure_edition.rst --- exopy/measurement/hooks/addtask_hook.py | 123 +++++++++++++++++++++ exopy/measurement/hooks/addtask_view.enaml | 35 ++++++ logo.svg | 16 +++ 3 files changed, 174 insertions(+) create mode 100644 exopy/measurement/hooks/addtask_hook.py create mode 100644 exopy/measurement/hooks/addtask_view.enaml create mode 100644 logo.svg diff --git a/exopy/measurement/hooks/addtask_hook.py b/exopy/measurement/hooks/addtask_hook.py new file mode 100644 index 00000000..26611534 --- /dev/null +++ b/exopy/measurement/hooks/addtask_hook.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright 2015-2018 by Exopy Authors, see AUTHORS for more details. +# +# Distributed under the terms of the BSD license. +# +# The full license is in the file LICENCE, distributed with this software. +# ----------------------------------------------------------------------------- +"""Implementaion of the AddTaskHook hook. + +""" + +from atom.api import Typed, Unicode, Tuple +from .base_hooks import BasePostExecutionHook +from ...tasks.api import RootTask +from ..engines.base_engine import ExecutionInfos + + +# remarques pour Madar: +# dans la doc, dire que le make_view prend au moins comme arguments +# workbench + l'outil + +# c'est pas clair dans la doc que le processor va nous passer +# workbench et engine + +class AddTasksHook(BasePostExecutionHook): + """Post-execusion hook to add a hierarchy of tasks. + + """ + root_task = Typed(RootTask) # dans Atom; à regarder + default_path = Unicode() + dependencies = Tuple() + + def __init__(self, declaration): + """ + Create an empty root task + """ + self.root_task = RootTask() + super(BasePostExecutionHook, self).__init__(declaration=declaration) + + def check(self, workbench, **kwargs): + """ Check that the post-hook task can be executed + + """ + # set the root_task default path to the one of the measure + self.root_task.default_path = self.measurement.root_task.default_path + res, traceback = self.root_task.check() + return res, traceback + + def run(self, workbench, engine): + """ + Execute the post-hook task + l.390 dans processor + remarque à Madar: c'est pas clair dans la doc que le processor va nous passer + workbench et engine + """ + meas = self.measurement + meas_id = meas.id + + # get the build dependencies + manager = workbench.get_plugin('exopy.tasks') + # t_infos = manager.get_task_infos(self.root_task.task_id) + # print('task infos', t_infos) + # print('t_id', self.root_task.task_id, type(self.root_task.task_id)) + # t_id c'est juste une string exopy.ComplexTask + # print('dependencies', t_infos.dependencies) + # set of the dependancies id + + # vérifier que le list_runtimes est toujours appelé avt run + deps = self.dependencies + infos = ExecutionInfos(id=meas_id+'.posttask', + task=self.root_task, + build_deps=deps[0].dependencies, + runtime_deps=deps[1].dependencies, + observed_entries=[], # no monitor for the hooks + checks=not meas.forced_enqueued, + ) + execution_result = engine.perform(infos) + print(execution_result) + return execution_result + + def pause(self): + """ + pause the task + """ + self.engine.pause() + + def resume(self): + """ + resume the task + """ + self.engine.resume() + + def stop(self, force=False): + """ + stop the task + """ + self.engine.stop(force) + + def list_runtimes(self, workbench): + """ + returns the run_time dependencies + """ + cmd = 'exopy.app.dependencies.analyse' + core = workbench.get_plugin('enaml.workbench.core') + deps = core.invoke_command(cmd, + {'obj': self.root_task, + 'dependencies': ['build', 'runtime']}) + self.dependencies = deps + # print('collected deps', deps) + return deps[1] + + # mesure collect_runtimes collects the pre/post hook dependencies + # rq: on peut avoir un même tache dans monitor + post hook car elles ne vont pas être exécutées en même temps + + # parent de BasePostHook est BaseToolDeclaration (dans base_tool) + # on voit que sur la mesure on a un get_state, set_state, mais pas sur le hook ! + # voir comment c'est appelé dans le workspace, et adapter éventuellement le workspace pour + # pouvoir faire ça pour hooks + + + # ajouter une option: à ne faire que si la mesure à échoué ? + # cf doc: ‘task_execution_result’ \ No newline at end of file diff --git a/exopy/measurement/hooks/addtask_view.enaml b/exopy/measurement/hooks/addtask_view.enaml new file mode 100644 index 00000000..12df1953 --- /dev/null +++ b/exopy/measurement/hooks/addtask_view.enaml @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright 2015-2018 by Exopy Authors, see AUTHORS for more details. +# +# Distributed under the terms of the BSD license. +# +# The full license is in the file LICENCE, distributed with this software. +# ----------------------------------------------------------------------------- +"""Widget associated with the AddTaskHook. + +""" +from atom.api import Typed +from enaml.widgets.api import Container, PushButton, Label +from ...tasks.tasks.base_views import RootTaskView + + +enamldef AddTasksView(Container): + """Widget used for the AddTaskHook + + """ + attr hook + + attr declaration + + attr workbench + + hug_width = 'ignore' + hug_height = 'ignore' + # Issue à créer: + # Si n'existe pas, erreur: NoneType object has no attribute width + RootTaskView: view: + core = workbench.get_plugin('enaml.workbench.core') + task = hook.root_task + show_path = False + # regarder ce que c'est la checkbox profile \ No newline at end of file diff --git a/logo.svg b/logo.svg new file mode 100644 index 00000000..897e4342 --- /dev/null +++ b/logo.svg @@ -0,0 +1,16 @@ + + + + + + + +01000101 01000011 01010000 01011001 + + + From f6cedd8f846df3d382b904aa97e816f440308e02 Mon Sep 17 00:00:00 2001 From: lcontami Date: Fri, 4 May 2018 14:52:37 +0200 Subject: [PATCH 4/8] Finishing the hook; adding the methods to save/load the file correcting the executions infos # Conflicts: # docs/source/dev_guide/measurement.rst --- docs/source/dev_guide/measurement.rst | 26 ++--- exopy/measurement/hooks/addtask_hook.py | 108 ++++++++++----------- exopy/measurement/hooks/addtask_view.enaml | 7 +- exopy/measurement/manifest.enaml | 3 +- exopy/measurement/measurement.py | 4 +- exopy/tasks/utils/saving.py | 8 +- 6 files changed, 79 insertions(+), 77 deletions(-) diff --git a/docs/source/dev_guide/measurement.rst b/docs/source/dev_guide/measurement.rst index 78930403..eed863da 100644 --- a/docs/source/dev_guide/measurement.rst +++ b/docs/source/dev_guide/measurement.rst @@ -44,8 +44,8 @@ pre-hook can have two purposes : Adding a pre-hook requires to : -- implement the logic by subclassing |BasePreExecutionHook|. The methods that can be - overridden are : +- implement the logic by subclassing |BasePreExecutionHook|. The methods that + can be overridden are : - check: make sure that the measurement is in a proper state to be executed. - run: execute any custom logic. If any task is to be executed it should be @@ -53,7 +53,7 @@ Adding a pre-hook requires to : - pause/resume/stop: to implement if the run method execution can take a long time (typically if tasks are involved). - list_runtimes: let the measurement know the runtime dependencies (such as - instrument drivers) if any. + instrument drivers) if any. They are then collected by the measurement. Additionally if any entry is contributed to the task hierarchy they should be added when the tool is linked (or later during edition of the tool). @@ -68,14 +68,16 @@ Adding a pre-hook requires to : - If a make_view method has been declared then one needs to create the associated widget which should inherit of |Container|. + The syntax of the make_view is defined in the |BaseToolDeclaration| class. Monitors ^^^^^^^^ -Monitors are used to follow the progress of a measurement. They specify a number of -database entries they are interested in and will receive notifications when -the concerned entry is updated during the execution of the task hierarchy. +Monitors are used to follow the progress of a measurement. They specify a +number of database entries they are interested in and will receive +notifications whenthe concerned entry is updated during the execution of the +task hierarchy. Adding a monitor requires to : @@ -119,8 +121,8 @@ asked not to run them). They are hence perfectly fitted to run clean up. Adding a post-hook requires to : -- implement the logic by subclassing |BasePostExecutionHook|. The methods that can be - overridden are : +- implement the logic by subclassing |BasePostExecutionHook|. The methods that + can be overridden are : - check: make sure that the measurement is in a proper state to be executed. - run: execute any custom logic. If any task is to be executed it should be @@ -130,9 +132,10 @@ Adding a post-hook requires to : - pause/resume/stop: to implement if the run method execution can take a long time (typically if tasks are involved). - list_runtimes: let the measurement know the runtime dependencies (such as - instrument drivers) if any. To access those dependencies inside the - `run` method one can use the |Measurement.get_runtime_dependencies| method - called with the id of the hook. + instrument drivers) if any. They are then collected by the measurement. + To access those dependencies inside the `run` method one can use + the |Measurement.get_runtime_dependencies| method called with the + id of the hook. Additionally if any entry is contributed to the task hierarchy they should be added when the tool is linked (or later during edition of the tool). @@ -147,6 +150,7 @@ Adding a post-hook requires to : - If a make_view method has been declared then one needs to create the associated widget which should inherit of |Container|. + The syntax of the make_view is defined in the |BaseToolDeclaration| class. .. note :: diff --git a/exopy/measurement/hooks/addtask_hook.py b/exopy/measurement/hooks/addtask_hook.py index 26611534..5aaa56a0 100644 --- a/exopy/measurement/hooks/addtask_hook.py +++ b/exopy/measurement/hooks/addtask_hook.py @@ -9,34 +9,29 @@ """Implementaion of the AddTaskHook hook. """ - +from enaml.workbench.api import Workbench from atom.api import Typed, Unicode, Tuple from .base_hooks import BasePostExecutionHook from ...tasks.api import RootTask from ..engines.base_engine import ExecutionInfos -# remarques pour Madar: -# dans la doc, dire que le make_view prend au moins comme arguments -# workbench + l'outil - -# c'est pas clair dans la doc que le processor va nous passer -# workbench et engine - class AddTasksHook(BasePostExecutionHook): """Post-execusion hook to add a hierarchy of tasks. """ - root_task = Typed(RootTask) # dans Atom; à regarder + root_task = Typed(RootTask) + workbench = Typed(Workbench) default_path = Unicode() dependencies = Tuple() - def __init__(self, declaration): - """ - Create an empty root task + def __init__(self, declaration, workbench): + """ Create an empty root task + """ self.root_task = RootTask() - super(BasePostExecutionHook, self).__init__(declaration=declaration) + self.workbench = workbench + super().__init__(declaration=declaration) def check(self, workbench, **kwargs): """ Check that the post-hook task can be executed @@ -48,58 +43,52 @@ def check(self, workbench, **kwargs): return res, traceback def run(self, workbench, engine): + """ Execute the post-hook task + """ - Execute the post-hook task - l.390 dans processor - remarque à Madar: c'est pas clair dans la doc que le processor va nous passer - workbench et engine - """ - meas = self.measurement - meas_id = meas.id - - # get the build dependencies - manager = workbench.get_plugin('exopy.tasks') - # t_infos = manager.get_task_infos(self.root_task.task_id) - # print('task infos', t_infos) - # print('t_id', self.root_task.task_id, type(self.root_task.task_id)) - # t_id c'est juste une string exopy.ComplexTask - # print('dependencies', t_infos.dependencies) - # set of the dependancies id - - # vérifier que le list_runtimes est toujours appelé avt run - deps = self.dependencies - infos = ExecutionInfos(id=meas_id+'.posttask', + # measure has collected the runtime dependencies given by list_runtimes + meas_deps = self.measurement.dependencies + runtime_deps = meas_deps.get_runtime_dependencies(self.declaration.id) + # on the other hand, we need to collect the build dependencies + build_deps = self.dependencies[0].dependencies + cmd = 'exopy.app.dependencies.collect' + core = workbench.get_plugin('enaml.workbench.core') + deps = core.invoke_command(cmd, dict(dependencies=build_deps, + kind='build')) + if deps.errors: + raise RuntimeError('Error when collecting the build dependencies') + + infos = ExecutionInfos(id=self.measurement.id+'.posttask', task=self.root_task, - build_deps=deps[0].dependencies, - runtime_deps=deps[1].dependencies, + build_deps=deps.dependencies, + runtime_deps=runtime_deps, observed_entries=[], # no monitor for the hooks - checks=not meas.forced_enqueued, + checks=not self.measurement.forced_enqueued, ) execution_result = engine.perform(infos) - print(execution_result) return execution_result def pause(self): - """ - pause the task + """ Pause the task + """ self.engine.pause() def resume(self): - """ - resume the task + """ Resume the task + """ self.engine.resume() def stop(self, force=False): - """ - stop the task + """ Stop the task + """ self.engine.stop(force) def list_runtimes(self, workbench): - """ - returns the run_time dependencies + """ Returns the run_time dependencies + """ cmd = 'exopy.app.dependencies.analyse' core = workbench.get_plugin('enaml.workbench.core') @@ -107,17 +96,28 @@ def list_runtimes(self, workbench): {'obj': self.root_task, 'dependencies': ['build', 'runtime']}) self.dependencies = deps - # print('collected deps', deps) return deps[1] - # mesure collect_runtimes collects the pre/post hook dependencies - # rq: on peut avoir un même tache dans monitor + post hook car elles ne vont pas être exécutées en même temps + def get_state(self): + """ Return the informations to save the post hook - # parent de BasePostHook est BaseToolDeclaration (dans base_tool) - # on voit que sur la mesure on a un get_state, set_state, mais pas sur le hook ! - # voir comment c'est appelé dans le workspace, et adapter éventuellement le workspace pour - # pouvoir faire ça pour hooks + """ + core = self.workbench.get_plugin('enaml.workbench.core') + cmd = 'exopy.tasks.save' + task_prefs = core.invoke_command(cmd, {'task': self.root_task}, self) + return task_prefs + def set_state(self, state): + """ Load the post hook - # ajouter une option: à ne faire que si la mesure à échoué ? - # cf doc: ‘task_execution_result’ \ No newline at end of file + """ + cmd = 'exopy.tasks.build_root' + kwarg = {'mode': 'from config', 'config': state, + 'build_dep': self.workbench} + try: + core = self.workbench.get_plugin('enaml.workbench.core') + self.root_task = core.invoke_command(cmd, kwarg) + except Exception: + msg = 'Building %s, failed to restore post hook task : %s' + errors['post hook'] = msg % (state.get('name'), format_exc()) + return None, errors diff --git a/exopy/measurement/hooks/addtask_view.enaml b/exopy/measurement/hooks/addtask_view.enaml index 12df1953..913935c2 100644 --- a/exopy/measurement/hooks/addtask_view.enaml +++ b/exopy/measurement/hooks/addtask_view.enaml @@ -15,7 +15,7 @@ from ...tasks.tasks.base_views import RootTaskView enamldef AddTasksView(Container): - """Widget used for the AddTaskHook + """ Widget used for the AddTaskHook """ attr hook @@ -26,10 +26,7 @@ enamldef AddTasksView(Container): hug_width = 'ignore' hug_height = 'ignore' - # Issue à créer: - # Si n'existe pas, erreur: NoneType object has no attribute width RootTaskView: view: core = workbench.get_plugin('enaml.workbench.core') task = hook.root_task - show_path = False - # regarder ce que c'est la checkbox profile \ No newline at end of file + show_path = False \ No newline at end of file diff --git a/exopy/measurement/manifest.enaml b/exopy/measurement/manifest.enaml index 13fb7212..2a52c92f 100644 --- a/exopy/measurement/manifest.enaml +++ b/exopy/measurement/manifest.enaml @@ -227,7 +227,8 @@ enamldef MeasureManifest(PluginManifest): manifest: # appelé dans plugin, l 266 new => (workbench, default=False): from .hooks.addtask_hook import AddTasksHook - return AddTasksHook(declaration=self) + return AddTasksHook(declaration=self, + workbench=workbench) make_view => (workbench, hook): with enaml.imports(): diff --git a/exopy/measurement/measurement.py b/exopy/measurement/measurement.py index c9992f7f..4dfa339f 100644 --- a/exopy/measurement/measurement.py +++ b/exopy/measurement/measurement.py @@ -322,8 +322,8 @@ def save(self, path): config.update(self.preferences_from_members()) # First save the task. - core = self.plugin.workbench.get_plugin(u'enaml.workbench.core') - cmd = u'exopy.tasks.save' + core = self.plugin.workbench.get_plugin('enaml.workbench.core') + cmd = 'exopy.tasks.save' task_prefs = core.invoke_command(cmd, {'task': self.root_task, 'mode': 'config'}, self) config['root_task'] = {} diff --git a/exopy/tasks/utils/saving.py b/exopy/tasks/utils/saving.py index 7d75af2d..65ced909 100644 --- a/exopy/tasks/utils/saving.py +++ b/exopy/tasks/utils/saving.py @@ -21,15 +21,15 @@ def save_task(event): - """Save a task in memory or in an .ini file. + """Save a task in memory. Parameters ---------- task : BaseTask Task to save. - mode : {'config', 'template'} - Should the task be returned as a dict (ConfigObj) or saved as a, + mode : {'config', 'template'}, optional + Should the task be returned as a dict (ConfigObj) or saved as a template. widget : optional @@ -42,7 +42,7 @@ def save_task(event): A dict is returned if the mode is 'config'. """ - mode = event.parameters['mode'] + mode = event.parameters.get('mode', 'config') if mode == 'template': manager = event.workbench.get_plugin('exopy.tasks') saver = TemplateSaverDialog(event.parameters.get('widget'), From 65c64611967adcd7d9bf419c2fcee556f0e13b26 Mon Sep 17 00:00:00 2001 From: lcontami Date: Fri, 1 Jun 2018 17:51:05 +0200 Subject: [PATCH 5/8] 2 files that escaped my attention --- ecpy/measure/hooks/addtask_hook.py | 82 ------------------------------ logo.svg | 16 ------ 2 files changed, 98 deletions(-) delete mode 100644 ecpy/measure/hooks/addtask_hook.py delete mode 100644 logo.svg diff --git a/ecpy/measure/hooks/addtask_hook.py b/ecpy/measure/hooks/addtask_hook.py deleted file mode 100644 index d36cf95f..00000000 --- a/ecpy/measure/hooks/addtask_hook.py +++ /dev/null @@ -1,82 +0,0 @@ -from .base_hooks import BasePostExecutionHook - - -class AddTasksHook(BasePostExecutionHook): - """Post-execusion hook to add a hierarchy of tasks. - - """ - root_task = Typed(RootTask) # dans Atom; à regarder - - def check(self, workbench, **kwargs): - ''' - check that the post-hook task can be executed - appelé dans measure.py l.454 - ''' - # pour ajouter une tâche; où est-ce que je mets ça ? dans un init ? - root_task.childen.append(task) - test, traceback = root_task.check() - - - def run(self, workbench, engine): - ''' - execute the post-hook task - l.390 dans processor - remarque à Madar: c'est pas clair dans la doc que le processor va nous passer - workbench et engine - ''' - - meas = self.measure - meas_id = meas.id - - # infos = ExecutionInfos(id=meas_id+'.posttask', - # task=self.root_task, - # build_deps=deps.get_build_dependencies().dependencies, - # runtime_deps=deps.get_runtime_dependencies('main'), - # observed_entries=meas.collect_monitored_entries(), - # checks=not meas.forced_enqueued, - # ) - # regarder comment adapter ça... appelé l.308 dans engine - - # Ask the engine to perform the task - execution_result = self.engine.perform(infos) - - - def pause(self): - ''' - pause the task - ''' - self.engine.pause() - - - def resume(self): - ''' - resume the task - ''' - self.engine.resume() - - - def stop(self, force=False): - ''' - stop the task - ''' - self.engine.stop(force) - - - def list_runtimes(self, workbench): - ''' - returns the run_time dependencies - ''' - # mesure collect_runtimes collects the pre/post hook dependencies - # rq: on peut avoir un même tache dans monitor + post hook car elles ne vont pas être exécutées en même temps - - - # note pour Lauriane - # dans le processeur (pour start, stop) ou dans l'objet Measure (pour check), - # il est prévu que ces méthodes soient appelées au bon moment. - # c'est pour ça qu'il faut les définir ici dans le cas spécifique - # de notre hook (qui utilise notamment l'engine) - - # parent de BasePostHook est BaseToolDeclaration (dans base_tool) - # on voit que sur la mesure on a un get_state, set_state, mais pas sur le hook ! - # voir comment c'est appelé dans le workspace, et adapter éventuellement le workspace pour - # pouvoir faire ça pour hooks \ No newline at end of file diff --git a/logo.svg b/logo.svg deleted file mode 100644 index 897e4342..00000000 --- a/logo.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - -01000101 01000011 01010000 01011001 - - - From 2873797f9a65cf3491f1009e0503c8c6502156b5 Mon Sep 17 00:00:00 2001 From: lcontami Date: Wed, 13 Jun 2018 14:57:45 +0200 Subject: [PATCH 6/8] changed nesting in the ToolsEdition page --- tests/measurement/workspace/test_tools_edition.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/measurement/workspace/test_tools_edition.py b/tests/measurement/workspace/test_tools_edition.py index 3d6f9cee..cebb5dde 100644 --- a/tests/measurement/workspace/test_tools_edition.py +++ b/tests/measurement/workspace/test_tools_edition.py @@ -44,7 +44,7 @@ def assert_selected(): def test_navigation_in_tools_editor(measurement, exopy_qtbot, dialog_sleep): - """Test navigating among the different measurement tools and accessing + """Test navigating among the different measurement tools and accessing their editors. """ @@ -82,7 +82,7 @@ def test_manipulating_tools(measurement, exopy_qtbot, dialog_sleep): exopy_qtbot.wait(dialog_sleep) nb = item.dock_widget().widgets()[0] - pre_hook_ed = nb.pages()[0].page_widget().widgets()[0] + pre_hook_ed = nb.pages()[0].page_widget() # Add a tool def add_tool_1(bot, dial): @@ -157,7 +157,7 @@ def test_ending_with_no_tools(measurement, exopy_qtbot, dialog_sleep): exopy_qtbot.wait(dialog_sleep) nb = item.dock_widget().widgets()[0] - mon_ed = nb.pages()[1].page_widget().widgets()[0] + mon_ed = nb.pages()[1].page_widget() # Add a tool def add_tool_1(bot, dial): From 5b49e018d6e1e0880839ba2a5e13ae5e9d66d003 Mon Sep 17 00:00:00 2001 From: lcontami Date: Wed, 13 Jun 2018 16:55:54 +0200 Subject: [PATCH 7/8] Adding the skeleton for the tests --- exopy/measurement/hooks/addtask_hook.py | 4 +- exopy/measurement/hooks/addtask_view.enaml | 2 +- exopy/measurement/manifest.enaml | 1 - tests/measurement/hooks/test_addtask_hook.py | 132 +++++++++++++++++++ 4 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 tests/measurement/hooks/test_addtask_hook.py diff --git a/exopy/measurement/hooks/addtask_hook.py b/exopy/measurement/hooks/addtask_hook.py index 5aaa56a0..73370693 100644 --- a/exopy/measurement/hooks/addtask_hook.py +++ b/exopy/measurement/hooks/addtask_hook.py @@ -13,7 +13,7 @@ from atom.api import Typed, Unicode, Tuple from .base_hooks import BasePostExecutionHook from ...tasks.api import RootTask -from ..engines.base_engine import ExecutionInfos +from ..engines.base_engine import ExecutionInfos, BaseEngine class AddTasksHook(BasePostExecutionHook): @@ -22,6 +22,7 @@ class AddTasksHook(BasePostExecutionHook): """ root_task = Typed(RootTask) workbench = Typed(Workbench) + engine = Typed(BaseEngine) default_path = Unicode() dependencies = Tuple() @@ -66,6 +67,7 @@ def run(self, workbench, engine): checks=not self.measurement.forced_enqueued, ) execution_result = engine.perform(infos) + self.engine = engine return execution_result def pause(self): diff --git a/exopy/measurement/hooks/addtask_view.enaml b/exopy/measurement/hooks/addtask_view.enaml index 913935c2..88ecb9df 100644 --- a/exopy/measurement/hooks/addtask_view.enaml +++ b/exopy/measurement/hooks/addtask_view.enaml @@ -29,4 +29,4 @@ enamldef AddTasksView(Container): RootTaskView: view: core = workbench.get_plugin('enaml.workbench.core') task = hook.root_task - show_path = False \ No newline at end of file + show_path = False diff --git a/exopy/measurement/manifest.enaml b/exopy/measurement/manifest.enaml index 2a52c92f..7ec5df85 100644 --- a/exopy/measurement/manifest.enaml +++ b/exopy/measurement/manifest.enaml @@ -224,7 +224,6 @@ enamldef MeasureManifest(PluginManifest): manifest: description = ('Run an additional task at the end of a measure,' 'even if it is stopped.') - # appelé dans plugin, l 266 new => (workbench, default=False): from .hooks.addtask_hook import AddTasksHook return AddTasksHook(declaration=self, diff --git a/tests/measurement/hooks/test_addtask_hook.py b/tests/measurement/hooks/test_addtask_hook.py new file mode 100644 index 00000000..5e7ebedc --- /dev/null +++ b/tests/measurement/hooks/test_addtask_hook.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright 2015-2018 by Exopy Authors, see AUTHORS for more details. +# +# Distributed under the terms of the BSD license. +# +# The full license is in the file LICENCE, distributed with this software. +# ----------------------------------------------------------------------------- +"""Test the AddTask post hook. + +""" +import pytest +from exopy.tasks.api import RootTask, SimpleTask +from enaml.workbench.api import Workbench +from exopy.testing.measurement.dummies import DummyEngine +from exopy.tasks.tasks.base_views import RootTaskView + + +@pytest.fixture +def addtaskhook(measurement): + """Create an AddTaskHook. + + """ + hook = measurement.plugin.create('post-hook', 'exopy.addtask_hook') + return hook + +@pytest.fixture +def addtaskview(measurement_workbench, addtaskhook): + """Create an AddTaskView. + + """ + # on est obligé d'aller chercher dans le vrai manifest de measurement pour + # trouver le make_view + meas = measurement_workbench.get_plugin('exopy.measurement') + decl = meas.get_declarations('exopy.addtask_hook') # check how to get the declaration + view = decl.make_view(measurement_workbench, addtaskhook) + return view + + +def test_new_addtask_hook(addtaskhook): + """Testing the creation of an AddTask post-hook + + """ + assert type(addtaskhook.root_task) == RootTask + assert type(addtaskhook.workbench) == Workbench + # assert hook.default_path + # assert hook.dependencies + # assert hook.engine + + +def test_get_state(addtaskhook): + """Testing saving the hook + + """ + root = addtaskhook.root_task + root.children = [SimpleTask(name='task', + database_entries={'val': 1}, + root=root, parent=root, + database=root.database)] + task_prefs = addtaskhook.get_state() + print(task_prefs) + # config = {'name': 'test', + # 'children_0': {'name': 'test_child', + # 'task_id': 'DummyTask'}} + assert task_prefs # blabla selon sa structure + + +def test_set_state(addtaskhook): + """Testing loading the hook from config + + """ + config = {} # config avec 1 tache SimpleTask + addtaskhook.set_state(config) + root = addtaskhook.root_task + assert len(root.children) == 1 + assert root.name == 'post_hooks' + assert isinstance(root.children[0], SimpleTask) + + +def test_pause(addtaskhook): + """Testing the engine pause + + """ + addtaskhook.engine = DummyEngine() + addtaskhook.pause() + assert addtaskhook.engine.should_pause == True + + +def test_resume(addtaskhook): + """Testing the engine resume + + """ + addtaskhook.engine = DummyEngine() + addtaskhook.resume() + assert addtaskhook.engine.should_resume == True + + +def test_stop(addtaskhook): + """Testing the engine stop + + """ + addtaskhook.engine = DummyEngine() + addtaskhook.stop(force=False) + assert addtaskhook.engine.stop_called == True + + +def test_force_stop(addtaskhook): + """Testing the engine force stop + + """ + addtaskhook.engine = DummyEngine() + addtaskhook.stop(force=True) + assert addtaskhook.engine.stop_called == True + +def test_view(addtaskview, addtaskhook): + """Testing the view + + """ + assert addtaskview.hook + assert addtaskview.declaration.id == 'exopy.addtask_hook' + assert addtaskview.workbench + assert type(addtaskview.widget()[0]) == RootTaskView + rootview = addtaskview.widget()[0] + assert rootview.show_path == False + # ca devrait marcher pcq on appelle une seule fois la fixture, ensuite c'est le même objet + assert rootview.task == addtaskhook.root_task + +def test_list_runtimes(): + """Testing list_runtimes + + TODO + """ \ No newline at end of file From 2286a8098465a1ac3c39de6cab928dd54d1d1f14 Mon Sep 17 00:00:00 2001 From: lcontami Date: Wed, 11 Jul 2018 16:13:41 +0200 Subject: [PATCH 8/8] Adressing some of Matthieu's comments --- exopy/measurement/hooks/addtask_hook.py | 13 ++++-- exopy/measurement/hooks/addtask_view.enaml | 3 ++ tests/measurement/hooks/test_addtask_hook.py | 47 +++++++++++--------- 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/exopy/measurement/hooks/addtask_hook.py b/exopy/measurement/hooks/addtask_hook.py index 73370693..1155e7bd 100644 --- a/exopy/measurement/hooks/addtask_hook.py +++ b/exopy/measurement/hooks/addtask_hook.py @@ -20,16 +20,23 @@ class AddTasksHook(BasePostExecutionHook): """Post-execusion hook to add a hierarchy of tasks. """ + #: Reference to the root task at the base of the hierarchy root_task = Typed(RootTask) + + #: Reference to the measurement workbench workbench = Typed(Workbench) + + #: Reference to the measurement engine engine = Typed(BaseEngine) + + #: Reference to the hook root task path; + #: it is the same as the one of the measurement and will not be displayed default_path = Unicode() + + #: Reference to the build and runtime dependencies of the hook tasks dependencies = Tuple() def __init__(self, declaration, workbench): - """ Create an empty root task - - """ self.root_task = RootTask() self.workbench = workbench super().__init__(declaration=declaration) diff --git a/exopy/measurement/hooks/addtask_view.enaml b/exopy/measurement/hooks/addtask_view.enaml index 88ecb9df..ca721054 100644 --- a/exopy/measurement/hooks/addtask_view.enaml +++ b/exopy/measurement/hooks/addtask_view.enaml @@ -18,10 +18,13 @@ enamldef AddTasksView(Container): """ Widget used for the AddTaskHook """ + #: Reference to the hook edited with this view attr hook + #: Reference to the corresponding declaration attr declaration + #: Reference to the corresponding measurement workbench attr workbench hug_width = 'ignore' diff --git a/tests/measurement/hooks/test_addtask_hook.py b/tests/measurement/hooks/test_addtask_hook.py index 5e7ebedc..de1100c6 100644 --- a/tests/measurement/hooks/test_addtask_hook.py +++ b/tests/measurement/hooks/test_addtask_hook.py @@ -10,11 +10,15 @@ """ import pytest +from atom.api import Tuple from exopy.tasks.api import RootTask, SimpleTask from enaml.workbench.api import Workbench from exopy.testing.measurement.dummies import DummyEngine from exopy.tasks.tasks.base_views import RootTaskView +from exopy.testing.util import show_widget +pytest_plugins = str('exopy.testing.tasks.fixtures') + @pytest.fixture def addtaskhook(measurement): @@ -24,6 +28,7 @@ def addtaskhook(measurement): hook = measurement.plugin.create('post-hook', 'exopy.addtask_hook') return hook + @pytest.fixture def addtaskview(measurement_workbench, addtaskhook): """Create an AddTaskView. @@ -43,37 +48,29 @@ def test_new_addtask_hook(addtaskhook): """ assert type(addtaskhook.root_task) == RootTask assert type(addtaskhook.workbench) == Workbench - # assert hook.default_path - # assert hook.dependencies - # assert hook.engine + assert type(addtaskhook.dependencies) == Tuple + assert addtaskhook.default_path + assert addtaskhook.engine -def test_get_state(addtaskhook): - """Testing saving the hook +def test_get_set_state(addtaskhook): + """Testing saving and loading the hook """ root = addtaskhook.root_task + # adding a task to the hook root.children = [SimpleTask(name='task', database_entries={'val': 1}, root=root, parent=root, database=root.database)] task_prefs = addtaskhook.get_state() print(task_prefs) - # config = {'name': 'test', - # 'children_0': {'name': 'test_child', - # 'task_id': 'DummyTask'}} - assert task_prefs # blabla selon sa structure - + assert task_prefs # blabla selon sa structure ? -def test_set_state(addtaskhook): - """Testing loading the hook from config - - """ - config = {} # config avec 1 tache SimpleTask - addtaskhook.set_state(config) - root = addtaskhook.root_task + root.children = [] + assert len(root.children) == 0 + addtaskhook.set_state(task_prefs) assert len(root.children) == 1 - assert root.name == 'post_hooks' assert isinstance(root.children[0], SimpleTask) @@ -101,7 +98,7 @@ def test_stop(addtaskhook): """ addtaskhook.engine = DummyEngine() addtaskhook.stop(force=False) - assert addtaskhook.engine.stop_called == True + assert addtaskhook.engine._stop == True def test_force_stop(addtaskhook): @@ -110,9 +107,9 @@ def test_force_stop(addtaskhook): """ addtaskhook.engine = DummyEngine() addtaskhook.stop(force=True) - assert addtaskhook.engine.stop_called == True + assert addtaskhook.engine._stop == True -def test_view(addtaskview, addtaskhook): +def test_view(addtaskview, addtaskhook, exopy_qtbot, dialog_sleep): """Testing the view """ @@ -125,8 +122,14 @@ def test_view(addtaskview, addtaskhook): # ca devrait marcher pcq on appelle une seule fois la fixture, ensuite c'est le même objet assert rootview.task == addtaskhook.root_task + # test the widget display + win = show_widget(exopy_qtbot, rootview) + exopy_qtbot.wait(dialog_sleep) + win.close() + + def test_list_runtimes(): """Testing list_runtimes TODO - """ \ No newline at end of file + """