Add linting
This commit is contained in:
parent
8730e0974f
commit
b69b6155b3
|
@ -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",
|
||||
|
|
|
@ -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_"):
|
||||
|
|
|
@ -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))
|
||||
logger.info("{}.{}: {} -> {}".format(obj_id, pathspec, old_value, value))
|
||||
return 0
|
||||
|
|
|
@ -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
|
||||
raise NotImplementedError() # TODO
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
return amanuensis.config.loader.json_rw(prepend(*path))
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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"
|
||||
return "ongoing"
|
||||
|
|
|
@ -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]
|
||||
del cfg.character[char.cid]
|
||||
|
|
|
@ -2,4 +2,4 @@ import pkg_resources
|
|||
|
||||
def get_stream(*path):
|
||||
rs_path = "/".join(path)
|
||||
return pkg_resources.resource_stream(__name__, rs_path)
|
||||
return pkg_resources.resource_stream(__name__, rs_path)
|
||||
|
|
|
@ -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)
|
||||
app.register_blueprint(lex_bp)
|
||||
|
|
|
@ -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/<name>/session/settings/"""
|
||||
configText = TextAreaField("Config file")
|
||||
submit = SubmitField("Submit")
|
||||
submit = SubmitField("Submit")
|
||||
|
|
|
@ -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
|
||||
return player_route
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue