PK!2ךsnakeless/__init__.pyimport sys from .cli_controller import SnakelessCli def main(argv=sys.argv[1:]): snakeless_cli = SnakelessCli() return snakeless_cli.run(argv) PK! <]]snakeless/__main__.pyimport sys from . import main if __name__ == '__main__': sys.exit(main(sys.argv[1:])) PK!v{WKKsnakeless/cli_controller.pyfrom cliff import help as cliff_help from cliff.app import App from cliff.commandmanager import CommandManager class SnakelessCli(App): def __init__(self): super().__init__( description="Snakeless CLI", version="0.1.2", command_manager=CommandManager("snakeless.cli"), deferred_help=True, ) def initialize_app(self, argv): if self.interactive_mode: # disable interactive mode action = cliff_help.HelpAction(None, None, default=self) action(self.parser, self.options, None, None) PK!˶CCsnakeless/commands/__init__.pyfrom .check import Check # noqa from .deploy import Deploy # noqaPK!{00snakeless/commands/check.pyimport logging import fs from cliff.command import Command from schema import SchemaError from ..constants import CURRENT_DIR from ..helpers import ( check_config_existence, snakeless_spinner, parse_config ) from ..exceptions import CommandFailure class Check(Command): "A health-check of your snakeless setup" logger = logging.getLogger(__name__) def check_config_existence(self, root_fs): with snakeless_spinner( text="Checking the config existence.", spinner="dots" ) as spinner: config_file_exists = check_config_existence(root_fs) if not config_file_exists: raise CommandFailure("Config was not found.") else: spinner.succeed("Config was found.") def validate_config(self, root_fs): with snakeless_spinner( text="Validating the config file.", spinner="dots" ) as spinner: try: parse_config(root_fs) except fs.errors.ResourceNotFound: raise CommandFailure("Config does not anymore exist.") except SchemaError as exc: raise CommandFailure("Config validation failed") else: spinner.succeed("Config is valid.") def take_action(self, parsed_args): with fs.open_fs(CURRENT_DIR) as root_fs: try: self.check_config_existence(root_fs) self.validate_config(root_fs) except Exception as exc: self.logger.exception(exc, exc_info=True) PK!fsnakeless/commands/deploy.pyimport logging import fs from cliff.command import Command from ..constants import CURRENT_DIR from ..mixins import DeployerMixin, ConfigLoaderMixin class Deploy(Command, DeployerMixin, ConfigLoaderMixin): "Deploy some functions or all of themn" logger = logging.getLogger(__name__) def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( "-f", nargs="*", default=".", help="A list of functions' names" ) return parser def take_action(self, parsed_args): parsed_args = vars(parsed_args) functions_to_deploy = parsed_args.get('f', []) with fs.open_fs(CURRENT_DIR) as root_fs: try: config = self.load_config(root_fs) if not functions_to_deploy: functions_to_deploy = config['functions'].keys() self.deploy_functions(config, functions_to_deploy) except Exception as exc: self.logger.exception(exc, exc_info=True) PK!C&&snakeless/constants.pyimport os CURRENT_DIR = os.getcwd() PK!E**snakeless/exceptions.pyclass CommandFailure(Exception): pass PK! W snakeless/helpers.pyfrom functools import lru_cache from pkg_resources import iter_entry_points from contextlib import contextmanager from halo import Halo from yaml import load from schema import Schema, And, Use, Optional from .exceptions import CommandFailure try: from yaml import CLoader as Loader except ImportError: from yaml import Loader @contextmanager def snakeless_spinner(*args, **kwargs): spinner = Halo(*args, **kwargs) spinner.start() try: yield spinner except CommandFailure as exc: spinner.fail(str(exc)) raise except Exception as exc: spinner.fail( spinner.text + "\n" + "Unexpected exception." ) raise finally: spinner.stop() def check_config_existence(root_fs, file_name="snakeless.yml"): return root_fs.exists(file_name) @lru_cache() def get_providers(): providers = {} for entry_points in iter_entry_points("snakeless.providers"): providers[entry_point.name] = entry_point.load() return providers @lru_cache() def get_schemas(): schemas = {} for entry_points in iter_entry_points("snakeless.schemas"): schemas[entry_point.name] = entry_point.load() return schemas def get_provider(provider_name, config): providers = get_providers() for entry_points in iter_entry_points("snakeless.providers"): providers[entry_point.name] = entry_point.load() return providers[provider_name](config) @lru_cache(maxsize=None) def get_schema(provider_name): PROVIDERS = get_providers() SUPPORTED_PROVIDERS = PROVIDERS.keys() BASE_SCHEMA = { "project": { "name": str, "provider": And( Use(str), lambda provider: provider in SUPPORTED_PROVIDERS, error="Unsupported provider", ), }, } schemas = get_schemas() provider_schema = schemas[provider_name] return merge(BASE_SCHEMA, provider_schema) def parse_config( root_fs, file_name="snakeless.yml" ): config_file_data = root_fs.gettext("snakeless.yml") raw_parsed_config = load(config_file_data, Loader=Loader) try: provider_name = raw_parsed_config["project"]["provider"] except KeyError: raise else: provider_schema = get_schema(provider_name) validator = Schema(provider_schema, ignore_extra_keys=True) parse_config = validator.validate(raw_parsed_config) # TODO: validate a few more fields mannualy return parse_config def merge(source, destination): for key, value in source.items(): if isinstance(value, dict): node = destination.setdefault(key, {}) merge(value, node) else: destination[key] = value return destination PK!8x{``snakeless/mixins/__init__.pyfrom .deployer import DeployerMixin # noqa from .config_loader import ConfigLoaderMixin # noqaPK!Cx!snakeless/mixins/config_loader.pyimport fs from schema import SchemaError from ..helpers import snakeless_spinner, parse_config from ..exceptions import CommandFailure class ConfigLoaderMixin(object): def load_config(self, root_fs): with snakeless_spinner( text="Loading the config file...", spinner="dots" ) as spinner: try: config = parse_config(root_fs) except fs.errors.ResourceNotFound: raise CommandFailure("Config does not exist.") except SchemaError as exc: raise CommandFailure("Config validation failed.") else: spinner.succeed("The config file was loaded.") return config PK!5snakeless/mixins/deployer.pyfrom ..helpers import snakeless_spinner, get_provider class DeployerMixin(object): def deploy_functions(self, config, functions_to_deploy): with snakeless_spinner( text="Deploying functions...", spinner="dots" ) as spinner: provider_name = config["project"]["provider"] provider = get_provider(provider_name, config) for function_to_deploy in functions_to_deploy: spinner.text = ( f"Deploying the { function_to_deploy } function..." ) provider.deploy_function(function_to_deploy) spinner.succeed("All functions were deployed!") PK!snakeless/providers/__init__.pyPK!rccsnakeless/providers/base.pyimport fs class BaseProvider(object): def __init__(self, config): raise NotImplemented def get_func_data(self, func_name, func_data_key, default=None): return self.config["functions"][func_name].get(func_data_key, default) def get_project_data(self, data_key): return self.config["project"][data_key] def get_func_or_project_data(self, func_name, func_data_key, default=None): return self.get_func_data( func_name, func_data_key, default ) or self.get_project_data(func_data_key) def get_env_variables(self, func_name): should_merge_env = self.get_func_data(func_name, "merge_env", False) project_env_file = self.get_project_data("env_file_path") if project_env_file: project_env = self.parse_env_file(project_env_file) else: project_env = {} func_env_file = self.get_func_data(func_name, "env_file_path") if func_env_file: func_env = self.parse_env_file(func_env_file) else: func_env = {} resulting_env = {} if func_env and should_merge_env: resulting_env.update({**project_env, **func_env}) elif func_env: resulting_env = func_env else: resulting_env = project_env return resulting_env def parse_env_file(self, env_file_path): env_variables = {} with fs.open_fs(".") as root_fs: with root_fs.open(env_file_path) as env_file: env_variables = { key: value for key, value in ( line.strip().split("=", maxsplit=2) for line in env_file ) } return env_variables def deploy_function(self, func_name, spinner): raise NotImplemented PK!HΫqP}*snakeless-0.2.0.dist-info/entry_points.txtN+I/N.,()*KNI-.r3󸸢z9\ٶHy)V ԂJl.`..PK!Í\--!snakeless-0.2.0.dist-info/LICENSEMIT License Copyright (c) 2018 German Ivanov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.PK!H\TTsnakeless-0.2.0.dist-info/WHEEL 1 0 нR \I$ơ7.ZON `h6oi14m,b4>4ɛpK>X;baP>PK!H"snakeless-0.2.0.dist-info/METADATAUn7}W>Ă/! jH*@n ۩DV$!Oo:nvn>i̙Y 'elpd=j$pXCqFn^#o'6HTXgۉ_m}'k\h4ԌŻFRLU!^oi G}# [ilzF*=1_&+G_v|T9ϴ$Rs,|és޶X ? ia"M_fJOG_l >WbRG9Vy9z2<&°ceN=F傽^*^'3j!`Iŏ\A" W\K5Mt$acx_#.\`3U ǫu- esjn{[gSX_/ 'vy?݅G m@1QCXZ TvPӶkοIÂ.;- FwZ#is)I'8$%)q˪8cX+âlCn[ Jy4QD9EH[BUwQP@zY,AuPN?1yGENfvl#*{d;̕tly'd$Ǘ0Seo|5+7v}蒗 k wr`v|mei;K+{|mUval(ܙfpʱG_slQ[N_䷜b| _pn˘u$$Mƫ;";17Hh* @ezނ,}_f-*/ L+re/R:ճ/8y PK!HPs{  snakeless-0.2.0.dist-info/RECORD}ےcZyz\ Q’8.a!յuM_}};&GQb /IQ9 C6Oreq,5͔(a(sVy k̊w_rz2Ȍĸ8)+??o 8C7s9<\^2փIw{Qޙ)h+(NK^7YcFUU][lx jaMq7Qܠ~ȩ ژB?W$$Rc1cE% <ТE.?`rXVb5iJA2}n&cT-i- YH)Lg%}쒖 Fs3oNW\J .Ŋm w$2>,Eop݅A*>o*5_4dL}R,#V47ߙh8ea JֺSS͝ʱy)ϟw\|nQOm!k'&#ר})`8sY-{w#5Q4ʓYo?PK!2ךsnakeless/__init__.pyPK! <]]snakeless/__main__.pyPK!v{WKK]snakeless/cli_controller.pyPK!˶CCsnakeless/commands/__init__.pyPK!{00`snakeless/commands/check.pyPK!f snakeless/commands/deploy.pyPK!C&&snakeless/constants.pyPK!E**ssnakeless/exceptions.pyPK! W snakeless/helpers.pyPK!8x{``snakeless/mixins/__init__.pyPK!Cx!snakeless/mixins/config_loader.pyPK!5snakeless/mixins/deployer.pyPK!|!snakeless/providers/__init__.pyPK!rcc!snakeless/providers/base.pyPK!HΫqP}*U)snakeless-0.2.0.dist-info/entry_points.txtPK!Í\--!)snakeless-0.2.0.dist-info/LICENSEPK!H\TTY.snakeless-0.2.0.dist-info/WHEELPK!H".snakeless-0.2.0.dist-info/METADATAPK!HPs{  2snakeless-0.2.0.dist-info/RECORDPK~6