lists.arthurdejong.org
RSS feed

python-stdnum branch master updated. 1.11-6-g8307b94

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

python-stdnum branch master updated. 1.11-6-g8307b94



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  8307b94a7204573c2387d2419422adfb4b005457 (commit)
       via  c1fb46ad0bdf542991b7b0564b99de01e78f947a (commit)
       via  48ff92e300696e2449070ac03b4916c76d4e77a3 (commit)
       via  3aeec68735d75a4835122990bef232ed415b07d5 (commit)
      from  e07a0e249fa112f722ceae6aa197ee9d5a626e9d (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 -----------------------------------------------------------------
https://arthurdejong.org/git/python-stdnum/commit/?id=8307b94a7204573c2387d2419422adfb4b005457

commit 8307b94a7204573c2387d2419422adfb4b005457
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sun Apr 28 23:46:00 2019 +0200

    Don't force "" strings to avoid escaping quotes
    
    Fixes test failures with recent flake8-quotes.

diff --git a/setup.cfg b/setup.cfg
index 73fec46..fdbe65b 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -32,6 +32,7 @@ ignore =
   E731  # we occasionally use lambda
   F403,F405  # we use * imports
   Q001  # we use '''...''' multi-line strings
+  Q003  # don't force "" strings to avoid escaping quotes
   T001  # we use print statements in the update scripts
   W504  # we put the binary operator on the preceding line
 max-complexity = 15

https://arthurdejong.org/git/python-stdnum/commit/?id=c1fb46ad0bdf542991b7b0564b99de01e78f947a

commit c1fb46ad0bdf542991b7b0564b99de01e78f947a
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sun Apr 28 23:43:08 2019 +0200

    Convert various reasonable unicode digits
    
    This converts many of the "reasonable" unicode digits that are just
    variations on ASCII 0-9 to their ASCII counterparts.

diff --git a/stdnum/util.py b/stdnum/util.py
index bbab8b0..e3afff2 100644
--- a/stdnum/util.py
+++ b/stdnum/util.py
@@ -101,6 +101,46 @@ _char_map = dict(_mk_char_map({
     'PUNCTUATION SPACE,THIN SPACE,HAIR SPACE,NARROW NO-BREAK SPACE,'
     'MEDIUM MATHEMATICAL SPACE,IDEOGRAPHIC SPACE':
         ' ',
+    'FULLWIDTH DIGIT ZERO,MATHEMATICAL BOLD DIGIT ZERO,'
+    'MATHEMATICAL DOUBLE-STRUCK DIGIT ZERO,MATHEMATICAL SANS-SERIF DIGIT ZERO,'
+    'MATHEMATICAL SANS-SERIF BOLD DIGIT ZERO,MATHEMATICAL MONOSPACE DIGIT 
ZERO':
+        '0',
+    'FULLWIDTH DIGIT ONE,MATHEMATICAL BOLD DIGIT ONE,'
+    'MATHEMATICAL DOUBLE-STRUCK DIGIT ONE,MATHEMATICAL SANS-SERIF DIGIT ONE,'
+    'MATHEMATICAL SANS-SERIF BOLD DIGIT ONE,MATHEMATICAL MONOSPACE DIGIT ONE':
+        '1',
+    'FULLWIDTH DIGIT TWO,MATHEMATICAL BOLD DIGIT TWO,'
+    'MATHEMATICAL DOUBLE-STRUCK DIGIT TWO,MATHEMATICAL SANS-SERIF DIGIT TWO,'
+    'MATHEMATICAL SANS-SERIF BOLD DIGIT TWO,MATHEMATICAL MONOSPACE DIGIT TWO':
+        '2',
+    'FULLWIDTH DIGIT THREE,MATHEMATICAL BOLD DIGIT THREE,'
+    'MATHEMATICAL DOUBLE-STRUCK DIGIT THREE,MATHEMATICAL SANS-SERIF DIGIT 
THREE,'
+    'MATHEMATICAL SANS-SERIF BOLD DIGIT THREE,MATHEMATICAL MONOSPACE DIGIT 
THREE':
+        '3',
+    'FULLWIDTH DIGIT FOUR,MATHEMATICAL BOLD DIGIT FOUR,'
+    'MATHEMATICAL DOUBLE-STRUCK DIGIT FOUR,MATHEMATICAL SANS-SERIF DIGIT FOUR,'
+    'MATHEMATICAL SANS-SERIF BOLD DIGIT FOUR,MATHEMATICAL MONOSPACE DIGIT 
FOUR':
+        '4',
+    'FULLWIDTH DIGIT FIVE,MATHEMATICAL BOLD DIGIT FIVE,'
+    'MATHEMATICAL DOUBLE-STRUCK DIGIT FIVE,MATHEMATICAL SANS-SERIF DIGIT FIVE,'
+    'MATHEMATICAL SANS-SERIF BOLD DIGIT FIVE,MATHEMATICAL MONOSPACE DIGIT 
FIVE':
+        '5',
+    'FULLWIDTH DIGIT SIX,MATHEMATICAL BOLD DIGIT SIX,'
+    'MATHEMATICAL DOUBLE-STRUCK DIGIT SIX,MATHEMATICAL SANS-SERIF DIGIT SIX,'
+    'MATHEMATICAL SANS-SERIF BOLD DIGIT SIX,MATHEMATICAL MONOSPACE DIGIT SIX':
+        '6',
+    'FULLWIDTH DIGIT SEVEN,MATHEMATICAL BOLD DIGIT SEVEN,'
+    'MATHEMATICAL DOUBLE-STRUCK DIGIT SEVEN,MATHEMATICAL SANS-SERIF DIGIT 
SEVEN,'
+    'MATHEMATICAL SANS-SERIF BOLD DIGIT SEVEN,MATHEMATICAL MONOSPACE DIGIT 
SEVEN':
+        '7',
+    'FULLWIDTH DIGIT EIGHT,MATHEMATICAL BOLD DIGIT EIGHT,'
+    'MATHEMATICAL DOUBLE-STRUCK DIGIT EIGHT,MATHEMATICAL SANS-SERIF DIGIT 
EIGHT,'
+    'MATHEMATICAL SANS-SERIF BOLD DIGIT EIGHT,MATHEMATICAL MONOSPACE DIGIT 
EIGHT':
+        '8',
+    'FULLWIDTH DIGIT NINE,MATHEMATICAL BOLD DIGIT NINE,'
+    'MATHEMATICAL DOUBLE-STRUCK DIGIT NINE,MATHEMATICAL SANS-SERIF DIGIT NINE,'
+    'MATHEMATICAL SANS-SERIF BOLD DIGIT NINE,MATHEMATICAL MONOSPACE DIGIT 
NINE':
+        '9',
 }))
 
 
diff --git a/tests/test_util.doctest b/tests/test_util.doctest
index e7af530..ff92978 100644
--- a/tests/test_util.doctest
+++ b/tests/test_util.doctest
@@ -25,7 +25,7 @@ stable and as such not part of the public API of stdnum.
 
 >>> from stdnum.util import (
 ...     get_number_modules, get_module_name, get_module_description,
-...     isdigits, to_unicode)
+...     clean, isdigits, to_unicode)
 
 
 The to_unicode() function is used to force conversion of a string to unicode

https://arthurdejong.org/git/python-stdnum/commit/?id=48ff92e300696e2449070ac03b4916c76d4e77a3

commit 48ff92e300696e2449070ac03b4916c76d4e77a3
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sun Apr 28 23:35:42 2019 +0200

    Use an internal isdigits() function instead of str.isdigit()
    
    The problem with the latter is that it will also accept all kinds of
    unicode digits that are not the ASCII 0-9 digits causing all kinds of
    problems in check digit calculations.
    
    Some of these unicode characters are also considered digits by int() but
    some are not (such as the SUPERSCRIPT TWO unicode character).
    
    Closes https://github.com/arthurdejong/python-stdnum/issues/96

diff --git a/stdnum/ar/cbu.py b/stdnum/ar/cbu.py
index 14d0e25..08f6f98 100644
--- a/stdnum/ar/cbu.py
+++ b/stdnum/ar/cbu.py
@@ -40,7 +40,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -62,7 +62,7 @@ def validate(number):
     number = compact(number)
     if len(number) != 22:
         raise InvalidLength()
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if calc_check_digit(number[:7]) != number[7]:
         raise InvalidChecksum()
diff --git a/stdnum/ar/cuit.py b/stdnum/ar/cuit.py
index fdf9ee0..6325dce 100644
--- a/stdnum/ar/cuit.py
+++ b/stdnum/ar/cuit.py
@@ -46,7 +46,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -67,7 +67,7 @@ def validate(number):
     number = compact(number)
     if len(number) != 11:
         raise InvalidLength()
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if calc_check_digit(number[:-1]) != number[-1]:
         raise InvalidChecksum()
diff --git a/stdnum/ar/dni.py b/stdnum/ar/dni.py
index 510018f..f97cb7a 100644
--- a/stdnum/ar/dni.py
+++ b/stdnum/ar/dni.py
@@ -39,7 +39,7 @@ InvalidLength: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -51,7 +51,7 @@ def compact(number):
 def validate(number):
     """Check if the number is a valid DNI."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) not in (7, 8):
         raise InvalidLength()
diff --git a/stdnum/at/businessid.py b/stdnum/at/businessid.py
index 20c2a0f..04fc564 100644
--- a/stdnum/at/businessid.py
+++ b/stdnum/at/businessid.py
@@ -1,7 +1,7 @@
 # businessid.py - functions for handling Austrian company register numbers
 #
 # Copyright (C) 2015 Holvi Payment Services Oy
-# Copyright (C) 2012, 2013 Arthur de Jong
+# Copyright (C) 2012-2019 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
@@ -38,10 +38,15 @@ Traceback (most recent call last):
 InvalidFormat: ...
 """
 
+import re
+
 from stdnum.exceptions import *
 from stdnum.util import clean
 
 
+_businessid_re = re.compile('^[0-9]+[a-z]$')
+
+
 def compact(number):
     """Convert the number to the minimal representation. This strips the
     number of any valid separators and removes surrounding whitespace.
@@ -56,7 +61,7 @@ def validate(number):
     """Check if the number is a valid company register number. This only
     checks the formatting."""
     number = compact(number)
-    if not number[-1:].isalpha() or not number[:-1].isdigit():
+    if not _businessid_re.match(number):
         raise InvalidFormat()
     return number
 
diff --git a/stdnum/at/postleitzahl.py b/stdnum/at/postleitzahl.py
index 39c3771..feafa84 100644
--- a/stdnum/at/postleitzahl.py
+++ b/stdnum/at/postleitzahl.py
@@ -46,7 +46,7 @@ InvalidFormat: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -66,7 +66,7 @@ def info(number):
 def validate(number):
     """Check if the number is a valid postal code."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 4:
         raise InvalidLength()
diff --git a/stdnum/at/tin.py b/stdnum/at/tin.py
index 71abb54..88f0037 100644
--- a/stdnum/at/tin.py
+++ b/stdnum/at/tin.py
@@ -45,7 +45,7 @@ More information:
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -86,7 +86,7 @@ def validate(number, office=None):
     number = compact(number)
     if len(number) != 9:
         raise InvalidLength()
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if calc_check_digit(number) != number[-1]:
         raise InvalidChecksum()
diff --git a/stdnum/at/uid.py b/stdnum/at/uid.py
index a3b103a..0ed537b 100644
--- a/stdnum/at/uid.py
+++ b/stdnum/at/uid.py
@@ -34,7 +34,7 @@ InvalidChecksum: ...
 
 from stdnum import luhn
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -56,7 +56,7 @@ def validate(number):
     """Check if the number 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():
+    if number[:1] != 'U' or not isdigits(number[1:]):
         raise InvalidFormat()
     if len(number) != 9:
         raise InvalidLength()
diff --git a/stdnum/at/vnr.py b/stdnum/at/vnr.py
index c189246..73745a0 100644
--- a/stdnum/at/vnr.py
+++ b/stdnum/at/vnr.py
@@ -38,7 +38,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -58,7 +58,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit() or number.startswith('0'):
+    if not isdigits(number) or number.startswith('0'):
         raise InvalidFormat()
     if len(number) != 10:
         raise InvalidLength()
diff --git a/stdnum/au/abn.py b/stdnum/au/abn.py
index 301eb13..a0d4724 100644
--- a/stdnum/au/abn.py
+++ b/stdnum/au/abn.py
@@ -40,7 +40,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -61,7 +61,7 @@ def validate(number):
     """Check if the number is a valid ABN. This checks the length, formatting
     and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 11:
         raise InvalidLength()
diff --git a/stdnum/au/acn.py b/stdnum/au/acn.py
index ac78695..77e6227 100644
--- a/stdnum/au/acn.py
+++ b/stdnum/au/acn.py
@@ -42,7 +42,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -60,7 +60,7 @@ def validate(number):
     """Check if the number is a valid ACN. This checks the length, formatting
     and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 9:
         raise InvalidLength()
diff --git a/stdnum/au/tfn.py b/stdnum/au/tfn.py
index 5cee2a8..d515bac 100644
--- a/stdnum/au/tfn.py
+++ b/stdnum/au/tfn.py
@@ -43,7 +43,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -62,7 +62,7 @@ def validate(number):
     """Check if the number is a valid TFN. This checks the length, formatting
     and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) not in (8, 9):
         raise InvalidLength()
diff --git a/stdnum/be/vat.py b/stdnum/be/vat.py
index 5e4c36d..e7bbfb9 100644
--- a/stdnum/be/vat.py
+++ b/stdnum/be/vat.py
@@ -36,7 +36,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -61,7 +61,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 10:
         raise InvalidLength()
diff --git a/stdnum/bg/egn.py b/stdnum/bg/egn.py
index 5ee9819..9591182 100644
--- a/stdnum/bg/egn.py
+++ b/stdnum/bg/egn.py
@@ -44,7 +44,7 @@ InvalidComponent: ...
 import datetime
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -82,7 +82,7 @@ def validate(number):
     """Check if the number is a valid national identification number. This
     checks the length, formatting, embedded date and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 10:
         raise InvalidLength()
diff --git a/stdnum/bg/pnf.py b/stdnum/bg/pnf.py
index 726159f..9486371 100644
--- a/stdnum/bg/pnf.py
+++ b/stdnum/bg/pnf.py
@@ -36,7 +36,7 @@ InvalidFormat: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -56,7 +56,7 @@ def validate(number):
     """Check if the number is a valid national identification number. This
     checks the length, formatting, embedded date and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 10:
         raise InvalidLength()
diff --git a/stdnum/bg/vat.py b/stdnum/bg/vat.py
index 808d1de..c3717c7 100644
--- a/stdnum/bg/vat.py
+++ b/stdnum/bg/vat.py
@@ -36,7 +36,7 @@ InvalidChecksum: ...
 
 from stdnum.bg import egn, pnf
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -68,7 +68,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) == 9:
         # 9 digit numbers are for legal entities
diff --git a/stdnum/br/cnpj.py b/stdnum/br/cnpj.py
index c153b8c..a9ad9b5 100644
--- a/stdnum/br/cnpj.py
+++ b/stdnum/br/cnpj.py
@@ -39,7 +39,7 @@ InvalidFormat: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -62,7 +62,7 @@ def validate(number):
     """Check if the number is a valid CNPJ. This checks the length and
     whether the check digits are correct."""
     number = compact(number)
-    if not number.isdigit() or int(number) <= 0:
+    if not isdigits(number) or int(number) <= 0:
         raise InvalidFormat()
     if len(number) != 14:
         raise InvalidLength()
diff --git a/stdnum/br/cpf.py b/stdnum/br/cpf.py
index 5287d64..8e4cd5f 100644
--- a/stdnum/br/cpf.py
+++ b/stdnum/br/cpf.py
@@ -43,7 +43,7 @@ InvalidFormat: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -65,7 +65,7 @@ def validate(number):
     """Check if the number 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:
+    if not isdigits(number) or int(number) <= 0:
         raise InvalidFormat()
     if len(number) != 11:
         raise InvalidLength()
diff --git a/stdnum/ca/bn.py b/stdnum/ca/bn.py
index d55f34a..0345d83 100644
--- a/stdnum/ca/bn.py
+++ b/stdnum/ca/bn.py
@@ -45,7 +45,7 @@ InvalidFormat: ...
 
 from stdnum import luhn
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -60,13 +60,13 @@ def validate(number):
     number = compact(number)
     if len(number) not in (9, 15):
         raise InvalidLength()
-    if not number[:9].isdigit():
+    if not isdigits(number[:9]):
         raise InvalidFormat()
     luhn.validate(number[:9])
     if len(number) == 15:
         if number[9:11] not in ('RC', 'RM', 'RP', 'RT'):
             raise InvalidComponent()
-        if not number[11:].isdigit():
+        if not isdigits(number[11:]):
             raise InvalidFormat()
     return number
 
diff --git a/stdnum/ca/sin.py b/stdnum/ca/sin.py
index 05508b5..9d07e5b 100644
--- a/stdnum/ca/sin.py
+++ b/stdnum/ca/sin.py
@@ -45,7 +45,7 @@ InvalidFormat: ...
 
 from stdnum import luhn
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -60,7 +60,7 @@ def validate(number):
     number = compact(number)
     if len(number) != 9:
         raise InvalidLength()
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     return luhn.validate(number)
 
diff --git a/stdnum/casrn.py b/stdnum/casrn.py
index de181b8..acd5d7f 100644
--- a/stdnum/casrn.py
+++ b/stdnum/casrn.py
@@ -35,7 +35,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -59,7 +59,7 @@ def validate(number):
     number = compact(number)
     if not 7 <= len(number) <= 12:
         raise InvalidLength()
-    if not number[:-5].isdigit() or not number[-4:-2].isdigit():
+    if not isdigits(number[:-5]) or not isdigits(number[-4:-2]):
         raise InvalidFormat()
     if number[-2] != '-' or number[-5] != '-':
         raise InvalidFormat()
diff --git a/stdnum/ch/uid.py b/stdnum/ch/uid.py
index 461715d..0e5fb2c 100644
--- a/stdnum/ch/uid.py
+++ b/stdnum/ch/uid.py
@@ -43,7 +43,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -68,7 +68,7 @@ def validate(number):
         raise InvalidLength()
     if not number.startswith('CHE'):
         raise InvalidComponent()
-    if not number[3:].isdigit():
+    if not isdigits(number[3:]):
         raise InvalidFormat()
     if number[-1] != calc_check_digit(number[3:-1]):
         raise InvalidChecksum()
diff --git a/stdnum/cl/rut.py b/stdnum/cl/rut.py
index 58b93b3..d15dba4 100644
--- a/stdnum/cl/rut.py
+++ b/stdnum/cl/rut.py
@@ -43,7 +43,7 @@ InvalidFormat: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -68,7 +68,7 @@ def validate(number):
     number = compact(number)
     if len(number) not in (8, 9):
         raise InvalidLength()
-    if not number[:-1].isdigit():
+    if not isdigits(number[:-1]):
         raise InvalidFormat()
     if number[-1] != calc_check_digit(number[:-1]):
         raise InvalidChecksum()
diff --git a/stdnum/cn/ric.py b/stdnum/cn/ric.py
index e528910..4b18cee 100644
--- a/stdnum/cn/ric.py
+++ b/stdnum/cn/ric.py
@@ -35,7 +35,7 @@ digit is the checksum.
 import datetime
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -81,9 +81,7 @@ def validate(number):
     number = compact(number)
     if len(number) != 18:
         raise InvalidLength()
-    if not number[:-1].isdigit():
-        raise InvalidFormat()
-    if not number[-1].isdigit() and number[-1] != 'X':
+    if not isdigits(number[:-1]):
         raise InvalidFormat()
     if number[-1] != calc_check_digit(number):
         raise InvalidChecksum()
diff --git a/stdnum/co/nit.py b/stdnum/co/nit.py
index fe3dd48..2b18205 100644
--- a/stdnum/co/nit.py
+++ b/stdnum/co/nit.py
@@ -36,7 +36,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -59,7 +59,7 @@ def validate(number):
     number = compact(number)
     if not 8 <= len(number) <= 16:
         raise InvalidLength()
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if calc_check_digit(number[:-1]) != number[-1]:
         raise InvalidChecksum()
diff --git a/stdnum/cu/ni.py b/stdnum/cu/ni.py
index 78e4df1..e9d3d5d 100644
--- a/stdnum/cu/ni.py
+++ b/stdnum/cu/ni.py
@@ -53,7 +53,7 @@ InvalidComponent: ...
 import datetime
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -95,7 +95,7 @@ def validate(number):
     number = compact(number)
     if len(number) != 11:
         raise InvalidLength()
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     get_birth_date(number)
     return number
diff --git a/stdnum/cy/vat.py b/stdnum/cy/vat.py
index df3674d..ce9115c 100644
--- a/stdnum/cy/vat.py
+++ b/stdnum/cy/vat.py
@@ -34,7 +34,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -63,7 +63,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number[:-1].isdigit():
+    if not isdigits(number[:-1]):
         raise InvalidFormat()
     if len(number) != 9:
         raise InvalidLength()
diff --git a/stdnum/cz/dic.py b/stdnum/cz/dic.py
index 25304a1..475521d 100644
--- a/stdnum/cz/dic.py
+++ b/stdnum/cz/dic.py
@@ -42,7 +42,7 @@ InvalidChecksum: ...
 
 from stdnum.cz import rc
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -72,7 +72,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) == 8:
         # legal entities
diff --git a/stdnum/cz/rc.py b/stdnum/cz/rc.py
index c86f00a..627c312 100644
--- a/stdnum/cz/rc.py
+++ b/stdnum/cz/rc.py
@@ -50,7 +50,7 @@ InvalidLength: ...
 import datetime
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -85,7 +85,7 @@ def validate(number):
     """Check if the number is a valid birth number. This checks the length,
     formatting, embedded date and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) not in (9, 10):
         raise InvalidLength()
diff --git a/stdnum/de/idnr.py b/stdnum/de/idnr.py
index c2f45c5..5c5a179 100644
--- a/stdnum/de/idnr.py
+++ b/stdnum/de/idnr.py
@@ -49,7 +49,7 @@ from collections import defaultdict
 
 from stdnum.exceptions import *
 from stdnum.iso7064 import mod_11_10
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -64,7 +64,7 @@ def validate(number):
     number = compact(number)
     if len(number) != 11:
         raise InvalidLength()
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if number.startswith('0'):
         raise InvalidFormat()
diff --git a/stdnum/de/stnr.py b/stdnum/de/stnr.py
index d10ce37..7838977 100644
--- a/stdnum/de/stnr.py
+++ b/stdnum/de/stnr.py
@@ -53,7 +53,7 @@ InvalidLength: ...
 import re
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 # The number formats per region (regional and country-wide format)
@@ -132,7 +132,7 @@ def validate(number, region=None):
     formatting. The region can be supplied to verify that the number is
     assigned in that region."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) not in (10, 11, 13):
         raise InvalidLength()
diff --git a/stdnum/de/vat.py b/stdnum/de/vat.py
index 3581e41..f2bfe29 100644
--- a/stdnum/de/vat.py
+++ b/stdnum/de/vat.py
@@ -34,7 +34,7 @@ InvalidChecksum: ...
 
 from stdnum.exceptions import *
 from stdnum.iso7064 import mod_11_10
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -50,7 +50,7 @@ def validate(number):
     """Check 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':
+    if not isdigits(number) or number[0] == '0':
         raise InvalidFormat()
     if len(number) != 9:
         raise InvalidLength()
diff --git a/stdnum/dk/cpr.py b/stdnum/dk/cpr.py
index 841c0e9..b6d636b 100644
--- a/stdnum/dk/cpr.py
+++ b/stdnum/dk/cpr.py
@@ -55,7 +55,7 @@ datetime.date(2052, 10, 21)
 import datetime
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -93,7 +93,7 @@ def validate(number):
     """Check 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():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 10:
         raise InvalidLength()
diff --git a/stdnum/dk/cvr.py b/stdnum/dk/cvr.py
index 808f1c9..aa80402 100644
--- a/stdnum/dk/cvr.py
+++ b/stdnum/dk/cvr.py
@@ -31,7 +31,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -53,7 +53,7 @@ def validate(number):
     """Check 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':
+    if not isdigits(number) or number[0] == '0':
         raise InvalidFormat()
     if len(number) != 8:
         raise InvalidLength()
diff --git a/stdnum/do/cedula.py b/stdnum/do/cedula.py
index 36cae57..7cf4627 100644
--- a/stdnum/do/cedula.py
+++ b/stdnum/do/cedula.py
@@ -40,7 +40,7 @@ InvalidFormat: ...
 from stdnum import luhn
 from stdnum.do import rnc
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 # list of Cedulas that do not match the checksum but are nonetheless valid
@@ -154,7 +154,7 @@ def compact(number):
 def validate(number):
     """Check if the number provided is a valid cedula."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if number in whitelist:
         return number
diff --git a/stdnum/do/ncf.py b/stdnum/do/ncf.py
index 4d70b1b..5a03fb8 100644
--- a/stdnum/do/ncf.py
+++ b/stdnum/do/ncf.py
@@ -43,7 +43,7 @@ InvalidFormat: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -67,10 +67,10 @@ def validate(number):
     """Check if the number provided is a valid NCF."""
     number = compact(number)
     if len(number) == 11:
-        if number[0] != 'B' or not number[1:].isdigit():
+        if number[0] != 'B' or not isdigits(number[1:]):
             raise InvalidFormat()
     elif len(number) == 19:
-        if number[0] not in 'AP' or not number[1:].isdigit():
+        if number[0] not in 'AP' or not isdigits(number[1:]):
             raise InvalidFormat()
     else:
         raise InvalidLength()
diff --git a/stdnum/do/rnc.py b/stdnum/do/rnc.py
index 7506c95..a6d785f 100644
--- a/stdnum/do/rnc.py
+++ b/stdnum/do/rnc.py
@@ -42,7 +42,7 @@ InvalidChecksum: ...
 import json
 
 from stdnum.exceptions import *
-from stdnum.util import clean, get_soap_client
+from stdnum.util import clean, get_soap_client, isdigits
 
 
 # list of RNCs that do not match the checksum but are nonetheless valid
@@ -74,7 +74,7 @@ def calc_check_digit(number):
 def validate(number):
     """Check if the number provided is a valid RNC."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if number in whitelist:
         return number
diff --git a/stdnum/ean.py b/stdnum/ean.py
index 36f825a..306474c 100644
--- a/stdnum/ean.py
+++ b/stdnum/ean.py
@@ -29,7 +29,7 @@ module handles numbers EAN-13, EAN-8 and UPC (12-digit) 
format.
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -50,7 +50,7 @@ def validate(number):
     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():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) not in (13, 12, 8):
         raise InvalidLength()
diff --git a/stdnum/ec/ci.py b/stdnum/ec/ci.py
index a9e8604..6e58851 100644
--- a/stdnum/ec/ci.py
+++ b/stdnum/ec/ci.py
@@ -36,7 +36,7 @@ InvalidLength: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -58,7 +58,7 @@ def validate(number):
     number = compact(number)
     if len(number) != 10:
         raise InvalidLength()
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if (number[:2] < '01' or number[:2] > '24') and (number[:2] not in ('30', 
'50')):
         raise InvalidComponent()  # invalid province code
diff --git a/stdnum/ec/ruc.py b/stdnum/ec/ruc.py
index b4b5fc5..6d0c961 100644
--- a/stdnum/ec/ruc.py
+++ b/stdnum/ec/ruc.py
@@ -38,6 +38,7 @@ InvalidLength: ...
 
 from stdnum.ec import ci
 from stdnum.exceptions import *
+from stdnum.util import isdigits
 
 
 __all__ = ['compact', 'validate', 'is_valid']
@@ -58,7 +59,7 @@ def validate(number):
     number = compact(number)
     if len(number) != 13:
         raise InvalidLength()
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if (number[:2] < '01' or number[:2] > '24') and (number[:2] not in ('30', 
'50')):
         raise InvalidComponent()  # invalid province code
diff --git a/stdnum/ee/ik.py b/stdnum/ee/ik.py
index eded9e5..6eadb84 100644
--- a/stdnum/ee/ik.py
+++ b/stdnum/ee/ik.py
@@ -42,7 +42,7 @@ datetime.date(1968, 5, 28)
 import datetime
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -98,7 +98,7 @@ def validate(number):
     """Check if the number provided is valid. This checks the length,
     formatting, embedded date and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 11:
         raise InvalidLength()
diff --git a/stdnum/ee/kmkr.py b/stdnum/ee/kmkr.py
index a5762e5..24ddbb3 100644
--- a/stdnum/ee/kmkr.py
+++ b/stdnum/ee/kmkr.py
@@ -31,7 +31,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -53,7 +53,7 @@ def validate(number):
     """Check if the number provided is a valid VAT number. This checks the
     length, formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 9:
         raise InvalidLength()
diff --git a/stdnum/ee/registrikood.py b/stdnum/ee/registrikood.py
index dad6980..8f0ebc1 100644
--- a/stdnum/ee/registrikood.py
+++ b/stdnum/ee/registrikood.py
@@ -49,7 +49,7 @@ InvalidComponent: ...
 
 from stdnum.ee.ik import calc_check_digit
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -62,7 +62,7 @@ def validate(number):
     """Check if the number provided is valid. This checks the length and
     check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 8:
         raise InvalidLength()
diff --git a/stdnum/es/ccc.py b/stdnum/es/ccc.py
index 69a4105..b9e1db1 100644
--- a/stdnum/es/ccc.py
+++ b/stdnum/es/ccc.py
@@ -63,7 +63,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -103,7 +103,7 @@ def validate(number):
     number = compact(number)
     if len(number) != 20:
         raise InvalidLength()
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if number[8:10] != calc_check_digits(number):
         raise InvalidChecksum()
diff --git a/stdnum/es/cif.py b/stdnum/es/cif.py
index 8802f87..982cdff 100644
--- a/stdnum/es/cif.py
+++ b/stdnum/es/cif.py
@@ -49,6 +49,7 @@ InvalidFormat: ...
 from stdnum import luhn
 from stdnum.es import dni
 from stdnum.exceptions import *
+from stdnum.util import isdigits
 
 
 __all__ = ['compact', 'validate', 'is_valid', 'split']
@@ -70,7 +71,7 @@ def validate(number):
     """Check if the number provided is a valid DNI number. This checks the
     length, formatting and check digit."""
     number = compact(number)
-    if not number[1:-1].isdigit():
+    if not isdigits(number[1:-1]):
         raise InvalidFormat()
     if len(number) != 9:
         raise InvalidLength()
diff --git a/stdnum/es/cups.py b/stdnum/es/cups.py
index 896240a..895bcc2 100644
--- a/stdnum/es/cups.py
+++ b/stdnum/es/cups.py
@@ -54,7 +54,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -92,11 +92,11 @@ def validate(number):
         raise InvalidLength()
     if number[:2] != 'ES':
         raise InvalidComponent()
-    if not number[2:18].isdigit():
+    if not isdigits(number[2:18]):
         raise InvalidFormat()
     if number[20:]:
         pnumber, ptype = number[20:]
-        if not pnumber.isdigit():
+        if not isdigits(pnumber):
             raise InvalidFormat()
         if ptype not in 'FPRCXYZ':
             raise InvalidFormat()
diff --git a/stdnum/es/dni.py b/stdnum/es/dni.py
index 9b529ef..d255efd 100644
--- a/stdnum/es/dni.py
+++ b/stdnum/es/dni.py
@@ -39,7 +39,7 @@ InvalidLength: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -58,7 +58,7 @@ def validate(number):
     """Check 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():
+    if not isdigits(number[:-1]):
         raise InvalidFormat()
     if len(number) != 9:
         raise InvalidLength()
diff --git a/stdnum/es/nie.py b/stdnum/es/nie.py
index 91c5918..1bd2325 100644
--- a/stdnum/es/nie.py
+++ b/stdnum/es/nie.py
@@ -38,6 +38,7 @@ InvalidLength: ...
 
 from stdnum.es import dni
 from stdnum.exceptions import *
+from stdnum.util import isdigits
 
 
 __all__ = ['compact', 'calc_check_digit', 'validate', 'is_valid']
@@ -59,7 +60,7 @@ def validate(number):
     """Check 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':
+    if not isdigits(number[1:-1]) or number[:1] not in 'XYZ':
         raise InvalidFormat()
     if len(number) != 9:
         raise InvalidLength()
diff --git a/stdnum/es/nif.py b/stdnum/es/nif.py
index b99049d..af7e1f4 100644
--- a/stdnum/es/nif.py
+++ b/stdnum/es/nif.py
@@ -46,7 +46,7 @@ InvalidChecksum: ...
 
 from stdnum.es import cif, dni, nie
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -62,7 +62,7 @@ def validate(number):
     """Check if the number provided is a valid VAT number. This checks the
     length, formatting and check digit."""
     number = compact(number)
-    if not number[1:-1].isdigit():
+    if not isdigits(number[1:-1]):
         raise InvalidFormat()
     if len(number) != 9:
         raise InvalidLength()
@@ -73,7 +73,7 @@ def validate(number):
         # these use the old checkdigit algorithm (the DNI one)
         if number[-1] != dni.calc_check_digit(number[1:-1]):
             raise InvalidChecksum()
-    elif number[0].isdigit():
+    elif isdigits(number[0]):
         # natural resident
         dni.validate(number)
     elif number[0] in 'XYZ':
diff --git a/stdnum/eu/banknote.py b/stdnum/eu/banknote.py
index f8d445d..24860e0 100644
--- a/stdnum/eu/banknote.py
+++ b/stdnum/eu/banknote.py
@@ -35,7 +35,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -47,13 +47,13 @@ def compact(number):
 def checksum(number):
     """Calculate the checksum over the number."""
     # replace letters by their ASCII number
-    return sum(int(x) if x.isdigit() else ord(x) for x in number) % 9
+    return sum(int(x) if isdigits(x) else ord(x) for x in number) % 9
 
 
 def validate(number):
     """Check if the number is a valid banknote serial number."""
     number = compact(number)
-    if not number[:2].isalnum() or not number[2:].isdigit():
+    if not number[:2].isalnum() or not isdigits(number[2:]):
         raise InvalidFormat()
     if len(number) != 12:
         raise InvalidLength()
diff --git a/stdnum/eu/nace.py b/stdnum/eu/nace.py
index 01ce652..9fdd8bf 100644
--- a/stdnum/eu/nace.py
+++ b/stdnum/eu/nace.py
@@ -53,7 +53,7 @@ InvalidLength: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -89,7 +89,7 @@ def validate(number):
         if not number.isalpha():
             raise InvalidFormat()
     else:
-        if not number.isdigit():
+        if not isdigits(number):
             raise InvalidFormat()
     info(number)
     return number
diff --git a/stdnum/fi/alv.py b/stdnum/fi/alv.py
index 9a0820c..6e3515c 100644
--- a/stdnum/fi/alv.py
+++ b/stdnum/fi/alv.py
@@ -31,7 +31,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -53,7 +53,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 8:
         raise InvalidLength()
diff --git a/stdnum/fi/associationid.py b/stdnum/fi/associationid.py
index be2fd13..4374671 100644
--- a/stdnum/fi/associationid.py
+++ b/stdnum/fi/associationid.py
@@ -43,7 +43,7 @@ InvalidFormat: The number has an invalid format.
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 # a collection of all registered numbers with 2 or less digits
@@ -63,7 +63,7 @@ def validate(number):
     """Check if the number is a valid Finnish association register number.
     This checks the length and format."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) < 1 or len(number) > 6:
         raise InvalidLength()
diff --git a/stdnum/fi/veronumero.py b/stdnum/fi/veronumero.py
index 78023e1..4513df9 100644
--- a/stdnum/fi/veronumero.py
+++ b/stdnum/fi/veronumero.py
@@ -44,7 +44,7 @@ InvalidLength: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -57,7 +57,7 @@ def validate(number):
     """Check if the number is a valid tax number. This checks the length and
     formatting."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 12:
         raise InvalidLength()
diff --git a/stdnum/figi.py b/stdnum/figi.py
index e8f1434..de5d648 100644
--- a/stdnum/figi.py
+++ b/stdnum/figi.py
@@ -38,7 +38,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -65,7 +65,7 @@ def validate(number):
         raise InvalidFormat()
     if len(number) != 12:
         raise InvalidLength()
-    if number[0].isdigit() or number[1].isdigit():
+    if isdigits(number[0]) or isdigits(number[1]):
         raise InvalidFormat()
     if number[:2] in ('BS', 'BM', 'GG', 'GB', 'VG'):
         raise InvalidComponent()
diff --git a/stdnum/fr/nif.py b/stdnum/fr/nif.py
index e3eb256..131c873 100644
--- a/stdnum/fr/nif.py
+++ b/stdnum/fr/nif.py
@@ -41,7 +41,7 @@ InvalidLength: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -53,7 +53,7 @@ def compact(number):
 def validate(number):
     """Check if the number provided is a valid NIF."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 13:
         raise InvalidLength()
diff --git a/stdnum/fr/nir.py b/stdnum/fr/nir.py
index 992fb61..bc02faf 100644
--- a/stdnum/fr/nir.py
+++ b/stdnum/fr/nir.py
@@ -62,7 +62,7 @@ InvalidLength: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -85,9 +85,9 @@ def validate(number):
     """Check if the number provided is valid. This checks the length
     and check digits."""
     number = compact(number)
-    if not number[:5].isdigit() or not number[7:].isdigit():
+    if not isdigits(number[:5]) or not isdigits(number[7:]):
         raise InvalidFormat()
-    if not number[5:7].isdigit() and number[5:7] not in ('2A', '2B'):
+    if not isdigits(number[5:7]) and number[5:7] not in ('2A', '2B'):
         raise InvalidFormat()
     if len(number) != 15:
         raise InvalidLength()
diff --git a/stdnum/fr/siren.py b/stdnum/fr/siren.py
index ced8224..afd8368 100644
--- a/stdnum/fr/siren.py
+++ b/stdnum/fr/siren.py
@@ -38,7 +38,7 @@ InvalidChecksum: ...
 
 from stdnum import luhn
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 # An online validation function is available but it does not provide an
@@ -57,7 +57,7 @@ def validate(number):
     """Check if the number provided is a valid SIREN. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 9:
         raise InvalidLength()
diff --git a/stdnum/fr/siret.py b/stdnum/fr/siret.py
index 5532147..7c4e0f5 100644
--- a/stdnum/fr/siret.py
+++ b/stdnum/fr/siret.py
@@ -45,7 +45,7 @@ InvalidChecksum: ...
 from stdnum import luhn
 from stdnum.exceptions import *
 from stdnum.fr import siren
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -58,7 +58,7 @@ def validate(number):
     """Check if the number is a valid SIRET. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 14:
         raise InvalidLength()
@@ -85,7 +85,7 @@ def to_siren(number):
     for char in number:
         if digit_count < 9:
             _siren.append(char)
-            if char.isdigit():
+            if isdigits(char):
                 digit_count += 1
     return ''.join(_siren)
 
diff --git a/stdnum/fr/tva.py b/stdnum/fr/tva.py
index 896d86f..b224aae 100644
--- a/stdnum/fr/tva.py
+++ b/stdnum/fr/tva.py
@@ -45,7 +45,7 @@ InvalidFormat: ...
 
 from stdnum.exceptions import *
 from stdnum.fr import siren
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 # the valid characters for the first two digits (O and I are missing)
@@ -67,20 +67,20 @@ def validate(number):
     number = compact(number)
     if not all(x in _alphabet for x in number[:2]):
         raise InvalidFormat()
-    if not number[2:].isdigit():
+    if not isdigits(number[2:]):
         raise InvalidFormat()
     if len(number) != 11:
         raise InvalidLength()
     if number[2:5] != '000':
         # numbers from Monaco are valid TVA but not SIREN
         siren.validate(number[2:])
-    if number.isdigit():
+    if isdigits(number):
         # 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():
+        if isdigits(number[0]):
             check = (
                 _alphabet.index(number[0]) * 24 +
                 _alphabet.index(number[1]) - 10)
diff --git a/stdnum/gb/nhs.py b/stdnum/gb/nhs.py
index 3460010..65e07c7 100644
--- a/stdnum/gb/nhs.py
+++ b/stdnum/gb/nhs.py
@@ -42,7 +42,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -61,7 +61,7 @@ def validate(number):
     """Check if the number is valid. This checks the length and check
     digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 10:
         raise InvalidLength()
diff --git a/stdnum/gb/sedol.py b/stdnum/gb/sedol.py
index cc1ac7d..a9c2727 100644
--- a/stdnum/gb/sedol.py
+++ b/stdnum/gb/sedol.py
@@ -34,7 +34,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 # the letters allowed in an SEDOL (vowels are never used)
@@ -62,7 +62,7 @@ def validate(number):
         raise InvalidFormat()
     if len(number) != 7:
         raise InvalidLength()
-    if number[0].isdigit() and not number.isdigit():
+    if isdigits(number[0]) and not isdigits(number):
         # new style SEDOLs are supposed to start with a letter, old-style
         # numbers should be fully numeric
         raise InvalidFormat()
diff --git a/stdnum/gb/upn.py b/stdnum/gb/upn.py
index 224082c..c0684be 100644
--- a/stdnum/gb/upn.py
+++ b/stdnum/gb/upn.py
@@ -49,7 +49,7 @@ InvalidComponent: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 # The allowed characters in an UPN.
@@ -91,7 +91,7 @@ def validate(number):
     number = compact(number)
     if len(number) != 13:
         raise InvalidLength()
-    if not number[1:-1].isdigit() or number[-1] not in _alphabet:
+    if not isdigits(number[1:-1]) or number[-1] not in _alphabet:
         raise InvalidFormat()
     if int(number[1:4]) not in _la_numbers:
         raise InvalidComponent()
diff --git a/stdnum/gb/vat.py b/stdnum/gb/vat.py
index 539e2f9..2c09da2 100644
--- a/stdnum/gb/vat.py
+++ b/stdnum/gb/vat.py
@@ -36,7 +36,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -60,7 +60,7 @@ def validate(number):
     formatting and check digit."""
     number = compact(number)
     if len(number) == 5:
-        if not number[2:].isdigit():
+        if not isdigits(number[2:]):
             raise InvalidFormat()
         if number.startswith('GD') and int(number[2:]) < 500:
             # government department
@@ -71,7 +71,7 @@ def validate(number):
         else:
             raise InvalidComponent()
     elif len(number) == 11 and number[0:6] in ('GD8888', 'HA8888'):
-        if not number[6:].isdigit():
+        if not isdigits(number[6:]):
             raise InvalidFormat()
         if number.startswith('GD') and int(number[6:9]) < 500:
             # government department
@@ -84,7 +84,7 @@ def validate(number):
         if int(number[6:9]) % 97 != int(number[9:11]):
             raise InvalidChecksum()
     elif len(number) in (9, 12):
-        if not number.isdigit():
+        if not isdigits(number):
             raise InvalidFormat()
         # standard number: nnn nnnn nn
         # branch trader: nnn nnnn nn nnn (ignore the last thee digits)
diff --git a/stdnum/gr/amka.py b/stdnum/gr/amka.py
index 5f9ac9c..867ac8e 100644
--- a/stdnum/gr/amka.py
+++ b/stdnum/gr/amka.py
@@ -45,7 +45,7 @@ import datetime
 
 from stdnum import luhn
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -84,7 +84,7 @@ def validate(number):
     """Check if the number is a valid AMKA. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 11:
         raise InvalidLength()
diff --git a/stdnum/gr/vat.py b/stdnum/gr/vat.py
index 0de5cc5..dc13c0c 100644
--- a/stdnum/gr/vat.py
+++ b/stdnum/gr/vat.py
@@ -33,7 +33,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -60,7 +60,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 9:
         raise InvalidLength()
diff --git a/stdnum/hr/oib.py b/stdnum/hr/oib.py
index 9c21e15..092c58c 100644
--- a/stdnum/hr/oib.py
+++ b/stdnum/hr/oib.py
@@ -34,7 +34,7 @@ InvalidChecksum: ...
 
 from stdnum.exceptions import *
 from stdnum.iso7064 import mod_11_10
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -50,7 +50,7 @@ def validate(number):
     """Check if the number is a valid OIB number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 11:
         raise InvalidLength()
diff --git a/stdnum/hu/anum.py b/stdnum/hu/anum.py
index e35fe8b..bb9b863 100644
--- a/stdnum/hu/anum.py
+++ b/stdnum/hu/anum.py
@@ -32,7 +32,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -54,7 +54,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 8:
         raise InvalidLength()
diff --git a/stdnum/ie/vat.py b/stdnum/ie/vat.py
index fa5bd4a..8db582c 100644
--- a/stdnum/ie/vat.py
+++ b/stdnum/ie/vat.py
@@ -42,7 +42,7 @@ InvalidFormat: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -70,13 +70,13 @@ def validate(number):
     """Check 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() or not number[2:7].isdigit():
+    if not isdigits(number[:1]) or not isdigits(number[2:7]):
         raise InvalidFormat()
     if not all(x in _alphabet for x in number[7:]):
         raise InvalidFormat()
     if len(number) not in (8, 9):
         raise InvalidLength()
-    if number[:7].isdigit():
+    if isdigits(number[:7]):
         # new system (7 digits followed by 1 or 2 letters)
         if number[7] != calc_check_digit(number[:7] + number[8:]):
             raise InvalidChecksum()
@@ -103,6 +103,6 @@ def convert(number):
     is a letter to the new 8-digit format where only the last digit is a
     character."""
     number = compact(number)
-    if len(number) == 8 and not number[1].isdigit():
+    if len(number) == 8 and not isdigits(number[1]):
         number = '0' + number[2:7] + number[0] + number[7:]
     return number
diff --git a/stdnum/il/idnr.py b/stdnum/il/idnr.py
index e638ab7..544d6df 100644
--- a/stdnum/il/idnr.py
+++ b/stdnum/il/idnr.py
@@ -45,7 +45,7 @@ InvalidLength: ...
 
 from stdnum import luhn
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -62,7 +62,7 @@ def validate(number):
     number = compact(number)
     if len(number) > 9:
         raise InvalidLength()
-    if not number.isdigit() or int(number) <= 0:
+    if not isdigits(number) or int(number) <= 0:
         raise InvalidFormat()
     luhn.validate(number)
     return number
diff --git a/stdnum/imei.py b/stdnum/imei.py
index a46a992..2ea1210 100644
--- a/stdnum/imei.py
+++ b/stdnum/imei.py
@@ -48,7 +48,7 @@ InvalidChecksum: ...
 
 from stdnum import luhn
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -60,7 +60,7 @@ def compact(number):
 def validate(number):
     """Check if the number provided is a valid IMEI (or IMEISV) number."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) == 15:
         # only 15 digit IMEI has check digit
diff --git a/stdnum/imo.py b/stdnum/imo.py
index a450bad..c4b380a 100644
--- a/stdnum/imo.py
+++ b/stdnum/imo.py
@@ -41,7 +41,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -62,7 +62,7 @@ def validate(number):
     """Check if the number provided is valid. This checks the length and
     check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 7:
         raise InvalidLength()
diff --git a/stdnum/imsi.py b/stdnum/imsi.py
index b887284..8cc9fc0 100644
--- a/stdnum/imsi.py
+++ b/stdnum/imsi.py
@@ -40,7 +40,7 @@ InvalidComponent: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -62,7 +62,7 @@ def split(number):
 def validate(number):
     """Check if the number provided is a valid IMSI."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) not in (14, 15):
         raise InvalidLength()
diff --git a/stdnum/in_/pan.py b/stdnum/in_/pan.py
index 6679b21..483e337 100644
--- a/stdnum/in_/pan.py
+++ b/stdnum/in_/pan.py
@@ -56,7 +56,7 @@ from stdnum.exceptions import *
 from stdnum.util import clean
 
 
-_pan_re = re.compile(r'^[A-Z]{3}[ABCFGHLJPTK][A-Z]\d{4}[A-Z]$')
+_pan_re = re.compile(r'^[A-Z]{5}[0-9]{4}[A-Z]$')
 
 
 def compact(number):
@@ -71,8 +71,7 @@ def validate(number):
     number = compact(number)
     if len(number) != 10:
         raise InvalidLength()
-    if not (number[:5].isalpha() and number[5:-1].isdigit() and
-            number[-1].isalpha()):
+    if not _pan_re.match(number):
         raise InvalidFormat()
     info(number)  # used to check 4th digit
     return number
diff --git a/stdnum/is_/vsk.py b/stdnum/is_/vsk.py
index e4cc552..6fd6ddd 100644
--- a/stdnum/is_/vsk.py
+++ b/stdnum/is_/vsk.py
@@ -31,7 +31,7 @@ InvalidLength: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -47,7 +47,7 @@ def validate(number):
     """Check if the number provided is a valid VAT number. This checks the
     length and formatting."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) not in (5, 6):
         raise InvalidLength()
diff --git a/stdnum/isbn.py b/stdnum/isbn.py
index d705958..06af4e8 100644
--- a/stdnum/isbn.py
+++ b/stdnum/isbn.py
@@ -63,7 +63,7 @@ InvalidChecksum: ...
 
 from stdnum import ean
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number, convert=False):
@@ -92,7 +92,7 @@ def validate(number, convert=False):
     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():
+    if not isdigits(number[:-1]):
         raise InvalidFormat()
     if len(number) == 10:
         if _calc_isbn10_check_digit(number[:-1]) != number[-1]:
diff --git a/stdnum/issn.py b/stdnum/issn.py
index 07e10e8..fcd8819 100644
--- a/stdnum/issn.py
+++ b/stdnum/issn.py
@@ -51,7 +51,7 @@ InvalidLength: ...
 
 from stdnum import ean
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -72,7 +72,7 @@ def validate(number):
     """Check if the number is a valid ISSN. This checks the length and
     whether the check digit is correct."""
     number = compact(number)
-    if not number[:-1].isdigit():
+    if not isdigits(number[:-1]):
         raise InvalidFormat()
     if len(number) != 8:
         raise InvalidLength()
diff --git a/stdnum/it/iva.py b/stdnum/it/iva.py
index a404711..2670f99 100644
--- a/stdnum/it/iva.py
+++ b/stdnum/it/iva.py
@@ -36,7 +36,7 @@ InvalidChecksum: ...
 
 from stdnum import luhn
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -52,7 +52,7 @@ def validate(number):
     """Check if the number 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:
+    if not isdigits(number) or int(number[0:7]) == 0:
         raise InvalidFormat()
     if len(number) != 11:
         raise InvalidLength()
diff --git a/stdnum/lt/asmens.py b/stdnum/lt/asmens.py
index 086a3d3..a45dcdf 100644
--- a/stdnum/lt/asmens.py
+++ b/stdnum/lt/asmens.py
@@ -39,7 +39,7 @@ InvalidChecksum: ...
 
 from stdnum.ee.ik import calc_check_digit, get_birth_date
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -52,7 +52,7 @@ def validate(number, validate_birth_date=True):
     """Check if the number provided is valid. This checks the length,
     formatting, embedded date and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 11:
         raise InvalidLength()
diff --git a/stdnum/lt/pvm.py b/stdnum/lt/pvm.py
index aadbb4a..a0984f4 100644
--- a/stdnum/lt/pvm.py
+++ b/stdnum/lt/pvm.py
@@ -38,7 +38,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -63,7 +63,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) == 9:
         # legal entities
diff --git a/stdnum/lu/tva.py b/stdnum/lu/tva.py
index 394f9cb..4ec8094 100644
--- a/stdnum/lu/tva.py
+++ b/stdnum/lu/tva.py
@@ -33,7 +33,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -54,7 +54,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 8:
         raise InvalidLength()
diff --git a/stdnum/lv/pvn.py b/stdnum/lv/pvn.py
index b874dd8..03db0f4 100644
--- a/stdnum/lv/pvn.py
+++ b/stdnum/lv/pvn.py
@@ -42,7 +42,7 @@ InvalidComponent: ...
 import datetime
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 # validation functions are available on-line but it is not allowed
@@ -91,7 +91,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 11:
         raise InvalidLength()
diff --git a/stdnum/md/idno.py b/stdnum/md/idno.py
index a8e15e1..4ba8063 100644
--- a/stdnum/md/idno.py
+++ b/stdnum/md/idno.py
@@ -38,7 +38,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -57,7 +57,7 @@ def validate(number):
     """Check if the number provided is valid. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 13:
         raise InvalidLength()
diff --git a/stdnum/meid.py b/stdnum/meid.py
index c5a349a..5dedd8f 100644
--- a/stdnum/meid.py
+++ b/stdnum/meid.py
@@ -39,7 +39,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 _hex_alphabet = '0123456789ABCDEF'
@@ -67,7 +67,7 @@ def _parse(number):
         return number[0:14], number[14:]
     elif len(number) in (18, 19):
         # 18-digit decimal representation
-        if not number.isdigit():
+        if not isdigits(number):
             raise InvalidFormat()
         return number[0:18], number[18:]
     else:
@@ -80,7 +80,7 @@ def calc_check_digit(number):
     # 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
-    if number.isdigit():
+    if isdigits(number):
         return luhn.calc_check_digit(number)
     else:
         return luhn.calc_check_digit(number, alphabet=_hex_alphabet)
@@ -132,7 +132,7 @@ def validate(number, strip_check_digit=True):
             raise InvalidComponent()
         number = '%08X%06X' % (manufacturer_code, serial_num)
         cd = calc_check_digit(number)
-    elif number.isdigit():
+    elif isdigits(number):
         # if the remaining hex format is fully decimal it is an IMEI number
         from stdnum import imei
         imei.validate(number + cd)
diff --git a/stdnum/mt/vat.py b/stdnum/mt/vat.py
index 61d2f83..6d7899e 100644
--- a/stdnum/mt/vat.py
+++ b/stdnum/mt/vat.py
@@ -31,7 +31,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -53,7 +53,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit() or number[0] == '0':
+    if not isdigits(number) or number[0] == '0':
         raise InvalidFormat()
     if len(number) != 8:
         raise InvalidLength()
diff --git a/stdnum/mu/nid.py b/stdnum/mu/nid.py
index 354de4a..5af67fd 100644
--- a/stdnum/mu/nid.py
+++ b/stdnum/mu/nid.py
@@ -39,11 +39,15 @@ More information:
 """
 
 import datetime
+import re
 
 from stdnum.exceptions import *
 from stdnum.util import clean
 
 
+_nid_re = re.compile('^[A-Z][0-9]+[0-9A-Z]$')
+
+
 # characters used for checksum calculation
 _alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
 
@@ -78,7 +82,7 @@ def validate(number):
     number = compact(number)
     if len(number) != 14:
         raise InvalidLength()
-    if not number[0].isalpha() or not number[1:-1].isdigit():
+    if not _nid_re.match(number):
         raise InvalidFormat()
     if calc_check_digit(number) != number[-1]:
         raise InvalidChecksum()
diff --git a/stdnum/my/nric.py b/stdnum/my/nric.py
index 7817228..37170d4 100644
--- a/stdnum/my/nric.py
+++ b/stdnum/my/nric.py
@@ -44,7 +44,7 @@ InvalidComponent: ...
 import datetime
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -90,7 +90,7 @@ def validate(number):
     number = compact(number)
     if len(number) != 12:
         raise InvalidLength()
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     get_birth_date(number)
     get_birth_place(number)
diff --git a/stdnum/nl/bsn.py b/stdnum/nl/bsn.py
index 4428e1f..b869100 100644
--- a/stdnum/nl/bsn.py
+++ b/stdnum/nl/bsn.py
@@ -45,7 +45,7 @@ InvalidLength: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -67,7 +67,7 @@ def validate(number):
     """Check if the number 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:
+    if not isdigits(number) or int(number) <= 0:
         raise InvalidFormat()
     if len(number) != 9:
         raise InvalidLength()
diff --git a/stdnum/nl/btw.py b/stdnum/nl/btw.py
index 69124a3..9485257 100644
--- a/stdnum/nl/btw.py
+++ b/stdnum/nl/btw.py
@@ -40,7 +40,7 @@ InvalidChecksum: ...
 
 from stdnum.exceptions import *
 from stdnum.nl import bsn
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -56,7 +56,7 @@ def validate(number):
     """Check if the number 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:
+    if not isdigits(number[10:]) or int(number[10:]) <= 0:
         raise InvalidFormat()
     if len(number) != 12:
         raise InvalidLength()
diff --git a/stdnum/nl/onderwijsnummer.py b/stdnum/nl/onderwijsnummer.py
index 4ed341a..ad3545c 100644
--- a/stdnum/nl/onderwijsnummer.py
+++ b/stdnum/nl/onderwijsnummer.py
@@ -45,6 +45,7 @@ InvalidFormat: ...
 
 from stdnum.exceptions import *
 from stdnum.nl.bsn import checksum, compact
+from stdnum.util import isdigits
 
 
 __all__ = ['compact', 'validate', 'is_valid']
@@ -55,7 +56,7 @@ def validate(number):
     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:
+    if not isdigits(number) or int(number) <= 0:
         raise InvalidFormat()
     if not number.startswith('10'):
         raise InvalidFormat()
diff --git a/stdnum/no/fodselsnummer.py b/stdnum/no/fodselsnummer.py
index 2d9b842..ce1ef13 100644
--- a/stdnum/no/fodselsnummer.py
+++ b/stdnum/no/fodselsnummer.py
@@ -42,7 +42,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -77,7 +77,7 @@ def validate(number):
     number = compact(number)
     if len(number) != 11:
         raise InvalidLength()
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if number[-2] != calc_check_digit1(number):
         raise InvalidChecksum()
diff --git a/stdnum/no/kontonr.py b/stdnum/no/kontonr.py
index 64a7d81..1a466e0 100644
--- a/stdnum/no/kontonr.py
+++ b/stdnum/no/kontonr.py
@@ -44,7 +44,7 @@ InvalidChecksum: ...
 
 from stdnum import luhn
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -65,7 +65,7 @@ def _calc_check_digit(number):
 def validate(number):
     """Check if the number provided is a valid bank account number."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) == 7:
         luhn.validate(number)
diff --git a/stdnum/no/orgnr.py b/stdnum/no/orgnr.py
index ebceac8..a386028 100644
--- a/stdnum/no/orgnr.py
+++ b/stdnum/no/orgnr.py
@@ -36,7 +36,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -55,7 +55,7 @@ def validate(number):
     """Check if the number is a valid organisation number. This checks the
     length, formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 9:
         raise InvalidLength()
diff --git a/stdnum/nz/bankaccount.py b/stdnum/nz/bankaccount.py
index 28c64c0..c451073 100644
--- a/stdnum/nz/bankaccount.py
+++ b/stdnum/nz/bankaccount.py
@@ -43,7 +43,7 @@ InvalidComponent: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 # The following algorithms and weights were taken from:
@@ -125,7 +125,7 @@ def _calc_checksum(number):
 def validate(number):
     """Check if the number provided is a valid bank account number."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 16:
         raise InvalidLength()
diff --git a/stdnum/pe/cui.py b/stdnum/pe/cui.py
index b50a46a..d9c37ea 100644
--- a/stdnum/pe/cui.py
+++ b/stdnum/pe/cui.py
@@ -43,7 +43,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -73,7 +73,7 @@ def validate(number):
     number = compact(number)
     if len(number) not in (8, 9):
         raise InvalidLength()
-    if not number[:8].isdigit():
+    if not isdigits(number[:8]):
         raise InvalidFormat()
     if len(number) > 8 and number[-1] not in calc_check_digits(number):
         raise InvalidChecksum()
diff --git a/stdnum/pe/ruc.py b/stdnum/pe/ruc.py
index 6e5ad80..b4ad3d5 100644
--- a/stdnum/pe/ruc.py
+++ b/stdnum/pe/ruc.py
@@ -41,7 +41,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -70,7 +70,7 @@ def validate(number):
     number = compact(number)
     if len(number) != 11:
         raise InvalidLength()
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if number[:2] not in ('10', '15', '17', '20'):
         raise InvalidComponent()  # not person or company
diff --git a/stdnum/pl/nip.py b/stdnum/pl/nip.py
index 950b031..2ff1c0f 100644
--- a/stdnum/pl/nip.py
+++ b/stdnum/pl/nip.py
@@ -33,7 +33,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -55,7 +55,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 10:
         raise InvalidLength()
diff --git a/stdnum/pl/pesel.py b/stdnum/pl/pesel.py
index 223b9cb..370082e 100644
--- a/stdnum/pl/pesel.py
+++ b/stdnum/pl/pesel.py
@@ -50,7 +50,7 @@ datetime.date(2002, 1, 13)
 import datetime
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -100,7 +100,7 @@ def validate(number):
     """Check if the number is a valid national identification number. This
     checks the length, formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 11:
         raise InvalidLength()
diff --git a/stdnum/pl/regon.py b/stdnum/pl/regon.py
index cf3697e..17b5c68 100644
--- a/stdnum/pl/regon.py
+++ b/stdnum/pl/regon.py
@@ -52,7 +52,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -76,7 +76,7 @@ def validate(number):
     """Check if the number is a valid REGON number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) not in (9, 14):
         raise InvalidLength()
diff --git a/stdnum/pt/nif.py b/stdnum/pt/nif.py
index 20c8f77..77a2808 100644
--- a/stdnum/pt/nif.py
+++ b/stdnum/pt/nif.py
@@ -33,7 +33,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -56,7 +56,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit() or number[0] == '0':
+    if not isdigits(number) or number[0] == '0':
         raise InvalidFormat()
     if len(number) != 9:
         raise InvalidLength()
diff --git a/stdnum/ro/cf.py b/stdnum/ro/cf.py
index 7b07fb9..dbede72 100644
--- a/stdnum/ro/cf.py
+++ b/stdnum/ro/cf.py
@@ -34,7 +34,7 @@ InvalidChecksum: ...
 
 from stdnum.exceptions import *
 from stdnum.ro import cnp
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -59,7 +59,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit() or number[0] == '0':
+    if not isdigits(number) or number[0] == '0':
         raise InvalidFormat()
     if len(number) == 13:
         # apparently a CNP can also be used (however, not all sources agree)
diff --git a/stdnum/ro/cnp.py b/stdnum/ro/cnp.py
index 46e034e..e6bf867 100644
--- a/stdnum/ro/cnp.py
+++ b/stdnum/ro/cnp.py
@@ -42,7 +42,7 @@ InvalidChecksum: ...
 import datetime
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -80,7 +80,7 @@ def validate(number):
     formatting and check digit."""
     number = compact(number)
     # first digit should be a known one (9=foreigner)
-    if not number.isdigit() or number[0] not in '1234569':
+    if not isdigits(number) or number[0] not in '1234569':
         raise InvalidFormat()
     if len(number) != 13:
         raise InvalidLength()
diff --git a/stdnum/rs/pib.py b/stdnum/rs/pib.py
index 8ba652b..d53e211 100644
--- a/stdnum/rs/pib.py
+++ b/stdnum/rs/pib.py
@@ -33,7 +33,7 @@ InvalidChecksum: ...
 
 from stdnum.exceptions import *
 from stdnum.iso7064 import mod_11_10
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -46,7 +46,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 9:
         raise InvalidLength()
diff --git a/stdnum/ru/inn.py b/stdnum/ru/inn.py
index 30f16d5..db30806 100644
--- a/stdnum/ru/inn.py
+++ b/stdnum/ru/inn.py
@@ -39,7 +39,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -70,7 +70,7 @@ def validate(number):
     """Check if the number is a valid ИНН. This checks the length, formatting
     and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) == 10:
         if calc_company_check_digit(number) != number[-1]:
diff --git a/stdnum/se/orgnr.py b/stdnum/se/orgnr.py
index 65f300c..2aea9eb 100644
--- a/stdnum/se/orgnr.py
+++ b/stdnum/se/orgnr.py
@@ -38,7 +38,7 @@ InvalidChecksum: ...
 
 from stdnum import luhn
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -51,7 +51,7 @@ def validate(number):
     """Check if the number is a valid organisation number. This checks
     the length, formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 10:
         raise InvalidLength()
diff --git a/stdnum/se/personnummer.py b/stdnum/se/personnummer.py
index fc8f345..1435e59 100644
--- a/stdnum/se/personnummer.py
+++ b/stdnum/se/personnummer.py
@@ -48,7 +48,7 @@ import datetime
 
 from stdnum import luhn
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -92,7 +92,7 @@ def validate(number):
     number = compact(number)
     if len(number) not in (10, 12):
         raise InvalidLength()
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     get_birth_date(number)
     luhn.validate(number[-10:])
diff --git a/stdnum/se/vat.py b/stdnum/se/vat.py
index efaabc4..9dcd356 100644
--- a/stdnum/se/vat.py
+++ b/stdnum/se/vat.py
@@ -34,7 +34,7 @@ InvalidChecksum: ...
 
 from stdnum.exceptions import *
 from stdnum.se import orgnr
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -50,7 +50,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit() or number[-2:] != '01':
+    if not isdigits(number) or number[-2:] != '01':
         raise InvalidFormat()
     orgnr.validate(number[:-2])
     return number
diff --git a/stdnum/si/ddv.py b/stdnum/si/ddv.py
index 5b16dbc..bb6d986 100644
--- a/stdnum/si/ddv.py
+++ b/stdnum/si/ddv.py
@@ -33,7 +33,7 @@ InvalidChecksum: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -57,7 +57,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit() or number.startswith('0'):
+    if not isdigits(number) or number.startswith('0'):
         raise InvalidFormat()
     if len(number) != 8:
         raise InvalidLength()
diff --git a/stdnum/sk/dph.py b/stdnum/sk/dph.py
index 97e08f2..c8cab28 100644
--- a/stdnum/sk/dph.py
+++ b/stdnum/sk/dph.py
@@ -33,7 +33,7 @@ InvalidChecksum: ...
 
 from stdnum.exceptions import *
 from stdnum.sk import rc
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -54,7 +54,7 @@ def validate(number):
     """Check if the number is a valid VAT number. This checks the length,
     formatting and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 10:
         raise InvalidLength()
diff --git a/stdnum/sm/coe.py b/stdnum/sm/coe.py
index f40ffaa..721bda5 100644
--- a/stdnum/sm/coe.py
+++ b/stdnum/sm/coe.py
@@ -40,7 +40,7 @@ InvalidLength: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 # a collection of all registered numbers with 2 or less digits
@@ -63,7 +63,7 @@ def validate(number):
     number = compact(number)
     if len(number) > 5 or len(number) == 0:
         raise InvalidLength()
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) < 3 and int(number) not in _lownumbers:
         raise InvalidComponent()
diff --git a/stdnum/tr/tckimlik.py b/stdnum/tr/tckimlik.py
index e5f84a6..0468d5f 100644
--- a/stdnum/tr/tckimlik.py
+++ b/stdnum/tr/tckimlik.py
@@ -46,7 +46,7 @@ InvalidFormat: ...
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean, get_soap_client
+from stdnum.util import clean, get_soap_client, isdigits
 
 
 tckimlik_wsdl = 'https://tckimlik.nvi.gov.tr/Service/KPSPublic.asmx?WSDL'
@@ -72,7 +72,7 @@ def validate(number):
     """Check if the number is a valid .C. Kimlik number. This checks the
     length and check digits."""
     number = compact(number)
-    if not number.isdigit() or number[0] == '0':
+    if not isdigits(number) or number[0] == '0':
         raise InvalidFormat()
     if len(number) != 11:
         raise InvalidLength()
diff --git a/stdnum/us/rtn.py b/stdnum/us/rtn.py
index 1e544fc..eb88926 100644
--- a/stdnum/us/rtn.py
+++ b/stdnum/us/rtn.py
@@ -43,7 +43,7 @@ InvalidChecksum: ..
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean
+from stdnum.util import clean, isdigits
 
 
 def compact(number):
@@ -69,7 +69,7 @@ def validate(number):
     """Check if the number is a valid routing number. This checks the length
     and check digit."""
     number = compact(number)
-    if not number.isdigit():
+    if not isdigits(number):
         raise InvalidFormat()
     if len(number) != 9:
         raise InvalidLength()
diff --git a/stdnum/util.py b/stdnum/util.py
index 8fa082a..bbab8b0 100644
--- a/stdnum/util.py
+++ b/stdnum/util.py
@@ -1,7 +1,7 @@
 # util.py - common utility functions
 # coding: utf-8
 #
-# Copyright (C) 2012-2018 Arthur de Jong
+# Copyright (C) 2012-2019 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
@@ -35,9 +35,14 @@ import warnings
 from stdnum.exceptions import *
 
 
+# Regular expression to match doctests in docstrings
 _strip_doctest_re = re.compile(r'^>>> .*\Z', re.DOTALL | re.MULTILINE)
 
 
+# Regular expression to match digits
+_digits_re = re.compile(r'^[0-9]+$')
+
+
 def _mk_char_map(mapping):
     """Transform a dictionary with comma separated uniode chracter names
     to tuples with unicode characters as key."""
@@ -129,6 +134,13 @@ def clean(number, deletechars=''):
     return ''.join(x for x in number if x not in deletechars)
 
 
+def isdigits(number):
+    """Check whether the provided string only consists of digits."""
+    # This function is meant to replace str.isdigit() which will also return
+    # True for all kind of unicode digits which is generally not what we want
+    return bool(_digits_re.match(number))
+
+
 def to_unicode(text):
     """Convert the specified text to a unicode string."""
     if not isinstance(text, type(u'')):
diff --git a/tests/test_cn_ric.doctest b/tests/test_cn_ric.doctest
index 5b21948..21ba405 100644
--- a/tests/test_cn_ric.doctest
+++ b/tests/test_cn_ric.doctest
@@ -56,10 +56,6 @@ True
 
 Invalid format:
 
->>> ric.validate('36042619910101007V')
-Traceback (most recent call last):
-    ...
-InvalidFormat: ...
 >>> ric.validate('T60426199101010078')
 Traceback (most recent call last):
     ...
diff --git a/tests/test_isbn.doctest b/tests/test_isbn.doctest
index 91f4863..212b0fd 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-2017 Arthur de Jong
+Copyright (C) 2010-2019 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
@@ -107,6 +107,18 @@ Regrouping tests.
 ('', '', '', '541317012152', '2')
 
 
+Explicit tests for invalid characters.
+
+>>> isbn.validate('978-90245᭓8270')
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> isbn.validate('978-9024538²70')
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+
+
 These have been found online and should all be valid numbers.
 
 >>> numbers = '''
diff --git a/tests/test_robustness.doctest b/tests/test_robustness.doctest
index b4ba36a..9250bc7 100644
--- a/tests/test_robustness.doctest
+++ b/tests/test_robustness.doctest
@@ -1,6 +1,6 @@
 test_robustness.doctest - test is_valid() functions to not break
 
-Copyright (C) 2011-2017 Arthur de Jong
+Copyright (C) 2011-2019 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,7 +24,7 @@ invalid junk.
 
 >>> testvalues = (
 ...     None, '*&^%$', '', 0, False, object(), 'Z', 'QQ', '3', '€', u'€',
-...     '😴', '¥')
+...     '😴', '¥', '3²', 'ⅷ', '⑱', '᭓', b'\xc2\xb2'.decode('utf-8'))
 >>> from stdnum.util import get_number_modules
 
 Go over each module and try every value.
diff --git a/tests/test_util.doctest b/tests/test_util.doctest
index 959a544..e7af530 100644
--- a/tests/test_util.doctest
+++ b/tests/test_util.doctest
@@ -1,6 +1,6 @@
 test_util.doctest - more detailed doctests for the stdnum.util package
 
-Copyright (C) 2017 Arthur de Jong
+Copyright (C) 2017-2019 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,7 +25,7 @@ stable and as such not part of the public API of stdnum.
 
 >>> from stdnum.util import (
 ...     get_number_modules, get_module_name, get_module_description,
-...     to_unicode)
+...     isdigits, to_unicode)
 
 
 The to_unicode() function is used to force conversion of a string to unicode
@@ -41,6 +41,31 @@ True
 True
 
 
+The clean function will also convert all kinds of unicode special characters
+to normal ASCII variations of the same characters to partially support OCR'ed
+text and text copy-pasted from other applications.
+
+>>> clean('0𝟽—𝟴𝟧 𝟟𝟑')  # various digits, a weird minus and a non-breaking space
+'07-85 73'
+
+
+The isdigits() function is used to replace the str.isdigit() function which
+will also return True for all kinds on non-ASCII digits.
+
+>>> three = b'\xe1\xad\x93'.decode('utf-8')  # ᭓  Balinese digit three
+>>> three.isdigit()
+True
+>>> isdigits(three)
+False
+>>> isdigits('3')
+True
+>>> super_two = b'\xc2\xb2'.decode('utf-8')  # ² to the power of two
+>>> super_two.isdigit()
+True
+>>> isdigits(super_two)
+False
+
+
 The get_module_name() function is used in the example WSGI application and
 release scripts to get the number's name. It should not end in a dot (even
 though the docstring subject should).

https://arthurdejong.org/git/python-stdnum/commit/?id=3aeec68735d75a4835122990bef232ed415b07d5

commit 3aeec68735d75a4835122990bef232ed415b07d5
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Thu Apr 4 19:54:52 2019 +0200

    Add Israeli identity number

diff --git a/stdnum/il/__init__.py b/stdnum/il/__init__.py
new file mode 100644
index 0000000..91a474e
--- /dev/null
+++ b/stdnum/il/__init__.py
@@ -0,0 +1,21 @@
+# __init__.py - collection of Israeli numbers
+# coding: utf-8
+#
+# Copyright (C) 2019 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 Israeli numbers."""
diff --git a/stdnum/il/idnr.py b/stdnum/il/idnr.py
new file mode 100644
index 0000000..e638ab7
--- /dev/null
+++ b/stdnum/il/idnr.py
@@ -0,0 +1,83 @@
+# idnr.py - functions for handling Israeli personal numbers
+# coding: utf-8
+#
+# Copyright (C) 2019 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
+
+"""Identity Number (Mispar Zehut, מספר זהות, Israeli identity number).
+
+The identity number (Mispar Zehut, מספר זהות) is issued at birth to Israeli
+citizens. The number consists of nine digits and includes a check digit.
+
+More information:
+
+* https://en.wikipedia.org/wiki/National_identification_number#Israel
+* https://en.wikipedia.org/wiki/Israeli_identity_card
+* https://he.wikipedia.org/wiki/מספר_זהות_(ישראל)
+
+>>> validate('3933742-3')
+'039337423'
+>>> format('39337423')
+'03933742-3'
+>>> validate('3933742-2')  # invalid check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('490154203237518')  # longer than 9 digits
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
+"""
+
+from stdnum import luhn
+from stdnum.exceptions import *
+from stdnum.util import clean
+
+
+def compact(number):
+    """Convert the number to the minimal representation. This strips the
+    number of any valid separators and removes surrounding whitespace."""
+    number = clean(number, ' -').strip()
+    # pad with leading zeroes
+    return (9 - len(number)) * '0' + number
+
+
+def validate(number):
+    """Check if the number provided is a valid ID. This checks the length,
+    formatting and check digit."""
+    number = compact(number)
+    if len(number) > 9:
+        raise InvalidLength()
+    if not number.isdigit() or int(number) <= 0:
+        raise InvalidFormat()
+    luhn.validate(number)
+    return number
+
+
+def is_valid(number):
+    """Check if the number provided is a valid ID. This checks the length,
+    formatting and check digit."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False
+
+
+def format(number):
+    """Reformat the number to the standard presentation format."""
+    number = compact(number)
+    return number[:-1] + '-' + number[-1:]

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

Summary of changes:
 setup.cfg                         |  1 +
 stdnum/ar/cbu.py                  |  4 +--
 stdnum/ar/cuit.py                 |  4 +--
 stdnum/ar/dni.py                  |  4 +--
 stdnum/at/businessid.py           |  9 +++--
 stdnum/at/postleitzahl.py         |  4 +--
 stdnum/at/tin.py                  |  4 +--
 stdnum/at/uid.py                  |  4 +--
 stdnum/at/vnr.py                  |  4 +--
 stdnum/au/abn.py                  |  4 +--
 stdnum/au/acn.py                  |  4 +--
 stdnum/au/tfn.py                  |  4 +--
 stdnum/be/vat.py                  |  4 +--
 stdnum/bg/egn.py                  |  4 +--
 stdnum/bg/pnf.py                  |  4 +--
 stdnum/bg/vat.py                  |  4 +--
 stdnum/br/cnpj.py                 |  4 +--
 stdnum/br/cpf.py                  |  4 +--
 stdnum/ca/bn.py                   |  6 ++--
 stdnum/ca/sin.py                  |  4 +--
 stdnum/casrn.py                   |  4 +--
 stdnum/ch/uid.py                  |  4 +--
 stdnum/cl/rut.py                  |  4 +--
 stdnum/cn/ric.py                  |  6 ++--
 stdnum/co/nit.py                  |  4 +--
 stdnum/cu/ni.py                   |  4 +--
 stdnum/cy/vat.py                  |  4 +--
 stdnum/cz/dic.py                  |  4 +--
 stdnum/cz/rc.py                   |  4 +--
 stdnum/de/idnr.py                 |  4 +--
 stdnum/de/stnr.py                 |  4 +--
 stdnum/de/vat.py                  |  4 +--
 stdnum/dk/cpr.py                  |  4 +--
 stdnum/dk/cvr.py                  |  4 +--
 stdnum/do/cedula.py               |  4 +--
 stdnum/do/ncf.py                  |  6 ++--
 stdnum/do/rnc.py                  |  4 +--
 stdnum/ean.py                     |  4 +--
 stdnum/ec/ci.py                   |  4 +--
 stdnum/ec/ruc.py                  |  3 +-
 stdnum/ee/ik.py                   |  4 +--
 stdnum/ee/kmkr.py                 |  4 +--
 stdnum/ee/registrikood.py         |  4 +--
 stdnum/es/ccc.py                  |  4 +--
 stdnum/es/cif.py                  |  3 +-
 stdnum/es/cups.py                 |  6 ++--
 stdnum/es/dni.py                  |  4 +--
 stdnum/es/nie.py                  |  3 +-
 stdnum/es/nif.py                  |  6 ++--
 stdnum/eu/banknote.py             |  6 ++--
 stdnum/eu/nace.py                 |  4 +--
 stdnum/fi/alv.py                  |  4 +--
 stdnum/fi/associationid.py        |  4 +--
 stdnum/fi/veronumero.py           |  4 +--
 stdnum/figi.py                    |  4 +--
 stdnum/fr/nif.py                  |  4 +--
 stdnum/fr/nir.py                  |  6 ++--
 stdnum/fr/siren.py                |  4 +--
 stdnum/fr/siret.py                |  6 ++--
 stdnum/fr/tva.py                  |  8 ++---
 stdnum/gb/nhs.py                  |  4 +--
 stdnum/gb/sedol.py                |  4 +--
 stdnum/gb/upn.py                  |  4 +--
 stdnum/gb/vat.py                  |  8 ++---
 stdnum/gr/amka.py                 |  4 +--
 stdnum/gr/vat.py                  |  4 +--
 stdnum/hr/oib.py                  |  4 +--
 stdnum/hu/anum.py                 |  4 +--
 stdnum/ie/vat.py                  |  8 ++---
 stdnum/{cu => il}/__init__.py     |  4 +--
 stdnum/{ar/cuit.py => il/idnr.py} | 69 ++++++++++++++++++---------------------
 stdnum/imei.py                    |  4 +--
 stdnum/imo.py                     |  4 +--
 stdnum/imsi.py                    |  4 +--
 stdnum/in_/pan.py                 |  5 ++-
 stdnum/is_/vsk.py                 |  4 +--
 stdnum/isbn.py                    |  4 +--
 stdnum/issn.py                    |  4 +--
 stdnum/it/iva.py                  |  4 +--
 stdnum/lt/asmens.py               |  4 +--
 stdnum/lt/pvm.py                  |  4 +--
 stdnum/lu/tva.py                  |  4 +--
 stdnum/lv/pvn.py                  |  4 +--
 stdnum/md/idno.py                 |  4 +--
 stdnum/meid.py                    |  8 ++---
 stdnum/mt/vat.py                  |  4 +--
 stdnum/mu/nid.py                  |  6 +++-
 stdnum/my/nric.py                 |  4 +--
 stdnum/nl/bsn.py                  |  4 +--
 stdnum/nl/btw.py                  |  4 +--
 stdnum/nl/onderwijsnummer.py      |  3 +-
 stdnum/no/fodselsnummer.py        |  4 +--
 stdnum/no/kontonr.py              |  4 +--
 stdnum/no/orgnr.py                |  4 +--
 stdnum/nz/bankaccount.py          |  4 +--
 stdnum/pe/cui.py                  |  4 +--
 stdnum/pe/ruc.py                  |  4 +--
 stdnum/pl/nip.py                  |  4 +--
 stdnum/pl/pesel.py                |  4 +--
 stdnum/pl/regon.py                |  4 +--
 stdnum/pt/nif.py                  |  4 +--
 stdnum/ro/cf.py                   |  4 +--
 stdnum/ro/cnp.py                  |  4 +--
 stdnum/rs/pib.py                  |  4 +--
 stdnum/ru/inn.py                  |  4 +--
 stdnum/se/orgnr.py                |  4 +--
 stdnum/se/personnummer.py         |  4 +--
 stdnum/se/vat.py                  |  4 +--
 stdnum/si/ddv.py                  |  4 +--
 stdnum/sk/dph.py                  |  4 +--
 stdnum/sm/coe.py                  |  4 +--
 stdnum/tr/tckimlik.py             |  4 +--
 stdnum/us/rtn.py                  |  4 +--
 stdnum/util.py                    | 54 +++++++++++++++++++++++++++++-
 tests/test_cn_ric.doctest         |  4 ---
 tests/test_isbn.doctest           | 14 +++++++-
 tests/test_robustness.doctest     |  4 +--
 tests/test_util.doctest           | 29 ++++++++++++++--
 118 files changed, 373 insertions(+), 282 deletions(-)
 copy stdnum/{cu => il}/__init__.py (90%)
 copy stdnum/{ar/cuit.py => il/idnr.py} (53%)


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