Fixed a lingering bug with @set that made it not work when assigning normal strings without quotes. Changed so that proper Python constructs (lists, dicts etc) now requires you to entering proper Python syntax (since this is parsed).

This commit is contained in:
Griatch 2012-04-15 23:09:56 +02:00
parent bcf214ee0d
commit 5a2228763f
3 changed files with 219 additions and 200 deletions

View file

@ -1153,10 +1153,19 @@ class CmdSetAttribute(ObjManipCommand):
Sets attributes on objects. The second form clears
a previously set attribute while the last form
inspects the current value of the attribute
(if any). You can also set lists [...] and dicts {...}
on attributes with @set (but not nested combinations). Also
note that such lists/dicts will always hold strings (never numbers).
Use @py if you need to set arbitrary lists and dicts.
(if any).
The most common data to save with this command are strings and
numbers. You can however also set Python primities such as lists,
dictionaries and tuples on objects (this might be important for
the functionality of certain custom objects). This is indicated
by you starting your value with one of {c'{n, {c"{n, {c({n, {c[{n or {c{ {n.
Note that you should leave a space after starting a dictionary ('{ ')
so as to not confuse the dictionary start with a colour code.
Remember that if you use Python primitives like this, you must
write proper Python syntax too - notably you must include quotes
around your strings or you will get an error.
"""
key = "@set"
@ -1201,14 +1210,17 @@ class CmdSetAttribute(ObjManipCommand):
for pair in obj[1:-1].split(',') if ":" in pair])
# if nothing matches, return as-is
return obj
if strobj.strip() and strobj.strip()[0] in ("'", '"', "(", "{ ", "["):
# this is a structure starting with a proper python structure,
# so treat it as such.
try:
# under python 2.6, literal_eval can do this for us.
from ast import literal_eval
return literal_eval(strobj)
except ImportError:
# fall back to old recursive solution (don't support nested lists/dicts)
return rec_convert(strobj.strip())
try:
# under python 2.6, literal_eval can do this for us.
from ast import literal_eval
return literal_eval(strobj)
except ImportError:
# fall back to old recursive solution (don't support nested lists/dicts)
return rec_convert(strobj.strip())
def func(self):
"Implement the set attribute - a limited form of @py."
@ -1253,8 +1265,15 @@ class CmdSetAttribute(ObjManipCommand):
else:
# setting attribute(s). Make sure to convert to real Python type before saving.
for attr in attrs:
obj.set_attribute(attr, self.convert_from_string(value))
string += "\nCreated attribute %s/%s = %s" % (obj.name, attr, value)
try:
obj.set_attribute(attr, self.convert_from_string(value))
string += "\nCreated attribute %s/%s = %s" % (obj.name, attr, value)
except SyntaxError:
# this means literal_eval tried to parse a faulty string
string = "{RPython syntax error in your value. By assigning a value starting with"
string += "\none of {r'{R, {r\"{R, {r[{R, {r({R or {r{{R we assume you are entering a proper Python"
string += "\nprimitive such as a list or a dictionary. You must then also use correct"
string += "\nPython syntax. Remember especially to put quotes around all strings.{n"
# send feedback
caller.msg(string.strip('\n'))

View file

@ -2,26 +2,26 @@
ANSI - Gives colour to text.
Use the codes defined in ANSIPARSER in your text
to apply colour to text according to the ANSI standard.
to apply colour to text according to the ANSI standard.
Examples:
Examples:
This is %crRed text%cn and this is normal again.
This is {rRed text{n and this is normal again.
Mostly you should not need to call parse_ansi() explicitly;
it is run by Evennia just before returning data to/from the
user.
user.
"""
import re
from src.utils import utils
# ANSI definitions
# ANSI definitions
ANSI_BEEP = "\07"
ANSI_ESCAPE = "\033"
ANSI_NORMAL = "\033[0m"
ANSI_UNDERLINE = "\033[4m"
ANSI_HILITE = "\033[1m"
ANSI_BLINK = "\033[5m"
@ -30,7 +30,7 @@ ANSI_INV_HILITE = "\033[1;7m"
ANSI_INV_BLINK = "\033[7;5m"
ANSI_BLINK_HILITE = "\033[1;5m"
ANSI_INV_BLINK_HILITE = "\033[1;5;7m"
# Foreground colors
ANSI_BLACK = "\033[30m"
ANSI_RED = "\033[31m"
@ -40,7 +40,7 @@ ANSI_BLUE = "\033[34m"
ANSI_MAGENTA = "\033[35m"
ANSI_CYAN = "\033[36m"
ANSI_WHITE = "\033[37m"
# Background colors
ANSI_BACK_BLACK = "\033[40m"
ANSI_BACK_RED = "\033[41m"
@ -50,7 +50,7 @@ ANSI_BACK_BLUE = "\033[44m"
ANSI_BACK_MAGENTA = "\033[45m"
ANSI_BACK_CYAN = "\033[46m"
ANSI_BACK_WHITE = "\033[47m"
# Formatting Characters
ANSI_RETURN = "\r\n"
ANSI_TAB = "\t"
@ -58,7 +58,7 @@ ANSI_SPACE = " "
class ANSIParser(object):
"""
A class that parses ansi markup
A class that parses ansi markup
to ANSI command sequences
"""
@ -110,14 +110,14 @@ class ANSIParser(object):
(r'{M', normal + ANSI_MAGENTA),
(r'{c', hilite + ANSI_CYAN),
(r'{C', normal + ANSI_CYAN),
(r'{w', hilite + ANSI_WHITE), # pure white
(r'{w', hilite + ANSI_WHITE), # pure white
(r'{W', normal + ANSI_WHITE), #light grey
(r'{x', hilite + ANSI_BLACK), #dark grey
(r'{X', normal + ANSI_BLACK), #pure black
(r'{n', normal) #reset
]
# xterm256 {123, %c134,
]
# xterm256 {123, %c134,
self.xterm256_map = [
(r'%c([1-5]{3})', self.parse_rgb), # %c123 - foreground colour
@ -125,10 +125,10 @@ class ANSIParser(object):
(r'{([1-5]{3})', self.parse_rgb), # {123 - foreground colour
(r'{(b[1-5]{3})', self.parse_rgb) # {b123 - background colour
]
# obs - order matters here, we want to do the xterms first since
# they collide with some of the other mappings otherwise.
self.ansi_map = self.xterm256_map + self.mux_ansi_map + self.ext_ansi_map
# obs - order matters here, we want to do the xterms first since
# they collide with some of the other mappings otherwise.
self.ansi_map = self.xterm256_map + self.mux_ansi_map + self.ext_ansi_map
# prepare regex matching
self.ansi_sub = [(re.compile(sub[0], re.DOTALL), sub[1])
@ -140,10 +140,10 @@ class ANSIParser(object):
def parse_rgb(self, rgbmatch):
"""
This is a replacer method called by re.sub with the matched
tag. It must return the correct ansi sequence.
tag. It must return the correct ansi sequence.
It checks self.do_xterm256 to determine if conversion
to standard ansi should be done or not.
to standard ansi should be done or not.
"""
if not rgbmatch:
return ""
@ -154,8 +154,8 @@ class ANSIParser(object):
red, green, blue = int(rgbtag[1]), int(rgbtag[2]), int(rgbtag[3])
else:
red, green, blue = int(rgbtag[0]), int(rgbtag[1]), int(rgbtag[2])
if self.do_xterm256:
if self.do_xterm256:
colval = 16 + (red * 36) + (green * 6) + blue
return "\033[%s8;5;%s%s%sm" % (3 + int(background), colval/100, (colval%100)/10, colval%10)
else:
@ -163,7 +163,7 @@ class ANSIParser(object):
if red == green and red == blue and red < 2:
if background: return ANSI_BACK_BLACK
elif red >= 1: return ANSI_HILITE + ANSI_BLACK
else: return ANSO_NORMAL + ANSI_BLACK
else: return ANSI_NORMAL + ANSI_BLACK
elif red == green and red == blue:
if background: return ANSI_BACK_WHITE
elif red >= 4: return ANSI_HILITE + ANSI_WHITE
@ -196,7 +196,7 @@ class ANSIParser(object):
def parse_ansi(self, string, strip_ansi=False, xterm256=False):
"""
Parses a string, subbing color codes according to
the stored mapping.
the stored mapping.
strip_ansi flag instead removes all ansi markup.
@ -206,16 +206,16 @@ class ANSIParser(object):
string = utils.to_str(string)
self.do_xterm256 = xterm256
# handle all subs
# handle all subs
for sub in self.ansi_sub:
# go through all available mappings and translate them
string = sub[0].sub(sub[1], string)
if strip_ansi:
# remove all ANSI escape codes
string = self.ansi_regex.sub("", string)
return string
return string
ANSI_PARSER = ANSIParser()
#