diff --git a/bin/project_rename.py b/bin/project_rename.py index f687a52484..6dc04069d4 100644 --- a/bin/project_rename.py +++ b/bin/project_rename.py @@ -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) diff --git a/docs/pylib/auto_link_remapper.py b/docs/pylib/auto_link_remapper.py index dbadb4e603..e817b1ed8e 100644 --- a/docs/pylib/auto_link_remapper.py +++ b/docs/pylib/auto_link_remapper.py @@ -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[\w -\[\]\`]+?)\]\((?P.+?)\)", re.I + re.S + re.U + re.M) + ref_regex = re.compile( + r"\[(?P[\w -\[\]\`]+?)\]\((?P.+?)\)", re.I + re.S + re.U + re.M + ) # in document references - ref_doc_regex = re.compile(r"\[(?P[\w -\`]+?)\]:\s+?(?P.+?)(?=$|\n)", re.I + re.S + re.U + re.M) + ref_doc_regex = re.compile( + r"\[(?P[\w -\`]+?)\]:\s+?(?P.+?)(?=$|\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() diff --git a/docs/pylib/build_search_index.py b/docs/pylib/build_search_index.py index 4c9497cf08..15d4d56b0a 100644 --- a/docs/pylib/build_search_index.py +++ b/docs/pylib/build_search_index.py @@ -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() diff --git a/docs/pylib/copy_from_wiki.py b/docs/pylib/copy_from_wiki.py index 151eb37b3f..8c5372bd25 100644 --- a/docs/pylib/copy_from_wiki.py +++ b/docs/pylib/copy_from_wiki.py @@ -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[\w -\[\]]+?)\]\((?P.+?)\)", 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) diff --git a/docs/source/conf.py b/docs/source/conf.py index dbf4a597b1..ead46f34a1 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -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#{1,2})\s*?(?P.*?)$", _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 diff --git a/evennia/accounts/accounts.py b/evennia/accounts/accounts.py index a894638dc2..f9c97a90f7 100644 --- a/evennia/accounts/accounts.py +++ b/evennia/accounts/accounts.py @@ -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") diff --git a/evennia/accounts/admin.py b/evennia/accounts/admin.py index 327d2b4eb2..a00169eb26 100644 --- a/evennia/accounts/admin.py +++ b/evennia/accounts/admin.py @@ -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, ) diff --git a/evennia/accounts/bots.py b/evennia/accounts/bots.py index 6c6a40d0db..420d96053d 100644 --- a/evennia/accounts/bots.py +++ b/evennia/accounts/bots.py @@ -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 diff --git a/evennia/accounts/models.py b/evennia/accounts/models.py index 18f2dd9bf0..734bab0276 100644 --- a/evennia/accounts/models.py +++ b/evennia/accounts/models.py @@ -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 diff --git a/evennia/commands/cmdhandler.py b/evennia/commands/cmdhandler.py index e0a3d86f87..dd0f84e872 100644 --- a/evennia/commands/cmdhandler.py +++ b/evennia/commands/cmdhandler.py @@ -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) diff --git a/evennia/commands/cmdsethandler.py b/evennia/commands/cmdsethandler.py index 4a746df33e..b2eb95c886 100644 --- a/evennia/commands/cmdsethandler.py +++ b/evennia/commands/cmdsethandler.py @@ -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 diff --git a/evennia/commands/default/account.py b/evennia/commands/default/account.py index c992d18a66..f013e79c60 100644 --- a/evennia/commands/default/account.py +++ b/evennia/commands/default/account.py @@ -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 diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index ca8800b3d8..87c6acd73b 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -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 diff --git a/evennia/commands/default/general.py b/evennia/commands/default/general.py index cfe7b8e098..12ec9acf62 100644 --- a/evennia/commands/default/general.py +++ b/evennia/commands/default/general.py @@ -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) diff --git a/evennia/commands/default/system.py b/evennia/commands/default/system.py index b6811cfb02..c121c27c4e 100644 --- a/evennia/commands/default/system.py +++ b/evennia/commands/default/system.py @@ -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, diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index 5fd1bb9149..bbf1e5428c 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -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(), "") diff --git a/evennia/contrib/puzzles.py b/evennia/contrib/puzzles.py index 924addfc32..6052b7d43f 100644 --- a/evennia/contrib/puzzles.py +++ b/evennia/contrib/puzzles.py @@ -611,7 +611,6 @@ class CmdUsePuzzleParts(MuxCommand): passed in. """ - key = "use" aliases = "combine" locks = "cmd:pperm(use) or pperm(Player)" diff --git a/evennia/help/manager.py b/evennia/help/manager.py index 646758d202..398312a203 100644 --- a/evennia/help/manager.py +++ b/evennia/help/manager.py @@ -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): diff --git a/evennia/locks/lockhandler.py b/evennia/locks/lockhandler.py index 19e0617bf0..25b2f2be45 100644 --- a/evennia/locks/lockhandler.py +++ b/evennia/locks/lockhandler.py @@ -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 diff --git a/evennia/objects/admin.py b/evennia/objects/admin.py index 5e18e8e0a4..59a1d85c68 100644 --- a/evennia/objects/admin.py +++ b/evennia/objects/admin.py @@ -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") diff --git a/evennia/objects/manager.py b/evennia/objects/manager.py index a3db831149..d29ca9a739 100644 --- a/evennia/objects/manager.py +++ b/evennia/objects/manager.py @@ -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): diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index 018ed61c8b..35ea3089e2 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -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): diff --git a/evennia/server/portal/ssh.py b/evennia/server/portal/ssh.py index 6d7f46eb20..0e89206410 100644 --- a/evennia/server/portal/ssh.py +++ b/evennia/server/portal/ssh.py @@ -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() diff --git a/evennia/server/portal/tests.py b/evennia/server/portal/tests.py index 8ba98732a3..6bd76db827 100644 --- a/evennia/server/portal/tests.py +++ b/evennia/server/portal/tests.py @@ -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"], {}])) diff --git a/evennia/server/server.py b/evennia/server/server.py index 880e9a1843..cec9d53f48 100644 --- a/evennia/server/server.py +++ b/evennia/server/server.py @@ -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 diff --git a/evennia/server/sessionhandler.py b/evennia/server/sessionhandler.py index ed7fb2b94b..0d099d6d30 100644 --- a/evennia/server/sessionhandler.py +++ b/evennia/server/sessionhandler.py @@ -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) diff --git a/evennia/server/tests/testrunner.py b/evennia/server/tests/testrunner.py index c2eb334a7d..cf940eaa22 100644 --- a/evennia/server/tests/testrunner.py +++ b/evennia/server/tests/testrunner.py @@ -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 ) - diff --git a/evennia/typeclasses/managers.py b/evennia/typeclasses/managers.py index 85f9a4b304..1227d9b4d5 100644 --- a/evennia/typeclasses/managers.py +++ b/evennia/typeclasses/managers.py @@ -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) diff --git a/evennia/typeclasses/models.py b/evennia/typeclasses/models.py index 3c78b0e803..8dddb5fedb 100644 --- a/evennia/typeclasses/models.py +++ b/evennia/typeclasses/models.py @@ -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 diff --git a/evennia/typeclasses/tests.py b/evennia/typeclasses/tests.py index 270a4f4525..44cdd8af6b 100644 --- a/evennia/typeclasses/tests.py +++ b/evennia/typeclasses/tests.py @@ -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) diff --git a/evennia/utils/create.py b/evennia/utils/create.py index 3fbc6e2ea9..888c24820f 100644 --- a/evennia/utils/create.py +++ b/evennia/utils/create.py @@ -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 diff --git a/evennia/utils/dbserialize.py b/evennia/utils/dbserialize.py index bca3021a6e..44f6dc409c 100644 --- a/evennia/utils/dbserialize.py +++ b/evennia/utils/dbserialize.py @@ -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)) diff --git a/evennia/utils/inlinefuncs.py b/evennia/utils/inlinefuncs.py index 7d3e517c50..834c8574e4 100644 --- a/evennia/utils/inlinefuncs.py +++ b/evennia/utils/inlinefuncs.py @@ -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 # diff --git a/evennia/utils/picklefield.py b/evennia/utils/picklefield.py index 2e62298ca5..33368e6c5a 100644 --- a/evennia/utils/picklefield.py +++ b/evennia/utils/picklefield.py @@ -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 diff --git a/evennia/utils/search.py b/evennia/utils/search.py index 45bdc86a87..c7231309ac 100644 --- a/evennia/utils/search.py +++ b/evennia/utils/search.py @@ -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 ) diff --git a/evennia/utils/test_resources.py b/evennia/utils/test_resources.py index f9809b8c7f..387f93577d 100644 --- a/evennia/utils/test_resources.py +++ b/evennia/utils/test_resources.py @@ -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 diff --git a/evennia/utils/tests/test_create_functions.py b/evennia/utils/tests/test_create_functions.py index f996880af3..392d62a9ee 100644 --- a/evennia/utils/tests/test_create_functions.py +++ b/evennia/utils/tests/test_create_functions.py @@ -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()) diff --git a/evennia/utils/tests/test_dbserialize.py b/evennia/utils/tests/test_dbserialize.py index 9d00abfae4..b9c2cc3d9b 100644 --- a/evennia/utils/tests/test_dbserialize.py +++ b/evennia/utils/tests/test_dbserialize.py @@ -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}]) diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index 8a4e205b6a..3127fc25a2 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -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 diff --git a/evennia/utils/validatorfuncs.py b/evennia/utils/validatorfuncs.py index 8d02525f0e..7d6f28cf39 100644 --- a/evennia/utils/validatorfuncs.py +++ b/evennia/utils/validatorfuncs.py @@ -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 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..f68f4353a7 --- /dev/null +++ b/pyproject.toml @@ -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 + + ) +'''