Deprecate to_str, to_unicode. Fix class_from_module, is_iter, make_iter.

This commit is contained in:
Ryan Stein 2017-10-29 23:29:21 -04:00
parent 5074c112af
commit 93475a6de5

View file

@ -6,8 +6,6 @@ They provide some useful string and conversion methods that might
be of use when designing your own game. be of use when designing your own game.
""" """
from builtins import object, range
from future.utils import viewkeys, raise_ from future.utils import viewkeys, raise_
import os import os
@ -20,6 +18,7 @@ import textwrap
import random import random
from os.path import join as osjoin from os.path import join as osjoin
from importlib import import_module from importlib import import_module
from importlib.util import find_spec, module_from_spec
from inspect import ismodule, trace, getmembers, getmodule from inspect import ismodule, trace, getmembers, getmodule
from collections import defaultdict, OrderedDict from collections import defaultdict, OrderedDict
from twisted.internet import threads, reactor, task from twisted.internet import threads, reactor, task
@ -32,10 +31,7 @@ _MULTIMATCH_TEMPLATE = settings.SEARCH_MULTIMATCH_TEMPLATE
_EVENNIA_DIR = settings.EVENNIA_DIR _EVENNIA_DIR = settings.EVENNIA_DIR
_GAME_DIR = settings.GAME_DIR _GAME_DIR = settings.GAME_DIR
try: import pickle
import pickle as pickle
except ImportError:
import pickle
ENCODINGS = settings.ENCODINGS ENCODINGS = settings.ENCODINGS
_GA = object.__getattribute__ _GA = object.__getattribute__
@ -45,15 +41,15 @@ _DA = object.__delattr__
_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH _DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
def is_iter(iterable): def is_iter(obj):
""" """
Checks if an object behaves iterably. Checks if an object behaves iterably.
Args: Args:
iterable (any): Entity to check for iterability. obj (any): Entity to check for iterability.
Returns: Returns:
is_iterable (bool): If `iterable` is iterable or not. is_iterable (bool): If `obj` is iterable or not.
Notes: Notes:
Strings are *not* accepted as iterable (although they are Strings are *not* accepted as iterable (although they are
@ -61,7 +57,13 @@ def is_iter(iterable):
what we want to do with a string. what we want to do with a string.
""" """
return hasattr(iterable, '__iter__') if isinstance(obj, (str, bytes, )):
return False
try:
return iter(obj) and True
except TypeError:
return False
def make_iter(obj): def make_iter(obj):
@ -76,7 +78,7 @@ def make_iter(obj):
passed-through or made iterable. passed-through or made iterable.
""" """
return not hasattr(obj, '__iter__') and [obj] or obj return not is_iter(obj) and [obj] or obj
def wrap(text, width=_DEFAULT_WIDTH, indent=0): def wrap(text, width=_DEFAULT_WIDTH, indent=0):
@ -599,7 +601,7 @@ def dbref(inp, reqhash=True):
inp.startswith("#") and inp.startswith("#") and
inp.lstrip('#').isdigit()) inp.lstrip('#').isdigit())
else None) else None)
return num if num > 0 else None return num if isinstance(num, int) and num > 0 else None
elif isinstance(inp, str): elif isinstance(inp, str):
inp = inp.lstrip('#') inp = inp.lstrip('#')
return int(inp) if inp.isdigit() and int(inp) > 0 else None return int(inp) if inp.isdigit() and int(inp) > 0 else None
@ -702,6 +704,10 @@ def latinify(unicode_string, default='?', pure_ascii=False):
def to_unicode(obj, encoding='utf-8', force_string=False): def to_unicode(obj, encoding='utf-8', force_string=False):
""" """
This function is deprecated in the Python 3 version of Evennia and is
likely to be phased out in future releases.
---
This decodes a suitable object to the unicode format. This decodes a suitable object to the unicode format.
Args: Args:
@ -723,35 +729,23 @@ def to_unicode(obj, encoding='utf-8', force_string=False):
""" """
if force_string and not isinstance(obj, str): if isinstance(obj, (str, bytes, )):
return obj
if force_string:
# some sort of other object. Try to # some sort of other object. Try to
# convert it to a string representation. # convert it to a string representation.
if hasattr(obj, '__str__'): obj = str(obj)
obj = obj.__str__()
elif hasattr(obj, '__unicode__'):
obj = obj.__unicode__()
else:
# last resort
obj = str(obj)
if isinstance(obj, str) and not isinstance(obj, str):
try:
obj = str(obj, encoding)
return obj
except UnicodeDecodeError:
for alt_encoding in ENCODINGS:
try:
obj = str(obj, alt_encoding)
return obj
except UnicodeDecodeError:
# if we still have an error, give up
pass
raise Exception("Error: '%s' contains invalid character(s) not in %s." % (obj, encoding))
return obj return obj
def to_str(obj, encoding='utf-8', force_string=False): def to_str(obj, encoding='utf-8', force_string=False):
""" """
This function is deprecated in the Python 3 version of Evennia and is
likely to be phased out in future releases.
---
This encodes a unicode string back to byte-representation, This encodes a unicode string back to byte-representation,
for printing, writing to disk etc. for printing, writing to disk etc.
@ -768,32 +762,14 @@ def to_str(obj, encoding='utf-8', force_string=False):
conversion of objects to strings. conversion of objects to strings.
""" """
if force_string and not isinstance(obj, str): if isinstance(obj, (str, bytes, )):
return obj
if force_string:
# some sort of other object. Try to # some sort of other object. Try to
# convert it to a string representation. # convert it to a string representation.
try: obj = str(obj)
obj = str(obj)
except Exception:
obj = str(obj)
if isinstance(obj, str) and isinstance(obj, str):
try:
obj = obj.encode(encoding)
return obj
except UnicodeEncodeError:
for alt_encoding in ENCODINGS:
try:
obj = obj.encode(alt_encoding)
return obj
except UnicodeEncodeError:
# if we still have an error, give up
pass
# if we get to this point we have not found any way to convert this string. Try to parse it manually,
try:
return latinify(obj, '?')
except Exception as err:
raise Exception("%s, Error: Unicode could not encode unicode string '%s'(%s) to a bytestring. " % (err, obj, encoding))
return obj return obj
@ -1355,7 +1331,7 @@ def class_from_module(path, defaultpaths=None):
Args: Args:
path (str): Full Python dot-path to module. path (str): Full Python dot-path to module.
defaultpaths (iterable, optional): If a direc import from `path` fails, defaultpaths (iterable, optional): If a direct import from `path` fails,
try subsequent imports by prepending those paths to `path`. try subsequent imports by prepending those paths to `path`.
Returns: Returns:
@ -1376,17 +1352,18 @@ def class_from_module(path, defaultpaths=None):
testpath, clsname = testpath.rsplit(".", 1) testpath, clsname = testpath.rsplit(".", 1)
else: else:
raise ImportError("the path '%s' is not on the form modulepath.Classname." % path) raise ImportError("the path '%s' is not on the form modulepath.Classname." % path)
try: try:
mod = import_module(testpath, package="evennia") if not find_spec(testpath, package='evennia'):
except ImportError:
if len(trace()) > 2:
# this means the error happened within the called module and
# we must not hide it.
exc = sys.exc_info()
raise_(exc[1], None, exc[2])
else:
# otherwise, try the next suggested path
continue continue
except ModuleNotFoundError:
continue
try:
mod = import_module(testpath, package='evennia')
except ModuleNotFoundError:
break
try: try:
cls = getattr(mod, clsname) cls = getattr(mod, clsname)
break break