Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 0F58A200BD8 for ; Wed, 7 Dec 2016 22:06:55 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id 0DB6A160B0C; Wed, 7 Dec 2016 21:06:55 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 39285160AF9 for ; Wed, 7 Dec 2016 22:06:53 +0100 (CET) Received: (qmail 24716 invoked by uid 500); 7 Dec 2016 21:06:52 -0000 Mailing-List: contact dev-help@ariatosca.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@ariatosca.incubator.apache.org Delivered-To: mailing list dev@ariatosca.incubator.apache.org Received: (qmail 24705 invoked by uid 99); 7 Dec 2016 21:06:52 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd1-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 07 Dec 2016 21:06:52 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd1-us-west.apache.org (ASF Mail Server at spamd1-us-west.apache.org) with ESMTP id BED59D489C for ; Wed, 7 Dec 2016 21:06:51 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd1-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -6.219 X-Spam-Level: X-Spam-Status: No, score=-6.219 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-2.999] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd1-us-west.apache.org [10.40.0.7]) (amavisd-new, port 10024) with ESMTP id myQLPHIWYB_Z for ; Wed, 7 Dec 2016 21:06:47 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with SMTP id 3F1895F238 for ; Wed, 7 Dec 2016 21:06:45 +0000 (UTC) Received: (qmail 24631 invoked by uid 99); 7 Dec 2016 21:06:44 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 07 Dec 2016 21:06:44 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 23CABDFF56; Wed, 7 Dec 2016 21:06:44 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: dankilman@apache.org To: dev@ariatosca.incubator.apache.org Message-Id: <68939649c4c94ce4bdbf864aeb0402be@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: incubator-ariatosca git commit: ARIA-31 Add registry mechanism for extensions [Forced Update!] Date: Wed, 7 Dec 2016 21:06:44 +0000 (UTC) archived-at: Wed, 07 Dec 2016 21:06:55 -0000 Repository: incubator-ariatosca Updated Branches: refs/heads/ARIA-31-extensions c07af3ea0 -> 32f26b480 (forced update) ARIA-31 Add registry mechanism for extensions Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/32f26b48 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/32f26b48 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/32f26b48 Branch: refs/heads/ARIA-31-extensions Commit: 32f26b4806b386ba54058065cd5dadfdd4a509ef Parents: fe974e4 Author: Dan Kilman Authored: Mon Dec 5 15:28:29 2016 +0200 Committer: Dan Kilman Committed: Wed Dec 7 23:06:38 2016 +0200 ---------------------------------------------------------------------- aria/__init__.py | 25 ++-- aria/cli/commands.py | 9 +- aria/extension.py | 126 ++++++++++++++++++ aria/orchestrator/events.py | 36 +++++ aria/orchestrator/events/__init__.py | 57 -------- .../events/builtin_event_handler.py | 123 ----------------- .../events/workflow_engine_event_handler.py | 74 ----------- aria/orchestrator/workflows/__init__.py | 3 + aria/orchestrator/workflows/core/engine.py | 2 + .../workflows/core/event_handler.py | 113 ++++++++++++++++ aria/orchestrator/workflows/logging.py | 65 +++++++++ aria/parser/__init__.py | 5 +- aria/parser/loading/__init__.py | 3 +- aria/parser/loading/uri.py | 5 +- aria/parser/presentation/__init__.py | 3 +- aria/parser/presentation/source.py | 7 +- aria/parser/specification.py | 6 +- aria/utils/plugin.py | 39 ------ extensions/aria_extension_tosca/__init__.py | 52 ++++---- .../simple_v1_0/data_types.py | 5 +- requirements.txt | 1 + tests/orchestrator/conftest.py | 23 ++++ tests/orchestrator/events/__init__.py | 14 -- .../events/test_builtin_event_handlers.py | 14 -- .../test_workflow_enginge_event_handlers.py | 0 .../workflows/executor/test_executor.py | 16 +-- tests/test_extension.py | 132 +++++++++++++++++++ 27 files changed, 571 insertions(+), 387 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/__init__.py ---------------------------------------------------------------------- diff --git a/aria/__init__.py b/aria/__init__.py index 3f81f98..cd27c25 100644 --- a/aria/__init__.py +++ b/aria/__init__.py @@ -17,14 +17,19 @@ Aria top level package """ -import sys import pkgutil +try: + import pkg_resources +except ImportError: + pkg_resources = None + from .VERSION import version as __version__ from .orchestrator.decorators import workflow, operation from .storage import ModelStorage, ResourceStorage, models, ModelDriver, ResourceDriver from . import ( + extension, utils, parser, storage, @@ -43,19 +48,17 @@ _resource_storage = {} def install_aria_extensions(): """ - Iterates all Python packages with names beginning with :code:`aria_extension_` and calls - their :code:`install_aria_extension` function if they have it. + Iterates all Python packages with names beginning with :code:`aria_extension_` and all + :code:`aria_extension` entry points and loads them. + It then invokes all registered extension functions. """ - for loader, module_name, _ in pkgutil.iter_modules(): if module_name.startswith('aria_extension_'): - module = loader.find_module(module_name).load_module(module_name) - - if hasattr(module, 'install_aria_extension'): - module.install_aria_extension() - - # Loading the module has contaminated sys.modules, so we'll clean it up - del sys.modules[module_name] + loader.find_module(module_name).load_module(module_name) + if pkg_resources: + for entry_point in pkg_resources.iter_entry_points(group='aria_extension'): + entry_point.load() + extension.init() def application_model_storage(driver): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/cli/commands.py ---------------------------------------------------------------------- diff --git a/aria/cli/commands.py b/aria/cli/commands.py index 3426bb0..dd893d6 100644 --- a/aria/cli/commands.py +++ b/aria/cli/commands.py @@ -28,13 +28,14 @@ from importlib import import_module from yaml import safe_load, YAMLError +from .. import extension from .. import (application_model_storage, application_resource_storage) from ..logger import LoggerMixin from ..storage import (FileSystemModelDriver, FileSystemResourceDriver) from ..orchestrator.context.workflow import WorkflowContext from ..orchestrator.workflows.core.engine import Engine from ..orchestrator.workflows.executor.thread import ThreadExecutor -from ..parser import (DSL_SPECIFICATION_PACKAGES, iter_specifications) +from ..parser import iter_specifications from ..parser.consumption import ( ConsumptionContext, ConsumerChain, @@ -45,7 +46,7 @@ from ..parser.consumption import ( Inputs, Instance ) -from ..parser.loading import (LiteralLocation, UriLocation, URI_LOADER_PREFIXES) +from ..parser.loading import LiteralLocation, UriLocation from ..utils.application import StorageManager from ..utils.caching import cachedmethod from ..utils.console import (puts, Colored, indent) @@ -315,7 +316,7 @@ class ParseCommand(BaseCommand): if args_namespace.prefix: for prefix in args_namespace.prefix: - URI_LOADER_PREFIXES.append(prefix) + extension.parser.uri_loader_prefix.registry.append(prefix) cachedmethod.ENABLED = args_namespace.cached_methods @@ -376,7 +377,7 @@ class SpecCommand(BaseCommand): super(SpecCommand, self).__call__(args_namespace, unknown_args) # Make sure that all @dsl_specification decorators are processed - for pkg in DSL_SPECIFICATION_PACKAGES: + for pkg in extension.parser.specification_package.registry: import_modules(pkg) # TODO: scan YAML documents as well http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/extension.py ---------------------------------------------------------------------- diff --git a/aria/extension.py b/aria/extension.py new file mode 100644 index 0000000..82293d6 --- /dev/null +++ b/aria/extension.py @@ -0,0 +1,126 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# pylint: disable=no-self-use + +from .utils import collections + + +class _Registerer(object): + + def __init__(self, registry): + self.registry = registry + self._registered_functions = [] + + def add(self, function): + """Add a function to the the registered functions list""" + self._registered_functions.append(function) + + def init(self): + """Initialize all registered functions""" + for function in self._registered_functions: + self._register(function) + + def _register(self, function): + result = function() + if isinstance(self.registry, dict): + self.registry.update(result) + elif isinstance(self.registry, list): + if not isinstance(result, (list, tuple, set)): + result = [result] + self.registry += list(result) + else: + raise RuntimeError('Unsupported registry type') + + +def _registerer(function): + function._registerer_function = True + return function + + +class _ExtensionRegistration(object): + """Base class for extension class decorators""" + + def __init__(self): + self._registerers = {} + for attr, value in vars(self.__class__).items(): + try: + is_registerer_function = value._registerer_function + except AttributeError: + is_registerer_function = False + if is_registerer_function: + registerer = _Registerer(registry=getattr(self, attr)()) + setattr(self, attr, registerer) + self._registerers[attr] = registerer + + def __call__(self, cls): + instance = cls() + for name, registerer in self._registerers.items(): + registrating_function = getattr(instance, name, None) + if registrating_function: + registerer.add(registrating_function) + return cls + + def init(self): + """ + Initialize all registerers by calling all registered functions + """ + for registerer in self._registerers.values(): + registerer.init() + + +class _ParserExtensionRegistration(_ExtensionRegistration): + """Parser extensions class decorator""" + + @_registerer + def presenter_class(self): + """ + Presentation class registration. + Implementing functions can return a single class or a list/tuple of classes + """ + return [] + + @_registerer + def specification_package(self): + """ + Specification package registration. + Implementing functions can return a package name or a list/tuple of names + """ + return [] + + @_registerer + def specification_url(self): + """ + Specification URL registration. + Implementing functions should return a dictionary from names to URLs + """ + return {} + + @_registerer + def uri_loader_prefix(self): + """ + URI loader prefix registration. + Implementing functions can return a single prefix or a list/tuple of prefixes + """ + return collections.StrictList(value_class=basestring) + +parser = _ParserExtensionRegistration() + + +def init(): + """ + Initialize all registerers by calling all registered functions + """ + parser.init() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/orchestrator/events.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/events.py b/aria/orchestrator/events.py new file mode 100644 index 0000000..a1c4922 --- /dev/null +++ b/aria/orchestrator/events.py @@ -0,0 +1,36 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +ARIA's events Sub-Package +Path: aria.events + +Events package provides events mechanism for different executions in aria. +""" + +from blinker import signal + +# workflow engine task signals: +sent_task_signal = signal('sent_task_signal') +start_task_signal = signal('start_task_signal') +on_success_task_signal = signal('success_task_signal') +on_failure_task_signal = signal('failure_task_signal') + +# workflow engine workflow signals: +start_workflow_signal = signal('start_workflow_signal') +on_cancelling_workflow_signal = signal('on_cancelling_workflow_signal') +on_cancelled_workflow_signal = signal('on_cancelled_workflow_signal') +on_success_workflow_signal = signal('on_success_workflow_signal') +on_failure_workflow_signal = signal('on_failure_workflow_signal') http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/orchestrator/events/__init__.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/events/__init__.py b/aria/orchestrator/events/__init__.py deleted file mode 100644 index fbc0f32..0000000 --- a/aria/orchestrator/events/__init__.py +++ /dev/null @@ -1,57 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -ARIA's events Sub-Package -Path: aria.events - -Events package provides events mechanism for different executions in aria. - - -1. storage_event_handler: implementation of storage handlers for workflow and operation events. -2. logger_event_handler: implementation of logger handlers for workflow and operation events. - -API: - * start_task_signal - * on_success_task_signal - * on_failure_task_signal - * start_workflow_signal - * on_success_workflow_signal - * on_failure_workflow_signal -""" - -import os - -from blinker import signal - -from aria.utils.plugin import plugin_installer - -# workflow engine task signals: -sent_task_signal = signal('sent_task_signal') -start_task_signal = signal('start_task_signal') -on_success_task_signal = signal('success_task_signal') -on_failure_task_signal = signal('failure_task_signal') - -# workflow engine workflow signals: -start_workflow_signal = signal('start_workflow_signal') -on_cancelling_workflow_signal = signal('on_cancelling_workflow_signal') -on_cancelled_workflow_signal = signal('on_cancelled_workflow_signal') -on_success_workflow_signal = signal('on_success_workflow_signal') -on_failure_workflow_signal = signal('on_failure_workflow_signal') - -plugin_installer( - path=os.path.dirname(os.path.realpath(__file__)), - plugin_suffix='_event_handler', - package=__package__) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/orchestrator/events/builtin_event_handler.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/events/builtin_event_handler.py b/aria/orchestrator/events/builtin_event_handler.py deleted file mode 100644 index c5cccfe..0000000 --- a/aria/orchestrator/events/builtin_event_handler.py +++ /dev/null @@ -1,123 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Aria's events Sub-Package -Path: aria.events.storage_event_handler - -Implementation of storage handlers for workflow and operation events. -""" - - -from datetime import ( - datetime, - timedelta, -) - -from . import ( - start_workflow_signal, - on_success_workflow_signal, - on_failure_workflow_signal, - on_cancelled_workflow_signal, - on_cancelling_workflow_signal, - sent_task_signal, - start_task_signal, - on_success_task_signal, - on_failure_task_signal, -) - - -@sent_task_signal.connect -def _task_sent(task, *args, **kwargs): - with task._update(): - task.status = task.SENT - - -@start_task_signal.connect -def _task_started(task, *args, **kwargs): - with task._update(): - task.started_at = datetime.utcnow() - task.status = task.STARTED - - -@on_failure_task_signal.connect -def _task_failed(task, *args, **kwargs): - with task._update(): - should_retry = ( - (task.retry_count < task.max_attempts - 1 or - task.max_attempts == task.INFINITE_RETRIES) and - # ignore_failure check here means the task will not be retries and it will be marked as - # failed. The engine will also look at ignore_failure so it won't fail the workflow. - not task.ignore_failure) - if should_retry: - task.status = task.RETRYING - task.retry_count += 1 - task.due_at = datetime.utcnow() + timedelta(seconds=task.retry_interval) - else: - task.ended_at = datetime.utcnow() - task.status = task.FAILED - - -@on_success_task_signal.connect -def _task_succeeded(task, *args, **kwargs): - with task._update(): - task.ended_at = datetime.utcnow() - task.status = task.SUCCESS - - -@start_workflow_signal.connect -def _workflow_started(workflow_context, *args, **kwargs): - execution = workflow_context.execution - execution.status = execution.STARTED - execution.started_at = datetime.utcnow() - workflow_context.execution = execution - - -@on_failure_workflow_signal.connect -def _workflow_failed(workflow_context, exception, *args, **kwargs): - execution = workflow_context.execution - execution.error = str(exception) - execution.status = execution.FAILED - execution.ended_at = datetime.utcnow() - workflow_context.execution = execution - - -@on_success_workflow_signal.connect -def _workflow_succeeded(workflow_context, *args, **kwargs): - execution = workflow_context.execution - execution.status = execution.TERMINATED - execution.ended_at = datetime.utcnow() - workflow_context.execution = execution - - -@on_cancelled_workflow_signal.connect -def _workflow_cancelled(workflow_context, *args, **kwargs): - execution = workflow_context.execution - # _workflow_cancelling function may have called this function - # already - if execution.status == execution.CANCELLED: - return - execution.status = execution.CANCELLED - execution.ended_at = datetime.utcnow() - workflow_context.execution = execution - - -@on_cancelling_workflow_signal.connect -def _workflow_cancelling(workflow_context, *args, **kwargs): - execution = workflow_context.execution - if execution.status == execution.PENDING: - return _workflow_cancelled(workflow_context=workflow_context) - execution.status = execution.CANCELLING - workflow_context.execution = execution http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/orchestrator/events/workflow_engine_event_handler.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/events/workflow_engine_event_handler.py b/aria/orchestrator/events/workflow_engine_event_handler.py deleted file mode 100644 index 7df11d1..0000000 --- a/aria/orchestrator/events/workflow_engine_event_handler.py +++ /dev/null @@ -1,74 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -""" -Aria's events Sub-Package -Path: aria.events.storage_event_handler - -Implementation of logger handlers for workflow and operation events. -""" - -from . import ( - start_task_signal, - on_success_task_signal, - on_failure_task_signal, - start_workflow_signal, - on_success_workflow_signal, - on_failure_workflow_signal, - on_cancelled_workflow_signal, - on_cancelling_workflow_signal, -) - - -@start_task_signal.connect -def _start_task_handler(task, **kwargs): - task.logger.debug('Event: Starting task: {task.name}'.format(task=task)) - - -@on_success_task_signal.connect -def _success_task_handler(task, **kwargs): - task.logger.debug('Event: Task success: {task.name}'.format(task=task)) - - -@on_failure_task_signal.connect -def _failure_operation_handler(task, **kwargs): - task.logger.error('Event: Task failure: {task.name}'.format(task=task), - exc_info=kwargs.get('exception', True)) - - -@start_workflow_signal.connect -def _start_workflow_handler(context, **kwargs): - context.logger.debug('Event: Starting workflow: {context.name}'.format(context=context)) - - -@on_failure_workflow_signal.connect -def _failure_workflow_handler(context, **kwargs): - context.logger.debug('Event: Workflow failure: {context.name}'.format(context=context)) - - -@on_success_workflow_signal.connect -def _success_workflow_handler(context, **kwargs): - context.logger.debug('Event: Workflow success: {context.name}'.format(context=context)) - - -@on_cancelled_workflow_signal.connect -def _cancel_workflow_handler(context, **kwargs): - context.logger.debug('Event: Workflow cancelled: {context.name}'.format(context=context)) - - -@on_cancelling_workflow_signal.connect -def _cancelling_workflow_handler(context, **kwargs): - context.logger.debug('Event: Workflow cancelling: {context.name}'.format(context=context)) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/orchestrator/workflows/__init__.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/workflows/__init__.py b/aria/orchestrator/workflows/__init__.py index ae1e83e..4522493 100644 --- a/aria/orchestrator/workflows/__init__.py +++ b/aria/orchestrator/workflows/__init__.py @@ -12,3 +12,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + +# Import required so that logging signals are registered +from . import logging http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/orchestrator/workflows/core/engine.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/workflows/core/engine.py b/aria/orchestrator/workflows/core/engine.py index 87ea8c6..2588f5d 100644 --- a/aria/orchestrator/workflows/core/engine.py +++ b/aria/orchestrator/workflows/core/engine.py @@ -29,6 +29,8 @@ from aria.orchestrator import events from .. import exceptions from . import task as engine_task from . import translation +# Import required so all signals are registered +from . import event_handler # pylint: disable=unused-import class Engine(logger.LoggerMixin): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/orchestrator/workflows/core/event_handler.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/workflows/core/event_handler.py b/aria/orchestrator/workflows/core/event_handler.py new file mode 100644 index 0000000..d05cbcb --- /dev/null +++ b/aria/orchestrator/workflows/core/event_handler.py @@ -0,0 +1,113 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Aria's events Sub-Package +Path: aria.events.storage_event_handler + +Implementation of storage handlers for workflow and operation events. +""" + + +from datetime import ( + datetime, + timedelta, +) + +from ... import events + + +@events.sent_task_signal.connect +def _task_sent(task, *args, **kwargs): + with task._update(): + task.status = task.SENT + + +@events.start_task_signal.connect +def _task_started(task, *args, **kwargs): + with task._update(): + task.started_at = datetime.utcnow() + task.status = task.STARTED + + +@events.on_failure_task_signal.connect +def _task_failed(task, *args, **kwargs): + with task._update(): + should_retry = ( + (task.retry_count < task.max_attempts - 1 or + task.max_attempts == task.INFINITE_RETRIES) and + # ignore_failure check here means the task will not be retries and it will be marked as + # failed. The engine will also look at ignore_failure so it won't fail the workflow. + not task.ignore_failure) + if should_retry: + task.status = task.RETRYING + task.retry_count += 1 + task.due_at = datetime.utcnow() + timedelta(seconds=task.retry_interval) + else: + task.ended_at = datetime.utcnow() + task.status = task.FAILED + + +@events.on_success_task_signal.connect +def _task_succeeded(task, *args, **kwargs): + with task._update(): + task.ended_at = datetime.utcnow() + task.status = task.SUCCESS + + +@events.start_workflow_signal.connect +def _workflow_started(workflow_context, *args, **kwargs): + execution = workflow_context.execution + execution.status = execution.STARTED + execution.started_at = datetime.utcnow() + workflow_context.execution = execution + + +@events.on_failure_workflow_signal.connect +def _workflow_failed(workflow_context, exception, *args, **kwargs): + execution = workflow_context.execution + execution.error = str(exception) + execution.status = execution.FAILED + execution.ended_at = datetime.utcnow() + workflow_context.execution = execution + + +@events.on_success_workflow_signal.connect +def _workflow_succeeded(workflow_context, *args, **kwargs): + execution = workflow_context.execution + execution.status = execution.TERMINATED + execution.ended_at = datetime.utcnow() + workflow_context.execution = execution + + +@events.on_cancelled_workflow_signal.connect +def _workflow_cancelled(workflow_context, *args, **kwargs): + execution = workflow_context.execution + # _workflow_cancelling function may have called this function + # already + if execution.status == execution.CANCELLED: + return + execution.status = execution.CANCELLED + execution.ended_at = datetime.utcnow() + workflow_context.execution = execution + + +@events.on_cancelling_workflow_signal.connect +def _workflow_cancelling(workflow_context, *args, **kwargs): + execution = workflow_context.execution + if execution.status == execution.PENDING: + return _workflow_cancelled(workflow_context=workflow_context) + execution.status = execution.CANCELLING + workflow_context.execution = execution http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/orchestrator/workflows/logging.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/workflows/logging.py b/aria/orchestrator/workflows/logging.py new file mode 100644 index 0000000..409ce0a --- /dev/null +++ b/aria/orchestrator/workflows/logging.py @@ -0,0 +1,65 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +Aria's events Sub-Package +Path: aria.events.storage_event_handler + +Implementation of logger handlers for workflow and operation events. +""" + +from .. import events + + +@events.start_task_signal.connect +def _start_task_handler(task, **kwargs): + task.logger.debug('Event: Starting task: {task.name}'.format(task=task)) + + +@events.on_success_task_signal.connect +def _success_task_handler(task, **kwargs): + task.logger.debug('Event: Task success: {task.name}'.format(task=task)) + + +@events.on_failure_task_signal.connect +def _failure_operation_handler(task, **kwargs): + task.logger.error('Event: Task failure: {task.name}'.format(task=task), + exc_info=kwargs.get('exception', True)) + + +@events.start_workflow_signal.connect +def _start_workflow_handler(context, **kwargs): + context.logger.debug('Event: Starting workflow: {context.name}'.format(context=context)) + + +@events.on_failure_workflow_signal.connect +def _failure_workflow_handler(context, **kwargs): + context.logger.debug('Event: Workflow failure: {context.name}'.format(context=context)) + + +@events.on_success_workflow_signal.connect +def _success_workflow_handler(context, **kwargs): + context.logger.debug('Event: Workflow success: {context.name}'.format(context=context)) + + +@events.on_cancelled_workflow_signal.connect +def _cancel_workflow_handler(context, **kwargs): + context.logger.debug('Event: Workflow cancelled: {context.name}'.format(context=context)) + + +@events.on_cancelling_workflow_signal.connect +def _cancelling_workflow_handler(context, **kwargs): + context.logger.debug('Event: Workflow cancelling: {context.name}'.format(context=context)) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/parser/__init__.py ---------------------------------------------------------------------- diff --git a/aria/parser/__init__.py b/aria/parser/__init__.py index 2a83cd4..9ab8785 100644 --- a/aria/parser/__init__.py +++ b/aria/parser/__init__.py @@ -13,8 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from .specification import (DSL_SPECIFICATION_PACKAGES, DSL_SPECIFICATION_URLS, dsl_specification, - iter_specifications) +from .specification import dsl_specification, iter_specifications MODULES = ( @@ -27,7 +26,5 @@ MODULES = ( __all__ = ( 'MODULES', - 'DSL_SPECIFICATION_PACKAGES', - 'DSL_SPECIFICATION_URLS', 'dsl_specification', 'iter_specifications') http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/parser/loading/__init__.py ---------------------------------------------------------------------- diff --git a/aria/parser/loading/__init__.py b/aria/parser/loading/__init__.py index f331e39..006f164 100644 --- a/aria/parser/loading/__init__.py +++ b/aria/parser/loading/__init__.py @@ -20,7 +20,7 @@ from .loader import Loader from .source import LoaderSource, DefaultLoaderSource from .location import Location, UriLocation, LiteralLocation from .literal import LiteralLoader -from .uri import URI_LOADER_PREFIXES, UriTextLoader +from .uri import UriTextLoader from .request import SESSION, SESSION_CACHE_PATH, RequestLoader, RequestTextLoader from .file import FileTextLoader @@ -37,7 +37,6 @@ __all__ = ( 'UriLocation', 'LiteralLocation', 'LiteralLoader', - 'URI_LOADER_PREFIXES', 'UriTextLoader', 'SESSION', 'SESSION_CACHE_PATH', http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/parser/loading/uri.py ---------------------------------------------------------------------- diff --git a/aria/parser/loading/uri.py b/aria/parser/loading/uri.py index f94a003..3ed53ec 100644 --- a/aria/parser/loading/uri.py +++ b/aria/parser/loading/uri.py @@ -16,6 +16,7 @@ import os from urlparse import urljoin +from ...extension import parser from ...utils.collections import StrictList from ...utils.uris import as_file from .loader import Loader @@ -23,8 +24,6 @@ from .file import FileTextLoader from .request import RequestTextLoader from .exceptions import DocumentNotFoundException -URI_LOADER_PREFIXES = StrictList(value_class=basestring) - class UriTextLoader(Loader): """ @@ -58,7 +57,7 @@ class UriTextLoader(Loader): add_prefix(origin_location.prefix) add_prefixes(context.prefixes) - add_prefixes(URI_LOADER_PREFIXES) + add_prefixes(parser.uri_loader_prefix.registry) # pylint: disable=no-member def open(self): try: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/parser/presentation/__init__.py ---------------------------------------------------------------------- diff --git a/aria/parser/presentation/__init__.py b/aria/parser/presentation/__init__.py index ba7a163..a681695 100644 --- a/aria/parser/presentation/__init__.py +++ b/aria/parser/presentation/__init__.py @@ -18,7 +18,7 @@ from .exceptions import PresenterException, PresenterNotFoundError from .context import PresentationContext from .presenter import Presenter from .presentation import Value, PresentationBase, Presentation, AsIsPresentation -from .source import PRESENTER_CLASSES, PresenterSource, DefaultPresenterSource +from .source import PresenterSource, DefaultPresenterSource from .null import NULL, none_to_null, null_to_none from .fields import (Field, has_fields, short_form_field, allow_unknown_fields, primitive_field, primitive_list_field, primitive_dict_field, primitive_dict_unknown_fields, @@ -42,7 +42,6 @@ __all__ = ( 'Presentation', 'AsIsPresentation', 'PresenterSource', - 'PRESENTER_CLASSES', 'DefaultPresenterSource', 'NULL', 'none_to_null', http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/parser/presentation/source.py ---------------------------------------------------------------------- diff --git a/aria/parser/presentation/source.py b/aria/parser/presentation/source.py index 8ff4cab..feba46c 100644 --- a/aria/parser/presentation/source.py +++ b/aria/parser/presentation/source.py @@ -13,9 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from .exceptions import PresenterNotFoundError -PRESENTER_CLASSES = [] +from ...extension import parser + +from .exceptions import PresenterNotFoundError class PresenterSource(object): @@ -36,7 +37,7 @@ class DefaultPresenterSource(PresenterSource): def __init__(self, classes=None): if classes is None: - classes = PRESENTER_CLASSES + classes = parser.presenter_class.registry # pylint: disable=no-member self.classes = classes def get_presenter(self, raw): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/parser/specification.py ---------------------------------------------------------------------- diff --git a/aria/parser/specification.py b/aria/parser/specification.py index 1c7e1f2..7ae350a 100644 --- a/aria/parser/specification.py +++ b/aria/parser/specification.py @@ -15,12 +15,10 @@ import re +from ..extension import parser from ..utils.collections import OrderedDict from ..utils.formatting import full_type_name - -DSL_SPECIFICATION_PACKAGES = [] -DSL_SPECIFICATION_URLS = {} _DSL_SPECIFICATIONS = {} @@ -84,7 +82,7 @@ def _section_key(value): def _fix_details(details, spec): code = details.get('code') doc = details.get('doc') - url = DSL_SPECIFICATION_URLS.get(spec) + url = parser.specification_url.registry.get(spec) # pylint: disable=no-member if (url is not None) and (doc is not None): # Look for a URL in ReST docstring that begins with our url http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/aria/utils/plugin.py ---------------------------------------------------------------------- diff --git a/aria/utils/plugin.py b/aria/utils/plugin.py deleted file mode 100644 index bb2b974..0000000 --- a/aria/utils/plugin.py +++ /dev/null @@ -1,39 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Contains utility methods that enable dynamic python code loading -# TODO: merge with tools.module -""" - -import os -from importlib import import_module - - -def plugin_installer(path, plugin_suffix, package=None, callback=None): - """ - Load each module under ``path`` that ends with ``plugin_suffix``. If ``callback`` is supplied, - call it with each loaded module. - """ - assert callback is None or callable(callback) - plugin_suffix = '{0}.py'.format(plugin_suffix) - - for file_name in os.listdir(path): - if not file_name.endswith(plugin_suffix): - continue - module_name = '{0}.{1}'.format(package, file_name[:-3]) if package else file_name[:-3] - module = import_module(module_name) - if callback: - callback(module) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/extensions/aria_extension_tosca/__init__.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/__init__.py b/extensions/aria_extension_tosca/__init__.py index 54e1c84..d93dce2 100644 --- a/extensions/aria_extension_tosca/__init__.py +++ b/extensions/aria_extension_tosca/__init__.py @@ -15,34 +15,38 @@ import os.path -from aria.parser import (DSL_SPECIFICATION_PACKAGES, DSL_SPECIFICATION_URLS) -from aria.parser.presentation import PRESENTER_CLASSES -from aria.parser.loading import URI_LOADER_PREFIXES +from aria import extension from .simple_v1_0 import ToscaSimplePresenter1_0 from .simple_nfv_v1_0 import ToscaSimpleNfvPresenter1_0 -def install_aria_extension(): - ''' - Installs the TOSCA extension to ARIA. - ''' - - global PRESENTER_CLASSES # pylint: disable=global-statement - PRESENTER_CLASSES += (ToscaSimplePresenter1_0, ToscaSimpleNfvPresenter1_0) - - # DSL specification - DSL_SPECIFICATION_PACKAGES.append('aria_extension_tosca') - DSL_SPECIFICATION_URLS['yaml-1.1'] = \ - 'http://yaml.org' - DSL_SPECIFICATION_URLS['tosca-simple-1.0'] = \ - 'http://docs.oasis-open.org/tosca/TOSCA-Simple-Profile-YAML/v1.0/cos01' \ - '/TOSCA-Simple-Profile-YAML-v1.0-cos01.html' - DSL_SPECIFICATION_URLS['tosca-simple-nfv-1.0'] = \ - 'http://docs.oasis-open.org/tosca/tosca-nfv/v1.0/tosca-nfv-v1.0.html' - - # Imports - the_dir = os.path.dirname(__file__) - URI_LOADER_PREFIXES.append(os.path.join(the_dir, 'profiles')) + +@extension.parser +class ParserExtensions(object): + + @staticmethod + def presenter_class(): + return ToscaSimplePresenter1_0, ToscaSimpleNfvPresenter1_0 + + @staticmethod + def specification_package(): + return 'aria_extension_tosca' + + @staticmethod + def specification_url(): + return { + 'yaml-1.1': 'http://yaml.org', + 'tosca-simple-1.0': 'http://docs.oasis-open.org/tosca/TOSCA-Simple-Profile-YAML/v1.0/' + 'cos01/TOSCA-Simple-Profile-YAML-v1.0-cos01.html', + 'tosca-simple-nfv-1.0': 'http://docs.oasis-open.org/tosca/tosca-nfv/v1.0/' + 'tosca-nfv-v1.0.html' + } + + @staticmethod + def uri_loader_prefix(): + the_dir = os.path.dirname(__file__) + return os.path.join(the_dir, 'profiles') + MODULES = ( 'simple_v1_0', http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/extensions/aria_extension_tosca/simple_v1_0/data_types.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/data_types.py b/extensions/aria_extension_tosca/simple_v1_0/data_types.py index 1fdbe6e..a06834c 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/data_types.py +++ b/extensions/aria_extension_tosca/simple_v1_0/data_types.py @@ -14,8 +14,11 @@ # limitations under the License. import re -from functools import total_ordering from datetime import datetime, tzinfo, timedelta +try: + from functools import total_ordering +except ImportError: + from total_ordering import total_ordering from aria.parser import dsl_specification from aria.utils.collections import StrictDict, OrderedDict http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/requirements.txt ---------------------------------------------------------------------- diff --git a/requirements.txt b/requirements.txt index e6d5393..4ab0c02 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,6 +17,7 @@ retrying==1.3.3 blinker==1.4 importlib==1.0.4 ; python_version < '2.7' ordereddict==1.1 ; python_version < '2.7' +total-ordering==0.1.0 ; python_version < '2.7' jsonpickle ruamel.yaml==0.11.15 Jinja2==2.8 http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/tests/orchestrator/conftest.py ---------------------------------------------------------------------- diff --git a/tests/orchestrator/conftest.py b/tests/orchestrator/conftest.py new file mode 100644 index 0000000..4b24f18 --- /dev/null +++ b/tests/orchestrator/conftest.py @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +import aria + + +@pytest.fixture(scope='session', autouse=True) +def install_aria_extensions(): + aria.install_aria_extensions() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/tests/orchestrator/events/__init__.py ---------------------------------------------------------------------- diff --git a/tests/orchestrator/events/__init__.py b/tests/orchestrator/events/__init__.py deleted file mode 100644 index ae1e83e..0000000 --- a/tests/orchestrator/events/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/tests/orchestrator/events/test_builtin_event_handlers.py ---------------------------------------------------------------------- diff --git a/tests/orchestrator/events/test_builtin_event_handlers.py b/tests/orchestrator/events/test_builtin_event_handlers.py deleted file mode 100644 index ae1e83e..0000000 --- a/tests/orchestrator/events/test_builtin_event_handlers.py +++ /dev/null @@ -1,14 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/tests/orchestrator/events/test_workflow_enginge_event_handlers.py ---------------------------------------------------------------------- diff --git a/tests/orchestrator/events/test_workflow_enginge_event_handlers.py b/tests/orchestrator/events/test_workflow_enginge_event_handlers.py deleted file mode 100644 index e69de29..0000000 http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/tests/orchestrator/workflows/executor/test_executor.py ---------------------------------------------------------------------- diff --git a/tests/orchestrator/workflows/executor/test_executor.py b/tests/orchestrator/workflows/executor/test_executor.py index a425799..654542c 100644 --- a/tests/orchestrator/workflows/executor/test_executor.py +++ b/tests/orchestrator/workflows/executor/test_executor.py @@ -20,6 +20,14 @@ from contextlib import contextmanager import pytest import retrying +try: + import celery as _celery + app = _celery.Celery() + app.conf.update(CELERY_RESULT_BACKEND='amqp://') +except ImportError: + _celery = None + app = None + from aria.storage import models from aria.orchestrator import events from aria.orchestrator.workflows.executor import ( @@ -29,14 +37,6 @@ from aria.orchestrator.workflows.executor import ( # celery ) -try: - import celery as _celery - app = _celery.Celery() - app.conf.update(CELERY_RESULT_BACKEND='amqp://') -except ImportError: - _celery = None - app = None - class TestExecutor(object): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/32f26b48/tests/test_extension.py ---------------------------------------------------------------------- diff --git a/tests/test_extension.py b/tests/test_extension.py new file mode 100644 index 0000000..419989c --- /dev/null +++ b/tests/test_extension.py @@ -0,0 +1,132 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +from aria import extension + +# #pylint: disable=no-member,no-method-argument,unused-variable + + +class TestRegisterer(object): + + def test_list_based_registerer_with_single_element_registration(self): + class ExtensionRegistration(extension._ExtensionRegistration): + @extension._registerer + def list_based_registerer(*_): + return [] + extension_registration = ExtensionRegistration() + + @extension_registration + class Extension(object): + def list_based_registerer(self): + return True + + assert extension_registration.list_based_registerer.registry == [] + extension_registration.init() + assert extension_registration.list_based_registerer.registry == [True] + + def test_list_based_registerer_with_sequence_element_registration(self): + class ExtensionRegistration(extension._ExtensionRegistration): + @extension._registerer + def list_based_registerer1(*_): + return [] + + @extension._registerer + def list_based_registerer2(*_): + return [] + + @extension._registerer + def list_based_registerer3(*_): + return [] + extension_registration = ExtensionRegistration() + + @extension_registration + class Extension(object): + def list_based_registerer1(*_): + return [True, True] + + def list_based_registerer2(*_): + return True, True + + def list_based_registerer3(*_): + return set([True]) + + extension_registration.init() + assert extension_registration.list_based_registerer1.registry == [True, True] + assert extension_registration.list_based_registerer2.registry == [True, True] + assert extension_registration.list_based_registerer3.registry == [True] + + def test_dict_based_registerer(self): + class ExtensionRegistration(extension._ExtensionRegistration): + @extension._registerer + def dict_based_registerer(*_): + return {} + extension_registration = ExtensionRegistration() + + @extension_registration + class Extension1(object): + def dict_based_registerer(self): + return { + 'a': 'a', + 'b': 'b' + } + + @extension_registration + class Extension2(object): + def dict_based_registerer(self): + return { + 'c': 'c', + 'd': 'd' + } + + assert extension_registration.dict_based_registerer.registry == {} + extension_registration.init() + assert extension_registration.dict_based_registerer.registry == { + 'a': 'a', + 'b': 'b', + 'c': 'c', + 'd': 'd' + } + + def test_unsupported_registerer(self): + with pytest.raises(RuntimeError): + class ExtensionRegistration(extension._ExtensionRegistration): + @extension._registerer + def unsupported_registerer(*_): + return set() + extension_registration = ExtensionRegistration() + + @extension_registration + class Extension(object): + def unsupported_registerer(self): + return True + + extension_registration.init() + + def test_unimplemented_registration(self): + class ExtensionRegistration(extension._ExtensionRegistration): + @extension._registerer + def list_based_registerer(*_): + return [] + extension_registration = ExtensionRegistration() + + @extension_registration + class Extension(object): + pass + + assert extension_registration.list_based_registerer.registry == [] + extension_registration.init() + assert extension_registration.list_based_registerer.registry == []