diff --git a/mypy.ini b/mypy.ini index 0022588..565ef62 100644 --- a/mypy.ini +++ b/mypy.ini @@ -30,3 +30,6 @@ ignore_missing_imports = True [mypy-docker.*] ignore_missing_imports = True + +[mypy-mysql] +ignore_missing_imports = True diff --git a/scone/common/misc.py b/scone/common/misc.py index f1c9875..9ae1328 100644 --- a/scone/common/misc.py +++ b/scone/common/misc.py @@ -16,8 +16,10 @@ # along with Scone. If not, see . import os +import re import sys from hashlib import sha256 +from typing import Dict def eprint(*args, **kwargs): @@ -57,3 +59,25 @@ def sha256_file(path: str) -> str: def sha256_bytes(data: bytes) -> str: return sha256(data).hexdigest() + + +def multireplace(string: str, replacements: Dict[str, str]) -> str: + """ + Given a string and a replacement map, it returns the replaced string. + + :param string: string to execute replacements on + :param replacements: replacement dictionary {value to find: value to replace} + + source: https://stackoverflow.com/a/36620263 + """ + # Place longer ones first to keep shorter substrings from matching + # where the longer ones should take place + # For instance given the replacements {'ab': 'AB', 'abc': 'ABC'} against + # the string 'hey abc', it should produce 'hey ABC' and not 'hey ABc' + substrs = sorted(replacements, key=len, reverse=True) + + # Create a big OR regex that matches any of the substrings to replace + regexp = re.compile("|".join(map(re.escape, substrs))) + + # For each match, look up the new string in the replacements + return regexp.sub(lambda match: replacements[match.group(0)], string) diff --git a/scone/default/recipes/apt.py b/scone/default/recipes/apt.py index 6f383df..9bc2e01 100644 --- a/scone/default/recipes/apt.py +++ b/scone/default/recipes/apt.py @@ -20,7 +20,7 @@ import logging import random from asyncio import Lock from collections import defaultdict -from typing import List, Tuple, Dict +from typing import Dict, List, Tuple from scone.default.utensils.basic_utensils import SimpleExec from scone.head.head import Head diff --git a/scone/default/recipes/mysql.py b/scone/default/recipes/mysql.py index 5f89da7..fffcdf1 100644 --- a/scone/default/recipes/mysql.py +++ b/scone/default/recipes/mysql.py @@ -16,7 +16,7 @@ # along with Scone. If not, see . from typing import List -from scone.default.utensils.db_utensils import PostgresTransaction, MysqlTransaction +from scone.default.utensils.db_utensils import MysqlTransaction from scone.head.head import Head from scone.head.kitchen import Kitchen, Preparation from scone.head.recipe import Recipe, RecipeContext @@ -36,7 +36,11 @@ def mysql_dodgy_escape_username(unescaped: str) -> str: parts = unescaped.split("@") if len(parts) != 2: raise ValueError(f"{unescaped!r} is not a valid sconified mysql user name.") - return mysql_dodgy_escape_literal(parts[0]) + "@" + mysql_dodgy_escape_literal(parts[1]) + return ( + mysql_dodgy_escape_literal(parts[0]) + + "@" + + mysql_dodgy_escape_literal(parts[1]) + ) class MysqlDatabase(Recipe): @@ -59,12 +63,7 @@ class MysqlDatabase(Recipe): async def cook(self, kitchen: Kitchen) -> None: ch = await kitchen.start(MysqlTransaction("mysql", "root", unix_socket=True)) - await ch.send( - ( - "SHOW DATABASES LIKE %s", - self.database_name, - ) - ) + await ch.send(("SHOW DATABASES LIKE %s", self.database_name,)) dbs = await ch.recv() if len(dbs) > 0: await ch.send(None) @@ -93,7 +92,7 @@ class MysqlDatabase(Recipe): if len(res) != 0: raise RuntimeError("expected empty result set.") - q = f""" + q = """ FLUSH PRIVILEGES """ await ch.send((q,)) diff --git a/scone/default/recipes/systemd.py b/scone/default/recipes/systemd.py index cc221eb..e94d7a8 100644 --- a/scone/default/recipes/systemd.py +++ b/scone/default/recipes/systemd.py @@ -18,7 +18,8 @@ from scone.default.steps.systemd_steps import ( cook_systemd_daemon_reload, cook_systemd_enable, - cook_systemd_start, cook_systemd_stop, + cook_systemd_start, + cook_systemd_stop, ) from scone.head.head import Head from scone.head.kitchen import Kitchen, Preparation diff --git a/scone/default/utensils/db_utensils.py b/scone/default/utensils/db_utensils.py index 967ecac..0817c3f 100644 --- a/scone/default/utensils/db_utensils.py +++ b/scone/default/utensils/db_utensils.py @@ -123,7 +123,9 @@ class MysqlTransaction(Utensil): unix_socket = "/var/run/mysqld/mysqld.sock" if self.unix_socket else None - conn = mysql_connector.connect(database=self.database, user=self.user, unix_socket=unix_socket) + conn = mysql_connector.connect( + database=self.database, user=self.user, unix_socket=unix_socket + ) cur = conn.cursor() try: await queryloop() diff --git a/scone/sous/__main__.py b/scone/sous/__main__.py index e2b227e..b9eb80a 100644 --- a/scone/sous/__main__.py +++ b/scone/sous/__main__.py @@ -16,7 +16,6 @@ # along with Scone. If not, see . import asyncio -import itertools import logging import os import pwd @@ -98,7 +97,9 @@ async def main(args: List[str]): logger.debug("going to sched task with %r", utensil) - awaitables.append(asyncio.create_task(run_utensil(utensil, channel, worktop))) + awaitables.append( + asyncio.create_task(run_utensil(utensil, channel, worktop)) + ) elif "lost" in message: # for a then-non-existent channel, but probably just waiting on us # retry without a default route.