lists.arthurdejong.org
RSS feed

python-stdnum branch master updated. 0.7-61-g999f2c3

[Date Prev][Date Next] [Thread Prev][Thread Next]

python-stdnum branch master updated. 0.7-61-g999f2c3



This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "python-stdnum".

The branch, master has been updated
       via  999f2c38f6fdef19254e505db35c9ab722a9f1af (commit)
       via  cb699211eead9e9d40eee8fc3d94249934476130 (commit)
       via  e00744c2a1ec70f732f2a207e4d6473958fbbad8 (commit)
       via  3d3a97d5954ba3349b924a9902330771e05f2079 (commit)
       via  4bfce3fa2825d36dc947b4bfab42c037f29be454 (commit)
       via  1aaf9028a5a3c411a51f243bbbeeebee738e811c (commit)
       via  8982d1e8bdbc27844319edad4d968826048518d6 (commit)
       via  522a5992cf6c12bea73761a9dc5e38fa843af941 (commit)
       via  8e7d807ba24fdae51721060e389c8d1e92ff838e (commit)
       via  7e865db1d6c1862da3e81a27c83c23e888b9f895 (commit)
       via  96c5080c83cf33d8e4c2b50c706553461fecd289 (commit)
       via  2ff4950470434dd3ec6a0ca6dcdef20fbd835f9f (commit)
       via  9845b0a4df29f2774d050058e9e64f71c8b4e2c4 (commit)
       via  04cfb84567e36ba2a54389feb55d9408ad6a93f3 (commit)
       via  b1d5a7274b5ab2251a7d5e08f66765bef6196503 (commit)
       via  083993ba44f4a2a3c8dd29e8beb2494d151ec4af (commit)
       via  301ba25dfc30384653fec153134673f68a3ca875 (commit)
       via  31f268433bfec7d01ae8071cb0c37084e455d150 (commit)
       via  1932f69012c6247d89b55bbcd7fbd4f1c668a81d (commit)
       via  10710dcccf7ae453983d785e8fbcc5a53dfb485e (commit)
       via  4753c09e81a0adb6b62d12ad60f5a5107d5033c2 (commit)
       via  2259cbb09e419e56c355efdbd865f99127d559c5 (commit)
       via  07c66e13b3928fb92fd0e537e23d3c5be2ad8956 (commit)
       via  8caecc5c3d0036faf9d2344b052d0e7ab25ce533 (commit)
       via  360480bc26844e8c8e1e4fbbc154204b30a9337b (commit)
       via  fce61960476f9e416f16b22949b58f7859fdb8e9 (commit)
       via  14e382ff517bc5c4e21eb062905bad895c42dd77 (commit)
       via  54ce2d77655d57571eb53276bc9b45c18f1cdfc6 (commit)
       via  608090744d97a68eede6d49bd01d2b26c324a337 (commit)
       via  33ce4e99a38f997943a755a41b1061114634e11d (commit)
       via  66d6259f692676291a2466bd02427fec6d2549a4 (commit)
       via  05547a4d9feac13aba968745e25be34d911bfb65 (commit)
       via  fc1432c89bb0e9fe5e1f5c3c2214b7869a458d96 (commit)
       via  62cafb46aa2da2a5309546fc23e9d6af2c353ad3 (commit)
       via  cf88e23388478eaa856843b90dbefc4bb862d4ff (commit)
       via  c6f41f62f99a258a3cb6ffeefecdd341ad75ca59 (commit)
       via  21f07b37a77e00600d80fac94a61ed77eb1c205f (commit)
       via  c07609f3646f78c8b03d2dfcd0f5819135c86cfa (commit)
       via  a18f1acea1cc03fd5dec7f5c6cd26804405548c8 (commit)
       via  3ac8164e42ca20a2bcb586034c2e97b566ef6cc7 (commit)
       via  12bd68408c1a4c0076c1976bd4a975929402b5f9 (commit)
       via  9cee495a39afd52d03e66f0ea667af3efdf664a9 (commit)
       via  6e4bb713ce07177976eaa6988fdf719fbe0d7b09 (commit)
       via  efa255097d9751319244901831ce758f90f6b7c2 (commit)
       via  5c9090b369440885e2c3a68eba46c6fb8baace97 (commit)
       via  9ad51399d680b4f1944662bd4a9b9e5a6d1d5420 (commit)
       via  958004665f1f8c56be96fb30ea8f6a60c9c8456b (commit)
       via  fa1864fc2df0c746d691e56716f6e553b20a150f (commit)
       via  8b9ef8f2cb68de1b67a2ee1940d24e67d3942d42 (commit)
       via  1ac5437537e4b944c382dbd215ba86b02a20e1fc (commit)
      from  99586c9258b77d4855e874c79055ce8e0dc479f3 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://arthurdejong.org/git/python-stdnum/commit/?id=999f2c38f6fdef19254e505db35c9ab722a9f1af

commit 999f2c38f6fdef19254e505db35c9ab722a9f1af
Merge: 99586c9 cb69921
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sat Jun 8 14:46:38 2013 +0200

    Provide a validate() function in all modules
    
    This provides an additional means of doing number validation that allows
    applications calling this library to get more information about why the
    validation failed and present more informative messages to the user.
    
    This introduces a collection of exceptions which will be raised by the
    validate() function in each module. All modules have been updated to
    provide this new function.


http://arthurdejong.org/git/python-stdnum/commit/?id=cb699211eead9e9d40eee8fc3d94249934476130

commit cb699211eead9e9d40eee8fc3d94249934476130
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Wed May 22 22:31:15 2013 +0200

    Document the validate() function and exceptions

diff --git a/README b/README
index e9fe209..0e70a1a 100644
--- a/README
+++ b/README
@@ -84,15 +84,18 @@ Interface
 
 Most of these modules implement the following functions:
 
-is_valid() - returns either True or False depending on whether the
-             passed number is in any supported and valid form
+validate() - validate the correctness of the passed number and return a
+             compact representation of the number
+             invalid numbers are rejected with one of the exceptions
+             from the stdnum.exceptions module
 compact() - return a compact representation of the number or code
             this function generally does not do validation but may raise
             exceptions for wildly incorrect numbers
 format() - return a formatted version of the number in the preferred format
            this function generally expects to be passed a valid number or code
 
-Apart from the above, the module may add extra parsing, validation or 
conversion functions.
+Apart from the above, the module may add extra parsing, validation or
+conversion functions.
 
 Requirements
 ------------
@@ -104,7 +107,7 @@ also work with older versions of Python.
 Copyright
 ---------
 
-Copyright (C) 2010, 2011, 2012 Arthur de Jong
+Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
diff --git a/docs/index.rst b/docs/index.rst
index dc82fbe..b631530 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -17,6 +17,13 @@ Common Interface
 
 Most of the number format modules implement the following functions:
 
+.. function:: validate(number)
+
+   Validate the number and return a compact, consistent representation of
+   the number or code. If the validation fails,
+   :mod:`an exception <.exceptions>` is raised that indicates the type of
+   error.
+
 .. function:: is_valid(number)
 
    Return either True or False depending on whether the passed number is
@@ -51,6 +58,16 @@ The check digit modules generally also provide the following 
functions:
 Apart from the above, the modules may add extra parsing, validation or
 conversion functions.
 
+
+Helper modules
+--------------
+
+.. autosummary::
+   :toctree:
+
+   exceptions
+
+
 Generic check digit algorithms
 ------------------------------
 
diff --git a/docs/stdnum.exceptions.rst b/docs/stdnum.exceptions.rst
new file mode 100644
index 0000000..620fdf0
--- /dev/null
+++ b/docs/stdnum.exceptions.rst
@@ -0,0 +1,31 @@
+stdnum.exceptions
+=================
+
+.. automodule:: stdnum.exceptions
+   :show-inheritance:
+   :member-order: bysource
+   :members:
+
+   The exceptions are organised hierarchically in the following structure:
+
+   ::
+
+      ValidationError
+       +-- InvalidFormat
+       |    +-- InvalidLength
+       +-- InvalidChecksum
+       +-- InvalidComponent
+
+   It is possible to change the exception messages by setting the `message`
+   class property. This allows localisation and application-specific error
+   messages.
+
+   >>> raise InvalidFormat()
+   Traceback (most recent call last):
+       ...
+   InvalidChecksum: The number has an invalid format.
+   >>> InvalidFormat.message = 'UNKNOWN'
+   >>> raise InvalidFormat()
+   Traceback (most recent call last):
+       ...
+   InvalidChecksum: UNKNOWN

http://arthurdejong.org/git/python-stdnum/commit/?id=e00744c2a1ec70f732f2a207e4d6473958fbbad8

commit e00744c2a1ec70f732f2a207e4d6473958fbbad8
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sat May 18 23:54:42 2013 +0200

    Use validate() in stdnum.util

diff --git a/stdnum/util.py b/stdnum/util.py
index e45b78f..a7db8cf 100644
--- a/stdnum/util.py
+++ b/stdnum/util.py
@@ -57,7 +57,7 @@ def get_number_modules(base='stdnum'):
                 ):
         __import__(name)
         module = sys.modules[name]
-        if hasattr(module, 'is_valid'):
+        if hasattr(module, 'validate'):
             yield module
 
 

http://arthurdejong.org/git/python-stdnum/commit/?id=3d3a97d5954ba3349b924a9902330771e05f2079

commit 3d3a97d5954ba3349b924a9902330771e05f2079
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 17:08:25 2013 +0200

    Implement validate() for U.S. Social Security Number

diff --git a/stdnum/us/ssn.py b/stdnum/us/ssn.py
index adf5131..81d43db 100644
--- a/stdnum/us/ssn.py
+++ b/stdnum/us/ssn.py
@@ -1,6 +1,6 @@
 # ssn.py - functions for handling SSNs
 #
-# Copyright (C) 2011, 2012 Arthur de Jong
+# Copyright (C) 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -22,12 +22,20 @@
 The Social Security Number is used to identify individuals for taxation
 purposes.
 
->>> is_valid('111-22-3333')
-True
->>> is_valid('1112-23333')
-False
->>> is_valid('666-00-0000')
-False
+>>> validate('536-90-4399')
+'536904399'
+>>> validate('1112-23333')  # dash in the wrong place
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> validate('666-00-0000')  # invalid area
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> validate('078-05-1120')  # blacklisted entry
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
 >>> compact('1234-56-789')
 '123456789'
 >>> format('111223333')
@@ -36,6 +44,7 @@ False
 
 import re
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -52,15 +61,12 @@ def compact(number):
     return clean(number, '-').strip()
 
 
-def is_valid(number):
+def validate(number):
     """Checks to see if the number provided is a valid SSN. This checks
     the length, groups and formatting if it is present."""
-    try:
-        match = _ssn_re.search(number.strip())
-    except:
-        return False
+    match = _ssn_re.search(clean(number, '').strip())
     if not match:
-        return False
+        raise InvalidFormat()
     area = match.group('area')
     group = match.group('group')
     serial = match.group('serial')
@@ -68,9 +74,20 @@ def is_valid(number):
     # (9xx also won't be issued which includes the advertising range)
     if area == '000' or area == '666' or area[0] == '9' or \
        group == '00' or serial == '0000':
-        return False
+        raise InvalidComponent()
     # check blacklists
-    return format(number) not in _ssn_blacklist
+    if format(number) in _ssn_blacklist:
+        raise InvalidComponent()
+    return compact(number)
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid SSN. This checks
+    the length, groups and formatting if it is present."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False
 
 
 def format(number):

http://arthurdejong.org/git/python-stdnum/commit/?id=4bfce3fa2825d36dc947b4bfab42c037f29be454

commit 4bfce3fa2825d36dc947b4bfab42c037f29be454
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 17:16:27 2013 +0200

    Implement validate() for European VAT numbers

diff --git a/stdnum/eu/vat.py b/stdnum/eu/vat.py
index 9961380..1c3c9f2 100644
--- a/stdnum/eu/vat.py
+++ b/stdnum/eu/vat.py
@@ -1,7 +1,7 @@
 # vat.py - functions for handling European VAT numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -28,16 +28,19 @@ The exact format of the numbers varies per country and a 
country-specific
 check is performed on the number using the VAT module that is relevant for
 that country.
 
->>> is_valid('ATU 57194903')
-True
->>> is_valid('BE697449992')
-True
->>> compact('FR 61 954 506 077')
+>>> compact('ATU 57194903')
+'ATU57194903'
+>>> validate('BE697449992')
+'BE0697449992'
+>>> validate('FR 61 954 506 077')
 'FR61954506077'
 >>> guess_country('00449544B01')
 ['nl']
 """
 
+from stdnum.exceptions import *
+from stdnum.util import clean
+
 
 country_codes = set([
     'at', 'be', 'bg', 'cy', 'cz', 'de', 'dk', 'ee', 'es', 'fi', 'fr', 'gb',
@@ -75,19 +78,30 @@ def _get_cc_module(cc):
 def compact(number):
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace."""
-    number = number.upper().strip()
-    return number[:2] + _get_cc_module(number[:2]).compact(number[2:])
+    number = clean(number, '').upper().strip()
+    module = _get_cc_module(number[:2])
+    if not module:
+        raise InvalidComponent()
+    return number[:2] + module.compact(number[2:])
+
+
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    performs the country-specific check for the number."""
+    number = clean(number, '').upper().strip()
+    module = _get_cc_module(number[:2])
+    if not module:
+        raise InvalidComponent()
+    return number[:2] + module.validate(number[2:])
 
 
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This
     performs the country-specific check for the number."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    module = _get_cc_module(number[:2])
-    return bool(module) and module.is_valid(number[2:])
 
 
 def guess_country(number):
@@ -107,9 +121,9 @@ def check_vies(number):  # pragma: no cover (no tests for 
this function)
     This returns a dict-like object."""
     # this function isn't automatically tested because it would require
     # network access for the tests and unnecessarily load the VIES website
+    number = compact(number)
     global _vies_client
     if not _vies_client:
         from suds.client import Client
         _vies_client = Client(vies_wsdl)
-    number = compact(number)
     return _vies_client.service.checkVat(number[:2], number[2:])
diff --git a/tests/test_eu_vat.doctest b/tests/test_eu_vat.doctest
index 82ccf8c..b1c67a0 100644
--- a/tests/test_eu_vat.doctest
+++ b/tests/test_eu_vat.doctest
@@ -1,6 +1,6 @@
 test_eu_vat.doctest - more detailed doctests for the stdnum.eu.vat module
 
-Copyright (C) 2012 Arthur de Jong
+Copyright (C) 2012, 2013 Arthur de Jong
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -22,6 +22,7 @@ This file contains more detailed doctests for the 
stdnum.eu.vat module. It
 tries to validate a number of VAT numbers that have been found online.
 
 >>> from stdnum.eu import vat
+>>> from stdnum.exceptions import *
 
 
 These have been found online and should all be valid numbers.
@@ -652,5 +653,177 @@ These have been found online and should all be valid 
numbers.
 ... SK2022193459
 ...
 ... '''
->>> [ x for x in numbers.splitlines() if x and not vat.is_valid(x) ]
+>>> [x for x in numbers.splitlines() if x and not vat.is_valid(x)]
+[]
+
+
+The following numbers are wrong in one way or another. First we need a
+function to be able to determine the kind of error.
+
+>>> def caught(number, exception):
+...     try:
+...         vat.validate(number)
+...         return False
+...     except exception:
+...         return True
+...
+
+
+These numbers should be mostly valid except that they have been mangled
+so their check digit is wrong.
+
+>>> numbers = '''
+...
+... ATU 143 43 102
+... ATU 15169209
+... ATU46080904
+... ATU61191628
+...
+... BE 0406.139.583
+... BE 0793 013 186
+... BE 456.973.432
+... BE697449982
+...
+... BG 1037735942
+... BG 131 172 009
+... BG 147147471
+... BG 200940556
+...
+... CY 10246672F
+... CY-10009489 A
+... CY-10257074Y
+... CY00632893 F
+...
+... CZ 25557481
+... CZ 48207726
+... CZ 640913926
+... CZ 8058018314
+...
+... DE 125014855
+... DE 246 495 415
+... DE 813 91 38 75
+... DE - 266297673
+...
+... DK 11 96 56 58
+... DK 2565 2202
+... DK-27996592
+... DK10159817
+...
+... EE 100 941 558
+... EE 100525487
+... EE 101586639
+... EE101256754
+...
+... ES 54362415K
+... ES K-2814015-F
+... ES-x-2322300w
+... Es-a48951310
+...
+... EL 094013834
+... EL 094068733
+... EL094501140
+... EL: 094269805
+...
+... FR 04409414264
+... FR 21 326 565 603
+... FR K 7399859312
+... FR L 7399859412
+...
+... IE 4731823H
+... IE 632 3421 C
+... IE 5339273F
+... IE8D79729I
+...
+... LV 40003189715
+... LV 90000528022
+... LV-10103241337
+... LV-42103048181
+...
+... SK 1078449164
+... SK 302 274 96 19
+... SK 2030 237 945
+... SK 2020457679
+...
+... '''
+>>> [x for x in numbers.splitlines() if x and not caught(x, InvalidChecksum)]
+[]
+
+
+These numbers should be mostly valid except that they have some formatting
+flaws.
+
+>>> numbers = '''
+...
+... AT1 142 43 102
+... ATU 1515B209
+...
+... BE 02A2.239.951
+... BE 0220,764.971
+...
+... BG 10X8735941
+...
+... CY-102590Z3P
+...
+... DE 246X595 415
+... DE 011125440
+...
+... IE 4550C59S
+... IE 069385V8
+...
+... NL 001241643801
+... NL 009122746B00
+... NL B06753742B01
+... NL 82X569759b01
+...
+... SK A078449064
+... SK 012 274 96 19
+... SK 2010 237 945
+...
+... '''
+>>> [x for x in numbers.splitlines() if x and not caught(x, InvalidFormat)]
+[]
+
+
+These numbers should be mostly valid except that they have some component
+that contains an invalid or unknown value.
+
+>>> numbers = '''
+...
+... CY-12259033P
+...
+... CZ 95123891
+...
+... IT 00687129980
+...
+... LT 100001354
+... LT 100001509922
+...
+... QQ 124567
+...
+... '''
+>>> [x for x in numbers.splitlines() if x and not caught(x, InvalidComponent)]
+[]
+>>> vat.compact('QQ 124567')
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+
+
+These numbers should be mostly valid except that they have the wrong length.
+
+>>> numbers = '''
+...
+... ATU 151592092
+...
+... CY-1225903322
+...
+... ES B-583784312
+...
+... NL006866304B021
+...
+... SE 55643359201
+... SE 5567003255201
+...
+... '''
+>>> [x for x in numbers.splitlines() if x and not caught(x, InvalidLength)]
 []

http://arthurdejong.org/git/python-stdnum/commit/?id=1aaf9028a5a3c411a51f243bbbeeebee738e811c

commit 1aaf9028a5a3c411a51f243bbbeeebee738e811c
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 17:02:15 2013 +0200

    Implement validate() for Slovak numbers

diff --git a/stdnum/sk/dph.py b/stdnum/sk/dph.py
index 669e109..3654316 100644
--- a/stdnum/sk/dph.py
+++ b/stdnum/sk/dph.py
@@ -1,7 +1,7 @@
 # vat.py - functions for handling Slovak VAT numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -23,14 +23,15 @@
 The IČ DPH (Identifikačné číslo pre daň z pridanej hodnoty) is a 10-digit
 number used for VAT purposes. It has a straightforward checksum.
 
->>> compact('SK 202 274 96 19')
+>>> validate('SK 202 274 96 19')
 '2022749619'
->>> is_valid('SK 202 274 96 19')
-True
->>> is_valid('SK 202 274 96 18')  # invalid check digits
-False
+>>> validate('SK 202 274 96 18')  # invalid check digits
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.sk import rc
 from stdnum.util import clean
 
@@ -49,16 +50,28 @@ def checksum(number):
     return int(number) % 11
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) != 10:
+        raise InvalidLength()
+    # it is unclear whether the RČ can be used as a valid VAT number
+    if rc.is_valid(number):
+        return number
+    if number[0] == '0' or int(number[2]) not in (2, 3, 4, 7, 8, 9):
+        raise InvalidFormat()
+    if checksum(number) != 0:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    if not number.isdigit() or len(number) != 10:
-        return False
-    # it is unclear whether the RČ can be used as a valid VAT number
-    return rc.is_valid(number) or \
-           (number[0] != '0' and int(number[2]) in (2, 3, 4, 7, 8, 9) and
-            checksum(number) == 0)
diff --git a/stdnum/sk/rc.py b/stdnum/sk/rc.py
index 9efbdcb..d0ffa8f 100644
--- a/stdnum/sk/rc.py
+++ b/stdnum/sk/rc.py
@@ -1,7 +1,7 @@
 # rc.py - functions for handling Slovak birth numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -27,23 +27,27 @@ person and their gender.
 
 This number is identical to the Czech counterpart.
 
->>> compact('710319/2745')
+>>> validate('710319/2745')
 '7103192745'
->>> is_valid('7103192745')
-True
->>> is_valid('991231123')
-True
->>> is_valid('7103192746')  # invalid check digit
-False
->>> is_valid('1103492745')  # invalid date
-False
->>> is_valid('590312/123')  # 9 digit number in 1959
-False
+>>> validate('991231123')
+'991231123'
+>>> validate('7103192746')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('1103492745')  # invalid date
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> validate('590312/123')  # 9 digit number in 1959
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
 >>> format('7103192745')
 '710319/2745'
 """
 
 # since this number is essentially the same as the Czech counterpart
 # (until 1993 the Czech Republic and Slovakia were one country)
-from stdnum.cz.rc import compact, is_valid, format
-__all__ = ['compact', 'is_valid', 'format']
+from stdnum.cz.rc import compact, validate, is_valid, format
+__all__ = ['compact', 'validate', 'is_valid', 'format']

http://arthurdejong.org/git/python-stdnum/commit/?id=8982d1e8bdbc27844319edad4d968826048518d6

commit 8982d1e8bdbc27844319edad4d968826048518d6
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 16:52:10 2013 +0200

    Implement validate() for Slovenian VAT numbers

diff --git a/stdnum/si/ddv.py b/stdnum/si/ddv.py
index e7992ef..59a156b 100644
--- a/stdnum/si/ddv.py
+++ b/stdnum/si/ddv.py
@@ -1,7 +1,7 @@
 # ddv.py - functions for handling Slovenian VAT numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -24,14 +24,15 @@ The DDV number (Davčna številka) is used for VAT (DDV, 
Davek na dodano
 vrednost) purposes and consist of 8 digits of which the last is a check
 digit.
 
->>> compact('SI 5022 3054')
+>>> validate('SI 5022 3054')
 '50223054'
->>> is_valid('SI 50223054')
-True
->>> is_valid('SI 50223055')  # invalid check digits
-False
+>>> validate('SI 50223055')  # invalid check digits
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -52,12 +53,23 @@ def calc_check_digit(number):
     return '0' if check == 10 else str(check)
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit() or number.startswith('0'):
+        raise InvalidFormat()
+    if len(number) != 8:
+        raise InvalidLength()
+    if calc_check_digit(number[:-1]) != number[-1]:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return number.isdigit() and len(number) == 8 and number[0] != '0' and \
-           calc_check_digit(number[:-1]) == number[-1]

http://arthurdejong.org/git/python-stdnum/commit/?id=522a5992cf6c12bea73761a9dc5e38fa843af941

commit 522a5992cf6c12bea73761a9dc5e38fa843af941
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 16:48:48 2013 +0200

    Implement validate() for Swedish VAT numbers

diff --git a/stdnum/se/vat.py b/stdnum/se/vat.py
index 0fb1e2c..88a73ac 100644
--- a/stdnum/se/vat.py
+++ b/stdnum/se/vat.py
@@ -1,7 +1,7 @@
 # vat.py - functions for handling Swedish VAT numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -24,15 +24,16 @@ The Momsregistreringsnummer is used for VAT (Moms, 
Mervärdesskatt)
 purposes and consists of 12 digits of which the last two should be 01. The
 first 10 digits should have a valid Luhn checksum.
 
->>> compact('SE 123456789701')
+>>> validate('SE 123456789701')
 '123456789701'
->>> is_valid('123456789701')
-True
->>> is_valid('123456789101')  # invalid check digits
-False
+>>> validate('123456789101')  # invalid check digits
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
 from stdnum import luhn
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -45,12 +46,22 @@ def compact(number):
     return number
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit() or number[-2:] != '01':
+        raise InvalidFormat()
+    if len(number) != 12:
+        raise InvalidLength()
+    luhn.validate(number[:-2])
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return number.isdigit() and len(number) == 12 and \
-           luhn.is_valid(number[:-2]) and 0 < int(number[-2:]) < 95

http://arthurdejong.org/git/python-stdnum/commit/?id=8e7d807ba24fdae51721060e389c8d1e92ff838e

commit 8e7d807ba24fdae51721060e389c8d1e92ff838e
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 16:46:19 2013 +0200

    Implement validate() for Portuguese VAT numbers

diff --git a/stdnum/pt/nif.py b/stdnum/pt/nif.py
index f7b8b76..f85df17 100644
--- a/stdnum/pt/nif.py
+++ b/stdnum/pt/nif.py
@@ -1,7 +1,7 @@
 # nif.py - functions for handling Portuguese VAT numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -24,14 +24,15 @@ The NIF (Número de identificação fiscal, NIPC, Número de 
Identificação de
 Pessoa Colectiva) is used for VAT purposes. It is a 9-digit number with a
 simple checksum.
 
->>> compact('PT 501 964 843')
+>>> validate('PT 501 964 843')
 '501964843'
->>> is_valid('PT 501 964 843')
-True
->>> is_valid('PT 501 964 842')  # invalid check digits
-False
+>>> validate('PT 501 964 842')  # invalid check digits
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -50,12 +51,23 @@ def calc_check_digit(number):
     return str((11 - sum((9 - i) * int(n) for i, n in enumerate(number)) ) % 
11 % 10)
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit() or number[0] == '0':
+        raise InvalidFormat()
+    if len(number) != 9:
+        raise InvalidLength()
+    if calc_check_digit(number[:-1]) != number[-1]:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return number.isdigit() and len(number) == 9 and \
-           number[0] != '0' and calc_check_digit(number[:-1]) == number[-1]

http://arthurdejong.org/git/python-stdnum/commit/?id=7e865db1d6c1862da3e81a27c83c23e888b9f895

commit 7e865db1d6c1862da3e81a27c83c23e888b9f895
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 16:43:24 2013 +0200

    Implement validate() for Polish numbers

diff --git a/stdnum/pl/nip.py b/stdnum/pl/nip.py
index 60a5d56..45e18da 100644
--- a/stdnum/pl/nip.py
+++ b/stdnum/pl/nip.py
@@ -1,6 +1,6 @@
 # nip.py - functions for handling Polish VAT numbers
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -22,16 +22,17 @@
 The NIP (Numer Identyfikacji Podatkowej) number consists of 10 digit with
 a straightforward weighted checksum.
 
->>> compact('PL 8567346215')
+>>> validate('PL 8567346215')
 '8567346215'
->>> is_valid('PL 8567346215')
-True
->>> is_valid('PL 8567346216')  # invalid check digits
-False
+>>> validate('PL 8567346216')  # invalid check digits
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 >>> format('PL 8567346215')
 '856-734-62-15'
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -50,15 +51,26 @@ def checksum(number):
     return sum(weights[i] * int(n) for i, n in enumerate(number)) % 11
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) != 10:
+        raise InvalidLength()
+    if checksum(number) != 0:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return number.isdigit() and len(number) == 10 and \
-           checksum(number) == 0
 
 
 def format(number):

http://arthurdejong.org/git/python-stdnum/commit/?id=96c5080c83cf33d8e4c2b50c706553461fecd289

commit 96c5080c83cf33d8e4c2b50c706553461fecd289
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 14:00:13 2013 +0200

    Implement validate() for Maltese numbers

diff --git a/stdnum/mt/vat.py b/stdnum/mt/vat.py
index dc5c7b7..197c9ca 100644
--- a/stdnum/mt/vat.py
+++ b/stdnum/mt/vat.py
@@ -1,6 +1,6 @@
 # vat.py - functions for handling Maltese VAT numbers
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -22,14 +22,15 @@
 The Maltese VAT registration number contains 8 digits and uses a simple
 weigted checksum.
 
->>> compact('MT 1167-9112')
+>>> validate('MT 1167-9112')
 '11679112'
->>> is_valid('1167-9112')
-True
->>> is_valid('1167-9113')  # invalid check digits
-False
+>>> validate('1167-9113')  # invalid check digits
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -48,12 +49,23 @@ def checksum(number):
     return sum(weights[i] * int(n) for i, n in enumerate(number)) % 37
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit() or number[0] == '0':
+        raise InvalidFormat()
+    if len(number) != 8:
+        raise InvalidLength()
+    if checksum(number) != 0:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return number.isdigit() and len(number) == 8 and \
-           number[0] != '0' and checksum(number) == 0

http://arthurdejong.org/git/python-stdnum/commit/?id=2ff4950470434dd3ec6a0ca6dcdef20fbd835f9f

commit 2ff4950470434dd3ec6a0ca6dcdef20fbd835f9f
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 13:57:34 2013 +0200

    Implement validate() for Latvian numbers

diff --git a/stdnum/lv/pvn.py b/stdnum/lv/pvn.py
index 0bb8697..595e0b8 100644
--- a/stdnum/lv/pvn.py
+++ b/stdnum/lv/pvn.py
@@ -1,7 +1,7 @@
 # pvn.py - functions for handling Latvian PVN (VAT) numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -25,20 +25,23 @@ entity (in which case the first digit > 3) or a natural 
person (in which
 case it should be the same as the personal code (personas kods)). Personal
 codes start with 6 digits to denote the birth date in the form ddmmyy.
 
->>> compact('LV 4000 3521 600')
+>>> validate('LV 4000 3521 600')
 '40003521600'
->>> is_valid('40003521600')
-True
->>> is_valid('40003521601')  # invalid check digit
-False
->>> is_valid('161175-19997')  # personal code
-True
->>> is_valid('161375-19997')  # invalid date
-False
+>>> validate('40003521601')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('161175-19997')  # personal code
+'16117519997'
+>>> validate('161375-19997')  # invalid date
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
 """
 
 import datetime
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -77,26 +80,37 @@ def get_birth_date(number):
     month = int(number[2:4])
     year = int(number[4:6])
     year += 1800 + int(number[6]) * 100
-    return datetime.date(year, month, day)
+    try:
+        return datetime.date(year, month, day)
+    except ValueError:
+        raise InvalidComponent()
 
 
-def is_valid(number):
+def validate(number):
     """Checks to see if the number provided is a valid VAT number. This checks
     the length, formatting and check digit."""
-    try:
-        number = compact(number)
-    except:
-        return False
-    if len(number) != 11 or not number.isdigit():
-        return False
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) != 11:
+        raise InvalidLength()
     if number[0] > '3':
         # legal entity
-        return checksum(number) == 3
+        if checksum(number) != 3:
+            raise InvalidChecksum()
     else:
         # natural resident, check if birth date is valid
-        try:
-            birth_date = get_birth_date(number)
-            # TODO: check that the birth date is not in the future
-        except ValueError:
-            return False
-        return calc_check_digit_pers(number[:-1]) == number[-1]
+        birth_date = get_birth_date(number)
+        # TODO: check that the birth date is not in the future
+        if calc_check_digit_pers(number[:-1]) != number[-1]:
+            raise InvalidChecksum()
+    return number
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid VAT number. This checks
+    the length, formatting and check digit."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False

http://arthurdejong.org/git/python-stdnum/commit/?id=9845b0a4df29f2774d050058e9e64f71c8b4e2c4

commit 9845b0a4df29f2774d050058e9e64f71c8b4e2c4
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 13:52:38 2013 +0200

    Implement validate() for Luxembourgian numbers

diff --git a/stdnum/lu/tva.py b/stdnum/lu/tva.py
index 5dc3c64..5fe9896 100644
--- a/stdnum/lu/tva.py
+++ b/stdnum/lu/tva.py
@@ -1,7 +1,7 @@
 # tva.py - functions for handling Luxembourgian VAT numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -24,14 +24,15 @@ The n° TVA (Numéro d'identification à la taxe sur la valeur 
ajoutée) is
 used for tax purposes in Luxembourg. The number consists of 8 digits of
 which the last two are check digits.
 
->>> compact('LU 150 274 42')
+>>> validate('LU 150 274 42')
 '15027442'
->>> is_valid('150 274 42')
-True
->>> is_valid('150 274 43')  # invalid check digits
-False
+>>> validate('150 274 43')  # invalid check digits
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -49,12 +50,23 @@ def calc_check_digits(number):
     return '%02d' % (int(number) % 89)
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) != 8:
+        raise InvalidLength()
+    if calc_check_digits(number[:6]) != number[-2:]:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return number.isdigit() and len(number) == 8 and \
-           calc_check_digits(number[:6]) == number[-2:]

http://arthurdejong.org/git/python-stdnum/commit/?id=04cfb84567e36ba2a54389feb55d9408ad6a93f3

commit 04cfb84567e36ba2a54389feb55d9408ad6a93f3
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 13:48:53 2013 +0200

    Implement validate() for Lithuanian numbers

diff --git a/stdnum/lt/pvm.py b/stdnum/lt/pvm.py
index c9d9b7f..b5b2016 100644
--- a/stdnum/lt/pvm.py
+++ b/stdnum/lt/pvm.py
@@ -1,7 +1,7 @@
 # pvm.py - functions for handling Lithuanian VAT numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -25,18 +25,19 @@ entities) or 12 digits long (for temporarily registered 
taxpayers). This
 module does not check the format of Lithuanian personal codes (Asmens
 kodas).
 
->>> compact('LT 100001919017')
+>>> validate('119511515')  # organisation
+'119511515'
+>>> validate('LT 100001919017')  # temporarily registered
 '100001919017'
->>> is_valid('119511515')  # organisation
-True
->>> is_valid('100001919017')  # temporarily registered
-True
->>> is_valid('100001919018')  # invalid check digit
-False
->>> is_valid('100004801610')  # second step in check digit calculation
-True
+>>> validate('100001919018')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('100004801610')  # second step in check digit calculation
+'100004801610'
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -58,21 +59,31 @@ def calc_check_digit(number):
     return str(check % 11 % 10)
 
 
-def is_valid(number):
+def validate(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
-    try:
-        number = compact(number)
-    except:
-        return False
+    number = compact(number)
     if not number.isdigit():
-        return False
+        raise InvalidFormat()
     if len(number) == 9:
         # legal entities
-        return number[7] == '1' and \
-               calc_check_digit(number[:-1]) == number[-1]
-    if len(number) == 12:
+        if number[7] != '1':
+            raise InvalidComponent()
+    elif len(number) == 12:
         # temporary tax payers and natural persons
-        return number[10] == '1' and \
-               calc_check_digit(number[:-1]) == number[-1]
-    return False
+        if number[10] != '1':
+            raise InvalidComponent()
+    else:
+        raise InvalidLength()
+    if calc_check_digit(number[:-1]) != number[-1]:
+        raise InvalidChecksum()
+    return number
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False

http://arthurdejong.org/git/python-stdnum/commit/?id=b1d5a7274b5ab2251a7d5e08f66765bef6196503

commit b1d5a7274b5ab2251a7d5e08f66765bef6196503
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 13:44:16 2013 +0200

    Implement validate() for Italian numbers

diff --git a/stdnum/it/iva.py b/stdnum/it/iva.py
index 1b20569..fdfdfca 100644
--- a/stdnum/it/iva.py
+++ b/stdnum/it/iva.py
@@ -1,6 +1,6 @@
-# vat.py - functions for handling Italian VAT numbers
+# iva.py - functions for handling Italian VAT numbers
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -26,15 +26,16 @@ of residence and the last is a check digit.
 The fiscal code for individuals is not accepted as valid code for
 intracommunity VAT related operations so it is ignored here.
 
->>> compact('IT 00743110157')
+>>> validate('IT 00743110157')
 '00743110157'
->>> is_valid('00743110157')
-True
->>> is_valid('00743110158')  # invalid check digit
-False
+>>> validate('00743110158')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
 from stdnum import luhn
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -47,15 +48,26 @@ def compact(number):
     return number
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This checks
+    the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit() or int(number[0:7]) == 0:
+        raise InvalidFormat()
+    if len(number) != 11:
+        raise InvalidLength()
+    # check the province of residence
+    if not('001' <= number[7:10] <= '100') and \
+            number[7:10] not in ('120', '121', '888', '999'):
+        raise InvalidComponent()
+    luhn.validate(number)
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This checks
     the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) == 11 and number.isdigit() and \
-           int(number[0:7]) > 0 and (
-              '001' <= number[7:10] <= '100' or
-              number[7:10] in ('120', '121', '888', '999')
-           ) and luhn.is_valid(number)

http://arthurdejong.org/git/python-stdnum/commit/?id=083993ba44f4a2a3c8dd29e8beb2494d151ec4af

commit 083993ba44f4a2a3c8dd29e8beb2494d151ec4af
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 13:39:22 2013 +0200

    Implement validate() for Irish numbers

diff --git a/stdnum/ie/pps.py b/stdnum/ie/pps.py
index 4e2e44c..0447bf3 100644
--- a/stdnum/ie/pps.py
+++ b/stdnum/ie/pps.py
@@ -1,6 +1,6 @@
 # pps.py - functions for handling Irish PPS numbers
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -24,18 +24,19 @@ are numeric and the last is the check character. The number 
is sometimes
 be followed by an extra letter that can be a 'W', 'T' or an 'X' and is
 ignored for the check algorithm.
 
->>> compact('6433435F')
+>>> validate('6433435F')
 '6433435F'
->>> is_valid('6433435F')
-True
->>> is_valid('6433435E')  # incorrect check digit
-False
+>>> validate('6433435E')  # incorrect check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
 import re
 
-from stdnum.util import clean
+from stdnum.exceptions import *
 from stdnum.ie import vat
+from stdnum.util import clean
 
 
 pps_re = re.compile('^\d{7}[A-W][WTX]?$')
@@ -48,13 +49,21 @@ def compact(number):
     return clean(number, ' -').upper().strip()
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid PPS number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not pps_re.match(number):
+        raise InvalidFormat()
+    if number[7] != vat.calc_check_digit(number[:7]):
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid PPS number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
-        return False
-    if not pps_re.match(number):
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return number[7] == vat.calc_check_digit(number[:7])
diff --git a/stdnum/ie/vat.py b/stdnum/ie/vat.py
index 2ff156c..1988093 100644
--- a/stdnum/ie/vat.py
+++ b/stdnum/ie/vat.py
@@ -1,6 +1,6 @@
 # vat.py - functions for handling Irish VAT numbers
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -22,18 +22,21 @@
 The Irish VAT number consists of 8 digits. The last digit is a check
 letter, the second digit may be a number, a letter, "+" or "*".
 
->>> compact('IE 6433435F')
+>>> validate('IE 6433435F')
 '6433435F'
->>> is_valid('6433435F')
-True
->>> is_valid('6433435E')  # incorrect check digit
-False
->>> is_valid('8D79739I')  # old style number
-True
->>> is_valid('8?79739J')  # incorrect old style
-False
+>>> validate('6433435E')  # incorrect check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('8D79739I')  # old style number
+'8D79739I'
+>>> validate('8?79739J')  # incorrect old style
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -54,20 +57,31 @@ def calc_check_digit(number):
     return alphabet[sum((8 - i) * int(n) for i, n in enumerate(number)) % 23]
 
 
-def is_valid(number):
+def validate(number):
     """Checks to see if the number provided is a valid VAT number. This checks
     the length, formatting and check digit."""
-    try:
-        number = compact(number)
-    except:
-        return False
-    if len(number) != 8 or not number[0].isdigit() or \
-       not number[2:7].isdigit():
-         return False
+    number = compact(number)
+    if not number[:1].isdigit() or not number[2:7].isdigit():
+        raise InvalidFormat()
+    if len(number) != 8:
+        raise InvalidLength()
     if number[:7].isdigit():
         # new system
-        return number[-1] == calc_check_digit(number[:-1])
-    if number[1] in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ+*':
+        if number[-1] != calc_check_digit(number[:-1]):
+            raise InvalidChecksum()
+    elif number[1] in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ+*':
         # old system
-        return number[-1] == calc_check_digit(number[2:-1] + number[0])
-    return False
+        if number[-1] != calc_check_digit(number[2:-1] + number[0]):
+            raise InvalidChecksum()
+    else:
+        raise InvalidFormat()
+    return number
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid VAT number. This checks
+    the length, formatting and check digit."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False

http://arthurdejong.org/git/python-stdnum/commit/?id=301ba25dfc30384653fec153134673f68a3ca875

commit 301ba25dfc30384653fec153134673f68a3ca875
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 13:28:46 2013 +0200

    Implement validate() for Hungarian numbers

diff --git a/stdnum/hu/anum.py b/stdnum/hu/anum.py
index f00a9ef..e6d9315 100644
--- a/stdnum/hu/anum.py
+++ b/stdnum/hu/anum.py
@@ -1,7 +1,7 @@
 # anum.py - functions for handling Hungarian VAT numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -23,14 +23,15 @@
 The ANUM is the Hungarian VAT (Közösségi adószám) number. It is an 8-digit
 taxpayer registration number that includes a weighted checksum.
 
->>> compact('HU-12892312')
+>>> validate('HU-12892312')
 '12892312'
->>> is_valid('HU-12892312')
-True
->>> is_valid('HU-12892313')  # invalid check digit
-False
+>>> validate('HU-12892313')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -49,11 +50,23 @@ def checksum(number):
     return sum(weights[i] * int(n) for i, n in enumerate(number)) % 10
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) != 8:
+        raise InvalidLength()
+    if checksum(number) != 0:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return number.isdigit() and len(number) == 8 and checksum(number) == 0

http://arthurdejong.org/git/python-stdnum/commit/?id=31f268433bfec7d01ae8071cb0c37084e455d150

commit 31f268433bfec7d01ae8071cb0c37084e455d150
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 13:26:37 2013 +0200

    Implement validate() for Croatian numbers

diff --git a/stdnum/hr/oib.py b/stdnum/hr/oib.py
index fad0707..c78dede 100644
--- a/stdnum/hr/oib.py
+++ b/stdnum/hr/oib.py
@@ -1,7 +1,7 @@
 # cnp.py - functions for handling Croatian OIB numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -24,16 +24,17 @@ The personal identification number is used to identify 
persons and legal
 entities in Croatia. It has 11 digits (sometimes prefixed by HR), contains
 no personal information and uses the ISO 7064 Mod 11, 10 checksum algorithm.
 
->>> compact('HR 33392005961')
+>>> validate('HR 33392005961')
 '33392005961'
->>> is_valid('33392005961')
-True
->>> is_valid('33392005962')  # invalid check digit
-False
+>>> validate('33392005962')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
-from stdnum.util import clean
+from stdnum.exceptions import *
 from stdnum.iso7064 import mod_11_10
+from stdnum.util import clean
 
 
 def compact(number):
@@ -45,12 +46,22 @@ def compact(number):
     return number
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid OIB number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) != 11:
+        raise InvalidLength()
+    mod_11_10.validate(number)
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid OIB number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) == 11 and number.isdigit() and \
-           mod_11_10.is_valid(number)

http://arthurdejong.org/git/python-stdnum/commit/?id=1932f69012c6247d89b55bbcd7fbd4f1c668a81d

commit 1932f69012c6247d89b55bbcd7fbd4f1c668a81d
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 13:22:44 2013 +0200

    Implement validate() for Greek numbers

diff --git a/stdnum/gr/vat.py b/stdnum/gr/vat.py
index 16c1986..0a28f43 100644
--- a/stdnum/gr/vat.py
+++ b/stdnum/gr/vat.py
@@ -1,7 +1,7 @@
 # vat.py - functions for handling Greek VAT numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -24,12 +24,15 @@ The FPA is a 9-digit number with a simple checksum.
 
 >>> compact('GR 23456783')
 '023456783'
->>> is_valid('EL 094259216 ')
-True
->>> is_valid('EL 123456781')
-False
+>>> validate('EL 094259216 ')
+'094259216'
+>>> validate('EL 123456781')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -53,12 +56,23 @@ def calc_check_digit(number):
     return str(checksum * 2 % 11 % 10)
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) != 9:
+        raise InvalidLength()
+    if calc_check_digit(number[:-1]) != number[-1]:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) == 9 and number.isdigit() and \
-           calc_check_digit(number[:-1]) == number[-1]

http://arthurdejong.org/git/python-stdnum/commit/?id=10710dcccf7ae453983d785e8fbcc5a53dfb485e

commit 10710dcccf7ae453983d785e8fbcc5a53dfb485e
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 13:18:31 2013 +0200

    Implement validate() for United Kingdom numbers

diff --git a/stdnum/gb/vat.py b/stdnum/gb/vat.py
index 98b4a35..f502a5c 100644
--- a/stdnum/gb/vat.py
+++ b/stdnum/gb/vat.py
@@ -1,6 +1,6 @@
 # vat.py - functions for handling United Kingdom VAT numbers
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -25,17 +25,18 @@ government departments (first two digits are GD) or a 
5-digit number for
 health authorities (first two digits are HA). The 9-digit variants use a
 weighted checksum.
 
->>> compact('GB 980 7806 84')
+>>> validate('GB 980 7806 84')
 '980780684'
->>> is_valid('980780684')
-True
->>> is_valid('802311781')  # invalid check digit
-False
+>>> validate('802311781')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 >>> format('980780684')
 '980 7806 84'
 """
 
 from stdnum.util import clean
+from stdnum.exceptions import *
 
 
 def compact(number):
@@ -54,26 +55,40 @@ def checksum(number):
     return sum(weights[i] * int(n) for i, n in enumerate(number)) % 97
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if len(number) == 5:
+        if not number[2:].isdigit():
+            raise InvalidFormat()
+        if number.startswith('GD') and int(number[2:]) < 500:
+            # government department
+            pass
+        elif number.startswith('HA') and int(number[2:]) >= 500:
+            # health authority
+            pass
+        else:
+            raise InvalidComponent()
+    elif len(number) in (9, 12):
+        if not number.isdigit():
+            raise InvalidFormat()
+        # standard number: nnn nnnn nn
+        # branch trader: nnn nnnn nn nnn (ignore the last thee digits)
+        if checksum(number[:9]) not in (0, 42):
+            raise InvalidChecksum()
+    else:
+        raise InvalidLength()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    if len(number) == 5 and number.startswith('GD') and number[2:].isdigit():
-        # government department
-        return int(number[2:]) < 500
-    if len(number) == 5 and number.startswith('HA') and number[2:].isdigit():
-        # health authority
-        return int(number[2:]) >= 500
-    if len(number) == 12 and number.isdigit():
-        # branch trader: nnn nnnn nn nnn (ignore the last thee digits)
-        return checksum(number[:-3]) in (0, 42)
-    if len(number) == 9 and number.isdigit():
-        # standard number: nnn nnnn nn
-        return checksum(number) in (0, 42)
-    return False
 
 
 def format(number):
diff --git a/tests/test_gb_vat.doctest b/tests/test_gb_vat.doctest
index 1537a5d..cdcfe36 100644
--- a/tests/test_gb_vat.doctest
+++ b/tests/test_gb_vat.doctest
@@ -1,6 +1,6 @@
 test_gb_vat.doctest - more detailed doctests for stdnum.gb.vat module
 
-Copyright (C) 2012 Arthur de Jong
+Copyright (C) 2012, 2013 Arthur de Jong
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -27,20 +27,46 @@ really useful as module documentation.
 
 Normal values that should just work.
 
->>> vat.is_valid('980780684')  # standard number
-True
->>> vat.is_valid('242338087388')  # branch trader
-True
->>> vat.is_valid('GD100')  # government department
-True
->>> vat.is_valid('HA501')  # health authority
-True
-
+>>> vat.validate('980780684')  # standard number
+'980780684'
+>>> vat.validate('242338087388')  # branch trader
+'242338087388'
+>>> vat.validate('GD100')  # government department
+'GD100'
+>>> vat.validate('HA501')  # health authority
+'HA501'
 
-Invalid checksum:
 
->>> vat.is_valid('802311781')  # invalid check digit
-False
+Invalid long numbers:
+
+>>> vat.validate('802311781')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> vat.validate('9807806842')  # too long for standard number
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
+>>> vat.validate('9807806B4')  # invalid digit
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+
+
+Some variations on the short format:
+
+>>> vat.validate('ZZ100')  # unknown code
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> vat.validate('GD600')  # government department with high number
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> vat.validate('HA201')  # health authority with low number
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
 
 
 Formatting tests:

http://arthurdejong.org/git/python-stdnum/commit/?id=4753c09e81a0adb6b62d12ad60f5a5107d5033c2

commit 4753c09e81a0adb6b62d12ad60f5a5107d5033c2
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 13:04:14 2013 +0200

    Implement validate() for Finnish numbers

diff --git a/stdnum/fi/alv.py b/stdnum/fi/alv.py
index 509ad9c..ed9457c 100644
--- a/stdnum/fi/alv.py
+++ b/stdnum/fi/alv.py
@@ -1,7 +1,7 @@
 # vat.py - functions for handling Finnish VAT numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -22,14 +22,15 @@
 
 The number is an 8-digit code with a weighted checksum.
 
->>> compact('FI- 2077474-0')
+>>> validate('FI 20774740')
 '20774740'
->>> is_valid('FI 20774740')
-True
->>> is_valid('FI 20774741')  # invalid check digit
-False
+>>> validate('FI 20774741')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -48,11 +49,23 @@ def checksum(number):
     return sum(weights[i] * int(n) for i, n in enumerate(number)) % 11
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) != 8:
+        raise InvalidLength()
+    if checksum(number) != 0:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return number.isdigit() and len(number) == 8 and checksum(number) == 0
diff --git a/stdnum/fi/hetu.py b/stdnum/fi/hetu.py
index b22465d..69b543e 100644
--- a/stdnum/fi/hetu.py
+++ b/stdnum/fi/hetu.py
@@ -2,6 +2,7 @@
 # coding: utf-8
 #
 # Copyright (C) 2011 Jussi Judin
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -25,12 +26,16 @@ See http://www.vaestorekisterikeskus.fi/default.aspx?id=45 
for checksum
 calculation details and http://tarkistusmerkit.teppovuori.fi/tarkmerk.htm#hetu1
 for historical details.
 
->>> is_valid('131052-308T')
-True
->>> is_valid('131052-308U')
-False
->>> is_valid('310252-308Y')
-False
+>>> validate('131052-308T')
+'131052-308T'
+>>> validate('131052-308U')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('310252-308Y')
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
 >>> compact('131052a308t')
 '131052A308T'
 """
@@ -38,12 +43,15 @@ False
 import re
 import datetime
 
+from stdnum.exceptions import *
+from stdnum.util import clean
+
 
 _century_codes = {
     '+': 1800,
     '-': 1900,
     'A': 2000,
-    }
+}
 
 # Finnish personal identity codes are composed of date part, century
 # indicating sign, individual number and control character.
@@ -56,23 +64,21 @@ _hetu_re = 
re.compile(r'^(?P<day>[0123]\d)(?P<month>[01]\d)(?P<year>\d\d)'
 def compact(number):
     """Convert the HETU to the minimal representation. This strips
     surrounding whitespace and converts it to upper case."""
-    return number.strip().upper()
+    return clean(number, '').upper().strip()
 
 
 def _calc_checksum(number):
     return '0123456789ABCDEFHJKLMNPRSTUVWXY'[int(number) % 31]
 
 
-def is_valid(number):
+def validate(number):
     """Checks to see if the number provided is a valid HETU. It checks the
     format, whether a valid date is given and whether the check digit is
     correct."""
-    try:
-        match = _hetu_re.search(compact(number))
-        if not match:
-            return False
-    except:
-        return False
+    number = compact(number)
+    match = _hetu_re.search(number)
+    if not match:
+        raise InvalidFormat()
     day = int(match.group('day'))
     month = int(match.group('month'))
     year = int(match.group('year'))
@@ -82,12 +88,24 @@ def is_valid(number):
     try:
         datetime.date(century + year, month, day)
     except ValueError:
-        return False
+        raise InvalidComponent()
     # for historical reasons individual IDs start from 002
     if individual < 2:
-        return False
+        raise InvalidComponent()
     checkable_number = '%02d%02d%02d%03d' % (day, month, year, individual)
-    return match.group('control') == _calc_checksum(checkable_number)
+    if match.group('control') != _calc_checksum(checkable_number):
+        raise InvalidChecksum()
+    return number
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid HETU. It checks the
+    format, whether a valid date is given and whether the check digit is
+    correct."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False
 
 
 # This is here just for completeness as there are no different length forms
diff --git a/tests/test_fi_hetu.doctest b/tests/test_fi_hetu.doctest
index 43f2f86..d68d12f 100644
--- a/tests/test_fi_hetu.doctest
+++ b/tests/test_fi_hetu.doctest
@@ -1,6 +1,7 @@
 test_fi_hetu.doctest - more detailed doctests for stdnum.fi.hetu module
 
 Copyright (C) 2011 Jussi Judin
+Copyright (C) 2013 Arthur de Jong
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -23,52 +24,67 @@ tries to cover more corner cases and detailed functionality 
that is not
 really useful as module documentation.
 
 >>> from stdnum.fi import hetu
+>>> from stdnum.exceptions import *
 
 
 Normal values that should just work.
 
->>> hetu.is_valid('131052-308T')
-True
->>> hetu.is_valid('131052+308T')
-True
->>> hetu.is_valid('131052A308T')
-True
->>> hetu.is_valid('131052a308t')
-True
+>>> hetu.validate('131052-308T')
+'131052-308T'
+>>> hetu.validate('131052+308T')
+'131052+308T'
+>>> hetu.validate('131052A308T')
+'131052A308T'
+>>> hetu.validate('131052a308t')
+'131052A308T'
 
 
 Invalid checksum:
 
->>> hetu.is_valid('131052-308U')
-False
+>>> hetu.validate('131052-308U')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 
 
 Invalid century indicator:
 
->>> hetu.is_valid('131052/308T')
-False
->>> hetu.is_valid('131052T308T')
-False
+>>> hetu.validate('131052/308T')
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> hetu.validate('131052T308T')
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
 
 
 Invalid birth date:
 
->>> hetu.is_valid('310252-308Y')
-False
->>> hetu.is_valid('130052-308R')
-False
+>>> hetu.validate('310252-308Y')
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> hetu.validate('130052-308R')
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
 
 Leaving out the first zero is wrong:
 
->>> hetu.is_valid('10101-0101')
-False
+>>> hetu.validate('10101-0101')
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
 
 
 Invalid individual number:
 (for historical reasons individual IDs start from 002)
 
->>> hetu.is_valid('131052-000V')
-False
+>>> hetu.validate('131052-000V')
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
 
 
 compact() and format() don't do much special:

http://arthurdejong.org/git/python-stdnum/commit/?id=2259cbb09e419e56c355efdbd865f99127d559c5

commit 2259cbb09e419e56c355efdbd865f99127d559c5
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 12:46:27 2013 +0200

    Implement validate() for Spanish numbers

diff --git a/stdnum/es/cif.py b/stdnum/es/cif.py
index 679e596..eeb0e72 100644
--- a/stdnum/es/cif.py
+++ b/stdnum/es/cif.py
@@ -1,7 +1,7 @@
 # cif.py - functions for handling Spanish fiscal numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -24,25 +24,32 @@ The CIF is a tax identification number for legal entities. 
It has 9 digits
 where the first digit is a letter (denoting the type of entity) and the
 last is a check digit (which may also be a letter).
 
->>> compact('J-99216582')
+>>> validate('J99216582')
 'J99216582'
->>> is_valid('J99216582')
-True
->>> is_valid('J99216583')  # invalid check digit
-False
->>> is_valid('M-1234567-L')
-True
->>> is_valid('O-1234567-L')  # invalid first character
-False
+>>> validate('J99216583')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('J992165831')  # too long
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
+>>> validate('M-1234567-L')
+'M1234567L'
+>>> validate('O-1234567-L')  # invalid first character
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
 >>> split('A13 585 625')
 ('A', '13', '58562', '5')
 """
 
 from stdnum import luhn
 from stdnum.es import dni
+from stdnum.exceptions import *
 
 
-__all__ = ['compact', 'is_valid', 'split']
+__all__ = ['compact', 'validate', 'is_valid', 'split']
 
 
 # use the same compact function as DNI
@@ -57,28 +64,40 @@ def calc_check_digits(number):
     return check + 'JABCDEFGHI'[int(check)]
 
 
-def is_valid(number):
+def validate(number):
     """Checks to see if the number provided is a valid DNI number. This
     checks the length, formatting and check digit."""
-    try:
-        number = compact(number)
-    except:
-        return False
-    if len(number) != 9 or not number[1:-1].isdigit():
-        return False
+    number = compact(number)
+    if not number[1:-1].isdigit():
+        raise InvalidFormat()
+    if len(number) != 9:
+        raise InvalidLength()
     if number[0] in 'KLM':
         # K: Spanish younger than 14 year old
         # L: Spanish living outside Spain without DNI
         # M: granted the tax to foreigners who have no NIE
         # these use the old checkdigit algorithm (the DNI one)
-        return number[-1] == dni.calc_check_digit(number[1:-1])
-    # there seems to be conflicting information on which organisation types
-    # should have which type of check digit (alphabetic or numeric) so
-    # we support either here
-    if number[0] in 'ABCDEFGHJNPQRSUVW':
-        return number[-1] in calc_check_digits(number[:-1])
-    # anything else is invalid
-    return False
+        if number[-1] != dni.calc_check_digit(number[1:-1]):
+            raise InvalidChecksum()
+    elif number[0] in 'ABCDEFGHJNPQRSUVW':
+        # there seems to be conflicting information on which organisation types
+        # should have which type of check digit (alphabetic or numeric) so
+        # we support either here
+        if number[-1] not in calc_check_digits(number[:-1]):
+            raise InvalidChecksum()
+    else:
+        # anything else is invalid
+        raise InvalidFormat()
+    return number
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid DNI number. This
+    checks the length, formatting and check digit."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False
 
 
 def split(number):
diff --git a/stdnum/es/dni.py b/stdnum/es/dni.py
index 3d5c508..18d0954 100644
--- a/stdnum/es/dni.py
+++ b/stdnum/es/dni.py
@@ -1,7 +1,7 @@
 # dni.py - functions for handling Spanish personal identity codes
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -26,14 +26,19 @@ digit is a checksum letter.
 Foreign nationals, since 2010 are issued an NIE (Número de Identificación
 de Extranjeros, Foreigner's Identity Number) instead.
 
->>> compact('54362315-K')
+>>> validate('54362315-K')
 '54362315K'
->>> is_valid('54362315-K')
-True
->>> is_valid('54362315Z')  # invalid check digit
-False
+>>> validate('54362315Z')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('54362315')  # digit missing
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -49,12 +54,23 @@ def calc_check_digit(number):
     return 'TRWAGMYFPDXBNJZSQVHLCKE'[int(number) % 23]
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid DNI number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not number[:-1].isdigit():
+        raise InvalidFormat()
+    if len(number) != 9:
+        raise InvalidLength()
+    if calc_check_digit(number[:-1]) != number[-1]:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid DNI number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return number[:-1].isdigit() and len(number) == 9 and \
-           calc_check_digit(number[:-1]) == number[-1]
diff --git a/stdnum/es/nie.py b/stdnum/es/nie.py
index fde7da0..9e510e6 100644
--- a/stdnum/es/nie.py
+++ b/stdnum/es/nie.py
@@ -1,7 +1,7 @@
 # nie.py - functions for handling Spanish foreigner identity codes
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -24,15 +24,20 @@ The NIE is an identification number for foreigners. It is a 
9 digit number
 where the first digit is either X, Y or Z and last digit is a checksum
 letter.
 
->>> compact('x-2482300w')
+>>> validate('x-2482300w')
 'X2482300W'
->>> is_valid('x-2482300w')
-True
->>> is_valid('x-2482300a')  # invalid check digit
-False
+>>> validate('x-2482300a')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('X2482300')  # digit missing
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
 """
 
 from stdnum.es import dni
+from stdnum.exceptions import *
 
 
 __all__ = ['compact', 'is_valid']
@@ -50,12 +55,23 @@ def calc_check_digit(number):
     return dni.calc_check_digit(number)
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid NIE. This checks
+    the length, formatting and check digit."""
+    number = compact(number)
+    if not number[1:-1].isdigit() or number[:1] not in 'XYZ':
+        raise InvalidFormat()
+    if len(number) != 9:
+        raise InvalidLength()
+    if calc_check_digit(number[:-1]) != number[-1]:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
-    """Checks to see if the number provided is a valid DNI number. This
-    checks the length, formatting and check digit."""
+    """Checks to see if the number provided is a valid NIE. This checks
+    the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) == 9 and number[1:-1].isdigit() and \
-           number[0] in 'XYZ' and calc_check_digit(number[:-1]) == number[-1]
diff --git a/stdnum/es/nif.py b/stdnum/es/nif.py
index e1bc7df..7cd689e 100644
--- a/stdnum/es/nif.py
+++ b/stdnum/es/nif.py
@@ -1,7 +1,7 @@
 # nif.py - functions for handling Spanish NIF (VAT) numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -30,18 +30,21 @@ entities and others).
 
 >>> compact('ES B-58378431')
 'B58378431'
->>> is_valid('B64717838')
-True
->>> is_valid('B64717839')  # invalid check digit
-False
->>> is_valid('54362315K')  # resident
-True
->>> is_valid('X-5253868-R')  # foreign person
-True
+>>> validate('B64717838')
+'B64717838'
+>>> validate('B64717839')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('54362315K')  # resident
+'54362315K'
+>>> validate('X-5253868-R')  # foreign person
+'X5253868R'
 """
 
-from stdnum.util import clean
 from stdnum.es import dni, nie, cif
+from stdnum.exceptions import *
+from stdnum.util import clean
 
 
 def compact(number):
@@ -53,20 +56,30 @@ def compact(number):
     return number
 
 
-def is_valid(number):
+def validate(number):
     """Checks to see if the number provided is a valid VAT number. This checks
     the length, formatting and check digit."""
-    try:
-        number = compact(number)
-    except:
-        return False
-    if len(number) != 9 or not number[1:-1].isdigit():
-        return False
+    number = compact(number)
+    if not number[1:-1].isdigit():
+        raise InvalidFormat()
+    if len(number) != 9:
+        raise InvalidLength()
     if number[0].isdigit():
         # natural resident
-        return dni.is_valid(number)
-    if number[0] in 'XYZ':
+        dni.validate(number)
+    elif number[0] in 'XYZ':
         # foreign natural person
-        return nie.is_valid(number)
-    # otherwise it has to be a valid CIF
-    return cif.is_valid(number)
+        nie.validate(number)
+    else:
+        # otherwise it has to be a valid CIF
+        cif.validate(number)
+    return number
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid VAT number. This checks
+    the length, formatting and check digit."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False

http://arthurdejong.org/git/python-stdnum/commit/?id=07c66e13b3928fb92fd0e537e23d3c5be2ad8956

commit 07c66e13b3928fb92fd0e537e23d3c5be2ad8956
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 11:59:19 2013 +0200

    Implement validate() for Estonian numbers

diff --git a/stdnum/ee/kmkr.py b/stdnum/ee/kmkr.py
index b8a3830..7a8dd30 100644
--- a/stdnum/ee/kmkr.py
+++ b/stdnum/ee/kmkr.py
@@ -1,7 +1,7 @@
 # kmkr.py - functions for handling Estonian VAT numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -22,12 +22,15 @@
 
 >>> compact('EE 100 931 558')
 '100931558'
->>> is_valid('100594102')
-True
->>> is_valid('100594103')  # incorrect check digit
-False
+>>> validate('100594102')
+'100594102'
+>>> validate('100594103')  # incorrect check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -46,11 +49,23 @@ def checksum(number):
     return sum(weights[i] * int(n) for i, n in enumerate(number)) % 10
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) != 9:
+        raise InvalidLength()
+    if checksum(number) != 0:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return number.isdigit() and len(number) == 9 and checksum(number) == 0

http://arthurdejong.org/git/python-stdnum/commit/?id=8caecc5c3d0036faf9d2344b052d0e7ab25ce533

commit 8caecc5c3d0036faf9d2344b052d0e7ab25ce533
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 11:47:20 2013 +0200

    Implement validate() for Danish numbers

diff --git a/stdnum/dk/cpr.py b/stdnum/dk/cpr.py
index 7a4cc0d..9be2cec 100644
--- a/stdnum/dk/cpr.py
+++ b/stdnum/dk/cpr.py
@@ -1,6 +1,6 @@
 # cpr.py - functions for handling Danish CPR numbers
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -27,14 +27,14 @@ digit of the sequence number indicates the century.
 The numbers used to validate using a checksum but since the sequence
 numbers ran out this was abandoned in 2007.
 
->>> compact('211062-5629')
+>>> validate('211062-5629')
 '2110625629'
->>> is_valid('211062-5629')
-True
 >>> checksum('2110625629')
 0
->>> is_valid('511062-5629')  # invalid date
-False
+>>> validate('511062-5629')  # invalid date
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
 >>> get_birth_date('2110620629')
 datetime.date(1962, 10, 21)
 >>> get_birth_date('2110525629')
@@ -45,6 +45,7 @@ datetime.date(2052, 10, 21)
 
 import datetime
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -72,25 +73,33 @@ def get_birth_date(number):
         year += 1900
     else:
         year += 2000
-    return datetime.date(year, month, day)
+    try:
+        return datetime.date(year, month, day)
+    except ValueError:
+        raise InvalidComponent()
 
 
-def is_valid(number):
-    """Checks to see if the number provided is a valid VAT number. This checks
-    the length, formatting and check digit."""
-    try:
-        number = compact(number)
-    except:
-        return False
-    if not number.isdigit() or len(number) != 10:
-        return False
+def validate(number):
+    """Checks to see if the number provided is a valid CPR number. This
+    checks the length, formatting, embedded date  and check digit."""
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) != 10:
+        raise InvalidLength()
     # check if birth date is valid
+    birth_date = get_birth_date(number)
+    # TODO: check that the birth date is not in the future
+    return number
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid CPR number. This
+    checks the length, formatting, embedded date  and check digit."""
     try:
-        birth_date = get_birth_date(number)
-        # TODO: check that the birth date is not in the future
-    except ValueError:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return True
 
 
 def format(number):
diff --git a/stdnum/dk/cvr.py b/stdnum/dk/cvr.py
index 6dbbfe8..7ded1ac 100644
--- a/stdnum/dk/cvr.py
+++ b/stdnum/dk/cvr.py
@@ -1,6 +1,6 @@
 # cvr.py - functions for handling Danish CVR numbers
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -22,14 +22,15 @@
 The CVR (Momsregistreringsnummer, VAT) is an 8 digit number with a
 straightforward check mechanism.
 
->>> compact('DK 13585628')
+>>> validate('DK 13585628')
 '13585628'
->>> is_valid('DK 13585628')
-True
->>> is_valid('DK 13585627')
-False
+>>> validate('DK 13585627')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -48,12 +49,23 @@ def checksum(number):
     return sum(weights[i] * int(n) for i, n in enumerate(number)) % 11
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This checks
+    the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit() or number[0] == '0':
+        raise InvalidFormat()
+    if len(number) != 8:
+        raise InvalidLength()
+    if checksum(number) != 0:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This checks
     the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) == 8 and number.isdigit() and \
-           number[0] != '0' and checksum(number) == 0

http://arthurdejong.org/git/python-stdnum/commit/?id=360480bc26844e8c8e1e4fbbc154204b30a9337b

commit 360480bc26844e8c8e1e4fbbc154204b30a9337b
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 11:29:06 2013 +0200

    Implement validate() for German numbers

diff --git a/stdnum/de/vat.py b/stdnum/de/vat.py
index 1151585..b36259b 100644
--- a/stdnum/de/vat.py
+++ b/stdnum/de/vat.py
@@ -1,6 +1,6 @@
 # vat.py - functions for handling German VAT numbers
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -24,12 +24,15 @@ algorithm.
 
 >>> compact('DE 136,695 976')
 '136695976'
->>> is_valid('DE136695976')
-True
->>> is_valid('136695978')
-False
+>>> validate('DE136695976')
+'136695976'
+>>> validate('136695978')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.iso7064 import mod_11_10
 from stdnum.util import clean
 
@@ -43,12 +46,22 @@ def compact(number):
     return number
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This checks
+    the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit() or number[0] == '0':
+        raise InvalidFormat()
+    if len(number) != 9:
+        raise InvalidLength()
+    mod_11_10.validate(number)
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This checks
     the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) == 9 and number.isdigit() and \
-           number[0] != '0' and mod_11_10.is_valid(number)

http://arthurdejong.org/git/python-stdnum/commit/?id=fce61960476f9e416f16b22949b58f7859fdb8e9

commit fce61960476f9e416f16b22949b58f7859fdb8e9
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 11:18:06 2013 +0200

    Implement validate() for Cypriot numbers

diff --git a/stdnum/cy/vat.py b/stdnum/cy/vat.py
index 789bd5a..7ab320e 100644
--- a/stdnum/cy/vat.py
+++ b/stdnum/cy/vat.py
@@ -1,7 +1,7 @@
 # vat.py - functions for handling Cypriot VAT numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -25,12 +25,15 @@ where the last one is a is a letter and functions as a 
check digit.
 
 >>> compact('CY-10259033P')
 '10259033P'
->>> is_valid('CY-10259033P ')
-True
->>> is_valid('CY-10259033Z')  # invalid check digit
-False
+>>> validate('CY-10259033P ')
+'10259033P'
+>>> validate('CY-10259033Z')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -56,13 +59,25 @@ def calc_check_digit(number):
     ) % 26]
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not number[:-1].isdigit():
+        raise InvalidFormat()
+    if len(number) != 9:
+        raise InvalidLength()
+    if number[0:2] == '12':
+        raise InvalidComponent()
+    if number[-1] != calc_check_digit(number[:-1]):
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) == 9 and number[:-1].isdigit() and \
-           number[0:2] != '12' and \
-           number[-1] == calc_check_digit(number[:-1])

http://arthurdejong.org/git/python-stdnum/commit/?id=14e382ff517bc5c4e21eb062905bad895c42dd77

commit 14e382ff517bc5c4e21eb062905bad895c42dd77
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 11:18:06 2013 +0200

    Implement validate() for Czech numbers

diff --git a/stdnum/cz/dic.py b/stdnum/cz/dic.py
index 5b57e15..fc24b24 100644
--- a/stdnum/cz/dic.py
+++ b/stdnum/cz/dic.py
@@ -1,7 +1,7 @@
 # dic.py - functions for handling Czech VAT numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -28,17 +28,20 @@ that begin with a 6).
 
 >>> compact('CZ 25123891')
 '25123891'
->>> is_valid('25123891')  # legal entity
-True
->>> is_valid('25123890')  # incorrect check digit
-False
->>> is_valid('7103192745')  # RČ
-True
->>> is_valid('640903926')  # special case
-True
+>>> validate('25123891')  # legal entity
+'25123891'
+>>> validate('25123890')  # incorrect check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('7103192745')  # RČ
+'7103192745'
+>>> validate('640903926')  # special case
+'640903926'
 """
 
 from stdnum.cz import rc
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -65,22 +68,34 @@ def calc_check_digit_special(number):
     return str(9 - check % 10)
 
 
-def is_valid(number):
+def validate(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
-    try:
-        number = compact(number)
-    except:
-        return False
+    number = compact(number)
     if not number.isdigit():
-        return False
-    if len(number) == 8 and not number.startswith('9'):
+        raise InvalidFormat()
+    if len(number) == 8:
         # legal entities
-        return calc_check_digit_legal(number[:-1]) == number[-1]
-    if len(number) == 9 and number.startswith('6'):
+        if number.startswith('9'):
+            raise InvalidComponent()
+        if calc_check_digit_legal(number[:-1]) != number[-1]:
+            raise InvalidChecksum()
+    elif len(number) == 9 and number.startswith('6'):
         # special cases (skip first digit in calculation)
-        return calc_check_digit_special(number[1:-1]) == number[-1]
-    if len(number) in (9, 10):
+        if calc_check_digit_special(number[1:-1]) != number[-1]:
+            raise InvalidChecksum()
+    elif len(number) in (9, 10):
         # 9 or 10 digit individual
-        return rc.is_valid(number)
-    return False
+        rc.validate(number)
+    else:
+        raise InvalidLength()
+    return number
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False
diff --git a/stdnum/cz/rc.py b/stdnum/cz/rc.py
index f7b9e82..d8b1c4d 100644
--- a/stdnum/cz/rc.py
+++ b/stdnum/cz/rc.py
@@ -1,7 +1,7 @@
 # rc.py - functions for handling Czech birth numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -27,24 +27,29 @@ person and their gender.
 
 This number is identical to the Slovak counterpart.
 
->>> compact('710319/2745')
+>>> validate('710319/2745')
 '7103192745'
->>> is_valid('7103192745')
-True
->>> is_valid('991231123')
-True
->>> is_valid('7103192746')  # invalid check digit
-False
->>> is_valid('1103492745')  # invalid date
-False
->>> is_valid('590312/123')  # 9 digit number in 1959
-False
+>>> validate('991231123')
+'991231123'
+>>> validate('7103192746')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('1103492745')  # invalid date
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> validate('590312/123')  # 9 digit number in 1959
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
 >>> format('7103192745')
 '710319/2745'
 """
 
 import datetime
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -66,35 +71,44 @@ def get_birth_date(number):
         if year >= 1980:
             year -= 100
         if year > 1953:
-            raise ValueError('No 9 digit birth numbers after 1953.')
+            raise InvalidLength('No 9 digit birth numbers after 1953.')
     elif year < 1954:
         year += 100
-    return datetime.date(year, month, day)
+    try:
+        return datetime.date(year, month, day)
+    except ValueError:
+        raise InvalidComponent()
 
 
-def is_valid(number):
+def validate(number):
     """Checks to see if the number provided is a valid birth number. This
     checks the length, formatting, embedded date and check digit."""
-    try:
-        number = compact(number)
-    except:
-        return False
-    if not number.isdigit() or len(number) not in (9, 10):
-        return False
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) not in (9, 10):
+        raise InvalidLength()
     # check if birth date is valid
-    try:
-        birth_date = get_birth_date(number)
-        # TODO: check that the birth date is not in the future
-    except ValueError:
-        return False
-    # check the check digit
+    birth_date = get_birth_date(number)
+    # TODO: check that the birth date is not in the future
+    # check the check digit (10 digit numbers only)
     if len(number) == 10:
         check = int(number[:-1]) % 11
         # before 1985 the checksum could be 0 or 10
         if birth_date < datetime.date(1985, 1, 1):
             check = check % 10
-        return number[-1] == str(check)
-    return True
+        if number[-1] != str(check):
+            raise InvalidChecksum()
+    return number
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid birth number. This
+    checks the length, formatting, embedded date and check digit."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False
 
 
 def format(number):

http://arthurdejong.org/git/python-stdnum/commit/?id=54ce2d77655d57571eb53276bc9b45c18f1cdfc6

commit 54ce2d77655d57571eb53276bc9b45c18f1cdfc6
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 10:52:42 2013 +0200

    Implement validate() for Brazillian numbers

diff --git a/stdnum/br/cpf.py b/stdnum/br/cpf.py
index 9eed595..742d464 100644
--- a/stdnum/br/cpf.py
+++ b/stdnum/br/cpf.py
@@ -1,7 +1,7 @@
 # cpf.py - functions for handling CPF numbers
 # coding: utf-8
 #
-# Copyright (C) 2011, 2012 Arthur de Jong
+# Copyright (C) 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -20,16 +20,21 @@
 
 """CPF (Cadastro de Pessoas Físicas, Brazillian national identifier).
 
->>> is_valid('390.533.447-05')
-True
->>> is_valid('231.002.999-00')
-False
->>> compact('390.533.447-05')
+>>> validate('390.533.447-05')
 '39053344705'
+>>> validate('231.002.999-00')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('390.533.447=0')  # invalid delimiter
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
 >>> format('23100299900')
 '231.002.999-00'
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -48,17 +53,26 @@ def _calc_check_digits(number):
     return '%d%d' % (d1, d2)
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid CPF. This checks
+    the length and whether the check digit is correct."""
+    number = compact(number)
+    if not number.isdigit() or int(number) <= 0:
+        raise InvalidFormat()
+    if len(number) != 11:
+        raise InvalidLength()
+    if _calc_check_digits(number) != number[-2:]:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
-    """Checks to see if the number provided is a valid BSN. This checks
+    """Checks to see if the number provided is a valid CPF. This checks
     the length and whether the check digit is correct."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) == 11 and \
-           number.isdigit() and \
-           int(number) > 0 and \
-           _calc_check_digits(number) == number[-2:]
 
 
 def format(number):

http://arthurdejong.org/git/python-stdnum/commit/?id=608090744d97a68eede6d49bd01d2b26c324a337

commit 608090744d97a68eede6d49bd01d2b26c324a337
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 10:44:42 2013 +0200

    Implement validate() for Bulgarian numbers

diff --git a/stdnum/bg/egn.py b/stdnum/bg/egn.py
index d704543..7f94946 100644
--- a/stdnum/bg/egn.py
+++ b/stdnum/bg/egn.py
@@ -1,7 +1,7 @@
 # egn.py - functions for handling Bulgarian national identification numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -27,18 +27,23 @@ digit.
 
 >>> compact('752316 926 3')
 '7523169263'
->>> is_valid('8032056031')
-True
+>>> validate('8032056031')
+'8032056031'
 >>> get_birth_date('7542011030')
 datetime.date(2075, 2, 1)
->>> is_valid('7552010004')  # invalid check digit
-False
->>> is_valid('8019010008')  # invalid date
-False
+>>> validate('7552A10004')  # invalid digit
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> validate('8019010008')  # invalid date
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
 """
 
 import datetime
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -66,24 +71,35 @@ def get_birth_date(number):
     elif month > 20:
         year -= 100
         month -= 20
-    return datetime.date(year, month, day)
+    try:
+        return datetime.date(year, month, day)
+    except ValueError:
+        raise InvalidComponent()
 
 
-def is_valid(number):
+def validate(number):
     """Checks to see if the number provided is a valid national
     identification number. This checks the length, formatting, embedded
     date and check digit."""
-    try:
-        number = compact(number)
-    except:
-        return False
-    if not number.isdigit() or len(number) != 10:
-        return False
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) != 10:
+        raise InvalidLength()
     # check if birth date is valid
+    birth_date = get_birth_date(number)
+    # TODO: check that the birth date is not in the future
+    # check the check digit
+    if calc_check_digit(number[:-1]) != number[-1]:
+        raise InvalidChecksum()
+    return number
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid national
+    identification number. This checks the length, formatting, embedded
+    date and check digit."""
     try:
-        birth_date = get_birth_date(number)
-        # TODO: check that the birth date is not in the future
-    except ValueError:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    # check the check digit
-    return calc_check_digit(number[:-1]) == number[-1]
diff --git a/stdnum/bg/pnf.py b/stdnum/bg/pnf.py
index e03d33a..bddfb2b 100644
--- a/stdnum/bg/pnf.py
+++ b/stdnum/bg/pnf.py
@@ -1,7 +1,7 @@
 # pnf.py - functions for handling Bulgarian personal number of a foreigner
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -23,14 +23,19 @@
 The personal number of a foreigner is a 10-digit number where the last digit
 is the result of a weighted checksum.
 
->>> compact('7111 042 925')
+>>> validate('7111 042 925')
 '7111042925'
->>> is_valid('7111042925')
-True
->>> is_valid('7111042922')  # invalid check digit
-False
+>>> validate('7111042922')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('71110A2922')  # invalid digit
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -47,13 +52,25 @@ def calc_check_digit(number):
     return str(sum(weights[i] * int(n) for i, n in enumerate(number)) % 10)
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid national
+    identification number. This checks the length, formatting, embedded
+    date and check digit."""
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) != 10:
+        raise InvalidLength()
+    if calc_check_digit(number[:-1]) != number[-1]:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid national
     identification number. This checks the length, formatting, embedded
     date and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return number.isdigit() and len(number) == 10 and \
-           calc_check_digit(number[:-1]) == number[-1]
diff --git a/stdnum/bg/vat.py b/stdnum/bg/vat.py
index 644af68..e04141d 100644
--- a/stdnum/bg/vat.py
+++ b/stdnum/bg/vat.py
@@ -1,7 +1,7 @@
 # vat.py - functions for handling Bulgarian VAT numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -26,14 +26,17 @@ others) long. Each type of number has it's own check digit 
algorithm.
 
 >>> compact('BG 175 074 752')
 '175074752'
->>> is_valid('175074752')
-True
->>> is_valid('175074751')  # invalid check digit
-False
+>>> validate('175074752')
+'175074752'
+>>> validate('175074751')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
-from stdnum.util import clean
 from stdnum.bg import egn, pnf
+from stdnum.exceptions import *
+from stdnum.util import clean
 
 
 def compact(number):
@@ -61,19 +64,30 @@ def calc_check_digit_other(number):
     return str((11 - sum(weights[i] * int(n) for i, n in enumerate(number))) % 
11)
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) == 9:
+        # 9 digit numbers are for legal entities
+        if number[-1] != calc_check_digit_legal(number[:-1]):
+            raise InvalidChecksum()
+    elif len(number) == 10:
+        # 10 digit numbers are for physical persons, foreigners and others
+        if not egn.is_valid(number) and not pnf.is_valid(number) and \
+                number[-1] != calc_check_digit_other(number[:-1]):
+            raise InvalidChecksum()
+    else:
+        raise InvalidLength()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    if len(number) == 9 and number.isdigit():
-        # 9 digit numbers are for legal entities
-        return number[-1] == calc_check_digit_legal(number[:-1])
-    if len(number) == 10 and number.isdigit():
-        # 10 digit numbers are for physical persons, foreigners and others
-        return egn.is_valid(number) or \
-               pnf.is_valid(number) or \
-               number[-1] == calc_check_digit_other(number[:-1])
-    return False
diff --git a/tests/test_bg_vat.doctest b/tests/test_bg_vat.doctest
index 0d60891..4fb1a0b 100644
--- a/tests/test_bg_vat.doctest
+++ b/tests/test_bg_vat.doctest
@@ -1,6 +1,6 @@
 test_bg_vat.doctest - more detailed doctests for stdnum.bg.vat module
 
-Copyright (C) 2012 Arthur de Jong
+Copyright (C) 2012, 2013 Arthur de Jong
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -27,23 +27,25 @@ really useful as module documentation.
 
 Normal values that should just work.
 
->>> vat.is_valid('103873594')  # 9-digit legal entity
-True
->>> vat.is_valid('131272009')  # legal entity with fallback checksum
-True
->>> vat.is_valid('7501020018')  # physical person
-True
->>> vat.is_valid('8001010008')  # physical person
-True
->>> vat.is_valid('8032056031')  # physical person
-True
->>> vat.is_valid('7111042925')  # foreigners
-True
->>> vat.is_valid('7153849522')  # others
-True
+>>> vat.validate('103873594')  # 9-digit legal entity
+'103873594'
+>>> vat.validate('131272009')  # legal entity with fallback checksum
+'131272009'
+>>> vat.validate('7501020018')  # physical person
+'7501020018'
+>>> vat.validate('8001010008')  # physical person
+'8001010008'
+>>> vat.validate('8032056031')  # physical person
+'8032056031'
+>>> vat.validate('7111042925')  # foreigners
+'7111042925'
+>>> vat.validate('7153849522')  # others
+'7153849522'
 
 
 Invalid checksum:
 
->>> vat.is_valid('175074751')  # invalid check digit
-False
+>>> vat.validate('175074751')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...

http://arthurdejong.org/git/python-stdnum/commit/?id=33ce4e99a38f997943a755a41b1061114634e11d

commit 33ce4e99a38f997943a755a41b1061114634e11d
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 10:32:19 2013 +0200

    Implement validate() for Belgian numbers

diff --git a/stdnum/be/vat.py b/stdnum/be/vat.py
index 28b3822..5bffa3f 100644
--- a/stdnum/be/vat.py
+++ b/stdnum/be/vat.py
@@ -1,6 +1,6 @@
 # vat.py - functions for handling Belgian VAT numbers
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -23,12 +23,15 @@
 '0403019261'
 >>> compact('(0)403019261')
 '0403019261'
->>> is_valid('BE 428759497')
-True
->>> is_valid('BE431150351')
-False
+>>> validate('BE 428759497')
+'0428759497'
+>>> validate('BE431150351')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -50,11 +53,23 @@ def checksum(number):
     return (int(number[:-2]) + int(number[-2:])) % 97
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This checks
+    the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) != 10:
+        raise InvalidLength()
+    if checksum(number) != 0:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This checks
     the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) == 10 and number.isdigit() and checksum(number) == 0

http://arthurdejong.org/git/python-stdnum/commit/?id=66d6259f692676291a2466bd02427fec6d2549a4

commit 66d6259f692676291a2466bd02427fec6d2549a4
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 10:28:38 2013 +0200

    Implement validate() for Austrian numbers

diff --git a/stdnum/at/uid.py b/stdnum/at/uid.py
index ff01200..a070d5a 100644
--- a/stdnum/at/uid.py
+++ b/stdnum/at/uid.py
@@ -1,6 +1,6 @@
 # vat.py - functions for handling Austrian VAT numbers
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -22,17 +22,18 @@
 The Austrian UID is a 9-digit number that starts with a U (optionally
 preceded with AT). The last digit is a check digit.
 
->>> compact('AT U13585627')
+>>> validate('AT U13585627')
 'U13585627'
->>> is_valid('U13585627')
-True
 >>> calc_check_digit('U1358562')
 '7'
->>> is_valid('U13585626')  # incorrect check digit
-False
+>>> validate('U13585626')  # incorrect check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
 from stdnum import luhn
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -51,13 +52,23 @@ def calc_check_digit(number):
     return str((6 - luhn.checksum(number[1:])) % 10)
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This checks
+    the length, formatting and check digit."""
+    number = compact(number)
+    if number[:1] != 'U' or not number[1:].isdigit():
+        raise InvalidFormat()
+    if len(number) != 9:
+        raise InvalidLength()
+    if calc_check_digit(number[:-1]) != number[-1]:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This checks
     the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) == 9 and number[0] == 'U' and \
-           number[1:].isdigit() and \
-           calc_check_digit(number[:-1]) == number[-1]

http://arthurdejong.org/git/python-stdnum/commit/?id=05547a4d9feac13aba968745e25be34d911bfb65

commit 05547a4d9feac13aba968745e25be34d911bfb65
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sat May 11 23:51:53 2013 +0200

    Implement validate() for Romanian numbers

diff --git a/stdnum/ro/cf.py b/stdnum/ro/cf.py
index 7f676bc..a27f980 100644
--- a/stdnum/ro/cf.py
+++ b/stdnum/ro/cf.py
@@ -1,7 +1,7 @@
 # cf.py - functions for handling Romanian CF (VAT) numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -22,16 +22,17 @@
 
 The Romanian CF is used for VAT purposes and can be from 2 to 10 digits long.
 
->>> compact('RO 185 472 90')
+>>> validate('RO 185 472 90')
 '18547290'
->>> is_valid('185 472 90')
-True
->>> is_valid('185 472 91')  # invalid check digit
-False
->>> is_valid('1630615123457')  # personal code
-True
+>>> validate('185 472 91')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('1630615123457')  # personal code
+'1630615123457'
 """
 
+from stdnum.exceptions import *
 from stdnum.ro import cnp
 from stdnum.util import clean
 
@@ -54,18 +55,27 @@ def calc_check_digit(number):
     return str(check % 11 % 10)
 
 
-def is_valid(number):
+def validate(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
-    try:
-        number = compact(number)
-    except:
-        return False
+    number = compact(number)
     if not number.isdigit() or number[0] == '0':
-        return False
+        raise InvalidFormat()
     if len(number) == 13:
         # apparently a CNP can also be used (however, not all sources agree)
-        return cnp.is_valid(number)
+        cnp.validate(number)
     elif 2 <= len(number) <= 10:
-        return calc_check_digit(number[:-1]) == number[-1]
-    return False
+        if calc_check_digit(number[:-1]) != number[-1]:
+            raise InvalidChecksum()
+    else:
+        raise InvalidLength()
+    return number
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False
diff --git a/stdnum/ro/cnp.py b/stdnum/ro/cnp.py
index f36b3bb..bb57172 100644
--- a/stdnum/ro/cnp.py
+++ b/stdnum/ro/cnp.py
@@ -1,7 +1,7 @@
 # cnp.py - functions for handling Romanian CNP numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -23,20 +23,25 @@
 The CNP is a 13 digit number that includes information on the person's
 gender, birth date and country zone.
 
->>> compact('1630615123457')
+>>> validate('1630615123457')
 '1630615123457'
->>> is_valid('1630615123457')
-True
->>> is_valid('8800101221144')  # invalid first digit
-False
->>> is_valid('1632215123457')  # invalid date
-False
->>> is_valid('1630615123458')  # invalid check digit
-False
+>>> validate('8800101221144')  # invalid first digit
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> validate('1632215123457')  # invalid date
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> validate('1630615123458')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
 import datetime
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -63,26 +68,34 @@ def get_birth_date(number):
     year = int(number[1:3]) + centuries.get(number[0], 1900)
     month = int(number[3:5])
     day = int(number[5:7])
-    return datetime.date(year, month, day)
+    try:
+        return datetime.date(year, month, day)
+    except ValueError:
+        raise InvalidComponent()
 
 
-def is_valid(number):
+def validate(number):
     """Checks to see if the number provided is a valid VAT number. This checks
     the length, formatting and check digit."""
-    try:
-        number = compact(number)
-    except:
-        return False
-    if len(number) != 13 or not number.isdigit():
-        return False
+    number = compact(number)
     # first digit should be a known one (9=foreigner)
-    if number[0] not in '1234569':
-        return False
+    if not number.isdigit() or number[0] not in '1234569':
+        raise InvalidFormat()
+    if len(number) != 13:
+        raise InvalidLength()
     # check if birth date is valid
+    birth_date = get_birth_date(number)
+    # TODO: check that the birth date is not in the future
+    # number[7:9] is the county, we ignore it for now, just check last digit
+    if calc_check_digit(number[:-1]) != number[-1]:
+        raise InvalidChecksum()
+    return number
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid VAT number. This checks
+    the length, formatting and check digit."""
     try:
-        birth_date = get_birth_date(number)
-        # TODO: check that the birth date is not in the future
-    except ValueError:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    # number[7:9] is the county, we ignore it for now, just check last digit
-    return calc_check_digit(number[:-1]) == number[-1]

http://arthurdejong.org/git/python-stdnum/commit/?id=fc1432c89bb0e9fe5e1f5c3c2214b7869a458d96

commit fc1432c89bb0e9fe5e1f5c3c2214b7869a458d96
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 17 23:30:58 2013 +0200

    Implement validate() for French numbers

diff --git a/stdnum/fr/siren.py b/stdnum/fr/siren.py
index c3a4b9a..f57b0fa 100644
--- a/stdnum/fr/siren.py
+++ b/stdnum/fr/siren.py
@@ -1,7 +1,7 @@
 # siren.py - functions for handling French SIREN numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -26,16 +26,19 @@ to validate the numbers.
 
 >>> compact('552 008 443')
 '552008443'
->>> is_valid('404833048')
-True
->>> is_valid('404833047')
-False
+>>> validate('404833048')
+'404833048'
+>>> validate('404833047')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 >>> to_tva('443 121 975')
 '46 443 121 975'
 """
 
-from stdnum.util import clean
 from stdnum import luhn
+from stdnum.exceptions import *
+from stdnum.util import clean
 
 
 # An online validation function is available but it does not provide an
@@ -50,14 +53,25 @@ def compact(number):
     return clean(number, ' ').strip()
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid number. This checks
+    the length, formatting and check digit."""
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) != 9:
+        raise InvalidLength()
+    luhn.validate(number)
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid number. This checks
     the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) == 9 and number.isdigit() and luhn.is_valid(number)
 
 
 def to_tva(number):
diff --git a/stdnum/fr/tva.py b/stdnum/fr/tva.py
index c6f8e01..dca4f49 100644
--- a/stdnum/fr/tva.py
+++ b/stdnum/fr/tva.py
@@ -1,7 +1,7 @@
 # tva.py - functions for handling French TVA numbers
 # coding: utf-8
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -27,18 +27,21 @@ style numbers at least one is a alphabetic.
 
 >>> compact('Fr 40 303 265 045')
 '40303265045'
->>> is_valid('23334175221')
-True
->>> is_valid('84 323 140 391')  # incorrect check digit
-False
->>> is_valid('K7399859412')  # new-style number
-True
->>> is_valid('4Z123456782')  # new-style number starting with digit
-True
+>>> validate('23334175221')
+'23334175221'
+>>> validate('84 323 140 391')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('K7399859412')  # new-style number
+'K7399859412'
+>>> validate('4Z123456782')  # new-style number starting with digit
+'4Z123456782'
 """
 
-from stdnum.util import clean
+from stdnum.exceptions import *
 from stdnum.fr import siren
+from stdnum.util import clean
 
 
 # the valid characters for the first two digits (O and I are missing)
@@ -54,26 +57,38 @@ def compact(number):
     return number
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid VAT number. This
+    checks the length, formatting and check digit."""
+    number = compact(number)
+    if not all(x in _alphabet for x in number[:2]):
+        raise InvalidFormat()
+    if len(number) != 11:
+        raise InvalidLength()
+    siren.validate(number[2:])
+    if number.isdigit():
+        # all-numeric digits
+        if int(number[:2]) != (int(number[2:] + '12') % 97):
+            raise InvalidChecksum()
+    else:
+        # one of the first two digits isn't a number
+        if number[0].isdigit():
+            check = (
+                _alphabet.index(number[0]) * 24 +
+                _alphabet.index(number[1]) - 10)
+        else:
+            check = (
+                _alphabet.index(number[0]) * 34 +
+                _alphabet.index(number[1]) - 100)
+        if (int(number[2:]) + 1 + check // 11) % 11 != (check % 11):
+            raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid VAT number. This
     checks the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
-        return False
-    if len(number) == 11 and siren.is_valid(number[2:]) and \
-       number[0] in _alphabet and number[1] in _alphabet:
-        if number.isdigit():
-            # all-numeric digits
-            return int(number[:2]) == int(number[2:] + '12') % 97
-        else:
-            # one of the first two digits isn't a number
-            if number[0].isdigit():
-                check = _alphabet.index(number[0]) * 24 + \
-                        _alphabet.index(number[1]) - 10
-            else:
-                check = _alphabet.index(number[0]) * 34 + \
-                        _alphabet.index(number[1]) - 100
-            return (int(number[2:]) + 1 + check // 11 ) % 11 == check % 11
-    else:
+        return bool(validate(number))
+    except ValidationError:
         return False

http://arthurdejong.org/git/python-stdnum/commit/?id=62cafb46aa2da2a5309546fc23e9d6af2c353ad3

commit 62cafb46aa2da2a5309546fc23e9d6af2c353ad3
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sun May 5 23:33:22 2013 +0200

    Implement validate() for Dutch numbers

diff --git a/stdnum/nl/bsn.py b/stdnum/nl/bsn.py
index 24c804b..b8926c9 100644
--- a/stdnum/nl/bsn.py
+++ b/stdnum/nl/bsn.py
@@ -1,6 +1,6 @@
 # bsn.py - functions for handling BSNs
 #
-# Copyright (C) 2010, 2011, 2012 Arthur de Jong
+# Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -22,16 +22,21 @@
 The BSN is a number with up to 9 digits (the leading 0's are commonly left
 out) which is used as the Dutch national identification number.
 
->>> is_valid('111222333')
-True
->>> is_valid('111252333')
-False
->>> compact('1234.56.782')
-'123456782'
+>>> validate('1112.22.333')
+'111222333'
+>>> validate('1112.52.333')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('1112223334')
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
 >>> format('111222333')
 '1112.22.333'
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -50,15 +55,26 @@ def checksum(number):
             int(number[-1])) % 11
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid BSN. This checks
+    the length and whether the check digit is correct."""
+    number = compact(number)
+    if not number.isdigit() or int(number) <= 0:
+        raise InvalidFormat()
+    if len(number) != 9:
+        raise InvalidLength()
+    if checksum(number) != 0:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid BSN. This checks
     the length and whether the check digit is correct."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) == 9 and number.isdigit() and \
-           int(number) > 0 and checksum(number) == 0
 
 
 def format(number):
diff --git a/stdnum/nl/btw.py b/stdnum/nl/btw.py
index eea0cce..77a7981 100644
--- a/stdnum/nl/btw.py
+++ b/stdnum/nl/btw.py
@@ -1,6 +1,6 @@
 # btw.py - functions for handling Dutch VAT numbers
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -23,14 +23,17 @@ The BTW-nummer is the Dutch number for VAT. It consists of 
a RSIN or BSN
 followed by a B and two digits that identify the unit within the
 organisation (usually 01).
 
->>> is_valid('004495445B01')
-True
->>> is_valid('NL4495445B01')
-True
->>> is_valid('123456789B90')
-False
+>>> validate('004495445B01')
+'004495445B01'
+>>> validate('NL4495445B01')
+'004495445B01'
+>>> validate('123456789B90')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.nl import bsn
 from stdnum.util import clean
 
@@ -44,14 +47,24 @@ def compact(number):
     return bsn.compact(number[:-3]) + number[-3:]
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid BTW number. This checks
+    the length, formatting and check digit."""
+    number = compact(number)
+    if not number[10:].isdigit() or int(number[10:]) <= 0:
+        raise InvalidFormat()
+    if len(number) != 12:
+        raise InvalidLength()
+    if number[9] != 'B':
+        raise InvalidFormat()
+    bsn.validate(number[:9])
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid BTW number. This checks
     the length, formatting and check digit."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) == 12 and \
-           number[9] == 'B' and \
-           number[10:].isdigit() and int(number[10:]) > 0 and \
-           bsn.is_valid(number[0:9])
diff --git a/stdnum/nl/onderwijsnummer.py b/stdnum/nl/onderwijsnummer.py
index a2bf610..db29fa1 100644
--- a/stdnum/nl/onderwijsnummer.py
+++ b/stdnum/nl/onderwijsnummer.py
@@ -1,6 +1,6 @@
 # onderwijsnummer.py - functions for handling onderwijsnummers
 #
-# Copyright (C) 2012 Arthur de Jong
+# Copyright (C) 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -23,18 +23,43 @@ The onderwijsnummers (school number) is very similar to the 
BSN (Dutch
 national identification number) but for students without a BSN. It uses a
 small variation of the BSN checksum.
 
->>> is_valid('101222331')
-True
->>> is_valid('100252333')
-False
->>> compact('1234.56.782')
-'123456782'
+>>> validate('1012.22.331')
+'101222331'
+>>> validate('100252333')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('1012.22.3333')
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
+>>> validate('2112.22.337')  # number must start with 10
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
 """
 
+from stdnum.exceptions import *
 from stdnum.nl.bsn import compact, checksum
 
 
-__all__ = ['compact', 'is_valid']
+__all__ = ['compact', 'validate', 'is_valid']
+
+
+def validate(number):
+    """Checks to see if the number provided is a valid onderwijsnummer.
+    This checks the length and whether the check digit is correct and
+    whether it starts with the right sequence."""
+    number = compact(number)
+    if not number.isdigit() or int(number) <= 0:
+        raise InvalidFormat()
+    if not number.startswith('10'):
+        raise InvalidFormat()
+    if len(number) != 9:
+        raise InvalidLength()
+    if checksum(number) != 5:
+        raise InvalidChecksum()
+    return number
 
 
 def is_valid(number):
@@ -42,11 +67,6 @@ def is_valid(number):
     This checks the length and whether the check digit is correct and
     whether it starts with the right sequence."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) == 9 and \
-           number.isdigit() and \
-           int(number) > 0 and \
-           checksum(number) == 5 and \
-           number.startswith('10')

http://arthurdejong.org/git/python-stdnum/commit/?id=cf88e23388478eaa856843b90dbefc4bb862d4ff

commit cf88e23388478eaa856843b90dbefc4bb862d4ff
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sat May 4 23:53:08 2013 +0200

    Implement validate() for MEID

diff --git a/stdnum/meid.py b/stdnum/meid.py
index 7d64329..2ae8488 100644
--- a/stdnum/meid.py
+++ b/stdnum/meid.py
@@ -1,6 +1,6 @@
 # meid.py - functions for handling Mobile Equipment Identifiers (MEIDs)
 #
-# Copyright (C) 2010, 2011, 2012 Arthur de Jong
+# Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -22,18 +22,21 @@
 The Mobile Equipment Identifier is used to identify a physical piece of
 CDMA mobile station equipment.
 
->>> compact('AF 01 23 45 0A BC DE C')
+>>> validate('AF 01 23 45 0A BC DE C')
 'AF0123450ABCDE'
->>> is_valid('AF 01 23 45 0A BC DE')
-True
->>> is_valid('AF 01 23 45 0A BC DE C')
-True
->>> is_valid('29360 87365 0070 3710 0')
-True
+>>> validate('29360 87365 0070 3710 0')
+'AF0123450ABCDE'
+>>> validate('29360 87365 0070 3710 1')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 >>> format('af0123450abcDEC', add_check_digit=True)
 'AF 01 23 45 0A BC DE C'
+>>> format('af0123450abcDEC', format='dec', add_check_digit=True)
+'29360 87365 0070 3710 0'
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -55,23 +58,23 @@ def _ishex(number):
 
 def _parse(number):
     number = _cleanup(number)
-    if len(number) == 14 and _ishex(number):
-        # 14-digit hex representation
-        return number, ''
-    elif len(number) == 15 and _ishex(number):
-        # 14-digit hex representation with check digit
-        return number[0:14], number[14]
-    elif len(number) == 18 and number.isdigit():
+    if len(number) in (14, 15):
+        # 14 or 15 digit hex representation
+        if not _ishex(number):
+            raise InvalidFormat()
+        return number[0:14], number[14:]
+    elif len(number) in (18, 19):
         # 18-digit decimal representation
-        return number, ''
-    elif len(number) == 19 and number.isdigit():
-        # 18-digit decimal representation witch check digit
-        return number[0:18], number[18]
+        if not number.isdigit():
+            raise InvalidFormat()
+        return number[0:18], number[18:]
     else:
-        return None
+        raise InvalidLength()
 
 
-def _calc_check_digit(number):
+def calc_check_digit(number):
+    """Calculate the check digit for the number. The number should not
+    already have a check digit."""
     # both the 18-digit decimal format and the 14-digit hex format
     # containing only decimal digits should use the decimal Luhn check
     from stdnum import luhn
@@ -95,28 +98,44 @@ def compact(number, strip_check_digit=True):
     if len(number) == 18:
         number = '%08X%06X' % (int(number[0:10]), int(number[10:18]))
         if cd:
-            cd = _calc_check_digit(number)
+            cd = calc_check_digit(number)
     # put parts back together again
     return number + cd
 
 
-def is_valid(number):
-    """Checks to see if the number provided is a valid MEID number."""
+def validate(number, strip_check_digit=True):
+    """Checks to see if the number provided is a valid MEID number. This
+    converts the representation format of the number (if it is
+    decimal it is not converted to hexadecimal)."""
     from stdnum import luhn
     # first parse the number
-    try:
-        number, cd = _parse(number)
-    except:
-        return False
-    # decimal format can be easily determined
+    number, cd = _parse(number)
     if len(number) == 18:
-        return not cd or luhn.is_valid(number + cd)
-    # if the remaining hex format is fully decimal it is an IMEI number
-    if number.isdigit():
+        # decimal format can be easily determined
+        if cd:
+            luhn.validate(number + cd)
+        # convert to hex
+        number = '%08X%06X' % (int(number[0:10]), int(number[10:18]))
+        cd = calc_check_digit(number)
+    elif number.isdigit():
+        # if the remaining hex format is fully decimal it is an IMEI number
         from stdnum import imei
-        return imei.is_valid(number + cd)
-    # normal hex Luhn validation
-    return not cd or luhn.is_valid(number + cd, alphabet=_hex_alphabet)
+        imei.validate(number + cd)
+    else:
+        # normal hex Luhn validation
+        if cd:
+            luhn.validate(number + cd, alphabet=_hex_alphabet)
+    if strip_check_digit:
+        cd = ''
+    return number + cd
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid MEID number."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False
 
 
 def format(number, separator=' ', format=None, add_check_digit=False):
@@ -132,15 +151,15 @@ def format(number, separator=' ', format=None, 
add_check_digit=False):
         # convert to decimal
         number = '%010d%08d' % (int(number[0:8], 16), int(number[8:14], 16))
         if cd:
-            cd = _calc_check_digit(number)
+            cd = calc_check_digit(number)
     elif format == 'hex' and len(number) == 18:
         # convert to hex
         number = '%08X%06X' % (int(number[0:10]), int(number[10:18]))
         if cd:
-            cd = _calc_check_digit(number)
+            cd = calc_check_digit(number)
     # see if we need to add a check digit
     if add_check_digit and not cd:
-        cd = _calc_check_digit(number)
+        cd = calc_check_digit(number)
     # split number according to format
     if len(number) == 14:
         number = [number[i * 2:i * 2 + 2]
@@ -164,7 +183,7 @@ def to_binary(number):
 
 def to_pseudo_esn(number):
     """Convert the provided MEID to a pseudo ESN (pESN). The ESN is returned
-    in compact HEX representation."""
+    in compact hexadecimal representation."""
     import hashlib
     # return the last 6 digits of the SHA1  hash prefixed with the reserved
     # manufacturer code
diff --git a/tests/test_meid.doctest b/tests/test_meid.doctest
index 6f948d6..c517bc6 100644
--- a/tests/test_meid.doctest
+++ b/tests/test_meid.doctest
@@ -1,6 +1,6 @@
 test_meid.doctest - more detailed doctests for stdnum.meid module
 
-Copyright (C) 2010 Arthur de Jong
+Copyright (C) 2010, 2011, 2013 Arthur de Jong
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -28,40 +28,50 @@ really useful as module documentation.
 IMEI numbers without the software version (but optionally with a check
 digit) should be valid numbers:
 
->>> meid.is_valid('49-015420-323751')
-True
->>> meid.is_valid('35-209900-176148-1')
-True
->>> meid.is_valid('35-209900-176148-2')
-False
+>>> meid.validate('49-015420-323751')
+'49015420323751'
+>>> meid.validate('35-209900-176148-1')
+'35209900176148'
+>>> meid.validate('35-209900-176148-2')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 
 
 MEIDs can be represented as HEX strings (with and without check digit):
 
->>> meid.is_valid('AF 01 23 45 0A BC DE')
-True
->>> meid.is_valid('AF 01 23 45 0A BC DE C')
-True
->>> meid.is_valid('AF 01 23 45 0A BC DE D')
-False
+>>> meid.validate('AF 01 23 45 0A BC DE')
+'AF0123450ABCDE'
+>>> meid.validate('AF 01 23 45 0A BC DE C')
+'AF0123450ABCDE'
+>>> meid.validate('AF 01 23 45 0A BC DE D')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 
 
 Also, MEIDs can be represented in decimal format (with and without check 
digit):
 
->>> meid.is_valid('29360 87365 0070 3710')
-True
->>> meid.is_valid('29360 87365 0070 3710 0')
-True
->>> meid.is_valid('29360 87365 0070 3710 1')
-False
-
-
-The is_valid() method should be fairly robust against invalid junk passed:
-
->>> meid.is_valid('29360 ABCDE 0070 3710')
-False
->>> meid.is_valid('GF 01 23 45 0A BC DE')
-False
+>>> meid.validate('29360 87365 0070 3710')
+'AF0123450ABCDE'
+>>> meid.validate('29360 87365 0070 3710 0')
+'AF0123450ABCDE'
+>>> meid.validate('29360 87365 0070 3710 1')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+
+
+The validate() method should be fairly robust against invalid junk passed:
+
+>>> meid.validate('29360 ABCDE 0070 3710')
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> meid.validate('GF 01 23 45 0A BC DE')
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
 
 
 The compact method should convert to HEX if needed and can optionally leave

http://arthurdejong.org/git/python-stdnum/commit/?id=c6f41f62f99a258a3cb6ffeefecdd341ad75ca59

commit c6f41f62f99a258a3cb6ffeefecdd341ad75ca59
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sat May 4 23:40:31 2013 +0200

    Implement validate() for ISSN

diff --git a/stdnum/issn.py b/stdnum/issn.py
index cf632c7..6cfabbf 100644
--- a/stdnum/issn.py
+++ b/stdnum/issn.py
@@ -1,6 +1,6 @@
 # issn.py - functions for handling ISSNs
 #
-# Copyright (C) 2010, 2011, 2012 Arthur de Jong
+# Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -22,10 +22,16 @@
 The ISSN (International Standard Serial Number) is the standard code to
 identify periodical publications. It has a checksum similar to ISBN-10.
 
->>> is_valid('0024-9319')
-True
->>> is_valid('0032147X') # incorrect check digit
-False
+>>> validate('0024-9319')
+'00249319'
+>>> validate('0032147X')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('003214712')
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
 >>> compact('0032-1478')
 '00321478'
 >>> format('00249319')
@@ -35,6 +41,7 @@ False
 """
 
 from stdnum import ean
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -52,16 +59,26 @@ def calc_check_digit(number):
     return 'X' if check == 10 else str(check)
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid ISSN. This checks
+    the length and whether the check digit is correct."""
+    number = compact(number)
+    if not number[:-1].isdigit():
+        raise InvalidFormat()
+    if len(number) != 8:
+        raise InvalidLength()
+    if calc_check_digit(number[:-1]) != number[-1]:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid ISSN. This checks
     the length and whether the check digit is correct."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) == 8 and \
-           number[:-1].isdigit() and \
-           calc_check_digit(number[:-1]) == number[-1]
 
 
 def format(number):
@@ -71,7 +88,6 @@ def format(number):
 
 
 def to_ean(number, issue_code='00'):
-    """Convert the number to EAN-13 format. The number is assumed to be a
-    valid ISSN."""
-    number = '977' + compact(number)[:-1] + issue_code
+    """Convert the number to EAN-13 format."""
+    number = '977' + validate(number)[:-1] + issue_code
     return number + ean.calc_check_digit(number)

http://arthurdejong.org/git/python-stdnum/commit/?id=21f07b37a77e00600d80fac94a61ed77eb1c205f

commit 21f07b37a77e00600d80fac94a61ed77eb1c205f
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sat May 4 23:33:06 2013 +0200

    Implement validate() for ISMN

diff --git a/stdnum/ismn.py b/stdnum/ismn.py
index 060ee59..a26d123 100644
--- a/stdnum/ismn.py
+++ b/stdnum/ismn.py
@@ -1,6 +1,6 @@
 # ismn.py - functions for handling ISMNs
 #
-# Copyright (C) 2010, 2011, 2012 Arthur de Jong
+# Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -22,14 +22,16 @@
 The ISMN (International Standard Music Number) is used to identify sheet
 music. This module handles both numbers in the 10-digit 13-digit format.
 
->>> is_valid('979-0-3452-4680-5')
-True
->>> is_valid('9790060115615')
-True
+>>> validate('979-0-3452-4680-5')
+'9790345246805'
+>>> validate('9790060115615')
+'9790060115615'
 >>> ismn_type(' M-2306-7118-7')
 'ISMN10'
->>> is_valid('9790060115614') # incorrect check digit
-False
+>>> validate('9790060115614')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 >>> compact('  979-0-3452-4680-5')
 '9790345246805'
 >>> format('9790060115615')
@@ -41,6 +43,7 @@ False
 """
 
 from stdnum import ean
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -50,27 +53,41 @@ def compact(number):
     return clean(number, ' -.').strip().upper()
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid ISMN (either a legacy
+    10-digit one or a 13-digit one). This checks the length and the check
+    bit but does not check if the publisher is known."""
+    number = compact(number)
+    if len(number) == 10:
+        if number[0] != 'M':
+            raise InvalidFormat()
+        ean.validate('9790' + number[1:])
+    else:
+        ean.validate(number)
+    return number
+
+
 def ismn_type(number):
     """Check the type of ISMN number passed and return 'ISMN13', 'ISMN10'
     or None (for invalid)."""
     try:
-        number = compact(number)
-    except:
+        number = validate(number)
+    except ValidationError:
         return None
-    if len(number) == 10 and number[0] == 'M' and number[1:].isdigit():
-        if ean.calc_check_digit('9790' + number[1:-1]) == number[-1]:
-            return 'ISMN10'
-    elif len(number) == 13 and number.isdigit():
-        if ean.calc_check_digit(number[:-1]) == number[-1]:
-            return 'ISMN13'
-    return None
+    if len(number) == 10:
+        return 'ISMN10'
+    elif len(number) == 13:
+        return 'ISMN13'
 
 
 def is_valid(number):
     """Checks to see if the number provided is a valid ISMN (either a legacy
     10-digit one or a 13-digit one). This checks the length and the check
     bit but does not check if the publisher is known."""
-    return ismn_type(number) is not None
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False
 
 
 def to_ismn13(number):
diff --git a/tests/test_ismn.doctest b/tests/test_ismn.doctest
index 3d3c61e..c3d46b6 100644
--- a/tests/test_ismn.doctest
+++ b/tests/test_ismn.doctest
@@ -1,6 +1,6 @@
 test_ismn.doctest - more detailed doctests for stdnum.ismn module
 
-Copyright (C) 2010 Arthur de Jong
+Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -27,28 +27,38 @@ really useful as module documentation.
 
 These are normal variations that should just work.
 
->>> ismn.is_valid('979-0-3217-6543-6')
-True
->>> ismn.is_valid('979-0-3217-6544-3')
-True
->>> ismn.is_valid('9790321765450')
-True
->>> ismn.is_valid('M-3217-6546-7')
-True
->>> ismn.is_valid('M321765474')
-True
->>> ismn.is_valid('979-0-260000438')
-True
+>>> ismn.validate('979-0-3217-6543-6')
+'9790321765436'
+>>> ismn.validate('979-0-3217-6544-3')
+'9790321765443'
+>>> ismn.validate('9790321765450')
+'9790321765450'
+>>> ismn.validate('M-3217-6546-7')
+'M321765467'
+>>> ismn.validate('M321765474')
+'M321765474'
+>>> ismn.validate('979-0-260000438')
+'9790260000438'
 
 
 Tests for mangling and incorect check digits.
 
->>> ismn.is_valid('979-0-3217-6543-x')
-False
->>> ismn.is_valid('M-3217-6546-8')
-False
->>> ismn.is_valid('979M321765450')
-False
+>>> ismn.validate('979-0-3217-6543-x')
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> ismn.validate('M-3217-6546-8')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> ismn.validate('979M321765450')
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> ismn.validate('Z-3217-6546-8')
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
 
 
 See if 10 to 13 digit conversion works.
@@ -61,6 +71,14 @@ See if 10 to 13 digit conversion works.
 '979 0 3217 65504'
 
 
+Test the ismn_type() function
+>>> ismn.ismn_type('M-3217-6546-7')
+'ISMN10'
+>>> ismn.ismn_type('BAD')
+>>> ismn.ismn_type('9790321765450')
+'ISMN13'
+
+
 Regrouping tests.
 
 >>> ismn.format('M-3217-6546-7')

http://arthurdejong.org/git/python-stdnum/commit/?id=c07609f3646f78c8b03d2dfcd0f5819135c86cfa

commit c07609f3646f78c8b03d2dfcd0f5819135c86cfa
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sat May 4 23:16:46 2013 +0200

    Implement validate() for ISIL

diff --git a/stdnum/isil.py b/stdnum/isil.py
index c459602..b7024a9 100644
--- a/stdnum/isil.py
+++ b/stdnum/isil.py
@@ -1,7 +1,7 @@
 # isil.py - functions for handling identifiers for libraries and related
 #           organizations
 #
-# Copyright (C) 2011 Arthur de Jong
+# Copyright (C) 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -23,16 +23,26 @@
 The ISIL is the International Standard Identifier for
 Libraries and Related Organizations.
 
->>> is_valid('IT-RM0267')
-True
->>> is_valid('OCLC-DLC')
-True
->>> is_valid('WW-RM0267') # unregistered country code
-False
+>>> validate('IT-RM0267')
+'IT-RM0267'
+>>> validate('OCLC-DLC')
+'OCLC-DLC'
+>>> validate('WW-RM0267')  # unregistered country code
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> validate('WW-RM026712423345334534512334534545')  # too long
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
 >>> format('it-RM0267')
 'IT-RM0267'
 """
 
+from stdnum.exceptions import *
+from stdnum.util import clean
+
+
 # the valid characters in an ISIL
 _alphabet = 
set('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-:/')
 
@@ -40,7 +50,7 @@ _alphabet = 
set('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-
 def compact(number):
     """Convert the ISIL to the minimal representation. This strips
     surrounding whitespace."""
-    return number.strip()
+    return clean(number, '').strip()
 
 
 def _known_agency(agency):
@@ -52,17 +62,27 @@ def _known_agency(agency):
     return len(results) == 1 and bool(results[0][1])
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid isil (or isilSV)
+    number."""
+    number = compact(number)
+    for n in number:
+        if n not in _alphabet:
+            raise InvalidFormat()
+    if len(number) > 15:
+        raise InvalidLength()
+    if not _known_agency(number.split('-')[0]):
+        raise InvalidComponent()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid isil (or isilSV)
     number."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    for n in number:
-        if n not in _alphabet:
-            return False
-    return len(number) <= 15 and _known_agency(number.split('-')[0])
 
 
 def format(number):

http://arthurdejong.org/git/python-stdnum/commit/?id=a18f1acea1cc03fd5dec7f5c6cd26804405548c8

commit a18f1acea1cc03fd5dec7f5c6cd26804405548c8
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sat May 4 21:55:06 2013 +0200

    Implement validate() for ISAN

diff --git a/stdnum/isan.py b/stdnum/isan.py
index 7638b7d..12d4f66 100644
--- a/stdnum/isan.py
+++ b/stdnum/isan.py
@@ -1,7 +1,7 @@
 # isan.py - functions for handling International Standard Audiovisual Numbers
 #           (ISANs)
 #
-# Copyright (C) 2010, 2011, 2012 Arthur de Jong
+# Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -28,15 +28,16 @@ and an episode or part. After that an optional check digit, 
optional
 version and optionally another check digit can be provided. The check
 digits are validated using the ISO 7064 Mod 37, 36 algorithm.
 
-
->>> is_valid('000000018947000000000000')
-True
+>>> validate('000000018947000000000000')
+'000000018947000000000000'
 >>> compact('0000-0000-D07A-0090-Q-0000-0000-X')
 '00000000D07A009000000000'
->>> is_valid('0000-0001-8CFA-0000-I-0000-0000-K')
-True
->>> is_valid('0000-0001-8CFA-0000-A-0000-0000-K') # invalid check digit
-False
+>>> validate('0000-0001-8CFA-0000-I-0000-0000-K')
+'000000018CFA0000I00000000K'
+>>> validate('0000-0001-8CFA-0000-A-0000-0000-K')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 >>> format('000000018947000000000000')
 '0000-0001-8947-0000-8-0000-0000-D'
 
@@ -46,7 +47,7 @@ False
 '<ISAN root="1881-66C7-3420" episode="6541" version="9F3A-0245" />'
 """
 
-
+from stdnum.exceptions import *
 from stdnum.iso7064 import mod_37_36
 from stdnum.util import clean
 
@@ -67,30 +68,48 @@ def split(number):
 def compact(number, strip_check_digits=True):
     """Convert the ISAN to the minimal representation. This strips the number
     of any valid separators and removes surrounding whitespace. The check
-    digits are also removed by default."""
+    digits are removed by default."""
     number = list(split(number))
     number[2] = number[4] = ''
     return ''.join(number)
 
 
-def _check(number, length, required=True):
-    if (number or required) and length != len(number):
-        return False
-    for x in number:
+def validate(number, strip_check_digits=False, add_check_digits=False):
+    """Checks to see if the number provided is a valid ISAN. If check digits
+    are present in the number they are validated. If strip_check_digits is
+    True any existing check digits will be removed (after checking). If
+    add_check_digits is True the check digit will be added if they are not
+    present yet."""
+    (root, episode, check1, version, check2) = split(number)
+    # check digits used
+    for x in root + episode + version:
         if x not in '0123456789ABCDEF':
-            return False
-    return True
+            raise InvalidFormat()
+    # check length of all components
+    if len(root) != 12 or len(episode) != 4 or len(check1) not in (0, 1) or \
+       len(version) not in (0, 8) or len(check1) not in (0, 1):
+        raise InvalidLength()
+    # check check digits
+    if check1:
+        mod_37_36.validate(root + episode + check1)
+    if check2:
+        mod_37_36.validate(root + episode + version + check2)
+    # remove or add check digits
+    if strip_check_digits:
+        check1 = check2 = ''
+    if add_check_digits and not check1:
+        check1 = mod_37_36.calc_check_digit(root + episode)
+    if add_check_digits and not check2 and version:
+        check2 = mod_37_36.calc_check_digit(root + episode + version)
+    return root + episode + check1 + version + check2
 
 
 def is_valid(number):
     """Checks to see if the number provided is a valid ISAN. If check digits
     are present in the number they are validated."""
     try:
-        (root, episode, check1, version, check2) = split(number)
-        return _check(root, 12) and _check(episode, 4) and _check(version, 8, 
False) \
-           and (not check1 or mod_37_36.is_valid(root + episode + check1)) \
-           and (not check2 or mod_37_36.is_valid(root + episode + version + 
check2))
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
 
 
@@ -131,7 +150,7 @@ def to_xml(number):
     """Returns the XML form of the ISAN as a string."""
     number = format(number, strip_check_digits=True, add_check_digits=False)
     return '<ISAN root="%s" episode="%s" version="%s" />' % (
-              number[0:14], number[15:19], number[20:])
+        number[0:14], number[15:19], number[20:])
 
 
 def to_urn(number):
diff --git a/tests/test_isan.doctest b/tests/test_isan.doctest
index 72b1357..7984e48 100644
--- a/tests/test_isan.doctest
+++ b/tests/test_isan.doctest
@@ -1,6 +1,6 @@
 test_isan.doctest - more detailed doctests for stdnum.isan module
 
-Copyright (C) 2010 Arthur de Jong
+Copyright (C) 2010, 2012, 2013 Arthur de Jong
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -55,22 +55,30 @@ Compacting should also work:
 
 These should be valid numbers:
 
->>> isan.is_valid('0000-0000-D07A-0090-Q-0000-0000-X')
-True
->>> isan.is_valid('1881-66C7-3420-6541-Y-9F3A-0245-O')
-True
->>> isan.is_valid('0000-0000-D07A-0090-Q-0000-0000-X')
-True
->>> isan.is_valid('0000-0000-D07A-0090-Q')
-True
+>>> isan.validate('0000-0000-D07A-0090-Q-0000-0000-X')
+'00000000D07A0090Q00000000X'
+>>> isan.validate('1881-66C7-3420-6541-Y-9F3A-0245-O')
+'188166C734206541Y9F3A0245O'
+>>> isan.validate('0000-0000-D07A-0090-Q-0000-0000-X')
+'00000000D07A0090Q00000000X'
+>>> isan.validate('0000-0000-D07A-0090-Q')
+'00000000D07A0090Q'
 
 
 And these should be invalid:
 
->>> isan.is_valid('00000000D07A0090-Z')  # wrong check digit
-False
->>> isan.is_valid('3abc6800-0041X1-20')  # illegal character
-False
+>>> isan.validate('00000000D07A0090-Z')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> isan.validate('3abc6800-0041X1-20')
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> isan.validate('3abc6800-0041')
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
 
 
 The format() function can add the check digit if needed. It should leave
@@ -83,6 +91,10 @@ or absence of a version number.
 '1881-66C7-3420-6541-Y-9F3A-0245-O'
 >>> isan.format('1881-66C7-3420-6541-?-9F3A-0245-?', strip_check_digits=True, 
 >>> add_check_digits=True)
 '1881-66C7-3420-6541-Y-9F3A-0245-O'
+>>> isan.validate('1881-66C7-3420-6541-Y-9F3A-0245-O', strip_check_digits=True)
+'188166C7342065419F3A0245'
+>>> isan.validate('188166C7342065419F3A0245', add_check_digits=True)
+'188166C734206541Y9F3A0245O'
 
 
 A simple test for the to_binary() function.

http://arthurdejong.org/git/python-stdnum/commit/?id=3ac8164e42ca20a2bcb586034c2e97b566ef6cc7

commit 3ac8164e42ca20a2bcb586034c2e97b566ef6cc7
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Wed May 1 23:13:56 2013 +0200

    Implement validate() for IBAN

diff --git a/stdnum/iban.py b/stdnum/iban.py
index 4b1e621..bab80b9 100644
--- a/stdnum/iban.py
+++ b/stdnum/iban.py
@@ -1,6 +1,6 @@
 # iban.py - functions for handling International Bank Account Numbers (IBANs)
 #
-# Copyright (C) 2011, 2012 Arthur de Jong
+# Copyright (C) 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -27,10 +27,10 @@ for the remainder of the number.
 Some countries may also use checksum algorithms within their number but
 this is currently not checked by this number.
 
->>> is_valid('GR16 0110 1050 0000 1054 7023 795')
-True
->>> is_valid('BE31435411161155')
-True
+>>> validate('GR16 0110 1050 0000 1054 7023 795')
+'GR1601101050000010547023795'
+>>> validate('BE31435411161155')
+'BE31435411161155'
 >>> compact('GR16 0110 1050 0000 1054 7023 795')
 'GR1601101050000010547023795'
 >>> format('GR1601101050000010547023795')
@@ -40,6 +40,7 @@ True
 import re
 
 from stdnum import numdb
+from stdnum.exceptions import *
 from stdnum.iso7064 import mod_97_10
 from stdnum.util import clean
 
@@ -60,7 +61,7 @@ def compact(number):
     return clean(number, ' -').strip().upper()
 
 
-def _convert(number):
+def _to_base10(number):
     """Prepare the number to it's base10 representation (also moving the
     check digits to the end) so it can be checked with the ISO 7064
     Mod 97, 10 algorithm."""
@@ -68,35 +69,44 @@ def _convert(number):
     return ''.join(str(_alphabet.index(x)) for x in number[4:] + number[:4])
 
 
-def _matches_structure(number, structure):
-    """Check the supplied number against the supplied structure."""
-    start = 0
-    for length, code in _struct_re.findall(structure):
-        length = int(length)
-        if code == 'n' and not number[start:start + length].isdigit():
-            return False
-        elif code == 'a' and not number[start:start + length].isalpha():
-            return False
-        elif code == 'c' and not number[start:start + length].isalnum():
-            return False  # pragma: no cover (due to checksum check)
-        start += length
-    # the whole number should be parsed now
-    return start == len(number)
+def _struct_to_re(structure):
+    """Convert an IBAN structure to a refular expression that can be used
+    to validate the number."""
+    def conv(match):
+        chars = {
+            'n': '[0-9]',
+            'a': '[A-Z]',
+            'c': '[A-Za-z0-9]',
+        }[match.group(2)]
+        return '%s{%s}' % (chars, match.group(1))
+    return re.compile('^%s$' % _struct_re.sub(conv, structure))
 
 
-def is_valid(number):
+def validate(number):
     """Checks to see if the number provided is a valid IBAN."""
+    number = compact(number)
     try:
-        number = compact(number)
-        # ensure that checksum is valid
-        if not mod_97_10.is_valid(_convert(number)):
-            return False
+        test_number = _to_base10(number)
     except:
-        return False
+        raise InvalidFormat()
+    # ensure that checksum is valid
+    mod_97_10.validate(test_number)
     # look up the number
     info = _ibandb.info(number)
-    # check if the number has the correct structure
-    return _matches_structure(number[4:], info[0][1].get('bban', ''))
+    # check if the bban part of number has the correct structure
+    bban = number[4:]
+    if not _struct_to_re(info[0][1].get('bban', '')).match(bban):
+        raise InvalidFormat()
+    # return the compact representation
+    return number
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid IBAN."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False
 
 
 def format(number, separator=' '):

http://arthurdejong.org/git/python-stdnum/commit/?id=12bd68408c1a4c0076c1976bd4a975929402b5f9

commit 12bd68408c1a4c0076c1976bd4a975929402b5f9
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 3 23:38:56 2013 +0200

    Implement validate() for GRid numbers

diff --git a/stdnum/grid.py b/stdnum/grid.py
index 1d6a630..b128161 100644
--- a/stdnum/grid.py
+++ b/stdnum/grid.py
@@ -23,20 +23,21 @@ The Global Release Identifier is used to identify releases 
of digital
 sound recordings and uses the ISO 7064 Mod 37, 36 algorithm to verify the
 correctness of the number.
 
->>> is_valid('A12425GABC1234002M')
-True
->>> is_valid('Grid: A1-2425G-ABC1234002-M')
-True
->>> is_valid('A1-2425G-ABC1234002-Q') # incorrect check digit
-False
->>> is_valid(None) # method should be robust
-False
+>>> validate('A12425GABC1234002M')
+'A12425GABC1234002M'
+>>> validate('Grid: A1-2425G-ABC1234002-M')
+'A12425GABC1234002M'
+>>> validate('A1-2425G-ABC1234002-Q') # incorrect check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 >>> compact('A1-2425G-ABC1234002-M')
 'A12425GABC1234002M'
 >>> format('A12425GABC1234002M')
 'A1-2425G-ABC1234002-M'
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -49,14 +50,21 @@ def compact(number):
     return number
 
 
-def is_valid(number):
+def validate(number):
     """Checks to see if the number provided is a valid GRid."""
     from stdnum.iso7064 import mod_37_36
+    number = compact(number)
+    if len(number) != 18:
+        raise InvalidLength()
+    return mod_37_36.validate(number)
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid GRid."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) == 18 and mod_37_36.is_valid(number)
 
 
 def format(number, separator='-'):

http://arthurdejong.org/git/python-stdnum/commit/?id=9cee495a39afd52d03e66f0ea667af3efdf664a9

commit 9cee495a39afd52d03e66f0ea667af3efdf664a9
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sat May 4 21:14:39 2013 +0200

    Implement validate() for IMSI

diff --git a/stdnum/imsi.py b/stdnum/imsi.py
index 175d65b..7bed628 100644
--- a/stdnum/imsi.py
+++ b/stdnum/imsi.py
@@ -1,7 +1,7 @@
 # imsi.py - functions for handling International Mobile Subscriber Identity
 #           (IMSI) numbers
 #
-# Copyright (C) 2011, 2012 Arthur de Jong
+# Copyright (C) 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -23,10 +23,12 @@
 The IMSI (International Mobile Subscriber Identity) is used to identify
 mobile phone users (the SIM).
 
->>> is_valid('429011234567890')
-True
->>> is_valid('439011234567890')  # unknown MCC
-False
+>>> validate('429011234567890')
+'429011234567890'
+>>> validate('439011234567890')  # unknown MCC
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
 >>> split('429011234567890')
 ('429', '01', '1234567890')
 >>> split('310150123456789')
@@ -37,6 +39,7 @@ False
 'China'
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -47,8 +50,8 @@ def compact(number):
 
 
 def split(number):
-    """Split the specified IMSI into a Mobile Country Code (MCC),
-    a Mobile Network Code (MNC), a Mobile Station Identification Number 
(MSIN)."""
+    """Split the specified IMSI into a Mobile Country Code (MCC), a Mobile
+    Network Code (MNC), a Mobile Station Identification Number (MSIN)."""
     from stdnum import numdb
     # clean up number
     number = compact(number)
@@ -56,6 +59,18 @@ def split(number):
     return tuple(numdb.get('imsi').split(number))
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid IMSI."""
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) not in (14, 15):
+        raise InvalidLength()
+    if len(split(number)) != 3:
+        raise InvalidFormat()
+    return number
+
+
 def info(number):
     """Return a dictionary of data about the supplied number."""
     from stdnum import numdb
@@ -76,9 +91,6 @@ def info(number):
 def is_valid(number):
     """Checks to see if the number provided is a valid IMSI."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) in (14, 15) and \
-           number.isdigit() and \
-           len(split(number)) == 3

http://arthurdejong.org/git/python-stdnum/commit/?id=6e4bb713ce07177976eaa6988fdf719fbe0d7b09

commit 6e4bb713ce07177976eaa6988fdf719fbe0d7b09
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sat May 4 18:28:24 2013 +0200

    Implement validate() for IMEI numbers

diff --git a/stdnum/imei.py b/stdnum/imei.py
index ccd4cfa..33d38c5 100644
--- a/stdnum/imei.py
+++ b/stdnum/imei.py
@@ -1,7 +1,7 @@
 # imei.py - functions for handling International Mobile Equipment Identity
 #           (IMEI) numbers
 #
-# Copyright (C) 2010, 2011, 2012 Arthur de Jong
+# Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -23,10 +23,12 @@
 The  IMEI is used to identify mobile phones. The IMEI may optionally
 include a check digit which is validated using the Luhn algorithm.
 
->>> is_valid('35686800-004141-20')
-True
->>> is_valid('35-417803-685978-1') # incorrect check digit
-False
+>>> validate('35686800-004141-20')
+'3568680000414120'
+>>> validate('35-417803-685978-1')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 >>> compact('35686800-004141-20')
 '3568680000414120'
 >>> format('354178036859789')
@@ -39,6 +41,8 @@ False
 ('35686800', '004141', '')
 """
 
+from stdnum import luhn
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -48,28 +52,41 @@ def compact(number):
     return clean(number, ' -').strip().upper()
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid IMEI (or IMEISV)
+    number."""
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) == 15:
+        # only 15 digit IMEI has check digit
+        luhn.validate(number)
+    elif len(number) not in (14, 16):
+        # neither IMEI without check digit or IMEISV (which doesn't have one)
+        raise InvalidLength()
+    return number
+
+
 def imei_type(number):
     """Check the passed number and returns 'IMEI', 'IMEISV' or None (for
     invalid) for checking the type of number passed."""
     try:
-        number = compact(number)
+        number = validate(number)
     except:
         return None
-    if len(number) == 14:  # IMEI without check digit
-        return 'IMEI' if number.isdigit() else None
-    if len(number) == 15:  # IMEI with check digit
-        from stdnum import luhn
-        return 'IMEI' if luhn.is_valid(number) else None
+    if len(number) in (14, 15):
+        return 'IMEI'
     elif len(number) == 16:
-        return 'IMEISV' if number.isdigit() else None
-    else:
-        return None
+        return 'IMEISV'
 
 
 def is_valid(number):
     """Checks to see if the number provided is a valid IMEI (or IMEISV)
     number."""
-    return imei_type(number) is not None
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False
 
 
 def split(number):
@@ -84,7 +101,6 @@ def format(number, separator='-', add_check_digit=False):
     """Reformat the passed number to the standard format."""
     number = compact(number)
     if len(number) == 14 and add_check_digit:
-        from stdnum import luhn
         number += luhn.calc_check_digit(number)
     number = (number[:2], number[2:8], number[8:14], number[14:])
     return separator.join(x for x in number if x)
diff --git a/tests/test_imei.doctest b/tests/test_imei.doctest
index 994ff69..7589d69 100644
--- a/tests/test_imei.doctest
+++ b/tests/test_imei.doctest
@@ -1,6 +1,6 @@
 test_imei.doctest - more detailed doctests for stdnum.imei module
 
-Copyright (C) 2010 Arthur de Jong
+Copyright (C) 2010, 2013 Arthur de Jong
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -27,16 +27,16 @@ really useful as module documentation.
 
 Should be valid numbers:
 
->>> imei.is_valid('49-015420-323751')
-True
->>> imei.is_valid('35-209900-176148-1')
-True
->>> imei.is_valid('35-209900-176148-23')
-True
->>> imei.is_valid('350077-52-323751-3')
-True
->>> imei.is_valid('354178036859789')
-True
+>>> imei.validate('49-015420-323751')
+'49015420323751'
+>>> imei.validate('35-209900-176148-1')
+'352099001761481'
+>>> imei.validate('35-209900-176148-23')
+'3520990017614823'
+>>> imei.validate('350077-52-323751-3')
+'350077523237513'
+>>> imei.validate('354178036859789')
+'354178036859789'
 
 
 These are normal variations that should just work. Getting the type:

http://arthurdejong.org/git/python-stdnum/commit/?id=efa255097d9751319244901831ce758f90f6b7c2

commit efa255097d9751319244901831ce758f90f6b7c2
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sat May 4 20:50:05 2013 +0200

    Implement validate() for ISO 7064 algorithms

diff --git a/stdnum/iso7064/mod_11_10.py b/stdnum/iso7064/mod_11_10.py
index bc4f93b..4b691ff 100644
--- a/stdnum/iso7064/mod_11_10.py
+++ b/stdnum/iso7064/mod_11_10.py
@@ -1,6 +1,6 @@
 # mod_11_10.py - functions for performing the ISO 7064 Mod 11, 10 algorithm
 #
-# Copyright (C) 2010, 2011, 2012 Arthur de Jong
+# Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -27,14 +27,16 @@ For a module that can do generic Mod x+1, x calculations 
see the
 
 >>> calc_check_digit('79462')
 '3'
->>> is_valid('794623')
-True
+>>> validate('794623')
+'794623'
 >>> calc_check_digit('00200667308')
 '5'
->>> is_valid('002006673085')
-True
+>>> validate('002006673085')
+'002006673085'
 """
 
+from stdnum.exceptions import *
+
 
 def checksum(number):
     """Calculate the checksum. A valid number should have a checksum of 1."""
@@ -50,9 +52,20 @@ def calc_check_digit(number):
     return str((1 - ((checksum(number) or 10) * 2) % 11) % 10)
 
 
-def is_valid(number):
+def validate(number):
     """Checks whether the check digit is valid."""
     try:
-        return bool(number) and checksum(number) == 1
+        valid = checksum(number) == 1
     except:
+        raise InvalidFormat()
+    if not valid:
+        raise InvalidChecksum()
+    return number
+
+
+def is_valid(number):
+    """Checks whether the check digit is valid."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
         return False
diff --git a/stdnum/iso7064/mod_11_2.py b/stdnum/iso7064/mod_11_2.py
index 2c9f2f7..69a1595 100644
--- a/stdnum/iso7064/mod_11_2.py
+++ b/stdnum/iso7064/mod_11_2.py
@@ -1,6 +1,6 @@
 # mod_11_2.py - functions for performing the ISO 7064 Mod 11, 2 algorithm
 #
-# Copyright (C) 2010, 2011, 2012 Arthur de Jong
+# Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -27,16 +27,18 @@ For a module that can do generic Mod x, 2 calculations see 
the
 
 >>> calc_check_digit('0794')
 '0'
->>> is_valid('07940')
-True
+>>> validate('07940')
+'07940'
 >>> calc_check_digit('079')
 'X'
->>> is_valid('079X')
-True
+>>> validate('079X')
+'079X'
 >>> checksum('079X')
 1
 """
 
+from stdnum.exceptions import *
+
 
 def checksum(number):
     """Calculate the checksum. A valid number should have a checksum of 1."""
@@ -53,9 +55,20 @@ def calc_check_digit(number):
     return 'X' if c == 10 else str(c)
 
 
-def is_valid(number):
+def validate(number):
     """Checks whether the check digit is valid."""
     try:
-        return bool(number) and checksum(number) == 1
+        valid = checksum(number) == 1
     except:
+        raise InvalidFormat()
+    if not valid:
+        raise InvalidChecksum()
+    return number
+
+
+def is_valid(number):
+    """Checks whether the check digit is valid."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
         return False
diff --git a/stdnum/iso7064/mod_37_2.py b/stdnum/iso7064/mod_37_2.py
index d9543a9..ecdf125 100644
--- a/stdnum/iso7064/mod_37_2.py
+++ b/stdnum/iso7064/mod_37_2.py
@@ -1,6 +1,6 @@
 # mod_37_2.py - functions for performing the ISO 7064 Mod 37, 2 algorithm
 #
-# Copyright (C) 2010, 2011, 2012 Arthur de Jong
+# Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -24,8 +24,8 @@ digit may also be numeric, a letter or '*'.
 
 >>> calc_check_digit('G123489654321')
 'Y'
->>> is_valid('G123489654321Y')
-True
+>>> validate('G123489654321Y')
+'G123489654321Y'
 >>> checksum('G123489654321Y')
 1
 
@@ -34,12 +34,14 @@ algorithm. For example Mod 11, 2:
 
 >>> calc_check_digit('079', alphabet='0123456789X')
 'X'
->>> is_valid('079', alphabet='0123456789X')
-True
->>> checksum('079', alphabet='0123456789X')
+>>> validate('079X', alphabet='0123456789X')
+'079X'
+>>> checksum('079X', alphabet='0123456789X')
 1
 """
 
+from stdnum.exceptions import *
+
 
 def checksum(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*'):
     """Calculate the checksum. A valid number should have a checksum of 1."""
@@ -57,9 +59,20 @@ def calc_check_digit(number, 
alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*'):
     return alphabet[(1 - 2 * checksum(number, alphabet)) % modulus]
 
 
-def is_valid(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*'):
+def validate(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*'):
     """Checks whether the check digit is valid."""
     try:
-        return bool(number) and checksum(number, alphabet) == 1
+        valid = checksum(number, alphabet) == 1
     except:
+        raise InvalidFormat()
+    if not valid:
+        raise InvalidChecksum()
+    return number
+
+
+def is_valid(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*'):
+    """Checks whether the check digit is valid."""
+    try:
+        return bool(validate(number, alphabet))
+    except ValidationError:
         return False
diff --git a/stdnum/iso7064/mod_37_36.py b/stdnum/iso7064/mod_37_36.py
index 431e8b9..6a153e0 100644
--- a/stdnum/iso7064/mod_37_36.py
+++ b/stdnum/iso7064/mod_37_36.py
@@ -1,6 +1,6 @@
 # mod_37_36.py - functions for performing the ISO 7064 Mod 37, 36 algorithm
 #
-# Copyright (C) 2010, 2011, 2012 Arthur de Jong
+# Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -26,18 +26,20 @@ itself may also contain letters.
 1
 >>> calc_check_digit('A12425GABC1234002')
 'M'
->>> is_valid('A12425GABC1234002M')
-True
+>>> validate('A12425GABC1234002M')
+'A12425GABC1234002M'
 
 By changing the alphabet this can be turned into any Mod x+1, x
 algorithm. For example Mod 11, 10:
 
 >>> calc_check_digit('00200667308', alphabet='0123456789')
 '5'
->>> is_valid('002006673085', alphabet='0123456789')
-True
+>>> validate('002006673085', alphabet='0123456789')
+'002006673085'
 """
 
+from stdnum.exceptions import *
+
 
 def checksum(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
     """Calculate the checksum. A valid number should have a checksum of 1."""
@@ -55,9 +57,20 @@ def calc_check_digit(number, 
alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
     return alphabet[(1 - ((checksum(number, alphabet) or modulus) * 2) % 
(modulus + 1)) % modulus]
 
 
-def is_valid(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
+def validate(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
     """Checks whether the check digit is valid."""
     try:
-        return bool(number) and checksum(number, alphabet) == 1
+        valid = checksum(number, alphabet) == 1
     except:
+        raise InvalidFormat()
+    if not valid:
+        raise InvalidChecksum()
+    return number
+
+
+def is_valid(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
+    """Checks whether the check digit is valid."""
+    try:
+        return bool(validate(number, alphabet))
+    except ValidationError:
         return False
diff --git a/stdnum/iso7064/mod_97_10.py b/stdnum/iso7064/mod_97_10.py
index 1476d2e..8696d3c 100644
--- a/stdnum/iso7064/mod_97_10.py
+++ b/stdnum/iso7064/mod_97_10.py
@@ -1,6 +1,6 @@
 # mod_97_10.py - functions for performing the ISO 7064 Mod 97, 10 algorithm
 #
-# Copyright (C) 2010, 2011, 2012 Arthur de Jong
+# Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -24,16 +24,18 @@ valid if the number modulo 97 is 1. As such it has two 
check digits.
 
 >>> calc_check_digits('99991234567890121414')
 '90'
->>> is_valid('9999123456789012141490')
-True
+>>> validate('9999123456789012141490')
+'9999123456789012141490'
 >>> calc_check_digits('4354111611551114')
 '31'
->>> is_valid('08686001256515001121751')
-True
+>>> validate('08686001256515001121751')
+'08686001256515001121751'
 >>> calc_check_digits('22181321402534321446701611')
 '35'
 """
 
+from stdnum.exceptions import *
+
 
 def checksum(number):
     """Calculate the checksum. A valid number should have a checksum of 1."""
@@ -46,9 +48,20 @@ def calc_check_digits(number):
     return '%02d' % ((98 - 100 * checksum(number)) % 97)
 
 
-def is_valid(number):
-    """Determines whether the number has a valid checksum."""
+def validate(number):
+    """Checks whether the check digit is valid."""
     try:
-        return bool(number) and checksum(number) == 1
+        valid = checksum(number) == 1
     except:
+        raise InvalidFormat()
+    if not valid:
+        raise InvalidChecksum()
+    return number
+
+
+def is_valid(number):
+    """Checks whether the check digit is valid."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
         return False
diff --git a/tests/test_iso7064.doctest b/tests/test_iso7064.doctest
index 58e2adb..28c6247 100644
--- a/tests/test_iso7064.doctest
+++ b/tests/test_iso7064.doctest
@@ -1,6 +1,6 @@
 test_doctest - more detailed doctests for the stdnum.iso7064 package
 
-Copyright (C) 2010, 2011 Arthur de Jong
+Copyright (C) 2010, 2011, 2013 Arthur de Jong
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -27,20 +27,22 @@ really useful as module documentation.
 
 These are normal variations of Mod 11, 10 that should just work.
 
->>> mod_11_10.is_valid('12323')
-True
->>> mod_11_10.is_valid('546794')
-True
+>>> mod_11_10.validate('12323')
+'12323'
+>>> mod_11_10.validate('546794')
+'546794'
 >>> mod_11_10.calc_check_digit('0794')
 '5'
->>> mod_11_10.is_valid('07945')
-True
+>>> mod_11_10.validate('07945')
+'07945'
 >>> mod_11_10.calc_check_digit('00200667308')
 '5'
->>> mod_11_10.is_valid('002006673085')
-True
->>> mod_11_10.is_valid('002006673084')
-False
+>>> mod_11_10.validate('002006673085')
+'002006673085'
+>>> mod_11_10.validate('002006673084')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 >>> mod_11_10.calc_check_digit('00200667309')
 '3'
 >>> mod_11_10.calc_check_digit('00200667310')
@@ -55,12 +57,12 @@ These normal tests of Mod 11, 2 should just work.
 
 >>> mod_11_2.calc_check_digit('0794')
 '0'
->>> mod_11_2.is_valid('07940')
-True
+>>> mod_11_2.validate('07940')
+'07940'
 >>> mod_11_2.calc_check_digit('079')
 'X'
->>> mod_11_2.is_valid('079')
-True
+>>> mod_11_2.validate('079X')
+'079X'
 
 These normal tests of Mod 37, 2 should just work
 

http://arthurdejong.org/git/python-stdnum/commit/?id=5c9090b369440885e2c3a68eba46c6fb8baace97

commit 5c9090b369440885e2c3a68eba46c6fb8baace97
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 3 22:52:17 2013 +0200

    Implement validate() for the Verhoeff checksum

diff --git a/stdnum/verhoeff.py b/stdnum/verhoeff.py
index 69fb4b7..4fb9315 100644
--- a/stdnum/verhoeff.py
+++ b/stdnum/verhoeff.py
@@ -1,6 +1,6 @@
 # verhoeff.py - functions for performing the Verhoeff checksum
 #
-# Copyright (C) 2010, 2011, 2012 Arthur de Jong
+# Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -22,16 +22,21 @@
 The Verhoeff algorithm uses two tables for permutations and
 multiplications to calculate a checksum.
 
->>> is_valid('1234')
-False
+>>> validate('1234')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 >>> checksum('1234')
 1
 >>> calc_check_digit('1234')
 '0'
->>> is_valid('12340')
-True
+>>> validate('12340')
+'12340'
 """
 
+from stdnum.exceptions import *
+
+
 # These are the multiplication and permutation tables used in the
 # Verhoeff algorithm.
 
@@ -70,11 +75,24 @@ def checksum(number):
     return check
 
 
-def is_valid(number):
+def validate(number):
     """Checks to see if the number provided passes the Verhoeff checksum."""
+    if not bool(number):
+        raise InvalidFormat()
     try:
-        return bool(number) and checksum(number) == 0
+        valid = checksum(number) == 0
     except:
+        raise InvalidFormat()
+    if not valid:
+        raise InvalidChecksum()
+    return number
+
+
+def is_valid(number):
+    """Checks to see if the number provided passes the Verhoeff checksum."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
         return False
 
 
diff --git a/tests/test_verhoeff.doctest b/tests/test_verhoeff.doctest
index 5e2faa6..4c22e9d 100644
--- a/tests/test_verhoeff.doctest
+++ b/tests/test_verhoeff.doctest
@@ -1,6 +1,6 @@
 test_verhoeff.doctest - more detailed doctests for stdnum.verhoeff module
 
-Copyright (C) 2010 Arthur de Jong
+Copyright (C) 2010, 2011, 2013 Arthur de Jong
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -37,25 +37,29 @@ These are normal variations that should just work. 
Calculating checksums:
 
 The same numbers but now simply ask for validation:
 
->>> verhoeff.is_valid('654')
-False
->>> verhoeff.is_valid('1428570')
-True
->>> verhoeff.is_valid('398438246238642378648236487236482734')
-False
+>>> verhoeff.validate('654')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> verhoeff.validate('1428570')
+'1428570'
+>>> verhoeff.validate('398438246238642378648236487236482734')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 
 
 Adding a check digit to the numbers so they are all valid:
 
 >>> verhoeff.calc_check_digit('654')
 '8'
->>> verhoeff.is_valid('6548')
-True
+>>> verhoeff.validate('6548')
+'6548'
 >>> verhoeff.calc_check_digit('1428570')
 '8'
->>> verhoeff.is_valid('1428570')
-True
+>>> verhoeff.validate('1428570')
+'1428570'
 >>> verhoeff.calc_check_digit('398438246238642378648236487236482734')
 '7'
->>> verhoeff.is_valid('3984382462386423786482364872364827347')
-True
+>>> verhoeff.validate('3984382462386423786482364872364827347')
+'3984382462386423786482364872364827347'

http://arthurdejong.org/git/python-stdnum/commit/?id=9ad51399d680b4f1944662bd4a9b9e5a6d1d5420

commit 9ad51399d680b4f1944662bd4a9b9e5a6d1d5420
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 3 23:15:48 2013 +0200

    Implement validate() for the Luhn algorithms

diff --git a/stdnum/luhn.py b/stdnum/luhn.py
index dbfb183..bc1b5ac 100644
--- a/stdnum/luhn.py
+++ b/stdnum/luhn.py
@@ -1,6 +1,6 @@
 # luhn.py - functions for performing the Luhn and Luhn mod N algorithms
 #
-# Copyright (C) 2010, 2011, 2012 Arthur de Jong
+# Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -22,24 +22,30 @@
 The Luhn algorithm is used to detect most accidental errors in various
 identification numbers.
 
->>> is_valid('7894')
-False
+>>> validate('7894')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 >>> checksum('7894')
 6
 >>> calc_check_digit('7894')
 '9'
->>> is_valid('78949')
-True
+>>> validate('78949')
+'78949'
 
 An alternative alphabet can be provided to use the Luhn mod N algorithm.
 The default alphabet is '0123456789'.
 
->>> is_valid('1234', alphabet='0123456789abcdef')
-False
+>>> validate('1234', alphabet='0123456789abcdef')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 >>> checksum('1234', alphabet='0123456789abcdef')
 14
 """
 
+from stdnum.exceptions import *
+
 
 def checksum(number, alphabet='0123456789'):
     """Calculate the Luhn checksum over the provided number. The checksum
@@ -52,11 +58,24 @@ def checksum(number, alphabet='0123456789'):
                 for i in number[1::2])) % n
 
 
-def is_valid(number, alphabet='0123456789'):
+def validate(number, alphabet='0123456789'):
     """Checks to see if the number provided passes the Luhn checksum."""
+    if not bool(number):
+        raise InvalidFormat()
     try:
-        return bool(number) and checksum(number, alphabet) == 0
+        valid = checksum(number, alphabet) == 0
     except:
+        raise InvalidFormat()
+    if not valid:
+        raise InvalidChecksum()
+    return number
+
+
+def is_valid(number, alphabet='0123456789'):
+    """Checks to see if the number provided passes the Luhn checksum."""
+    try:
+        return bool(validate(number, alphabet))
+    except ValidationError:
         return False
 
 
diff --git a/tests/test_luhn.doctest b/tests/test_luhn.doctest
index c7689c3..4409514 100644
--- a/tests/test_luhn.doctest
+++ b/tests/test_luhn.doctest
@@ -1,6 +1,6 @@
 test_luhn.doctest - more detailed doctests for stdnum.luhn module
 
-Copyright (C) 2010 Arthur de Jong
+Copyright (C) 2010, 2011, 2013 Arthur de Jong
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -37,24 +37,26 @@ These are normal variations that should just work. 
Calculating checksums:
 
 The same numbers but now simply ask for validation:
 
->>> luhn.is_valid('4992739871')
-False
->>> luhn.is_valid('490154203237518')
-True
->>> luhn.is_valid('abcdefe', alphabet='abcdef')
-True
+>>> luhn.validate('4992739871')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> luhn.validate('490154203237518')
+'490154203237518'
+>>> luhn.validate('abcdefe', alphabet='abcdef')
+'abcdefe'
 
 Adding a check digit to the numbers so they are all valid:
 
 >>> luhn.calc_check_digit('4992739871')
 '6'
->>> luhn.is_valid('49927398716')
-True
+>>> luhn.validate('49927398716')
+'49927398716'
 >>> luhn.calc_check_digit('142857')
 '2'
->>> luhn.is_valid('1428572')
-True
+>>> luhn.validate('1428572')
+'1428572'
 >>> luhn.calc_check_digit('398438246238642378648236487236482734')
 '7'
->>> luhn.is_valid('3984382462386423786482364872364827347')
-True
+>>> luhn.validate('3984382462386423786482364872364827347')
+'3984382462386423786482364872364827347'

http://arthurdejong.org/git/python-stdnum/commit/?id=958004665f1f8c56be96fb30ea8f6a60c9c8456b

commit 958004665f1f8c56be96fb30ea8f6a60c9c8456b
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 3 23:37:56 2013 +0200

    Implement validate() for ISBN

diff --git a/stdnum/isbn.py b/stdnum/isbn.py
index 0eee01e..9b6c722 100644
--- a/stdnum/isbn.py
+++ b/stdnum/isbn.py
@@ -1,6 +1,6 @@
 # isbn.py - functions for handling ISBNs
 #
-# Copyright (C) 2010, 2011, 2012 Arthur de Jong
+# Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -23,10 +23,12 @@ The ISBN is the International Standard Book Number, used to 
identify
 publications. This module supports both numbers in ISBN-10 (10-digit) and
 ISBN-13 (13-digit) format.
 
->>> is_valid('978-9024538270')
-True
->>> is_valid('978-9024538271') # incorrect check digit
-False
+>>> validate('978-9024538270')
+'9789024538270'
+>>> validate('978-9024538271')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 >>> compact('1-85798-218-5')
 '1857982185'
 >>> format('9780471117094')
@@ -44,6 +46,7 @@ False
 """
 
 from stdnum import ean
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -68,24 +71,36 @@ def _calc_isbn10_check_digit(number):
     return 'X' if check == 10 else str(check)
 
 
+def validate(number, convert=False):
+    """Checks to see if the number provided is a valid ISBN (either a legacy
+    10-digit one or a 13-digit one). This checks the length and the check
+    bit but does not check if the group and publisher are valid (use split()
+    for that)."""
+    number = compact(number, convert=False)
+    if not number[:-1].isdigit():
+        raise InvalidFormat()
+    if len(number) == 10:
+        if _calc_isbn10_check_digit(number[:-1]) != number[-1]:
+            raise InvalidChecksum()
+    elif len(number) == 13:
+        ean.validate(number)
+    else:
+        raise InvalidLength()
+    if convert:
+        number = to_isbn13(number)
+    return number
+
+
 def isbn_type(number):
     """Check the passed number and returns 'ISBN13', 'ISBN10' or None (for
     invalid) for checking the type of number passed."""
     try:
-        number = compact(number)
-    except:
+        number = validate(number, convert=False)
+    except ValidationError:
         return None
     if len(number) == 10:
-        if not number[:-1].isdigit():
-            return None
-        if _calc_isbn10_check_digit(number[:-1]) != number[-1]:
-            return None
         return 'ISBN10'
     elif len(number) == 13:
-        if not number.isdigit():
-            return None
-        if ean.calc_check_digit(number[:-1]) != number[-1]:
-            return None
         return 'ISBN13'
 
 
@@ -94,13 +109,16 @@ def is_valid(number):
     10-digit one or a 13-digit one). This checks the length and the check
     bit but does not check if the group and publisher are valid (use split()
     for that)."""
-    return isbn_type(number) is not None
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False
 
 
 def to_isbn13(number):
     """Convert the number to ISBN-13 format."""
     number = number.strip()
-    min_number = compact(number)
+    min_number = compact(number, convert=False)
     if len(min_number) == 13:
         return number  # nothing to do, already ISBN-13
     # put new check digit in place
@@ -117,13 +135,13 @@ def to_isbn13(number):
 def to_isbn10(number):
     """Convert the number to ISBN-10 format."""
     number = number.strip()
-    min_number = compact(number)
+    min_number = compact(number, convert=False)
     if len(min_number) == 10:
         return number  # nothing to do, already ISBN-13
     elif isbn_type(min_number) != 'ISBN13':
-        raise ValueError('Not a valid ISBN13.')
+        raise InvalidFormat('Not a valid ISBN13.')
     elif not number.startswith('978'):
-        raise ValueError('Does not use 978 Bookland prefix.')
+        raise InvalidFormat('Does not use 978 Bookland prefix.')
     # strip EAN prefix
     number = number[3:-1].strip().strip('-')
     digit = _calc_isbn10_check_digit(min_number[3:-1])
diff --git a/tests/test_isbn.doctest b/tests/test_isbn.doctest
index e759114..d38d51f 100644
--- a/tests/test_isbn.doctest
+++ b/tests/test_isbn.doctest
@@ -1,6 +1,6 @@
 test_isbn.doctest - more detailed doctests for stdnum.isbn module
 
-Copyright (C) 2010, 2011 Arthur de Jong
+Copyright (C) 2010, 2011, 2013 Arthur de Jong
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -27,12 +27,22 @@ really useful as module documentation.
 
 Tests for mangling and incorect check digits.
 
->>> isbn.is_valid('08515x-629-2')  # added X in the middle
-False
->>> isbn.is_valid('85152-629-1')  # incorrect check digit
-False
->>> isbn.is_valid('978-902453827X')  # ISBN with X check digit
-False
+>>> isbn.validate('08515x-629-2')  # added X in the middle
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> isbn.validate('85152-629-1')  # incorrect check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> isbn.validate('978-902453827X')  # ISBN13 with X check digit
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> isbn.validate('978-902453827')  # invalid length
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
 
 
 See if ISBN10 to 13 conversion works.
@@ -45,10 +55,12 @@ See if ISBN10 to 13 conversion works.
 '9781857982183'
 >>> isbn.to_isbn13('1-85798-218-5')
 '978-1-85798-218-3'
->>> isbn.is_valid(isbn.to_isbn13('1 85798218 5'))
-True
+>>> isbn.validate(isbn.to_isbn13('1 85798218 5'))
+'9781857982183'
 >>> isbn.compact('1 85798218 5', convert=True)
 '9781857982183'
+>>> isbn.validate('1 85798218 5', convert=True)
+'9781857982183'
 
 
 See if ISBN13 to 10 conversion works.
@@ -64,11 +76,11 @@ See if ISBN13 to 10 conversion works.
 >>> isbn.to_isbn10('979-20-1234567-8')  # incorrect check digit
 Traceback (most recent call last):
     ...
-ValueError: Not a valid ISBN13.
+InvalidFormat: ...
 >>> isbn.to_isbn10('9791843123391')
 Traceback (most recent call last):
     ...
-ValueError: Does not use 978 Bookland prefix.
+InvalidFormat: ...
 
 
 Regrouping tests.

http://arthurdejong.org/git/python-stdnum/commit/?id=fa1864fc2df0c746d691e56716f6e553b20a150f

commit fa1864fc2df0c746d691e56716f6e553b20a150f
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri May 3 23:38:30 2013 +0200

    Implement validate() for EAN

diff --git a/stdnum/ean.py b/stdnum/ean.py
index d04e9b8..ec6007e 100644
--- a/stdnum/ean.py
+++ b/stdnum/ean.py
@@ -1,6 +1,6 @@
 # ean.py - functions for handling EANs
 #
-# Copyright (C) 2011, 2012 Arthur de Jong
+# Copyright (C) 2011, 2012, 2013 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -22,12 +22,13 @@
 Module for handling EAN (International Article Number) codes. This
 module handles numbers EAN-13, EAN-8 and UPC (12-digit) format.
 
->>> is_valid('73513537')
-True
->>> is_valid('978-0-471-11709-4') # ISBN-13 format
-True
+>>> validate('73513537')
+'73513537'
+>>> validate('978-0-471-11709-4') # EAN-13 format
+'9780471117094'
 """
 
+from stdnum.exceptions import *
 from stdnum.util import clean
 
 
@@ -44,14 +45,25 @@ def calc_check_digit(number):
                          for i, n in enumerate(reversed(number)))) % 10)
 
 
+def validate(number):
+    """Checks to see if the number provided is a valid EAN-13. This checks
+    the length and the check bit but does not check whether a known GS1
+    Prefix and company identifier are referenced."""
+    number = compact(number)
+    if not number.isdigit():
+        raise InvalidFormat()
+    if len(number) not in (13, 12, 8):
+        raise InvalidLength()
+    if calc_check_digit(number[:-1]) != number[-1]:
+        raise InvalidChecksum()
+    return number
+
+
 def is_valid(number):
     """Checks to see if the number provided is a valid EAN-13. This checks
     the length and the check bit but does not check whether a known GS1
     Prefix and company identifier are referenced."""
     try:
-        number = compact(number)
-    except:
+        return bool(validate(number))
+    except ValidationError:
         return False
-    return len(number) in (13, 12, 8) and \
-           number.isdigit() and \
-           calc_check_digit(number[:-1]) == number[-1]
diff --git a/tests/test_ean.doctest b/tests/test_ean.doctest
new file mode 100644
index 0000000..d541184
--- /dev/null
+++ b/tests/test_ean.doctest
@@ -0,0 +1,43 @@
+test_ean.doctest - more detailed doctests for the stdnum.ean module
+
+Copyright (C) 2013 Arthur de Jong
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA
+
+
+This file contains more detailed doctests for the stdnum.ean module. It
+tries to test more corner cases and detailed functionality that is not
+really useful as module documentation.
+
+>>> from stdnum import ean
+
+
+These numbers have broken checksums or are mangled:
+
+>>> ean.validate('7501031311309')
+'7501031311309'
+>>> ean.validate('75010313113')
+Traceback (most recent call last):
+   ...
+InvalidLength: ...
+>>> ean.validate('750103AAAA309')
+Traceback (most recent call last):
+   ...
+InvalidFormat: ...
+>>> ean.validate('7501031311308')
+Traceback (most recent call last):
+   ...
+InvalidChecksum: ...

http://arthurdejong.org/git/python-stdnum/commit/?id=8b9ef8f2cb68de1b67a2ee1940d24e67d3942d42

commit 8b9ef8f2cb68de1b67a2ee1940d24e67d3942d42
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri Jun 7 23:38:07 2013 +0200

    Raise a proper exception if cleaning fails

diff --git a/stdnum/util.py b/stdnum/util.py
index b9fab29..e45b78f 100644
--- a/stdnum/util.py
+++ b/stdnum/util.py
@@ -29,6 +29,8 @@ import pydoc
 import re
 import sys
 
+from stdnum.exceptions import *
+
 
 _strip_doctest_re = re.compile('^>>> .*\Z', re.DOTALL | re.MULTILINE)
 
@@ -39,7 +41,10 @@ def clean(number, deletechars):
     >>> clean('123-456:78 9', ' -:')
     '123456789'
     """
-    return ''.join(x for x in number if x not in deletechars)
+    try:
+        return ''.join(x for x in number if x not in deletechars)
+    except:
+        raise InvalidFormat()
 
 
 def get_number_modules(base='stdnum'):

http://arthurdejong.org/git/python-stdnum/commit/?id=1ac5437537e4b944c382dbd215ba86b02a20e1fc

commit 1ac5437537e4b944c382dbd215ba86b02a20e1fc
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Fri Jun 7 23:37:58 2013 +0200

    Provide a module with validation exceptions
    
    This introduces a new module for switching the validation scheme. Instead of
    using the is_valid() function that returns a boolean a validate() function
    either returns the sanitised number or raises an exception that should
    indicate the kind of validation failure.
    
    This should make it easier for applications calling this library to present
    more informative messages to the user.

diff --git a/setup.cfg b/setup.cfg
index 9962704..abf5bb2 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,6 +5,7 @@ group=root
 [nosetests]
 with-doctest=true
 doctest-extension=doctest
+doctest-options=+IGNORE_EXCEPTION_DETAIL
 with-coverage=true
 cover-package=stdnum
 cover-erase=true
diff --git a/stdnum/exceptions.py b/stdnum/exceptions.py
new file mode 100644
index 0000000..733b525
--- /dev/null
+++ b/stdnum/exceptions.py
@@ -0,0 +1,66 @@
+# exceptions.py - collection of stdnum exceptions
+# coding: utf-8
+#
+# Copyright (C) 2013 Arthur de Jong
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 USA
+
+"""Collection of exceptions.
+
+The validation functions of stdnum should raise one of the below exceptions
+when validation of the number fails.
+"""
+
+
+class ValidationError(Exception):
+    """Top-level error for validating numbers.
+
+    This exception should normally not be raised, only subclasses of this
+    exception."""
+
+    def __str__(self):
+        return getattr(self, 'message', '')
+
+
+class InvalidFormat(ValidationError):
+    """Something is wrong with the format of the number.
+
+    This generally means characters or delimiters that are not allowed are
+    part of the number or required parts are missing."""
+
+    message = 'The number has an invalid format.'
+
+
+class InvalidChecksum(ValidationError):
+    """The number's internal checksum or check digit does not match."""
+
+    message = "The number's checksum or check digit is invalid."
+
+
+class InvalidLength(InvalidFormat):
+    """The length of the number is wrong."""
+
+    message = 'The number has an invalid length.'
+
+
+class InvalidComponent(ValidationError):
+    """One of the parts of the number has an invalid reference.
+
+    Some part of the number refers to some external entity like a country
+    code, a date or a predefined collection of values. The number contains
+    some invalid reference."""
+
+    message = 'One of the parts of the number are invalid or unknown.'

-----------------------------------------------------------------------

Summary of changes:
 README                                             |   11 +-
 docs/index.rst                                     |   17 ++
 docs/stdnum.exceptions.rst                         |   31 ++++
 setup.cfg                                          |    1 +
 stdnum/at/uid.py                                   |   33 ++--
 stdnum/be/vat.py                                   |   31 +++-
 stdnum/bg/egn.py                                   |   56 ++++---
 stdnum/bg/pnf.py                                   |   37 ++--
 stdnum/bg/vat.py                                   |   48 ++++--
 stdnum/br/cpf.py                                   |   40 +++--
 stdnum/cy/vat.py                                   |   35 ++--
 stdnum/cz/dic.py                                   |   59 ++++---
 stdnum/cz/rc.py                                    |   72 ++++----
 stdnum/de/vat.py                                   |   31 +++-
 stdnum/dk/cpr.py                                   |   49 +++---
 stdnum/dk/cvr.py                                   |   32 ++--
 stdnum/ean.py                                      |   32 ++--
 stdnum/ee/kmkr.py                                  |   31 +++-
 stdnum/es/cif.py                                   |   71 +++++---
 stdnum/es/dni.py                                   |   36 ++--
 stdnum/es/nie.py                                   |   40 +++--
 stdnum/es/nif.py                                   |   57 ++++---
 stdnum/eu/vat.py                                   |   40 +++--
 stdnum/exceptions.py                               |   66 ++++++++
 stdnum/fi/alv.py                                   |   31 +++-
 stdnum/fi/hetu.py                                  |   54 ++++--
 stdnum/fr/siren.py                                 |   32 +++-
 stdnum/fr/tva.py                                   |   71 ++++----
 stdnum/gb/vat.py                                   |   57 ++++---
 stdnum/gr/vat.py                                   |   32 +++-
 stdnum/grid.py                                     |   32 ++--
 stdnum/hr/oib.py                                   |   33 ++--
 stdnum/hu/anum.py                                  |   31 +++-
 stdnum/iban.py                                     |   66 ++++----
 stdnum/ie/pps.py                                   |   33 ++--
 stdnum/ie/vat.py                                   |   58 ++++---
 stdnum/imei.py                                     |   48 ++++--
 stdnum/imsi.py                                     |   36 ++--
 stdnum/isan.py                                     |   63 ++++---
 stdnum/isbn.py                                     |   58 ++++---
 stdnum/isil.py                                     |   48 ++++--
 stdnum/ismn.py                                     |   51 ++++--
 stdnum/iso7064/mod_11_10.py                        |   27 ++-
 stdnum/iso7064/mod_11_2.py                         |   27 ++-
 stdnum/iso7064/mod_37_2.py                         |   29 +++-
 stdnum/iso7064/mod_37_36.py                        |   27 ++-
 stdnum/iso7064/mod_97_10.py                        |   29 +++-
 stdnum/issn.py                                     |   42 +++--
 stdnum/it/iva.py                                   |   40 +++--
 stdnum/lt/pvm.py                                   |   55 +++---
 stdnum/lu/tva.py                                   |   32 ++--
 stdnum/luhn.py                                     |   37 +++-
 stdnum/lv/pvn.py                                   |   64 ++++---
 stdnum/meid.py                                     |   97 ++++++-----
 stdnum/mt/vat.py                                   |   32 ++--
 stdnum/nl/bsn.py                                   |   38 +++--
 stdnum/nl/btw.py                                   |   39 +++--
 stdnum/nl/onderwijsnummer.py                       |   50 ++++--
 stdnum/pl/nip.py                                   |   32 ++--
 stdnum/pt/nif.py                                   |   32 ++--
 stdnum/ro/cf.py                                    |   44 +++--
 stdnum/ro/cnp.py                                   |   63 ++++---
 stdnum/se/vat.py                                   |   31 ++--
 stdnum/si/ddv.py                                   |   32 ++--
 stdnum/sk/dph.py                                   |   41 +++--
 stdnum/sk/rc.py                                    |   32 ++--
 stdnum/us/ssn.py                                   |   47 ++++--
 stdnum/util.py                                     |    9 +-
 stdnum/verhoeff.py                                 |   32 +++-
 tests/test_bg_vat.doctest                          |   36 ++--
 .../{test_robustness.doctest => test_ean.doctest}  |   35 ++--
 tests/test_eu_vat.doctest                          |  177 +++++++++++++++++++-
 tests/test_fi_hetu.doctest                         |   60 ++++---
 tests/test_gb_vat.doctest                          |   52 ++++--
 tests/test_imei.doctest                            |   22 +--
 tests/test_isan.doctest                            |   38 +++--
 tests/test_isbn.doctest                            |   34 ++--
 tests/test_ismn.doctest                            |   56 ++++---
 tests/test_iso7064.doctest                         |   32 ++--
 tests/test_luhn.doctest                            |   28 ++--
 tests/test_meid.doctest                            |   64 ++++---
 tests/test_verhoeff.doctest                        |   30 ++--
 82 files changed, 2400 insertions(+), 1114 deletions(-)
 create mode 100644 docs/stdnum.exceptions.rst
 create mode 100644 stdnum/exceptions.py
 copy tests/{test_robustness.doctest => test_ean.doctest} (50%)


hooks/post-receive
-- 
python-stdnum
-- 
To unsubscribe send an email to
python-stdnum-commits-unsubscribe@lists.arthurdejong.org or see
http://lists.arthurdejong.org/python-stdnum-commits/