-
Notifications
You must be signed in to change notification settings - Fork 148
[WIP] feat: path resolver for languages #35
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from aws_lambda_builders.utils import which | ||
|
||
|
||
class PathResolver(object): | ||
|
||
def __init__(self, language, runtime): | ||
self.language = language | ||
self.runtime = runtime | ||
self.executables = [self.runtime, self.language] | ||
|
||
def _which(self): | ||
for executable in self.executables: | ||
path = which(executable) | ||
if path: | ||
return path | ||
raise ValueError("Path resolution for runtime: {} of language: " | ||
"{} was not successful".format(self.runtime, self.language)) | ||
|
||
@property | ||
def path(self): | ||
return self._which() |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,13 +3,77 @@ | |
""" | ||
|
||
import shutil | ||
import sys | ||
import os | ||
import logging | ||
|
||
|
||
LOG = logging.getLogger(__name__) | ||
|
||
|
||
def which(cmd, mode=os.F_OK | os.X_OK, path=None): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. couldnt find a port, but wil check the license. |
||
"""Given a command, mode, and a PATH string, return the path which | ||
conforms to the given mode on the PATH, or None if there is no such | ||
file. | ||
|
||
`mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result | ||
of os.environ.get("PATH"), or can be overridden with a custom search | ||
path. | ||
|
||
""" | ||
|
||
# Check that a given file can be accessed with the correct mode. | ||
# Additionally check that `file` is not a directory, as on Windows | ||
# directories pass the os.access check. | ||
def _access_check(fn, mode): | ||
return (os.path.exists(fn) and os.access(fn, mode) | ||
and not os.path.isdir(fn)) | ||
|
||
# If we're given a path with a directory part, look it up directly rather | ||
# than referring to PATH directories. This includes checking relative to the | ||
# current directory, e.g. ./script | ||
if os.path.dirname(cmd): | ||
if _access_check(cmd, mode): | ||
return cmd | ||
return None | ||
|
||
if path is None: | ||
path = os.environ.get("PATH", os.defpath) | ||
if not path: | ||
return None | ||
path = path.split(os.pathsep) | ||
|
||
if sys.platform == "win32": | ||
# The current directory takes precedence on Windows. | ||
if not os.curdir in path: | ||
path.insert(0, os.curdir) | ||
|
||
# PATHEXT is necessary to check on Windows. | ||
pathext = os.environ.get("PATHEXT", "").split(os.pathsep) | ||
# See if the given file matches any of the expected path extensions. | ||
# This will allow us to short circuit when given "python.exe". | ||
# If it does match, only test that one, otherwise we have to try | ||
# others. | ||
if any(cmd.lower().endswith(ext.lower()) for ext in pathext): | ||
files = [cmd] | ||
else: | ||
files = [cmd + ext for ext in pathext] | ||
else: | ||
# On other platforms you don't have things like PATHEXT to tell you | ||
# what file suffixes are executable, so just pass on cmd as-is. | ||
files = [cmd] | ||
|
||
seen = set() | ||
for dir in path: | ||
normdir = os.path.normcase(dir) | ||
if not normdir in seen: | ||
seen.add(normdir) | ||
for thefile in files: | ||
name = os.path.join(dir, thefile) | ||
if _access_check(name, mode): | ||
return name | ||
return None | ||
|
||
def copytree(source, destination, ignore=None): | ||
""" | ||
Similar to shutil.copytree except that it removes the limitation that the destination directory should | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
""" | ||
Base Class for Runtime Validators | ||
""" | ||
|
||
import abc | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a big fan of ABCs. Let's be grown ups and let the sub-classes decide which methods to impelement. Obviously if you didn't implement the right methods your subclass code will fail. raising Thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think abcs are helpful for a contract to be established. In the case of this class, its fairly opinionated on what it does. but if the class goes beyond what it currently does, we can think of moving away from abcs. But other than that, I dont see any harm in this ABC. |
||
import six | ||
|
||
|
||
@six.add_metaclass(abc.ABCMeta) | ||
class RuntimeValidator(object): | ||
SUPPORTED_RUNTIMES = [] | ||
|
||
@abc.abstractmethod | ||
def has_runtime(self): | ||
""" | ||
Checks if the runtime is supported. | ||
:param string runtime: Runtime to check | ||
:return bool: True, if the runtime is supported. | ||
""" | ||
raise NotImplementedError | ||
|
||
@abc.abstractmethod | ||
def validate_runtime(self, runtime_path): | ||
""" | ||
Checks if the language supplied matches the required lambda runtime | ||
:param string runtime_path: runtime language path to validate | ||
:raises MisMatchRuntimeError: Version mismatch of the language vs the required runtime | ||
""" | ||
raise NotImplementedError |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,8 @@ | |
""" | ||
|
||
from aws_lambda_builders.actions import BaseAction, Purpose, ActionFailedError | ||
from .packager import PythonPipDependencyBuilder, PackagerError | ||
from aws_lambda_builders.workflows.python_pip.utils import OSUtils | ||
from .packager import PythonPipDependencyBuilder, PackagerError, DependencyBuilder, SubprocessPip, PipRunner | ||
|
||
|
||
class PythonPipBuildAction(BaseAction): | ||
|
@@ -12,15 +13,22 @@ class PythonPipBuildAction(BaseAction): | |
DESCRIPTION = "Installing dependencies from PIP" | ||
PURPOSE = Purpose.RESOLVE_DEPENDENCIES | ||
|
||
def __init__(self, artifacts_dir, manifest_path, scratch_dir, runtime): | ||
def __init__(self, artifacts_dir, manifest_path, scratch_dir, runtime, runtime_path): | ||
self.artifacts_dir = artifacts_dir | ||
self.manifest_path = manifest_path | ||
self.scratch_dir = scratch_dir | ||
self.runtime = runtime | ||
self.package_builder = PythonPipDependencyBuilder() | ||
self.language_exec_path = runtime_path | ||
self.pip = SubprocessPip(osutils=OSUtils(), python_exe=runtime_path) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is nice and useful |
||
self.pip_runner = PipRunner(python_exe=runtime_path, pip=self.pip) | ||
self.package_builder = PythonPipDependencyBuilder( | ||
dependency_builder=DependencyBuilder(osutils=OSUtils(), | ||
pip_runner=self.pip_runner)) | ||
|
||
def execute(self): | ||
try: | ||
# import ipdb | ||
# ipdb.set_trace() | ||
self.package_builder.build_dependencies( | ||
self.artifacts_dir, | ||
self.manifest_path, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is super limiting. I can have
dotnetcore2.0
be the runtime anddotnetcore
be the language. But the actual executable is calleddotnet
I think you have optimized specifically for Python. We need to think more generally for other languages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True it is inspired from python's packages (tox, pipenv). My thinking is that the lambda runtime needs to be prioritized so that we can create arbitrary mapping via symlinks.