8000 Provide a validate() function in all modules · odony/python-stdnum@999f2c3 · GitHub
[go: up one dir, main page]

Skip to content

Commit 999f2c3

Browse files
committed
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.
2 parents 99586c9 + cb69921 commit 999f2c3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+2421
-1101
lines changed

README

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,18 @@ Interface
8484

8585
Most of these modules implement the following functions:
8686

87-
is_valid() - returns either True or False depending on whether the
88-
passed number is in any supported and valid form
87+
validate() - validate the correctness of the passed number and return a
88+
compact representation of the number
89+
invalid numbers are rejected with one of the exceptions
90+
from the stdnum.exceptions module
8991
compact() - return a compact representation of the number or code
9092
this function generally does not do validation but may raise
9193
exceptions for wildly incorrect numbers
9294
format() - return a formatted version of the number in the preferred format
9395
this function generally expects to be passed a valid number or code
9496

95-
Apart from the above, the module may add extra parsing, validation or conversion functions.
97+
Apart from the above, the module may add extra parsing, validation or
98+
conversion functions.
9699

97100
Requirements
98101
------------
@@ -104,7 +107,7 @@ also work with older versions of Python.
104107
Copyright
105108
---------
106109

107-
Copyright (C) 2010, 2011, 2012 Arthur de Jong
110+
Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
108111

109112
This library is free software; you can redistribute it and/or
110113
modify it under the terms of the GNU Lesser General Public

docs/index.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ Common Interface
1717

1818
Most of the number format modules implement the following functions:
1919

20+
.. function:: validate(number)
21+
22+
Validate the number and return a compact, consistent representation of
23+
the number or code. If the validation fails,
24+
:mod:`an exception <.exceptions>` is raised that indicates the type of
25+
error.
26+
2027
.. function:: is_valid(number)
2128

2229
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:
5158
Apart from the above, the modules may add extra parsing, validation or
5259
conversion functions.
5360

61+
62+
Helper modules
63+
--------------
64+
65+
.. autosummary::
66+
:toctree:
67+
68+
exceptions
69+
70+
5471
Generic check digit algorithms
5572
------------------------------
5673

docs/stdnum.exceptions.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
stdnum.exceptions
2+
=================
3+
4+
.. automodule:: stdnum.exceptions
5+
:show-inheritance:
6+
:member-order: bysource
7+
:members:
8+
9+
The exceptions are organised hierarchically in the following structure:
10+
11+
::
12+
13+
ValidationError
14+
+-- InvalidFormat
15+
| +-- InvalidLength
16+
+-- InvalidChecksum
17+
+-- InvalidComponent
18+
19+
It is possible to change the exception messages by setting the `message`
20+
class property. This allows localisation and application-specific error
21+
messages.
22+
23+
>>> raise InvalidFormat()
24+
Traceback (most recent call last):
25+
...
26+
InvalidChecksum: The number has an invalid format.
27+
>>> InvalidFormat.message = 'UNKNOWN'
28+
>>> raise InvalidFormat()
29+
Traceback (most recent call last):
30+
...
31+
InvalidChecksum: UNKNOWN

setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ group=root
55
[nosetests]
66
with-doctest=true
77
doctest-extension=doctest
8+
doctest-options=+IGNORE_EXCEPTION_DETAIL
89
with-coverage=true
910
cover-package=stdnum
1011
cover-erase=true

stdnum/at/uid.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# vat.py - functions for handling Austrian VAT numbers
22
#
3-
# Copyright (C) 2012 Arthur de Jong
3+
# Copyright (C) 2012, 2013 Arthur de Jong
44
#
55
# This library is free software; you can redistribute it and/or
66
# modify it under the terms of the GNU Lesser General Public
@@ -22,17 +22,18 @@
2222
The Austrian UID is a 9-digit number that starts with a U (optionally
2323
preceded with AT). The last digit is a check digit.
2424
25-
>>> compact('AT U13585627')
25+
>>> validate('AT U13585627')
2626
'U13585627'
27-
>>> is_valid('U13585627')
28-
True
2927
>>> calc_check_digit('U1358562')
3028
'7'
31-
>>> is_valid('U13585626') # incorrect check digit
32-
False
29+
>>> validate('U13585626') # incorrect check digit
30+
Traceback (most recent call last):
31+
...
32+
InvalidChecksum: ...
3333
"""
3434

3535
from stdnum import luhn
36+
from stdnum.exceptions import *
3637
from stdnum.util import clean
3738

3839

@@ -51,13 +52,23 @@ def calc_check_digit(number):
5152
return str((6 - luhn.checksum(number[1:])) % 10)
5253

5354

55+
def validate(number):
56+
"""Checks to see if the number provided is a valid VAT number. This checks
57+
the length, formatting and check digit."""
58+
number = compact(number)
59+
if number[:1] != 'U' or not number[1:].isdigit():
60+
raise InvalidFormat()
61+
if len(number) != 9:
62+
raise InvalidLength()
63+
if calc_check_digit(number[:-1]) != number[-1]:
64+
raise InvalidChecksum()
65+
return number
66+
67+
5468
def is_valid(number):
5569
10000 """Checks to see if the number provided is a valid VAT number. This checks
5670
the length, formatting and check digit."""
5771
try:
58-
number = compact(number)
59-
except:
72+
return bool(validate(number))
73+
except ValidationError:
6074
return False
61-
return len(number) == 9 and number[0] == 'U' and \
62-
number[1:].isdigit() and \
63-
calc_check_digit(number[:-1]) == number[-1]

stdnum/be/vat.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# vat.py - functions for handling Belgian VAT numbers
22
#
3-
# Copyright (C) 2012 Arthur de Jong
3+
# Copyright (C) 2012, 2013 Arthur de Jong
44
#
55
# This library is free software; you can redistribute it and/or
66
# modify it under the terms of the GNU Lesser General Public
@@ -23,12 +23,15 @@
2323
'0403019261'
2424
>>> compact('(0)403019261')
2525
'0403019261'
26-
>>> is_valid('BE 428759497')
27-
True
28-
>>> is_valid('BE431150351')
29-
False
26+
>>> validate('BE 428759497')
27+
'0428759497'
28+
>>> validate('BE431150351')
29+
Traceback (most recent call last):
30+
...
31+
InvalidChecksum: ...
3032
"""
3133

34+
from stdnum.exceptions import *
3235
from stdnum.util import clean
3336

3437

@@ -50,11 +53,23 @@ def checksum(number):
5053
return (int(number[:-2]) + int(number[-2:])) % 97
5154

5255

56+
def validate(number):
57+
"""Checks to see if the number provided is a valid VAT number. This checks
58+
the length, formatting and check digit."""
59+
number = compact(number)
60+
if not number.isdigit():
61+
raise InvalidFormat()
62+
if len(number) != 10:
63+
raise InvalidLength()
64+
if checksum(number) != 0:
65+
raise InvalidChecksum()
66+
return number
67+
68+
5369
def is_valid(number):
5470
"""Checks to see if the number provided is a valid VAT number. This checks
5571
the length, formatting and check digit."""
5672
try:
57-
number = compact(number)
58-
except:
73+
return bool(validate(number))
74+
except ValidationError:
5975
return False
60-
return len(number) == 10 and number.isdigit() and checksum(number) == 0

stdnum/bg/egn.py

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# egn.py - functions for handling Bulgarian national identification numbers
22
# coding: utf-8
33
#
4-
# Copyright (C) 2012 Arthur de Jong
4+
# Copyright (C) 2012, 2013 Arthur de Jong
55
#
66
# This library is free software; you can redistribute it and/or
77
# modify it under the terms of the GNU Lesser General Public
@@ -27,18 +27,23 @@
2727
2828
>>> compact('752316 926 3')
2929
'7523169263'
30-
>>> is_valid('8032056031')
31-
True
30+
>>> validate('8032056031')
31+
'8032056031'
3232
>>> get_birth_date('7542011030')
3333
datetime.date(2075, 2, 1)
34-
>>> is_valid('7552010004') # invalid check digit
35-
False
36-
>>> is_valid('8019010008') # invalid date
37-
False
34+
>>> validate('7552A10004') # invalid digit
35+
Traceback (most recent call last):
36+
...
37+
InvalidFormat: ...
38+
>>> validate('8019010008') # invalid date
39+
Traceback (most recent call last):
40+
...
41+
InvalidComponent: ...
3842
"""
3943

4044
import datetime
4145

46+
from stdnum.exceptions import *
4247
from stdnum.util import clean
4348

4449

@@ -66,24 +71,35 @@ def get_birth_date(number):
6671
elif month > 20:
6772
year -= 100
6873
month -= 20
69-
return datetime.date(year, month, day)
74+
try:
75+
return datetime.date(year, month, day)
76+
except ValueError:
77+
raise InvalidComponent()
7078

7179

72-
def is_valid(number):
80+
def validate(number):
7381
"""Checks to see if the number provided is a valid national
7482
identification number. This checks the length, formatting, embedded
7583
date and check digit."""
76-
try:
77-
number = compact(number)
78-
except:
79-
return False
80-
if not number.isdigit() or len(number) != 10:
81-
return False
84+
number = compact(number)
85+
if not number.isdigit():
86+
raise InvalidFormat()
87+
if len(number) != 10:
88+
raise InvalidLength()
8289
# check if birth date is valid
90+
birth_date = get_birth_date(number)
91+
# TODO: check that the birth date is not in the future
92+
# check the check digit
93+
if calc_check_digit(number[:-1]) != number[-1]:
94+
raise InvalidChecksum()
95+
return number
96+
97+
98+
def is_valid(number):
99+
"""Checks to see if the number provided is a valid national
100+
identification number. This checks the length, formatting, embedded
101+
date and check digit."""
83102
try:
84-
birth_date = get_birth_date(number)
85-
# TODO: check that the birth date is not in the future
86-
except ValueError:
103+
return bool(validate(number))
104+
except ValidationError:
87105
return False
88-
# check the check digit
89-
return calc_check_digit(number[:-1]) == number[-1]

stdnum/bg/pnf.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# pnf.py - functions for handling Bulgarian personal number of a foreigner
22
# coding: utf-8
33
#
4-
# Copyright (C) 2012 Arthur de Jong
4+
# Copyright (C) 2012, 2013 Arthur de Jong
55
#
66
# This library is free software; you can redistribute it and/or
77
# modify it under the terms of the GNU Lesser General Public
@@ -23,14 +23,19 @@
2323
The personal number of a foreigner is a 10-digit number where the last digit
2424
is the result of a weighted checksum.
2525
26-
>>> compact('7111 042 925')
26+
>>> validate('7111 042 925')
2727
'7111042925'
28-
>>> is_valid('7111042925')
29-
True
30-
>>> is_valid('7111042922') # invalid check digit
31-
False
28+
>>> validate('7111042922') # invalid check digit
29+
Traceback (most recent call last):
30+
...
31+
InvalidChecksum: ...
32+
>>> validate('71110A2922') # invalid digit
33+
Traceback (most recent call last):
34+
...
35+
InvalidFormat: ...
3236
"""
3337

38+
from stdnum.exceptions import *
3439
from stdnum.util import clean
3540

3641

@@ -47,13 +52,25 @@ def calc_check_digit(number):
4752
return str(sum(weights[i] * int(n) for i, n in enumerate(number)) % 10)
4853

4954

55+
def validate(number):
56+
"""Checks to see if the number provided is a valid national
57+
identification number. This checks the length, formatting, embedded
58+
date and check digit."""
59+
number = compact(number)
60+
if not number.isdigit():
61+
raise InvalidFormat()
62+
if len(number) != 10:
63+
raise InvalidLength()
64+
if calc_check_digit(number[:-1]) != number[-1]:
65+
raise InvalidChecksum()
66+
return number
67+
68+
5069
def is_valid(number):
5170
"""Checks to see if the number provided is a valid national
5271
identification number. This checks the length, formatting, embedded
5372
date and check digit."""
5473
try:
55-
number = compact(number)
56-
except:
74+
return bool(validate(number))
75+
except ValidationError:
5776
return False
58-
return number.isdigit() and len(number) == 10 and \
59-
calc_check_digit(number[:-1]) == number[-1]

0 commit comments

Comments
 (0)
0