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

Commit

Permalink
Merge 8fde6a3 into b84c9f3
Browse files Browse the repository at this point in the history
  • Loading branch information
sernst committed Nov 29, 2018
2 parents b84c9f3 + 8fde6a3 commit 263a205
Show file tree
Hide file tree
Showing 18 changed files with 140 additions and 58 deletions.
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ python:
- "3.5"
- "3.6"
- "3.6-dev" # 3.6 development branch
# - "3.7-dev" # 3.7 development branch (pandas not yet available)
# These have been disabled due to ongoing lack of support from TravisCI
# - "3.7"
# - "3.7-dev" # 3.7 development branch

# command to install dependencies
install:
Expand Down
6 changes: 4 additions & 2 deletions cauldron/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from cauldron.session.reloading import refresh as _refresh
from cauldron.session.caching import SharedCache as _SharedCache
from cauldron.session import spark as _spark
from cauldron.session.definitions import ExecutionResult
from cauldron import environ as _environ

# Version Information in commonly viewed formats
Expand Down Expand Up @@ -63,7 +64,7 @@ def run_project(
logging_path: str = None,
reader_path: str = None,
**kwargs
) -> _environ.Response:
) -> ExecutionResult:
"""
Runs a project as a single command directly within the current Python
interpreter.
Expand All @@ -89,7 +90,8 @@ def run_project(
Any variables to be available in the cauldron.shared object during
execution of the project can be specified here as keyword arguments.
:return:
A response object that contains information about the run process.
A response object that contains information about the run process
and the shared data from the final state of the project.
"""
from cauldron.cli import batcher
return batcher.run_project(
Expand Down
62 changes: 46 additions & 16 deletions cauldron/cli/batcher.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import tempfile

import cauldron
from cauldron import cli
Expand All @@ -9,6 +10,8 @@
from cauldron.cli.commands import run as run_command
from cauldron.cli.commands import save as save_command
from cauldron.environ import logger
from cauldron.session.caching import SharedCache
from cauldron.session.definitions import ExecutionResult


def initialize_logging_path(path: str = None) -> str:
Expand All @@ -24,11 +27,10 @@ def initialize_logging_path(path: str = None) -> str:
The absolute path to the log file that will be used when this project
is executed.
"""

path = environ.paths.clean(path if path else '.')

if os.path.isdir(path) and os.path.exists(path):
path = os.path.join('cauldron_run.log')
path = os.path.join(path, 'cauldron_run.log')
elif os.path.exists(path):
os.remove(path)

Expand All @@ -45,7 +47,7 @@ def run_project(
log_path: str = None,
shared_data: dict = None,
reader_path: str = None
) -> environ.Response:
) -> ExecutionResult:
"""
Opens, executes and closes a Cauldron project in a single command in
production mode (non-interactive).
Expand All @@ -68,47 +70,75 @@ def run_project(
:return:
The response result from the project execution
"""

if not log_path:
log_path = tempfile.mkdtemp()
log_path = initialize_logging_path(log_path)
logger.add_output_path(log_path)

def on_complete(message: str = None) -> environ.Response:
def on_complete(
command_response: environ.Response,
project_data: SharedCache = None,
message: str = None
) -> ExecutionResult:
environ.modes.remove(environ.modes.SINGLE_RUN)
if message:
logger.log(message)
logger.remove_output_path(log_path)
return response
return ExecutionResult(
command_response=command_response,
project_data=project_data or SharedCache()
)

environ.modes.add(environ.modes.SINGLE_RUN)

response = open_command.execute(
open_response = open_command.execute(
context=cli.make_command_context(open_command.NAME),
path=project_directory,
results_path=output_directory
)
if response.failed:
return on_complete('[ERROR]: Aborted trying to open project')
if open_response.failed:
return on_complete(
command_response=open_response,
message='[ERROR]: Aborted trying to open project'
)

project = cauldron.project.get_internal_project()
project.shared.put(**(shared_data if shared_data is not None else dict()))

commander.preload()
response = run_command.execute(
run_response = run_command.execute(
context=cli.make_command_context(run_command.NAME)
)
if response.failed:
return on_complete('[ERROR]: Aborted trying to run project steps')

project_cache = SharedCache().put(
**project.shared._shared_cache_data
)

if run_response.failed:
return on_complete(
command_response=run_response,
project_data=project_cache,
message='[ERROR]: Aborted trying to run project steps'
)

if reader_path:
save_command.execute(
context=cli.make_command_context(save_command.NAME),
path=reader_path
)

response = close_command.execute(
close_response = close_command.execute(
context=cli.make_command_context(close_command.NAME)
)
if response.failed:
return on_complete('[ERROR]: Failed to close project cleanly after run')
if close_response.failed:
return on_complete(
command_response=close_response,
project_data=project_cache,
message='[ERROR]: Failed to close project cleanly after run'
)

return on_complete('Project execution complete')
return on_complete(
command_response=run_response,
project_data=project_cache,
message='Project execution complete'
)
2 changes: 1 addition & 1 deletion cauldron/cli/commands/steps/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ def modify_step(

step_data = {'name': new_name}
if title is None:
if old_step.definition.get('title'):
if old_step.definition.title:
step_data['title'] = old_step.definition.title
else:
step_data['title'] = title.strip('"')
Expand Down
4 changes: 2 additions & 2 deletions cauldron/cli/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from cauldron.environ import Response

FLAG_PATTERN = re.compile(
'^(?P<prefix>-{1,2})(?P<name>[a-zA-Z0-9\-_]+)=?(?P<value>.*)$'
r'^(?P<prefix>-{1,2})(?P<name>[a-zA-Z0-9\-_]+)=?(?P<value>.*)$'
)

ARGS_RESPONSE_NT = namedtuple(
Expand Down Expand Up @@ -39,7 +39,7 @@ def explode_line(raw: str) -> typing.List[str]:
:return:
"""

raw = re.sub('\s+', ' ', raw.strip())
raw = re.sub(r'\s+', ' ', raw.strip())
out = ['']
breaker = ' '

Expand Down
7 changes: 3 additions & 4 deletions cauldron/cli/server/routes/execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,13 @@ def parse_command_args(response: 'Response') -> typing.Tuple[str, str]:
return name, args


def execute(exec_async: bool = False):
def execute(asynchronous: bool = False):
"""
:param exec_async:
:param asynchronous:
Whether or not to allow asynchronous command execution that returns
before the command is complete with a run_uid that can be used to
track the continued execution of the command until completion.
"""

r = Response()
r.update(server=server_runner.get_server_data())

Expand All @@ -118,7 +117,7 @@ def execute(exec_async: bool = False):
if not r.thread:
return flask.jsonify(r.serialize())

if not exec_async:
if not asynchronous:
r.thread.join()

server_runner.active_execution_responses[r.thread.uid] = r
Expand Down
3 changes: 1 addition & 2 deletions cauldron/cli/server/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,9 @@ def execute(
:param authentication_code:
:return:
"""

if kwargs.get('version'):
environ.log('VERSION: {}'.format(environ.version))
sys.exit(0)
return environ.systems.end(0)

if host is None and public:
host = '0.0.0.0'
Expand Down
1 change: 0 additions & 1 deletion cauldron/invoke/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ def initialize():

def run(arguments: typing.List[str] = None):
"""Executes the cauldron command"""

initialize()

from cauldron.invoke import parser
Expand Down
4 changes: 0 additions & 4 deletions cauldron/invoke/invoker.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ def in_project_directory() -> bool:
Returns whether or not the current working directory is a Cauldron project
directory, which contains a cauldron.json file.
"""

current_directory = os.path.realpath(os.curdir)
project_path = os.path.join(current_directory, 'cauldron.json')
return os.path.exists(project_path) and os.path.isfile(project_path)
Expand Down Expand Up @@ -43,7 +42,6 @@ def load_shared_data(path: typing.Union[str, None]) -> dict:

def run_version(args: dict) -> int:
"""Displays the current version"""

version = environ.package_settings.get('version', 'unknown')
print('VERSION: {}'.format(version))
return 0
Expand Down Expand Up @@ -78,7 +76,6 @@ def run_shell(args: dict) -> int:

def run_kernel(args: dict) -> int:
"""Runs the kernel sub command"""

server_run.execute(**args)
return 0

Expand All @@ -93,7 +90,6 @@ def run(action: str, args: dict) -> int:
:param args:
The arguments parsed for the specified action
"""

if args.get('show_version_info'):
return run_version(args)

Expand Down
2 changes: 1 addition & 1 deletion cauldron/render/texts.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def markdown(
)

pattern = re.compile('src="(?P<url>[^"]+)"')
body = pattern.sub('data-src="\g<url>"', body)
body = pattern.sub(r'data-src="\g<url>"', body)
return dict(
body=body,
library_includes=library_includes,
Expand Down
34 changes: 34 additions & 0 deletions cauldron/session/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,41 @@
import typing
import warnings

from cauldron import environ
from cauldron.session import projects
from cauldron.session import caching


class ExecutionResult:
"""Data Structure for data returned by batched project runs."""

def __init__(
self,
command_response: 'environ.Response',
project_data: 'caching.SharedCache'
):
self._response = command_response
self._shared = project_data

@property
def success(self) -> bool:
"""Whether or not the project execution succeeded."""
return self._response.success

@property
def failed(self) -> bool:
"""Whether or not the execution failed."""
return self._response.failed

@property
def response(self) -> 'environ.Response':
"""Command execution response"""
return self._response

@property
def shared(self) -> 'caching.SharedCache':
"""Shared data from the final execution state of the project."""
return self._shared


class FileDefinition(object):
Expand Down
2 changes: 1 addition & 1 deletion cauldron/session/display/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ def latex(source: str):
Add a mathematical equation in latex math-mode syntax to the display.
Instead of the traditional backslash escape character, the @ character is
used instead to prevent backslash conflicts with Python strings. For
example, \delta would be @delta.
example, \\delta would be @delta.
:param source:
The string representing the latex equation to be rendered.
Expand Down
2 changes: 1 addition & 1 deletion cauldron/session/naming.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def explode_filename(name: str, scheme: str) -> dict:
char = scheme[offset]
next_char = scheme[offset + 1] if (offset + 1) < len(scheme) else None

if char in '.()^$?*+\[]|':
if char in r'.()^$?*+\[]|':
addition = '\\{}'.format(char)
scheme_pattern += addition
empty_scheme_pattern += addition
Expand Down
11 changes: 9 additions & 2 deletions cauldron/session/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from cauldron.session import projects
from cauldron.session.buffering import RedirectBuffer
from cauldron.session.caching import SharedCache
from cauldron.session import definitions


class Report(object):
Expand All @@ -21,7 +22,11 @@ def __init__(self, step=None):
self.css = [] # type: typing.List[str]
self.data = SharedCache()
self.files = SharedCache()
self.title = self.definition.get('title')
self.title = (
self.definition.title
if hasattr(self.definition, 'title') else
self.definition.get('title')
)
self.subtitle = self.definition.get('subtitle')
self.summary = self.definition.get('summary')
self.library_includes = []
Expand Down Expand Up @@ -69,7 +74,9 @@ def id(self):
return self.step.definition.name if self.step else None

@property
def definition(self) -> dict:
def definition(
self
) -> typing.Union[None, dict, 'definitions.FileDefinition']:
return self.step.definition if self.step else None

def clear(self) -> 'Report':
Expand Down

0 comments on commit 263a205

Please sign in to comment.