Source code for jasy.env.Task

#
# Jasy - Web Tooling Framework
# Copyright 2010-2012 Zynga Inc.
# Copyright 2013-2014 Sebastian Werner
#

"""Tasks are basically functions with some managment code allow them to run in jasyscript.py."""

import types
import os
import sys
import inspect
import subprocess

import jasy.core.Console as Console
import jasy.core.Util as Util

from jasy.env.State import session
from jasy import UserError


__all__ = ("task", "executeTask", "runTask", "printTasks", "setCommand", "setOptions", "getOptions")


class Task(object):

    __slots__ = ["func", "name", "curry", "availableArgs", "hasFlexArgs", "__doc__", "__name__"]


    def __init__(self, func, **curry):
        """Creates a task bound to the given function and currying in static parameters."""

        self.func = func
        self.name = func.__name__

        self.__name__ = "Task: %s" % func.__name__

        # Circular reference to connect both, function and task
        func.task = self

        # The are curried in arguments which are being merged with
        # dynamic command line arguments on each execution
        self.curry = curry

        # Extract doc from function and attach it to the task
        self.__doc__ = inspect.getdoc(func)

        # Analyse arguments for help screen
        result = inspect.getfullargspec(func)
        self.availableArgs = result.args
        self.hasFlexArgs = result.varkw is not None

        # Register task globally
        addTask(self)


    def __call__(self, **kwargs):

        # Let session know about current task
        session.setCurrentTask(self.name)

        # Combine all arguments
        merged = {}
        merged.update(self.curry)
        merged.update(kwargs)

        # Execute internal function
        retval = self.func(**merged)

        # Let session know about current task
        session.setCurrentTask(None)

        # Return result
        return retval


    def __repr__(self):
        return "Task: " + self.__name__




[docs]def task(*args, **kwargs): """Specifies that this function is a task.""" if len(args) == 1: func = args[0] if isinstance(func, Task): return func elif isinstance(func, types.FunctionType): return Task(func) # Compat to old Jasy 0.7.x task declaration elif isinstance(func, str): return task(**kwargs) else: raise UserError("Invalid task") else: def wrapper(func): return Task(func, **kwargs) return wrapper
# Local task managment __taskRegistry = {} def addTask(task): """Registers the given task with its name.""" if task.name in __taskRegistry: Console.debug("Overriding task: %s" % task.name) else: Console.debug("Registering task: %s" % task.name) __taskRegistry[task.name] = task
[docs]def executeTask(taskname, **kwargs): """Executes the given task by name with any optional named arguments.""" if taskname in __taskRegistry: try: camelCaseArgs = {Util.camelize(key) : kwargs[key] for key in kwargs} return __taskRegistry[taskname](**camelCaseArgs) except UserError as err: raise except: Console.error("Unexpected error! Could not finish task %s successfully!" % taskname) raise else: raise UserError("No such task: %s" % taskname)
[docs]def printTasks(indent=16): """Prints out a list of all avaible tasks and their descriptions.""" for name in sorted(__taskRegistry): obj = __taskRegistry[name] formattedName = name if obj.__doc__: space = (indent - len(name)) * " " print(" %s: %s%s" % (formattedName, space, Console.colorize(obj.__doc__, "magenta"))) else: print(" %s" % formattedName) if obj.availableArgs or obj.hasFlexArgs: text = "" if obj.availableArgs: text += Util.hyphenate("--%s <var>" % " <var> --".join(obj.availableArgs)) if obj.hasFlexArgs: if text: text += " ..." else: text += "--<name> <var>" print(" %s" % (Console.colorize(text, "grey")))
# Jasy reference for executing remote tasks __command = None __options = None
[docs]def setCommand(cmd): """Sets the jasy command which should be used to execute tasks with runTask()""" global __command __command = cmd
def getCommand(): """Returns the "jasy" command which is currently executed.""" return __command
[docs]def setOptions(options): """ Sets currently configured command line options. Mainly used for printing help screens. """ global __options __options = options
[docs]def getOptions(): """ Returns the options as passed to the jasy command. Useful for printing all command line arguments. """ return __options
[docs]def runTask(project, task, **kwargs): """ Executes the given task of the given projects. This happens inside a new sandboxed session during which the current session is paused/resumed automatically. """ remote = session.getProjectByName(project) if remote is not None: remotePath = remote.getPath() remoteName = remote.getName() elif os.path.isdir(project): remotePath = project remoteName = os.path.basename(project) else: raise UserError("Unknown project or invalid path: %s" % project) Console.info("Running %s of project %s...", Console.colorize(task, "bold"), Console.colorize(remoteName, "bold")) # Pauses this session to allow sub process fully accessing the same projects session.pause() # Build parameter list from optional arguments params = ["--%s=%s" % (key, kwargs[key]) for key in kwargs] if not "prefix" in kwargs: params.append("--prefix=%s" % session.getCurrentPrefix()) # Full list of args to pass to subprocess args = [__command, task] + params # Change into sub folder and execute jasy task oldPath = os.getcwd() os.chdir(remotePath) returnValue = subprocess.call(args, shell=sys.platform == "win32") os.chdir(oldPath) # Resumes this session after sub process was finished session.resume() # Error handling if returnValue != 0: raise UserError("Executing of sub task %s from project %s failed" % (task, project))