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

View file

@ -15,18 +15,25 @@ _IGNORE_FILES = []
_SOURCEDIR_NAME = "source"
_SOURCE_DIR = pathjoin(dirname(dirname(abspath(__file__))), _SOURCEDIR_NAME)
_TOC_FILE = pathjoin(_SOURCE_DIR, "toc.md")
_NO_REMAP_STARTSWITH = ["http://", "https://", "github:", "api:",
"feature-request", "report-bug", "issue", "bug-report"]
_NO_REMAP_STARTSWITH = [
"http://",
"https://",
"github:",
"api:",
"feature-request",
"report-bug",
"issue",
"bug-report",
]
TXT_REMAPS = {
}
URL_REMAPS = {
}
TXT_REMAPS = {}
URL_REMAPS = {}
_USED_REFS = {}
_CURRFILE = None
def auto_link_remapper():
"""
- 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
ind = pathparts[-5:].index(_SOURCEDIR_NAME)
# get the part after source/
pathparts = pathparts[-5 + 1 + ind:]
pathparts = pathparts[-5 + 1 + ind :]
url = "/".join(pathparts)
# get the reference, without .md
url = url.rsplit(".", 1)[0]
@ -71,7 +78,8 @@ def auto_link_remapper():
raise DocumentError(
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"
" name, even in different folders. Rename one.\n")
" name, even in different folders. Rename one.\n"
)
toc_map[fname] = src_url
# find relative links to all other files
@ -86,17 +94,20 @@ def auto_link_remapper():
url = "./" + url
docref_map[sourcepath][targetname] = url.rsplit(".", 1)[0]
# 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
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):
# inline reference links
global _USED_REFS
grpdict = match.groupdict()
txt, url = grpdict['txt'], grpdict['url']
txt, url = grpdict["txt"], grpdict["url"]
txt = TXT_REMAPS.get(txt, txt)
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]:
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:
print(f" {cfilename}: [{txt}]({url}) -> [{txt}]({urlout})")
else:
@ -129,7 +140,7 @@ def auto_link_remapper():
# reference links set at the bottom of the page
global _USED_REFS
grpdict = match.groupdict()
txt, url = grpdict['txt'], grpdict['url']
txt, url = grpdict["txt"], grpdict["url"]
txt = TXT_REMAPS.get(txt, txt)
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]:
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:
print(f" {cfilename}: [{txt}]: {url} -> [{txt}]: {urlout}")
else:
@ -165,12 +176,12 @@ def auto_link_remapper():
# from pudb import debugger;debugger.Debugger().set_trace()
_CURRFILE = path.as_posix()
with open(path, 'r') as fil:
with open(path, "r") as fil:
intxt = fil.read()
outtxt = ref_regex.sub(_sub, intxt)
outtxt = ref_doc_regex.sub(_sub_doc, outtxt)
if intxt != outtxt:
with open(path, 'w') as fil:
with open(path, "w") as fil:
fil.write(outtxt)
count += 1
print(f" -- Auto-relinked links in {path.name}")
@ -207,5 +218,6 @@ def auto_link_remapper():
print(" -- Auto-Remapper finished.")
if __name__ == "__main__":
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="")
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]
url = f"{URL_BASE}{sep}{filename}.html".strip()
title = filename.replace("-", " ").strip()
@ -61,16 +61,7 @@ def create_search_index(sourcedir, outfile):
idx = lunr(
ref="url",
documents=outlist,
fields=[
{
"field_name": "title",
"boost": 10
},
{
"field_name": "text",
"boost": 1
}
],
fields=[{"field_name": "title", "boost": 10}, {"field_name": "text", "boost": 1}],
)
with open(outfile, "w") as fil:
@ -83,10 +74,18 @@ if __name__ == "__main__":
parser = ArgumentParser(description="Build a static search index.")
parser.add_argument("-i", dest="sourcedir", 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.")
parser.add_argument(
"-i",
dest="sourcedir",
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()

View file

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

View file

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

View file

@ -508,8 +508,10 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
if banned:
# this is a banned IP or name!
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}).")
LOGIN_THROTTLE.update(ip, "Too many sightings of banned artifact.")
@ -673,7 +675,9 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
# Load the appropriate Character class
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)
# Create the character
@ -683,7 +687,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
ip=character_ip,
typeclass=character_typeclass,
permissions=character_permissions,
**kwargs
**kwargs,
)
if character:
# Update playable character list
@ -761,9 +765,9 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
banned = cls.is_banned(username=username, ip=ip)
if banned:
# this is a banned IP or name!
string = (
_("|rYou have been banned and cannot continue from here."
"\nIf you feel this ban is in error, please email an admin.|x")
string = _(
"|rYou have been banned and cannot continue from here."
"\nIf you feel this ban is in error, please email an admin.|x"
)
errors.append(string)
return None, errors
@ -778,7 +782,9 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
except Exception as e:
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()
return None, errors
@ -802,7 +808,9 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
if account and settings.MULTISESSION_MODE < 2:
# 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:
errors.extend(errs)
@ -990,9 +998,7 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
searchdata, categories=("account",), include_account=False
)
if search_object:
matches = ObjectDB.objects.object_search(
searchdata, typeclass=typeclass
)
matches = ObjectDB.objects.object_search(searchdata, typeclass=typeclass)
else:
matches = AccountDB.objects.account_search(searchdata, typeclass=typeclass)
@ -1340,7 +1346,9 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
"""
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):
"""
@ -1489,7 +1497,9 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
if is_su or len(characters) < charmax:
if not characters:
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:
result.append("\n |w@charcreate <name> [=description]|n - create new character")

View file

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

View file

@ -329,7 +329,9 @@ class IRCBot(Bot):
chstr = f"{self.db.irc_channel} ({self.db.irc_network}:{self.db.irc_port})"
nicklist = ", ".join(sorted(kwargs["nicklist"], key=lambda n: n.lower()))
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 = []
return
@ -338,7 +340,11 @@ class IRCBot(Bot):
if hasattr(self, "_ping_callers") and self._ping_callers:
chstr = f"{self.db.irc_channel} ({self.db.irc_network}:{self.db.irc_port})"
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 = []
return

View file

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

View file

@ -743,7 +743,9 @@ def cmdhandler(
sysarg = raw_string
else:
# 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(
raw_string,
cmdset.get_all_cmd_keys_and_aliases(caller),
@ -751,7 +753,9 @@ def cmdhandler(
maxnum=3,
)
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:
sysarg += _(' Type "help" for help.')
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)
else:
# try next suggested path
errstring += _("\n(Unsuccessfully tried '{path}').").format(path=python_path)
errstring += _("\n(Unsuccessfully tried '{path}').").format(
path=python_path
)
continue
try:
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()
raise exc.with_traceback(tb)
else:
errstring += _("\n(Unsuccessfully tried '{path}').").format(path=python_path)
errstring += _("\n(Unsuccessfully tried '{path}').").format(
path=python_path
)
continue
_CACHED_CMDSETS[python_path] = cmdsetclass

View file

@ -314,8 +314,12 @@ class CmdIC(COMMAND_DEFAULT_CLASS):
if account.db._playable_characters:
# look at the playable_characters list first
character_candidates.extend(
account.search(self.args, candidates=account.db._playable_characters,
search_object=True, quiet=True)
account.search(
self.args,
candidates=account.db._playable_characters,
search_object=True,
quiet=True,
)
)
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
# playable_characers in list and is not standing in a room
# with a matching char.
character_candidates.extend([
char for char in search.object_search(self.args) if char.access(account, "puppet")]
character_candidates.extend(
[
char
for char in search.object_search(self.args)
if char.access(account, "puppet")
]
)
# handle possible candidates

View file

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

View file

@ -431,7 +431,9 @@ class CmdGet(COMMAND_DEFAULT_CLASS):
caller.msg("This can't be picked up.")
else:
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
obj.at_get(caller)

View file

@ -446,7 +446,9 @@ def format_script_list(scripts):
table.add_row(
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.interval if script.interval > 0 else "--",
nextrep,

View file

@ -155,6 +155,7 @@ class CommandTest(EvenniaTest):
prt = ""
for ic, char in enumerate(msg):
import re
prt += char
sep1 = "\n" + "=" * 30 + "Wanted message" + "=" * 34 + "\n"
@ -369,11 +370,13 @@ class TestAccount(CommandTest):
def test_ic__nonaccess(self):
self.account.unpuppet_object(self.session)
self.call(
account.CmdIC(), "Nonexistent", "That is not a valid character choice.",
caller=self.account, receiver=self.account
account.CmdIC(),
"Nonexistent",
"That is not a valid character choice.",
caller=self.account,
receiver=self.account,
)
def test_password(self):
self.call(
account.CmdPassword(),
@ -485,7 +488,11 @@ class TestBuilding(CommandTest):
# escape inlinefuncs
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.call(building.CmdExamine(), "")

View file

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

View file

@ -131,7 +131,9 @@ class HelpEntryManager(TypedObjectManager):
for topic in topics:
topic.help_category = default_category
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)
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)
continue
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))
evalstring = evalstring.replace(funcstring, "%s")
if len(lock_funcs) < nfuncs:
@ -246,7 +252,11 @@ class LockHandler(object):
evalstring = " ".join(_RE_OK.findall(evalstring))
eval(evalstring % tuple(True for func in funclist), {}, {})
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
if access_type in locks:
duplicates += 1

View file

@ -10,6 +10,7 @@ from evennia.objects.models import ObjectDB
from django.contrib.admin.utils import flatten_fieldsets
from django.utils.translation import gettext as _
class ObjectAttributeInline(AttributeInline):
"""
Defines inline descriptions of Attributes (experimental)
@ -61,7 +62,7 @@ class ObjectCreateForm(forms.ModelForm):
required=False,
widget=forms.TextInput(attrs={"size": "78"}),
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")

View file

@ -175,15 +175,12 @@ class ObjectDBManager(TypedObjectManager):
)
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
results = (
self
.filter(
cand_restriction
& type_restriction
& Q(db_attributes__db_key=attribute_name)
& Q(db_attributes__db_value=attribute_value))
.order_by("id")
)
results = self.filter(
cand_restriction
& type_restriction
& Q(db_attributes__db_key=attribute_name)
& Q(db_attributes__db_value=attribute_value)
).order_by("id")
return results
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.
# Will be formatted with the appropriate attributes.
lockstring = ("puppet:id({character_id}) or pid({account_id}) or perm(Developer) or pperm(Developer);"
"delete:id({account_id}) or perm(Admin)")
lockstring = (
"puppet:id({character_id}) or pid({account_id}) or perm(Developer) or pperm(Developer);"
"delete:id({account_id}) or perm(Admin)"
)
@classmethod
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.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()
private_key_string = rsa_key.toString(type="OPENSSH").decode()

View file

@ -1,4 +1,3 @@
try:
from django.utils.unittest import TestCase
except ImportError:
@ -290,7 +289,7 @@ class TestWebSocket(EvenniaTest):
self.proto.sessionhandler = PORTAL_SESSIONS
self.proto.sessionhandler.portal = Mock()
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.setTcpKeepAlive = Mock()
self.proto.state = MagicMock()
@ -318,4 +317,4 @@ class TestWebSocket(EvenniaTest):
self.proto.sendLine = MagicMock()
msg = json.dumps(["logged_in", (), {}])
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:
# adjust the runtime not with 60s but with the actual elapsed time
# 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
# update game time and save it across reloads

View file

@ -221,9 +221,7 @@ class SessionHandler(dict):
elif isinstance(data, (str, bytes)):
data = _utf8(data)
if (_INLINEFUNC_ENABLED
and not raw
and isinstance(self, ServerSessionHandler)):
if _INLINEFUNC_ENABLED and not raw and isinstance(self, ServerSessionHandler):
# only parse inlinefuncs on the outgoing path (sessionhandler->)
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
# we'd get unclean reactor errors across test boundaries.
from evennia.server.portal.portal import PORTAL
PORTAL.maintenance_task.stop()
import evennia
@ -34,4 +35,3 @@ class EvenniaTestSuiteRunner(DiscoverRunner):
return super(EvenniaTestSuiteRunner, self).build_suite(
test_labels, extra_tests=extra_tests, **kwargs
)

View file

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

View file

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

View file

@ -43,10 +43,12 @@ class TestAttributes(EvenniaTest):
self.assertEqual(self.obj1.attributes.get(key), value)
def test_batch_add(self):
attrs = [("key1", "value1"),
("key2", "value2", "category2"),
("key3", "value3"),
("key4", "value4", "category4", "attrread:id(1)", False)]
attrs = [
("key1", "value1"),
("key2", "value2", "category2"),
("key3", "value3"),
("key4", "value4", "category4", "attrread:id(1)", False),
]
new_attrs = self.obj1.attributes.batch_add(*attrs)
attrobj = self.obj1.attributes.get(key="key4", category="category4", return_obj=True)
self.assertEqual(attrobj.value, "value4")
@ -69,16 +71,12 @@ class TestTypedObjectManager(EvenniaTest):
self.obj2.tags.add("tag4")
self.obj2.tags.add("tag2c")
self.assertEqual(self._manager("get_by_tag", "tag1"), [self.obj1])
self.assertEqual(
set(self._manager("get_by_tag", "tag2")), set([self.obj1, self.obj2])
)
self.assertEqual(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", "tag3 with spaces"), [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", "tag4", "tag2c"]), [self.obj2]
)
self.assertEqual(self._manager("get_by_tag", ["tag2a", "tag4", "tag2c"]), [self.obj2])
def test_get_by_tag_and_category(self):
self.obj1.tags.add("tag5", "category1")
@ -94,24 +92,17 @@ class TestTypedObjectManager(EvenniaTest):
self.obj1.tags.add("tag8", "category6")
self.obj2.tags.add("tag9", "category6")
self.assertEqual(
self._manager("get_by_tag", "tag5", "category1"), [self.obj1, self.obj2]
)
self.assertEqual(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", "category3"), [self.obj1, self.obj2]
)
self.assertEqual(self._manager("get_by_tag", "tag6", "category3"), [self.obj1, self.obj2])
self.assertEqual(
self._manager("get_by_tag", ["tag5", "tag6"], ["category1", "category3"]),
[self.obj1, self.obj2],
)
self.assertEqual(
self._manager("get_by_tag", ["tag5", "tag7"], "category1"),
[self.obj1, self.obj2],
)
self.assertEqual(
self._manager("get_by_tag", category="category1"), [self.obj1, self.obj2]
self._manager("get_by_tag", ["tag5", "tag7"], "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=["category1", "category3"]),
@ -121,49 +112,33 @@ class TestTypedObjectManager(EvenniaTest):
self._manager("get_by_tag", category=["category1", "category2"]),
[self.obj1, self.obj2],
)
self.assertEqual(
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=["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])
def test_get_tag_with_all(self):
self.obj1.tags.add("tagA", "categoryA")
self.assertEqual(
self._manager(
"get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="all"
),
self._manager("get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="all"),
[],
)
def test_get_tag_with_any(self):
self.obj1.tags.add("tagA", "categoryA")
self.assertEqual(
self._manager(
"get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="any"
),
self._manager("get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="any"),
[self.obj1],
)
def test_get_tag_withnomatch(self):
self.obj1.tags.add("tagC", "categoryC")
self.assertEqual(
self._manager(
"get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="any"
),
self._manager("get_by_tag", ["tagA", "tagB"], ["categoryA", "categoryB"], match="any"),
[],
)
def test_batch_add(self):
tags = ["tag1",
("tag2", "category2"),
"tag3",
("tag4", "category4", "data4")
]
tags = ["tag1", ("tag2", "category2"), "tag3", ("tag4", "category4", "data4")]
self.obj1.tags.batch_add(*tags)
self.assertEqual(self.obj1.tags.get("tag1"), "tag1")
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
def create_message(senderobj, message, channels=None, receivers=None,
locks=None, tags=None, header=None):
def create_message(
senderobj, message, channels=None, receivers=None, locks=None, tags=None, header=None
):
"""
Create a new communication Msg. Msgs represent a unit of
database-persistent communication between entites.
@ -424,7 +425,9 @@ message = 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
for distributing Msgs to groups of people without specifying the

View file

@ -235,7 +235,6 @@ class _SaverMutable(object):
def __gt__(self, other):
return self._data > other
@_save
def __setitem__(self, key, 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
def random(*args, **kwargs):
"""
Inlinefunc. Returns a random number between
@ -99,11 +100,11 @@ def random(*args, **kwargs):
nargs = len(args)
if nargs == 1:
# only maxval given
minval, maxval = '0', args[0]
minval, maxval = "0", args[0]
elif nargs > 1:
minval, maxval = args[:2]
else:
minval, maxval = ('0', '1')
minval, maxval = ("0", "1")
if "." in minval or "." in maxval:
# float mode
@ -518,10 +519,13 @@ def raw(string):
Args:
string (str): String with inlinefuncs to escape.
"""
def _escape(match):
return "\\" + match.group(0)
return _RE_STARTTOKEN.sub(_escape, string)
#
# 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)
if compress_object:
value = compress(value)
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)
def search_object_attribute(key=None, category=None, value=None,
strvalue=None, attrtype=None, **kwargs):
def search_object_attribute(
key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
):
return ObjectDB.objects.get_by_attribute(
key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs
)
def search_account_attribute(key=None, category=None, value=None,
strvalue=None, attrtype=None, **kwargs):
def search_account_attribute(
key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
):
return AccountDB.objects.get_by_attribute(
key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs
)
def search_script_attribute(key=None, category=None, value=None,
strvalue=None, attrtype=None, **kwargs):
def search_script_attribute(
key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
):
return ScriptDB.objects.get_by_attribute(
key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs
)
def search_channel_attribute(key=None, category=None, value=None,
strvalue=None, attrtype=None, **kwargs):
def search_channel_attribute(
key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
):
return Channel.objects.get_by_attribute(
key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs
)

View file

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

View file

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

View file

@ -11,8 +11,9 @@ class TestDbSerialize(TestCase):
"""
Database serialization operations.
"""
def setUp(self):
self.obj = DefaultObject(db_key="Tester", )
self.obj = DefaultObject(db_key="Tester",)
self.obj.save()
def test_constants(self):
@ -54,5 +55,5 @@ class TestDbSerialize(TestCase):
self.assertEqual(self.obj.db.test, [[1, 2, 3], [4, 5, 6]])
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}])

View file

@ -390,6 +390,7 @@ def iter_to_string(initer, endsep="and", addquote=False):
return str(initer[0])
return ", ".join(str(v) for v in initer[:-1]) + "%s %s" % (endsep, initer[-1])
# legacy alias
list_to_string = iter_to_string
@ -1862,8 +1863,6 @@ def display_len(target):
return len(target)
# -------------------------------------------------------------------
# Search handler function
# -------------------------------------------------------------------
@ -1911,7 +1910,9 @@ def at_search_result(matches, caller, query="", quiet=False, **kwargs):
if multimatch_string:
error = "%s\n" % multimatch_string
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):
# 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:
from_tz = _pytz.timezone(acct_tz)
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:
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
)
'''