好的,所以这个问题的一般背景是我试图创建一个自定义字典类,它将创建字典的字符串表示,它只是查找其中一个值(都是unicode)值)。在实际代码中,根据某些内部逻辑,其中一个键被选为查找的当前默认值,因此unicode(dict_obj)
将在字典中返回单个值,例如u'Some value'
或者如果当前默认密钥的值不存在:u'None'
此功能没有问题。真正的问题在于在zope页面模板的应用程序中使用它时,该模板将对象包装在安全代理中。代理对象的行为与原始对象的行为不同。
以下是自定义词典类的简化代码:
class IDefaultKeyDict(Interface):
def __unicode__():
"""Create a unicode representation of the dictionary."""
def __str__():
"""Create a string representation of the dictionary."""
class DefaultKeyDict(dict):
"""A custom dictionary for handling default values"""
implements(IDefaultKeyDict)
def __init__(self, default, *args, **kwargs):
super(DefaultKeyDict, self).__init__(*args, **kwargs)
self._default = default
def __unicode__(self):
print "In DefaultKeyDict.__unicode__"
key = self.get_current_default()
result = self.get(key)
return unicode(result)
def __str__(self):
print "In DefaultKeyDict.__str__"
return unicode(self).encode('utf-8')
def get_current_default(self):
return self._default
此类的关联zcml权限:
<class class=".utils.DefaultKeyDict">
<require
interface=".utils.IDefaultKeyDict"
permission="zope.View" />
</class>
我已经在__unicode__
和__str__
方法中留下了print语句,以显示代理对象的不同行为。因此,使用预定义的默认密钥创建一个虚拟字典类:
>>> dummy = DefaultKeyDict(u'key2', {u'key1': u'Normal ascii text', u'key2': u'Espa\xf1ol'})
>>> dummy
{u'key2': u'Espa\xf1ol', u'key1': u'Normal ascii text'}
>>> str(dummy)
In DefaultKeyDict.__str__
In DefaultKeyDict.__unicode__
'Espa\xc3\xb1ol'
>>> unicode(dummy)
In DefaultKeyDict.__unicode__
u'Espa\xf1ol'
>>> print dummy
In DefaultKeyDict.__str__
In DefaultKeyDict.__unicode__
Español
一切都按预期工作。现在,我可以将对象包装在zope.security
包中的安全代理中,并执行相同的测试以显示错误:
>>> from zope.security.checker import ProxyFactory
>>> prox = ProxyFactory(dummy)
>>> prox
{u'key2': u'Espa\xf1ol', u'key1': u'Normal ascii text'}
>>> type(prox)
<type 'zope.security._proxy._Proxy'>
>>> str(prox)
In DefaultKeyDict.__str__
In DefaultKeyDict.__unicode__
'Espa\xc3\xb1ol'
>>> unicode(prox)
In DefaultKeyDict.__str__
In DefaultKeyDict.__unicode__
*** UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 4: ordinal not in range(128)
正如您所看到的,如果代理对象包含任何特殊字符,则无法再调用代理对象unicode
。我可以看到来自zope.security
的代理对象主要是使用C代码定义的,我对C Python API并不熟悉,但似乎__str__
和__repr__
方法在C代码中定义,但不是__unicode__
。所以对我来说,似乎正在发生的事情是,当它试图创建这个代理对象的unicode表示时,不是直接调用__unicode__
方法,而是调用__str__
方法(就像你能做到的那样)从上面的最后几个打印语句中看到,它返回一个utf-8
编码的字节字符串,但然后将其转换为unicode(使用默认的ascii编码)。所以发生的事情似乎相当于:
>>> unicode(prox.__str__())
In DefaultKeyDict.__str__
In DefaultKeyDict.__unicode__
*** UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 4: ordinal not in range(128)
当然,在这种情况下会导致UnicodeDecodeError,尝试使用ascii解码utf-8
字符串。正如预期的那样,如果我可以指定utf-8
的编码,那么就不会有问题。
>>> unicode(prox.__str__(), encoding='utf-8')
In DefaultKeyDict.__str__
In DefaultKeyDict.__unicode__
u'Espa\xf1ol'
但我无法改变,因为我们正在谈论从所有类型的对象中创建unicode表示的zope.pagetemplate
和zope.tales
包,它们似乎总是在起作用使用安全代理对象(来自zope.security
)。另外值得注意的是,直接在对象上调用__unicode__
方法没有问题。
>>> prox.__unicode__()
In DefaultKeyDict.__unicode__
u'Espa\xf1ol'
所以真正的问题是unicode(prox)
调用了__str__
方法。我已经在这上面旋转了一段时间,并且不知道现在还有什么路要走。任何见解都会非常感激。
答案 0 :(得分:0)
根据您对定义__str__
和__repr__
方法而非__unicode__
方法的C API所说的话,我怀疑您使用的C库已被编写为兼容python 3.我不熟悉zope,但我相信这应该是这样的。
在Python 2中,对象模型指定 str ()和 unicode () 方法。如果存在这些方法,则必须返回str(bytes)和 unicode(文本)。
在Python 3中,只有 str (),它必须返回str(文本)。
我可能会略微忽略您的程序,但您真的需要定义__unicode__
方法吗?正如你所说,dict中的所有内容都属于unicode字符集。因此,调用__str__
方法会将其解码为utf-8,如果您想查看字符串的二进制文件,为什么不只是encode
呢?
请注意decode()
返回一个字符串对象,而encode()
返回一个字节对象。
如果可以的话,请发布编辑/评论,这样我就可以了解你想要做的更多。
答案 1 :(得分:0)
如果有人正在寻找这个问题的临时解决方案,我可以分享我们实施的monkeypatch修复程序。从zope.tal
和zope.tales
修补这两个方法似乎可以解决问题。只要您知道编码始终为utf-8
。
from zope.tal import talinterpreter
def do_insertStructure_tal(self, (expr, repldict, block)):
"""Patch for zope.security proxied I18NDicts.
The Proxy wrapper doesn't support a unicode hook for now. The only way to
fix this is to monkey patch this method which calls 'unicode'.
"""
structure = self.engine.evaluateStructure(expr)
if structure is None:
return
if structure is self.Default:
self.interpret(block)
return
if isinstance(structure, talinterpreter.I18nMessageTypes):
text = self.translate(structure)
else:
try:
text = unicode(structure)
except UnicodeDecodeError:
text = unicode(str(structure), encoding='utf-8')
if not (repldict or self.strictinsert):
# Take a shortcut, no error checking
self.stream_write(text)
return
if self.html:
self.insertHTMLStructure(text, repldict)
else:
self.insertXMLStructure(text, repldict)
talinterpreter.TALInterpreter.do_insertStructure_tal = do_insertStructure_tal
talinterpreter.TALInterpreter.bytecode_handlers_tal["insertStructure"] = \
do_insertStructure_tal
和这一个:
from zope.tales import tales
def evaluateText(self, expr):
"""Patch for zope.security proxied I18NDicts.
The Proxy wrapper doesn't support a unicode hook for now. The only way to
fix this is to monkey patch this method which calls 'unicode'.
"""
text = self.evaluate(expr)
if text is self.getDefault() or text is None:
return text
if isinstance(text, basestring):
# text could already be something text-ish, e.g. a Message object
return text
try:
return unicode(text)
except UnicodeDecodeError:
return unicode(str(text), encoding='utf-8')
tales.Context.evaluateText = evaluateText