lists.arthurdejong.org
RSS feed

python-stdnum branch master updated. 1.6-14-g8f6fa7d

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

python-stdnum branch master updated. 1.6-14-g8f6fa7d



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  8f6fa7db038a29941b70862386ae4120499637d2 (commit)
       via  fbc92f8400d4565a86b81329053a1302ce21c2f8 (commit)
      from  b8389ebd405f74bdbaa1c09c91b4a816fcd0873b (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=8f6fa7db038a29941b70862386ae4120499637d2

commit 8f6fa7db038a29941b70862386ae4120499637d2
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Mon Aug 28 20:26:38 2017 +0200

    Ensure 100% branch coverage
    
    This ensures that the tests fail if 100% branch coverage is not
    achieved. It also adds some pragma statements for code that cannot be
    covered or is Python version dependent.
    
    Furthermore, the get_module_list() function was removed from stdnum.util
    and more tests were made from stdnum.util and stdnum.numdb. The
    functionality to call format() in a country-specific IBAN implementation
    was also dropped because it was not used.

diff --git a/setup.cfg b/setup.cfg
index 501bdbc..c37a5d4 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -13,6 +13,7 @@ cover-inclusive=true
 cover-erase=true
 cover-html=true
 cover-html-dir=coverage
+cover-min-percentage=100
 
 [build_sphinx]
 all_files  = 1
diff --git a/stdnum/iban.py b/stdnum/iban.py
index a9c56b3..5908a71 100644
--- a/stdnum/iban.py
+++ b/stdnum/iban.py
@@ -129,7 +129,4 @@ def is_valid(number, check_country=True):
 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))
diff --git a/stdnum/ismn.py b/stdnum/ismn.py
index b04c763..d7337f0 100644
--- a/stdnum/ismn.py
+++ b/stdnum/ismn.py
@@ -121,8 +121,8 @@ def split(number):
     6 digits) and a check digit."""
     # clean up number
     number = to_ismn13(compact(number))
-    # rind the correct range and split the number
-    for length, low, high in _ranges:
+    # find the correct range and split the number
+    for length, low, high in _ranges:  # pragma: no branch (all ranges covered)
         if low <= number[4:4 + length] <= high:
             return (number[:3], number[3], number[4:4 + length],
                     number[4 + length:-1], number[-1])
diff --git a/stdnum/meid.py b/stdnum/meid.py
index 3077753..db43866 100644
--- a/stdnum/meid.py
+++ b/stdnum/meid.py
@@ -107,10 +107,11 @@ def compact(number, strip_check_digit=True):
 
 def _bit_length(n):
     """Number of bits necessary to represent the number in binary."""
-    if hasattr(n, 'bit_length'):
+    try:
         return n.bit_length()
-    import math
-    return int(math.log(n, 2)) + 1
+    except AttributeError:  # pragma: no cover (Python 2.6 only)
+        import math
+        return int(math.log(n, 2)) + 1
 
 
 def validate(number, strip_check_digit=True):
diff --git a/stdnum/numdb.py b/stdnum/numdb.py
index ea3c3b9..e3e48e2 100644
--- a/stdnum/numdb.py
+++ b/stdnum/numdb.py
@@ -1,6 +1,6 @@
 # numdb.py - module for handling hierarchically organised numbers
 #
-# Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong
+# Copyright (C) 2010-2017 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
@@ -76,7 +76,7 @@ True
 True
 >>> dbfile.info('633322') == [
 ...     ('6', {'prop1': 'boo'}),
-...     ('333', {'prop2': 'bar', 'prop3': 'baz'}),
+...     ('333', {'prop2': 'bar', 'prop3': 'baz', 'prop4': 'bla'}),
 ...     ('22', {}),
 ... ]
 True
@@ -187,8 +187,7 @@ def read(fp):
     for indent, length, low, high, props in _parse(fp):
         if indent > last_indent:
             # populate the children field of the last indent
-            if stack[last_indent][-1][4] is None:
-                stack[last_indent][-1][4] = []
+            stack[last_indent][-1][4] = []
             stack[indent] = stack[last_indent][-1][4]
         stack[indent].append([length, low, high, props, None])
         last_indent = indent
diff --git a/stdnum/util.py b/stdnum/util.py
index 111f5af..3d4b04f 100644
--- a/stdnum/util.py
+++ b/stdnum/util.py
@@ -120,7 +120,7 @@ def clean(number, deletechars=''):
         number = ''.join(x for x in number)
     except Exception:
         raise InvalidFormat()
-    if sys.version < '3' and isinstance(number, str):  # pragma: no cover 
(Python 2/3 specific code)
+    if sys.version < '3' and isinstance(number, str):  # pragma: no cover 
(Python 2 specific code)
         try:
             number = _clean_chars(number.decode()).encode()
         except UnicodeError:
@@ -128,7 +128,7 @@ def clean(number, deletechars=''):
                 number = _clean_chars(number.decode('utf-8')).encode('utf-8')
             except UnicodeError:
                 pass
-    else:  # pragma: no cover (Python 2/3 specific code)
+    else:  # pragma: no cover (Python 3 specific code)
         number = _clean_chars(number)
     return ''.join(x for x in number if x not in deletechars)
 
@@ -138,8 +138,7 @@ def get_number_modules(base='stdnum'):
     __import__(base)
     module = sys.modules[base]
     for loader, name, is_pkg in pkgutil.walk_packages(
-            module.__path__, module.__name__ + '.',
-            onerror=lambda x: None):
+            module.__path__, module.__name__ + '.'):
         __import__(name)
         module = sys.modules[name]
         if hasattr(module, 'validate'):
@@ -158,14 +157,6 @@ def get_module_description(module):
     return _strip_doctest_re.sub('', doc).strip()
 
 
-def get_module_list():
-    for module in get_number_modules():
-        yield ' * %s: %s' % (
-            module.__name__.replace('stdnum.', ''),
-            get_module_name(module),
-        )
-
-
 def get_cc_module(cc, name):
     """Find the country-specific named module."""
     cc = cc.lower()
diff --git a/tests/numdb-test.dat b/tests/numdb-test.dat
index 2dfad4e..6b25454 100644
--- a/tests/numdb-test.dat
+++ b/tests/numdb-test.dat
@@ -3,6 +3,7 @@
   100-999 prop2="bar"
   200,300-399 prop3="baz"
 6 prop1="boo"
+  333 prop4="bla"
 90-99 prop1="booz"
   00-89 prop2="foo"
   900-999 prop2="fooz"
diff --git a/tests/test_util.doctest b/tests/test_util.doctest
new file mode 100644
index 0000000..28674b7
--- /dev/null
+++ b/tests/test_util.doctest
@@ -0,0 +1,50 @@
+test_util.doctest - more detailed doctests for the stdnum.util package
+
+Copyright (C) 2017 Arthur de Jong
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA
+
+
+This file contains more detailed doctests for the stdnum.util package. It
+tries to test more corner cases and detailed functionality. 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.
+
+>>> from stdnum.util import (
+...     get_number_modules, get_module_name, get_module_description)
+
+
+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).
+
+>>> from stdnum import isbn
+>>> get_module_name(isbn)
+'ISBN (International Standard Book Number)'
+>>> any(get_module_name(mod).endswith('.') for mod in get_number_modules())
+False
+
+
+The get_module_description() function is used in the example WSGI application
+to extract the extended description of the number for presentation purposes.
+The doctests should not be present in the descriptions.
+
+>>> from stdnum import isbn
+>>> get_module_description(isbn).startswith(
+...     'The ISBN is the International Standard Book Number')
+True
+>>> any('>>>' in get_module_description(mod) for mod in get_number_modules())
+False

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

commit fbc92f8400d4565a86b81329053a1302ce21c2f8
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sun Aug 27 23:10:48 2017 +0200

    Add English Unique Pupil Number (UPN)

diff --git a/stdnum/gb/upn.py b/stdnum/gb/upn.py
new file mode 100644
index 0000000..0c65371
--- /dev/null
+++ b/stdnum/gb/upn.py
@@ -0,0 +1,109 @@
+# upn.py - functions for handling English UPNs
+#
+# Copyright (C) 2017 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
+
+"""UPN (English Unique Pupil Number).
+
+The Unique Pupil Number (UPN) is a 13-character code that identifies pupils
+in English state schools and is designed to aid tracking pupil progress
+through the school system.
+
+The number consists of a check letter, a 3-digit LA (Local Authority) number
+for the issuing school, a 4-digit DfE number (School Establishment Number),
+2 digits for the issue year and 3 digits for a serial number. Temporary
+numbers have a 2-digit serial and a letter.
+
+More information:
+
+* https://www.gov.uk/government/publications/unique-pupil-numbers
+
+>>> validate('B801200005001')
+'B801200005001'
+>>> validate('A801200005001')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
+>>> validate('X80120000A001')  # middle part must be numeric
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> validate('E000200005001')  # LA number must be known
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+"""
+
+from stdnum.util import clean
+from stdnum.exceptions import *
+
+
+# The allowed characters in an UPN.
+_alphabet = 'ABCDEFGHJKLMNPQRTUVWXYZ0123456789'
+
+
+# The known values for the LA (Local Authority) number.
+# 
https://www.gov.uk/government/statistics/new-local-authority-codes-january-2011
+_la_numbers = set((
+    201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 301,
+    302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315,
+    316, 317, 318, 319, 320, 330, 331, 332, 333, 334, 335, 336, 340, 341,
+    342, 343, 344, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 370,
+    371, 372, 373, 380, 381, 382, 383, 384, 390, 391, 392, 393, 394, 420,
+    800, 801, 802, 803, 805, 806, 807, 808, 810, 811, 812, 813, 815, 816,
+    821, 822, 823, 825, 826, 830, 831, 835, 836, 837, 840, 841, 845, 846,
+    850, 851, 852, 855, 856, 857, 860, 861, 865, 866, 867, 868, 869, 870,
+    871, 872, 873, 874, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885,
+    886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 908, 909, 916,
+    919, 921, 925, 926, 928, 929, 931, 933, 935, 936, 937, 938))
+
+
+def compact(number):
+    """Convert the number to the minimal representation. This strips the
+    number of any valid separators and removes surrounding whitespace."""
+    return clean(number, ' ').upper().strip()
+
+
+def calc_check_digit(number):
+    """Calculate the check digit for the number."""
+    check = sum(i * _alphabet.index(n)
+                for i, n in enumerate(number[-12:], 2)) % 23
+    return _alphabet[check]
+
+
+def validate(number):
+    """Check to see if the number provided is a valid UPN. This checks
+    length, formatting and check digits."""
+    number = compact(number)
+    if len(number) != 13:
+        raise InvalidLength()
+    if not number[1:-1].isdigit() or number[-1] not in _alphabet:
+        raise InvalidFormat()
+    if int(number[1:4]) not in _la_numbers:
+        raise InvalidComponent()
+    if calc_check_digit(number[1:]) != number[0]:
+        raise InvalidChecksum()
+    return number
+
+
+def is_valid(number):
+    """Check to see if the number provided is a valid UPN. This checks
+    length, formatting and check digits."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False

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

Summary of changes:
 setup.cfg               |   1 +
 stdnum/gb/upn.py        | 109 ++++++++++++++++++++++++++++++++++++++++++++++++
 stdnum/iban.py          |   3 --
 stdnum/ismn.py          |   4 +-
 stdnum/meid.py          |   7 ++--
 stdnum/numdb.py         |   7 ++--
 stdnum/util.py          |  15 ++-----
 tests/numdb-test.dat    |   1 +
 tests/test_util.doctest |  50 ++++++++++++++++++++++
 9 files changed, 173 insertions(+), 24 deletions(-)
 create mode 100644 stdnum/gb/upn.py
 create mode 100644 tests/test_util.doctest


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/