diff --git a/amanuensis/__main__.py b/amanuensis/__main__.py index c43b07e..92114d5 100644 --- a/amanuensis/__main__.py +++ b/amanuensis/__main__.py @@ -46,7 +46,10 @@ def repl(args): traceback.print_exc() def process_doc(docstring): - return '\n'.join([line.strip() for line in (docstring or "").strip().splitlines()]) + return '\n'.join([ + line.strip() + for line in (docstring or "").strip().splitlines() + ]) def get_parser(valid_commands): # Set up the top-level parser. @@ -84,7 +87,10 @@ def get_parser(valid_commands): metavar="USERNAME", dest="tl_username", help="Specify a user to operate on") - parser.set_defaults(func=lambda args: repl(args) if args.tl_lexicon else parser.print_help()) + parser.set_defaults( + func=lambda args: repl(args) + if args.tl_lexicon + else parser.print_help()) subp = parser.add_subparsers( metavar="COMMAND", dest="command", diff --git a/amanuensis/cli/__init__.py b/amanuensis/cli/__init__.py index de0b932..c4d4e5a 100644 --- a/amanuensis/cli/__init__.py +++ b/amanuensis/cli/__init__.py @@ -9,7 +9,8 @@ # def server_commands(commands={}): - if commands: return commands + if commands: + return commands import amanuensis.cli.server for name, func in vars(amanuensis.cli.server).items(): if name.startswith("command_"): @@ -18,7 +19,8 @@ def server_commands(commands={}): return commands def lexicon_commands(commands={}): - if commands: return commands + if commands: + return commands import amanuensis.cli.lexicon for name, func in vars(amanuensis.cli.lexicon).items(): if name.startswith("command_"): @@ -27,7 +29,8 @@ def lexicon_commands(commands={}): return commands def user_commands(commands={}): - if commands: return commands + if commands: + return commands import amanuensis.cli.user for name, func in vars(amanuensis.cli.user).items(): if name.startswith("command_"): diff --git a/amanuensis/cli/helpers.py b/amanuensis/cli/helpers.py index e21feb0..652a54e 100644 --- a/amanuensis/cli/helpers.py +++ b/amanuensis/cli/helpers.py @@ -1,6 +1,8 @@ # Standard library imports -from argparse import ArgumentParser, Namespace +from argparse import ArgumentParser from functools import wraps +from json.decoder import JSONDecodeError + # These function wrappers allow us to use the same function for executing a # command and for configuring it. This keeps command arg configuration close to @@ -9,70 +11,108 @@ from functools import wraps def add_argument(*args, **kwargs): """Passes the given args and kwargs to subparser.add_argument""" + def argument_adder(command): - second_layer = command.__dict__.get('wrapper', False) @wraps(command) def augmented_command(cmd_args): - if type(cmd_args) is ArgumentParser: + # Add this wrapper's command in the parser pass + if isinstance(cmd_args, ArgumentParser): cmd_args.add_argument(*args, **kwargs) - if type(cmd_args) is not ArgumentParser or second_layer: - command(cmd_args) + # If there are more command wrappers, pass through to them + if command.__dict__.get('wrapper', False): + command(cmd_args) + # Parser pass doesn't return a value + return None + + # Pass through transparently in the execute pass + return command(cmd_args) + + # Mark the command as wrapped so control passes through augmented_command.__dict__['wrapper'] = True return augmented_command + return argument_adder + def no_argument(command): """Noops for subparsers""" @wraps(command) def augmented_command(cmd_args): - if type(cmd_args) is not ArgumentParser: - command(cmd_args) + # Noop in the parser pass + if isinstance(cmd_args, ArgumentParser): + return None + # Pass through in the execute pass + return command(cmd_args) + return augmented_command + # Wrappers for commands requiring lexicon or username options def requires_lexicon(command): @wraps(command) def augmented_command(cmd_args): - if type(cmd_args) is ArgumentParser: - cmd_args.add_argument("-n", metavar="LEXICON", dest="lexicon", help="Specify a lexicon to operate on") + # Add lexicon argument in parser pass + if isinstance(cmd_args, ArgumentParser): + cmd_args.add_argument( + "-n", metavar="LEXICON", dest="lexicon", + help="Specify a lexicon to operate on") + # If there are more command wrappers, pass through to them if command.__dict__.get('wrapper', False): command(cmd_args) - if type(cmd_args) is Namespace: - base_val = hasattr(cmd_args, "tl_lexicon") and getattr(cmd_args, "tl_lexicon") - subp_val = hasattr(cmd_args, "lexicon") and getattr(cmd_args, "lexicon") - val = subp_val or base_val or None - if not val: - from amanuensis.config import logger - logger.error("This command requires specifying a lexicon") - return -1 - from amanuensis.lexicon import LexiconModel - cmd_args.lexicon = val#LexiconModel.by(name=val).name - command(cmd_args) + # Parser pass doesn't return a value + return None + + # Verify lexicon argument in execute pass + base_val = (hasattr(cmd_args, "tl_lexicon") + and getattr(cmd_args, "tl_lexicon")) + subp_val = (hasattr(cmd_args, "lexicon") + and getattr(cmd_args, "lexicon")) + val = subp_val or base_val or None + if not val: + from amanuensis.config import logger + logger.error("This command requires specifying a lexicon") + return -1 + # from amanuensis.lexicon import LexiconModel + cmd_args.lexicon = val#LexiconModel.by(name=val).name TODO + return command(cmd_args) + augmented_command.__dict__['wrapper'] = True return augmented_command + def requires_username(command): @wraps(command) def augmented_command(cmd_args): - if type(cmd_args) is ArgumentParser: - cmd_args.add_argument("-u", metavar="USERNAME", dest="username", help="Specify a user to operate on") + # Add user argument in parser pass + if isinstance(cmd_args, ArgumentParser): + cmd_args.add_argument( + "-u", metavar="USERNAME", dest="username", + help="Specify a user to operate on") + # If there are more command wrappers, pass through to them if command.__dict__.get('wrapper', False): command(cmd_args) - if type(cmd_args) is Namespace: - base_val = hasattr(cmd_args, "tl_username") and getattr(cmd_args, "tl_lexicon") - subp_val = hasattr(cmd_args, "username") and getattr(cmd_args, "username") - val = subp_val or base_val or None - if not val: - from amanuensis.config import logger - logger.error("This command requires specifying a user") - return -1 - from amanuensis.user import UserModel - cmd_args.username = val#UserModel.by(name=val).name - command(cmd_args) + # Parser pass doesn't return a value + return None + + # Verify user argument in execute pass + base_val = (hasattr(cmd_args, "tl_username") + and getattr(cmd_args, "tl_lexicon")) + subp_val = (hasattr(cmd_args, "username") + and getattr(cmd_args, "username")) + val = subp_val or base_val or None + if not val: + from amanuensis.config import logger + logger.error("This command requires specifying a user") + return -1 + # from amanuensis.user import UserModel + cmd_args.username = val#UserModel.by(name=val).name TODO + return command(cmd_args) + augmented_command.__dict__['wrapper'] = True return augmented_command + # Helpers for common command tasks CONFIG_GET_ROOT_VALUE = object() @@ -96,6 +136,7 @@ def config_get(cfg, pathspec): return -1 cfg = cfg.get(spec) print(json.dumps(cfg, indent=2)) + return 0 def config_set(obj_id, cfg, set_tuple): """ @@ -112,7 +153,7 @@ def config_set(obj_id, cfg, set_tuple): path = pathspec.split('.') try: value = json.loads(value) - except: + except JSONDecodeError: pass # Leave value as string for spec in path[:-1]: if spec not in cfg: @@ -125,4 +166,5 @@ def config_set(obj_id, cfg, set_tuple): return -1 old_value = cfg[key] cfg[key] = value - logger.info("{}.{}: {} -> {}".format(obj_id, pathspec, old_value, value)) \ No newline at end of file + logger.info("{}.{}: {} -> {}".format(obj_id, pathspec, old_value, value)) + return 0 diff --git a/amanuensis/cli/lexicon.py b/amanuensis/cli/lexicon.py index a5e9525..096df26 100644 --- a/amanuensis/cli/lexicon.py +++ b/amanuensis/cli/lexicon.py @@ -7,7 +7,9 @@ from amanuensis.cli.helpers import ( # @requires_lexicon -@add_argument("--editor", "-e", required=True, help="Name of the user who will be editor") +@add_argument( + "--editor", "-e", required=True, + help="Name of the user who will be editor") def command_create(args): """ Create a lexicon @@ -33,6 +35,7 @@ def command_create(args): # Internal call create_lexicon(args.lexicon, editor) + return 0 @requires_lexicon @@ -55,6 +58,7 @@ def command_delete(args): # Internal call delete_lexicon(lex, args.purge) logger.info("Deleted lexicon '{}'".format(args.lexicon)) + return 0 @no_argument @@ -74,9 +78,11 @@ def command_list(args): elif lex.turn['current'] > lex.turn['max']: statuses.append("{0.lid} {0.name} ({1})".format(lex, "Completed")) else: - statuses.append("{0.lid} {0.name} (Turn {1}/{2})".format(lex, lex.turn['current'], lex.turn['max'])) + statuses.append("{0.lid} {0.name} (Turn {1}/{2})".format( + lex, lex.turn['current'], lex.turn['max'])) for s in statuses: print(s) + return 0 @requires_lexicon @@ -112,6 +118,8 @@ def command_config(args): with json_rw(lex.config_path) as cfg: config_set(lex.id, cfg, args.set) + return 0 + # # Player/character commands # @@ -140,6 +148,7 @@ def command_player_add(args): # Internal call add_player(lex, u) + return 0 @requires_lexicon @@ -172,6 +181,7 @@ def command_player_remove(args): # Internal call remove_player(lex, u) + return 0 @requires_lexicon @@ -181,6 +191,7 @@ def command_player_list(args): """ import json # Module imports + from amanuensis.config import logger from amanuensis.lexicon import LexiconModel from amanuensis.user import UserModel @@ -197,6 +208,7 @@ def command_player_list(args): players.append(u.username) print(json.dumps(players, indent=2)) + return 0 @requires_lexicon @@ -227,6 +239,7 @@ def command_char_create(args): # Internal call add_character(lex, u, {"name": args.charname}) + return 0 @requires_lexicon @@ -251,6 +264,7 @@ def command_char_delete(args): # Internal call delete_character(lex, args.charname) + return 0 @requires_lexicon def command_char_list(args): @@ -259,6 +273,7 @@ def command_char_list(args): """ import json # Module imports + from amanuensis.config import logger from amanuensis.lexicon import LexiconModel # Verify arguments @@ -269,6 +284,7 @@ def command_char_list(args): # Internal call print(json.dumps(lex.character, indent=2)) + return 0 # # Procedural commands @@ -292,6 +308,5 @@ def command_publish_turn(args): settings. """ # Module imports - from amanuensis.config import logger - raise NotImplementedError() # TODO \ No newline at end of file + raise NotImplementedError() # TODO diff --git a/amanuensis/cli/server.py b/amanuensis/cli/server.py index db6fede..5eb2467 100644 --- a/amanuensis/cli/server.py +++ b/amanuensis/cli/server.py @@ -28,6 +28,7 @@ def command_init(args): # Internal call create_config_dir(args.config_dir, args.refresh) + return 0 @no_argument @@ -38,7 +39,6 @@ def command_generate_secret(args): The Flask server will not run unless a secret key has been generated. """ - import os # Module imports from amanuensis.config import json_rw, logger @@ -46,6 +46,7 @@ def command_generate_secret(args): with json_rw("config.json") as cfg: cfg['secret_key'] = secret_key.hex() logger.info("Regenerated Flask secret key") + return 0 @add_argument("-a", "--address", default="127.0.0.1") @@ -54,7 +55,7 @@ def command_generate_secret(args): def command_run(args): """ Run the default Flask server - + The default Flask server is not secure, and should only be used for development. """ @@ -62,9 +63,11 @@ def command_run(args): from amanuensis.config import get, logger if get("secret_key") is None: - logger.error("Can't run server without a secret_key. Run generate-secret first") + logger.error("Can't run server without a secret_key. Run generate-sec" + "ret first") return -1 app.run(host=args.address, port=args.port, debug=args.debug) + return 0 @add_argument("--get", metavar="PATHSPEC", dest="get", @@ -78,7 +81,6 @@ def command_config(args): PATHSPEC is a path into the config object formatted as a dot-separated sequence of keys. """ - import json # Module imports from amanuensis.config import json_ro, json_rw, logger @@ -93,3 +95,5 @@ def command_config(args): if args.set: with json_rw('config.json') as cfg: config_set("config", cfg, args.set) + + return 0 diff --git a/amanuensis/cli/user.py b/amanuensis/cli/user.py index 215b74d..eff9dfd 100644 --- a/amanuensis/cli/user.py +++ b/amanuensis/cli/user.py @@ -14,11 +14,13 @@ def command_create(args): import json # Module imports from amanuensis.config import logger, json_ro - from amanuensis.user import UserModel, valid_username, valid_email, create_user + from amanuensis.user import ( + UserModel, valid_username, valid_email, create_user) # Verify or query parameters if not valid_username(args.username): - logger.error("Invalid username: usernames may only contain alphanumeric characters, dashes, and underscores") + logger.error("Invalid username: usernames may only contain alphanumer" + "ic characters, dashes, and underscores") return -1 if UserModel.by(name=args.username) is not None: logger.error("Invalid username: username is already taken") @@ -33,7 +35,10 @@ def command_create(args): new_user, tmp_pw = create_user(args.username, args.displayname, args.email) with json_ro(new_user.config_path) as js: print(json.dumps(js, indent=2)) - print("Username: {}\nUser ID: {}\nPassword: {}".format(args.username, new_user.uid, tmp_pw)) + print("Username: {}\nUser ID: {}\nPassword: {}".format( + args.username, new_user.uid, tmp_pw)) + + return 0 @add_argument("--id", required=True, help="id of user to delete") def command_delete(args): @@ -42,20 +47,21 @@ def command_delete(args): """ import os # Module imports - from amanuensis.config import logger, prepend + from amanuensis.config import logger, prepend, json_rw user_path = prepend('user', args.id) if not os.path.isdir(user_path): logger.error("No user with that id") return -1 - else: - shutil.rmtree(user_path) + shutil.rmtree(user_path) with json_rw('user', 'index.json') as j: - if args.id in j: - del j[uid] + if args.id in j: # TODO this is wrong + del j[args.id] # TODO + return 0 + @no_argument def command_list(args): """List all users""" @@ -66,12 +72,16 @@ def command_list(args): user_dirs = os.listdir(prepend('user')) users = [] for uid in user_dirs: - if uid == "index.json": continue + if uid == "index.json": + continue with json_ro('user', uid, 'config.json') as user: users.append(user) users.sort(key=lambda u: u['username']) for user in users: - print("{0} {1} ({2})".format(user['uid'], user['displayname'], user['username'])) + print("{0} {1} ({2})".format( + user['uid'], user['displayname'], user['username'])) + + return 0 @requires_username @add_argument( @@ -84,7 +94,6 @@ def command_config(args): """ Interact with a user's config """ - import json # Module imports from amanuensis.config import logger, json_ro, json_rw from amanuensis.user import UserModel @@ -106,6 +115,8 @@ def command_config(args): with json_rw('user', u.id, 'config.json') as cfg: config_set(u.id, cfg, args.set) + return 0 + @add_argument("--username", help="The user to change password for") @add_argument("--password", help="The password to set. Not recommended") def command_passwd(args): @@ -113,7 +124,6 @@ def command_passwd(args): Set a user's password """ import getpass - import os # Module imports from amanuensis.config import logger from amanuensis.user import UserModel @@ -126,3 +136,5 @@ def command_passwd(args): return -1 pw = args.password or getpass.getpass("Password: ") u.set_password(pw) + + return 0 diff --git a/amanuensis/config/__init__.py b/amanuensis/config/__init__.py index f886393..34cc4d0 100644 --- a/amanuensis/config/__init__.py +++ b/amanuensis/config/__init__.py @@ -10,11 +10,11 @@ import amanuensis.config.loader # Environment variable name constants -ENV_SECRET_KEY = "AMANUENSIS_SECRET_KEY" -ENV_CONFIG_DIR = "AMANUENSIS_CONFIG_DIR" -ENV_LOG_FILE = "AMANUENSIS_LOG_FILE" +ENV_SECRET_KEY = "AMANUENSIS_SECRET_KEY" +ENV_CONFIG_DIR = "AMANUENSIS_CONFIG_DIR" +ENV_LOG_FILE = "AMANUENSIS_LOG_FILE" ENV_LOG_FILE_SIZE = "AMANUENSIS_LOG_FILE_SIZE" -ENV_LOG_FILE_NUM = "AMANUENSIS_LOG_FILE_NUM" +ENV_LOG_FILE_NUM = "AMANUENSIS_LOG_FILE_NUM" # # The config directory can be set by cli input, so the config infrastructure @@ -32,7 +32,8 @@ def init_config(args): global CONFIG_DIR, GLOBAL_CONFIG, logger CONFIG_DIR = args.config_dir amanuensis.config.init.verify_config_dir(CONFIG_DIR) - with amanuensis.config.loader.json_ro(os.path.join(CONFIG_DIR, "config.json")) as cfg: + with amanuensis.config.loader.json_ro( + os.path.join(CONFIG_DIR, "config.json")) as cfg: GLOBAL_CONFIG = cfg amanuensis.config.init.init_logging(args, GLOBAL_CONFIG['logging']) logger = logging.getLogger("amanuensis") @@ -56,4 +57,4 @@ def json_ro(*path): return amanuensis.config.loader.json_ro(prepend(*path)) def json_rw(*path): - return amanuensis.config.loader.json_rw(prepend(*path)) \ No newline at end of file + return amanuensis.config.loader.json_rw(prepend(*path)) diff --git a/amanuensis/config/init.py b/amanuensis/config/init.py index 933aecb..4c12b30 100644 --- a/amanuensis/config/init.py +++ b/amanuensis/config/init.py @@ -114,14 +114,17 @@ def verify_config_dir(config_dir): # Check that global config file exists global_config_path = os.path.join(config_dir, "config.json") if not os.path.isfile(global_config_path): - raise MissingConfigError("Config directory missing global config file: {}".format(config_dir)) + raise MissingConfigError("Config directory missing global config file" + ": {}".format(config_dir)) # Check that global config file has all the default settings def_cfg_s = get_stream("global.json") def_cfg = json.load(def_cfg_s) with json_ro(global_config_path) as global_config_file: for key in def_cfg.keys(): if key not in global_config_file.keys(): - raise MalformedConfigError("Missing '{}' in global config. If you updated Amanuensis, run init --refresh to pick up new config keys".format(key)) + raise MalformedConfigError("Missing '{}' in global config. If" + " you updated Amanuensis, run init --refresh to pick up n" + "ew config keys".format(key)) # Configs verified return True @@ -147,4 +150,3 @@ def init_logging(args, logging_config): logging.config.dictConfig(cfg) except: raise MalformedConfigError("Failed to load logging config") - diff --git a/amanuensis/config/loader.py b/amanuensis/config/loader.py index 87e694a..a8b312a 100644 --- a/amanuensis/config/loader.py +++ b/amanuensis/config/loader.py @@ -2,7 +2,6 @@ from collections import OrderedDict import fcntl import json -import os # Module imports from amanuensis.errors import ReadOnlyError @@ -90,4 +89,3 @@ class json_rw(open_ex): json.dump(self.config, self.fd, allow_nan=False, indent='\t') self.fd.truncate() super().__exit__(exc_type, exc_value, traceback) - diff --git a/amanuensis/errors.py b/amanuensis/errors.py index 76b3bd5..370c116 100644 --- a/amanuensis/errors.py +++ b/amanuensis/errors.py @@ -1,23 +1,17 @@ class AmanuensisError(Exception): """Base class for exceptions in amanuensis""" - pass class MissingConfigError(AmanuensisError): """A config file is missing that was expected to be present""" - pass class MalformedConfigError(AmanuensisError): """A config file could not be read and parsed""" - pass class ReadOnlyError(AmanuensisError): """A config was edited in readonly mode""" - pass class InternalMisuseError(AmanuensisError): """An internal helper method was called wrongly""" - pass class IndexMismatchError(AmanuensisError): """An id was obtained from an index, but the object doesn't exist""" - pass \ No newline at end of file diff --git a/amanuensis/lexicon/__init__.py b/amanuensis/lexicon/__init__.py index 5086001..08818b8 100644 --- a/amanuensis/lexicon/__init__.py +++ b/amanuensis/lexicon/__init__.py @@ -1,10 +1,12 @@ import os import time -from amanuensis.errors import InternalMisuseError, IndexMismatchError, MissingConfigError +from amanuensis.errors import ( + InternalMisuseError, IndexMismatchError, MissingConfigError) from amanuensis.config import prepend, json_ro, json_rw class LexiconModel(): + @staticmethod def by(lid=None, name=None): """ Gets the LexiconModel with the given lid or username @@ -14,7 +16,8 @@ class LexiconModel(): the lexicon's config, raises an error. """ if lid and name: - raise InternalMisuseError("lid and name both specified to LexiconModel.by()") + raise InternalMisuseError("lid and name both specified to Lexicon" + "Model.by()") if not lid and not name: raise ValueError("One of lid or name must be not None") if not lid: @@ -51,7 +54,6 @@ class LexiconModel(): def status(self): if self.turn.current is None: return "unstarted" - elif self.turn.current > self.turn.max: + if self.turn.current > self.turn.max: return "completed" - else: - return "ongoing" \ No newline at end of file + return "ongoing" diff --git a/amanuensis/lexicon/manage.py b/amanuensis/lexicon/manage.py index 1b2d849..a0e6487 100644 --- a/amanuensis/lexicon/manage.py +++ b/amanuensis/lexicon/manage.py @@ -80,7 +80,7 @@ def delete_lexicon(lex, purge=False): # Verify arguments if lex is None: raise ValueError("Invalid lexicon: '{}'".format(lex)) - + # Delete the lexicon from the index with json_rw('lexicon', 'index.json') as j: if lex.id in j: @@ -150,7 +150,7 @@ def add_player(lex, player): if player.id not in cfg.join.joined: cfg.join.joined.append(player.id) added = True - + # Log to the lexicon's log if added: lex.log("Player '{0.username}' joined ({0.id})".format(player)) @@ -166,7 +166,9 @@ def remove_player(lex, player): if player is None: raise ValueError("Invalid player: '{}'".format(player)) if lex.editor == player.id: - raise ValueError("Can't remove the editor '{}' from lexicon '{}'".format(player.username, lex.name)) + raise ValueError( + "Can't remove the editor '{}' from lexicon '{}'".format( + player.username, lex.name)) # Idempotently remove player with json_rw(lex.config_path) as cfg: @@ -190,7 +192,9 @@ def add_character(lex, player, charinfo={}): if not charinfo or not charinfo.get("name"): raise ValueError("Invalid character info: '{}'".format(charinfo)) charname = charinfo.get("name") - if any([char.name for char in lex.character.values() if char.name == charname]): + if any([ + char.name for char in lex.character.values() + if char.name == charname]): raise ValueError("Duplicate character name: '{}'".format(charinfo)) # Load the character template @@ -221,14 +225,16 @@ def delete_character(lex, charname): if lex is None: raise ValueError("Invalid lexicon: '{}'".format(lex)) if charname is None: - raise ValueError("Invalid character name: '{}'".format(charinfo)) + raise ValueError("Invalid character name: '{}'".format(charname)) # Find character in this lexicon - matches = [char for cid, char in lex.character.items() if char.name == charname] + matches = [ + char for cid, char in lex.character.items() + if char.name == charname] if len(matches) != 1: raise ValueError(matches) char = matches[0] # Remove character from character list with json_rw(lex.config_path) as cfg: - del cfg.character[char.cid] \ No newline at end of file + del cfg.character[char.cid] diff --git a/amanuensis/resources/__init__.py b/amanuensis/resources/__init__.py index 737d67a..7341f8a 100644 --- a/amanuensis/resources/__init__.py +++ b/amanuensis/resources/__init__.py @@ -2,4 +2,4 @@ import pkg_resources def get_stream(*path): rs_path = "/".join(path) - return pkg_resources.resource_stream(__name__, rs_path) \ No newline at end of file + return pkg_resources.resource_stream(__name__, rs_path) diff --git a/amanuensis/server/__init__.py b/amanuensis/server/__init__.py index eb88b87..f9f144d 100644 --- a/amanuensis/server/__init__.py +++ b/amanuensis/server/__init__.py @@ -11,7 +11,10 @@ from amanuensis.server.lexicon import get_bp as get_lex_bp # Flask app init static_root = os.path.abspath(get("static_root")) -app = Flask(__name__, template_folder="../templates", static_folder=static_root) +app = Flask( + __name__, + template_folder="../templates", + static_folder=static_root) app.secret_key = bytes.fromhex(get('secret_key')) app.jinja_options['trim_blocks'] = True app.jinja_options['lstrip_blocks'] = True @@ -29,4 +32,4 @@ home_bp = get_home_bp() app.register_blueprint(home_bp) lex_bp = get_lex_bp() -app.register_blueprint(lex_bp) \ No newline at end of file +app.register_blueprint(lex_bp) diff --git a/amanuensis/server/forms.py b/amanuensis/server/forms.py index 30239d7..f140617 100644 --- a/amanuensis/server/forms.py +++ b/amanuensis/server/forms.py @@ -1,5 +1,6 @@ from flask_wtf import FlaskForm -from wtforms import StringField, PasswordField, BooleanField, SubmitField, TextAreaField +from wtforms import ( + StringField, PasswordField, BooleanField, SubmitField, TextAreaField) from wtforms.validators import DataRequired, ValidationError from amanuensis.config import json_ro @@ -7,7 +8,8 @@ from amanuensis.config import json_ro # Custom validators def user(exists=True): - template = 'User "{{}}" {}'.format("not found" if exists else "already exists") + template = 'User "{{}}" {}'.format( + "not found" if exists else "already exists") should_exist = bool(exists) def validate_user(form, field): with json_ro('user', 'index.json') as index: @@ -17,7 +19,8 @@ def user(exists=True): def lexicon(exists=True): - template = 'Lexicon "{{}}" {}'.format("not found" if exists else "already exists") + template = 'Lexicon "{{}}" {}'.format( + "not found" if exists else "already exists") should_exist = bool(exists) def validate_lexicon(form, field): with json_ro('lexicon', 'index.json') as index: @@ -37,8 +40,12 @@ class LoginForm(FlaskForm): class LexiconCreateForm(FlaskForm): """/admin/create/""" - lexiconName = StringField('Lexicon name', validators=[DataRequired(), lexicon(exists=False)]) - editorName = StringField('Username of editor', validators=[DataRequired(), user(exists=True)]) + lexiconName = StringField( + 'Lexicon name', + validators=[DataRequired(), lexicon(exists=False)]) + editorName = StringField( + 'Username of editor', + validators=[DataRequired(), user(exists=True)]) promptText = TextAreaField("Prompt") submit = SubmitField('Create') @@ -46,4 +53,4 @@ class LexiconCreateForm(FlaskForm): class LexiconConfigForm(FlaskForm): """/lexicon//session/settings/""" configText = TextAreaField("Config file") - submit = SubmitField("Submit") \ No newline at end of file + submit = SubmitField("Submit") diff --git a/amanuensis/server/helpers.py b/amanuensis/server/helpers.py index 1dd3ba7..59f334b 100644 --- a/amanuensis/server/helpers.py +++ b/amanuensis/server/helpers.py @@ -49,4 +49,4 @@ def player_required(route): flash("You must be a player to view this page") return redirect(url_for('home.home')) return route(*args, **kwargs) - return player_route \ No newline at end of file + return player_route diff --git a/amanuensis/server/home.py b/amanuensis/server/home.py index bb4ef59..edadf12 100644 --- a/amanuensis/server/home.py +++ b/amanuensis/server/home.py @@ -1,10 +1,5 @@ -from functools import wraps -import json - -from flask import Blueprint, render_template, url_for, redirect, flash -from flask_login import login_required, current_user -from flask_wtf import FlaskForm -from wtforms import TextAreaField, SubmitField, StringField +from flask import Blueprint, render_template +from flask_login import login_required from amanuensis.config import json_ro from amanuensis.lexicon import LexiconModel @@ -22,6 +17,7 @@ def get_bp(): return render_template('home/home.html') @bp.route('/admin/', methods=['GET']) + @login_required @admin_required def admin(): users = [] @@ -37,6 +33,7 @@ def get_bp(): return render_template('home/admin.html', users=users, lexicons=lexicons) @bp.route("/admin/create/", methods=['GET', 'POST']) + @login_required @admin_required def admin_create(): form = LexiconCreateForm() diff --git a/amanuensis/server/lexicon.py b/amanuensis/server/lexicon.py index d992e6b..a3be654 100644 --- a/amanuensis/server/lexicon.py +++ b/amanuensis/server/lexicon.py @@ -1,4 +1,3 @@ -from functools import wraps import json from flask import Blueprint, render_template, url_for, redirect, g, flash @@ -6,10 +5,8 @@ from flask_login import login_required, current_user from amanuensis.config import json_ro, open_ex from amanuensis.config.loader import ReadOnlyOrderedDict -from amanuensis.lexicon import LexiconModel from amanuensis.server.forms import LexiconConfigForm -from amanuensis.server.helpers import lexicon_param, player_required -from amanuensis.user import UserModel +from amanuensis.server.helpers import lexicon_param def get_bp(): @@ -54,8 +51,9 @@ def get_bp(): if form.validate(): # Check input is valid json try: - cfg = json.loads(form.configText.data, object_pairs_hook=ReadOnlyOrderedDict) - except: + cfg = json.loads(form.configText.data, + object_pairs_hook=ReadOnlyOrderedDict) + except json.decoder.JsonDecodeError: flash("Invalid JSON") return render_template("lexicon/settings.html", form=form) # Check input has all the required fields diff --git a/amanuensis/user.py b/amanuensis/user.py index f5af951..4244996 100644 --- a/amanuensis/user.py +++ b/amanuensis/user.py @@ -6,7 +6,8 @@ import uuid from flask_login import UserMixin from werkzeug.security import generate_password_hash, check_password_hash -from amanuensis.errors import InternalMisuseError, MissingConfigError, IndexMismatchError +from amanuensis.errors import ( + InternalMisuseError, MissingConfigError, IndexMismatchError) from amanuensis.config import prepend, json_ro, json_rw from amanuensis.resources import get_stream from amanuensis.lexicon.manage import get_all_lexicons diff --git a/pylintrc b/pylintrc new file mode 100644 index 0000000..00319fc --- /dev/null +++ b/pylintrc @@ -0,0 +1,37 @@ +# pylint configuration + +[MASTER] + +[MESSAGES CONTROL] + +disable= + bad-continuation, + broad-except, + dangerous-default-value, + duplicate-code, + fixme, + global-statement, + len-as-condition, + logging-format-interpolation, + import-outside-toplevel, + invalid-name, + missing-docstring, + mixed-indentation, + no-member, + no-self-use, + redefined-variable-type, + too-few-public-methods, + too-many-arguments, + too-many-branches, + too-many-instance-attributes, + too-many-lines, + too-many-locals, + too-many-public-methods, + too-many-return-statements, + too-many-statements, + unused-argument, + unused-variable, + +[FORMAT] + +max-line-length=79 diff --git a/requirements.txt b/requirements.txt index c1dfb70..2e1e2f3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,18 @@ +astroid==2.3.3 Click==7.0 Flask==1.1.1 Flask-Login==0.4.1 Flask-WTF==0.14.2 +isort==4.3.21 itsdangerous==1.1.0 Jinja2==2.10.3 +lazy-object-proxy==1.4.3 MarkupSafe==1.1.1 +mccabe==0.6.1 pkg-resources==0.0.0 +pylint==2.4.4 +six==1.14.0 +typed-ast==1.4.1 Werkzeug==0.16.0 +wrapt==1.11.2 WTForms==2.2.1