Skip to content

Commit

Permalink
feat: Path Resolver and Validator (#55)
Browse files Browse the repository at this point in the history
The function "which" at aws_lambda_builders/utils.py was copied from
https://github.com/python/cpython/blob/3.7/Lib/shutil.py
SPDX-License-Identifier: Python-2.0
Copyright 2019 by the Python Software Foundation
  • Loading branch information
sriram-mv authored and jfuss committed Jan 14, 2019
1 parent 3f24c82 commit f53dc86
Show file tree
Hide file tree
Showing 32 changed files with 493 additions and 159 deletions.
3 changes: 2 additions & 1 deletion .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ install:
# To run Nodejs workflow integ tests
- ps: Install-Product node 8.10

- "set PATH=%PYTHON%\\Scripts;%PYTHON%\\bin;%PATH%"
- "set PATH=%PYTHON%;%PYTHON%\\Scripts;%PYTHON%\\bin;%PATH%"
- "%PYTHON%\\python.exe -m pip install -r requirements/dev.txt"
- "%PYTHON%\\python.exe -m pip install -e ."
- "set PATH=C:\\Ruby25-x64\\bin;%PATH%"
- "gem --version"
- "gem install bundler -v 1.17.3 --no-ri --no-rdoc"
- "bundler --version"
- "echo %PATH%"

# setup go
- rmdir c:\go /s /q
Expand Down
4 changes: 2 additions & 2 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=compat.py
ignore=compat.py, utils.py

# Pickle collected data for later comparisons.
persistent=yes
Expand Down Expand Up @@ -360,4 +360,4 @@ int-import-graph=

# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception
overgeneral-exceptions=Exception
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ install:
# To run Nodejs workflow integ tests
- nvm install 8.10.0
- nvm use 8.10.0
# To run Ruby workflow integ tests
- rvm install ruby-2.5.3
- rvm use ruby-2.5.3

# Go workflow integ tests require Go 1.11+
- eval "$(gimme 1.11.2)"
Expand Down
4 changes: 4 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
AWS Lambda Builders
Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.

The function "which" at aws_lambda_builders/utils.py was copied from https://github.com/python/cpython/blob/3.7/Lib/shutil.py
SPDX-License-Identifier: Python-2.0
Copyright 2019 by the Python Software Foundation
21 changes: 21 additions & 0 deletions aws_lambda_builders/binary_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""
Class containing resolved path of binary given a validator and a resolver and the name of the binary.
"""


class BinaryPath(object):

def __init__(self, resolver, validator, binary, binary_path=None):
self.resolver = resolver
self.validator = validator
self.binary = binary
self._binary_path = binary_path
self.path_provided = True if self._binary_path else False

@property
def binary_path(self):
return self._binary_path

@binary_path.setter
def binary_path(self, binary_path):
self._binary_path = binary_path
14 changes: 0 additions & 14 deletions aws_lambda_builders/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import logging

from aws_lambda_builders.registry import get_workflow, DEFAULT_REGISTRY
from aws_lambda_builders.validate import RuntimeValidator
from aws_lambda_builders.workflow import Capability

LOG = logging.getLogger(__name__)
Expand Down Expand Up @@ -91,8 +90,6 @@ def build(self, source_dir, artifacts_dir, scratch_dir, manifest_path,
:param options:
Optional dictionary of options ot pass to build action. **Not supported**.
"""
if runtime:
self._validate_runtime(runtime)

if not os.path.exists(scratch_dir):
os.makedirs(scratch_dir)
Expand All @@ -107,16 +104,5 @@ def build(self, source_dir, artifacts_dir, scratch_dir, manifest_path,

return workflow.run()

def _validate_runtime(self, runtime):
"""
validate runtime and local runtime version to make sure they match
:type runtime: str
:param runtime:
String matching a lambda runtime eg: python3.6
"""
RuntimeValidator.validate_runtime(required_language=self.capability.language,
required_runtime=runtime)

def _clear_workflows(self):
DEFAULT_REGISTRY.clear()
2 changes: 1 addition & 1 deletion aws_lambda_builders/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class UnsupportedManifestError(LambdaBuilderError):
class MisMatchRuntimeError(LambdaBuilderError):
MESSAGE = "{language} executable found in your path does not " \
"match runtime. " \
"\n Expected version: {required_runtime}, Found version: {found_runtime}. " \
"\n Expected version: {required_runtime}, Found version: {runtime_path}. " \
"\n Possibly related: https://github.com/awslabs/aws-lambda-builders/issues/30"


Expand Down
28 changes: 28 additions & 0 deletions aws_lambda_builders/path_resolver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""
Basic Path Resolver that looks for the executable by runtime first, before proceeding to 'language' in PATH.
"""

from aws_lambda_builders.utils import which


class PathResolver(object):

def __init__(self, binary, runtime):
self.binary = binary
self.runtime = runtime
self.executables = [self.runtime, self.binary]

def _which(self):
exec_paths = []
for executable in [executable for executable in self.executables if executable is not None]:
paths = which(executable)
exec_paths.extend(paths)

if not exec_paths:
raise ValueError("Path resolution for runtime: {} of binary: "
"{} was not successful".format(self.runtime, self.binary))
return exec_paths

@property
def exec_paths(self):
return self._which()
75 changes: 75 additions & 0 deletions aws_lambda_builders/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

import shutil
import sys
import os
import logging

Expand Down Expand Up @@ -57,3 +58,77 @@ def copytree(source, destination, ignore=None):
copytree(new_source, new_destination, ignore=ignore)
else:
shutil.copy2(new_source, new_destination)

# NOTE: The below function is copied from Python source code and modified
# slightly to return a list of paths that match a given command
# instead of returning just the first match

# The function "which" at aws_lambda_builders/utils.py was copied from https://github.com/python/cpython/blob/3.7/Lib/shutil.py
# SPDX-License-Identifier: Python-2.0
# Copyright 2019 by the Python Software Foundation


def which(cmd, mode=os.F_OK | os.X_OK, path=None): # pragma: no cover
"""Given a command, mode, and a PATH string, return the paths 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.
Note: This function was backported from the Python 3 source code.
"""
# 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 os.curdir not 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()
paths = []

for dir in path:
normdir = os.path.normcase(dir)
if normdir not in seen:
seen.add(normdir)
for thefile in files:
name = os.path.join(dir, thefile)
if _access_check(name, mode):
paths.append(name)
return paths
74 changes: 0 additions & 74 deletions aws_lambda_builders/validate.py

This file was deleted.

18 changes: 18 additions & 0 deletions aws_lambda_builders/validator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""
No-op validator that does not validate the runtime_path for a specified language.
"""

import logging

LOG = logging.getLogger(__name__)


class RuntimeValidator(object):

def __init__(self, runtime):
self.runtime = runtime
self._runtime_path = None

def validate(self, runtime_path):
self._runtime_path = runtime_path
return runtime_path
Loading

0 comments on commit f53dc86

Please sign in to comment.