Skip to content

Commit

Permalink
Merge pull request #6 from Colorbleed/FUS4
Browse files Browse the repository at this point in the history
Implement configuration actions
  • Loading branch information
BigRoy authored Mar 30, 2018
2 parents 38068bc + aa6b905 commit 89b6f62
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 41 deletions.
23 changes: 20 additions & 3 deletions launcher/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
from .actions import register_default_actions
import sys

# todo(roy): Maybe install studio config instead of "launcher" default
register_default_actions()

self = sys.modules[__name__]
self._is_installed = False


def install():
"""Register actions"""

if self._is_installed:
return

from .actions import register_config_actions, register_default_actions

print("Registering default actions..")
register_default_actions()
print("Registering config actions..")
register_config_actions()

self._is_installed = True
18 changes: 16 additions & 2 deletions launcher/actions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import os
import importlib

import avalon.api
from avalon import lib

Expand All @@ -12,7 +15,6 @@ def is_compatible(self, session):
return "AVALON_PROJECT" in session

def process(self, session, **kwargs):

return lib.launch(executable="python",
args=["-u", "-m", "avalon.tools.projectmanager",
session['AVALON_PROJECT']])
Expand All @@ -28,7 +30,6 @@ def is_compatible(self, session):
return "AVALON_PROJECT" in session

def process(self, session, **kwargs):

return lib.launch(executable="python",
args=["-u", "-m", "avalon.tools.cbloader",
session['AVALON_PROJECT']])
Expand All @@ -38,3 +39,16 @@ def register_default_actions():
"""Register default actions for Launcher"""
avalon.api.register_plugin(avalon.api.Action, ProjectManagerAction)
avalon.api.register_plugin(avalon.api.Action, LoaderAction)


def register_config_actions():
"""Register actions from the configuration for Launcher"""

module_name = os.environ["AVALON_CONFIG"]
config = importlib.import_module(module_name)
if not hasattr(config, "register_launcher_actions"):
print("Current configuration `%s` has no 'register_launcher_actions'"
% config.__name__)
return

config.register_launcher_actions()
4 changes: 4 additions & 0 deletions launcher/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ def __init__(self, root, source):
except IOError:
raise # Server refused to connect

# Install actions
from . import install
install()

terminal.init()

controller = control.Controller(root, self)
Expand Down
88 changes: 53 additions & 35 deletions launcher/control.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,28 +199,7 @@ def push(self, index):
handler(index)

# Push the compatible applications
actions = []
for Action in self._registered_actions:
frame = self.current_frame()

# Build a session from current frame
session = {"AVALON_{}".format(key.upper()): value for
key, value in frame.get("environment", {}).items()}
session["AVALON_PROJECTS"] = api.registered_root()
if not Action().is_compatible(session):
continue

actions.append({
"name": str(Action.name),
"icon": str(Action.icon or "cube"),
"label": str(Action.label or Action.name),
"color": getattr(Action, "color", None),
"order": Action.order
})

# Sort by order and name
actions = sorted(actions, key=lambda action: (action["order"],
action["name"]))
actions = self.collect_compatible_actions(self._registered_actions)
self._actions.push(actions)

self.navigated.emit()
Expand Down Expand Up @@ -267,19 +246,20 @@ def init(self):
"name": project["name"],
}, **project["data"])
for project in sorted(io.projects(), key=lambda x: x['name'])

# Discard hidden projects
if project["data"].get("visible", True)
if project["data"].get("visible", True) # Discard hidden projects
])

# No actions outside of projects
self._actions.push([])

frame = {
"environment": {},
}
frame = {"environment": {}}
self._frames[:] = [frame]

# Discover all registered actions
discovered_actions = api.discover(api.Action)
self._registered_actions[:] = discovered_actions

# Validate actions based on compatibility
actions = self.collect_compatible_actions(discovered_actions)
self._actions.push(actions)

self.pushed.emit(header)
self.navigated.emit()
terminal.log("ready")
Expand All @@ -301,8 +281,7 @@ def on_project_changed(self, index):
# Get available project actions and the application actions
actions = api.discover(api.Action)
apps = lib.get_apps(project)
actions.extend(apps)
self._registered_actions[:] = actions
self._registered_actions[:] = actions + apps

silos = io.distinct("silo")
self._model.push([
Expand Down Expand Up @@ -429,13 +408,14 @@ def trigger_action(self, index):
name = model.data(index, "name")

# Get the action
Action = next(a for a in self._registered_actions if a.name == name)
Action = next((a for a in self._registered_actions if a.name == name),
None)
assert Action, "No action found"
action = Action()

# Run the action within current session
self.log("Running action: %s" % name, level=INFO)
popen = action.process(api.Session.copy())

# Action might return popen that pipes stdout
# in which case we listen for it.
process = {}
Expand Down Expand Up @@ -470,6 +450,44 @@ def run(self):
def log(self, message, level=DEBUG):
print(message)

def collect_compatible_actions(self, actions):
"""Collect all actions which are compatible with the environment
Each compatible action will be translated to a dictionary to ensure
the action can be visualized in the launcher.
Args:
actions (list): list of classes
Returns:
list: collection of dictionaries sorted on order int he
"""

compatible = []
for Action in actions:
frame = self.current_frame()

# Build a session from current frame
session = {"AVALON_{}".format(key.upper()): value for
key, value in frame.get("environment", {}).items()}
session["AVALON_PROJECTS"] = api.registered_root()
if not Action().is_compatible(session):
continue

compatible.append({
"name": str(Action.name),
"icon": str(Action.icon or "cube"),
"label": str(Action.label or Action.name),
"color": getattr(Action, "color", None),
"order": Action.order
})

# Sort by order and name
compatible = sorted(compatible, key=lambda action: (action["order"],
action["name"]))

return compatible


def dirs(root):
try:
Expand Down
9 changes: 8 additions & 1 deletion launcher/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,14 @@ def stream(stream):


def get_apps(project):
"""Define dynamic Application classes for project using `.toml` files"""
"""Define dynamic Application classes for project using `.toml` files
Args:
project (dict): project document from the database
Returns:
list: list of dictionaries
"""

import avalon.lib
import avalon.api as api
Expand Down

0 comments on commit 89b6f62

Please sign in to comment.