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:
7
python/packages/formencode/__init__.py
Normal file
7
python/packages/formencode/__init__.py
Normal 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
|
||||
494
python/packages/formencode/api.py
Normal file
494
python/packages/formencode/api.py
Normal 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
|
||||
|
||||
221
python/packages/formencode/compound.py
Normal file
221
python/packages/formencode/compound.py
Normal 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
|
||||
168
python/packages/formencode/context.py
Normal file
168
python/packages/formencode/context.py
Normal 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
|
||||
|
||||
232
python/packages/formencode/declarative.py
Normal file
232
python/packages/formencode/declarative.py
Normal 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__)
|
||||
|
||||
137
python/packages/formencode/doctest_xml_compare.py
Normal file
137
python/packages/formencode/doctest_xml_compare.py
Normal 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
|
||||
|
||||
13
python/packages/formencode/fieldstorage.py
Normal file
13
python/packages/formencode/fieldstorage.py
Normal 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
|
||||
151
python/packages/formencode/foreach.py
Normal file
151
python/packages/formencode/foreach.py
Normal 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]
|
||||
538
python/packages/formencode/htmlfill.py
Normal file
538
python/packages/formencode/htmlfill.py
Normal file
File diff suppressed because it is too large
Load Diff
118
python/packages/formencode/htmlfill_schemabuilder.py
Normal file
118
python/packages/formencode/htmlfill_schemabuilder.py
Normal 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)
|
||||
190
python/packages/formencode/htmlgen.py
Normal file
190
python/packages/formencode/htmlgen.py
Normal 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\"><Yahoo></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
|
||||
78
python/packages/formencode/htmlrename.py
Normal file
78
python/packages/formencode/htmlrename.py
Normal 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
|
||||
543
python/packages/formencode/i18n/FormEncode.pot
Normal file
543
python/packages/formencode/i18n/FormEncode.pot
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/cs/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/cs/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
547
python/packages/formencode/i18n/cs/LC_MESSAGES/FormEncode.po
Normal file
547
python/packages/formencode/i18n/cs/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/de/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/de/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
563
python/packages/formencode/i18n/de/LC_MESSAGES/FormEncode.po
Normal file
563
python/packages/formencode/i18n/de/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/el/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/el/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
564
python/packages/formencode/i18n/el/LC_MESSAGES/FormEncode.po
Normal file
564
python/packages/formencode/i18n/el/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/es/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/es/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
559
python/packages/formencode/i18n/es/LC_MESSAGES/FormEncode.po
Normal file
559
python/packages/formencode/i18n/es/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/et/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/et/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
552
python/packages/formencode/i18n/et/LC_MESSAGES/FormEncode.po
Normal file
552
python/packages/formencode/i18n/et/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/fi/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/fi/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
559
python/packages/formencode/i18n/fi/LC_MESSAGES/FormEncode.po
Normal file
559
python/packages/formencode/i18n/fi/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/fr/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/fr/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
570
python/packages/formencode/i18n/fr/LC_MESSAGES/FormEncode.po
Normal file
570
python/packages/formencode/i18n/fr/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/it/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/it/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
559
python/packages/formencode/i18n/it/LC_MESSAGES/FormEncode.po
Normal file
559
python/packages/formencode/i18n/it/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/ja/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/ja/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
544
python/packages/formencode/i18n/ja/LC_MESSAGES/FormEncode.po
Normal file
544
python/packages/formencode/i18n/ja/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/lt/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/lt/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
554
python/packages/formencode/i18n/lt/LC_MESSAGES/FormEncode.po
Normal file
554
python/packages/formencode/i18n/lt/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
203
python/packages/formencode/i18n/msgfmt.py
Executable file
203
python/packages/formencode/i18n/msgfmt.py
Executable 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()
|
||||
BIN
python/packages/formencode/i18n/nb_NO/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/nb_NO/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
551
python/packages/formencode/i18n/nb_NO/LC_MESSAGES/FormEncode.po
Normal file
551
python/packages/formencode/i18n/nb_NO/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/nl/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/nl/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
561
python/packages/formencode/i18n/nl/LC_MESSAGES/FormEncode.po
Normal file
561
python/packages/formencode/i18n/nl/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/pl/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/pl/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
556
python/packages/formencode/i18n/pl/LC_MESSAGES/FormEncode.po
Normal file
556
python/packages/formencode/i18n/pl/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
556
python/packages/formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.po
Normal file
556
python/packages/formencode/i18n/pt_BR/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
557
python/packages/formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.po
Normal file
557
python/packages/formencode/i18n/pt_PT/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/ru/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/ru/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
555
python/packages/formencode/i18n/ru/LC_MESSAGES/FormEncode.po
Normal file
555
python/packages/formencode/i18n/ru/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/sk/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/sk/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
554
python/packages/formencode/i18n/sk/LC_MESSAGES/FormEncode.po
Normal file
554
python/packages/formencode/i18n/sk/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/sl/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/sl/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
556
python/packages/formencode/i18n/sl/LC_MESSAGES/FormEncode.po
Normal file
556
python/packages/formencode/i18n/sl/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/tr/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/tr/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
559
python/packages/formencode/i18n/tr/LC_MESSAGES/FormEncode.po
Normal file
559
python/packages/formencode/i18n/tr/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/zh_CN/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/zh_CN/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
547
python/packages/formencode/i18n/zh_CN/LC_MESSAGES/FormEncode.po
Normal file
547
python/packages/formencode/i18n/zh_CN/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
python/packages/formencode/i18n/zh_TW/LC_MESSAGES/FormEncode.mo
Normal file
BIN
python/packages/formencode/i18n/zh_TW/LC_MESSAGES/FormEncode.mo
Normal file
Binary file not shown.
547
python/packages/formencode/i18n/zh_TW/LC_MESSAGES/FormEncode.po
Normal file
547
python/packages/formencode/i18n/zh_TW/LC_MESSAGES/FormEncode.po
Normal file
File diff suppressed because it is too large
Load Diff
71
python/packages/formencode/interfaces.py
Normal file
71
python/packages/formencode/interfaces.py
Normal 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')
|
||||
61
python/packages/formencode/javascript/ordering.js
Normal file
61
python/packages/formencode/javascript/ordering.js
Normal 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);
|
||||
}
|
||||
752
python/packages/formencode/national.py
Normal file
752
python/packages/formencode/national.py
Normal file
File diff suppressed because it is too large
Load Diff
161
python/packages/formencode/rewritingparser.py
Normal file
161
python/packages/formencode/rewritingparser.py
Normal 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
|
||||
467
python/packages/formencode/schema.py
Normal file
467
python/packages/formencode/schema.py
Normal 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)
|
||||
1
python/packages/formencode/util/__init__.py
Normal file
1
python/packages/formencode/util/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
#
|
||||
2665
python/packages/formencode/util/doctest24.py
Normal file
2665
python/packages/formencode/util/doctest24.py
Normal file
File diff suppressed because it is too large
Load Diff
38
python/packages/formencode/util/threadinglocal.py
Normal file
38
python/packages/formencode/util/threadinglocal.py
Normal 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()))
|
||||
|
||||
2947
python/packages/formencode/validators.py
Normal file
2947
python/packages/formencode/validators.py
Normal file
File diff suppressed because it is too large
Load Diff
152
python/packages/formencode/variabledecode.py
Normal file
152
python/packages/formencode/variabledecode.py
Normal 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 {}
|
||||
Reference in New Issue
Block a user