Run black on sources; add black config

This commit is contained in:
Griatch 2020-07-27 21:12:06 +02:00
parent b5d148b00a
commit b24d4f0e1e
41 changed files with 400 additions and 344 deletions

View file

@ -108,13 +108,7 @@ def _case_sensitive_replace(string, old, new):
result.append(new_word[ind + 1 :].lower()) result.append(new_word[ind + 1 :].lower())
out.append("".join(result)) out.append("".join(result))
# if we have more new words than old ones, just add them verbatim # if we have more new words than old ones, just add them verbatim
out.extend( out.extend([new_word for ind, new_word in enumerate(new_words) if ind >= len(old_words)])
[
new_word
for ind, new_word in enumerate(new_words)
if ind >= len(old_words)
]
)
return " ".join(out) return " ".join(out)
regex = re.compile(re.escape(old), re.I) regex = re.compile(re.escape(old), re.I)
@ -154,9 +148,7 @@ def rename_in_tree(path, in_list, out_list, excl_list, fileend_list, is_interact
print("%s skipped (excluded)." % full_path) print("%s skipped (excluded)." % full_path)
continue continue
if not fileend_list or any( if not fileend_list or any(file.endswith(ending) for ending in fileend_list):
file.endswith(ending) for ending in fileend_list
):
rename_in_file(full_path, in_list, out_list, is_interactive) rename_in_file(full_path, in_list, out_list, is_interactive)
# rename file - always ask # rename file - always ask
@ -164,9 +156,7 @@ def rename_in_tree(path, in_list, out_list, excl_list, fileend_list, is_interact
for src, dst in repl_mapping: for src, dst in repl_mapping:
new_file = _case_sensitive_replace(new_file, src, dst) new_file = _case_sensitive_replace(new_file, src, dst)
if new_file != file: if new_file != file:
inp = input( inp = input(_green("Rename %s\n -> %s\n Y/[N]? > " % (file, new_file)))
_green("Rename %s\n -> %s\n Y/[N]? > " % (file, new_file))
)
if inp.upper() == "Y": if inp.upper() == "Y":
new_full_path = os.path.join(root, new_file) new_full_path = os.path.join(root, new_file)
try: try:
@ -182,9 +172,7 @@ def rename_in_tree(path, in_list, out_list, excl_list, fileend_list, is_interact
for src, dst in repl_mapping: for src, dst in repl_mapping:
new_root = _case_sensitive_replace(new_root, src, dst) new_root = _case_sensitive_replace(new_root, src, dst)
if new_root != root: if new_root != root:
inp = input( inp = input(_green("Dir Rename %s\n -> %s\n Y/[N]? > " % (root, new_root)))
_green("Dir Rename %s\n -> %s\n Y/[N]? > " % (root, new_root))
)
if inp.upper() == "Y": if inp.upper() == "Y":
try: try:
os.rename(root, new_root) os.rename(root, new_root)
@ -252,9 +240,7 @@ def rename_in_file(path, in_list, out_list, is_interactive):
while True: while True:
for iline, renamed_line in sorted( for iline, renamed_line in sorted(list(renamed.items()), key=lambda tup: tup[0]):
list(renamed.items()), key=lambda tup: tup[0]
):
print("%3i orig: %s" % (iline + 1, org_lines[iline])) print("%3i orig: %s" % (iline + 1, org_lines[iline]))
print(" new : %s" % (_yellow(renamed_line))) print(" new : %s" % (_yellow(renamed_line)))
print(_green("%s (%i lines changed)" % (path, len(renamed)))) print(_green("%s (%i lines changed)" % (path, len(renamed))))
@ -297,11 +283,7 @@ def rename_in_file(path, in_list, out_list, is_interactive):
input(_HELP_TEXT.format(sources=in_list, targets=out_list)) input(_HELP_TEXT.format(sources=in_list, targets=out_list))
elif ret.startswith("i"): elif ret.startswith("i"):
# ignore one or more lines # ignore one or more lines
ignores = [ ignores = [int(ind) - 1 for ind in ret[1:].split(",") if ind.strip().isdigit()]
int(ind) - 1
for ind in ret[1:].split(",")
if ind.strip().isdigit()
]
if not ignores: if not ignores:
input("Ignore example: i 2,7,34,133\n (return to continue)") input("Ignore example: i 2,7,34,133\n (return to continue)")
continue continue
@ -313,9 +295,7 @@ def rename_in_file(path, in_list, out_list, is_interactive):
if __name__ == "__main__": if __name__ == "__main__":
import argparse import argparse
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(description="Rename text in a source tree, or a single file")
description="Rename text in a source tree, or a single file"
)
parser.add_argument( parser.add_argument(
"-i", "-i",
@ -326,27 +306,19 @@ if __name__ == "__main__":
parser.add_argument( parser.add_argument(
"-o", "--output", action="append", help="Word to rename a matching src-word to" "-o", "--output", action="append", help="Word to rename a matching src-word to"
) )
parser.add_argument( parser.add_argument("-x", "--exc", action="append", help="File path patterns to exclude")
"-x", "--exc", action="append", help="File path patterns to exclude"
)
parser.add_argument( parser.add_argument(
"-a", "--auto", action="store_true", help="Automatic mode, don't ask to rename" "-a", "--auto", action="store_true", help="Automatic mode, don't ask to rename"
) )
parser.add_argument( parser.add_argument("-r", "--recursive", action="store_true", help="Recurse subdirs")
"-r", "--recursive", action="store_true", help="Recurse subdirs"
)
parser.add_argument( parser.add_argument(
"-f", "-f",
"--fileending", "--fileending",
action="append", action="append",
help="Change which file endings to allow (default .py and .html)", help="Change which file endings to allow (default .py and .html)",
) )
parser.add_argument( parser.add_argument("--nocolor", action="store_true", help="Turn off in-program color")
"--nocolor", action="store_true", help="Turn off in-program color" parser.add_argument("--fake", action="store_true", help="Simulate run but don't actually save")
)
parser.add_argument(
"--fake", action="store_true", help="Simulate run but don't actually save"
)
parser.add_argument("path", help="File or directory in which to rename text") parser.add_argument("path", help="File or directory in which to rename text")
args = parser.parse_args() args = parser.parse_args()
@ -362,9 +334,7 @@ if __name__ == "__main__":
print("At least one source- and destination word must be given.") print("At least one source- and destination word must be given.")
sys.exit() sys.exit()
if len(in_list) != len(out_list): if len(in_list) != len(out_list):
print( print("Number of sources must be identical to the number of destination arguments.")
"Number of sources must be identical to the number of destination arguments."
)
sys.exit() sys.exit()
exc_list = exc_list or [] exc_list = exc_list or []
@ -376,8 +346,6 @@ if __name__ == "__main__":
FAKE_MODE = args.fake FAKE_MODE = args.fake
if is_recursive: if is_recursive:
rename_in_tree( rename_in_tree(args.path, in_list, out_list, exc_list, fileend_list, is_interactive)
args.path, in_list, out_list, exc_list, fileend_list, is_interactive
)
else: else:
rename_in_file(args.path, in_list, out_list, is_interactive) rename_in_file(args.path, in_list, out_list, is_interactive)

View file

@ -15,18 +15,25 @@ _IGNORE_FILES = []
_SOURCEDIR_NAME = "source" _SOURCEDIR_NAME = "source"
_SOURCE_DIR = pathjoin(dirname(dirname(abspath(__file__))), _SOURCEDIR_NAME) _SOURCE_DIR = pathjoin(dirname(dirname(abspath(__file__))), _SOURCEDIR_NAME)
_TOC_FILE = pathjoin(_SOURCE_DIR, "toc.md") _TOC_FILE = pathjoin(_SOURCE_DIR, "toc.md")
_NO_REMAP_STARTSWITH = ["http://", "https://", "github:", "api:", _NO_REMAP_STARTSWITH = [
"feature-request", "report-bug", "issue", "bug-report"] "http://",
"https://",
"github:",
"api:",
"feature-request",
"report-bug",
"issue",
"bug-report",
]
TXT_REMAPS = { TXT_REMAPS = {}
} URL_REMAPS = {}
URL_REMAPS = {
}
_USED_REFS = {} _USED_REFS = {}
_CURRFILE = None _CURRFILE = None
def auto_link_remapper(): def auto_link_remapper():
""" """
- Auto-Remaps links to fit with the actual document file structure. Requires - Auto-Remaps links to fit with the actual document file structure. Requires
@ -44,7 +51,7 @@ def auto_link_remapper():
# we allow a max of 4 levels of nesting in the source dir # we allow a max of 4 levels of nesting in the source dir
ind = pathparts[-5:].index(_SOURCEDIR_NAME) ind = pathparts[-5:].index(_SOURCEDIR_NAME)
# get the part after source/ # get the part after source/
pathparts = pathparts[-5 + 1 + ind:] pathparts = pathparts[-5 + 1 + ind :]
url = "/".join(pathparts) url = "/".join(pathparts)
# get the reference, without .md # get the reference, without .md
url = url.rsplit(".", 1)[0] url = url.rsplit(".", 1)[0]
@ -71,7 +78,8 @@ def auto_link_remapper():
raise DocumentError( raise DocumentError(
f" Tried to add {src_url}.md, but a file {duplicate_src_url}.md already exists.\n" f" Tried to add {src_url}.md, but a file {duplicate_src_url}.md already exists.\n"
" Evennia's auto-link-corrector does not accept doc-files with the same \n" " Evennia's auto-link-corrector does not accept doc-files with the same \n"
" name, even in different folders. Rename one.\n") " name, even in different folders. Rename one.\n"
)
toc_map[fname] = src_url toc_map[fname] = src_url
# find relative links to all other files # find relative links to all other files
@ -86,17 +94,20 @@ def auto_link_remapper():
url = "./" + url url = "./" + url
docref_map[sourcepath][targetname] = url.rsplit(".", 1)[0] docref_map[sourcepath][targetname] = url.rsplit(".", 1)[0]
# normal reference-links [txt](urls) # normal reference-links [txt](urls)
ref_regex = re.compile(r"\[(?P<txt>[\w -\[\]\`]+?)\]\((?P<url>.+?)\)", re.I + re.S + re.U + re.M) ref_regex = re.compile(
r"\[(?P<txt>[\w -\[\]\`]+?)\]\((?P<url>.+?)\)", re.I + re.S + re.U + re.M
)
# in document references # in document references
ref_doc_regex = re.compile(r"\[(?P<txt>[\w -\`]+?)\]:\s+?(?P<url>.+?)(?=$|\n)", re.I + re.S + re.U + re.M) ref_doc_regex = re.compile(
r"\[(?P<txt>[\w -\`]+?)\]:\s+?(?P<url>.+?)(?=$|\n)", re.I + re.S + re.U + re.M
)
def _sub(match): def _sub(match):
# inline reference links # inline reference links
global _USED_REFS global _USED_REFS
grpdict = match.groupdict() grpdict = match.groupdict()
txt, url = grpdict['txt'], grpdict['url'] txt, url = grpdict["txt"], grpdict["url"]
txt = TXT_REMAPS.get(txt, txt) txt = TXT_REMAPS.get(txt, txt)
url = URL_REMAPS.get(url, url) url = URL_REMAPS.get(url, url)
@ -117,7 +128,7 @@ def auto_link_remapper():
if _CURRFILE in docref_map and fname in docref_map[_CURRFILE]: if _CURRFILE in docref_map and fname in docref_map[_CURRFILE]:
cfilename = _CURRFILE.rsplit("/", 1)[-1] cfilename = _CURRFILE.rsplit("/", 1)[-1]
urlout = docref_map[_CURRFILE][fname] + ('#' + anchor[0] if anchor else '') urlout = docref_map[_CURRFILE][fname] + ("#" + anchor[0] if anchor else "")
if urlout != url: if urlout != url:
print(f" {cfilename}: [{txt}]({url}) -> [{txt}]({urlout})") print(f" {cfilename}: [{txt}]({url}) -> [{txt}]({urlout})")
else: else:
@ -129,7 +140,7 @@ def auto_link_remapper():
# reference links set at the bottom of the page # reference links set at the bottom of the page
global _USED_REFS global _USED_REFS
grpdict = match.groupdict() grpdict = match.groupdict()
txt, url = grpdict['txt'], grpdict['url'] txt, url = grpdict["txt"], grpdict["url"]
txt = TXT_REMAPS.get(txt, txt) txt = TXT_REMAPS.get(txt, txt)
url = URL_REMAPS.get(url, url) url = URL_REMAPS.get(url, url)
@ -150,7 +161,7 @@ def auto_link_remapper():
if _CURRFILE in docref_map and fname in docref_map[_CURRFILE]: if _CURRFILE in docref_map and fname in docref_map[_CURRFILE]:
cfilename = _CURRFILE.rsplit("/", 1)[-1] cfilename = _CURRFILE.rsplit("/", 1)[-1]
urlout = docref_map[_CURRFILE][fname] + ('#' + anchor[0] if anchor else '') urlout = docref_map[_CURRFILE][fname] + ("#" + anchor[0] if anchor else "")
if urlout != url: if urlout != url:
print(f" {cfilename}: [{txt}]: {url} -> [{txt}]: {urlout}") print(f" {cfilename}: [{txt}]: {url} -> [{txt}]: {urlout}")
else: else:
@ -165,12 +176,12 @@ def auto_link_remapper():
# from pudb import debugger;debugger.Debugger().set_trace() # from pudb import debugger;debugger.Debugger().set_trace()
_CURRFILE = path.as_posix() _CURRFILE = path.as_posix()
with open(path, 'r') as fil: with open(path, "r") as fil:
intxt = fil.read() intxt = fil.read()
outtxt = ref_regex.sub(_sub, intxt) outtxt = ref_regex.sub(_sub, intxt)
outtxt = ref_doc_regex.sub(_sub_doc, outtxt) outtxt = ref_doc_regex.sub(_sub_doc, outtxt)
if intxt != outtxt: if intxt != outtxt:
with open(path, 'w') as fil: with open(path, "w") as fil:
fil.write(outtxt) fil.write(outtxt)
count += 1 count += 1
print(f" -- Auto-relinked links in {path.name}") print(f" -- Auto-relinked links in {path.name}")
@ -207,5 +218,6 @@ def auto_link_remapper():
print(" -- Auto-Remapper finished.") print(" -- Auto-Remapper finished.")
if __name__ == "__main__": if __name__ == "__main__":
auto_link_remapper() auto_link_remapper()

View file

@ -45,7 +45,7 @@ def create_search_index(sourcedir, outfile):
print(f"Building Search index from {len(filepaths)} files ... ", end="") print(f"Building Search index from {len(filepaths)} files ... ", end="")
for filepath in filepaths: for filepath in filepaths:
with open(filepath, 'r') as fil: with open(filepath, "r") as fil:
filename = filepath.rsplit(sep, 1)[1].split(".", 1)[0] filename = filepath.rsplit(sep, 1)[1].split(".", 1)[0]
url = f"{URL_BASE}{sep}{filename}.html".strip() url = f"{URL_BASE}{sep}{filename}.html".strip()
title = filename.replace("-", " ").strip() title = filename.replace("-", " ").strip()
@ -61,16 +61,7 @@ def create_search_index(sourcedir, outfile):
idx = lunr( idx = lunr(
ref="url", ref="url",
documents=outlist, documents=outlist,
fields=[ fields=[{"field_name": "title", "boost": 10}, {"field_name": "text", "boost": 1}],
{
"field_name": "title",
"boost": 10
},
{
"field_name": "text",
"boost": 1
}
],
) )
with open(outfile, "w") as fil: with open(outfile, "w") as fil:
@ -83,10 +74,18 @@ if __name__ == "__main__":
parser = ArgumentParser(description="Build a static search index.") parser = ArgumentParser(description="Build a static search index.")
parser.add_argument("-i", dest="sourcedir", default=DEFAULT_SOURCE_DIR, parser.add_argument(
help="Absolute path to the documentation source dir") "-i",
parser.add_argument("-o", dest="outfile", default=DEFAULT_OUTFILE, dest="sourcedir",
help="Absolute path to the index file to output.") default=DEFAULT_SOURCE_DIR,
help="Absolute path to the documentation source dir",
)
parser.add_argument(
"-o",
dest="outfile",
default=DEFAULT_OUTFILE,
help="Absolute path to the index file to output.",
)
args = parser.parse_args() args = parser.parse_args()

View file

@ -15,7 +15,7 @@ We also need to build the toc-tree and should do so automatically for now.
import glob import glob
import re import re
import datetime import datetime
_RE_MD_LINK = re.compile(r"\[(?P<txt>[\w -\[\]]+?)\]\((?P<url>.+?)\)", re.I + re.S + re.U) _RE_MD_LINK = re.compile(r"\[(?P<txt>[\w -\[\]]+?)\]\((?P<url>.+?)\)", re.I + re.S + re.U)
_RE_REF_LINK = re.compile(r"\[[\w -\[\]]*?\]\(.+?\)", re.I + re.S + re.U) _RE_REF_LINK = re.compile(r"\[[\w -\[\]]*?\]\(.+?\)", re.I + re.S + re.U)
@ -43,8 +43,11 @@ _INDEX_PREFIX = f"""
""" """
_WIKI_DIR = "../../../evennia.wiki/" _WIKI_DIR = "../../../evennia.wiki/"
_INFILES = [path for path in sorted(glob.glob(_WIKI_DIR + "/*.md")) _INFILES = [
if path.rsplit('/', 1)[-1] not in _IGNORE_FILES] path
for path in sorted(glob.glob(_WIKI_DIR + "/*.md"))
if path.rsplit("/", 1)[-1] not in _IGNORE_FILES
]
_FILENAMES = [path.rsplit("/", 1)[-1] for path in _INFILES] _FILENAMES = [path.rsplit("/", 1)[-1] for path in _INFILES]
_FILENAMES = [path.split(".", 1)[0] for path in _FILENAMES] _FILENAMES = [path.split(".", 1)[0] for path in _FILENAMES]
_FILENAMESLOW = [path.lower() for path in _FILENAMES] _FILENAMESLOW = [path.lower() for path in _FILENAMES]
@ -95,8 +98,17 @@ _ABSOLUTE_LINK_SKIP = (
# specific references tokens that should be ignored. Should be given # specific references tokens that should be ignored. Should be given
# without any #anchor. # without any #anchor.
_REF_SKIP = ( _REF_SKIP = (
"[5](Win)", "[6](Win)", "[7](Win)", "[10](Win)", "[11](Mac)", "[13](Win)", "[5](Win)",
"[14](IOS)", "[15](IOS)", "[16](Andr)", "[17](Andr)", "[18](Unix)", "[6](Win)",
"[7](Win)",
"[10](Win)",
"[11](Mac)",
"[13](Win)",
"[14](IOS)",
"[15](IOS)",
"[16](Andr)",
"[17](Andr)",
"[18](Unix)",
"[21](Chrome)", "[21](Chrome)",
# these should be checked # these should be checked
"[EvTable](EvTable)", "[EvTable](EvTable)",
@ -126,20 +138,19 @@ def _sub_remap(match):
def _sub_link(match): def _sub_link(match):
mdict = match.groupdict() mdict = match.groupdict()
txt, url_orig = mdict['txt'], mdict['url'] txt, url_orig = mdict["txt"], mdict["url"]
url = url_orig url = url_orig
# if not txt: # if not txt:
# # the 'comment' is not supported by Mkdocs # # the 'comment' is not supported by Mkdocs
# return "" # return ""
print(f" [{txt}]({url})") print(f" [{txt}]({url})")
url = _CUSTOM_LINK_REMAP.get(url, url) url = _CUSTOM_LINK_REMAP.get(url, url)
url, *anchor = url.rsplit("#", 1) url, *anchor = url.rsplit("#", 1)
if url in _ABSOLUTE_LINK_SKIP: if url in _ABSOLUTE_LINK_SKIP:
url += (("#" + anchor[0]) if anchor else "") url += ("#" + anchor[0]) if anchor else ""
return f"[{txt}]({url})" return f"[{txt}]({url})"
if url.startswith("evennia"): if url.startswith("evennia"):
@ -166,11 +177,10 @@ def _sub_link(match):
# this happens on same-file #labels in wiki # this happens on same-file #labels in wiki
url = _CURRENT_TITLE url = _CURRENT_TITLE
if (url not in _FILENAMES and if url not in _FILENAMES and not url.startswith("http") and not url.startswith(_CODE_PREFIX):
not url.startswith("http") and not url.startswith(_CODE_PREFIX)):
url_cap = url.capitalize() url_cap = url.capitalize()
url_plur = url[:-3] + 's' + ".md" url_plur = url[:-3] + "s" + ".md"
url_cap_plur = url_plur.capitalize() url_cap_plur = url_plur.capitalize()
link = f"[{txt}]({url})" link = f"[{txt}]({url})"
@ -201,6 +211,7 @@ def _sub_link(match):
return f"[{txt}]({url})" return f"[{txt}]({url})"
def create_toctree(files): def create_toctree(files):
with open("../source/toc.md", "w") as fil: with open("../source/toc.md", "w") as fil:
@ -216,13 +227,14 @@ def create_toctree(files):
fil.write(f"\n* [{linkname}]({ref}.md)") fil.write(f"\n* [{linkname}]({ref}.md)")
def convert_links(files, outdir): def convert_links(files, outdir):
global _CURRENT_TITLE global _CURRENT_TITLE
for inpath in files: for inpath in files:
is_index = False is_index = False
outfile = inpath.rsplit('/', 1)[-1] outfile = inpath.rsplit("/", 1)[-1]
if outfile == "Home.md": if outfile == "Home.md":
outfile = "index.md" outfile = "index.md"
is_index = True is_index = True
@ -235,26 +247,33 @@ def convert_links(files, outdir):
text = fil.read() text = fil.read()
if is_index: if is_index:
text = _INDEX_PREFIX + text text = _INDEX_PREFIX + text
lines = text.split("\n") lines = text.split("\n")
lines = (lines[:-11] lines = (
+ [" - The [TOC](toc) lists all regular documentation pages.\n\n"] lines[:-11]
+ lines[-11:]) + [" - The [TOC](toc) lists all regular documentation pages.\n\n"]
+ lines[-11:]
)
text = "\n".join(lines) text = "\n".join(lines)
_CURRENT_TITLE = title.replace(" ", "-") _CURRENT_TITLE = title.replace(" ", "-")
text = _RE_CLEAN.sub("", text) text = _RE_CLEAN.sub("", text)
text = _RE_REF_LINK.sub(_sub_remap, text) text = _RE_REF_LINK.sub(_sub_remap, text)
text = _RE_MD_LINK.sub(_sub_link, text) text = _RE_MD_LINK.sub(_sub_link, text)
text = text.split('\n')[1:] if text.split('\n')[0].strip().startswith('[]') else text.split('\n') text = (
text.split("\n")[1:]
if text.split("\n")[0].strip().startswith("[]")
else text.split("\n")
)
text = "\n".join(text) text = "\n".join(text)
if not is_index: if not is_index:
text = f"# {title}\n\n{text}" text = f"# {title}\n\n{text}"
with open(outfile, 'w') as fil: with open(outfile, "w") as fil:
fil.write(text) fil.write(text)
if __name__ == "__main__": if __name__ == "__main__":
create_toctree(_INFILES) create_toctree(_INFILES)

View file

@ -128,7 +128,7 @@ def url_resolver(url):
if url.endswith(choose_issue): if url.endswith(choose_issue):
return _github_issue_choose return _github_issue_choose
elif githubstart in url: elif githubstart in url:
urlpath = url[url.index(githubstart) + len(githubstart):] urlpath = url[url.index(githubstart) + len(githubstart) :]
if not (urlpath.startswith("develop/") or urlpath.startswith("master")): if not (urlpath.startswith("develop/") or urlpath.startswith("master")):
urlpath = "master/" + urlpath urlpath = "master/" + urlpath
return _github_code_root + urlpath return _github_code_root + urlpath
@ -137,14 +137,14 @@ def url_resolver(url):
ind = url.index(apistart) ind = url.index(apistart)
depth = url[:ind].count("/") + 1 depth = url[:ind].count("/") + 1
path = "../".join("" for _ in range(depth)) path = "../".join("" for _ in range(depth))
urlpath = path + "api/" + url[ind + len(apistart):] + ".html" urlpath = path + "api/" + url[ind + len(apistart) :] + ".html"
return urlpath return urlpath
elif sourcestart in url: elif sourcestart in url:
ind = url.index(sourcestart) ind = url.index(sourcestart)
depth = url[:ind].count("/") + 1 depth = url[:ind].count("/") + 1
path = "../".join("" for _ in range(depth)) path = "../".join("" for _ in range(depth))
modpath, *inmodule = url[ind + len(sourcestart):].rsplit("#", 1) modpath, *inmodule = url[ind + len(sourcestart) :].rsplit("#", 1)
modpath = "/".join(modpath.split(".")) modpath = "/".join(modpath.split("."))
inmodule = "#" + inmodule[0] if inmodule else "" inmodule = "#" + inmodule[0] if inmodule else ""
modpath = modpath + ".html" + inmodule modpath = modpath + ".html" + inmodule
@ -252,13 +252,12 @@ def autodoc_post_process_docstring(app, what, name, obj, options, lines):
def _sub_codeblock(match): def _sub_codeblock(match):
code = match.group(1) code = match.group(1)
return "::\n\n {}".format( return "::\n\n {}".format("\n ".join(lne for lne in code.split("\n")))
"\n ".join(lne for lne in code.split("\n")))
underline_map = { underline_map = {
1: "-", 1: "-",
2: "=", 2: "=",
3: '^', 3: "^",
4: '"', 4: '"',
} }
@ -271,11 +270,14 @@ def autodoc_post_process_docstring(app, what, name, obj, options, lines):
return f"{title}\n" + (underline_map[lvl] * len(title)) return f"{title}\n" + (underline_map[lvl] * len(title))
doc = "\n".join(lines) doc = "\n".join(lines)
doc = re.sub(r"```python\s*\n+(.*?)```", _sub_codeblock, doc, doc = re.sub(
flags=re.MULTILINE + re.DOTALL) r"```python\s*\n+(.*?)```", _sub_codeblock, doc, flags=re.MULTILINE + re.DOTALL
)
doc = re.sub(r"```", "", doc, flags=re.MULTILINE) doc = re.sub(r"```", "", doc, flags=re.MULTILINE)
doc = re.sub(r"`{1}", "**", doc, flags=re.MULTILINE) doc = re.sub(r"`{1}", "**", doc, flags=re.MULTILINE)
doc = re.sub(r"^(?P<hashes>#{1,2})\s*?(?P<title>.*?)$", _sub_header, doc, flags=re.MULTILINE) doc = re.sub(
r"^(?P<hashes>#{1,2})\s*?(?P<title>.*?)$", _sub_header, doc, flags=re.MULTILINE
)
newlines = doc.split("\n") newlines = doc.split("\n")
# we must modify lines in-place # we must modify lines in-place

View file

@ -508,8 +508,10 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
if banned: if banned:
# this is a banned IP or name! # this is a banned IP or name!
errors.append( errors.append(
_("|rYou have been banned and cannot continue from here." _(
"\nIf you feel this ban is in error, please email an admin.|x") "|rYou have been banned and cannot continue from here."
"\nIf you feel this ban is in error, please email an admin.|x"
)
) )
logger.log_sec(f"Authentication Denied (Banned): {username} (IP: {ip}).") logger.log_sec(f"Authentication Denied (Banned): {username} (IP: {ip}).")
LOGIN_THROTTLE.update(ip, "Too many sightings of banned artifact.") LOGIN_THROTTLE.update(ip, "Too many sightings of banned artifact.")
@ -673,7 +675,9 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
# Load the appropriate Character class # Load the appropriate Character class
character_typeclass = kwargs.pop("typeclass", None) character_typeclass = kwargs.pop("typeclass", None)
character_typeclass = character_typeclass if character_typeclass else settings.BASE_CHARACTER_TYPECLASS character_typeclass = (
character_typeclass if character_typeclass else settings.BASE_CHARACTER_TYPECLASS
)
Character = class_from_module(character_typeclass) Character = class_from_module(character_typeclass)
# Create the character # Create the character
@ -683,7 +687,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
ip=character_ip, ip=character_ip,
typeclass=character_typeclass, typeclass=character_typeclass,
permissions=character_permissions, permissions=character_permissions,
**kwargs **kwargs,
) )
if character: if character:
# Update playable character list # Update playable character list
@ -761,9 +765,9 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
banned = cls.is_banned(username=username, ip=ip) banned = cls.is_banned(username=username, ip=ip)
if banned: if banned:
# this is a banned IP or name! # this is a banned IP or name!
string = ( string = _(
_("|rYou have been banned and cannot continue from here." "|rYou have been banned and cannot continue from here."
"\nIf you feel this ban is in error, please email an admin.|x") "\nIf you feel this ban is in error, please email an admin.|x"
) )
errors.append(string) errors.append(string)
return None, errors return None, errors
@ -778,7 +782,9 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
except Exception as e: except Exception as e:
errors.append( errors.append(
_("There was an error creating the Account. If this problem persists, contact an admin.") _(
"There was an error creating the Account. If this problem persists, contact an admin."
)
) )
logger.log_trace() logger.log_trace()
return None, errors return None, errors
@ -802,7 +808,9 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
if account and settings.MULTISESSION_MODE < 2: if account and settings.MULTISESSION_MODE < 2:
# Auto-create a character to go with this account # Auto-create a character to go with this account
character, errs = account.create_character(typeclass=kwargs.get("character_typeclass")) character, errs = account.create_character(
typeclass=kwargs.get("character_typeclass")
)
if errs: if errs:
errors.extend(errs) errors.extend(errs)
@ -990,9 +998,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
searchdata, categories=("account",), include_account=False searchdata, categories=("account",), include_account=False
) )
if search_object: if search_object:
matches = ObjectDB.objects.object_search( matches = ObjectDB.objects.object_search(searchdata, typeclass=typeclass)
searchdata, typeclass=typeclass
)
else: else:
matches = AccountDB.objects.account_search(searchdata, typeclass=typeclass) matches = AccountDB.objects.account_search(searchdata, typeclass=typeclass)
@ -1340,7 +1346,9 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
""" """
reason = f" ({reason if reason else ''})" reason = f" ({reason if reason else ''})"
self._send_to_connect_channel(_("|R{key} disconnected{reason}|n").format(key=self.key, reason=reason)) self._send_to_connect_channel(
_("|R{key} disconnected{reason}|n").format(key=self.key, reason=reason)
)
def at_post_disconnect(self, **kwargs): def at_post_disconnect(self, **kwargs):
""" """
@ -1489,7 +1497,9 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
if is_su or len(characters) < charmax: if is_su or len(characters) < charmax:
if not characters: if not characters:
result.append( result.append(
_("\n\n You don't have any characters yet. See |whelp @charcreate|n for creating one.") _(
"\n\n You don't have any characters yet. See |whelp @charcreate|n for creating one."
)
) )
else: else:
result.append("\n |w@charcreate <name> [=description]|n - create new character") result.append("\n |w@charcreate <name> [=description]|n - create new character")

View file

@ -274,27 +274,28 @@ class AccountDBAdmin(BaseUserAdmin):
) )
@sensitive_post_parameters_m @sensitive_post_parameters_m
def user_change_password(self, request, id, form_url=''): def user_change_password(self, request, id, form_url=""):
user = self.get_object(request, unquote(id)) user = self.get_object(request, unquote(id))
if not self.has_change_permission(request, user): if not self.has_change_permission(request, user):
raise PermissionDenied raise PermissionDenied
if user is None: if user is None:
raise Http404('%(name)s object with primary key %(key)r does not exist.') % { raise Http404("%(name)s object with primary key %(key)r does not exist.") % {
'name': self.model._meta.verbose_name, "name": self.model._meta.verbose_name,
'key': escape(id), "key": escape(id),
} }
if request.method == 'POST': if request.method == "POST":
form = self.change_password_form(user, request.POST) form = self.change_password_form(user, request.POST)
if form.is_valid(): if form.is_valid():
form.save() form.save()
change_message = self.construct_change_message(request, form, None) change_message = self.construct_change_message(request, form, None)
self.log_change(request, user, change_message) self.log_change(request, user, change_message)
msg = 'Password changed successfully.' msg = "Password changed successfully."
messages.success(request, msg) messages.success(request, msg)
update_session_auth_hash(request, form.user) update_session_auth_hash(request, form.user)
return HttpResponseRedirect( return HttpResponseRedirect(
reverse( reverse(
'%s:%s_%s_change' % ( "%s:%s_%s_change"
% (
self.admin_site.name, self.admin_site.name,
user._meta.app_label, user._meta.app_label,
# the model_name is something we need to hardcode # the model_name is something we need to hardcode
@ -307,25 +308,24 @@ class AccountDBAdmin(BaseUserAdmin):
else: else:
form = self.change_password_form(user) form = self.change_password_form(user)
fieldsets = [(None, {'fields': list(form.base_fields)})] fieldsets = [(None, {"fields": list(form.base_fields)})]
adminForm = admin.helpers.AdminForm(form, fieldsets, {}) adminForm = admin.helpers.AdminForm(form, fieldsets, {})
context = { context = {
'title': 'Change password: %s' % escape(user.get_username()), "title": "Change password: %s" % escape(user.get_username()),
'adminForm': adminForm, "adminForm": adminForm,
'form_url': form_url, "form_url": form_url,
'form': form, "form": form,
'is_popup': (IS_POPUP_VAR in request.POST or "is_popup": (IS_POPUP_VAR in request.POST or IS_POPUP_VAR in request.GET),
IS_POPUP_VAR in request.GET), "add": True,
'add': True, "change": False,
'change': False, "has_delete_permission": False,
'has_delete_permission': False, "has_change_permission": True,
'has_change_permission': True, "has_absolute_url": False,
'has_absolute_url': False, "opts": self.model._meta,
'opts': self.model._meta, "original": user,
'original': user, "save_as": False,
'save_as': False, "show_save": True,
'show_save': True,
**self.admin_site.each_context(request), **self.admin_site.each_context(request),
} }
@ -333,8 +333,7 @@ class AccountDBAdmin(BaseUserAdmin):
return TemplateResponse( return TemplateResponse(
request, request,
self.change_user_password_template or self.change_user_password_template or "admin/auth/user/change_password.html",
'admin/auth/user/change_password.html',
context, context,
) )

View file

@ -329,7 +329,9 @@ class IRCBot(Bot):
chstr = f"{self.db.irc_channel} ({self.db.irc_network}:{self.db.irc_port})" chstr = f"{self.db.irc_channel} ({self.db.irc_network}:{self.db.irc_port})"
nicklist = ", ".join(sorted(kwargs["nicklist"], key=lambda n: n.lower())) nicklist = ", ".join(sorted(kwargs["nicklist"], key=lambda n: n.lower()))
for obj in self._nicklist_callers: for obj in self._nicklist_callers:
obj.msg(_("Nicks at {chstr}:\n {nicklist}").format(chstr=chstr, nicklist=nicklist)) obj.msg(
_("Nicks at {chstr}:\n {nicklist}").format(chstr=chstr, nicklist=nicklist)
)
self._nicklist_callers = [] self._nicklist_callers = []
return return
@ -338,7 +340,11 @@ class IRCBot(Bot):
if hasattr(self, "_ping_callers") and self._ping_callers: if hasattr(self, "_ping_callers") and self._ping_callers:
chstr = f"{self.db.irc_channel} ({self.db.irc_network}:{self.db.irc_port})" chstr = f"{self.db.irc_channel} ({self.db.irc_network}:{self.db.irc_port})"
for obj in self._ping_callers: for obj in self._ping_callers:
obj.msg(_("IRC ping return from {chstr} took {time}s.").format(chstr=chstr, time=kwargs['timing'])) obj.msg(
_("IRC ping return from {chstr} took {time}s.").format(
chstr=chstr, time=kwargs["timing"]
)
)
self._ping_callers = [] self._ping_callers = []
return return

View file

@ -109,8 +109,8 @@ class AccountDB(TypedObject, AbstractUser):
__applabel__ = "accounts" __applabel__ = "accounts"
__settingsclasspath__ = settings.BASE_SCRIPT_TYPECLASS __settingsclasspath__ = settings.BASE_SCRIPT_TYPECLASS
# class Meta: # class Meta:
# verbose_name = "Account" # verbose_name = "Account"
# cmdset_storage property # cmdset_storage property
# This seems very sensitive to caching, so leaving it be for now /Griatch # This seems very sensitive to caching, so leaving it be for now /Griatch

View file

@ -743,7 +743,9 @@ def cmdhandler(
sysarg = raw_string sysarg = raw_string
else: else:
# fallback to default error text # fallback to default error text
sysarg = _("Command '{command}' is not available.").format(command=raw_string) sysarg = _("Command '{command}' is not available.").format(
command=raw_string
)
suggestions = string_suggestions( suggestions = string_suggestions(
raw_string, raw_string,
cmdset.get_all_cmd_keys_and_aliases(caller), cmdset.get_all_cmd_keys_and_aliases(caller),
@ -751,7 +753,9 @@ def cmdhandler(
maxnum=3, maxnum=3,
) )
if suggestions: if suggestions:
sysarg += _(" Maybe you meant {command}?").format(command=utils.list_to_string(suggestions, _("or"), addquote=True)) sysarg += _(" Maybe you meant {command}?").format(
command=utils.list_to_string(suggestions, _("or"), addquote=True)
)
else: else:
sysarg += _(' Type "help" for help.') sysarg += _(' Type "help" for help.')
raise ExecSystemCommand(syscmd, sysarg) raise ExecSystemCommand(syscmd, sysarg)

View file

@ -184,7 +184,9 @@ def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False):
raise exc.with_traceback(tb) raise exc.with_traceback(tb)
else: else:
# try next suggested path # try next suggested path
errstring += _("\n(Unsuccessfully tried '{path}').").format(path=python_path) errstring += _("\n(Unsuccessfully tried '{path}').").format(
path=python_path
)
continue continue
try: try:
cmdsetclass = getattr(module, classname) cmdsetclass = getattr(module, classname)
@ -194,7 +196,9 @@ def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False):
dum, dum, tb = sys.exc_info() dum, dum, tb = sys.exc_info()
raise exc.with_traceback(tb) raise exc.with_traceback(tb)
else: else:
errstring += _("\n(Unsuccessfully tried '{path}').").format(path=python_path) errstring += _("\n(Unsuccessfully tried '{path}').").format(
path=python_path
)
continue continue
_CACHED_CMDSETS[python_path] = cmdsetclass _CACHED_CMDSETS[python_path] = cmdsetclass

View file

@ -314,8 +314,12 @@ class CmdIC(COMMAND_DEFAULT_CLASS):
if account.db._playable_characters: if account.db._playable_characters:
# look at the playable_characters list first # look at the playable_characters list first
character_candidates.extend( character_candidates.extend(
account.search(self.args, candidates=account.db._playable_characters, account.search(
search_object=True, quiet=True) self.args,
candidates=account.db._playable_characters,
search_object=True,
quiet=True,
)
) )
if account.locks.check_lockstring(account, "perm(Builder)"): if account.locks.check_lockstring(account, "perm(Builder)"):
@ -337,8 +341,12 @@ class CmdIC(COMMAND_DEFAULT_CLASS):
# fall back to global search only if Builder+ has no # fall back to global search only if Builder+ has no
# playable_characers in list and is not standing in a room # playable_characers in list and is not standing in a room
# with a matching char. # with a matching char.
character_candidates.extend([ character_candidates.extend(
char for char in search.object_search(self.args) if char.access(account, "puppet")] [
char
for char in search.object_search(self.args)
if char.access(account, "puppet")
]
) )
# handle possible candidates # handle possible candidates

View file

@ -1459,7 +1459,12 @@ class CmdOpen(ObjManipCommand):
if not typeclass: if not typeclass:
typeclass = settings.BASE_EXIT_TYPECLASS typeclass = settings.BASE_EXIT_TYPECLASS
exit_obj = create.create_object( exit_obj = create.create_object(
typeclass, key=exit_name, location=location, aliases=exit_aliases, locks=lockstring, report_to=caller typeclass,
key=exit_name,
location=location,
aliases=exit_aliases,
locks=lockstring,
report_to=caller,
) )
if exit_obj: if exit_obj:
# storing a destination is what makes it an exit! # storing a destination is what makes it an exit!
@ -2375,7 +2380,7 @@ class CmdExamine(ObjManipCommand):
value (any): Attribute value. value (any): Attribute value.
Returns: Returns:
""" """
if attr is None: if attr is None:
return "No such attribute was found." return "No such attribute was found."
value = utils.to_str(value) value = utils.to_str(value)
if crop: if crop:
@ -2413,13 +2418,14 @@ class CmdExamine(ObjManipCommand):
output = {} output = {}
if db_attr and db_attr[0]: if db_attr and db_attr[0]:
output["Persistent attribute(s)"] = "\n " + "\n ".join( output["Persistent attribute(s)"] = "\n " + "\n ".join(
sorted(self.list_attribute(crop, attr, category, value) sorted(
for attr, value, category in db_attr) self.list_attribute(crop, attr, category, value)
for attr, value, category in db_attr
)
) )
if ndb_attr and ndb_attr[0]: if ndb_attr and ndb_attr[0]:
output["Non-Persistent attribute(s)"] = " \n" + " \n".join( output["Non-Persistent attribute(s)"] = " \n" + " \n".join(
sorted(self.list_attribute(crop, attr, None, value) sorted(self.list_attribute(crop, attr, None, value) for attr, value in ndb_attr)
for attr, value in ndb_attr)
) )
return output return output
@ -2449,7 +2455,7 @@ class CmdExamine(ObjManipCommand):
output["Typeclass"] = f"{obj.typename} ({obj.typeclass_path})" output["Typeclass"] = f"{obj.typename} ({obj.typeclass_path})"
# sessions # sessions
if hasattr(obj, "sessions") and obj.sessions.all(): if hasattr(obj, "sessions") and obj.sessions.all():
output["Session id(s)"] = ", ".join(f"#{sess.sessid}" for sess in obj.sessions.all()) output["Session id(s)"] = ", ".join(f"#{sess.sessid}" for sess in obj.sessions.all())
# email, if any # email, if any
if hasattr(obj, "email") and obj.email: if hasattr(obj, "email") and obj.email:
output["Email"] = f"{dclr}{obj.email}|n" output["Email"] = f"{dclr}{obj.email}|n"
@ -2499,20 +2505,19 @@ class CmdExamine(ObjManipCommand):
locks = str(obj.locks) locks = str(obj.locks)
if locks: if locks:
locks_string = "\n" + utils.fill( locks_string = "\n" + utils.fill(
"; ".join([lock for lock in locks.split(";")]), indent=2) "; ".join([lock for lock in locks.split(";")]), indent=2
)
else: else:
locks_string = " Default" locks_string = " Default"
output["Locks"] = locks_string output["Locks"] = locks_string
# cmdsets # cmdsets
if not (len(obj.cmdset.all()) == 1 and obj.cmdset.current.key == "_EMPTY_CMDSET"): if not (len(obj.cmdset.all()) == 1 and obj.cmdset.current.key == "_EMPTY_CMDSET"):
# all() returns a 'stack', so make a copy to sort. # all() returns a 'stack', so make a copy to sort.
stored_cmdsets = sorted(obj.cmdset.all(), key=lambda x: x.priority, stored_cmdsets = sorted(obj.cmdset.all(), key=lambda x: x.priority, reverse=True)
reverse=True) output["Stored Cmdset(s)"] = "\n " + "\n ".join(
output["Stored Cmdset(s)"] = ( f"{cmdset.path} [{cmdset.key}] ({cmdset.mergetype}, prio {cmdset.priority})"
"\n " + "\n ".join( for cmdset in stored_cmdsets
f"{cmdset.path} [{cmdset.key}] ({cmdset.mergetype}, prio {cmdset.priority})" if cmdset.key != "_EMPTY_CMDSET"
for cmdset in stored_cmdsets if cmdset.key != "_EMPTY_CMDSET"
)
) )
# this gets all components of the currently merged set # this gets all components of the currently merged set
@ -2546,11 +2551,9 @@ class CmdExamine(ObjManipCommand):
pass pass
all_cmdsets = [cmdset for cmdset in dict(all_cmdsets).values()] all_cmdsets = [cmdset for cmdset in dict(all_cmdsets).values()]
all_cmdsets.sort(key=lambda x: x.priority, reverse=True) all_cmdsets.sort(key=lambda x: x.priority, reverse=True)
output["Merged Cmdset(s)"] = ( output["Merged Cmdset(s)"] = "\n " + "\n ".join(
"\n " + "\n ".join( f"{cmdset.path} [{cmdset.key}] ({cmdset.mergetype} prio {cmdset.priority})"
f"{cmdset.path} [{cmdset.key}] ({cmdset.mergetype} prio {cmdset.priority})" for cmdset in all_cmdsets
for cmdset in all_cmdsets
)
) )
# list the commands available to this object # list the commands available to this object
avail_cmdset = sorted([cmd.key for cmd in avail_cmdset if cmd.access(obj, "cmd")]) avail_cmdset = sorted([cmd.key for cmd in avail_cmdset if cmd.access(obj, "cmd")])
@ -2565,9 +2568,7 @@ class CmdExamine(ObjManipCommand):
# Tags # Tags
tags = obj.tags.all(return_key_and_category=True) tags = obj.tags.all(return_key_and_category=True)
tags_string = "\n" + utils.fill( tags_string = "\n" + utils.fill(
", ".join( ", ".join(sorted(f"{tag}[{category}]" for tag, category in tags)), indent=2,
sorted(f"{tag}[{category}]" for tag, category in tags )),
indent=2,
) )
if tags: if tags:
output["Tags[category]"] = tags_string output["Tags[category]"] = tags_string
@ -2584,9 +2585,13 @@ class CmdExamine(ObjManipCommand):
else: else:
things.append(content) things.append(content)
if exits: if exits:
output["Exits (has .destination)"] = ", ".join(f"{exit.name}({exit.dbref})" for exit in exits) output["Exits (has .destination)"] = ", ".join(
f"{exit.name}({exit.dbref})" for exit in exits
)
if pobjs: if pobjs:
output["Characters"] = ", ".join(f"{dclr}{pobj.name}|n({pobj.dbref})" for pobj in pobjs) output["Characters"] = ", ".join(
f"{dclr}{pobj.name}|n({pobj.dbref})" for pobj in pobjs
)
if things: if things:
output["Contents"] = ", ".join( output["Contents"] = ", ".join(
f"{cont.name}({cont.dbref})" f"{cont.name}({cont.dbref})"
@ -2601,7 +2606,6 @@ class CmdExamine(ObjManipCommand):
mainstr = "\n".join(f"{hclr}{header}|n: {block}" for (header, block) in output.items()) mainstr = "\n".join(f"{hclr}{header}|n: {block}" for (header, block) in output.items())
return f"{sep}\n{mainstr}\n{sep}" return f"{sep}\n{mainstr}\n{sep}"
def func(self): def func(self):
"""Process command""" """Process command"""
caller = self.caller caller = self.caller
@ -2671,7 +2675,10 @@ class CmdExamine(ObjManipCommand):
# we are only interested in specific attributes # we are only interested in specific attributes
ret = "\n".join( ret = "\n".join(
f"{self.header_color}{header}|n:{value}" f"{self.header_color}{header}|n:{value}"
for header, value in self.format_attributes(obj, attrname, crop=False).items()) for header, value in self.format_attributes(
obj, attrname, crop=False
).items()
)
self.caller.msg(ret) self.caller.msg(ret)
else: else:
session = None session = None

View file

@ -431,7 +431,9 @@ class CmdGet(COMMAND_DEFAULT_CLASS):
caller.msg("This can't be picked up.") caller.msg("This can't be picked up.")
else: else:
caller.msg("You pick up %s." % obj.name) caller.msg("You pick up %s." % obj.name)
caller.location.msg_contents("%s picks up %s." % (caller.name, obj.name), exclude=caller) caller.location.msg_contents(
"%s picks up %s." % (caller.name, obj.name), exclude=caller
)
# calling at_get hook method # calling at_get hook method
obj.at_get(caller) obj.at_get(caller)

View file

@ -446,7 +446,9 @@ def format_script_list(scripts):
table.add_row( table.add_row(
script.id, script.id,
f"{script.obj.key}({script.obj.dbref})" if (hasattr(script, "obj") and script.obj) else "<Global>", f"{script.obj.key}({script.obj.dbref})"
if (hasattr(script, "obj") and script.obj)
else "<Global>",
script.key, script.key,
script.interval if script.interval > 0 else "--", script.interval if script.interval > 0 else "--",
nextrep, nextrep,

View file

@ -155,6 +155,7 @@ class CommandTest(EvenniaTest):
prt = "" prt = ""
for ic, char in enumerate(msg): for ic, char in enumerate(msg):
import re import re
prt += char prt += char
sep1 = "\n" + "=" * 30 + "Wanted message" + "=" * 34 + "\n" sep1 = "\n" + "=" * 30 + "Wanted message" + "=" * 34 + "\n"
@ -369,11 +370,13 @@ class TestAccount(CommandTest):
def test_ic__nonaccess(self): def test_ic__nonaccess(self):
self.account.unpuppet_object(self.session) self.account.unpuppet_object(self.session)
self.call( self.call(
account.CmdIC(), "Nonexistent", "That is not a valid character choice.", account.CmdIC(),
caller=self.account, receiver=self.account "Nonexistent",
"That is not a valid character choice.",
caller=self.account,
receiver=self.account,
) )
def test_password(self): def test_password(self):
self.call( self.call(
account.CmdPassword(), account.CmdPassword(),
@ -485,7 +488,11 @@ class TestBuilding(CommandTest):
# escape inlinefuncs # escape inlinefuncs
self.char1.db.test2 = "this is a $random() value." self.char1.db.test2 = "this is a $random() value."
self.call(building.CmdExamine(), "self/test2", "Persistent attribute(s):\n test2 = this is a \$random() value.") self.call(
building.CmdExamine(),
"self/test2",
"Persistent attribute(s):\n test2 = this is a \$random() value.",
)
self.room1.scripts.add(self.script.__class__) self.room1.scripts.add(self.script.__class__)
self.call(building.CmdExamine(), "") self.call(building.CmdExamine(), "")

View file

@ -611,7 +611,6 @@ class CmdUsePuzzleParts(MuxCommand):
passed in. passed in.
""" """
key = "use" key = "use"
aliases = "combine" aliases = "combine"
locks = "cmd:pperm(use) or pperm(Player)" locks = "cmd:pperm(use) or pperm(Player)"

View file

@ -131,7 +131,9 @@ class HelpEntryManager(TypedObjectManager):
for topic in topics: for topic in topics:
topic.help_category = default_category topic.help_category = default_category
topic.save() topic.save()
string = _("Help database moved to category {default_category}").format(default_category=default_category) string = _("Help database moved to category {default_category}").format(
default_category=default_category
)
logger.log_info(string) logger.log_info(string)
def search_help(self, ostring, help_category=None): def search_help(self, ostring, help_category=None):

View file

@ -236,7 +236,13 @@ class LockHandler(object):
elist.append(_("Lock: lock-function '%s' is not available.") % funcstring) elist.append(_("Lock: lock-function '%s' is not available.") % funcstring)
continue continue
args = list(arg.strip() for arg in rest.split(",") if arg and "=" not in arg) args = list(arg.strip() for arg in rest.split(",") if arg and "=" not in arg)
kwargs = dict([(part.strip() for part in arg.split("=", 1)) for arg in rest.split(",") if arg and "=" in arg]) kwargs = dict(
[
(part.strip() for part in arg.split("=", 1))
for arg in rest.split(",")
if arg and "=" in arg
]
)
lock_funcs.append((func, args, kwargs)) lock_funcs.append((func, args, kwargs))
evalstring = evalstring.replace(funcstring, "%s") evalstring = evalstring.replace(funcstring, "%s")
if len(lock_funcs) < nfuncs: if len(lock_funcs) < nfuncs:
@ -246,7 +252,11 @@ class LockHandler(object):
evalstring = " ".join(_RE_OK.findall(evalstring)) evalstring = " ".join(_RE_OK.findall(evalstring))
eval(evalstring % tuple(True for func in funclist), {}, {}) eval(evalstring % tuple(True for func in funclist), {}, {})
except Exception: except Exception:
elist.append(_("Lock: definition '{lock_string}' has syntax errors.").format(lock_string=raw_lockstring)) elist.append(
_("Lock: definition '{lock_string}' has syntax errors.").format(
lock_string=raw_lockstring
)
)
continue continue
if access_type in locks: if access_type in locks:
duplicates += 1 duplicates += 1

View file

@ -10,6 +10,7 @@ from evennia.objects.models import ObjectDB
from django.contrib.admin.utils import flatten_fieldsets from django.contrib.admin.utils import flatten_fieldsets
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
class ObjectAttributeInline(AttributeInline): class ObjectAttributeInline(AttributeInline):
""" """
Defines inline descriptions of Attributes (experimental) Defines inline descriptions of Attributes (experimental)
@ -61,7 +62,7 @@ class ObjectCreateForm(forms.ModelForm):
required=False, required=False,
widget=forms.TextInput(attrs={"size": "78"}), widget=forms.TextInput(attrs={"size": "78"}),
help_text="Most non-character objects don't need a cmdset" help_text="Most non-character objects don't need a cmdset"
" and can leave this field blank." " and can leave this field blank.",
) )
raw_id_fields = ("db_destination", "db_location", "db_home") raw_id_fields = ("db_destination", "db_location", "db_home")

View file

@ -175,15 +175,12 @@ class ObjectDBManager(TypedObjectManager):
) )
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q() type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
results = ( results = self.filter(
self cand_restriction
.filter( & type_restriction
cand_restriction & Q(db_attributes__db_key=attribute_name)
& type_restriction & Q(db_attributes__db_value=attribute_value)
& Q(db_attributes__db_key=attribute_name) ).order_by("id")
& Q(db_attributes__db_value=attribute_value))
.order_by("id")
)
return results return results
def get_objs_with_db_property(self, property_name, candidates=None): def get_objs_with_db_property(self, property_name, candidates=None):

View file

@ -2028,8 +2028,10 @@ class DefaultCharacter(DefaultObject):
# lockstring of newly created rooms, for easy overloading. # lockstring of newly created rooms, for easy overloading.
# Will be formatted with the appropriate attributes. # Will be formatted with the appropriate attributes.
lockstring = ("puppet:id({character_id}) or pid({account_id}) or perm(Developer) or pperm(Developer);" lockstring = (
"delete:id({account_id}) or perm(Admin)") "puppet:id({character_id}) or pid({account_id}) or perm(Developer) or pperm(Developer);"
"delete:id({account_id}) or perm(Admin)"
)
@classmethod @classmethod
def create(cls, key, account=None, **kwargs): def create(cls, key, account=None, **kwargs):

View file

@ -467,7 +467,11 @@ def getKeyPair(pubkeyfile, privkeyfile):
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.asymmetric import rsa
rsa_key = Key(rsa.generate_private_key(public_exponent=65537, key_size=_KEY_LENGTH, backend=default_backend())) rsa_key = Key(
rsa.generate_private_key(
public_exponent=65537, key_size=_KEY_LENGTH, backend=default_backend()
)
)
public_key_string = rsa_key.public().toString(type="OPENSSH").decode() public_key_string = rsa_key.public().toString(type="OPENSSH").decode()
private_key_string = rsa_key.toString(type="OPENSSH").decode() private_key_string = rsa_key.toString(type="OPENSSH").decode()

View file

@ -1,4 +1,3 @@
try: try:
from django.utils.unittest import TestCase from django.utils.unittest import TestCase
except ImportError: except ImportError:
@ -290,7 +289,7 @@ class TestWebSocket(EvenniaTest):
self.proto.sessionhandler = PORTAL_SESSIONS self.proto.sessionhandler = PORTAL_SESSIONS
self.proto.sessionhandler.portal = Mock() self.proto.sessionhandler.portal = Mock()
self.proto.transport = proto_helpers.StringTransport() self.proto.transport = proto_helpers.StringTransport()
#self.proto.transport = proto_helpers.FakeDatagramTransport() # self.proto.transport = proto_helpers.FakeDatagramTransport()
self.proto.transport.client = ["localhost"] self.proto.transport.client = ["localhost"]
self.proto.transport.setTcpKeepAlive = Mock() self.proto.transport.setTcpKeepAlive = Mock()
self.proto.state = MagicMock() self.proto.state = MagicMock()
@ -318,4 +317,4 @@ class TestWebSocket(EvenniaTest):
self.proto.sendLine = MagicMock() self.proto.sendLine = MagicMock()
msg = json.dumps(["logged_in", (), {}]) msg = json.dumps(["logged_in", (), {}])
self.proto.sessionhandler.data_out(self.proto, text=[["Excepting Alice"], {}]) self.proto.sessionhandler.data_out(self.proto, text=[["Excepting Alice"], {}])
self.proto.sendLine.assert_called_with(json.dumps(['text', ['Excepting Alice'], {}])) self.proto.sendLine.assert_called_with(json.dumps(["text", ["Excepting Alice"], {}]))

View file

@ -132,7 +132,7 @@ def _server_maintenance():
else: else:
# adjust the runtime not with 60s but with the actual elapsed time # adjust the runtime not with 60s but with the actual elapsed time
# in case this may varies slightly from 60s. # in case this may varies slightly from 60s.
_GAMETIME_MODULE.SERVER_RUNTIME += (now - _LAST_SERVER_TIME_SNAPSHOT) _GAMETIME_MODULE.SERVER_RUNTIME += now - _LAST_SERVER_TIME_SNAPSHOT
_LAST_SERVER_TIME_SNAPSHOT = now _LAST_SERVER_TIME_SNAPSHOT = now
# update game time and save it across reloads # update game time and save it across reloads

View file

@ -221,9 +221,7 @@ class SessionHandler(dict):
elif isinstance(data, (str, bytes)): elif isinstance(data, (str, bytes)):
data = _utf8(data) data = _utf8(data)
if (_INLINEFUNC_ENABLED if _INLINEFUNC_ENABLED and not raw and isinstance(self, ServerSessionHandler):
and not raw
and isinstance(self, ServerSessionHandler)):
# only parse inlinefuncs on the outgoing path (sessionhandler->) # only parse inlinefuncs on the outgoing path (sessionhandler->)
data = parse_inlinefunc(data, strip=strip_inlinefunc, session=session) data = parse_inlinefunc(data, strip=strip_inlinefunc, session=session)

View file

@ -26,6 +26,7 @@ class EvenniaTestSuiteRunner(DiscoverRunner):
# can't mock it - instead we stop it before starting the test - otherwise # can't mock it - instead we stop it before starting the test - otherwise
# we'd get unclean reactor errors across test boundaries. # we'd get unclean reactor errors across test boundaries.
from evennia.server.portal.portal import PORTAL from evennia.server.portal.portal import PORTAL
PORTAL.maintenance_task.stop() PORTAL.maintenance_task.stop()
import evennia import evennia
@ -34,4 +35,3 @@ class EvenniaTestSuiteRunner(DiscoverRunner):
return super(EvenniaTestSuiteRunner, self).build_suite( return super(EvenniaTestSuiteRunner, self).build_suite(
test_labels, extra_tests=extra_tests, **kwargs test_labels, extra_tests=extra_tests, **kwargs
) )

View file

@ -31,14 +31,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
# Attribute manager methods # Attribute manager methods
def get_attribute( def get_attribute(
self, self, key=None, category=None, value=None, strvalue=None, obj=None, attrtype=None, **kwargs
key=None,
category=None,
value=None,
strvalue=None,
obj=None,
attrtype=None,
**kwargs
): ):
""" """
Return Attribute objects by key, by category, by value, by Return Attribute objects by key, by category, by value, by
@ -82,9 +75,9 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
# no reason to make strvalue/value mutually exclusive at this level # no reason to make strvalue/value mutually exclusive at this level
query.append(("attribute__db_value", value)) query.append(("attribute__db_value", value))
return Attribute.objects.filter( return Attribute.objects.filter(
pk__in=self.model.db_attributes.through.objects.filter( pk__in=self.model.db_attributes.through.objects.filter(**dict(query)).values_list(
**dict(query) "attribute_id", flat=True
).values_list("attribute_id", flat=True) )
) )
def get_nick(self, key=None, category=None, value=None, strvalue=None, obj=None): def get_nick(self, key=None, category=None, value=None, strvalue=None, obj=None):
@ -111,13 +104,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
) )
def get_by_attribute( def get_by_attribute(
self, self, key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
key=None,
category=None,
value=None,
strvalue=None,
attrtype=None,
**kwargs
): ):
""" """
Return objects having attributes with the given key, category, Return objects having attributes with the given key, category,
@ -174,15 +161,11 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
obj (list): Objects having the matching Nicks. obj (list): Objects having the matching Nicks.
""" """
return self.get_by_attribute( return self.get_by_attribute(key=key, category=category, strvalue=nick, attrtype="nick")
key=key, category=category, strvalue=nick, attrtype="nick"
)
# Tag manager methods # Tag manager methods
def get_tag( def get_tag(self, key=None, category=None, obj=None, tagtype=None, global_search=False):
self, key=None, category=None, obj=None, tagtype=None, global_search=False
):
""" """
Return Tag objects by key, by category, by object (it is Return Tag objects by key, by category, by object (it is
stored on) or with a combination of those criteria. stored on) or with a combination of those criteria.
@ -226,9 +209,9 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
if category: if category:
query.append(("tag__db_category", category)) query.append(("tag__db_category", category))
return Tag.objects.filter( return Tag.objects.filter(
pk__in=self.model.db_tags.through.objects.filter( pk__in=self.model.db_tags.through.objects.filter(**dict(query)).values_list(
**dict(query) "tag_id", flat=True
).values_list("tag_id", flat=True) )
) )
def get_permission(self, key=None, category=None, obj=None): def get_permission(self, key=None, category=None, obj=None):
@ -310,9 +293,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
dbmodel = self.model.__dbclass__.__name__.lower() dbmodel = self.model.__dbclass__.__name__.lower()
query = ( query = (
self.filter( self.filter(db_tags__db_tagtype__iexact=tagtype, db_tags__db_model__iexact=dbmodel)
db_tags__db_tagtype__iexact=tagtype, db_tags__db_model__iexact=dbmodel
)
.distinct() .distinct()
.order_by("id") .order_by("id")
) )
@ -332,9 +313,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
clauses = Q() clauses = Q()
for ikey, key in enumerate(keys): for ikey, key in enumerate(keys):
# ANY mode; must match any one of the given tags/categories # ANY mode; must match any one of the given tags/categories
clauses |= Q( clauses |= Q(db_key__iexact=key, db_category__iexact=categories[ikey])
db_key__iexact=key, db_category__iexact=categories[ikey]
)
else: else:
# only one or more categories given # only one or more categories given
clauses = Q() clauses = Q()
@ -344,8 +323,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
tags = _Tag.objects.filter(clauses) tags = _Tag.objects.filter(clauses)
query = query.filter(db_tags__in=tags).annotate( query = query.filter(db_tags__in=tags).annotate(
matches=Count("db_tags__pk", filter=Q(db_tags__in=tags), matches=Count("db_tags__pk", filter=Q(db_tags__in=tags), distinct=True)
distinct=True)
) )
if anymatch: if anymatch:
@ -412,9 +390,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
# try to get old tag # try to get old tag
dbmodel = self.model.__dbclass__.__name__.lower() dbmodel = self.model.__dbclass__.__name__.lower()
tag = self.get_tag( tag = self.get_tag(key=key, category=category, tagtype=tagtype, global_search=True)
key=key, category=category, tagtype=tagtype, global_search=True
)
if tag and data is not None: if tag and data is not None:
# get tag from list returned by get_tag # get tag from list returned by get_tag
tag = tag[0] tag = tag[0]
@ -428,9 +404,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
from evennia.typeclasses.models import Tag as _Tag from evennia.typeclasses.models import Tag as _Tag
tag = _Tag.objects.create( tag = _Tag.objects.create(
db_key=key.strip().lower() if key is not None else None, db_key=key.strip().lower() if key is not None else None,
db_category=category.strip().lower() db_category=category.strip().lower() if category and key is not None else None,
if category and key is not None
else None,
db_data=data, db_data=data,
db_model=dbmodel, db_model=dbmodel,
db_tagtype=tagtype.strip().lower() if tagtype is not None else None, db_tagtype=tagtype.strip().lower() if tagtype is not None else None,
@ -539,8 +513,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
typeclass=F("db_typeclass_path"), typeclass=F("db_typeclass_path"),
# Calculate this class' percentage of total composition # Calculate this class' percentage of total composition
percent=ExpressionWrapper( percent=ExpressionWrapper(
((F("count") / float(self.count())) * 100.0), ((F("count") / float(self.count())) * 100.0), output_field=FloatField(),
output_field=FloatField(),
), ),
) )
.values("typeclass", "count", "percent") .values("typeclass", "count", "percent")
@ -560,9 +533,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
stats = self.get_typeclass_totals().order_by("typeclass") stats = self.get_typeclass_totals().order_by("typeclass")
return {x.get("typeclass"): x.get("count") for x in stats} return {x.get("typeclass"): x.get("count") for x in stats}
def typeclass_search( def typeclass_search(self, typeclass, include_children=False, include_parents=False):
self, typeclass, include_children=False, include_parents=False
):
""" """
Searches through all objects returning those which has a Searches through all objects returning those which has a
certain typeclass. If location is set, limit search to objects certain typeclass. If location is set, limit search to objects
@ -837,8 +808,7 @@ class TypeclassManager(TypedObjectManager):
""" """
paths = [self.model.path] + [ paths = [self.model.path] + [
"%s.%s" % (cls.__module__, cls.__name__) "%s.%s" % (cls.__module__, cls.__name__) for cls in self._get_subclasses(self.model)
for cls in self._get_subclasses(self.model)
] ]
kwargs.update({"db_typeclass_path__in": paths}) kwargs.update({"db_typeclass_path__in": paths})
return super().get(**kwargs) return super().get(**kwargs)
@ -860,8 +830,7 @@ class TypeclassManager(TypedObjectManager):
""" """
# query, including all subclasses # query, including all subclasses
paths = [self.model.path] + [ paths = [self.model.path] + [
"%s.%s" % (cls.__module__, cls.__name__) "%s.%s" % (cls.__module__, cls.__name__) for cls in self._get_subclasses(self.model)
for cls in self._get_subclasses(self.model)
] ]
kwargs.update({"db_typeclass_path__in": paths}) kwargs.update({"db_typeclass_path__in": paths})
return super().filter(*args, **kwargs) return super().filter(*args, **kwargs)
@ -876,7 +845,6 @@ class TypeclassManager(TypedObjectManager):
""" """
paths = [self.model.path] + [ paths = [self.model.path] + [
"%s.%s" % (cls.__module__, cls.__name__) "%s.%s" % (cls.__module__, cls.__name__) for cls in self._get_subclasses(self.model)
for cls in self._get_subclasses(self.model)
] ]
return super().all().filter(db_typeclass_path__in=paths) return super().all().filter(db_typeclass_path__in=paths)

View file

@ -126,7 +126,6 @@ class TypeclassBase(SharedMemoryModelBase):
if not dbmodel: if not dbmodel:
raise TypeError(f"{name} does not appear to inherit from a database model.") raise TypeError(f"{name} does not appear to inherit from a database model.")
# typeclass proxy setup # typeclass proxy setup
# first check explicit __applabel__ on the typeclass, then figure # first check explicit __applabel__ on the typeclass, then figure
# it out from the dbmodel # it out from the dbmodel
@ -135,6 +134,7 @@ class TypeclassBase(SharedMemoryModelBase):
attrs["__applabel__"] = dbmodel._meta.app_label attrs["__applabel__"] = dbmodel._meta.app_label
if "Meta" not in attrs: if "Meta" not in attrs:
class Meta: class Meta:
proxy = True proxy = True
app_label = attrs.get("__applabel__", "typeclasses") app_label = attrs.get("__applabel__", "typeclasses")
@ -156,8 +156,7 @@ class TypeclassBase(SharedMemoryModelBase):
# attach signals # attach signals
signals.post_save.connect(call_at_first_save, sender=new_class) signals.post_save.connect(call_at_first_save, sender=new_class)
signals.pre_delete.connect( signals.pre_delete.connect(remove_attributes_on_delete, sender=new_class)
remove_attributes_on_delete, sender=new_class)
return new_class return new_class

View file

@ -43,10 +43,12 @@ class TestAttributes(EvenniaTest):
self.assertEqual(self.obj1.attributes.get(key), value) self.assertEqual(self.obj1.attributes.get(key), value)
def test_batch_add(self): def test_batch_add(self):
attrs = [("key1", "value1"), attrs = [
("key2", "value2", "category2"), ("key1", "value1"),
("key3", "value3"), ("key2", "value2", "category2"),
("key4", "value4", "category4", "attrread:id(1)", False)] ("key3", "value3"),
("key4", "value4", "category4", "attrread:id(1)", False),
]
new_attrs = self.obj1.attributes.batch_add(*attrs) new_attrs = self.obj1.attributes.batch_add(*attrs)
attrobj = self.obj1.attributes.get(key="key4", category="category4", return_obj=True) attrobj = self.obj1.attributes.get(key="key4", category="category4", return_obj=True)
self.assertEqual(attrobj.value, "value4") self.assertEqual(attrobj.value, "value4")
@ -69,16 +71,12 @@ class TestTypedObjectManager(EvenniaTest):
self.obj2.tags.add("tag4") self.obj2.tags.add("tag4")
self.obj2.tags.add("tag2c") self.obj2.tags.add("tag2c")
self.assertEqual(self._manager("get_by_tag", "tag1"), [self.obj1]) self.assertEqual(self._manager("get_by_tag", "tag1"), [self.obj1])
self.assertEqual( self.assertEqual(set(self._manager("get_by_tag", "tag2")), set([self.obj1, self.obj2]))
set(self._manager("get_by_tag", "tag2")), set([self.obj1, self.obj2])
)
self.assertEqual(self._manager("get_by_tag", "tag2a"), [self.obj2]) self.assertEqual(self._manager("get_by_tag", "tag2a"), [self.obj2])
self.assertEqual(self._manager("get_by_tag", "tag3 with spaces"), [self.obj2]) self.assertEqual(self._manager("get_by_tag", "tag3 with spaces"), [self.obj2])
self.assertEqual(self._manager("get_by_tag", ["tag2a", "tag2b"]), [self.obj2]) self.assertEqual(self._manager("get_by_tag", ["tag2a", "tag2b"]), [self.obj2])
self.assertEqual(self._manager("get_by_tag", ["tag2a", "tag1"]), []) self.assertEqual(self._manager("get_by_tag", ["tag2a", "tag1"]), [])
self.assertEqual( self.assertEqual(self._manager("get_by_tag", ["tag2a", "tag4", "tag2c"]), [self.obj2])
self._manager("get_by_tag", ["tag2a", "tag4", "tag2c"]), [self.obj2]
)
def test_get_by_tag_and_category(self): def test_get_by_tag_and_category(self):
self.obj1.tags.add("tag5", "category1") self.obj1.tags.add("tag5", "category1")
@ -94,24 +92,17 @@ class TestTypedObjectManager(EvenniaTest):
self.obj1.tags.add("tag8", "category6") self.obj1.tags.add("tag8", "category6")
self.obj2.tags.add("tag9", "category6") self.obj2.tags.add("tag9", "category6")
self.assertEqual( self.assertEqual(self._manager("get_by_tag", "tag5", "category1"), [self.obj1, self.obj2])
self._manager("get_by_tag", "tag5", "category1"), [self.obj1, self.obj2]
)
self.assertEqual(self._manager("get_by_tag", "tag6", "category1"), []) self.assertEqual(self._manager("get_by_tag", "tag6", "category1"), [])
self.assertEqual( self.assertEqual(self._manager("get_by_tag", "tag6", "category3"), [self.obj1, self.obj2])
self._manager("get_by_tag", "tag6", "category3"), [self.obj1, self.obj2]
)
self.assertEqual( self.assertEqual(
self._manager("get_by_tag", ["tag5", "tag6"], ["category1", "category3"]), self._manager("get_by_tag", ["tag5", "tag6"], ["category1", "category3"]),
[self.obj1, self.obj2], [self.obj1, self.obj2],
) )
self.assertEqual( self.assertEqual(
self._manager("get_by_tag", ["tag5", "tag7"], "category1"), self._manager("get_by_tag", ["tag5", "tag7"], "category1"), [self.obj1, self.obj2],
[self.obj1, self.obj2],
)
self.assertEqual(
self._manager("get_by_tag", category="category1"), [self.obj1, self.obj2]
) )
self.assertEqual(self._manager("get_by_tag", category="category1"), [self.obj1, self.obj2])
self.assertEqual(self._manager("get_by_tag", category="category2"), [self.obj2]) self.assertEqual(self._manager("get_by_tag", category="category2"), [self.obj2])
self.assertEqual( self.assertEqual(
self._manager("get_by_tag", category=["category1", "category3"]), self._manager("get_by_tag", category=["category1", "category3"]),
@ -121,49 +112,33 @@ class TestTypedObjectManager(EvenniaTest):
self._manager("get_by_tag", category=["category1", "category2"]), self._manager("get_by_tag", category=["category1", "category2"]),
[self.obj1, self.obj2], [self.obj1, self.obj2],
) )
self.assertEqual( self.assertEqual(self._manager("get_by_tag", category=["category5", "category4"]), [])
self._manager("get_by_tag", category=["category5", "category4"]), [] self.assertEqual(self._manager("get_by_tag", category="category1"), [self.obj1, self.obj2])
) self.assertEqual(self._manager("get_by_tag", category="category6"), [self.obj1, self.obj2])
self.assertEqual(
self._manager("get_by_tag", category="category1"), [self.obj1, self.obj2]
)
self.assertEqual(
self._manager("get_by_tag", category="category6"), [self.obj1, self.obj2]
)
def test_get_tag_with_all(self): def test_get_tag_with_all(self):
self.obj1.tags.add("tagA", "categoryA") self.obj1.tags.add("tagA", "categoryA")
self.assertEqual( self.assertEqual(
self._manager( self._manager("get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="all"),
"get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="all"
),
[], [],
) )
def test_get_tag_with_any(self): def test_get_tag_with_any(self):
self.obj1.tags.add("tagA", "categoryA") self.obj1.tags.add("tagA", "categoryA")
self.assertEqual( self.assertEqual(
self._manager( self._manager("get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="any"),
"get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="any"
),
[self.obj1], [self.obj1],
) )
def test_get_tag_withnomatch(self): def test_get_tag_withnomatch(self):
self.obj1.tags.add("tagC", "categoryC") self.obj1.tags.add("tagC", "categoryC")
self.assertEqual( self.assertEqual(
self._manager( self._manager("get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="any"),
"get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="any"
),
[], [],
) )
def test_batch_add(self): def test_batch_add(self):
tags = ["tag1", tags = ["tag1", ("tag2", "category2"), "tag3", ("tag4", "category4", "data4")]
("tag2", "category2"),
"tag3",
("tag4", "category4", "data4")
]
self.obj1.tags.batch_add(*tags) self.obj1.tags.batch_add(*tags)
self.assertEqual(self.obj1.tags.get("tag1"), "tag1") self.assertEqual(self.obj1.tags.get("tag1"), "tag1")
tagobj = self.obj1.tags.get("tag4", category="category4", return_tagobj=True) tagobj = self.obj1.tags.get("tag4", category="category4", return_tagobj=True)

View file

@ -369,8 +369,9 @@ help_entry = create_help_entry
# Comm system methods # Comm system methods
def create_message(senderobj, message, channels=None, receivers=None, def create_message(
locks=None, tags=None, header=None): senderobj, message, channels=None, receivers=None, locks=None, tags=None, header=None
):
""" """
Create a new communication Msg. Msgs represent a unit of Create a new communication Msg. Msgs represent a unit of
database-persistent communication between entites. database-persistent communication between entites.
@ -424,7 +425,9 @@ message = create_message
create_msg = create_message create_msg = create_message
def create_channel(key, aliases=None, desc=None, locks=None, keep_log=True, typeclass=None, tags=None): def create_channel(
key, aliases=None, desc=None, locks=None, keep_log=True, typeclass=None, tags=None
):
""" """
Create A communication Channel. A Channel serves as a central hub Create A communication Channel. A Channel serves as a central hub
for distributing Msgs to groups of people without specifying the for distributing Msgs to groups of people without specifying the

View file

@ -235,7 +235,6 @@ class _SaverMutable(object):
def __gt__(self, other): def __gt__(self, other):
return self._data > other return self._data > other
@_save @_save
def __setitem__(self, key, value): def __setitem__(self, key, value):
self._data.__setitem__(key, self._convert_mutables(value)) self._data.__setitem__(key, self._convert_mutables(value))

View file

@ -73,6 +73,7 @@ _STACK_MAXSIZE = settings.INLINEFUNC_STACK_MAXSIZE
# example/testing inline functions # example/testing inline functions
def random(*args, **kwargs): def random(*args, **kwargs):
""" """
Inlinefunc. Returns a random number between Inlinefunc. Returns a random number between
@ -99,11 +100,11 @@ def random(*args, **kwargs):
nargs = len(args) nargs = len(args)
if nargs == 1: if nargs == 1:
# only maxval given # only maxval given
minval, maxval = '0', args[0] minval, maxval = "0", args[0]
elif nargs > 1: elif nargs > 1:
minval, maxval = args[:2] minval, maxval = args[:2]
else: else:
minval, maxval = ('0', '1') minval, maxval = ("0", "1")
if "." in minval or "." in maxval: if "." in minval or "." in maxval:
# float mode # float mode
@ -518,10 +519,13 @@ def raw(string):
Args: Args:
string (str): String with inlinefuncs to escape. string (str): String with inlinefuncs to escape.
""" """
def _escape(match): def _escape(match):
return "\\" + match.group(0) return "\\" + match.group(0)
return _RE_STARTTOKEN.sub(_escape, string) return _RE_STARTTOKEN.sub(_escape, string)
# #
# Nick templating # Nick templating
# #

View file

@ -102,7 +102,6 @@ def dbsafe_encode(value, compress_object=False, pickle_protocol=DEFAULT_PROTOCOL
value = dumps(value, protocol=pickle_protocol) value = dumps(value, protocol=pickle_protocol)
if compress_object: if compress_object:
value = compress(value) value = compress(value)
value = b64encode(value).decode() # decode bytes to str value = b64encode(value).decode() # decode bytes to str

View file

@ -205,29 +205,33 @@ help_entries = search_help
# not the attribute object itself (this is usually what you want) # not the attribute object itself (this is usually what you want)
def search_object_attribute(key=None, category=None, value=None, def search_object_attribute(
strvalue=None, attrtype=None, **kwargs): key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
):
return ObjectDB.objects.get_by_attribute( return ObjectDB.objects.get_by_attribute(
key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs
) )
def search_account_attribute(key=None, category=None, value=None, def search_account_attribute(
strvalue=None, attrtype=None, **kwargs): key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
):
return AccountDB.objects.get_by_attribute( return AccountDB.objects.get_by_attribute(
key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs
) )
def search_script_attribute(key=None, category=None, value=None, def search_script_attribute(
strvalue=None, attrtype=None, **kwargs): key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
):
return ScriptDB.objects.get_by_attribute( return ScriptDB.objects.get_by_attribute(
key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs
) )
def search_channel_attribute(key=None, category=None, value=None, def search_channel_attribute(
strvalue=None, attrtype=None, **kwargs): key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
):
return Channel.objects.get_by_attribute( return Channel.objects.get_by_attribute(
key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs
) )

View file

@ -158,11 +158,13 @@ class EvenniaTest(TestCase):
self.account2.delete() self.account2.delete()
super().tearDown() super().tearDown()
class LocalEvenniaTest(EvenniaTest): class LocalEvenniaTest(EvenniaTest):
""" """
This test class is intended for inheriting in mygame tests. This test class is intended for inheriting in mygame tests.
It helps ensure your tests are run with your own objects. It helps ensure your tests are run with your own objects.
""" """
account_typeclass = settings.BASE_ACCOUNT_TYPECLASS account_typeclass = settings.BASE_ACCOUNT_TYPECLASS
object_typeclass = settings.BASE_OBJECT_TYPECLASS object_typeclass = settings.BASE_OBJECT_TYPECLASS
character_typeclass = settings.BASE_CHARACTER_TYPECLASS character_typeclass = settings.BASE_CHARACTER_TYPECLASS

View file

@ -109,11 +109,17 @@ class TestCreateHelpEntry(TestCase):
def test_create_help_entry__complex(self): def test_create_help_entry__complex(self):
locks = "foo:false();bar:true()" locks = "foo:false();bar:true()"
aliases = ['foo', 'bar', 'tst'] aliases = ["foo", "bar", "tst"]
tags = [("tag1", "help"), ("tag2", "help"), ("tag3", "help")] tags = [("tag1", "help"), ("tag2", "help"), ("tag3", "help")]
entry = create.create_help_entry("testentry", self.help_entry, category="Testing", entry = create.create_help_entry(
locks=locks, aliases=aliases, tags=tags) "testentry",
self.help_entry,
category="Testing",
locks=locks,
aliases=aliases,
tags=tags,
)
self.assertTrue(all(lock in entry.locks.all() for lock in locks.split(";"))) self.assertTrue(all(lock in entry.locks.all() for lock in locks.split(";")))
self.assertEqual(list(entry.aliases.all()).sort(), aliases.sort()) self.assertEqual(list(entry.aliases.all()).sort(), aliases.sort())
self.assertEqual(entry.tags.all(return_key_and_category=True), tags) self.assertEqual(entry.tags.all(return_key_and_category=True), tags)
@ -137,21 +143,28 @@ class TestCreateMessage(EvenniaTest):
def test_create_msg__channel(self): def test_create_msg__channel(self):
chan1 = create.create_channel("DummyChannel1") chan1 = create.create_channel("DummyChannel1")
chan2 = create.create_channel("DummyChannel2") chan2 = create.create_channel("DummyChannel2")
msg = create.create_message(self.char1, self.msgtext, channels=[chan1, chan2], header="TestHeader") msg = create.create_message(
self.char1, self.msgtext, channels=[chan1, chan2], header="TestHeader"
)
self.assertEqual(list(msg.channels), [chan1, chan2]) self.assertEqual(list(msg.channels), [chan1, chan2])
def test_create_msg__custom(self): def test_create_msg__custom(self):
locks = "foo:false();bar:true()" locks = "foo:false();bar:true()"
tags = ["tag1", "tag2", "tag3"] tags = ["tag1", "tag2", "tag3"]
msg = create.create_message(self.char1, self.msgtext, header="TestHeader", msg = create.create_message(
receivers=[self.char1, self.char2], locks=locks, tags=tags) self.char1,
self.msgtext,
header="TestHeader",
receivers=[self.char1, self.char2],
locks=locks,
tags=tags,
)
self.assertEqual(msg.receivers, [self.char1, self.char2]) self.assertEqual(msg.receivers, [self.char1, self.char2])
self.assertTrue(all(lock in msg.locks.all() for lock in locks.split(";"))) self.assertTrue(all(lock in msg.locks.all() for lock in locks.split(";")))
self.assertEqual(msg.tags.all(), tags) self.assertEqual(msg.tags.all(), tags)
class TestCreateChannel(TestCase): class TestCreateChannel(TestCase):
def test_create_channel__simple(self): def test_create_channel__simple(self):
chan = create.create_channel("TestChannel1", desc="Testing channel") chan = create.create_channel("TestChannel1", desc="Testing channel")
self.assertEqual(chan.key, "TestChannel1") self.assertEqual(chan.key, "TestChannel1")
@ -160,10 +173,11 @@ class TestCreateChannel(TestCase):
def test_create_channel__complex(self): def test_create_channel__complex(self):
locks = "foo:false();bar:true()" locks = "foo:false();bar:true()"
tags = ["tag1", "tag2", "tag3"] tags = ["tag1", "tag2", "tag3"]
aliases = ['foo', 'bar', 'tst'] aliases = ["foo", "bar", "tst"]
chan = create.create_channel("TestChannel2", desc="Testing channel", chan = create.create_channel(
aliases=aliases, locks=locks, tags=tags) "TestChannel2", desc="Testing channel", aliases=aliases, locks=locks, tags=tags
)
self.assertTrue(all(lock in chan.locks.all() for lock in locks.split(";"))) self.assertTrue(all(lock in chan.locks.all() for lock in locks.split(";")))
self.assertEqual(chan.tags.all(), tags) self.assertEqual(chan.tags.all(), tags)
self.assertEqual(list(chan.aliases.all()).sort(), aliases.sort()) self.assertEqual(list(chan.aliases.all()).sort(), aliases.sort())

View file

@ -11,8 +11,9 @@ class TestDbSerialize(TestCase):
""" """
Database serialization operations. Database serialization operations.
""" """
def setUp(self): def setUp(self):
self.obj = DefaultObject(db_key="Tester", ) self.obj = DefaultObject(db_key="Tester",)
self.obj.save() self.obj.save()
def test_constants(self): def test_constants(self):
@ -54,5 +55,5 @@ class TestDbSerialize(TestCase):
self.assertEqual(self.obj.db.test, [[1, 2, 3], [4, 5, 6]]) self.assertEqual(self.obj.db.test, [[1, 2, 3], [4, 5, 6]])
self.obj.db.test = [{1: 0}, {0: 1}] self.obj.db.test = [{1: 0}, {0: 1}]
self.assertEqual(self.obj.db.test, [{1: 0}, {0: 1}]) self.assertEqual(self.obj.db.test, [{1: 0}, {0: 1}])
self.obj.db.test.sort(key=lambda d: str(d)) self.obj.db.test.sort(key=lambda d: str(d))
self.assertEqual(self.obj.db.test, [{0: 1}, {1: 0}]) self.assertEqual(self.obj.db.test, [{0: 1}, {1: 0}])

View file

@ -390,6 +390,7 @@ def iter_to_string(initer, endsep="and", addquote=False):
return str(initer[0]) return str(initer[0])
return ", ".join(str(v) for v in initer[:-1]) + "%s %s" % (endsep, initer[-1]) return ", ".join(str(v) for v in initer[:-1]) + "%s %s" % (endsep, initer[-1])
# legacy alias # legacy alias
list_to_string = iter_to_string list_to_string = iter_to_string
@ -1862,8 +1863,6 @@ def display_len(target):
return len(target) return len(target)
# ------------------------------------------------------------------- # -------------------------------------------------------------------
# Search handler function # Search handler function
# ------------------------------------------------------------------- # -------------------------------------------------------------------
@ -1911,7 +1910,9 @@ def at_search_result(matches, caller, query="", quiet=False, **kwargs):
if multimatch_string: if multimatch_string:
error = "%s\n" % multimatch_string error = "%s\n" % multimatch_string
else: else:
error = _("More than one match for '{query}' (please narrow target):\n").format(query=query) error = _("More than one match for '{query}' (please narrow target):\n").format(
query=query
)
for num, result in enumerate(matches): for num, result in enumerate(matches):
# we need to consider Commands, where .aliases is a list # we need to consider Commands, where .aliases is a list

View file

@ -65,7 +65,11 @@ def datetime(entry, option_key="Datetime", account=None, from_tz=None, **kwargs)
try: try:
from_tz = _pytz.timezone(acct_tz) from_tz = _pytz.timezone(acct_tz)
except Exception as err: except Exception as err:
raise ValueError(_("Timezone string '{acct_tz}' is not a valid timezone ({err})").format(acct_tz=acct_tz, err=err)) raise ValueError(
_("Timezone string '{acct_tz}' is not a valid timezone ({err})").format(
acct_tz=acct_tz, err=err
)
)
else: else:
from_tz = _pytz.UTC from_tz = _pytz.UTC

22
pyproject.toml Normal file
View file

@ -0,0 +1,22 @@
[tool.black]
line-length = 100
target-version = ['py37', 'py38']
exclude = '''
(
/(
\.eggs # exclude a few common directories in the
| \.git # root of the project
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
)/
| migrations
)
'''