Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend and improve acre #2

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 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
4 changes: 4 additions & 0 deletions env_prototype/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
compute,
merge,
get_tools,
which,
execute,

CycleError,
DynamicKeyClashError
Expand All @@ -15,6 +17,8 @@
"compute",
"merge",
"get_tools",
"which",
"execute",

"CycleError",
"DynamicKeyClashError"
Expand Down
108 changes: 102 additions & 6 deletions env_prototype/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import re
import os
import platform
import subprocess
import sys

from . import lib

Expand Down Expand Up @@ -154,7 +156,11 @@ def parse(env, platform_name=None):


def append(env, env_b):
"""Append paths of environment b into environment"""
"""Append paths of environment b into environment

Returns:
env (dict)
"""
# todo: should this be refactored to "join" or "extend"
# todo: this function name might also be confusing with "merge"
env = env.copy()
Expand All @@ -177,8 +183,8 @@ def get_tools(tools, platform_name=None):
tool X can rely on variables of tool Y).

Examples:
get_environment(["maya2018", "yeti2.01", "mtoa2018"])
get_environment(["global", "fusion9", "ofxplugins"])
get_tools(["maya2018", "yeti2.01", "mtoa2018"])
get_tools(["global", "fusion9", "ofxplugins"])

Args:
tools (list): List of tool names.
Expand Down Expand Up @@ -209,18 +215,18 @@ def get_tools(tools, platform_name=None):
environment = dict()
for tool_path in tool_paths:

# Load tool
# Load tool environment
try:
with open(tool_path, "r") as f:
tool_env = json.load(f)
log.debug('Read tool successfully: {}'.format(tool_path))
except IOError:
log.debug(
log.error(
'Unable to find the environment file: "{}"'.format(tool_path)
)
continue
except ValueError as e:
log.debug(
log.error(
'Unable to read the environment file: "{0}", due to:'
'\n{1}'.format(tool_path, e)
)
Expand Down Expand Up @@ -257,3 +263,93 @@ def merge(env, current_env):

return result


def which(program, env):
"""Locate `program` in PATH

Ensure `PATHEXT` is declared in the environment if you want to alter the
priority of the system extensions:

Example : ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC"

Arguments:
program (str): Name of program, e.g. "python"
env (dict): an environment dictionary

"""

def is_exe(fpath):
if os.path.isfile(fpath) and os.access(fpath, os.X_OK):
return True
return False

paths = env["PATH"].split(os.pathsep)
extensions = env.get("PATHEXT", os.getenv("PATHEXT", ""))

for path in paths:
for ext in extensions.split(os.pathsep):
fname = program + ext.lower()
abspath = os.path.join(path.strip('"'), fname)
if is_exe(abspath):
return abspath

return None


def execute(executable, args=None, environment=None, cwd=None):
Copy link
Owner

Choose a reason for hiding this comment

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

What's the reason for refactoring this to execute. It's the same method as in Avalon pipeline currently, but there it's called launch? Any reason why the original was a bad name?

Copy link
Owner

Choose a reason for hiding this comment

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

@aardschok should we still refactor this to launch?

"""Launch a new subprocess of `args`

Arguments:
executable (str): Relative or absolute path to executable
args (list): Command passed to `subprocess.Popen`
environment (dict, optional): Custom environment passed
to Popen instance.
cwd (str): the current working directory

Returns:
Popen instance of newly spawned process

Exceptions:
OSError on internal error
ValueError on `executable` not found

"""

CREATE_NO_WINDOW = 0x08000000
CREATE_NEW_CONSOLE = 0x00000010
IS_WIN32 = sys.platform == "win32"
PY2 = sys.version_info[0] == 2

abspath = executable

env = (environment or os.environ)

if PY2:
# Protect against unicode, and other unsupported
# types amongst environment variables
enc = sys.getfilesystemencoding()
env = {k.encode(enc): v.encode(enc) for k, v in env.items()}

kwargs = dict(
args=[abspath] + args or list(),
env=env,
cwd=cwd,

stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,

# Output `str` through stdout on Python 2 and 3
universal_newlines=True,
)

if env.get("CREATE_NEW_CONSOLE"):
kwargs["creationflags"] = CREATE_NEW_CONSOLE
kwargs.pop("stdout")
kwargs.pop("stderr")
else:
if IS_WIN32:
kwargs["creationflags"] = CREATE_NO_WINDOW

popen = subprocess.Popen(**kwargs)

return popen
39 changes: 39 additions & 0 deletions env_prototype/launcher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import os
import pprint

from . import api


def launch(tools, executable, args):

tools_env = api.get_tools(tools.split(";"))
env = api.compute(tools_env)

env = api.merge(env, current_env=dict(os.environ))
print("Environment:\n%s" % pprint.pformat(env, indent=4))

# Search for the executable within the tool's environment
# by temporarily taking on its `PATH` settings
exe = api.which(executable, env)
if not exe:
raise ValueError("Unable to find executable: %s" % executable)

print("Launching: %s" % exe)
api.execute(exe, environment=env, args=args)


if __name__ == '__main__':

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--tools",
help="The tool environments to include. "
"These should be separated by `;`",
required=True)
parser.add_argument("--executable",
help="The executable to run. ",
required=True)

kwargs, args = parser.parse_known_args()

launch(tools=kwargs.tools, executable=kwargs.executable, args=args)