Metadata-Version: 2.5
Name: isbnid
Version: 0.5.2
Summary: Python ISBN ids
Project-URL: Homepage, https://gitlab.com/nekobcn/isbnid
Author-email: ISBNid GitHub <pdfnorm@gmx.com>
License-Expression: LGPL-3.0-only
License-File: LICENSE
Keywords: ISBN
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Education
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Indexing
Requires-Python: >=3.10
Description-Content-Type: text/markdown

isbnid
======

Python ISBN identifier library

isbnid is a simple library to handle ISBN identification numbers. isbnid will store, check and convert ISBNs in ISBN10, and ISBN13 formats and it will transform between them and output in URN form.

isbnid can also output ISBN numbers with the correct hyphens corresponding to the actual issuance authorities. The information is retrieved from <https://www.isbn-international.org/>. ISBN numbers have a complex internal structure which roughly represents the country, the language and the publisher. See also <https://en.wikipedia.org/wiki/ISBN>.

Install
-------

isbnid has no dependencies. The simplest way to install it in python is to execute. 

    pip install isbnid

It can also be installed from source as

    pip install .

isbnid requires Python 3.10 or later.

Usage
-----

The class isbn.ISBN constructor takes a string containing the ISBN. The string can be inputed in ISBN10, ISBN13 with or without hyphens. It will raise an exception in case it is not formated correctly or the check digit is not valid.

    >>> import isbn
    >>> isbnid = isbn.ISBN("9780553109535")
    >>> isbnid.isbn10()
    '0553109537'
    >>> isbnid.isbn13()
    '9780553109535'
    >>> isbnid.urn()
    'URN:ISBN:9780553109535'
    >>> isbnid.hyphen()
    '978-0-553-10953-5'
    >>> isbnid = isbn.ISBN("978-0-553-10953-0")
    isbn.isbn.ISBNError: 'Invalid ISBN check digit: 978-0-553-10953-0'

Command line
------------

isbnid installs three command line tools. Each takes one argument, an ISBN in either format with or without hyphens, and prints the result to stdout.

    isbn10 ISBN     print the ISBN-10 form
    isbn13 ISBN     print the ISBN-13 form
    isbnmask ISBN   print the hyphenated ISBN-13 form

Examples:

    $ isbn10 9780553109535
    0553109537
    $ isbn13 0553109537
    9780553109535
    $ isbnmask 978-0-553-10953-5
    978-0-553-10953-5

The same tools run straight from the package, with no scripts installed:

    $ python3 -m isbn isbn13 0553109537
    9780553109535

The tool name comes first, then the ISBN; exit codes are the tool's own.

Exit codes:

    0   success, result printed to stdout
    1   invalid ISBN (bad format or check digit)
    2   usage error
    3   no ISBN-10 equivalent (979 prefix), isbn10 only
    4   unknown agency range, isbnmask only

ISBN structure and hyphenation
------------------------------

An ISBN is made of five elements: the prefix element (978 or 979 for ISBN-13), the registration group (roughly a country or language area), the registrant (publisher) number, the publication number and a check digit.

The positions of the hyphens are not fixed. They depend on how long the ranges assigned to each agency are, so producing the correct hyphenation requires knowing which range an ISBN belongs to. isbnid bundles that table and looks up the group and registrant lengths for every number it hyphenates. ISBNs with the 979 prefix have no ISBN-10 equivalent.

Range data
----------

The range table is generated from the "RangeMessage" XML file published by ISBN International at <https://www.isbn-international.org/export_rangemessage.xml>. The bundled snapshot in data/RangeMessage.xml records the date it was downloaded; the MessageID serial next to it is stamped per request by the server and means nothing on its own. isbn/ranges.py repeats the fetch date as SNAPSHOT_DATE in ISO 8601 form, so a bug report about hyphenation can name the snapshot the library was built against.

Background on the ISBN structure can be found at <https://en.wikipedia.org/wiki/ISBN> and <https://www.isbn-international.org/>.

To refresh the range data, run the update script from the repository root:

    $ python3 data/upd_ranges.py

It downloads the latest RangeMessage.xml, updates data/RangeMessage.xml and isbn/ranges.py when the ranges changed, and verifies with the test suite. The manual equivalent is:

    $ curl -o data/RangeMessage.xml https://www.isbn-international.org/export_rangemessage.xml
    $ cd data && python3 gen_ranges.py > ../isbn/ranges.py

Note that isbn/ranges.py is auto-generated. Do not edit it by hand; change the generator or the source XML instead. The lookup logic in isbn/hyphen.py is hand-written and never regenerated; the parse core and the emitter live together in data/gen_ranges.py.

Development decisions
---------------------

- No runtime dependencies, standard library only.
- Packaging uses PEP 621 metadata with hatchling; the version is resolved dynamically from isbn/__init__.py so there is a single source of truth.
- The wheel ships isbn/ only, and the sdist excludes the repository-only data/, tests/ and notes, so releases stay small.
- The license is LGPL-3.0-only (previously GPL-3.0). The LICENSE file is the official GNU document: the LGPL v3 short form plus the full GNU GPL appendix.
- The range table is generated from the official XML instead of being maintained by hand, so it can be refreshed whenever ISBN International publishes a new RangeMessage.
- Only the tables are generated: the lookup logic stays hand-written in isbn/hyphen.py, so a logic change is a small reviewable edit and a data refresh rewrites just isbn/ranges.py. The generated file carries no serial or date, which is what makes the refresh's whole-file comparison stable.
- Range changes, not the download timestamp, decide whether a refresh rewrites ranges.py: the server stamps every request with a fresh serial and date, and the SNAPSHOT_DATE line in ranges.py is stripped before the comparison.
- The whole tree, generated code and dev tools included, is annotated and passes mypy --strict.

Testing
-------

Run the test suite from the repository root:

    $ python3 -m unittest discover tests

A single module can be run with:

    $ python3 -m unittest tests.test_cli

Type checking and linting use:

    $ mypy --strict isbn tests data
    $ ruff check .

The suite covers validation, format conversion, hyphenation, URN/DOI output, the command line exit codes, the RangeMessage parse core and the shape of the shipped range tables. Test vectors include the first and last ISBN of several agency ranges, so the range lookup is exercised at its boundaries.

Project layout
--------------

    isbn/__init__.py     package init, version
    isbn/isbn.py         ISBN class (validation, conversion, urn, doi)
    isbn/error.py        ISBNError, ISBNRangeError
    isbn/cli.py          isbn10 / isbn13 / isbnmask command line tools
    isbn/__main__.py     python3 -m isbn entry point
    isbn/hyphen.py       hyphenation lookup logic (hand-written)
    isbn/ranges.py       generated agency range tables and their fetch date
    data/RangeMessage.xml  source XML from ISBN International
    data/upd_ranges.py   update RangeMessage.xml and regenerate ranges.py
    data/gen_ranges.py   parse core and generator: RangeMessage.xml -> ranges.py
    tests/__init__.py   test package marker
    tests/fixtures.py   shared ISBN/CLI test data
    tests/test_isbn.py   ISBN class tests
    tests/test_cli.py    command-line tests
    tests/test_gen_ranges.py  parse core and shipped range table tests
  
