1
0
mirror of https://github.com/djohnlewis/stackdump synced 2026-04-02 16:49:17 +00:00

Added PowerShell equivalents to launch and manage Stackdump on Windows.

This commit is contained in:
Samuel Lai
2013-11-28 21:53:45 +11:00
commit 2fea457b06
1152 changed files with 169042 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
from api import *
# @@ ianb 2005-05: should these be lazily loaded? Especially validators?
from schema import *
from compound import *
from foreach import *
import validators
from variabledecode import NestedVariables

View File

@@ -0,0 +1,494 @@
"""
Core classes for validation.
"""
import declarative
import gettext
import os
import re
import textwrap
try:
from pkg_resources import resource_filename
except ImportError:
resource_filename = None
__all__ = ['NoDefault', 'Invalid', 'Validator', 'Identity',
'FancyValidator', 'is_validator']
def get_localedir():
"""
Retrieve the location of locales.
If we're built as an egg, we need to find the resource within the egg.
Otherwise, we need to look for the locales on the filesystem or in the
system message catalog.
"""
locale_dir = ''
# Check the egg first
if resource_filename is not None:
try:
locale_dir = resource_filename(__name__, "/i18n")
except NotImplementedError:
# resource_filename doesn't work with non-egg zip files
pass
if not hasattr(os, 'access'):
# This happens on Google App Engine
return os.path.join(os.path.dirname(__file__), 'i18n')
if os.access(locale_dir, os.R_OK | os.X_OK):
# If the resource is present in the egg, use it
return locale_dir
# Otherwise, search the filesystem
locale_dir = os.path.join(os.path.dirname(__file__), 'i18n')
if not os.access(locale_dir, os.R_OK | os.X_OK):
# Fallback on the system catalog
locale_dir = os.path.normpath('/usr/share/locale')
return locale_dir
def set_stdtranslation(domain="FormEncode", languages=None,
localedir = get_localedir()):
t = gettext.translation(domain=domain,
languages=languages,
localedir=localedir, fallback=True)
global _stdtrans
_stdtrans = t.ugettext
set_stdtranslation()
# Dummy i18n translation function, nothing is translated here.
# Instead this is actually done in api.Validator.message.
# The surrounding _('string') of the strings is only for extracting
# the strings automatically.
# If you run pygettext with this source comment this function out temporarily.
_ = lambda s: s
class NoDefault(object):
"""A dummy value used for parameters with no default."""
def is_validator(obj):
return (isinstance(obj, Validator) or
(isinstance(obj, type) and issubclass(obj, Validator)))
class Invalid(Exception):
"""
This is raised in response to invalid input. It has several
public attributes:
msg:
The message, *without* values substituted. For instance, if
you want HTML quoting of values, you can apply that.
substituteArgs:
The arguments (a dictionary) to go with `msg`.
str(self):
The message describing the error, with values substituted.
value:
The offending (invalid) value.
state:
The state that went with this validator. This is an
application-specific object.
error_list:
If this was a compound validator that takes a repeating value,
and sub-validator(s) had errors, then this is a list of those
exceptions. The list will be the same length as the number of
values -- valid values will have None instead of an exception.
error_dict:
Like `error_list`, but for dictionary compound validators.
"""
def __init__(self, msg,
value, state, error_list=None, error_dict=None):
Exception.__init__(self, msg, value, state, error_list, error_dict)
self.msg = msg
self.value = value
self.state = state
self.error_list = error_list
self.error_dict = error_dict
assert (not self.error_list or not self.error_dict), (
"Errors shouldn't have both error dicts and lists "
"(error %s has %s and %s)"
% (self, self.error_list, self.error_dict))
def __str__(self):
val = self.msg
#if self.value:
# val += " (value: %s)" % repr(self.value)
return val
def __unicode__(self):
if isinstance(self.msg, unicode):
return self.msg
elif isinstance(self.msg, str):
return self.msg.decode('utf8')
else:
return unicode(self.msg)
def unpack_errors(self, encode_variables=False, dict_char='.',
list_char='-'):
"""
Returns the error as a simple data structure -- lists,
dictionaries, and strings.
If ``encode_variables`` is true, then this will return a flat
dictionary, encoded with variable_encode
"""
if self.error_list:
assert not encode_variables, (
"You can only encode dictionary errors")
assert not self.error_dict
result = []
for item in self.error_list:
if not item:
result.append(item)
else:
result.append(item.unpack_errors())
return result
elif self.error_dict:
result = {}
for name, item in self.error_dict.items():
if isinstance(item, (str, unicode)):
result[name] = item
else:
result[name] = item.unpack_errors()
if encode_variables:
import variabledecode
result = variabledecode.variable_encode(result, add_repetitions=False,
dict_char=dict_char,
list_char=list_char)
for key in result.keys():
if not result[key]:
del result[key]
return result
else:
assert not encode_variables, (
"You can only encode dictionary errors")
return self.msg
############################################################
## Base Classes
############################################################
class Validator(declarative.Declarative):
"""
The base class of most validators. See `IValidator` for more, and
`FancyValidator` for the more common (and more featureful) class.
"""
_messages = {}
if_missing = NoDefault
repeating = False
compound = False
gettextargs = {}
use_builtins_gettext = True # In case you don't want to use __builtins__._
# although it may be defined, set this to False
__singletonmethods__ = ('to_python', 'from_python', 'message', 'all_messages',
'subvalidators')
def __classinit__(cls, new_attrs):
if 'messages' in new_attrs:
cls._messages = cls._messages.copy()
cls._messages.update(cls.messages)
del cls.messages
cls._initialize_docstring()
def __init__(self, *args, **kw):
if 'messages' in kw:
self._messages = self._messages.copy()
self._messages.update(kw.pop('messages'))
declarative.Declarative.__init__(self, *args, **kw)
def to_python(self, value, state=None):
return value
def from_python(self, value, state=None):
return value
def message(self, msgName, state, **kw):
# determine translation function
try:
trans = state._
except AttributeError:
try:
if self.use_builtins_gettext:
import __builtin__
trans = __builtin__._
else:
trans = _stdtrans
except AttributeError:
trans = _stdtrans
if not callable(trans):
trans = _stdtrans
msg = self._messages[msgName]
msg = trans(msg, **self.gettextargs)
try:
return msg % kw
except KeyError, e:
raise KeyError(
"Key not found (%s) for %r=%r %% %r (from: %s)"
% (e, msgName, self._messages.get(msgName), kw,
', '.join(self._messages.keys())))
def all_messages(self):
"""
Return a dictionary of all the messages of this validator, and
any subvalidators if present. Keys are message names, values
may be a message or list of messages. This is really just
intended for documentation purposes, to show someone all the
messages that a validator or compound validator (like Schemas)
can produce.
@@: Should this produce a more structured set of messages, so
that messages could be unpacked into a rendered form to see
the placement of all the messages? Well, probably so.
"""
msgs = self._messages.copy()
for v in self.subvalidators():
inner = v.all_messages()
for key, msg in inner:
if key in msgs:
if msgs[key] == msg:
continue
if isinstance(msgs[key], list):
msgs[key].append(msg)
else:
msgs[key] = [msgs[key], msg]
else:
msgs[key] = msg
return msgs
def subvalidators(self):
"""
Return any validators that this validator contains. This is
not useful for functional, except to inspect what values are
available. Specifically the ``.all_messages()`` method uses
this to accumulate all possible messages.
"""
return []
def _initialize_docstring(cls):
"""
This changes the class's docstring to include information
about all the messages this validator uses.
"""
doc = cls.__doc__ or ''
doc = [textwrap.dedent(doc).rstrip()]
messages = cls._messages.items()
messages.sort()
doc.append('\n\n**Messages**\n\n')
for name, default in messages:
default = re.sub(r'(%\(.*?\)[rsifcx])', r'``\1``', default)
doc.append('``'+name+'``:\n')
doc.append(' '+default+'\n\n')
cls.__doc__ = ''.join(doc)
_initialize_docstring = classmethod(_initialize_docstring)
class _Identity(Validator):
def __repr__(self):
return 'validators.Identity'
Identity = _Identity()
class FancyValidator(Validator):
"""
FancyValidator is the (abstract) superclass for various validators
and converters. A subclass can validate, convert, or do both.
There is no formal distinction made here.
Validators have two important external methods:
* .to_python(value, state):
Attempts to convert the value. If there is a problem, or the
value is not valid, an Invalid exception is raised. The
argument for this exception is the (potentially HTML-formatted)
error message to give the user.
* .from_python(value, state):
Reverses to_python.
There are five important methods for subclasses to override,
however none of these *have* to be overridden, only the ones that
are appropriate for the validator:
* __init__():
if the `declarative.Declarative` model doesn't work for this.
* .validate_python(value, state):
This should raise an error if necessary. The value is a Python
object, either the result of to_python, or the input to
from_python.
* .validate_other(value, state):
Validates the source, before to_python, or after from_python.
It's more common to use `.validate_python()` however.
* ._to_python(value, state):
This returns the converted value, or raises an Invalid
exception if there is an error. The argument to this exception
should be the error message.
* ._from_python(value, state):
Should undo .to_python() in some reasonable way, returning
a string.
Validators should have no internal state besides the
values given at instantiation. They should be reusable and
reentrant.
All subclasses can take the arguments/instance variables:
* if_empty:
If set, then this value will be returned if the input evaluates
to false (empty list, empty string, None, etc), but not the 0 or
False objects. This only applies to ``.to_python()``.
* not_empty:
If true, then if an empty value is given raise an error.
(Both with ``.to_python()`` and also ``.from_python()``
if ``.validate_python`` is true).
* strip:
If true and the input is a string, strip it (occurs before empty
tests).
* if_invalid:
If set, then when this validator would raise Invalid during
``.to_python()``, instead return this value.
* if_invalid_python:
If set, when the Python value (converted with
``.from_python()``) is invalid, this value will be returned.
* accept_python:
If True (the default), then ``.validate_python()`` and
``.validate_other()`` will not be called when
``.from_python()`` is used.
"""
if_invalid = NoDefault
if_invalid_python = NoDefault
if_empty = NoDefault
not_empty = False
accept_python = True
strip = False
messages = {
'empty': _("Please enter a value"),
'badType': _("The input must be a string (not a %(type)s: %(value)r)"),
'noneType': _("The input must be a string (not None)"),
}
def to_python(self, value, state=None):
try:
if self.strip and isinstance(value, (str, unicode)):
value = value.strip()
elif hasattr(value, 'mixed'):
# Support Paste's MultiDict
value = value.mixed()
if self.is_empty(value):
if self.not_empty:
raise Invalid(self.message('empty', state), value, state)
else:
if self.if_empty is not NoDefault:
return self.if_empty
else:
return self.empty_value(value)
vo = self.validate_other
if vo and vo is not self._validate_noop:
vo(value, state)
tp = self._to_python
if tp:
value = tp(value, state)
vp = self.validate_python
if vp and vp is not self._validate_noop:
vp(value, state)
return value
except Invalid:
if self.if_invalid is NoDefault:
raise
else:
return self.if_invalid
def from_python(self, value, state=None):
try:
if self.strip and isinstance(value, (str, unicode)):
value = value.strip()
if not self.accept_python:
if self.is_empty(value):
if self.not_empty:
raise Invalid(self.message('empty', state),
value, state)
else:
return self.empty_value(value)
vp = self.validate_python
if vp and vp is not self._validate_noop:
vp(value, state)
fp = self._from_python
if fp:
value = fp(value, state)
vo = self.validate_other
if vo and vo is not self._validate_noop:
vo(value, state)
return value
else:
if self.is_empty(value):
return self.empty_value(value)
fp = self._from_python
if fp:
value = self._from_python(value, state)
return value
except Invalid:
if self.if_invalid_python is NoDefault:
raise
else:
return self.if_invalid_python
def is_empty(self, value):
# None and '' are "empty"
return value is None or value == '' or (
isinstance(value, (list, tuple, dict)) and not value)
def empty_value(self, value):
return None
def assert_string(self, value, state):
if not isinstance(value, (str, unicode)):
raise Invalid(self.message('badType', state,
type=type(value), value=value),
value, state)
def base64encode(self, value):
"""
Encode a string in base64, stripping whitespace and removing
newlines.
"""
return value.encode('base64').strip().replace('\n', '')
def _validate_noop(self, value, state):
"""
A validation method that doesn't do anything.
"""
pass
validate_python = validate_other = _validate_noop
_to_python = None
_from_python = None

View File

@@ -0,0 +1,221 @@
"""
Validators for applying validations in sequence.
"""
from api import *
# @@ ianb 2005-05: should CompoundValidator be included?
__all__ = ['Any', 'All', 'Pipe']
############################################################
## Compound Validators
############################################################
def to_python(validator, value, state):
return validator.to_python(value, state)
def from_python(validator, value, state):
return validator.from_python(value, state)
class CompoundValidator(FancyValidator):
if_invalid = NoDefault
validators = []
__unpackargs__ = ('*', 'validatorArgs')
__mutableattributes__ = ('validators',)
def __classinit__(cls, new_attrs):
toAdd = []
for name, value in new_attrs.items():
if name in ('view',):
continue
if is_validator(value) and value is not Identity:
toAdd.append((name, value))
# @@: Should we really delete too?
delattr(cls, name)
toAdd.sort()
cls.validators.extend([v for n, v in toAdd])
def __init__(self, *args, **kw):
Validator.__init__(self, *args, **kw)
self.validators = self.validators[:]
self.validators.extend(self.validatorArgs)
def _reprVars(names):
return [n for n in Validator._reprVars(names)
if n != 'validatorArgs']
_reprVars = staticmethod(_reprVars)
def attempt_convert(self, value, state, convertFunc):
raise NotImplementedError, "Subclasses must implement attempt_convert"
def _to_python(self, value, state=None):
return self.attempt_convert(value, state,
to_python)
def _from_python(self, value, state=None):
return self.attempt_convert(value, state,
from_python)
def subvalidators(self):
return self.validators
class Any(CompoundValidator):
"""
This class is like an 'or' operator for validators. The first
validator/converter that validates the value will be used. (You
can pass in lists of validators, which will be ANDed)
"""
def attempt_convert(self, value, state, validate):
lastException = None
if validate is to_python:
validators = self.validators[::-1]
else:
validators = self.validators
for validator in validators:
try:
return validate(validator, value, state)
except Invalid, e:
lastException = e
if self.if_invalid is NoDefault:
raise lastException
else:
return self.if_invalid
def not_empty__get(self):
not_empty = True
for validator in self.validators:
not_empty = not_empty and getattr(validator, 'not_empty', False)
return not_empty
not_empty = property(not_empty__get)
def is_empty(self, value):
# sub-validators should handle emptiness.
return False
class All(CompoundValidator):
"""
This class is like an 'and' operator for validators. All
validators must work, and the results are passed in turn through
all validators for conversion.
"""
def __repr__(self):
return '<All %s>' % self.validators
def attempt_convert(self, value, state, validate):
# To preserve the order of the transformations, we do them
# differently when we are converting to and from python.
if validate is to_python:
validators = list(self.validators)
validators.reverse()
else:
validators = self.validators
try:
for validator in validators:
value = validate(validator, value, state)
return value
except Invalid:
if self.if_invalid is NoDefault:
raise
return self.if_invalid
def with_validator(self, validator):
"""
Adds the validator (or list of validators) to a copy of
this validator.
"""
new = self.validators[:]
if isinstance(validator, list) or isinstance(validator, tuple):
new.extend(validator)
else:
new.append(validator)
return self.__class__(*new, **dict(if_invalid=self.if_invalid))
def join(cls, *validators):
"""
Joins several validators together as a single validator,
filtering out None and trying to keep `All` validators from
being nested (which isn't needed).
"""
validators = filter(lambda v: v and v is not Identity, validators)
if not validators:
return Identity
if len(validators) == 1:
return validators[0]
elif isinstance(validators[0], All):
return validators[0].with_validator(validators[1:])
else:
return cls(*validators)
join = classmethod(join)
def if_missing__get(self):
for validator in self.validators:
v = validator.if_missing
if v is not NoDefault:
return v
return NoDefault
if_missing = property(if_missing__get)
def not_empty__get(self):
not_empty = False
for validator in self.validators:
not_empty = not_empty or getattr(validator, 'not_empty', False)
return not_empty
not_empty = property(not_empty__get)
def is_empty(self, value):
# sub-validators should handle emptiness.
return False
class Pipe(All):
"""
This class works like 'All', all validators muss pass, but the result
of one validation pass is handled over to the next validator. A behaviour
known to Unix and GNU users as 'pipe'.
::
>>> from validators import DictConverter
>>> pv = Pipe(validators=[DictConverter({1: 2}), DictConverter({2: 3}), DictConverter({3: 4})])
>>> pv.to_python(1)
4
>>> pv.to_python(1)
4
>>> pv.from_python(4)
1
>>> pv.from_python(4)
1
>>> pv.to_python(1)
4
"""
def __repr__(self):
return '<Pipe %s>' % self.validators
def attempt_convert(self, value, state, validate):
# To preserve the order of the transformations, we do them
# differently when we are converting to and from Python.
if validate is from_python:
validators = list(self.validators)
validators.reverse()
else:
validators = self.validators
try:
for validator in validators:
value = validate(validator, value, state)
return value
except Invalid:
if self.if_invalid is NoDefault:
raise
return self.if_invalid

View File

@@ -0,0 +1,168 @@
"""
A dynamic-scope-like system, aka fluid variables.
The idea behind dynamic scoped variables is for when, at one level,
you want to change the behavior of something you call. Except you
can't pass in any new arguments (e.g., there's some function or object
inbetween you and the thing you want to change), or you can't predict
exactly what you will want to change.
You should use it like::
context = Context()
def do_stuff():
state = context.set(inside='do_stuff')
try:
do stuff...
finally:
state.restore()
Then ``context.inside`` will be set to ``'do_stuff'`` inside that try
block. If a value isn't set, you'll get an attribute error.
Note that all values are thread local; this means you cannot use a
context object to pass information to another thread. In a
single-thread environment it doesn't really matter.
Typically you will create ``Context`` instances for your application,
environment, etc. These should be global module-level variables, that
may be imported by any interested module; each instance is a namespace
of its own.
Sometimes it's nice to have default values, instead of getting
attribute errors. This makes it easier to put in new variables that
are intended to be used elsewhere, without having to use
``getattr(context, 'var', default)`` to avoid AttributeErrors.
There are two ways (that can be used together) to do this.
First, when instantiating a ``Context`` object, you can give it a
``default`` value. If given, then all variables will default to that
value. ``None`` is a typical value for that.
Another is ``context.set_default(**vars)``, which will set only those
variables to default values. This will not effect the stack of
scopes, but will only add defaults.
When Python 2.5 comes out, this syntax would certainly be useful::
with context(page='view'):
do stuff...
And ``page`` will be set to ``'view'`` only inside that ``with`` block.
"""
from itertools import count
from formencode.util import threadinglocal
__all__ = ['Context', 'ContextRestoreError']
_restore_ids = count()
class NoDefault(object):
"""A dummy value used for parameters with no default."""
class ContextRestoreError(Exception):
"""Raised when something is restored out-of-order."""
class Context(object):
def __init__(self, default=NoDefault):
self.__dict__['_local'] = threadinglocal.local()
self.__dict__['_default'] = default
def __getattr__(self, attr):
if attr.startswith('_'):
raise AttributeError
try:
stack = self._local.stack
except AttributeError:
stack = []
for i in range(len(stack)-1, -1, -1):
if attr in stack[i][0]:
return stack[i][0][attr]
if self._default is NoDefault:
raise AttributeError(
"The attribute %s has not been set on %r"
% (attr, self))
return self._default
def __setattr__(self, attr, value):
raise AttributeError(
"You can only write attribute on context object with the .set() method")
def set(self, **kw):
state_id = _restore_ids.next()
try:
stack = self._local.stack
except AttributeError:
stack = self._local.stack = [({}, -1)]
restorer = RestoreState(self, state_id)
stack.append((kw, state_id))
return restorer
def _restore(self, state_id):
try:
stack = self._local.stack
except AttributeError:
raise ContextRestoreError(
"Tried to restore context %r (to state ID %s) but no variables have been set in context"
% (self, state_id))
if stack[-1][1] == -1:
raise ContextRestoreError(
"Out of order restoration of context %r (to state ID %s); the stack state is empty"
% (self, state_id))
if stack[-1][1] != state_id:
raise ContextRestoreError(
"Out of order restoration of context %r (to state ID %s) when last state is %s"
% (self, state_id, stack[-1][1]))
stack.pop()
def set_default(self, **kw):
try:
stack = self._local.stack
except AttributeError:
stack = self._local.stack = [({}, -1)]
stack[0][0].update(kw)
def __repr__(self):
try:
stack = self._local.stack
except AttributeError:
stack = []
myid = hex(abs(id(self)))[2:]
if not stack:
return '<%s %s (empty)>' % (self.__class__.__name__, myid)
cur = {}
for vars, state_id in stack:
cur.update(vars)
keys = cur.keys()
keys.sort()
varlist = []
for key in keys:
rep = repr(cur[key])
if len(rep) > 10:
rep = rep[:9]+'...'+rep[-1]
varlist.append('%s=%s' % (key, rep))
return '<%s %s %s>' % (
self.__class__.__name__, myid, ' '.join(varlist))
class RestoreState(object):
def __init__(self, context, state_id):
self.state_id = state_id
self.context = context
self.restored = False
def restore(self):
if self.restored:
# @@: Should this really be allowed?
return
self.context._restore(self.state_id)
self.restored = True

View File

@@ -0,0 +1,232 @@
"""
Declarative objects for FormEncode.
Declarative objects have a simple protocol: you can use classes in
lieu of instances and they are equivalent, and any keyword arguments
you give to the constructor will override those instance variables.
(So if a class is received, we'll simply instantiate an instance with
no arguments).
You can provide a variable __unpackargs__ (a list of strings), and if
the constructor is called with non-keyword arguments they will be
interpreted as the given keyword arguments.
If __unpackargs__ is ('*', name), then all the arguments will be put
in a variable by that name.
Also, you can define a __classinit__(cls, new_attrs) method, which
will be called when the class is created (including subclasses).
"""
import copy
import new
from itertools import count
class classinstancemethod(object):
"""
Acts like a class method when called from a class, like an
instance method when called by an instance. The method should
take two arguments, 'self' and 'cls'; one of these will be None
depending on how the method was called.
"""
def __init__(self, func):
self.func = func
def __get__(self, obj, type=None):
return _methodwrapper(self.func, obj=obj, type=type)
class _methodwrapper(object):
def __init__(self, func, obj, type):
self.func = func
self.obj = obj
self.type = type
def __call__(self, *args, **kw):
assert 'self' not in kw and 'cls' not in kw, (
"You cannot use 'self' or 'cls' arguments to a "
"classinstancemethod")
return self.func(*((self.obj, self.type) + args), **kw)
def __repr__(self):
if self.obj is None:
return ('<bound class method %s.%s>'
% (self.type.__name__, self.func.func_name))
else:
return ('<bound method %s.%s of %r>'
% (self.type.__name__, self.func.func_name, self.obj))
class DeclarativeMeta(type):
def __new__(meta, class_name, bases, new_attrs):
cls = type.__new__(meta, class_name, bases, new_attrs)
for name in cls.__mutableattributes__:
setattr(cls, name, copy.copy(getattr(cls, name)))
cls.declarative_count = cls.counter.next()
if ('__classinit__' in new_attrs
and not isinstance(cls.__classinit__, staticmethod)):
setattr(cls, '__classinit__',
staticmethod(cls.__classinit__.im_func))
cls.__classinit__(cls, new_attrs)
names = getattr(cls, '__singletonmethods__', None)
if names:
for name in names:
meth = cls.__dict__.get(name)
if meth and not isinstance(meth, singletonmethod):
setattr(cls, name, singletonmethod(meth))
return cls
class singletonmethod(object):
"""
For Declarative subclasses, this decorator will call the method
on the cls.singleton() object if called as a class method (or
as normal if called as an instance method).
"""
def __init__(self, func):
self.func = func
def __get__(self, obj, type=None):
if obj is None:
obj = type.singleton()
if type is None:
type = obj.__class__
return new.instancemethod(self.func, obj, type)
class Declarative(object):
__unpackargs__ = ()
__mutableattributes__ = ()
__metaclass__ = DeclarativeMeta
__singletonmethods__ = ()
counter = count()
def __classinit__(cls, new_attrs):
pass
def __init__(self, *args, **kw):
if self.__unpackargs__ and self.__unpackargs__[0] == '*':
assert len(self.__unpackargs__) == 2, (
"When using __unpackargs__ = ('*', varname),"
" you must only provide a single variable name"
" (you gave %r)" % self.__unpackargs__)
name = self.__unpackargs__[1]
if name in kw:
raise TypeError(
"keyword parameter '%s' was given by position and name"
% name)
kw[name] = args
else:
if len(args) > len(self.__unpackargs__):
raise TypeError(
'%s() takes at most %i arguments (%i given)'
% (self.__class__.__name__,
len(self.__unpackargs__),
len(args)))
for name, arg in zip(self.__unpackargs__, args):
if name in kw:
raise TypeError(
"keyword parameter '%s' was given by position and name"
% name)
kw[name] = arg
for name in self.__mutableattributes__:
if name not in kw:
setattr(self, name, copy.copy(getattr(self, name)))
for name, value in kw.items():
setattr(self, name, value)
if 'declarative_count' not in kw:
self.declarative_count = self.counter.next()
self.__initargs__(kw)
def __initargs__(self, new_attrs):
pass
def __call__(self, *args, **kw):
current = self.__dict__.copy()
current.update(kw)
return self.__class__(*args, **current)
def singleton(cls):
name = '_%s__singleton' % cls.__name__
if not hasattr(cls, name):
setattr(cls, name, cls(declarative_count=cls.declarative_count))
return getattr(cls, name)
singleton = classmethod(singleton)
def __sourcerepr__(self, source, binding=None):
if binding and len(self.__dict__) > 3:
return self._source_repr_class(source, binding=binding)
else:
vals = self.__dict__.copy()
if 'declarative_count' in vals:
del vals['declarative_count']
args = []
if (self.__unpackargs__ and self.__unpackargs__[0] == '*'
and self.__unpackargs__[1] in vals):
v = vals[self.__unpackargs__[1]]
if isinstance(v, (list, int)):
args.extend(map(source.makeRepr, v))
del v[self.__unpackargs__[1]]
for name in self.__unpackargs__:
if name in vals:
args.append(source.makeRepr(vals[name]))
del vals[name]
else:
break
args.extend(['%s=%s' % (name, source.makeRepr(value))
for (name, value) in vals.items()])
return '%s(%s)' % (self.__class__.__name__,
', '.join(args))
def _source_repr_class(self, source, binding=None):
d = self.__dict__.copy()
if 'declarative_count' in d:
del d['declarative_count']
return source.makeClass(self, binding, d,
(self.__class__,))
def __classsourcerepr__(cls, source, binding=None):
d = cls.__dict__.copy()
del d['declarative_count']
return source.makeClass(cls, binding or cls.__name__, d,
cls.__bases__)
__classsourcerepr__ = classmethod(__classsourcerepr__)
def __repr__(self, cls):
if self:
name = '%s object' % self.__class__.__name__
v = self.__dict__.copy()
else:
name = '%s class' % cls.__name__
v = cls.__dict__.copy()
if 'declarative_count' in v:
name = '%s %i' % (name, v.pop('declarative_count'))
names = v.keys()
args = []
for n in self._repr_vars(names):
args.append('%s=%r' % (n, v[n]))
if not args:
return '<%s>' % name
else:
return '<%s %s>' % (name, ' '.join(args))
def _repr_vars(dictNames):
names = [n for n in dictNames
if not n.startswith('_') and n != 'declarative_count']
names.sort()
return names
_repr_vars = staticmethod(_repr_vars)
__repr__ = classinstancemethod(__repr__)

View File

@@ -0,0 +1,137 @@
try:
import doctest
doctest.OutputChecker
except AttributeError: # Python < 2.4
import util.doctest24 as doctest
try:
import xml.etree.ElementTree as ET
except ImportError:
import elementtree.ElementTree as ET
from xml.parsers.expat import ExpatError as XMLParseError
RealOutputChecker = doctest.OutputChecker
def debug(*msg):
import sys
print >> sys.stderr, ' '.join(map(str, msg))
class HTMLOutputChecker(RealOutputChecker):
def check_output(self, want, got, optionflags):
normal = RealOutputChecker.check_output(self, want, got, optionflags)
if normal or not got:
return normal
try:
want_xml = make_xml(want)
except XMLParseError:
pass
else:
try:
got_xml = make_xml(got)
except XMLParseError:
pass
else:
if xml_compare(want_xml, got_xml):
return True
return False
def output_difference(self, example, got, optionflags):
actual = RealOutputChecker.output_difference(
self, example, got, optionflags)
want_xml = got_xml = None
try:
want_xml = make_xml(example.want)
want_norm = make_string(want_xml)
except XMLParseError, e:
if example.want.startswith('<'):
want_norm = '(bad XML: %s)' % e
# '<xml>%s</xml>' % example.want
else:
return actual
try:
got_xml = make_xml(got)
got_norm = make_string(got_xml)
except XMLParseError, e:
if example.want.startswith('<'):
got_norm = '(bad XML: %s)' % e
else:
return actual
s = '%s\nXML Wanted: %s\nXML Got : %s\n' % (
actual, want_norm, got_norm)
if got_xml and want_xml:
result = []
xml_compare(want_xml, got_xml, result.append)
s += 'Difference report:\n%s\n' % '\n'.join(result)
return s
def xml_compare(x1, x2, reporter=None):
if x1.tag != x2.tag:
if reporter:
reporter('Tags do not match: %s and %s' % (x1.tag, x2.tag))
return False
for name, value in x1.attrib.items():
if x2.attrib.get(name) != value:
if reporter:
reporter('Attributes do not match: %s=%r, %s=%r'
% (name, value, name, x2.attrib.get(name)))
return False
for name in x2.attrib.keys():
if name not in x1.attrib:
if reporter:
reporter('x2 has an attribute x1 is missing: %s'
% name)
return False
if not text_compare(x1.text, x2.text):
if reporter:
reporter('text: %r != %r' % (x1.text, x2.text))
return False
if not text_compare(x1.tail, x2.tail):
if reporter:
reporter('tail: %r != %r' % (x1.tail, x2.tail))
return False
cl1 = x1.getchildren()
cl2 = x2.getchildren()
if len(cl1) != len(cl2):
if reporter:
reporter('children length differs, %i != %i'
% (len(cl1), len(cl2)))
return False
i = 0
for c1, c2 in zip(cl1, cl2):
i += 1
if not xml_compare(c1, c2, reporter=reporter):
if reporter:
reporter('children %i do not match: %s'
% (i, c1.tag))
return False
return True
def text_compare(t1, t2):
if not t1 and not t2:
return True
if t1 == '*' or t2 == '*':
return True
return (t1 or '').strip() == (t2 or '').strip()
def make_xml(s):
return ET.XML('<xml>%s</xml>' % s)
def make_string(xml):
if isinstance(xml, (str, unicode)):
xml = make_xml(xml)
s = ET.tostring(xml)
if s == '<xml />':
return ''
assert s.startswith('<xml>') and s.endswith('</xml>'), repr(s)
return s[5:-6]
def install():
doctest.OutputChecker = HTMLOutputChecker

View File

@@ -0,0 +1,13 @@
## FormEncode, a Form processor
## Copyright (C) 2003, Ian Bicking <ianb@colorstudy.com>
"""
Wrapper class for use with cgi.FieldStorage types for file uploads
"""
import cgi
def convert_fieldstorage(fs):
if fs.filename:
return fs
else:
return None

View File

@@ -0,0 +1,151 @@
"""
Validator for repeating items.
"""
import warnings
try:
set
except NameError: # Python < 2.4
from sets import Set as set
filters = warnings.filters[:]
warnings.simplefilter('ignore', DeprecationWarning)
warnings.filters = filters
from api import NoDefault, Invalid
from compound import CompoundValidator, from_python
__all__ = ['ForEach']
class ForEach(CompoundValidator):
"""
Use this to apply a validator/converter to each item in a list.
For instance::
ForEach(AsInt(), InList([1, 2, 3]))
Will take a list of values and try to convert each of them to
an integer, and then check if each integer is 1, 2, or 3. Using
multiple arguments is equivalent to::
ForEach(All(AsInt(), InList([1, 2, 3])))
Use convert_to_list=True if you want to force the input to be a
list. This will turn non-lists into one-element lists, and None
into the empty list. This tries to detect sequences by iterating
over them (except strings, which aren't considered sequences).
ForEach will try to convert the entire list, even if errors are
encountered. If errors are encountered, they will be collected
and a single Invalid exception will be raised at the end (with
error_list set).
If the incoming value is a set, then we return a set.
"""
convert_to_list = True
if_empty = NoDefault
repeating = True
_if_missing = ()
def attempt_convert(self, value, state, validate):
if self.convert_to_list:
value = self._convert_to_list(value)
if self.if_empty is not NoDefault and not value:
return self.if_empty
if self.not_empty and not value:
if validate is from_python and self.accept_python:
return []
raise Invalid(
self.message('empty', state),
value, state)
new_list = []
errors = []
all_good = True
is_set = isinstance(value, set)
if state is not None:
previous_index = getattr(state, 'index', NoDefault)
previous_full_list = getattr(state, 'full_list', NoDefault)
index = 0
state.full_list = value
try:
for sub_value in value:
if state:
state.index = index
index += 1
good_pass = True
for validator in self.validators:
try:
sub_value = validate(validator, sub_value, state)
except Invalid, e:
errors.append(e)
all_good = False
good_pass = False
break
if good_pass:
errors.append(None)
new_list.append(sub_value)
if all_good:
if is_set:
new_list = set(new_list)
return new_list
else:
raise Invalid(
'Errors:\n%s' % '\n'.join([unicode(e) for e in errors if e]),
value,
state,
error_list=errors)
finally:
if state is not None:
if previous_index is NoDefault:
try:
del state.index
except AttributeError:
pass
else:
state.index = previous_index
if previous_full_list is NoDefault:
try:
del state.full_list
except AttributeError:
pass
else:
state.full_list = previous_full_list
def empty_value(self, value):
return []
class _IfMissing(object):
def __get__(self, obj, type=None):
if obj is None:
return []
elif obj._if_missing is ForEach._if_missing:
return []
else:
return obj._if_missing
def __set__(self, obj, value):
obj._if_missing = value
def __delete__(self, obj):
obj._if_missing = NoDefault
if_missing = _IfMissing()
del _IfMissing
def _convert_to_list(self, value):
if isinstance(value, (str, unicode)):
return [value]
elif value is None:
return []
elif isinstance(value, (list, tuple)):
return value
try:
for n in value:
break
return value
## @@: Should this catch any other errors?:
except TypeError:
return [value]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,118 @@
"""
Extension to ``htmlfill`` that can parse out schema-defining
statements.
You can either pass ``SchemaBuilder`` to ``htmlfill.render`` (the
``listen`` argument), or call ``parse_schema`` to just parse out a
``Schema`` object.
"""
import validators, schema, compound, htmlfill
__all__ = ['parse_schema', 'SchemaBuilder']
def parse_schema(form):
"""
Given an HTML form, parse out the schema defined in it and return
that schema.
"""
listener = SchemaBuilder()
p = htmlfill.FillingParser(
defaults={}, listener=listener)
p.feed(form)
p.close()
return listener.schema()
default_validators = dict(
[(name.lower(), getattr(validators, name))
for name in dir(validators)])
def get_messages(cls, message):
if not message:
return {}
else:
return dict([(k, message) for k in cls._messages.keys()])
def to_bool(value):
value = value.strip().lower()
if value in ('true', 't', 'yes', 'y', 'on', '1'):
return True
elif value in ('false', 'f', 'no', 'n', 'off', '0'):
return False
else:
raise ValueError("Not a boolean value: %r (use 'true'/'false')")
def force_list(v):
"""
Force single items into a list. This is useful for checkboxes.
"""
if isinstance(v, list):
return v
elif isinstance(v, tuple):
return list(v)
else:
return [v]
class SchemaBuilder(object):
def __init__(self, validators=default_validators):
self.validators = validators
self._schema = None
def reset(self):
self._schema = schema.Schema()
def schema(self):
return self._schema
def listen_input(self, parser, tag, attrs):
get_attr = parser.get_attr
name = get_attr(attrs, 'name')
if not name:
# @@: should warn if you try to validate unnamed fields
return
v = compound.All(validators.Identity())
add_to_end = None
# for checkboxes, we must set if_missing = False
if tag.lower() == "input":
type_attr = get_attr(attrs, "type").lower().strip()
if type_attr == "submit":
v.validators.append(validators.Bool())
elif type_attr == "checkbox":
v.validators.append(validators.Wrapper(to_python = force_list))
elif type_attr == "file":
add_to_end = validators.FieldStorageUploadConverter()
message = get_attr(attrs, 'form:message')
required = to_bool(get_attr(attrs, 'form:required', 'false'))
if required:
v.validators.append(
validators.NotEmpty(
messages=get_messages(validators.NotEmpty, message)))
else:
v.validators[0].if_missing = False
if add_to_end:
v.validators.append(add_to_end)
v_type = get_attr(attrs, 'form:validate', None)
if v_type:
pos = v_type.find(':')
if pos != -1:
# @@: should parse args
args = (v_type[pos+1:],)
v_type = v_type[:pos]
else:
args = ()
v_type = v_type.lower()
v_class = self.validators.get(v_type)
if not v_class:
raise ValueError("Invalid validation type: %r" % v_type)
kw_args={'messages': get_messages(v_class, message)}
v_inst = v_class(
*args, **kw_args)
v.validators.append(v_inst)
self._schema.add_field(name, v)

View File

@@ -0,0 +1,190 @@
"""
Kind of like htmlgen, only much simpler. The only important symbol
that is exported is ``html``.
This builds ElementTree nodes, but with some extra useful methods.
(Open issue: should it use ``ElementTree`` more, and the raw
``Element`` stuff less?)
You create tags with attribute access. I.e., the ``A`` anchor tag is
``html.a``. The attributes of the HTML tag are done with keyword
arguments. The contents of the tag are the non-keyword arguments
(concatenated). You can also use the special ``c`` keyword, passing a
list, tuple, or single tag, and it will make up the contents (this is
useful because keywords have to come after all non-keyword arguments,
which is non-intuitive). Or you can chain them, adding the keywords
with one call, then the body with a second call, like::
>>> print html.a(href='http://yahoo.com')('<Yahoo>')
<a href=\"http://yahoo.com\">&lt;Yahoo&gt;</a>
Note that strings will be quoted; only tags given explicitly will
remain unquoted.
If the value of an attribute is None, then no attribute
will be inserted. So::
>>> print html.a(href='http://www.yahoo.com', name=None,
... c='Click Here')
<a href=\"http://www.yahoo.com\">Click Here</a>
If the value is None, then the empty string is used. Otherwise str()
is called on the value.
``html`` can also be called, and it will produce a special list from
its arguments, which adds a ``__str__`` method that does ``html.str``
(which handles quoting, flattening these lists recursively, and using
'' for ``None``).
``html.comment`` will generate an HTML comment, like
``html.comment('comment text')`` -- note that it cannot take keyword
arguments (because they wouldn't mean anything).
Examples::
>>> print html.html(
... html.head(html.title(\"Page Title\")),
... html.body(
... bgcolor='#000066',
... text='#ffffff',
... c=[html.h1('Page Title'),
... html.p('Hello world!')],
... ))
<html><head><title>Page Title</title></head><body bgcolor=\"#000066\" text=\"#ffffff\"><h1>Page Title</h1><p>Hello world!</p></body></html>
>>> print html.a(href='#top')('return to top')
<a href=\"#top\">return to top</a>
"""
from cgi import escape
try:
import xml.etree.ElementTree as ET
except ImportError: # Python < 2.5
import elementtree.ElementTree as ET
__all__ = ['html']
default_encoding = 'utf-8'
class _HTML:
def __getattr__(self, attr):
if attr.startswith('_'):
raise AttributeError
attr = attr.lower()
if attr.endswith('_'):
attr = attr[:-1]
if attr.find('__') != -1:
attr = attr.replace('__', ':')
if attr == 'comment':
return Element(ET.Comment, {})
else:
return Element(attr, {})
def __call__(self, *args):
return ElementList(args)
def quote(self, arg):
if arg is None:
return ''
return escape(unicode(arg).encode(default_encoding), 1)
def str(self, arg, encoding=None):
if isinstance(arg, str):
return arg
elif arg is None:
return ''
elif isinstance(arg, unicode):
return arg.encode(default_encoding)
elif isinstance(arg, (list, tuple)):
return ''.join(map(self.str, arg))
elif isinstance(arg, Element):
return str(arg)
else:
return unicode(arg).encode(default_encoding)
html = _HTML()
class Element(ET._ElementInterface):
def __call__(self, *args, **kw):
el = self.__class__(self.tag, self.attrib)
if 'c' in kw:
if args:
raise ValueError(
"You may either provide positional arguments or a "
"'c' keyword argument, but not both")
args = kw.pop('c')
if not isinstance(args, (list, tuple)):
args = (args,)
for name, value in kw.items():
if value is None:
del kw[name]
continue
kw[name] = unicode(value)
if name.endswith('_'):
kw[name[:-1]] = value
del kw[name]
if name.find('__') != -1:
new_name = name.replace('__', ':')
kw[new_name] = value
del kw[name]
el.attrib.update(kw)
el.text = self.text
last = None
for item in self.getchildren():
last = item
el.append(item)
for arg in flatten(args):
if arg is None:
continue
if not ET.iselement(arg):
if last is None:
if el.text is None:
el.text = unicode(arg)
else:
el.text += unicode(arg)
else:
if last.tail is None:
last.tail = unicode(arg)
else:
last.tail += unicode(arg)
else:
last = arg
el.append(last)
return el
def __str__(self):
return ET.tostring(self, default_encoding)
def __unicode__(self):
# This is lame!
return str(self).decode(default_encoding)
def __repr__(self):
content = str(self)
if len(content) > 25:
content = repr(content[:25]) + '...'
else:
content = repr(content)
return '<Element %r>' % content
class ElementList(list):
def __str__(self):
return html.str(self)
def __repr__(self):
return 'ElementList(%s)' % list.__repr__(self)
def flatten(items):
for item in items:
if isinstance(item, (list, tuple)):
for sub in flatten(item):
yield sub
else:
yield item

View File

@@ -0,0 +1,78 @@
"""
Module to rename form fields
"""
from formencode.rewritingparser import RewritingParser
__all__ = ['rename', 'add_prefix']
def rename(form, rename_func):
"""
Rename all the form fields in the form (a string), using rename_func
rename_func will be called with one argument, the name of the
field, and should return a new name.
"""
p = RenamingParser(rename_func)
p.feed(form)
p.close()
return p.text()
def add_prefix(form, prefix, dotted=False):
"""
Add the given prefix to all the fields in the form.
If dotted is true, then add a dot between prefix and the previous
name. Empty fields will use the prefix as the name (with no dot).
"""
def rename_func(field_name):
if dotted:
if field_name:
return prefix + '.' + field_name
else:
return prefix
else:
return prefix + field_name
return rename(form, rename_func)
class RenamingParser(RewritingParser):
def __init__(self, rename_func):
RewritingParser.__init__(self)
self.rename_func = rename_func
def close(self):
self.handle_misc(None)
RewritingParser.close(self)
self._text = self._get_text()
def text(self):
try:
return self._text
except AttributeError:
raise Exception(
"You must .close() a parser instance before getting "
"the text from it")
def handle_starttag(self, tag, attrs, startend=False):
self.write_pos()
if tag in ('input', 'textarea', 'select'):
self.handle_field(tag, attrs, startend)
else:
return
def handle_startendtag(self, tag, attrs):
return self.handle_starttag(tag, attrs, True)
def handle_field(self, tag, attrs, startend):
name = self.get_attr(attrs, 'name', '')
new_name = self.rename_func(name)
if name is None:
self.del_attr(attrs, 'name')
else:
self.set_attr(attrs, 'name', new_name)
self.write_tag(tag, attrs)
self.skip_next = True

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,203 @@
#! /usr/bin/python
# -*- coding: iso-8859-1 -*-
# Written by Martin v. Löwis <loewis@informatik.hu-berlin.de>
"""Generate binary message catalog from textual translation description.
This program converts a textual Uniforum-style message catalog (.po file) into
a binary GNU catalog (.mo file). This is essentially the same function as the
GNU msgfmt program, however, it is a simpler implementation.
Usage: msgfmt.py [OPTIONS] filename.po
Options:
-o file
--output-file=file
Specify the output file to write to. If omitted, output will go to a
file named filename.mo (based off the input file name).
-h
--help
Print this message and exit.
-V
--version
Display version information and exit.
"""
import sys
import os
import getopt
import struct
import array
__version__ = "1.1"
MESSAGES = {}
def usage(code, msg=''):
print >> sys.stderr, __doc__
if msg:
print >> sys.stderr, msg
sys.exit(code)
def add(id, str, fuzzy):
"Add a non-fuzzy translation to the dictionary."
global MESSAGES
if not fuzzy and str:
MESSAGES[id] = str
def generate():
"Return the generated output."
global MESSAGES
keys = MESSAGES.keys()
# the keys are sorted in the .mo file
keys.sort()
offsets = []
ids = strs = ''
for id in keys:
# For each string, we need size and file offset. Each string is NUL
# terminated; the NUL does not count into the size.
offsets.append((len(ids), len(id), len(strs), len(MESSAGES[id])))
ids += id + '\0'
strs += MESSAGES[id] + '\0'
output = ''
# The header is 7 32-bit unsigned integers. We don't use hash tables, so
# the keys start right after the index tables.
# translated string.
keystart = 7*4+16*len(keys)
# and the values start after the keys
valuestart = keystart + len(ids)
koffsets = []
voffsets = []
# The string table first has the list of keys, then the list of values.
# Each entry has first the size of the string, then the file offset.
for o1, l1, o2, l2 in offsets:
koffsets += [l1, o1+keystart]
voffsets += [l2, o2+valuestart]
offsets = koffsets + voffsets
output = struct.pack("Iiiiiii",
0x950412deL, # Magic
0, # Version
len(keys), # # of entries
7*4, # start of key index
7*4+len(keys)*8, # start of value index
0, 0) # size and offset of hash table
output += array.array("i", offsets).tostring()
output += ids
output += strs
return output
def make(filename, outfile):
ID = 1
STR = 2
# Compute .mo name from .po name and arguments
if filename.endswith('.po'):
infile = filename
else:
infile = filename + '.po'
if outfile is None:
outfile = os.path.splitext(infile)[0] + '.mo'
try:
lines = open(infile).readlines()
except IOError, msg:
print >> sys.stderr, msg
sys.exit(1)
section = None
fuzzy = 0
# Parse the catalog
lno = 0
for l in lines:
lno += 1
# If we get a comment line after a msgstr, this is a new entry
if l[0] == '#' and section == STR:
add(msgid, msgstr, fuzzy)
section = None
fuzzy = 0
# Record a fuzzy mark
if l[:2] == '#,' and 'fuzzy' in l:
fuzzy = 1
# Skip comments
if l[0] == '#':
continue
# Now we are in a msgid section, output previous section
if l.startswith('msgid'):
if section == STR:
add(msgid, msgstr, fuzzy)
section = ID
l = l[5:]
msgid = msgstr = ''
# Now we are in a msgstr section
elif l.startswith('msgstr'):
section = STR
l = l[6:]
# Skip empty lines
l = l.strip()
if not l:
continue
# XXX: Does this always follow Python escape semantics?
l = eval(l)
if section == ID:
msgid += l
elif section == STR:
msgstr += l
else:
print >> sys.stderr, 'Syntax error on %s:%d' % (infile, lno), \
'before:'
print >> sys.stderr, l
sys.exit(1)
# Add last entry
if section == STR:
add(msgid, msgstr, fuzzy)
# Compute output
output = generate()
try:
open(outfile,"wb").write(output)
except IOError,msg:
print >> sys.stderr, msg
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], 'hVo:',
['help', 'version', 'output-file='])
except getopt.error, msg:
usage(1, msg)
outfile = None
# parse options
for opt, arg in opts:
if opt in ('-h', '--help'):
usage(0)
elif opt in ('-V', '--version'):
print >> sys.stderr, "msgfmt.py", __version__
sys.exit(0)
elif opt in ('-o', '--output-file'):
outfile = arg
# do it
if not args:
print >> sys.stderr, 'No input file given'
print >> sys.stderr, "Try `msgfmt --help' for more information."
return
for filename in args:
make(filename, outfile)
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,71 @@
"""
Interfaces for FormEncode (for documentation purposes only)
"""
class Attribute(object):
def __init__(self, description, name=None):
self.description = description
self.name = name
class Interface(object):
pass
class IDeclarative(Interface):
def __init__(**kw):
"""
Instantiates this class with all the keywords being used to
update the instance variables.
"""
def __call__(**kw):
"""
Returns a copy with all attributes using the given keywords,
being updated.
"""
class IValidator(IDeclarative):
messages = Attribute("""
A dictionary of messages (with formatting strings) for error
responses""", name='messages')
if_missing = Attribute("""
If the source that this validator would handle is missing (e.g.,
a field that was not specified), use this value. If
Validator.NoDefault, then if the field is missing an exception
should be raised.""", name='ifMissing')
repeating = Attribute("""
A boolean; this object accepts lists if true, subvalidators can be
found in the validators attribute.""", name='repeating')
compound = Attribute("""
A boolean; this object has a dictionary of validators if this is
true, subvalidators can be found in the field attribute (a
dictionary).""", name='compound')
def to_python(value, state=None):
"""
Convert `value` from its foreign representation to its Python
representation. `state` is for application-specific hooks.
"""
def from_python(value, state=None):
"""
Convert `value` from its Python representation to the foreign
representation. `state` is for application-specific hooks.
"""
def message(name, default):
"""
Return the message (from the `messages` attribute) that goes
with `name`, or return default if `name` not found `default`.
"""
class ISchema(IValidator):
fields = Attribute('A dictionary of (field name: validator)', name='fields')

View File

@@ -0,0 +1,61 @@
function up(formElement) {
var i, select;
select = getSelect(formElement);
i = select.selectedIndex;
if (i == -1 || i == 0) {
return;
}
swapOptions(select, i, i-1);
select.selectedIndex = i-1;
saveValue(select);
}
function down(formElement) {
var i, select;
select = getSelect(formElement);
i = select.selectedIndex;
if (i == -1 || i == select.length-1) {
return;
}
swapOptions(select, i, i+1);
select.selectedIndex = i+1;
saveValue(select);
}
function getSelect(formElement) {
return formElement.form['%(name)s']
}
function swapOptions(select, op1, op2) {
var tmpValue, tmpText;
tmpValue = select.options[op1].value;
tmpText = select.options[op1].text;
select.options[op1].value = select.options[op2].value;
select.options[op1].text = select.options[op2].text;
select.options[op2].value = tmpValue;
select.options[op2].text = tmpText;
}
function saveValue(select) {
if (origValues == false) {
saveOrigValues(select);
}
var s = "", i;
for (i=0; i < select.length; i++) {
s = s + escape(select.options[i].value) + " ";
}
select.form['%(hidden_name)s'].value = s;
}
function saveOrigValues(select) {
origValues = new Array();
for (i=0; i<select.length; i++) {
origValues[i*2] = select.options[i].value;
origValues[i*2+1] = select.options[i].text;
}
}
origValues = false;
function resetEntries(formElement) {
var select;
select = getSelect(formElement);
for (i=0; i<origValues.length; i+=2) {
select.options[Math.floor(i/2)] = new Option(origValues[i+1], origValues[i], false, false);
select.options[Math.floor(i/2)].selected = false;
}
saveValue(select);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,161 @@
import cgi
import HTMLParser
import re
from htmlentitydefs import name2codepoint
def html_quote(v):
if v is None:
return ''
elif hasattr(v, '__html__'):
return v.__html__()
elif isinstance(v, basestring):
return cgi.escape(v, 1)
else:
if hasattr(v, '__unicode__'):
v = unicode(v)
else:
v = str(v)
return cgi.escape(v, 1)
class RewritingParser(HTMLParser.HTMLParser):
listener = None
skip_next = False
def __init__(self):
self._content = []
HTMLParser.HTMLParser.__init__(self)
def feed(self, data):
self.data_is_str = isinstance(data, str)
self.source = data
self.lines = data.split('\n')
self.source_pos = 1, 0
if self.listener:
self.listener.reset()
HTMLParser.HTMLParser.feed(self, data)
_entityref_re = re.compile('&([a-zA-Z][-.a-zA-Z\d]*);')
_charref_re = re.compile('&#(\d+|[xX][a-fA-F\d]+);')
def unescape(self, s):
s = self._entityref_re.sub(self._sub_entityref, s)
s = self._charref_re.sub(self._sub_charref, s)
return s
def _sub_entityref(self, match):
name = match.group(1)
if name not in name2codepoint:
# If we don't recognize it, pass it through as though it
# wasn't an entity ref at all
return match.group(0)
return unichr(name2codepoint[name])
def _sub_charref(self, match):
num = match.group(1)
if num.lower().startswith('x'):
num = int(num[1:], 16)
else:
num = int(num)
return unichr(num)
def handle_misc(self, whatever):
self.write_pos()
handle_charref = handle_misc
handle_entityref = handle_misc
handle_data = handle_misc
handle_comment = handle_misc
handle_decl = handle_misc
handle_pi = handle_misc
unknown_decl = handle_misc
handle_endtag = handle_misc
def write_tag(self, tag, attrs, startend=False):
attr_text = ''.join([' %s="%s"' % (n, html_quote(v))
for (n, v) in attrs
if not n.startswith('form:')])
if startend:
attr_text += " /"
self.write_text('<%s%s>' % (tag, attr_text))
def skip_output(self):
return False
def write_pos(self):
cur_line, cur_offset = self.getpos()
if self.skip_output():
self.source_pos = self.getpos()
return
if self.skip_next:
self.skip_next = False
self.source_pos = self.getpos()
return
if cur_line == self.source_pos[0]:
self.write_text(
self.lines[cur_line-1][self.source_pos[1]:cur_offset])
else:
self.write_text(
self.lines[self.source_pos[0]-1][self.source_pos[1]:])
self.write_text('\n')
for i in range(self.source_pos[0]+1, cur_line):
self.write_text(self.lines[i-1])
self.write_text('\n')
self.write_text(self.lines[cur_line-1][:cur_offset])
self.source_pos = self.getpos()
def write_text(self, text):
self._content.append(text)
def get_attr(self, attr, name, default=None):
for n, value in attr:
if n.lower() == name:
return value
return default
def set_attr(self, attr, name, value):
for i in range(len(attr)):
if attr[i][0].lower() == name:
attr[i] = (name, value)
return
attr.append((name, value))
def del_attr(self, attr, name):
for i in range(len(attr)):
if attr[i][0].lower() == name:
del attr[i]
break
def add_class(self, attr, class_name):
current = self.get_attr(attr, 'class', '')
new = current + ' ' + class_name
self.set_attr(attr, 'class', new.strip())
def text(self):
try:
return self._text
except AttributeError:
raise Exception(
"You must .close() a parser instance before getting "
"the text from it")
def _get_text(self):
try:
return ''.join([
t for t in self._content if not isinstance(t, tuple)])
except UnicodeDecodeError, e:
if self.data_is_str:
e.reason += (
" the form was passed in as an encoded string, but"
" some data or error messages were unicode strings;"
" the form should be passed in as a unicode string")
else:
e.reason += (
" the form was passed in as an unicode string, but"
" some data or error message was an encoded string;"
" the data and error messages should be passed in as"
" unicode strings")
raise

View File

@@ -0,0 +1,467 @@
from interfaces import *
from api import *
from api import _
import declarative
__all__ = ['Schema']
class Schema(FancyValidator):
"""
A schema validates a dictionary of values, applying different
validators (be key) to the different values. If
allow_extra_fields=True, keys without validators will be allowed;
otherwise they will raise Invalid. If filter_extra_fields is
set to true, then extra fields are not passed back in the results.
Validators are associated with keys either with a class syntax, or
as keyword arguments (class syntax is usually easier). Something
like::
class MySchema(Schema):
name = Validators.PlainText()
phone = Validators.PhoneNumber()
These will not be available as actual instance variables, but will
be collected in a dictionary. To remove a validator in a subclass
that is present in a superclass, set it to None, like::
class MySubSchema(MySchema):
name = None
Note that missing fields are handled at the Schema level. Missing
fields can have the 'missing' message set to specify the error
message, or if that does not exist the *schema* message
'missingValue' is used.
"""
# These validators will be applied before this schema:
pre_validators = []
# These validators will be applied after this schema:
chained_validators = []
# If true, then it is not an error when keys that aren't
# associated with a validator are present:
allow_extra_fields = False
# If true, then keys that aren't associated with a validator
# are removed:
filter_extra_fields = False
# If this is given, then any keys that aren't available but
# are expected will be replaced with this value (and then
# validated!) This does not override a present .if_missing
# attribute on validators:
if_key_missing = NoDefault
# If true, then missing keys will be missing in the result,
# if the validator doesn't have if_missing on it already:
ignore_key_missing = False
compound = True
fields = {}
order = []
messages = dict(
notExpected=_('The input field %(name)s was not expected.'),
missingValue=_('Missing value'),
badDictType=_('The input must be dict-like'
' (not a %(type)s: %(value)r)'))
__mutableattributes__ = ('fields', 'chained_validators',
'pre_validators')
def __classinit__(cls, new_attrs):
FancyValidator.__classinit__(cls, new_attrs)
# Don't bother doing anything if this is the most parent
# Schema class (which is the only class with just
# FancyValidator as a superclass):
if cls.__bases__ == (FancyValidator,):
return cls
# Scan through the class variables we've defined *just*
# for this subclass, looking for validators (both classes
# and instances):
for key, value in new_attrs.items():
if key in ('pre_validators', 'chained_validators', 'view'):
continue
if is_validator(value):
cls.fields[key] = value
delattr(cls, key)
# This last case means we're overwriting a validator
# from a superclass:
elif key in cls.fields:
del cls.fields[key]
for name, value in cls.fields.items():
cls.add_field(name, value)
def __initargs__(self, new_attrs):
for key, value in new_attrs.items():
if key in ('pre_validators', 'chained_validators', 'view'):
continue
if is_validator(value):
self.fields[key] = value
delattr(self, key)
# This last case means we're overwriting a validator
# from a superclass:
elif key in self.fields:
del self.fields[key]
for name, value in self.fields.items():
self.add_field(name, value)
def assert_dict(self, value, state):
"""
Helper to assure we have proper input
"""
if not hasattr(value, 'items'):
# Not a dict or dict-like object
raise Invalid(
self.message('badDictType', state,
type=type(value), value=value), value, state)
def _to_python(self, value_dict, state):
if not value_dict:
if self.if_empty is not NoDefault:
return self.if_empty
else:
value_dict = {}
for validator in self.pre_validators:
value_dict = validator.to_python(value_dict, state)
self.assert_dict(value_dict, state)
new = {}
errors = {}
unused = self.fields.keys()
if state is not None:
previous_key = getattr(state, 'key', None)
previous_full_dict = getattr(state, 'full_dict', None)
state.full_dict = value_dict
try:
for name, value in value_dict.items():
try:
unused.remove(name)
except ValueError:
if not self.allow_extra_fields:
raise Invalid(
self.message('notExpected', state, name=repr(name)),
value_dict, state)
else:
if not self.filter_extra_fields:
new[name] = value
continue
validator = self.fields[name]
try:
new[name] = validator.to_python(value, state)
except Invalid, e:
errors[name] = e
for name in unused:
validator = self.fields[name]
try:
if_missing = validator.if_missing
except AttributeError:
if_missing = NoDefault
if if_missing is NoDefault:
if self.ignore_key_missing:
continue
if self.if_key_missing is NoDefault:
try:
message = validator.message('missing', state)
except KeyError:
message = self.message('missingValue', state)
errors[name] = Invalid(message, None, state)
else:
try:
new[name] = validator.to_python(self.if_key_missing, state)
except Invalid, e:
errors[name] = e
else:
new[name] = validator.if_missing
for validator in self.chained_validators:
if (not hasattr(validator, 'validate_partial')
or not getattr(validator, 'validate_partial_form', False)):
continue
try:
validator.validate_partial(value_dict, state)
except Invalid, e:
sub_errors = e.unpack_errors()
if not isinstance(sub_errors, dict):
# Can't do anything here
continue
merge_dicts(errors, sub_errors)
if errors:
raise Invalid(
format_compound_error(errors),
value_dict, state, error_dict=errors)
for validator in self.chained_validators:
new = validator.to_python(new, state)
return new
finally:
if state is not None:
state.key = previous_key
state.full_dict = previous_full_dict
def _from_python(self, value_dict, state):
chained = self.chained_validators[:]
chained.reverse()
finished = []
for validator in chained:
__traceback_info__ = 'for_python chained_validator %s (finished %s)' % (validator, ', '.join(map(repr, finished)) or 'none')
finished.append(validator)
value_dict = validator.from_python(value_dict, state)
self.assert_dict(value_dict, state)
new = {}
errors = {}
unused = self.fields.keys()
if state is not None:
previous_key = getattr(state, 'key', None)
previous_full_dict = getattr(state, 'full_dict', None)
state.full_dict = value_dict
try:
__traceback_info__ = None
for name, value in value_dict.items():
__traceback_info__ = 'for_python in %s' % name
try:
unused.remove(name)
except ValueError:
if not self.allow_extra_fields:
raise Invalid(
self.message('notExpected', state, name=repr(name)),
value_dict, state)
if not self.filter_extra_fields:
new[name] = value
else:
try:
new[name] = self.fields[name].from_python(value, state)
except Invalid, e:
errors[name] = e
del __traceback_info__
for name in unused:
validator = self.fields[name]
try:
new[name] = validator.from_python(None, state)
except Invalid, e:
errors[name] = e
if errors:
raise Invalid(
format_compound_error(errors),
value_dict, state, error_dict=errors)
pre = self.pre_validators[:]
pre.reverse()
for validator in pre:
__traceback_info__ = 'for_python pre_validator %s' % validator
new = validator.from_python(new, state)
return new
finally:
if state is not None:
state.key = previous_key
state.full_dict = previous_full_dict
def add_chained_validator(self, cls, validator):
if self is not None:
if self.chained_validators is cls.chained_validators:
self.chained_validators = cls.chained_validators[:]
self.chained_validators.append(validator)
else:
cls.chained_validators.append(validator)
add_chained_validator = declarative.classinstancemethod(
add_chained_validator)
def add_field(self, cls, name, validator):
if self is not None:
if self.fields is cls.fields:
self.fields = cls.fields.copy()
self.fields[name] = validator
else:
cls.fields[name] = validator
add_field = declarative.classinstancemethod(add_field)
def add_pre_validator(self, cls, validator):
if self is not None:
if self.pre_validators is cls.pre_validators:
self.pre_validators = cls.pre_validators[:]
self.pre_validators.append(validator)
else:
cls.pre_validators.append(validator)
add_pre_validator = declarative.classinstancemethod(add_pre_validator)
def subvalidators(self):
result = []
result.extend(self.pre_validators)
result.extend(self.chained_validators)
result.extend(self.fields.values())
return result
def is_empty(self, value):
## Generally nothing is empty for us
return False
def empty_value(self, value):
return {}
def format_compound_error(v, indent=0):
if isinstance(v, Exception):
try:
return str(v)
except (UnicodeDecodeError, UnicodeEncodeError):
# There doesn't seem to be a better way to get a str()
# version if possible, and unicode() if necessary, because
# testing for the presence of a __unicode__ method isn't
# enough
return unicode(v)
elif isinstance(v, dict):
l = v.items()
l.sort()
return ('%s\n' % (' '*indent)).join(
["%s: %s" % (k, format_compound_error(value, indent=len(k)+2))
for k, value in l
if value is not None])
elif isinstance(v, list):
return ('%s\n' % (' '*indent)).join(
['%s' % (format_compound_error(value, indent=indent))
for value in v
if value is not None])
elif isinstance(v, basestring):
return v
else:
assert 0, "I didn't expect something like %s" % repr(v)
def merge_dicts(d1, d2):
for key in d2:
if key in d1:
d1[key] = merge_values(d1[key], d2[key])
else:
d1[key] = d2[key]
return d1
def merge_values(v1, v2):
if (isinstance(v1, (str, unicode))
and isinstance(v2, (str, unicode))):
return v1 + '\n' + v2
elif (isinstance(v1, (list, tuple))
and isinstance(v2, (list, tuple))):
return merge_lists(v1, v2)
elif isinstance(v1, dict) and isinstance(v2, dict):
return merge_dicts(v1, v2)
else:
# @@: Should we just ignore errors? Seems we do...
return v1
def merge_lists(l1, l2):
if len(l1) < len(l2):
l1 = l1 + [None]*(len(l2)-len(l1))
elif len(l2) < len(l1):
l2 = l2 + [None]*(len(l1)-len(l2))
result = []
for l1item, l2item in zip(l1, l2):
item = None
if l1item is None:
item = l2item
elif l2item is None:
item = l1item
else:
item = merge_values(l1item, l2item)
result.append(item)
return result
class SimpleFormValidator(FancyValidator):
"""
This validator wraps a simple function that validates the form.
The function looks something like this::
>>> def validate(form_values, state, validator):
... if form_values.get('country', 'US') == 'US':
... if not form_values.get('state'):
... return dict(state='You must enter a state')
... if not form_values.get('country'):
... form_values['country'] = 'US'
This tests that the field 'state' must be filled in if the country
is US, and defaults that country value to 'US'. The ``validator``
argument is the SimpleFormValidator instance, which you can use to
format messages or keep configuration state in if you like (for
simple ad hoc validation you are unlikely to need it).
To create a validator from that function, you would do::
>>> from formencode.schema import SimpleFormValidator
>>> validator = SimpleFormValidator(validate)
>>> validator.to_python({'country': 'US', 'state': ''}, None)
Traceback (most recent call last):
...
Invalid: state: You must enter a state
>>> validator.to_python({'state': 'IL'}, None)
{'country': 'US', 'state': 'IL'}
The validate function can either return a single error message
(that applies to the whole form), a dictionary that applies to the
fields, None which means the form is valid, or it can raise
Invalid.
Note that you may update the value_dict *in place*, but you cannot
return a new value.
Another way to instantiate a validator is like this::
>>> @SimpleFormValidator.decorate()
... def MyValidator(value_dict, state):
... return None # or some more useful validation
After this ``MyValidator`` will be a ``SimpleFormValidator``
instance (it won't be your function).
"""
__unpackargs__ = ('func',)
validate_partial_form = False
def __initargs__(self, new_attrs):
self.__doc__ = getattr(self.func, '__doc__', None)
def to_python(self, value_dict, state):
# Since we aren't really supposed to modify things in-place,
# we'll give the validation function a copy:
value_dict = value_dict.copy()
errors = self.func(value_dict, state, self)
if not errors:
return value_dict
if isinstance(errors, basestring):
raise Invalid(errors, value_dict, state)
elif isinstance(errors, dict):
raise Invalid(
format_compound_error(errors),
value_dict, state, error_dict=errors)
elif isinstance(errors, Invalid):
raise errors
else:
raise TypeError(
"Invalid error value: %r" % errors)
return value_dict
validate_partial = to_python
def decorate(cls, **kw):
def decorator(func):
return cls(func, **kw)
return decorator
decorate = classmethod(decorate)

View File

@@ -0,0 +1 @@
#

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
try:
import threading
except ImportError:
# No threads, so "thread local" means process-global
class local(object):
pass
else:
try:
local = threading.local
except AttributeError: # Python < 2.4
import thread
class local(object):
def __init__(self):
self.__dict__['__objs'] = {}
def __getattr__(self, attr, g=thread.get_ident):
try:
return self.__dict__['__objs'][g()][attr]
except KeyError:
raise AttributeError(
"No variable %s defined for the thread %s"
% (attr, g()))
def __setattr__(self, attr, value, g=thread.get_ident):
self.__dict__['__objs'].setdefault(g(), {})[attr] = value
def __delattr__(self, attr, g=thread.get_ident):
try:
del self.__dict__['__objs'][g()][attr]
except KeyError:
raise AttributeError(
"No variable %s defined for thread %s"
% (attr, g()))

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,152 @@
"""
Takes GET/POST variable dictionary, as might be returned by ``cgi``,
and turns them into lists and dictionaries.
Keys (variable names) can have subkeys, with a ``.`` and
can be numbered with ``-``, like ``a.b-3=something`` means that
the value ``a`` is a dictionary with a key ``b``, and ``b``
is a list, the third(-ish) element with the value ``something``.
Numbers are used to sort, missing numbers are ignored.
This doesn't deal with multiple keys, like in a query string of
``id=10&id=20``, which returns something like ``{'id': ['10',
'20']}``. That's left to someplace else to interpret. If you want to
represent lists in this model, you use indexes, and the lists are
explicitly ordered.
If you want to change the character that determines when to split for
a dict or list, both variable_decode and variable_encode take dict_char
and list_char keyword args. For example, to have the GET/POST variables,
``a_1=something`` as a list, you would use a list_char='_'.
"""
import api
__all__ = ['variable_decode', 'variable_encode', 'NestedVariables']
def variable_decode(d, dict_char='.', list_char='-'):
"""
Decode the flat dictionary d into a nested structure.
"""
result = {}
dicts_to_sort = {}
known_lengths = {}
for key, value in d.items():
keys = key.split(dict_char)
new_keys = []
was_repetition_count = False
for key in keys:
if key.endswith('--repetitions'):
key = key[:-len('--repetitions')]
new_keys.append(key)
known_lengths[tuple(new_keys)] = int(value)
was_repetition_count = True
break
elif list_char in key:
key, index = key.split(list_char)
new_keys.append(key)
dicts_to_sort[tuple(new_keys)] = 1
new_keys.append(int(index))
else:
new_keys.append(key)
if was_repetition_count:
continue
place = result
for i in range(len(new_keys)-1):
try:
if not isinstance(place[new_keys[i]], dict):
place[new_keys[i]] = {None: place[new_keys[i]]}
place = place[new_keys[i]]
except KeyError:
place[new_keys[i]] = {}
place = place[new_keys[i]]
if new_keys[-1] in place:
if isinstance(place[new_keys[-1]], dict):
place[new_keys[-1]][None] = value
elif isinstance(place[new_keys[-1]], list):
if isinstance(value, list):
place[new_keys[-1]].extend(value)
else:
place[new_keys[-1]].append(value)
else:
if isinstance(value, list):
place[new_keys[-1]] = [place[new_keys[-1]]]
place[new_keys[-1]].extend(value)
else:
place[new_keys[-1]] = [place[new_keys[-1]], value]
else:
place[new_keys[-1]] = value
try:
to_sort_keys = sorted(dicts_to_sort, key=len, reverse=True)
except NameError: # Python < 2.4
to_sort_keys = dicts_to_sort.keys()
to_sort_keys.sort(lambda a, b: -cmp(len(a), len(b)))
for key in to_sort_keys:
to_sort = result
source = None
last_key = None
for sub_key in key:
source = to_sort
last_key = sub_key
to_sort = to_sort[sub_key]
if None in to_sort:
noneVals = [(0, x) for x in to_sort.pop(None)]
noneVals.extend(to_sort.items())
to_sort = noneVals
else:
to_sort = to_sort.items()
to_sort.sort()
to_sort = [v for k, v in to_sort]
if key in known_lengths:
if len(to_sort) < known_lengths[key]:
to_sort.extend(['']*(known_lengths[key] - len(to_sort)))
source[last_key] = to_sort
return result
def variable_encode(d, prepend='', result=None, add_repetitions=True,
dict_char='.', list_char='-'):
"""
Encode a nested structure into a flat dictionary.
"""
if result is None:
result = {}
if isinstance(d, dict):
for key, value in d.items():
if key is None:
name = prepend
elif not prepend:
name = key
else:
name = "%s%s%s" % (prepend, dict_char, key)
variable_encode(value, name, result, add_repetitions,
dict_char=dict_char, list_char=list_char)
elif isinstance(d, list):
for i in range(len(d)):
variable_encode(d[i], "%s%s%i" % (prepend, list_char, i), result,
add_repetitions, dict_char=dict_char, list_char=list_char)
if add_repetitions:
if prepend:
repName = '%s--repetitions' % prepend
else:
repName = '__repetitions__'
result[repName] = str(len(d))
else:
result[prepend] = d
return result
class NestedVariables(api.FancyValidator):
def _to_python(self, value, state):
return variable_decode(value)
def _from_python(self, value, state):
return variable_encode(value)
def empty_value(self, value):
return {}