def write_settings(settings_path, settings, dry_run): if dry_run: return parent_dir = os.path.dirname(settings_path) if not os.path.exists(parent_dir): makedirs(parent_dir) json_dump_file(settings_path, settings, internal=True)
def inner_function(*args, **kwargs): # calculate a cache key based on the decorated method signature key = u"{}{}{}{}".format( re.sub( "pluginbase\._internalspace.[^\.]+\.", "click_project.plugins.", f.__module__), f.__name__, args, kwargs).encode(u"utf-8") # print(key) key = hashlib.sha1(key).hexdigest() if not os.path.exists(cache_folder): makedirs(cache_folder) filepath = os.path.join(cache_folder, key) # verify that the cached object exists and is less than $seconds old if os.path.exists(filepath): modified = os.path.getmtime(filepath) age_seconds = time.time() - modified if expire is None or age_seconds < expire: return pickle.load(open(filepath, u"rb")) # call the decorated function... result = f(*args, **kwargs) # ... and save the cached object for next time pickle.dump(result, open(filepath, u"wb")) return result
def write_settings(self): if self.read_only: raise click.UsageError( "You cannot edit the configuration if the migration is not persisted" ) makedirs(self.location) self.write_version() return write_settings(self.settings_path, self.settings, self.dry_run)
def write_version(self): if self.frozen_during_migration: raise click.UsageError( "You cannot edit the configuration if the migration is not persisted" ) makedirs(self.location) version = self.version createfile(self.version_file_name, ensure_unicode(str(version)), internal=True)
def write_settings(self): if self.readonly: raise click.UsageError( f"Cannot write into {self.name}. It is read only.") if self.frozen_during_migration: raise click.UsageError( "You cannot edit the configuration if the migration is not persisted" ) makedirs(self.location) self.write_version() return write_settings(self.settings_path, self.settings, self.dry_run)
def _move(customcommand, profile, force): """Move a custom commands""" new_location = Path(profile.location) / "bin" / Path( customcommand.customcommand_path).name if new_location.exists() and not force: raise click.UsageError(f"I won't overwrite {new_location}," " unless called with --force") makedirs(new_location.parent) move(customcommand.customcommand_path, new_location) LOGGER.status( f"Moved {customcommand.customcommand_path} into {new_location}")
def _move(profile_source, plugin, profile_destination, force): """Move a custom commands""" old_location = Path(profile_source.location) / "plugins" / (plugin + ".py") new_location = Path( profile_destination.location) / "plugins" / (plugin + ".py") if new_location.exists() and not force: raise click.UsageError(f"I won't overwrite {new_location}," " unless called with --force") makedirs(new_location.parent) move(old_location, new_location) LOGGER.status(f"Moved {old_location} into {new_location}")
def migrate_settings_to_recipe(settings_level_file, name): if open(settings_level_file, "rb").read().decode("utf-8").strip() == "{}": return False makedirs(recipes_dir + "/" + name) enabled = not json.load(open(settings_level_file)).get("_self", {}).get("disabled", False) order = json.load(open(settings_level_file)).get("_self", {}).get("order", 100) move(settings_level_file, recipes_dir + "/" + name + "/{}.json".format(self.app_name)) createfile(recipes_dir + "/" + name + "/version.txt", str(self.version + 1)) createfile(recipes_dir + "/" + name + ".json", json.dumps( { "enabled": enabled, "order": order, } ) ) return True
def python(name, open, force, description, body): """Create a bash custom command""" if not name.endswith(".py"): name += ".py" script_path = Path( config.customcommands.profile.location) / "python" / name makedirs(script_path.parent) if script_path.exists() and not force: raise click.UsageError(f"Won't overwrite {script_path} unless" " explicitly asked so with --force") command_name = script_path.name[:-len(".py")] script_path.write_text(f"""#!/usr/bin/env python3 # -*- coding:utf-8 -*- from pathlib import Path import click from click_project.decorators import ( argument, flag, option, command, use_settings, table_format, table_fields, ) from click_project.lib import ( TablePrinter, call, ) from click_project.config import config from click_project.log import get_logger from click_project.types import DynamicChoice LOGGER = get_logger(__name__) @command() def {command_name}(): "{description}" {body} """) if open: click.edit(filename=str(script_path))
def migrate(self, persist=True): LOGGER.action("migrate profile in {}".format(self.location)) if self.dry_run: return False if os.path.exists(self.backup_location): LOGGER.error( "{} already exists. Cannot migrate.".format(self.backup_location) ) return False makedirs(self.backup_location) for name in self.migration_impact: if os.path.exists(os.path.join(self.location, name)): copy( os.path.join(self.location, name), os.path.join(self.backup_location, name), ) try: res = self._migrate(persist) except Exception as e: import traceback LOGGER.error(e) LOGGER.develop(traceback.format_exc()) res = False if res: rm(self.backup_location) else: if persist: LOGGER.warning( "The migration of {} did not go well," " Restoring backup from {}".format( self.location, self.backup_location ) ) for name in self.migration_impact: if os.path.exists(os.path.join(self.backup_location, name)): if os.path.exists(os.path.join(self.location, name)): rm( os.path.join(self.location, name) ) copy( os.path.join(self.backup_location, name), os.path.join(self.location, name), ) rm(self.backup_location) return res
def create(profile_source, new_name, open, force, body, description): """Rename the plugin""" script_path = Path( profile_source.location) / "plugins" / (new_name + ".py") makedirs(script_path.parent) if script_path.exists() and not force: raise click.UsageError(f"Won't overwrite {script_path} unless" " explicitly asked so with --force") script_path.write_text(f"""#!/usr/bin/env python3 # -*- coding:utf-8 -*- "{description}" from pathlib import Path from click_project.config import config def load_plugin(): "Put here the entrypoint of the plugin." {body} """) if open: click.edit(filename=str(script_path))
def record_log(output_directory): for c in config.slack.conversations.values(): LOGGER.info("Dumping history of {}".format(c.data["name_fmt"])) output_directory_conversation = os.path.join( output_directory, c.data["name_fmt"]) last_record_file = os.path.join( output_directory_conversation, ".oldest") if os.path.exists(last_record_file): with open(last_record_file) as f: oldest = f.read() else: oldest = None makedirs(output_directory_conversation) with open(os.path.join( output_directory_conversation, "messages.json"), "a", encoding="utf-8" ) as message_file: now = datetime.datetime.now() for message in c.history(oldest=oldest): message_file.write(json.dumps(message.data)) message_file.write("\n") with open(last_record_file, "w", encoding="utf-8") as f: f.write("{}".format(now.strftime("%s")))
def install_plugin(profile, force, plugin, login, password, develop, no_deps): """Install a plugin""" dest_dir = profile.pluginsdir def plugin_location(plugin): return os.path.join(dest_dir, "{}.py".format(plugin_file_name(plugin))) for _plugin in plugin: if not force and os.path.exists(plugin_location(_plugin)): raise click.UsageError( "{} already installed in profile {}." " If you really want to install it, use --force or" " run plugin uninstall --{} {}".format(_plugin, profile.name, profile.name, _plugin)) for _plugin in plugin: if is_local_plugin(_plugin): if _plugin.startswith("file://"): content = open(_plugin[len("file://"):], "rb").read().decode("utf-8") plugin_file = os.path.abspath(_plugin[len("file://"):]) else: content = open(_plugin, "rb").read().decode("utf-8") plugin_file = os.path.abspath(_plugin) plugin_name = os.path.basename(plugin_file)[:-3] plugin_dir = os.path.dirname(plugin_file) if os.path.basename(plugin_dir) != "{}_plugins".format( config.app_name): raise click.UsageError( "{} does not look like a plugin directory".format( plugin_dir)) user_name = os.path.basename(os.path.dirname(plugin_dir)) plugin_name = "{}/{}".format(user_name, plugin_name) else: content = get_plugin_from_remote(_plugin) plugin_name = _plugin def install_requirements(): require_prefix = "# require: " requires = [ line[len(require_prefix):] for line in content.splitlines() if line.startswith(require_prefix) ] for require in requires: LOGGER.status("Installing requirement {}".format(require)) args = ['--upgrade', require] if force: args.append("--force-reinstall") pip("install", args) plugin_require_prefix = "# plugin_require: " plugin_requires = [ line[len(plugin_require_prefix):] for line in content.splitlines() if line.startswith(plugin_require_prefix) ] for plugin_require in plugin_requires: LOGGER.status( "Installing plugin requirements {}".format(plugin_require)) install_plugin( profile=profile, force=force, login=login, password=password, plugin=[plugin_require], develop=False, no_deps=no_deps, ) if not no_deps: install_requirements() LOGGER.status("Installing plugin {} in profile {}".format( plugin_name, profile.name)) location = plugin_location(plugin_name) makedirs(os.path.dirname(location)) if is_local_plugin(_plugin) and develop: LOGGER.debug("Installing {} in develop mode".format(plugin_name)) if force and os.path.exists(location): rm(location) ln(plugin_file, location) else: createfile(location, content)
def bash(name, open, force, description, body, from_alias, flowdeps, source_bash_helpers): """Create a bash custom command""" if name.endswith(".sh"): LOGGER.warning("Removing the extra .sh so that clk won't confuse it" " with a command name.") name = name[:len(".sh")] script_path = Path(config.customcommands.profile.location) / "bin" / name makedirs(script_path.parent) if script_path.exists() and not force: raise click.UsageError(f"Won't overwrite {script_path} unless" " explicitly asked so with --force") options = [] arguments = [] flags = [] remaining = "" args = "" if from_alias: if body: body = body + "\n" body = body + "\n".join( config.main_command.path + " " + " ".join(map(quote, command)) for command in config.settings["alias"][from_alias]["commands"]) flowdeps = list(flowdeps) + get_flow_commands_to_run(from_alias) alias_cmd = get_command(from_alias) if description: description = description + "\n" description = description + f"Converted from the alias {from_alias}" def guess_type(param): if type(param.type) == click.Choice: return json.dumps(list(param.type.choices)) elif param.type == int: return "int" elif param.type == float: return "float" else: return "str" for param in alias_cmd.params: if type(param) == Option: if param.is_flag: flags.append( f"F:{','.join(param.opts)}:{param.help}:{param.default}" ) args += f""" if [ "${{{config.main_command.path.upper()}___{param.name.upper()}}}" == "True" ] then args+=({param.opts[-1]}) fi""" else: options.append( f"O:{','.join(param.opts)}:{guess_type(param)}:{param.help}" ) args += f""" if [ -n "${{{config.main_command.path.upper()}___{param.name.upper()}}}" ] then args+=({param.opts[-1]} "${{{config.main_command.path.upper()}___{param.name.upper()}}}") fi""" elif type(param) == Argument: if param.nargs == -1: remaining = param.help else: arguments.append( f"A:{','.join(param.opts)}:{guess_type(param)}:{param.help}" ) args += f""" args+=("${{{config.main_command.path.upper()}___{param.name.upper()}}}") """ if args: args = """# Build the arguments of the last command of the alias args=()""" + args body += ' "${args[@]}"' if remaining: body += ' "${@}"' if flowdeps: flowdeps_str = "flowdepends: " + ", ".join(flowdeps) + "\n" else: flowdeps_str = "" if options: options_str = "\n".join(options) + "\n" else: options_str = "" if arguments: arguments_str = "\n".join(arguments) + "\n" else: arguments_str = "" if flags: flags_str = "\n".join(flags) + "\n" else: flags_str = "" if remaining: remaining_str = f"N:{remaining}\n" else: remaining_str = "" script_path.write_text(f"""#!/bin/bash -eu source "${{CLK_INSTALL_LOCATION}}/commands/customcommand/_clk.sh" clk_usage () {{ cat<<EOF $0 {description} -- {flowdeps_str}{options_str}{flags_str}{arguments_str}{remaining_str} EOF }} clk_help_handler "$@" {args} {body} """) chmod(script_path, 0o755) if open: click.edit(filename=str(script_path))
def fork(force, name): """Create a brand new project, based on clk that can be used by itself.""" output = Path(name) if output.exists(): if force: rm(output) else: raise click.UsageError(f"{output} already exist") makedirs(output) createfile( output / "setup.py", f"""#!/usr/bin/env python3 # -*- coding:utf-8 -*- from setuptools import setup, find_packages setup( name='{name}', version="0.0.0", packages=find_packages(), zip_safe=False, install_requires=[ "click-project", ], entry_points={{ 'console_scripts': [ '{name}={name}.main:main', ] }}, ) """) package = (output / name) makedirs(package) createfile( package / "main.py", f"""#!/usr/bin/env python3 # -*- coding:utf-8 -*- from click_project.setup import basic_entry_point, main from click_project.decorators import command, argument, flag, option @basic_entry_point( __name__, extra_command_packages=["{name}.commands"], exclude_core_commands=["git-sync"], ) def {name}(**kwargs): pass if __name__ == "__main__": main() """) createfile(package / "__init__.py", f"""#!/usr/bin/env python3""") commands = (package / "commands") makedirs(commands) (commands / "somecommand.py", """#!/usr/bin/env python3 # -*- coding:utf-8 -*- import click from click_project.decorators import command, argument, flag, option from click_project.log import get_logger LOGGER = get_logger(__name__) @command() @argument("some-argument", help="some argument") @flag("--some-flag/--no-some-flag", help="some flag") @option("--some-option", help="some option") def somecommand(some_argument, some_flag, some_option): "Some command" LOGGER.info(some_argument) LOGGER.info(some_flag) LOGGER.info(some_option) """) print(f"Now, install {name} with `python3 -m pip install {name}`," f" enable its completion with `{name} completion install`" f" and don't forget to have fun")