mirror of
https://github.com/evennia/evennia.git
synced 2026-03-29 20:17:16 +02:00
Many more tests, debugging of protfuncs/inlinefuncs
This commit is contained in:
parent
646b73e872
commit
721cdb5ae0
5 changed files with 267 additions and 92 deletions
|
|
@ -23,8 +23,8 @@ are specified as functions
|
|||
where *args are the arguments given in the prototype, and **kwargs are inserted by Evennia:
|
||||
|
||||
- session (Session): The Session of the entity spawning using this prototype.
|
||||
- prototype_key (str): The currently spawning prototype-key.
|
||||
- prototype (dict): The dict this protfunc is a part of.
|
||||
- current_key (str): The active key this value belongs to in the prototype.
|
||||
- testing (bool): This is set if this function is called as part of the prototype validation; if
|
||||
set, the protfunc should take care not to perform any persistent actions, such as operate on
|
||||
objects or add things to the database.
|
||||
|
|
@ -38,68 +38,10 @@ prototype key (this value must be possible to serialize in an Attribute).
|
|||
from ast import literal_eval
|
||||
from random import randint as base_randint, random as base_random
|
||||
|
||||
from django.conf import settings
|
||||
from evennia.utils import inlinefuncs
|
||||
from evennia.utils.utils import callables_from_module
|
||||
from evennia.utils.utils import justify as base_justify, is_iter
|
||||
from evennia.utils import search
|
||||
from evennia.utils.utils import justify as base_justify, is_iter, to_str
|
||||
|
||||
_PROTLIB = None
|
||||
_PROT_FUNCS = {}
|
||||
|
||||
for mod in settings.PROT_FUNC_MODULES:
|
||||
try:
|
||||
callables = callables_from_module(mod)
|
||||
if mod == __name__:
|
||||
callables.pop("protfunc_parser", None)
|
||||
_PROT_FUNCS.update(callables)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def protfunc_parser(value, available_functions=None, **kwargs):
|
||||
"""
|
||||
Parse a prototype value string for a protfunc and process it.
|
||||
|
||||
Available protfuncs are specified as callables in one of the modules of
|
||||
`settings.PROTFUNC_MODULES`, or specified on the command line.
|
||||
|
||||
Args:
|
||||
value (any): The value to test for a parseable protfunc. Only strings will be parsed for
|
||||
protfuncs, all other types are returned as-is.
|
||||
available_functions (dict, optional): Mapping of name:protfunction to use for this parsing.
|
||||
|
||||
Kwargs:
|
||||
any (any): Passed on to the inlinefunc.
|
||||
|
||||
Returns:
|
||||
any (any): A structure to replace the string on the prototype level. If this is a
|
||||
callable or a (callable, (args,)) structure, it will be executed as if one had supplied
|
||||
it to the prototype directly. This structure is also passed through literal_eval so one
|
||||
can get actual Python primitives out of it (not just strings). It will also identify
|
||||
eventual object #dbrefs in the output from the protfunc.
|
||||
|
||||
|
||||
"""
|
||||
global _PROTLIB
|
||||
if not _PROTLIB:
|
||||
from evennia.prototypes import prototypes as _PROTLIB
|
||||
|
||||
if not isinstance(value, basestring):
|
||||
return value
|
||||
available_functions = _PROT_FUNCS if available_functions is None else available_functions
|
||||
result = inlinefuncs.parse_inlinefunc(value, available_funcs=available_functions, **kwargs)
|
||||
# at this point we have a string where all procfuncs were parsed
|
||||
try:
|
||||
result = literal_eval(result)
|
||||
except ValueError:
|
||||
# this is due to the string not being valid for literal_eval - keep it a string
|
||||
pass
|
||||
|
||||
result = _PROTLIB.value_to_obj_or_any(result)
|
||||
try:
|
||||
return literal_eval(result)
|
||||
except ValueError:
|
||||
return result
|
||||
|
||||
|
||||
# default protfuncs
|
||||
|
|
@ -180,7 +122,7 @@ def protkey(*args, **kwargs):
|
|||
"""
|
||||
if args:
|
||||
prototype = kwargs['prototype']
|
||||
return prototype[args[0]]
|
||||
return prototype[args[0].strip()]
|
||||
|
||||
|
||||
def add(*args, **kwargs):
|
||||
|
|
@ -193,7 +135,16 @@ def add(*args, **kwargs):
|
|||
"""
|
||||
if len(args) > 1:
|
||||
val1, val2 = args[0], args[1]
|
||||
return literal_eval(val1) + literal_eval(val2)
|
||||
# try to convert to python structures, otherwise, keep as strings
|
||||
try:
|
||||
val1 = literal_eval(val1.strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
val2 = literal_eval(val2.strip())
|
||||
except Exception:
|
||||
pass
|
||||
return val1 + val2
|
||||
raise ValueError("$add requires two arguments.")
|
||||
|
||||
|
||||
|
|
@ -207,11 +158,20 @@ def sub(*args, **kwargs):
|
|||
"""
|
||||
if len(args) > 1:
|
||||
val1, val2 = args[0], args[1]
|
||||
return literal_eval(val1) - literal_eval(val2)
|
||||
# try to convert to python structures, otherwise, keep as strings
|
||||
try:
|
||||
val1 = literal_eval(val1.strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
val2 = literal_eval(val2.strip())
|
||||
except Exception:
|
||||
pass
|
||||
return val1 - val2
|
||||
raise ValueError("$sub requires two arguments.")
|
||||
|
||||
|
||||
def mul(*args, **kwargs):
|
||||
def mult(*args, **kwargs):
|
||||
"""
|
||||
Usage: $mul(val1, val2)
|
||||
Returns the value of val1 * val2. The values must be
|
||||
|
|
@ -221,7 +181,16 @@ def mul(*args, **kwargs):
|
|||
"""
|
||||
if len(args) > 1:
|
||||
val1, val2 = args[0], args[1]
|
||||
return literal_eval(val1) * literal_eval(val2)
|
||||
# try to convert to python structures, otherwise, keep as strings
|
||||
try:
|
||||
val1 = literal_eval(val1.strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
val2 = literal_eval(val2.strip())
|
||||
except Exception:
|
||||
pass
|
||||
return val1 * val2
|
||||
raise ValueError("$mul requires two arguments.")
|
||||
|
||||
|
||||
|
|
@ -234,10 +203,33 @@ def div(*args, **kwargs):
|
|||
"""
|
||||
if len(args) > 1:
|
||||
val1, val2 = args[0], args[1]
|
||||
return literal_eval(val1) / float(literal_eval(val2))
|
||||
# try to convert to python structures, otherwise, keep as strings
|
||||
try:
|
||||
val1 = literal_eval(val1.strip())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
val2 = literal_eval(val2.strip())
|
||||
except Exception:
|
||||
pass
|
||||
return val1 / float(val2)
|
||||
raise ValueError("$mult requires two arguments.")
|
||||
|
||||
|
||||
def toint(*args, **kwargs):
|
||||
"""
|
||||
Usage: $toint(<number>)
|
||||
Returns <number> as an integer.
|
||||
"""
|
||||
if args:
|
||||
val = args[0]
|
||||
try:
|
||||
return int(literal_eval(val.strip()))
|
||||
except ValueError:
|
||||
return val
|
||||
raise ValueError("$toint requires one argument.")
|
||||
|
||||
|
||||
def eval(*args, **kwargs):
|
||||
"""
|
||||
Usage $eval(<expression>)
|
||||
|
|
@ -247,16 +239,79 @@ def eval(*args, **kwargs):
|
|||
- those will then be evaluated *after* $eval.
|
||||
|
||||
"""
|
||||
string = args[0] if args else ''
|
||||
global _PROTLIB
|
||||
if not _PROTLIB:
|
||||
from evennia.prototypes import prototypes as _PROTLIB
|
||||
|
||||
string = ",".join(args)
|
||||
struct = literal_eval(string)
|
||||
|
||||
if isinstance(struct, basestring):
|
||||
# we must shield the string, otherwise it will be merged as a string and future
|
||||
# literal_evals will pick up e.g. '2' as something that should be converted to a number
|
||||
struct = '"{}"'.format(struct)
|
||||
|
||||
def _recursive_parse(val):
|
||||
# an extra round of recursive parsing, to catch any escaped $$profuncs
|
||||
# an extra round of recursive parsing after literal_eval, to catch any
|
||||
# escaped $$profuncs. This is commonly useful for object references.
|
||||
if is_iter(val):
|
||||
stype = type(val)
|
||||
if stype == dict:
|
||||
return {_recursive_parse(key): _recursive_parse(v) for key, v in val.items()}
|
||||
return stype((_recursive_parse(v) for v in val))
|
||||
return protfunc_parser(val)
|
||||
return _PROTLIB.protfunc_parser(val)
|
||||
|
||||
return _recursive_parse(struct)
|
||||
|
||||
|
||||
def _obj_search(return_list=False, *args, **kwargs):
|
||||
"Helper function to search for an object"
|
||||
|
||||
query = "".join(args)
|
||||
session = kwargs.get("session", None)
|
||||
|
||||
if not session:
|
||||
raise ValueError("$obj called by Evennia without Session. This is not supported.")
|
||||
account = session.account
|
||||
if not account:
|
||||
raise ValueError("$obj requires a logged-in account session.")
|
||||
targets = search.search_object(query)
|
||||
|
||||
if return_list:
|
||||
retlist = []
|
||||
for target in targets:
|
||||
if target.access(account, target, 'control'):
|
||||
retlist.append(target)
|
||||
return retlist
|
||||
else:
|
||||
# single-match
|
||||
if not targets:
|
||||
raise ValueError("$obj: Query '{}' gave no matches.".format(query))
|
||||
if targets.count() > 1:
|
||||
raise ValueError("$obj: Query '{query}' gave {nmatches} matches. Limit your "
|
||||
"query or use $objlist instead.".format(
|
||||
query=query, nmatches=targets.count()))
|
||||
target = target[0]
|
||||
if not target.access(account, target, 'control'):
|
||||
raise ValueError("$obj: Obj {target}(#{dbref} cannot be added - "
|
||||
"Account {account} does not have 'control' access.".format(
|
||||
target=target.key, dbref=target.id, account=account))
|
||||
return target
|
||||
|
||||
|
||||
def obj(*args, **kwargs):
|
||||
"""
|
||||
Usage $obj(<query>)
|
||||
Returns one Object searched globally by key, alias or #dbref. Error if more than one.
|
||||
|
||||
"""
|
||||
return _obj_search(*args, **kwargs)
|
||||
|
||||
|
||||
def objlist(*args, **kwargs):
|
||||
"""
|
||||
Usage $objlist(<query>)
|
||||
Returns list with one or more Objects searched globally by key, alias or #dbref.
|
||||
|
||||
"""
|
||||
return _obj_search(return_list=True, *args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -5,17 +5,17 @@ Handling storage of prototypes, both database-based ones (DBPrototypes) and thos
|
|||
|
||||
"""
|
||||
|
||||
from ast import literal_eval
|
||||
from django.conf import settings
|
||||
|
||||
from evennia.scripts.scripts import DefaultScript
|
||||
from evennia.objects.models import ObjectDB
|
||||
from evennia.utils.create import create_script
|
||||
from evennia.utils.utils import (
|
||||
all_from_module, make_iter, is_iter, dbid_to_obj)
|
||||
all_from_module, make_iter, is_iter, dbid_to_obj, callables_from_module)
|
||||
from evennia.locks.lockhandler import validate_lockstring, check_lockstring
|
||||
from evennia.utils import logger
|
||||
from evennia.utils import inlinefuncs
|
||||
from evennia.utils.evtable import EvTable
|
||||
from evennia.prototypes.protfuncs import protfunc_parser
|
||||
|
||||
|
||||
_MODULE_PROTOTYPE_MODULES = {}
|
||||
|
|
@ -23,6 +23,7 @@ _MODULE_PROTOTYPES = {}
|
|||
_PROTOTYPE_META_NAMES = ("prototype_key", "prototype_desc", "prototype_tags", "prototype_locks")
|
||||
_PROTOTYPE_TAG_CATEGORY = "from_prototype"
|
||||
_PROTOTYPE_TAG_META_CATEGORY = "db_prototype"
|
||||
_PROT_FUNCS = {}
|
||||
|
||||
|
||||
class PermissionError(RuntimeError):
|
||||
|
|
@ -36,6 +37,68 @@ class ValidationError(RuntimeError):
|
|||
pass
|
||||
|
||||
|
||||
# Protfunc parsing
|
||||
|
||||
for mod in settings.PROT_FUNC_MODULES:
|
||||
try:
|
||||
callables = callables_from_module(mod)
|
||||
_PROT_FUNCS.update(callables)
|
||||
except ImportError:
|
||||
logger.log_trace()
|
||||
raise
|
||||
|
||||
|
||||
def protfunc_parser(value, available_functions=None, testing=False, **kwargs):
|
||||
"""
|
||||
Parse a prototype value string for a protfunc and process it.
|
||||
|
||||
Available protfuncs are specified as callables in one of the modules of
|
||||
`settings.PROTFUNC_MODULES`, or specified on the command line.
|
||||
|
||||
Args:
|
||||
value (any): The value to test for a parseable protfunc. Only strings will be parsed for
|
||||
protfuncs, all other types are returned as-is.
|
||||
available_functions (dict, optional): Mapping of name:protfunction to use for this parsing.
|
||||
testing (bool, optional): Passed to protfunc. If in a testing mode, some protfuncs may
|
||||
behave differently.
|
||||
|
||||
Kwargs:
|
||||
session (Session): Passed to protfunc. Session of the entity spawning the prototype.
|
||||
protototype (dict): Passed to protfunc. The dict this protfunc is a part of.
|
||||
current_key(str): Passed to protfunc. The key in the prototype that will hold this value.
|
||||
any (any): Passed on to the protfunc.
|
||||
|
||||
Returns:
|
||||
testresult (tuple): If `testing` is set, returns a tuple (error, result) where error is
|
||||
either None or a string detailing the error from protfunc_parser or seen when trying to
|
||||
run `literal_eval` on the parsed string.
|
||||
any (any): A structure to replace the string on the prototype level. If this is a
|
||||
callable or a (callable, (args,)) structure, it will be executed as if one had supplied
|
||||
it to the prototype directly. This structure is also passed through literal_eval so one
|
||||
can get actual Python primitives out of it (not just strings). It will also identify
|
||||
eventual object #dbrefs in the output from the protfunc.
|
||||
|
||||
"""
|
||||
if not isinstance(value, basestring):
|
||||
return value
|
||||
available_functions = _PROT_FUNCS if available_functions is None else available_functions
|
||||
result = inlinefuncs.parse_inlinefunc(
|
||||
value, available_funcs=available_functions, testing=testing, **kwargs)
|
||||
# at this point we have a string where all procfuncs were parsed
|
||||
# print("parse_inlinefuncs(\"{}\", available_funcs={}) => {}".format(value, available_functions, result))
|
||||
result = value_to_obj_or_any(result)
|
||||
err = None
|
||||
try:
|
||||
result = literal_eval(result)
|
||||
except ValueError:
|
||||
pass
|
||||
except Exception as err:
|
||||
err = str(err)
|
||||
if testing:
|
||||
return err, result
|
||||
return result
|
||||
|
||||
|
||||
# helper functions
|
||||
|
||||
def value_to_obj(value, force=True):
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ class TestProtLib(EvenniaTest):
|
|||
pass
|
||||
|
||||
|
||||
@override_settings(PROT_FUNC_MODULES=['evennia.prototypes.protfuncs'])
|
||||
@override_settings(PROT_FUNC_MODULES=['evennia.prototypes.protfuncs'], CLIENT_DEFAULT_WIDTH=20)
|
||||
class TestProtFuncs(EvenniaTest):
|
||||
|
||||
def setUp(self):
|
||||
|
|
@ -176,11 +176,55 @@ class TestProtFuncs(EvenniaTest):
|
|||
"prototype_desc": "testing prot",
|
||||
"key": "ExampleObj"}
|
||||
|
||||
@mock.patch("random.random", new=mock.MagicMock(return_value=0.5))
|
||||
@mock.patch("random.randint", new=mock.MagicMock(return_value=5))
|
||||
@mock.patch("evennia.prototypes.protfuncs.base_random", new=mock.MagicMock(return_value=0.5))
|
||||
@mock.patch("evennia.prototypes.protfuncs.base_randint", new=mock.MagicMock(return_value=5))
|
||||
def test_protfuncs(self):
|
||||
self.assertEqual(protfuncs.protfunc_parser("$random()", 0.5))
|
||||
self.assertEqual(protfuncs.protfunc_parser("$randint(1, 10)", 5))
|
||||
self.assertEqual(protlib.protfunc_parser("$random()"), 0.5)
|
||||
self.assertEqual(protlib.protfunc_parser("$randint(1, 10)"), 5)
|
||||
self.assertEqual(protlib.protfunc_parser("$left_justify( foo )"), "foo ")
|
||||
self.assertEqual(protlib.protfunc_parser("$right_justify( foo )"), " foo")
|
||||
self.assertEqual(protlib.protfunc_parser("$center_justify(foo )"), " foo ")
|
||||
self.assertEqual(protlib.protfunc_parser(
|
||||
"$full_justify(foo bar moo too)"), 'foo bar moo too')
|
||||
self.assertEqual(
|
||||
protlib.protfunc_parser("$right_justify( foo )", testing=True),
|
||||
('unexpected indent (<unknown>, line 1)', ' foo'))
|
||||
|
||||
test_prot = {"key1": "value1",
|
||||
"key2": 2}
|
||||
|
||||
self.assertEqual(protlib.protfunc_parser(
|
||||
"$protkey(key1)", testing=True, prototype=test_prot), (None, "value1"))
|
||||
self.assertEqual(protlib.protfunc_parser(
|
||||
"$protkey(key2)", testing=True, prototype=test_prot), (None, 2))
|
||||
|
||||
self.assertEqual(protlib.protfunc_parser("$add(1, 2)"), 3)
|
||||
self.assertEqual(protlib.protfunc_parser("$add(10, 25)"), 35)
|
||||
self.assertEqual(protlib.protfunc_parser(
|
||||
"$add('''[1,2,3]''', '''[4,5,6]''')"), [1, 2, 3, 4, 5, 6])
|
||||
self.assertEqual(protlib.protfunc_parser("$add(foo, bar)"), "foo bar")
|
||||
|
||||
self.assertEqual(protlib.protfunc_parser("$sub(5, 2)"), 3)
|
||||
self.assertRaises(TypeError, protlib.protfunc_parser, "$sub(5, test)")
|
||||
|
||||
self.assertEqual(protlib.protfunc_parser("$mult(5, 2)"), 10)
|
||||
self.assertEqual(protlib.protfunc_parser("$mult( 5 , 10)"), 50)
|
||||
self.assertEqual(protlib.protfunc_parser("$mult('foo',3)"), "foofoofoo")
|
||||
self.assertEqual(protlib.protfunc_parser("$mult(foo,3)"), "foofoofoo")
|
||||
self.assertRaises(TypeError, protlib.protfunc_parser, "$mult(foo, foo)")
|
||||
|
||||
self.assertEqual(protlib.protfunc_parser("$toint(5.3)"), 5)
|
||||
|
||||
self.assertEqual(protlib.protfunc_parser("$div(5, 2)"), 2.5)
|
||||
self.assertEqual(protlib.protfunc_parser("$toint($div(5, 2))"), 2)
|
||||
self.assertEqual(protlib.protfunc_parser("$sub($add(5, 3), $add(10, 2))"), -4)
|
||||
|
||||
self.assertEqual(protlib.protfunc_parser("$eval('2')"), '2')
|
||||
|
||||
self.assertEqual(protlib.protfunc_parser(
|
||||
"$eval(['test', 1, '2', 3.5, \"foo\"])"), ['test', 1, '2', 3.5, 'foo'])
|
||||
self.assertEqual(protlib.protfunc_parser(
|
||||
"$eval({'test': '1', 2:3, 3: $toint(3.5)})"), {'test': '1', 2: 3, 3: 3})
|
||||
|
||||
|
||||
class TestPrototypeStorage(EvenniaTest):
|
||||
|
|
|
|||
|
|
@ -161,13 +161,15 @@ def clr(*args, **kwargs):
|
|||
def null(*args, **kwargs):
|
||||
return args[0] if args else ''
|
||||
|
||||
_INLINE_FUNCS = {}
|
||||
|
||||
# we specify a default nomatch function to use if no matching func was
|
||||
# found. This will be overloaded by any nomatch function defined in
|
||||
# the imported modules.
|
||||
_INLINE_FUNCS = {"nomatch": lambda *args, **kwargs: "<UKNOWN>",
|
||||
"stackfull": lambda *args, **kwargs: "\n (not parsed: "
|
||||
"inlinefunc stack size exceeded.)"}
|
||||
_DEFAULT_FUNCS = {"nomatch": lambda *args, **kwargs: "<UNKNOWN>",
|
||||
"stackfull": lambda *args, **kwargs: "\n (not parsed: "}
|
||||
|
||||
_INLINE_FUNCS.update(_DEFAULT_FUNCS)
|
||||
|
||||
# load custom inline func modules.
|
||||
for module in utils.make_iter(settings.INLINEFUNC_MODULES):
|
||||
|
|
@ -285,6 +287,11 @@ def parse_inlinefunc(string, strip=False, available_funcs=None, **kwargs):
|
|||
if not available_funcs:
|
||||
available_funcs = _INLINE_FUNCS
|
||||
usecache = True
|
||||
else:
|
||||
# make sure the default keys are available, but also allow overriding
|
||||
tmp = _DEFAULT_FUNCS.copy()
|
||||
tmp.update(available_funcs)
|
||||
available_funcs = tmp
|
||||
|
||||
if usecache and string in _PARSING_CACHE:
|
||||
# stack is already cached
|
||||
|
|
@ -299,9 +306,14 @@ def parse_inlinefunc(string, strip=False, available_funcs=None, **kwargs):
|
|||
# process string on stack
|
||||
ncallable = 0
|
||||
nlparens = 0
|
||||
|
||||
# print("STRING: {} =>".format(string))
|
||||
|
||||
for match in _RE_TOKEN.finditer(string):
|
||||
gdict = match.groupdict()
|
||||
# print("match: {}".format({key: val for key, val in gdict.items() if val}))
|
||||
|
||||
# print(" MATCH: {}".format({key: val for key, val in gdict.items() if val}))
|
||||
|
||||
if gdict["singlequote"]:
|
||||
stack.append(gdict["singlequote"])
|
||||
elif gdict["doublequote"]:
|
||||
|
|
@ -386,10 +398,10 @@ def parse_inlinefunc(string, strip=False, available_funcs=None, **kwargs):
|
|||
kwargs["inlinefunc_stack_depth"] = depth
|
||||
retval = "" if strip else func(*args, **kwargs)
|
||||
return utils.to_str(retval, force_string=True)
|
||||
|
||||
# print("STACK:\n{}".format(stack))
|
||||
retval = "".join(_run_stack(item) for item in stack)
|
||||
# print("STACK: \n{} => {}\n".format(stack, retval))
|
||||
# execute the stack
|
||||
return "".join(_run_stack(item) for item in stack)
|
||||
return retval
|
||||
|
||||
#
|
||||
# Nick templating
|
||||
|
|
|
|||
|
|
@ -43,8 +43,6 @@ _GA = object.__getattribute__
|
|||
_SA = object.__setattr__
|
||||
_DA = object.__delattr__
|
||||
|
||||
_DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH
|
||||
|
||||
|
||||
def is_iter(iterable):
|
||||
"""
|
||||
|
|
@ -80,7 +78,7 @@ def make_iter(obj):
|
|||
return not hasattr(obj, '__iter__') and [obj] or obj
|
||||
|
||||
|
||||
def wrap(text, width=_DEFAULT_WIDTH, indent=0):
|
||||
def wrap(text, width=None, indent=0):
|
||||
"""
|
||||
Safely wrap text to a certain number of characters.
|
||||
|
||||
|
|
@ -93,6 +91,7 @@ def wrap(text, width=_DEFAULT_WIDTH, indent=0):
|
|||
text (str): Properly wrapped text.
|
||||
|
||||
"""
|
||||
width = width if width else settings.CLIENT_DEFAULT_WIDTH
|
||||
if not text:
|
||||
return ""
|
||||
text = to_unicode(text)
|
||||
|
|
@ -104,7 +103,7 @@ def wrap(text, width=_DEFAULT_WIDTH, indent=0):
|
|||
fill = wrap
|
||||
|
||||
|
||||
def pad(text, width=_DEFAULT_WIDTH, align="c", fillchar=" "):
|
||||
def pad(text, width=None, align="c", fillchar=" "):
|
||||
"""
|
||||
Pads to a given width.
|
||||
|
||||
|
|
@ -119,6 +118,7 @@ def pad(text, width=_DEFAULT_WIDTH, align="c", fillchar=" "):
|
|||
text (str): The padded text.
|
||||
|
||||
"""
|
||||
width = width if width else settings.CLIENT_DEFAULT_WIDTH
|
||||
align = align if align in ('c', 'l', 'r') else 'c'
|
||||
fillchar = fillchar[0] if fillchar else " "
|
||||
if align == 'l':
|
||||
|
|
@ -129,7 +129,7 @@ def pad(text, width=_DEFAULT_WIDTH, align="c", fillchar=" "):
|
|||
return text.center(width, fillchar)
|
||||
|
||||
|
||||
def crop(text, width=_DEFAULT_WIDTH, suffix="[...]"):
|
||||
def crop(text, width=None, suffix="[...]"):
|
||||
"""
|
||||
Crop text to a certain width, throwing away text from too-long
|
||||
lines.
|
||||
|
|
@ -147,7 +147,7 @@ def crop(text, width=_DEFAULT_WIDTH, suffix="[...]"):
|
|||
text (str): The cropped text.
|
||||
|
||||
"""
|
||||
|
||||
width = width if width else settings.CLIENT_DEFAULT_WIDTH
|
||||
utext = to_unicode(text)
|
||||
ltext = len(utext)
|
||||
if ltext <= width:
|
||||
|
|
@ -179,7 +179,7 @@ def dedent(text):
|
|||
return textwrap.dedent(text)
|
||||
|
||||
|
||||
def justify(text, width=_DEFAULT_WIDTH, align="f", indent=0):
|
||||
def justify(text, width=None, align="f", indent=0):
|
||||
"""
|
||||
Fully justify a text so that it fits inside `width`. When using
|
||||
full justification (default) this will be done by padding between
|
||||
|
|
@ -198,6 +198,7 @@ def justify(text, width=_DEFAULT_WIDTH, align="f", indent=0):
|
|||
justified (str): The justified and indented block of text.
|
||||
|
||||
"""
|
||||
width = width if width else settings.CLIENT_DEFAULT_WIDTH
|
||||
|
||||
def _process_line(line):
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue