Author: ianb
Date: 2008-09-16 13:47:05 -0600 (Tue, 16 Sep 2008)
New Revision: 3590

Modified:
   FormEncode/trunk/formencode/htmlfill.py
   FormEncode/trunk/formencode/validators.py
   FormEncode/trunk/tests/test_validators.py
Log:
typo in 'ignore'

Modified: FormEncode/trunk/formencode/htmlfill.py
===================================================================
--- FormEncode/trunk/formencode/htmlfill.py     2008-09-16 18:53:55 UTC (rev 
3589)
+++ FormEncode/trunk/formencode/htmlfill.py     2008-09-16 19:47:05 UTC (rev 
3590)
@@ -502,5 +502,5 @@
                           'none': none_formatter,
                           'escape': escape_formatter,
                           'escapenl': escapenl_formatter,
-                          'ingore': ignore_formatter,
+                          'ignore': ignore_formatter,
                           }

Modified: FormEncode/trunk/formencode/validators.py
===================================================================
--- FormEncode/trunk/formencode/validators.py   2008-09-16 18:53:55 UTC (rev 
3589)
+++ FormEncode/trunk/formencode/validators.py   2008-09-16 19:47:05 UTC (rev 
3590)
@@ -1515,6 +1515,214 @@
                     self.message('status', state, status=res.status),
                     state, url)
 
+    
+class XRI(FancyValidator):
+    r"""
+    Validator for XRIs.
+    
+    It supports both i-names and i-numbers, of the first version of the XRI
+    standard.
+    
+    ::
+        
+        >>> inames = XRI(xri_type="i-name")
+        >>> inames.to_python("   =John.Smith ")
+        '=John.Smith'
+        >>> inames.to_python("@Free.Software.Foundation")
+        '@Free.Software.Foundation'
+        >>> inames.to_python("Python.Software.Foundation")
+        Traceback (most recent call last):
+            ...
+        Invalid: The type of i-name is not defined; it may be either 
individual or organizational
+        >>> inames.to_python("http://example.org";)
+        Traceback (most recent call last):
+            ...
+        Invalid: The type of i-name is not defined; it may be either 
individual or organizational
+        >>> inames.to_python("=!2C43.1A9F.B6F6.E8E6")
+        Traceback (most recent call last):
+            ...
+        Invalid: "!2C43.1A9F.B6F6.E8E6" is an invalid i-name
+        >>> iname_with_schema = XRI(True, xri_type="i-name")
+        >>> iname_with_schema.to_python("=Richard.Stallman")
+        'xri://=Richard.Stallman'
+        >>> inames.to_python("=John Smith")
+        Traceback (most recent call last):
+            ...
+        formencode.api.Invalid: "John Smith" is an invalid i-name
+        >>> inumbers = XRI(xri_type="i-number")
+        >>> inumbers.to_python("!!1000!de21.4536.2cb2.8074")
+        '!!1000!de21.4536.2cb2.8074'
+        >>> inumbers.to_python("@!1000.9554.fabd.129c!2847.df3c")
+        '@!1000.9554.fabd.129c!2847.df3c'
+    
+    """
+    
+    iname_valid_pattern = re.compile(r"""
+    ^
+    [\w]+                  # A global alphanumeric i-name
+    (\.[\w]+)*             # An i-name with dots
+    (\*[\w]+(\.[\w]+)*)*   # A community i-name
+    $
+    """, re.VERBOSE|re.UNICODE)
+    
+    
+    iname_invalid_start = re.compile(r"^[\d\.-]", re.UNICODE)
+    """@cvar: These characters must not be at the beggining of the i-name"""
+    
+    inumber_pattern = re.compile(r"""
+    ^
+    (
+    [EMAIL PROTECTED]       # It's a personal or organization i-number
+    |
+    !!          # It's a network i-number
+    )
+    [\dA-F]{1,4}(\.[\dA-F]{1,4}){0,3}       # A global i-number
+    (![\dA-F]{1,4}(\.[\dA-F]{1,4}){0,3})*   # Zero or more sub i-numbers
+    $
+    """, re.VERBOSE|re.IGNORECASE)
+    
+    messages = {
+        'noType': _("The type of i-name is not defined; it may be either 
individual or organizational"),
+        'repeatedChar': _("Dots and dashes may not be repeated consecutively"),
+        'badIname': _('"%(iname)s" is an invalid i-name'),
+        'badInameStart': _("i-names may not start with numbers nor punctuation 
"
+                           "marks"),
+        'badInumber': _('"%(inumber)s" is an invalid i-number'),
+        'badType': _("The XRI must be a string (not a %(type)s: %(value)r)"),
+        'badXri': _('"%(xri_type)s" is not a valid type of XRI')
+        }
+    
+    def __init__(self, add_xri=False, xri_type="i-name", **kwargs):
+        """Create an XRI validator.
+        
+        @param add_xri: Should the schema be added if not present? Officially
+            it's optional.
+        @type add_xri: C{bool}
+        @param xri_type: What type of XRI should be validated? Possible values:
+            C{i-name} or C{i-number}.
+        @type xri_type: C{str}
+        
+        """
+        self.add_xri = add_xri
+        assert xri_type in ('i-name', 'i-number'), \
+                           ('xri_type must be "i-name" or "i-number"')
+        self.xri_type = xri_type
+        super(XRI, self).__init__(**kwargs)
+    
+    def _to_python(self, value, state):
+        """Prepend the 'xri://' schema if necessary and then remove trailing
+        spaces"""
+        value = value.strip()
+        if self.add_xri and not value.startswith("xri://"):
+            value = "xri://" + value
+        return value
+    
+    def validate_python(self, value, state=None):
+        """Validate an XRI
+        
+        @raise Invalid: If at least one of the following conditions in met:
+            - C{value} is not a string.
+            - The XRI is not a personal, organizational or network one.
+            - The relevant validator (i-name or i-number) considers the XRI
+                is not valid.
+        
+        """
+        if not (isinstance(value, str) or isinstance(value, unicode)):
+            raise Invalid(self.message("badType", state, type=str(type(value)),
+                                       value=value),
+                          value, state)
+        
+        # Let's remove the schema, if any
+        if value.startswith("xri://"):
+            value = value[6:]
+        
+        if not value[0] in ('@', '=') and not (self.xri_type == "i-number" \
+        and value[0] == '!'):
+            raise Invalid(self.message("noType", state), value, state)
+        
+        if self.xri_type == "i-name":
+            self._validate_iname(value, state)
+        else:
+            self._validate_inumber(value, state)
+    
+    def _validate_iname(self, iname, state):
+        """Validate an i-name"""
+        # The type is not required here:
+        iname = iname[1:]
+        if ".." in iname or "--" in iname:
+            raise Invalid(self.message("repeatedChar", state), iname, state)
+        if self.iname_invalid_start.match(iname):
+            raise Invalid(self.message("badInameStart", state), iname, state)
+        if not self.iname_valid_pattern.match(iname) or "_" in iname:
+            raise Invalid(self.message("badIname", state, iname=iname), iname,
+                          state)
+    
+    def _validate_inumber(self, inumber, state):
+        """Validate an i-number"""
+        if not self.__class__.inumber_pattern.match(inumber):
+            raise Invalid(self.message("badInumber", state, inumber=inumber,
+                                       value=inumber),
+                          inumber, state)
+
+
+class OpenId(FancyValidator):
+    r"""
+    OpenId validator.
+    
+    ::
+        >>> v = OpenId(add_schema=True)
+        >>> v.to_python(' example.net ')
+        'http://example.net'
+        >>> v.to_python('@TurboGears')
+        'xri://@TurboGears'
+        >>> w = OpenId(add_schema=False)
+        >>> w.to_python(' example.net ')
+        Traceback (most recent call last):
+        ...
+        Invalid: "example.net" is not a valid OpenId (it is neither an URL nor 
an XRI)
+        >>> w.to_python('!!1000')
+        '!!1000'
+        >>> w.to_python('[EMAIL PROTECTED]')
+        Traceback (most recent call last):
+        ...
+        Invalid: "[EMAIL PROTECTED]" is not a valid OpenId (it is neither an 
URL nor an XRI)
+    
+    """
+    
+    messages = {
+        'badId': _('"%(id)s" is not a valid OpenId (it is neither an URL nor 
an XRI)')
+        }
+    
+    def __init__(self, add_schema=False, **kwargs):
+        """Create an OpenId validator.
+        
+        @param add_schema: Should the schema be added if not present?
+        @type add_schema: C{bool}
+        
+        """
+        self.url_validator = URL(add_http=add_schema)
+        self.iname_validator = XRI(add_schema, xri_type="i-name")
+        self.inumber_validator = XRI(add_schema, xri_type="i-number")
+    
+    def _to_python(self, value, state):
+        value = value.strip()
+        try:
+            return self.url_validator.to_python(value, state)
+        except Invalid:
+            try:
+                return self.iname_validator.to_python(value, state)
+            except Invalid:
+                try:
+                    return self.inumber_validator.to_python(value, state)
+                except Invalid:
+                    pass
+        # It's not an OpenId!
+        raise Invalid(self.message("badId", state, id=value), value, state)
+    
+    def validate_python(self, value, state):
+        self._to_python(value, state)
+
+
 def StateProvince(*kw, **kwargs):
     warnings.warn("please use formencode.national.USStateProvince", 
DeprecationWarning, stacklevel=2)
     from formencode.national import USStateProvince

Modified: FormEncode/trunk/tests/test_validators.py
===================================================================
--- FormEncode/trunk/tests/test_validators.py   2008-09-16 18:53:55 UTC (rev 
3589)
+++ FormEncode/trunk/tests/test_validators.py   2008-09-16 19:47:05 UTC (rev 
3590)
@@ -1,4 +1,8 @@
-from formencode.validators import String, UnicodeString, Invalid, Int
+# -*- coding: utf-8 -*-
+import unittest
+
+from formencode.validators import String, UnicodeString, Invalid, Int, XRI, \
+                                  OpenId
 from formencode.validators import DateConverter
 from formencode.variabledecode import NestedVariables
 from formencode.schema import Schema
@@ -127,3 +131,171 @@
     else:
         raise Exception("Shouldn't be valid data", values, start_values)
 
+
+
+
+
+class TestXRIValidator(unittest.TestCase):
+    """Generic tests for the XRI validator"""
+    
+    def test_creation_valid_params(self):
+        """The creation of an XRI validator with valid parameters must
+        succeed"""
+        XRI()
+        XRI(True, "i-name")
+        XRI(True, "i-number")
+        XRI(False, "i-name")
+        XRI(False, "i-number")
+    
+    def test_creation_invalid_xri(self):
+        """Only "i-name" and "i-number" are valid XRIs"""
+        self.assertRaises(AssertionError, XRI, True, 'i-something')
+    
+    def test_valid_simple_individual_iname_without_type(self):
+        """XRIs must start with either an equals sign or an at sign"""
+        validator = XRI(True, "i-name")
+        self.assertRaises(Invalid, validator.validate_python, 'Gustavo')
+    
+    def test_valid_iname_with_schema(self):
+        """XRIs may have their schema in the beggining"""
+        validator = XRI()
+        self.assertEqual(validator.to_python('xri://=Gustavo'),
+                         'xri://=Gustavo')
+    
+    def test_schema_is_added_if_asked(self):
+        """The schema must be added to an XRI if explicitly asked"""
+        validator = XRI(True)
+        self.assertEqual(validator.to_python('=Gustavo'),
+                         'xri://=Gustavo')
+    
+    def test_schema_not_added_if_not_asked(self):
+        """The schema must not be added to an XRI unless explicitly asked"""
+        validator = XRI()
+        self.assertEqual(validator.to_python('=Gustavo'), '=Gustavo')
+    
+    def test_spaces_are_trimmed(self):
+        """Spaces at the beggining or end of the XRI are removed"""
+        validator = XRI()
+        self.assertEqual(validator.to_python('    =Gustavo  '), '=Gustavo')
+
+
+class TestINameValidator(unittest.TestCase):
+    """Tests for the XRI i-names validator"""
+    
+    def setUp(self):
+        self.validator = XRI(xri_type="i-name")
+    
+    def test_valid_global_individual_iname(self):
+        """Global & valid individual i-names must pass validation"""
+        self.validator.validate_python('=Gustavo')
+    
+    def test_valid_global_organizational_iname(self):
+        """Global & valid organizational i-names must pass validation"""
+        self.validator.validate_python('@Canonical')
+    
+    def test_invalid_iname(self):
+        """Non-string i-names are rejected"""
+        self.assertRaises(Invalid, self.validator.validate_python, None)
+    
+    def test_exclamation_in_inames(self):
+        """Exclamation marks at the beggining of XRIs is something specific
+        to i-numbers and must be rejected in i-names"""
+        self.assertRaises(Invalid, self.validator.validate_python,
+                          "!!1000!de21.4536.2cb2.8074")
+    
+    def test_repeated_characters(self):
+        """Dots and dashes must not be consecutively repeated in i-names"""
+        self.assertRaises(Invalid, self.validator.validate_python,
+                          "=Gustavo--Narea")
+        self.assertRaises(Invalid, self.validator.validate_python,
+                          "=Gustavo..Narea")
+    
+    def test_punctuation_marks_at_beggining(self):
+        """Punctuation marks at the beggining of i-names are forbidden"""
+        self.assertRaises(Invalid, self.validator.validate_python,
+                          "=.Gustavo")
+        self.assertRaises(Invalid, self.validator.validate_python,
+                          "=-Gustavo.Narea")
+    
+    def test_numerals_at_beggining(self):
+        """Numerals at the beggining of i-names are forbidden"""
+        self.assertRaises(Invalid, self.validator.validate_python,
+                          "=1Gustavo")
+    
+    def test_non_english_inames(self):
+        """i-names with non-English characters are valid"""
+        self.validator.validate_python(u'=Gustavo.Narea.García')
+        self.validator.validate_python(u'@名前.例')
+    
+    def test_inames_plus_paths(self):
+        """i-names with paths are valid but not supported"""
+        self.assertRaises(Invalid, self.validator.validate_python,
+                          "=Gustavo/(+email)")
+    
+    def test_communities(self):
+        """i-names may have so-called 'communities'"""
+        self.validator.validate_python(u'=María*Yolanda*Liliana*Gustavo')
+        self.validator.validate_python(u'=Gustavo*Andreina')
+        self.validator.validate_python(u'@IBM*Lenovo')
+        
+        
+class TestINumberValidator(unittest.TestCase):
+    """Tests for the XRI i-number validator"""
+    
+    def setUp(self):
+        self.validator = XRI(xri_type="i-number")
+    
+    def test_valid_global_personal_inumber(self):
+        """Global & valid personal i-numbers must pass validation"""
+        self.validator.validate_python('=!1000.a1b2.93d2.8c73')
+    
+    def test_valid_global_organizational_inumber(self):
+        """Global & valid organizational i-numbers must pass validation"""
+        self.validator.validate_python('@!1000.a1b2.93d2.8c73')
+    
+    def test_valid_global_network_inumber(self):
+        """Global & valid network i-numbers must pass validation"""
+        self.validator.validate_python('!!1000')
+    
+    def test_valid_community_personal_inumbers(self):
+        """Community & valid personal i-numbers must pass validation"""
+        self.validator.validate_python('=!1000.a1b2.93d2.8c73!3ae2!1490')
+    
+    def test_valid_community_organizational_inumber(self):
+        """Community & valid organizational i-numbers must pass validation"""
+        self.validator.validate_python('@!1000.9554.fabd.129c!2847.df3c')
+    
+    def test_valid_community_network_inumber(self):
+        """Community & valid network i-numbers must pass validation"""
+        self.validator.validate_python('!!1000!de21.4536.2cb2.8074')
+
+
+class TestOpenIdValidator(unittest.TestCase):
+    """Tests for the OpenId validator"""
+    
+    def setUp(self):
+        self.validator = OpenId(add_schema=False)
+    
+    def test_url(self):
+        self.assertEqual(self.validator.to_python('http://example.org'),
+                         'http://example.org')
+    
+    def test_iname(self):
+        self.assertEqual(self.validator.to_python('=Gustavo'), '=Gustavo')
+    
+    def test_inumber(self):
+        self.assertEqual(self.validator.to_python('!!1000'), '!!1000')
+    
+    def test_email(self):
+        """Email addresses are not valid OpenIds!"""
+        self.assertRaises(Invalid, self.validator.to_python,
+                          "[EMAIL PROTECTED]")
+    
+    def test_prepending_schema(self):
+        validator = OpenId(add_schema=True)
+        self.assertEqual(validator.to_python("example.org"),
+                         "http://example.org";)
+        self.assertEqual(validator.to_python("=Gustavo"),
+                         "xri://=Gustavo")
+        self.assertEqual(validator.to_python("!!1000"),
+                         "xri://!!1000")


-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
FormEncode-CVS mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/formencode-cvs

Reply via email to