Source code for evennia.contrib.utils.random_string_generator.random_string_generator

-"""
+r"""
 Pseudo-random generator and registry
 
 Evennia contribution - Vincent Le Goff 2017
diff --git a/docs/latest/_modules/evennia/objects/manager.html b/docs/latest/_modules/evennia/objects/manager.html
index 4ce010c6f9..db4e62b0c6 100644
--- a/docs/latest/_modules/evennia/objects/manager.html
+++ b/docs/latest/_modules/evennia/objects/manager.html
@@ -761,6 +761,11 @@
                     "or the setting is malformed."
                 )
 
+        # db_key has NOT NULL constraint, convert None to empty string.
+        # at_first_save() will convert empty string to #dbref
+        if key is None:
+            key = ""
+
         # create new instance
         new_object = typeclass(
             db_key=key,
diff --git a/docs/latest/_modules/evennia/prototypes/prototypes.html b/docs/latest/_modules/evennia/prototypes/prototypes.html
index 20727b801e..bdce9287fd 100644
--- a/docs/latest/_modules/evennia/prototypes/prototypes.html
+++ b/docs/latest/_modules/evennia/prototypes/prototypes.html
@@ -234,7 +234,7 @@
         "prototype-{}".format(hashlib.md5(bytes(str(time.time()), "utf-8")).hexdigest()[:7]),
     )
     homogenized["prototype_tags"] = homogenized.get("prototype_tags", [])
-    homogenized["prototype_locks"] = homogenized.get("prototype_lock", _PROTOTYPE_FALLBACK_LOCK)
+    homogenized["prototype_locks"] = homogenized.get("prototype_locks", _PROTOTYPE_FALLBACK_LOCK)
     homogenized["prototype_desc"] = homogenized.get("prototype_desc", "")
     if "typeclass" not in prototype and "prototype_parent" not in prototype:
         homogenized["typeclass"] = settings.BASE_OBJECT_TYPECLASS
diff --git a/docs/latest/_modules/evennia/prototypes/spawner.html b/docs/latest/_modules/evennia/prototypes/spawner.html
index b1a0567a32..f6d8e9456f 100644
--- a/docs/latest/_modules/evennia/prototypes/spawner.html
+++ b/docs/latest/_modules/evennia/prototypes/spawner.html
@@ -715,7 +715,8 @@
             if it's not set in the prototype. With `exact=True`, all un-specified properties of the
             objects will be removed if they exist. This will lead to a more accurate 1:1 correlation
             between the  object and the prototype but is usually impractical.
-        caller (Object or Account, optional): This may be used by protfuncs to do permission checks.
+        caller (Object or Account, optional): The object requesting the update. Required when using
+            protofuncs that perform searches. For example ($obj, $objlist, $dbref, $search).
         protfunc_raise_errors (bool): Have protfuncs raise explicit errors if malformed/not found.
             This is highly recommended.
     Returns:
@@ -866,7 +867,7 @@
             changed += 1
             obj.save()
             if spawn_hook := getattr(obj, "at_object_post_spawn", None):
-                spawn_hook(prototype=prototype)
+                spawn_hook(prototype=new_prototype)
 
     return changed
@@ -958,7 +959,8 @@ prototype_key (will be used to find the prototype) or a full prototype dictionary. These will be batched-spawned as one object each. Keyword Args: - caller (Object or Account, optional): This may be used by protfuncs to do access checks. + caller (Object or Account, optional): The object requesting the update. Required when using + protofuncs that perform searches. For example ($obj, $objlist, $dbref, $search). prototype_modules (str or list): A python-path to a prototype module, or a list of such paths. These will be used to build the global protparents dictionary accessible by the input diff --git a/docs/latest/_modules/evennia/server/evennia_launcher.html b/docs/latest/_modules/evennia/server/evennia_launcher.html index bae2ff9c6c..84992c32ac 100644 --- a/docs/latest/_modules/evennia/server/evennia_launcher.html +++ b/docs/latest/_modules/evennia/server/evennia_launcher.html @@ -59,7 +59,7 @@ import signal import sys from argparse import ArgumentParser -from distutils.version import LooseVersion +from packaging.version import Version from subprocess import STDOUT, CalledProcessError, Popen, call, check_output import django @@ -1372,9 +1372,9 @@ def _test_python_version(): """Test Python version""" python_version = ".".join(str(num) for num in sys.version_info if isinstance(num, int)) - python_curr = LooseVersion(python_version) - python_min = LooseVersion(PYTHON_MIN) - python_max = LooseVersion(PYTHON_MAX_TESTED) + python_curr = Version(python_version) + python_min = Version(PYTHON_MIN) + python_max = Version(PYTHON_MAX_TESTED) if python_curr < python_min: print(ERROR_PYTHON_VERSION.format(python_version=python_version, python_min=PYTHON_MIN)) @@ -1398,8 +1398,8 @@ return False else: twisted_version = twisted.version.short() - twisted_curr = LooseVersion(twisted_version) - twisted_min = LooseVersion(TWISTED_MIN) + twisted_curr = Version(twisted_version) + twisted_min = Version(TWISTED_MIN) if twisted_curr < twisted_min: print( @@ -1420,11 +1420,9 @@ return False else: django_version = ".".join(str(num) for num in django.VERSION if isinstance(num, int)) - # only the main version (1.5, not 1.5.4.0) - django_version = ".".join(django_version.split(".")[:2]) - django_curr = LooseVersion(django_version) - django_min = LooseVersion(DJANGO_MIN) - django_max = LooseVersion(DJANGO_MAX_TESTED) + django_curr = Version(django_version) + django_min = Version(DJANGO_MIN) + django_max = Version(DJANGO_MAX_TESTED) if django_curr < django_min: print( diff --git a/docs/latest/_modules/evennia/utils/ansi.html b/docs/latest/_modules/evennia/utils/ansi.html index ed3d8204c6..0e161713c2 100644 --- a/docs/latest/_modules/evennia/utils/ansi.html +++ b/docs/latest/_modules/evennia/utils/ansi.html @@ -784,12 +784,17 @@ def wrapped(self, *args, **kwargs): replacement_string = _query_super(func_name)(self, *args, **kwargs) + + # Convert to sets for O(1) membership testing + code_indexes_set = set(self._code_indexes) + char_indexes_set = set(self._char_indexes) + to_string = [] char_counter = 0 for index in range(0, len(self._raw_string)): - if index in self._code_indexes: + if index in code_indexes_set: to_string.append(self._raw_string[index]) - elif index in self._char_indexes: + elif index in char_indexes_set: to_string.append(replacement_string[char_counter]) char_counter += 1 return ANSIString( @@ -1120,10 +1125,12 @@ return ANSIString("") last_mark = slice_indexes[0] # Check between the slice intervals for escape sequences. + # Convert to set for O(1) membership testing + code_indexes_set = set(self._code_indexes) i = None for i in slice_indexes[1:]: for index in range(last_mark, i): - if index in self._code_indexes: + if index in code_indexes_set: string += self._raw_string[index] last_mark = i try: @@ -1157,15 +1164,24 @@ append_tail = self._get_interleving(item + 1) else: append_tail = "" - item = self._char_indexes[item] - clean = self._raw_string[item] - result = "" - # Get the character they're after, and replay all escape sequences - # previous to it. - for index in range(0, item + 1): - if index in self._code_indexes: - result += self._raw_string[index] + char_pos = self._char_indexes[item] + clean = self._raw_string[char_pos] + + code_indexes_set = set(self._code_indexes) + + # Only collect codes after the last reset to avoid accumulating + # cancelled codes when slicing + start_pos = self._find_last_reset_before(char_pos) + + result_chars = [ + self._raw_string[index] + for index in range(start_pos, char_pos + 1) + if index in code_indexes_set + ] + + result = "".join(result_chars) + return ANSIString(result + clean + append_tail, decoded=True)
@@ -1254,9 +1270,28 @@ # Plain string, no ANSI codes. return code_indexes, list(range(0, len(self._raw_string))) # all indexes not occupied by ansi codes are normal characters - char_indexes = [i for i in range(len(self._raw_string)) if i not in code_indexes] + code_indexes_set = set(code_indexes) + char_indexes = [i for i in range(len(self._raw_string)) if i not in code_indexes_set] + return code_indexes, char_indexes + def _find_last_reset_before(self, pos): + """ + Find the end position of the last ANSI reset sequence + that occurs before the given position. + + Args: + pos (int): Position in _raw_string to search before. + + Returns: + int: The index immediately after the last reset sequence, + or 0 if no reset was found before pos. + """ + reset_pos = self._raw_string.rfind(ANSI_NORMAL, 0, pos) + if reset_pos == -1: + return 0 + return reset_pos + len(ANSI_NORMAL) + def _get_interleving(self, index): """ Get the code characters from the given slice end to the next @@ -1267,12 +1302,17 @@ index = self._char_indexes[index - 1] except IndexError: return "" + + # Convert to sets for O(1) membership testing + char_indexes_set = set(self._char_indexes) + code_indexes_set = set(self._code_indexes) + s = "" while True: index += 1 - if index in self._char_indexes: + if index in char_indexes_set: break - elif index in self._code_indexes: + elif index in code_indexes_set: s += self._raw_string[index] else: break diff --git a/docs/latest/_modules/evennia/utils/batchprocessors.html b/docs/latest/_modules/evennia/utils/batchprocessors.html index 704fb47bcb..23d187a166 100644 --- a/docs/latest/_modules/evennia/utils/batchprocessors.html +++ b/docs/latest/_modules/evennia/utils/batchprocessors.html @@ -276,6 +276,9 @@ if not text and decoderr: raise UnicodeDecodeError("\n".join(decoderr), bytearray(), 0, 0, "") + if text: + text = text.replace("\r\n", "\n").replace("\r", "\n") + return text
diff --git a/docs/latest/_modules/evennia/utils/evmore.html b/docs/latest/_modules/evennia/utils/evmore.html index bf2b7ada50..82add2bbe2 100644 --- a/docs/latest/_modules/evennia/utils/evmore.html +++ b/docs/latest/_modules/evennia/utils/evmore.html @@ -170,7 +170,7 @@ more.page_quit() # re-fire the command (in new cmdset) - self.caller.execute_cmd(self.raw_string)