Skip to content

feat: Path Resolver and Validator #55

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

Merged
merged 18 commits into from
Jan 14, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ 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 install bundler --no-ri --no-rdoc"
- "bundler --version"
- "echo %PATH%"

test_script:
- "%PYTHON%\\python.exe -m pytest --cov aws_lambda_builders --cov-report term-missing tests/unit tests/functional"
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

# Install the code requirements
- make init
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