lists.arthurdejong.org
RSS feed

python-stdnum branch master updated. 1.4-8-gbe24790

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

python-stdnum branch master updated. 1.4-8-gbe24790



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  be24790818d1c8c32a055613c22fd98556455800 (commit)
       via  251093268dfe1be05ecfc6c7c53a08577e77e930 (commit)
       via  d8cca82e40e7e8fee6de3f20b3b6284d9a925e66 (commit)
      from  1622873beb762ff31ce45b0356edb7c7ab834dd8 (commit)

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

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

commit be24790818d1c8c32a055613c22fd98556455800
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Thu Sep 8 18:56:06 2016 +0200

    Add Spanish IBAN number module
    
    This validates the country-specific part of the IBAN.

diff --git a/stdnum/es/iban.py b/stdnum/es/iban.py
new file mode 100644
index 0000000..5d25774
--- /dev/null
+++ b/stdnum/es/iban.py
@@ -0,0 +1,79 @@
+# iban.py - functions for handling Spanish IBANs
+# coding: utf-8
+#
+# Copyright (C) 2016 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
+
+"""Spanish IBAN (International Bank Account Number).
+
+The IBAN is used to identify bank accounts across national borders. The
+Spanish IBAN is built up of the IBAN prefix (ES) and check digits, followed
+by the 20 digit CCC (Código Cuenta Corriente).
+
+>>> validate('ES77 1234-1234-16 1234567890')
+'ES7712341234161234567890'
+>>> to_ccc('ES77 1234-1234-16 1234567890')
+'12341234161234567890'
+>>> format('ES771234-1234-16 1234567890')
+'ES77 1234 1234 1612 3456 7890'
+>>> validate('GR1601101050000010547023795')  # different country
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> validate('ES12 1234-1234-16 1234567890')  # invalid IBAN check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('ES15 1234-1234-17 1234567890')  # invalid CCC check digit
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+"""
+
+from stdnum import iban
+from stdnum.es import ccc
+from stdnum.exceptions import *
+
+
+__all__ = ['compact', 'format', 'to_ccc', 'validate', 'is_valid']
+
+
+compact = iban.compact
+format = iban.format
+
+
+def to_ccc(number):
+    """Return the CCC (Código Cuenta Corriente) part of the number."""
+    number = compact(number)
+    if not number.startswith('ES'):
+        raise InvalidComponent()
+    return number[4:]
+
+
+def validate(number):
+    """Checks to see if the number provided is a valid Spanish IBAN."""
+    number = iban.validate(number, check_country=False)
+    ccc.validate(to_ccc(number))
+    return number
+
+
+def is_valid(number):
+    """Checks to see if the number provided is a valid Spanish IBAN."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False
diff --git a/tests/test_iban.doctest b/tests/test_iban.doctest
index b2d91b8..10b66e8 100644
--- a/tests/test_iban.doctest
+++ b/tests/test_iban.doctest
@@ -199,3 +199,15 @@ or have an unknown country code:
 ... '''
 >>> [ x for x in numbers.splitlines() if x and iban.is_valid(x) ]
 []
+
+
+These are mostly for the normal IBAN check but they are not correct according
+to the country-specific validation.
+
+>>> numbers = '''
+... ES2121000418450200051331
+... '''
+>>> [ x for x in numbers.splitlines() if x and not iban.is_valid(x, 
check_country=False) ]
+[]
+>>> [ x for x in numbers.splitlines() if x and iban.is_valid(x) ]
+[]

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

commit 251093268dfe1be05ecfc6c7c53a08577e77e930
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Thu Sep 8 18:55:03 2016 +0200

    Validate country-specific part of IBAN
    
    This adds the possible of validating the country-specific part of the
    IBAN. If the country has an IBAN module, checking is also delegated to
    that module.

diff --git a/stdnum/iban.py b/stdnum/iban.py
index e0d4898..18a9faa 100644
--- a/stdnum/iban.py
+++ b/stdnum/iban.py
@@ -25,7 +25,7 @@ for the ISO 7064 Mod 97, 10 checksum. Each country uses its 
own format
 for the remainder of the number.
 
 Some countries may also use checksum algorithms within their number but
-this is currently not checked by this number.
+this is only checked for a few countries.
 
 More information:
 
@@ -49,7 +49,7 @@ import re
 from stdnum import numdb
 from stdnum.exceptions import *
 from stdnum.iso7064 import mod_97_10
-from stdnum.util import clean
+from stdnum.util import clean, get_cc_module
 
 
 # our open copy of the IBAN database
@@ -61,6 +61,9 @@ _alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
 # regular expression to check IBAN structure
 _struct_re = re.compile(r'([1-9][0-9]*)!([nac])')
 
+# cache of country codes to modules
+_country_modules = dict()
+
 
 def compact(number):
     """Convert the iban number to the minimal representation. This strips the
@@ -102,8 +105,18 @@ def _struct_to_re(structure):
     return re.compile('^%s$' % _struct_re.sub(conv, structure))
 
 
-def validate(number):
-    """Checks to see if the number provided is a valid IBAN."""
+def _get_cc_module(cc):
+    """Get the VAT number module based on the country code."""
+    # Greece uses a "wrong" country code
+    cc = cc.lower()
+    if cc not in _country_modules:
+        _country_modules[cc] = get_cc_module(cc, 'iban')
+    return _country_modules[cc]
+
+
+def validate(number, check_country=True):
+    """Checks to see if the number provided is a valid IBAN. The country-
+    specific check can be disabled with the check_country argument."""
     number = compact(number)
     # ensure that checksum is valid
     mod_97_10.validate(_to_base10(number))
@@ -113,14 +126,19 @@ def validate(number):
     bban = number[4:]
     if not _struct_to_re(info[0][1].get('bban', '')).match(bban):
         raise InvalidFormat()
+    # check the country-specific module if it exists
+    if check_country:
+        module = _get_cc_module(number[:2])
+        if module:
+            module.validate(number)
     # return the compact representation
     return number
 
 
-def is_valid(number):
+def is_valid(number, check_country=True):
     """Checks to see if the number provided is a valid IBAN."""
     try:
-        return bool(validate(number))
+        return bool(validate(number, check_country=check_country))
     except ValidationError:
         return False
 
@@ -128,4 +146,7 @@ def is_valid(number):
 def format(number, separator=' '):
     """Reformat the passed number to the space-separated format."""
     number = compact(number)
+    module = _get_cc_module(number[:2])
+    if module and hasattr(module, 'format') and module.format != format:
+        return module.format(number, separator=separator)
     return separator.join(number[i:i + 4] for i in range(0, len(number), 4))

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

commit d8cca82e40e7e8fee6de3f20b3b6284d9a925e66
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Tue Sep 6 18:16:21 2016 +0200

    Introduce get_cc_module() utility function
    
    This changes the get_vat_module() function to a more generic
    get_cc_module() function so that it can also be used for other things
    like IBAN checking.

diff --git a/stdnum/eu/vat.py b/stdnum/eu/vat.py
index 88d97ef..3604aba 100644
--- a/stdnum/eu/vat.py
+++ b/stdnum/eu/vat.py
@@ -1,7 +1,7 @@
 # vat.py - functions for handling European VAT numbers
 # coding: utf-8
 #
-# Copyright (C) 2012-2015 Arthur de Jong
+# Copyright (C) 2012-2016 Arthur de Jong
 # Copyright (C) 2015 Lionel Elie Mamane
 #
 # This library is free software; you can redistribute it and/or
@@ -40,7 +40,7 @@ that country.
 """
 
 from stdnum.exceptions import *
-from stdnum.util import clean, get_vat_module, get_soap_client
+from stdnum.util import clean, get_cc_module, get_soap_client
 
 
 country_codes = set([
@@ -69,7 +69,7 @@ def _get_cc_module(cc):
     if cc not in country_codes:
         return
     if cc not in _country_modules:
-        _country_modules[cc] = get_vat_module(cc)
+        _country_modules[cc] = get_cc_module(cc, 'vat')
     return _country_modules[cc]
 
 
diff --git a/stdnum/util.py b/stdnum/util.py
index 21307ba..111f5af 100644
--- a/stdnum/util.py
+++ b/stdnum/util.py
@@ -24,9 +24,9 @@ This module is meant for internal use by stdnum modules and 
is not
 guaranteed to remain stable and as such not part of the public API of
 stdnum.
 
->>> get_vat_module('nl').__name__
+>>> get_cc_module('nl', 'vat').__name__
 'stdnum.nl.btw'
->>> get_vat_module('is').__name__
+>>> get_cc_module('is', 'vat').__name__
 'stdnum.is_.vsk'
 """
 
@@ -103,13 +103,6 @@ _char_map = dict(_mk_char_map({
 }))
 
 
-# mapping of country codes to internally used names
-# used in the get_vat_module() function
-_cc_translations = {
-    'el': 'gr', 'is': 'is_',
-}
-
-
 def _clean_chars(number):
     """Replace various Unicode characters with their ASCII counterpart."""
     return ''.join(_char_map.get(x, x) for x in number)
@@ -173,11 +166,17 @@ def get_module_list():
         )
 
 
-def get_vat_module(cc):
-    """Find the VAT number module based on the country code."""
+def get_cc_module(cc, name):
+    """Find the country-specific named module."""
     cc = cc.lower()
-    cc = _cc_translations.get(cc, cc)
-    return __import__('stdnum.%s' % cc, globals(), locals(), ['vat']).vat
+    # add suffix for python reserved words
+    if cc in ('in', 'is', 'if'):
+        cc += '_'
+    try:
+        mod = __import__('stdnum.%s' % cc, globals(), locals(), [name])
+        return getattr(mod, name, None)
+    except ImportError:
+        return
 
 
 def get_soap_client(wsdlurl):  # pragma: no cover (no tests for this function)

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

Summary of changes:
 stdnum/es/iban.py       | 79 +++++++++++++++++++++++++++++++++++++++++++++++++
 stdnum/eu/vat.py        |  6 ++--
 stdnum/iban.py          | 33 +++++++++++++++++----
 stdnum/util.py          | 25 ++++++++--------
 tests/test_iban.doctest | 12 ++++++++
 5 files changed, 133 insertions(+), 22 deletions(-)
 create mode 100644 stdnum/es/iban.py


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/