Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 80 additions & 6 deletions leapp/actors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ class Actor(object):
actors in the workflow.
"""

current_instance = None
"""
Instance of the currently executed actor. Within a process only exist one Actor instance. This will allow
convenience functions for library developers to be available.
"""

ErrorSeverity = ErrorSeverity
""" Convenience forward for the :py:class:`leapp.models.error_severity.ErrorSeverity` constants. """

Expand Down Expand Up @@ -60,13 +66,15 @@ class Actor(object):
"""

def __init__(self, messaging=None, logger=None):
Actor.current_instance = self
install_translation_for_actor(type(self))
self._messaging = messaging
self.log = (logger or logging.getLogger('leapp.actors')).getChild(self.name)
""" A configured logger instance for the current actor. """

def request_answers(self, dialog):
"""
Requests the answers for a dialog. The dialog needs be predefined in :py:attr:`dialogs`.

:param dialog: Dialog instance to show
:return: dictionary with the requested answers, None if not a defined dialog
Expand All @@ -92,36 +100,102 @@ def common_files_paths(self):
""" Returns all common repository file paths. """
return os.getenv("LEAPP_COMMON_FILES", "").split(":")

def get_folder_path(self, name):
def _get_folder_path(self, directories, name):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we are mixing folder and directory (not only here)? I prefer to use directory equivalent everywhere. So _get_dir_path, ....

"""
Finds the first matching folder path within :py:attr:`files_paths`.
Finds the first matching folder path within directories.

:param name: Name of the folder
:type name: str
:return: Found folder path
:rtype: str or None
"""
for path in self.files_paths:
for path in directories:
path = os.path.join(path, name)
if os.path.isdir(path):
return path
return None

def get_file_path(self, name):
def get_folder_path(self, name):
"""
Finds the first matching file path within :py:attr:`files_paths`.
Finds the first matching folder path within :py:attr:`files_paths`.

:param name: Name of the folder
:type name: str
:return: Found folder path
:rtype: str or None
"""
return self._get_folder_path(self.files_paths, name)

def get_common_folder_path(self, name):
"""
Finds the first matching folder path within :py:attr:`common_files_paths`.

:param name: Name of the folder
:type name: str
:return: Found folder path
:rtype: str or None
"""
return self._get_folder_path(self.common_files_paths, name)

def get_actor_folder_path(self, name):
"""
Finds the first matching folder path within :py:attr:`actor_files_paths`.

:param name: Name of the folder
:type name: str
:return: Found folder path
:rtype: str or None
"""
return self._get_folder_path(self.actor_files_paths, name)

def _get_file_path(self, directories, name):
"""
Finds the first matching file path within directories.

:param name: Name of the file
:type name: str
:return: Found file path
:rtype: str or None
"""
for path in self.files_paths:
for path in directories:
path = os.path.join(path, name)
if os.path.isfile(path):
return path
return None

def get_file_path(self, name):
"""
Finds the first matching file path within :py:attr:`files_paths`.

:param name: Name of the file
:type name: str
:return: Found file path
:rtype: str or None
"""
return self._get_file_path(self.files_paths, name)

def get_common_file_path(self, name):
"""
Finds the first matching file path within :py:attr:`common_files_paths`.

:param name: Name of the file
:type name: str
:return: Found file path
:rtype: str or None
"""
return self._get_file_path(self.common_files_paths, name)

def get_actor_file_path(self, name):
"""
Finds the first matching file path within :py:attr:`actor_files_paths`.

:param name: Name of the file
:type name: str
:return: Found file path
:rtype: str or None
"""
return self._get_file_path(self.actor_files_paths, name)

def run(self, *args):
""" Runs the actor calling the method :py:func:`process`. """
os.environ['LEAPP_CURRENT_ACTOR'] = self.name
Expand Down
163 changes: 163 additions & 0 deletions leapp/libraries/stdlib/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
"""
This module implements a convenience API for actions that are accessible to actors.

Any code that wants use this convenience library has to be called from within the actors context.
This is true for actors, actor private libraries and repository libraries.
"""
from leapp.actors import Actor


ErrorSeverity = Actor.ErrorSeverity


def current_actor():
"""
Retrieve the Actor class instance of the current active actor.
:return: Instance of the currently instantiated actor.
:rtype: leapp.actors.Actor
"""
return Actor.current_instance


def report_error(message, severity=ErrorSeverity.ERROR, details=None):
"""
Reports an execution error

:param message: A message to print the possible error
:type message: str
:param severity: Severity of the error default :py:attr:`leapp.messaging.errors.ErrorSeverity.ERROR`
:type severity: str with defined values from :py:attr:`leapp.messaging.errors.ErrorSeverity.ERROR`
:param details: A dictionary where additional context information is passed along with the error
:type details: dict
:return: None
"""
return current_actor().report_error(message=message, severity=severity, details=details)


def current_logger():
"""
Retrieve the logger of the current active actor.
:return: Logger instance for the current actor.
:rtype: logging.Logger
"""
return current_actor().log


def produce(*model_instances):
"""
By calling produce, model instances are stored as messages. Those messages can be then consumed by other actors.

:param model_instances: Messages to be sent (those model types have to be specified in :py:attr:`produces`
:type model_instances: Variable number of instances of derived classes from :py:class:`leapp.models.Model`
"""
return current_actor().produce(*model_instances)


def consume(*models):
"""
Retrieve messages specified in the actors :py:attr:`consumes` attribute, and filter message types by
models.

:param models: Models to use as a filter for the messages to return
:type models: Variable number of the derived classes from :py:class:`leapp.models.Model`
"""
return current_actor().consume(*models)


def request_answers(dialog):
"""
Requests the answers for a dialog. The dialog needs be predefined in :py:attr:`dialogs` of the actor.

:param dialog: Dialog instance to show
:return: dictionary with the requested answers, None if not a defined dialog
"""
return current_actor().request_answers(dialog)


def actor_files_paths():
"""
Returns the file paths that are bundled with the actor. (Path to the content of the actor's file directory).
"""
return current_actor().actor_files_paths


def files_paths():
""" Returns all actor file paths related to the actor and common actors file paths. """
return current_actor().files_paths


def common_files_paths():
""" Returns all common repository file paths. """
return current_actor().common_files_paths


def get_common_folder_path(name):
"""
Finds the first matching folder path within :py:attr:`files_paths`.

:param name: Name of the folder
:type name: str
:return: Found folder path
:rtype: str or None
"""
return current_actor().get_common_folder_path(name)


def get_actor_folder_path(name):
"""
Finds the first matching folder path within :py:attr:`files_paths`.

:param name: Name of the folder
:type name: str
:return: Found folder path
:rtype: str or None
"""
return current_actor().get_actor_folder_path(name)


def get_folder_path(name):
"""
Finds the first matching folder path within :py:attr:`files_paths`.

:param name: Name of the folder
:type name: str
:return: Found folder path
:rtype: str or None
"""
return current_actor().get_folder_path(name)


def get_common_file_path(name):
"""
Finds the first matching file path within :py:attr:`files_paths`.

:param name: Name of the file
:type name: str
:return: Found file path
:rtype: str or None
"""
return current_actor().get_common_file_path(name)


def get_actor_file_path(name):
"""
Finds the first matching file path within :py:attr:`files_paths`.

:param name: Name of the file
:type name: str
:return: Found file path
:rtype: str or None
"""
return current_actor().get_actor_file_path(name)


def get_file_path(name):
"""
Finds the first matching file path within :py:attr:`files_paths`.

:param name: Name of the file
:type name: str
:return: Found file path
:rtype: str or None
"""
return current_actor().get_file_path(name)
12 changes: 9 additions & 3 deletions leapp/snactor/fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@ def _get_actor(module, repository):
return None


@pytest.fixture(scope='module')
def leapp_forked():
pass


def _execute_test(q, pyfuncitem):
"""
This function is called in the child process from pytest_pyfunc_call via multiprocessing.Process.
Expand All @@ -243,8 +248,9 @@ def _execute_test(q, pyfuncitem):
:return: None
"""
try:
actor = _get_actor(pyfuncitem.module, pyfuncitem.funcargs['current_actor_context'].repository)
pyfuncitem.funcargs['current_actor_context'].set_actor(actor)
if 'current_actor_context' in pyfuncitem.funcargs:
actor = _get_actor(pyfuncitem.module, pyfuncitem.funcargs['current_actor_context'].repository)
pyfuncitem.funcargs['current_actor_context'].set_actor(actor)
original_pytest_pyfunc_call(pyfuncitem=pyfuncitem)
q.put((True, None))
except BaseException: # noqa
Expand All @@ -263,7 +269,7 @@ def pytest_pyfunc_call(pyfuncitem):
:py:func:`current_actor_context` fixture. If it doesn't use the :py:func:`current_actor_context` fixture, it
will default to the default `pytest_pyfunc_call` implementation.
"""
if 'current_actor_context' not in pyfuncitem.funcargs:
if not any([arg in pyfuncitem.funcargs for arg in ('current_actor_context', 'leapp_forked')]):
return None
q = Queue()
p = Process(target=_execute_test, args=(q, pyfuncitem))
Expand Down
1 change: 1 addition & 0 deletions tests/data/actor-api-tests/.leapp/info
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name": "actor-api-tests", "id": "e2b55c03-1c07-4cb6-bab8-cca7315f86f0"}
6 changes: 6 additions & 0 deletions tests/data/actor-api-tests/.leapp/leapp.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

[repositories]
repo_path=${repository:root_dir}

[database]
path=${repository:state_dir}/leapp.db
14 changes: 14 additions & 0 deletions tests/data/actor-api-tests/actors/first/actor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from leapp.actors import Actor
from leapp.tags import ActorFileApiTag
from leapp.models import ApiTestConsume, ApiTestProduce


class First(Actor):
name = 'first'
description = 'No description has been provided for the first actor.'
consumes = (ApiTestConsume,)
produces = (ApiTestProduce,)
tags = (ActorFileApiTag,)

def process(self):
pass
Empty file.
1 change: 1 addition & 0 deletions tests/data/actor-api-tests/actors/first/files/duplicate
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
first-actor
14 changes: 14 additions & 0 deletions tests/data/actor-api-tests/actors/second/actor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from leapp.actors import Actor
from leapp.tags import ActorFileApiTag
from leapp.models import ApiTestConsume, ApiTestProduce


class Second(Actor):
name = 'second'
description = 'No description has been provided for the second actor.'
consumes = (ApiTestConsume,)
produces = (ApiTestProduce,)
tags = (ActorFileApiTag,)

def process(self):
pass
Empty file.
1 change: 1 addition & 0 deletions tests/data/actor-api-tests/actors/second/files/duplicate
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
second-actor
Empty file.
1 change: 1 addition & 0 deletions tests/data/actor-api-tests/files/duplicate
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
repository
16 changes: 16 additions & 0 deletions tests/data/actor-api-tests/models/apitest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from leapp.models import Model, fields
from leapp.topics import ApiTestTopic


class ApiTest(Model):
topic = ApiTestTopic

data = fields.String()


class ApiTestProduce(ApiTest):
pass


class ApiTestConsume(ApiTest):
pass
Loading