What is z3c.schema?

z3c.schema provides additional Zope 3 schema fields.

URLs

BaseURL

A base url field is useful if you need to append view names to a url and doesn’t like to check every time if the url ends with a backslash before you append the view name.

Let’s first create the BaseURL field:

>>> from z3c.schema.baseurl import BaseURL
>>> baseURL = BaseURL()

Let’s first check the validation of some common base urls.

>>> baseURL.validate("http://www.python.org/")
>>> baseURL.validate("http://www.python.org/foo/")
>>> baseURL.validate("http://123.123.123.123/")
>>> baseURL.validate("http://123.123.123.123/foo/")

A port specification is also allowed:

>>> baseURL.validate("http://www.python.org:389/")
>>> baseURL.validate("http://www.python.org:389/foo/")

However, a missing backslash is not permitted:

>>> baseURL.validate("http://www.python.org/foo")
Traceback (most recent call last):
...
InvalidBaseURL: http://www.python.org/foo

And a missing protocol is also not permitted:

>>> baseURL.validate("www.python.org/foo/")
Traceback (most recent call last):
...
InvalidBaseURL: www.python.org/foo/

Let’s check some other invalid forms:

>>> baseURL.validate("$www.python.org/")
Traceback (most recent call last):
...
InvalidBaseURL: $www.python.org/
>>> baseURL.validate("333.123.123.123/")
Traceback (most recent call last):
...
InvalidBaseURL: 333.123.123.123/

Let’s also ensure that we can convert to base urls from unicode:

>>> baseURL.fromUnicode("http://www.python.org:389/")
'http://www.python.org:389/'
>>> baseURL.fromUnicode("          http://www.python.org:389/")
'http://www.python.org:389/'
>>> baseURL.fromUnicode("      \n    http://www.python.org:389/\n")
'http://www.python.org:389/'
>>> baseURL.fromUnicode("http://www.pyt hon.org:389/")
Traceback (most recent call last):
...
InvalidBaseURL: http://www.pyt hon.org:389/

Reference

interface z3c.schema.baseurl.interfaces.IBaseURL[source]

Extends: zope.schema.interfaces.IURI, zope.schema._bootstrapinterfaces.IFromUnicode

Base URL field.

Such a base url must end with a /. This makes it simpler for append a view name.

exception z3c.schema.baseurl.interfaces.InvalidBaseURL[source]

Bases: zope.schema._bootstrapinterfaces.ValidationError

The specified base URL is not valid.

z3c.schema.baseurl.field.isValidBaseURL()

Determine whether the URL is a base URL.

class z3c.schema.baseurl.field.BaseURL(min_length=0, max_length=None, **kw)[source]

Bases: zope.schema._field.URI

Base URL field.

Such a base url must end with a /. This makes it simpler for append a view name.

Date Selection

The date selection field was designed to support the UI requirement of allowing users to select a date using multi-select input fields. The DateSelect field extends the zope.schema.Date field merely by a few additional attributes.

>>> from z3c.schema.dateselect import field

the first attribute is the range of years that will be offered to the user:

>>> birthday = field.DateSelect(
...     title=u'Birthday',
...     yearRange=list(range(1920, 2007)))

In this case the user will be offered all years from 1920 to 2007.

>>> birthday.yearRange
[1920, ..., 2006]

The second attribute allows you to specify an initial date for the selection:

>>> import datetime
>>> birthday = field.DateSelect(
...     title=u'Birthday',
...     yearRange=range(1920, 2007),
...     initialDate=datetime.date(2000, 1, 1))
>>> birthday.initialDate
datetime.date(2000, 1, 1)

And this is really it. Please read the documentation on the Date for more information.

Reference

class z3c.schema.dateselect.IDateSelect[source]

Bases: zope.schema.interfaces.IDate

class z3c.schema.dateselect.DateSelect(yearRange=None, initialDate=None, **kw)[source]

Bases: zope.schema._field.Date

RFC 822 Mail Address

Let’s first generate an E-mail field:

>>> from z3c.schema.email import RFC822MailAddress
>>> email = RFC822MailAddress()

Check that the constraints of the value are fulfilled:

>>> email.constraint('foo\n')
False
>>> email.constraint('foo\r')
False
>>> email.constraint('foo')
True

Now make sure the E-mail addresses validate:

>>> email.validate(10)
Traceback (most recent call last):
...
WrongType: (10, <type 'unicode'>, '')
>>> email.validate(u'foo@bar.')
Traceback (most recent call last):
...
NotValidRFC822MailAdress: foo@bar.
>>> email.validate(u'foo@bar.com')

Since the field uses a simple function to validate its E-mail fields, it is easier to use it for the tests:

>>> from z3c.schema.email import isValidMailAddress
>>> isValidMailAddress(u'foo@bar.com')
True
>>> isValidMailAddress(u'foo.blah@bar.com')
True
  • Name failures

    >>> isValidMailAddress(u'foo\r@bar.com')
    False
    >>> isValidMailAddress(u'foo<@bar.com')
    False
    >>> isValidMailAddress(u'foo:@bar.com')
    False
    
  • Overall failures

    >>> isValidMailAddress(u'')
    False
    >>> isValidMailAddress(u'foo.')
    False
    >>> isValidMailAddress(u'foo.@bar.com')
    False
    >>> isValidMailAddress(u'.foo@bar.com')
    False
    >>> isValidMailAddress(u'foo@bar.com.')
    False
    
  • Domain failures

    >>> isValidMailAddress(u'foo@')
    False
    >>> isValidMailAddress(u'foo@bar.')
    False
    >>> isValidMailAddress(u'foo@bar')
    False
    >>> isValidMailAddress(u'foo@bar..com')
    False
    >>> isValidMailAddress(u'foo@bar\r.com')
    False
    >>> isValidMailAddress(u'foo@bar<.com')
    False
    >>> isValidMailAddress(u'foo@bar:.com')
    False
    

Reference

interface z3c.schema.email.interfaces.IRFC822MailAddress[source]

Extends: zope.schema.interfaces.ITextLine

A valid RFC822 email address field.

exception z3c.schema.email.interfaces.NotValidRFC822MailAdress[source]

Bases: zope.schema._bootstrapinterfaces.ValidationError

Not a valid email address

z3c.schema.email.field.isValidMailAddress(addr)[source]

Returns True if the email address is valid and False if not.

class z3c.schema.email.field.RFC822MailAddress(*args, **kw)[source]

Bases: zope.schema._bootstrapfields.TextLine

A valid email address.

Hostname

Let’s first create the hostname field:

>>> from z3c.schema.hostname import HostName
>>> hostname = HostName()

Let’s first check the validation of some common hostnames. Hostnames can be either domain or IP addresses.

>>> hostname.validate("www.python.org")
>>> hostname.validate("123.123.123.123")

A port specification is also allowed:

>>> hostname.validate("www.python.org:389")

However, the protocol is not permitted:

>>> hostname.validate("http://www.python.org")
Traceback (most recent call last):
...
InvalidHostName: http://www.python.org
>>> hostname.validate("ldap://www.python.org/foo")
Traceback (most recent call last):
...
InvalidHostName: ldap://www.python.org/foo

Let’s check some other invalid forms:

>>> hostname.validate("$www.python.org")
Traceback (most recent call last):
...
InvalidHostName: $www.python.org
>>> hostname.validate("333.123.123.123")
Traceback (most recent call last):
...
InvalidHostName: 333.123.123.123

Let’s also ensure that we can convert to hostnames from unicode:

>>> hostname.fromUnicode("www.python.org:389")
'www.python.org:389'
>>> hostname.fromUnicode("          www.python.org:389")
'www.python.org:389'
>>> hostname.fromUnicode("      \n    www.python.org:389\n")
'www.python.org:389'
>>> hostname.fromUnicode("www.pyt hon.org:389")
Traceback (most recent call last):
...
InvalidHostName: www.pyt hon.org:389

Reference

interface z3c.schema.hostname.interfaces.IHostName[source]

Extends: zope.schema.interfaces.IURI, zope.schema._bootstrapinterfaces.IFromUnicode

Host name field.

exception z3c.schema.hostname.interfaces.InvalidHostName[source]

Bases: zope.schema._bootstrapinterfaces.ValidationError

The specified host name is not valid.

z3c.schema.hostname.field.isValidHostName()

Check for a valid hostname.

class z3c.schema.hostname.field.HostName(min_length=0, max_length=None, **kw)[source]

Bases: zope.schema._field.URI

HostName schema field.

This is a IP Address or a host name.

IP Addresses

This module provides a field for IP addresses. Let’s first generate an IP field:

>>> from z3c.schema import ip
>>> myip = ip.IPAddress()

Now make sure the IP addresses validate:

>>> myip.validate(10)
Traceback (most recent call last):
...
WrongType: (10, <type 'str'>, '')
>>> myip.validate('12.123.231.wee')
Traceback (most recent call last):
...
NotValidIPAdress: 12.123.231.wee
>>> myip.validate('10.0.0.1')

Since the field uses a simple function to validate its IP addresses, it is easier to use it for the tests:

>>> from z3c.schema.ip import isValidIPAddress
>>> isValidIPAddress('0.0.0.0')
True
>>> isValidIPAddress('255.255.255.255')
True
  • Number of pieces failures

    >>> isValidIPAddress('12.3.1')
    False
    >>> isValidIPAddress('1.0.0.0.0')
    False
    >>> isValidIPAddress('1.0.0.0.')
    False
    
  • Not integers failures

    >>> isValidIPAddress('x.0.0.0')
    False
    >>> isValidIPAddress('0x8.0.0.0')
    False
    
  • Not in range failures

    >>> isValidIPAddress('-1.0.0.0')
    False
    >>> isValidIPAddress('256.0.0.0')
    False
    >>> isValidIPAddress('1.-1.256.0')
    False
    

Reference

IP Address Field Interfaces

interface z3c.schema.ip.interfaces.IIPAddress[source]

Extends: zope.schema.interfaces.IBytesLine

A valid IP address field.

exception z3c.schema.ip.interfaces.NotValidIPAdress[source]

Bases: zope.schema._bootstrapinterfaces.ValidationError

Not a valid IP address.

z3c.schema.ip.field.isValidIPAddress(addr)[source]

Returns True if the IP address is valid and False if not.

class z3c.schema.ip.field.IPAddress(min_length=0, max_length=None, **kw)[source]

Bases: zope.schema._field.BytesLine

A valid IP address.

Optional Choices

The optional choice field is desiged to offer a set of default choices or allow, optionally, to enter a custom value. The custom value has to conform to a specified field.

Here is an example of creating such a field:

>>> import zope.schema
>>> from z3c.schema.optchoice import OptionalChoice
>>> optchoice = OptionalChoice(
...     title=u'Occupation',
...     values=(u'Programmer', u'Designer', u'Project Manager'),
...     value_type=zope.schema.TextLine())

Note that the value type must be a field:

>>> OptionalChoice(
...     title=u'Occupation',
...     values=(u'Programmer', u'Designer', u'Project Manager'),
...     value_type=object())
Traceback (most recent call last):
ValueError: 'value_type' must be field instance.

Let’s now ensure that we can validate not only choices, but also custom values:

>>> optchoice.validate(u'Programmer')
>>> optchoice.validate(u'Project Manager')
>>> optchoice.validate(u'Scripter')
>>> optchoice.validate(u'Scripter\nHTML\n')
Traceback (most recent call last):
...
ConstraintNotSatisfied: Scripter
HTML

Let’s now ensure that we can convert values from unicode to a real value as well. To demonstrate this feature, we have to create a more restrictive optional choice field:

>>> optchoice = OptionalChoice(
...     title=u'Age',
...     values=(10, 20, 30, 40, 50),
...     value_type=zope.schema.Int(min=0))
>>> optchoice.fromUnicode(u'10')
10
>>> optchoice.fromUnicode(u'40')
40
>>> optchoice.fromUnicode(u'45')
45
>>> optchoice.fromUnicode(u'-10')
Traceback (most recent call last):
...
TooSmall: (-10, 0)

Reference

interface z3c.schema.optchoice.interfaces.IOptionalChoice[source]

Extends: zope.schema.interfaces.IChoice, zope.schema._bootstrapinterfaces.IFromUnicode

Optional Choice

This field can either represent a choice or a textline value.

Validation proceeds as follows:

  1. Check whether the value is one of the choices. If so, return successfully.
  2. If no choice match was found, validate the value against the value_type field.

The conversion from unicode values proceeds in a similar fashion:

  1. Try to match the unicode value to a token in the vocabulary.
  2. If no match was made, call the unicode conversion method of the value_type field.
value_type

Value Type

The freely entered values must be of this type.

class z3c.schema.optchoice.field.OptionalChoice(value_type, **kw)[source]

Bases: zope.schema._field.Choice

Optional Choice field.

Payment Data (Credit Cards)

z3c.schema.payments provides some level of error detection in payment data prior to storing the information or sending it to a payment processor. Currently this module only supports validation of credit card numbers, but this could conceivably be extended to other payment forms

Credit Cards

Credit card numbering specifications are defined in ISO 7812-1:1983. Verifying that the credit card number supplied by a user conforms to the ISO standard provides some error checking which can catch typographical errors, transposition, etc. This does not validate the card against the financial networks as a valid account. However, verifying that the card number is well formed is fast and catching typographical errors in this way is much faster than sending the card number to a credit card processor.

First, let’s setup a credit card field:

>>> from z3c.schema.payments import CreditCard
>>> from z3c.schema.payments import interfaces
>>> cc = CreditCard()
>>> interfaces.IISO7812CreditCard.providedBy(cc)
True

The simple restrictions are quick to check. Credit cards should be all numeric, no alpha characters allowed:

>>> cc.constraint('44444444444AAAA8')
False
>>> cc.constraint('4444444444444448')
True

Also, we can’t have any returns or line endings in the number:

>>> cc.constraint('444444444444\n4448')
False
>>> cc.constraint('44444444\r44444448')
False

One of the first specifications of ISO 7812 is a “Major Industry Identifier,” which is the first number of an ISO 7812 compliant account number. Originally, banking, financial, and merchandizing (store account) cards were limited to the major industry identifiers 4, 5, and 6. However American Express, Diner’s Club, and Carte Blanche were all assigned a major industry number 3. So a valid card must start with one of these numbers:

>>> cc.validate(u'0000000000000000')
Traceback (most recent call last):
...
NotValidISO7812CreditCard: 0000000000000000
>>> cc.validate(u'1111111111111117')
Traceback (most recent call last):
...
NotValidISO7812CreditCard: 1111111111111117
>>> cc.validate(u'2222222222222224')
Traceback (most recent call last):
...
NotValidISO7812CreditCard: 2222222222222224
>>> cc.validate(u'3333333333333331')
>>> cc.validate(u'4111111111111111')
>>> cc.validate(u'5555555555555557')
>>> cc.validate(u'3333333333333331')
>>> cc.validate(u'6666666666666664')
>>> cc.validate(u'7777777777777771')
Traceback (most recent call last):
...
NotValidISO7812CreditCard: 7777777777777771
>>> cc.validate(u'8888888888888888')
Traceback (most recent call last):
...
NotValidISO7812CreditCard: 8888888888888888
>>> cc.validate(u'9999999999999995')
Traceback (most recent call last):
...
NotValidISO7812CreditCard: 9999999999999995

The ISO specification also defines a check digit which should always be the last digit of a card number. The check digit is calculated using the Luhn (Mod 10) formula. In this way, each credit card number contains its own CRC of sorts. This is our main validation that a credit card number is well formed.

Validating a number with a check digit that uses the LUHN formula:

Step 1: Starting with the next-to-last digit and moving left, double the value of every other digit. The calculation starts with the next-to-last digit because the last digit is the check digit.

  • When selecting every other digit, always work right-to-left and do not start with the rightmost digit (since that is the check digit).
  • The last digit (check digit) is considered #1 (odd number) and the next-to-last digit is #2 (even number). You will only double the values of the even-numbered digits.

Step 2: Add all unaffected digits to the values obtained in Step 1.

  • If any of the values resulting from Step 1 are double-digits, do not add the double-digit value to the total, but rather add the two digits, and add this sum to the total.

Result: The total obtained in Step 2 must be a number ending in zero (exactly divisible by 10) for the number to be valid.

The validate method of z3c.schema.payments.ISO7812CreditCard does the Luhn calculation on the provided card number. If the calculation fails, there is either an error in the number or the card is simply not valid. We use in our tests here and above numbers that technically meet the criteria of the ISO specification without the risk of the number actually being a valid card number registered with a financial institution:

>>> cc.validate(u'4444444444444448')
>>> cc.validate(u'4444444444444449')
Traceback (most recent call last):
...
NotValidISO7812CreditCard: 4444444444444449

Reference

interface z3c.schema.payments.interfaces.IISO7812CreditCard[source]

Extends: zope.schema.interfaces.ITextLine

A credit card with a valid check digit

exception z3c.schema.payments.interfaces.NotValidISO7812CreditCard[source]

Bases: zope.schema._bootstrapinterfaces.ValidationError

The credit card number is incorrect.

z3c.schema.payments.field.isValidCreditCard(cardNum)[source]

Returns True if the credit card number is a valid Luhn (Mod 10) number and False if not. This, of course, does not validate the number, but will catch typos. There is the chance that two typographic errors could return a false positive if they offset one another, but the likelihood is low and pre-validating is fast

class z3c.schema.payments.field.ISO7812CreditCard(*args, **kw)[source]

Bases: zope.schema._bootstrapfields.TextLine

A credit card with a valid check digit

Regular Expressions

Let’s first create the regex field.

>>> from z3c.schema.regex import Regex
>>> regex = Regex()

The regex field only allows compilable regular expressions.

>>> regex.validate(r'.*')
>>> regex.validate(r'^\s+$')

It does not validate regular expressions that do not compile.

>>> regex.validate('(i')
Traceback (most recent call last):
...
InvalidRegex: '(i', unbalanced parenthesis

When used to process input, only valid values are returned.

>>> regex.fromUnicode(u'.*')
'.*'
>>> regex.fromUnicode(u'(i')
Traceback (most recent call last):
...
InvalidRegex: '(i', unbalanced parenthesis

Reference

interface z3c.schema.regex.interfaces.IRegex[source]

Extends: zope.schema.interfaces.IASCIILine

Regular Expression field

exception z3c.schema.regex.interfaces.InvalidRegex[source]

Bases: zope.schema._bootstrapinterfaces.ValidationError

The specified regular expression is not valid.

class z3c.schema.regex.field.Regex(min_length=0, max_length=None, **kw)[source]

Bases: zope.schema._field.ASCIILine

Regex schema field.

Must be a compilable regular expression

CHANGES

1.2.0 (unreleased)

  • Nothing changed yet.

1.1.0 (2016-09-19)

  • Fixed credit card industry validation to check the first number, not the second.
  • Added support for Python 3.4, 3.5 and PyPy.
  • Dropped support for Python 2.6.
  • Achieve 100% code test coverage.
  • Moved documentation to http://z3cschema.readthedocs.io (see pull request #11).

1.0.0 (2013-03-01)

  • Added support for Python 3.3.
  • Replaced deprecated zope.interface.implements usage with equivalent zope.interface.implementer decorator.
  • Dropped support for Python 2.4 and 2.5.

0.7.2 (2011-04-03)

  • Fixed i18n files after something injected ‘fuzzy’ into the PO files.

0.7.1 (2011-04-03)

  • Add nl translations.

0.7.0 (2010-10-18)

  • Updated tests to run with zope.schema >= 3.6, thus requiring at least this version.
  • Using Python’s doctest module instead of depreacted zope.testing.doctest[unit].

0.6.1 (2010-10-18)

  • Added french translations.
  • Added doctests to long_description so they appear on PyPI.

0.6.0 (2009-01-26)

  • Humanized exception’s doc strings.
  • Added translations support.
  • Added german translations.
  • Added russian translations.
  • Changed mailing list address to zope-dev at zope.org, as zope3-dev is retired now.

0.5.0 (2008-10-21)

  • Initial release on PyPI.

0.2.0 (2007-09-21)

  • Feature: Added DateSelect field.

0.1.0 (2007-06-04)

  • Initial release.

Development

z3c.schema is hosted at GitHub:

Project URLs

Indices and tables