Skip to content
This repository has been archived by the owner on Jan 14, 2024. It is now read-only.

Commit

Permalink
#66: Make possible to implement custom lanaguages in MultiStepLanguag…
Browse files Browse the repository at this point in the history
…eAgnosticTask
  • Loading branch information
blackandred committed Aug 28, 2021
1 parent ab09929 commit 268b37d
Show file tree
Hide file tree
Showing 16 changed files with 603 additions and 67 deletions.
3 changes: 3 additions & 0 deletions docs/source/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Todo:

- [ ] Environment variables loading and inheritance
9 changes: 9 additions & 0 deletions docs/source/standardlib/jinja.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ Renders a single file from JINJA2.
rkd :j2:render -s SOURCE-FILE.yaml.j2 -o OUTPUT-FILE.yaml
.. autoclass:: rkd.core.standardlib.jinja.FileRendererTask


Jinja2Language
~~~~~~~~~~~~~~

.. autoclass:: rkd.core.standardlib.jinja.Jinja2Language


:j2:directory-to-directory
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. jinja:: j2_render
Expand Down
6 changes: 6 additions & 0 deletions docs/source/standardlib/php.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,9 @@ PhpScriptTask
.. autoclass:: rkd.php.script.PhpScriptTask
:exclude-members: get_name, get_group_name
:members:


PhpLanguage
~~~~~~~~~~~

.. autoclass:: rkd.php.script.PhpLanguage
16 changes: 14 additions & 2 deletions docs/source/standardlib/shell.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,12 @@ Executes a Bash script. Can be multi-line. Script can read from stdin instead of

Works identically as **:sh**, but for spawns a single process. Does not allow a multi-line script syntax.

Class to import: BaseShellCommandWithArgumentParsingTask
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BaseShellCommandWithArgumentParsingTask
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. WARNING::

To be used only in Python syntax

Creates a command that executes bash script and provides argument parsing using Python's argparse.
Parsed arguments are registered as ARG_{{argument_name}} eg. --activity-type would be exported as ARG_ACTIVITY_TYPE.
Expand All @@ -60,3 +64,11 @@ Parsed arguments are registered as ARG_{{argument_name}} eg. --activity-type wou
]
MultiStepLanguageAgnosticTask
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Used by default in YAML syntax (if "extends" is not specified). Allows to execute multiple steps in various languages.

It has very similar behavior to the GNU Makefile - each step is ran in a separate shell.

.. autoclass:: rkd.core.standardlib.syntax.MultiStepLanguageAgnosticTask
9 changes: 9 additions & 0 deletions src/core/rkd/core/api/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,9 @@ def can_mutate_globals(self) -> bool:

return self._allow_mutating_globals

def get_args(self) -> Dict[str, str]:
return self.args


class TaskInterface(TaskUtilities):
_io: IO
Expand Down Expand Up @@ -591,3 +594,9 @@ class PipelinePartInterface(object):
@abstractmethod
def to_pipeline_part(self) -> List[str]:
pass


class MultiStepLanguageExtensionInterface(ABC):
@abstractmethod
def with_predefined_details(self, code: str, name: str, step_num: int):
pass
72 changes: 45 additions & 27 deletions src/core/rkd/core/api/parsing.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,51 @@
import importlib
from types import FunctionType
from typing import List
from typing import List, Type

from rkd.core.api.contract import TaskInterface

from ..exception import ParsingException
from .syntax import TaskDeclaration


class SyntaxParsing(object):
@staticmethod
def parse_imports_by_list_of_classes(classes_or_modules: List[str]) -> List[TaskDeclaration]:
def parse_import_as_type(import_str: str) -> Type[TaskInterface]:
"""
Import a Python class as a type
Example: rkd.core.standardlib.jinja.FileRendererTask
:param import_str:
:return:
"""

parts = import_str.split('.')
class_name = parts[-1]
import_path = '.'.join(parts[:-1])

# importing just a full module name eg. "rkd_python"
if len(parts) == 1:
import_path = import_str
class_name = 'imports'
# Test if it's not a class name
# In this case we treat is as a module and import an importing method imports()
elif class_name.lower() == class_name:
import_path += '.' + class_name
class_name = 'imports'

try:
module = importlib.import_module(import_path)
except ImportError as e:
raise ParsingException.from_import_error(import_str, class_name, e)

if class_name not in dir(module):
raise ParsingException.from_class_not_found_in_module_error(import_str, class_name, import_path)

return module.__getattribute__(class_name)

@classmethod
def parse_imports_by_list_of_classes(cls, classes_or_modules: List[str]) -> List[TaskDeclaration]:
"""
Parses a List[str] of imports, like in YAML syntax.
Produces a List[TaskDeclaration] with imported list of tasks.
Expand All @@ -25,31 +63,11 @@ def parse_imports_by_list_of_classes(classes_or_modules: List[str]) -> List[Task
parsed: List[TaskDeclaration] = []

for import_str in classes_or_modules:
parts = import_str.split('.')
class_name = parts[-1]
import_path = '.'.join(parts[:-1])

# importing just a full module name eg. "rkd_python"
if len(parts) == 1:
import_path = import_str
class_name = 'imports'
# Test if it's not a class name
# In this case we treat is as a module and import an importing method imports()
elif class_name.lower() == class_name:
import_path += '.' + class_name
class_name = 'imports'

try:
module = importlib.import_module(import_path)
except ImportError as e:
raise ParsingException.from_import_error(import_str, class_name, e)

if class_name not in dir(module):
raise ParsingException.from_class_not_found_in_module_error(import_str, class_name, import_path)

if isinstance(module.__getattribute__(class_name), FunctionType):
parsed += module.__getattribute__(class_name)()
imported_type = cls.parse_import_as_type(import_str)

if isinstance(imported_type, FunctionType):
parsed += imported_type()
else:
parsed.append(TaskDeclaration(module.__getattribute__(class_name)()))
parsed.append(TaskDeclaration(imported_type()))

return parsed
3 changes: 3 additions & 0 deletions src/core/rkd/core/api/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,11 @@ class BasicTestingCase(TestCase):
_envs = None
_cwd = None
should_backup_env = False
temp: TempManager

def setUp(self) -> None:
os.environ['RKD_PATH'] = ''
self.temp = TempManager()

if self.should_backup_env:
self._envs = deepcopy(os.environ)
Expand All @@ -99,6 +101,7 @@ def tearDown(self) -> None:

os.chdir(self._cwd)

self.temp.finally_clean_up()
super().tearDown()

@contextmanager
Expand Down

0 comments on commit 268b37d

Please sign in to comment.