From 783a828bb05ef1c946e5517f330a9ba7c664e107 Mon Sep 17 00:00:00 2001 From: David Peckham Date: Fri, 2 Feb 2024 22:22:12 -0500 Subject: [PATCH 1/2] use mkdocs to build docs --- {docs => docs.sphinx}/Makefile | 0 {docs => docs.sphinx}/requirements.txt | 0 {docs => docs.sphinx}/source/api.rst | 0 {docs => docs.sphinx}/source/changelog.rst | 0 {docs => docs.sphinx}/source/conf.py | 0 {docs => docs.sphinx}/source/index.rst | 0 docs/.snippets/abbrs.txt | 1 + docs/.snippets/links.txt | 6 + docs/api.md | 6 + docs/index.md | 72 ++++++++ docs/vpic/about.md | 6 + hatch.toml | 44 +++++ mkdocs.yml | 164 ++++++++++++++++++ pyproject.toml | 2 +- src/vin/__init__.py | 112 ++++++++---- .../vin/scripts}/get-manufacturers.py | 0 16 files changed, 376 insertions(+), 37 deletions(-) rename {docs => docs.sphinx}/Makefile (100%) rename {docs => docs.sphinx}/requirements.txt (100%) rename {docs => docs.sphinx}/source/api.rst (100%) rename {docs => docs.sphinx}/source/changelog.rst (100%) rename {docs => docs.sphinx}/source/conf.py (100%) rename {docs => docs.sphinx}/source/index.rst (100%) create mode 100644 docs/.snippets/abbrs.txt create mode 100644 docs/.snippets/links.txt create mode 100644 docs/api.md create mode 100644 docs/index.md create mode 100644 docs/vpic/about.md create mode 100644 mkdocs.yml rename {scripts => src/vin/scripts}/get-manufacturers.py (100%) diff --git a/docs/Makefile b/docs.sphinx/Makefile similarity index 100% rename from docs/Makefile rename to docs.sphinx/Makefile diff --git a/docs/requirements.txt b/docs.sphinx/requirements.txt similarity index 100% rename from docs/requirements.txt rename to docs.sphinx/requirements.txt diff --git a/docs/source/api.rst b/docs.sphinx/source/api.rst similarity index 100% rename from docs/source/api.rst rename to docs.sphinx/source/api.rst diff --git a/docs/source/changelog.rst b/docs.sphinx/source/changelog.rst similarity index 100% rename from docs/source/changelog.rst rename to docs.sphinx/source/changelog.rst diff --git a/docs/source/conf.py b/docs.sphinx/source/conf.py similarity index 100% rename from docs/source/conf.py rename to docs.sphinx/source/conf.py diff --git a/docs/source/index.rst b/docs.sphinx/source/index.rst similarity index 100% rename from docs/source/index.rst rename to docs.sphinx/source/index.rst diff --git a/docs/.snippets/abbrs.txt b/docs/.snippets/abbrs.txt new file mode 100644 index 0000000..2e3bb9e --- /dev/null +++ b/docs/.snippets/abbrs.txt @@ -0,0 +1 @@ +*[PyPI]: Python Package Index diff --git a/docs/.snippets/links.txt b/docs/.snippets/links.txt new file mode 100644 index 0000000..e0d4e1e --- /dev/null +++ b/docs/.snippets/links.txt @@ -0,0 +1,6 @@ +[PEP 440 version specifiers]: https://peps.python.org/pep-0440/#version-specifiers +[PEP 508]: https://peps.python.org/pep-0508/ +[PEP 517]: https://peps.python.org/pep-0517/ +[PEP 639]: https://peps.python.org/pep-0639/ +[PEP 660]: https://peps.python.org/pep-0660/ +[project metadata standard]: https://packaging.python.org/en/latest/specifications/pyproject-toml/#declaring-project-metadata-the-project-table diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..60ee907 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,6 @@ +# VPIC API + +::: vin.VIN + options: + members_order: source + filters: ['!_vin', '!__repr__', '!__str__'] diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..5c35f23 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,72 @@ +# VIN + +[![PyPI - Version](https://img.shields.io/pypi/v/vin.svg)](https://pypi.org/project/vin) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/vin.svg)](https://pypi.org/project/vin) + +----- + +A ``VIN`` is a *unique 17-character Vehicle Identification Number*. + +* Assigned by vehicle manufacturers +* Uniquely identifies vehicles manufactured for sale or use in the United States since 1980 +* Governed by the U.S. National Highway Traffic Safety Administration (NHTSA) + +The structure of the VIN is: + + model year + | + WMI check digit | plant + |-----| | | | |--- serial ----| + Position 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + |-----------| |---------------------| + VDS VIS + +The World Manufacturer Identifier (WMI) holds the country, region, and +name of the vehicle manufacturer. Mass-market manufacturers are assigned +a three-character WMI. Specialized manufacturers are assigned a six- +character WMI (positions 1-3 and 12-14) + +The Vehicle Description Section (VDS) is defined by manufacturers to +identify the vehicle make, model, body type, engine type, restraints, +and the weight class (for trucks and multi-purpose vehicles). + +The Vehicle Identification Section (VIS) identifies the model year, +plant where the vehicle was made, and the vehicle's serial number. + +Use :class:`VIN`-object by calling the default constructor with the +17-character VIN string. To encode the VIN, convert it to a string: + + >>> vin = VIN("4T1BE46K19U856421") + >>> str(vin) + '4T1BE46K19U856421' + +For more information, see the [specification](https://www.ecfr.gov/current/title-49/subtitle-B/chapter-V/part-565). + +Installation +------------ + +Use ``pip`` to install the library: + + $ pip install vin + +Basic Usage +----------- + +Create a new ``VIN`` object from a 17-character string: + + >>> from vin import VIN + >>> VIN('4T1BE46K19U856421') + VIN(4T1BE46K19U856421) + +## API + +See [API](api.md) + +## Vehicle Information + +Vehicle information is provided by the U.S. National Highway Traffic Safety Administration (NHTSA) Product Information Catalog and Vehicle Listing (vPIC). See [About VPIC](vpic/about.md) for more information. + +## License + +VIN is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. + diff --git a/docs/vpic/about.md b/docs/vpic/about.md new file mode 100644 index 0000000..5187614 --- /dev/null +++ b/docs/vpic/about.md @@ -0,0 +1,6 @@ +# About VPIC + +----- + + +[Product Information Catalog and Vehicle Listing](https://vpic.nhtsa.dot.gov) \ No newline at end of file diff --git a/hatch.toml b/hatch.toml index 4c7bac0..0527c5d 100644 --- a/hatch.toml +++ b/hatch.toml @@ -26,3 +26,47 @@ fmt = [ "black {args:.}", "ruff --fix {args:.}", ] + +[envs.docs] +dependencies = [ + "mkdocs~=1.5.3", + "mkdocs-material~=9.5.1", + # Plugins + "mkdocs-minify-plugin~=0.7.1", + "mkdocs-git-revision-date-localized-plugin~=1.2.1", + "mkdocs-git-committers-plugin-2~=2.2.2", + "mkdocstrings-python~=1.8.0", + "mkdocs-redirects~=1.2.1", + "mkdocs-glightbox~=0.3.5", + "mike~=2.0.0", + # Extensions + "mkdocs-click~=0.8.1", + "pymdown-extensions~=10.5.0", + # Necessary for syntax highlighting in code blocks + "pygments~=2.17.2", + # Validation + # https://github.com/linkchecker/linkchecker/pull/669#issuecomment-1267236287 + "linkchecker @ git+https://github.com/linkchecker/linkchecker.git@d9265bb71c2054bf57b8c5734a4825d62505c779", +] +# pre-install-commands = [ +# "python scripts/install_mkdocs_material_insiders.py", +# ] +# [envs.docs.overrides] +# env.GH_TOKEN_MKDOCS_MATERIAL_INSIDERS.env-vars = [ +# { key = "MKDOCS_CONFIG", value = "mkdocs.insiders.yml" }, +# { key = "MKDOCS_CONFIG", value = "mkdocs.yml", if = [""] }, +# { key = "MKDOCS_IMAGE_PROCESSING", value = "true" }, +# ] +# [envs.docs.env-vars] +# SOURCE_DATE_EPOCH = "1580601600" +# PYTHONUNBUFFERED = "1" +# MKDOCS_CONFIG = "mkdocs.yml" +[envs.docs.scripts] +build = "mkdocs build --clean --strict" +serve = "mkdocs serve --dev-addr localhost:8000" +validate = "linkchecker --config .linkcheckerrc site" +# https://github.com/linkchecker/linkchecker/issues/678 +build-check = [ + "build --no-directory-urls", + "validate", +] diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..cfa3086 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,164 @@ +site_name: VIN +site_description: A Python library for validating and using Vehicle Identification Numbers +site_author: David Peckham +site_url: https://vin.readthedocs.io +repo_name: davidpeckham/vin +repo_url: https://github.com/davidpeckham/vin +# edit_uri: blob/master/docs +copyright: 'Copyright © David Peckham 2024' + +docs_dir: docs +site_dir: site +theme: + name: material + # custom_dir: docs/.overrides + language: en + # favicon: assets/images/logo.svg + # icon: + # repo: fontawesome/brands/github-alt + # logo: material/egg + # font: + # text: Roboto + # code: Roboto Mono + # palette: + # - media: "(prefers-color-scheme: dark)" + # scheme: slate + # primary: indigo + # accent: indigo + # toggle: + # icon: material/weather-night + # name: Switch to light mode + # - media: "(prefers-color-scheme: light)" + # scheme: default + # primary: indigo + # accent: indigo + # toggle: + # icon: material/weather-sunny + # name: Switch to dark mode + features: + - content.action.edit + - content.code.copy + - content.tabs.link + - content.tooltips + - navigation.expand + - navigation.footer + - navigation.instant + - navigation.sections + - navigation.tabs + - navigation.tabs.sticky + +nav: + - Home: + - About: index.md + - API: api.md + - VPIC: + - About: vpic/about.md + +watch: +- src/vin + +# hooks: +# - docs/.hooks/plugin_register.py + +plugins: + # Built-in + search: {} + # Extra + glightbox: {} + minify: + minify_html: true + git-revision-date-localized: + type: date + strict: false + mkdocstrings: + default_handler: python + handlers: + python: + paths: [src] + options: + show_root_heading: true + show_root_full_path: false + show_if_no_docstring: true + show_signature_annotations: true + show_bases: false + +markdown_extensions: + - admonition + - pymdownx.details + - pymdownx.superfences + +# markdown_extensions: +# # Built-in +# - markdown.extensions.abbr: +# - markdown.extensions.admonition: +# - markdown.extensions.attr_list: +# - markdown.extensions.footnotes: +# - markdown.extensions.md_in_html: +# - markdown.extensions.meta: +# - markdown.extensions.tables: +# - markdown.extensions.toc: +# permalink: true +# # Extra +# - mkdocs-click: +# - pymdownx.arithmatex: +# - pymdownx.betterem: +# smart_enable: all +# - pymdownx.caret: +# - pymdownx.critic: +# - pymdownx.details: +# - pymdownx.emoji: +# # https://github.com/twitter/twemoji +# # https://raw.githubusercontent.com/facelessuser/pymdown-extensions/master/pymdownx/twemoji_db.py +# emoji_index: !!python/name:material.extensions.emoji.twemoji +# emoji_generator: !!python/name:material.extensions.emoji.to_svg +# - pymdownx.highlight: +# guess_lang: false +# linenums_style: pymdownx-inline +# use_pygments: true +# - pymdownx.inlinehilite: +# - pymdownx.keys: +# - pymdownx.magiclink: +# repo_url_shortener: true +# repo_url_shorthand: true +# social_url_shortener: true +# social_url_shorthand: true +# normalize_issue_symbols: true +# provider: github +# user: pypa +# repo: hatch +# - pymdownx.mark: +# - pymdownx.progressbar: +# - pymdownx.saneheaders: +# - pymdownx.smartsymbols: +# - pymdownx.snippets: +# check_paths: true +# base_path: +# - docs/.snippets +# auto_append: +# - links.txt +# - abbrs.txt +# - pymdownx.superfences: +# - pymdownx.tabbed: +# alternate_style: true +# slugify: !!python/object/apply:pymdownx.slugs.slugify +# kwds: +# case: lower +# - pymdownx.tasklist: +# custom_checkbox: true +# - pymdownx.tilde: + +# extra: +# version: +# provider: mike +# social: +# - icon: fontawesome/brands/github-alt +# link: https://github.com/ofek +# - icon: fontawesome/solid/blog +# link: https://ofek.dev/words/ +# - icon: fontawesome/brands/twitter +# link: https://twitter.com/Ofekmeister +# - icon: fontawesome/brands/linkedin +# link: https://www.linkedin.com/in/ofeklev/ +# extra_css: +# - assets/css/custom.css +# - https://cdn.jsdelivr.net/npm/firacode@6.2.0/distr/fira_code.css diff --git a/pyproject.toml b/pyproject.toml index 8a5bea9..65af435 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -121,4 +121,4 @@ content-type = "text/x-rst" [[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] path = "README.rst" -start-after = ".. teaser-begin" \ No newline at end of file +start-after = ".. teaser-begin" diff --git a/src/vin/__init__.py b/src/vin/__init__.py index 1cf88f2..1999d33 100644 --- a/src/vin/__init__.py +++ b/src/vin/__init__.py @@ -2,7 +2,9 @@ # # SPDX-License-Identifier: MIT -"""A Vehicle Identification Number (VIN)""" +"""A Vehicle Identification Number (VIN). + +""" # ruff: noqa: TRY003, EM101, EM102 @@ -20,24 +22,23 @@ WMI = json.loads(files("vin").joinpath("wmi.json").read_text(encoding="UTF-8")) +"""Maps WMI to manufacturer name""" class VIN: """ - The :class:`VIN` object is a unique 17-character Vehicle Identification Number. + The `VIN` object is a unique 17-character Vehicle Identification Number. Manufacturers assign the VIN, which uniquely identifies vehicles manufactured since 1980 for sale or use in the United States. - .. code-block:: text - - model year - | - WMI check digit | plant - |-----| | | | |--- serial ----| - Position 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 - |-----------| |---------------------| - VDS VIS + model year + | + WMI check digit | plant + |-----| | | | |--- serial ----| + Position 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + |-----------| |---------------------| + VDS VIS The World Manufacturer Identifier (WMI) holds the country, region, and name of the vehicle manufacturer. Mass-market manufacturers are @@ -51,8 +52,8 @@ class VIN: The Vehicle Identification Section (VIS) identifies the model year, plant where the vehicle was made, and the vehicle's serial number. - Use :class:`VIN`-object by calling the default constructor with the - 17-character VIN string. To encode the VIN, convert it to a string: + Use `VIN` by calling the default constructor with the 17-character + VIN string. To encode the VIN, convert it to a string: >>> vin = VIN("4T1BE46K19U856421") >>> str(vin) @@ -64,22 +65,49 @@ class VIN: """ - def __init__(self, vin: str) -> None: - """create a VIN""" + def __init__(self, vin: str, fix_check_digit: bool = False) -> None: + """Initialize a VIN. + + Args: + vin: The 17-digit Vehicle Identification Number. + fix_check_digit: If True, fix an incorrect check digit + instead of raising a ValueError. + + Raises: + TypeError: `vin` is not a string. + ValueError: `vin` is not 17 characters + ValueError: `vin` has characters that aren't allowed in a VIN + ValueError: `vin` check digit isn't correct + + """ if not isinstance(vin, str): raise TypeError("VIN must be a string") if len(vin) != VIN_LENGTH: raise ValueError(f"VIN must be exactly {VIN_LENGTH} characters long") if not all(c in VIN_CHARACTERS for c in vin): raise ValueError(f"VIN must have only these characters {VIN_CHARACTERS}") - if vin[8:9] != self.calculate_check_digit(vin): - raise ValueError("VIN check digit is incorrect") - self.vin: str = vin + + check_digit = self.calculate_check_digit(vin) + if vin[8:9] != check_digit: + if fix_check_digit: + vin = f"{vin[:8]}{check_digit}{vin[9:]}" + else: + raise ValueError("VIN check digit is incorrect") + + self._vin: str = vin return @classmethod def calculate_check_digit(cls, vin: str) -> str: - """Calculate and return the VIN check digit""" + """Calculate and return the VIN check digit. + + Args: + vin: The 17-digit Vehicle Identification Number. + + Returns: + The calculated check digit character. + + """ total = 0 for n in range(VIN_LENGTH): if n == VIN_CHECK_DIGIT_POSITION: @@ -95,18 +123,18 @@ def wmi(self) -> str: Mass-market manufacturers are assigned a three-character WMI. For example, some Honda cars have WMI 5FN: - .. code-block:: text - - 5FNYF5H59HB011946 - ^^^ + 5FNYF5H59HB011946 + ^^^ Specialized manufacturers are assigned six-character WMI. For example, Koenigsegg cars have WMI YT9007: - .. code-block:: text + YT9NN1U14KA007175 + ^^^ ^^^ - YT9NN1U14KA007175 - ^^^ ^^^ + Returns: + The 3-character WMI for a mass-market manufacturer, or 6-character + WMI for a specialized manufacturer. Examples: @@ -115,12 +143,15 @@ def wmi(self) -> str: >>> vin("YT9NN1U14KA007175").wmi YT9007 """ - return f"{self.vin[:3]}{self.vin[11:14]}" if self.vin[2] == "9" else self.vin[:3] + return f"{self._vin[:3]}{self._vin[11:14]}" if self._vin[2] == "9" else self._vin[:3] @property def manufacturer(self) -> str: """The name of the vehicle manufacturer. + Returns: + The name of the vehicle manufacturer. + Examples: >>> vin("5FNYF5H59HB011946").manufacturer @@ -132,31 +163,37 @@ def manufacturer(self) -> str: @property def vds(self) -> str: - """The Vehicle Description Section (VDS) from the VIN + """The Vehicle Description Section (VDS) from the VIN. + + Returns: + The Vehicle Description Section (VDS) from the VIN. Examples: >>> vin("5FNYF5H59HB011946").vds 'YF5H5' """ - return self.vin[3:8] + return self._vin[3:8] @property def vis(self) -> str: """The Vehicle Identification Section (VIS) from the VIN + Returns: + The Vehicle Identification Section (VIS) from the VIN + Examples: >>> vin("5FNYF5H59HB011946").vis 'HB011946' """ - return self.vin[9:] + return self._vin[9:] # @property # def descriptor(self) -> str: - # descriptor = self.vin.ljust(17, "*") + # descriptor = self._vin.ljust(17, "*") # descriptor = descriptor[:8] + "*" + descriptor[9:] - # if self.vin[2] == "9": + # if self._vin[2] == "9": # return descriptor[:14] # else: # return descriptor[:11] @@ -165,12 +202,15 @@ def vis(self) -> str: def model_year(self) -> int: """The vehicle model year + Returns: + The vehicle model year. + Examples: >>> vin("5FNYF5H59HB011946").model_year 2017 """ - year_code = self.vin[9] + year_code = self._vin[9] if year_code in "ABCDEFGH": model_year = 2010 + ord(year_code) - ord("A") @@ -185,10 +225,10 @@ def model_year(self) -> int: elif year_code in "123456789": model_year = 2031 + ord(year_code) - ord("1") - if self.vin[6].isdigit(): + if self._vin[6].isdigit(): # cars and light trucks manufactured on or before April 30, 2009 (1980 to 2009) model_year = model_year - 30 - elif self.vin[6].isalpha(): + elif self._vin[6].isalpha(): # cars and light trucks manufactured after April 30, 2009 (2010 to 2039) pass @@ -201,4 +241,4 @@ def __repr__(self) -> str: return f"VIN({self!s})" def __str__(self) -> str: - return self.vin + return self._vin diff --git a/scripts/get-manufacturers.py b/src/vin/scripts/get-manufacturers.py similarity index 100% rename from scripts/get-manufacturers.py rename to src/vin/scripts/get-manufacturers.py From aa1e80454da37da2047c5d5bda524918da2e4990 Mon Sep 17 00:00:00 2001 From: David Peckham Date: Sat, 3 Feb 2024 14:57:15 -0500 Subject: [PATCH 2/2] version 0.2.0 --- .gitignore | 1 + .linkcheckerrc | 5 + CHANGELOG.md | 20 +++ CHANGELOG.rst | 49 ------ README.md | 73 ++++++++- docs.sphinx/Makefile | 20 --- docs.sphinx/requirements.txt | 1 - docs.sphinx/source/api.rst | 13 -- docs.sphinx/source/changelog.rst | 1 - docs.sphinx/source/conf.py | 87 ---------- docs.sphinx/source/index.rst | 31 ---- docs/.snippets/abbrs.txt | 1 - docs/.snippets/links.txt | 6 - docs/api.md | 2 +- docs/help.md | 9 + docs/hooks.py | 5 + docs/index.md | 50 +++--- docs/vpic/about.md | 6 - hatch.toml | 74 ++++----- mkdocs.yml | 155 +++++------------- .../scripts => scripts}/get-manufacturers.py | 3 +- src/vin/__about__.py | 2 +- src/vin/__init__.py | 2 +- 23 files changed, 210 insertions(+), 406 deletions(-) create mode 100644 .linkcheckerrc create mode 100644 CHANGELOG.md delete mode 100644 CHANGELOG.rst delete mode 100644 docs.sphinx/Makefile delete mode 100644 docs.sphinx/requirements.txt delete mode 100644 docs.sphinx/source/api.rst delete mode 100644 docs.sphinx/source/changelog.rst delete mode 100644 docs.sphinx/source/conf.py delete mode 100644 docs.sphinx/source/index.rst delete mode 100644 docs/.snippets/abbrs.txt delete mode 100644 docs/.snippets/links.txt create mode 100644 docs/help.md create mode 100644 docs/hooks.py delete mode 100644 docs/vpic/about.md rename {src/vin/scripts => scripts}/get-manufacturers.py (97%) diff --git a/.gitignore b/.gitignore index 7c9d22a..01a32ab 100644 --- a/.gitignore +++ b/.gitignore @@ -161,3 +161,4 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ vin/.DS_Store +docs/changelog.md diff --git a/.linkcheckerrc b/.linkcheckerrc new file mode 100644 index 0000000..765d331 --- /dev/null +++ b/.linkcheckerrc @@ -0,0 +1,5 @@ +# https://linkchecker.github.io/linkchecker/man/linkcheckerrc.html +[filtering] +ignore= + +[AnchorCheck] diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f7b10b1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,20 @@ +# Changelog + +## v0.2.0 (2024-02-03) + +[GitHub release](https://github.com/davidpeckham/vin/releases/tag/v0.2.0) + +### New Features + +* Validate VIN length, characters, and check digit, with the option to correct the check digit. +* Decode the manufacturer and model year from a VIN +* Documentation + +### Fixes + +* Updated annotations for `VIN.vds` and `VIN.vis` to show that they don't return None +* Annotated `VIN.model_year` to show that it returns int, and removed the else condition that allowed it to return None +* Inlined `VIN.is_vin_character` in the default constructor +* Converted property `VIN.check_digit` to class method `VIN.calculate_check_digit` +* Converted ``constants.VIN_CHARACTERS`` from list to string +* Renamed GitHub workflows as .yml (from .yaml) diff --git a/CHANGELOG.rst b/CHANGELOG.rst deleted file mode 100644 index 8534836..0000000 --- a/CHANGELOG.rst +++ /dev/null @@ -1,49 +0,0 @@ -.. _changelog: - -Changelog -========= - -Versions follow `Semantic Versioning `_ - -`0.1.2`_ - 2024-02-01 ---------------------- - -Changed -~~~~~~~ -* Removed the case that allowed :meth:`.VIN.model_year` to return None - -`0.1.1`_ - 2024-02-01 ---------------------- - -Changed -~~~~~~~ -* Removed None from return annotation for :meth:`.VIN.vds` and :meth:`.VIN.vis` -* Annotated :meth:`.VIN.model_year` to show that it returns int - -`0.1.0`_ - 2024-02-01 ---------------------- - -Changed -~~~~~~~ -* docs/source/conf.py now uses Hatch version from vin/__about__.py - -`0.0.2`_ - 2024-02-01 ---------------------- - -Added -~~~~~ -* Documentation - -Changed -~~~~~~~ -* Renamed GitHub workflows as .yml (from .yaml) -* Inlined :meth:`.VIN.is_vin_character` in the default constructor -* Converted property :meth:`.VIN.check_digit` to class method :meth:`.VIN.calculate_check_digit` -* Converted ``constants.VIN_CHARACTERS`` from list to string - -`0.0.1`_ - 2024-01-31 ---------------------- - -Added -~~~~~ -* First public release diff --git a/README.md b/README.md index 81fe9fa..9aa3810 100644 --- a/README.md +++ b/README.md @@ -2,23 +2,80 @@ [![PyPI - Version](https://img.shields.io/pypi/v/vin.svg)](https://pypi.org/project/vin) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/vin.svg)](https://pypi.org/project/vin) +[![license](https://img.shields.io/github/license/davidpeckham/vin.svg)](https://github.com/davidpeckham/vin/blob/main/LICENSE) + ----- -**Table of Contents** +**Contents** +- [Why use VIN?](#why-use-vin) +- [Vehicle Identification Number](#vehicle-identification-number) +- [Vehicle Data](#vehicle-data) - [Installation](#installation) - [License](#license) -## Installation +VIN validates Vehicle Identification Numbers and decodes the vehicle's manufacturer, make, model, and model year. -```console -pip install vin -``` + >>> from vin import VIN -## License + >>> vin("5FNYF5H59HB011946").manufacturer + Honda + + >>> vin("YT9NN1U14KA007175").manufacturer + Koenigsegg + + >>> vin("5FNYF5H59HB011946").model_year + 2017 + +## Why use VIN? + +- **Accurate** — Vehicle information is provided by the National Highway Traffic Safety Administration. +- **Fast** — Vehicle data is included and periodically updated, so validation and decoding are fast. + +## Vehicle Identification Number + +A ``VIN`` is a unique 17-character Vehicle Identification Number. + +* Assigned by vehicle manufacturers +* Uniquely identifies vehicles manufactured for sale or use in the United States since 1980 +* Governed by the U.S. National Highway Traffic Safety Administration (NHTSA) + +The structure of the VIN is: -`vin` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. + model year + | + WMI check digit | plant + |-----| | | | |--- serial ----| + Position 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + |-----------| |---------------------| + VDS VIS +The World Manufacturer Identifier (WMI) holds the country, region, and +name of the vehicle manufacturer. Mass-market manufacturers are assigned +a three-character WMI. Specialized manufacturers are assigned a six- +character WMI (positions 1-3 and 12-14) + +The Vehicle Description Section (VDS) is defined by manufacturers to +identify the vehicle make, model, body type, engine type, restraints, +and the weight class (for trucks and multi-purpose vehicles). + +The Vehicle Identification Section (VIS) identifies the model year, +plant where the vehicle was made, and the vehicle's serial number. + +For more information, see the [VIN specification](https://www.ecfr.gov/current/title-49/subtitle-B/chapter-V/part-565). + +Installation +------------ + +Use ``pip`` to install the library: + + $ pip install vin + +## Vehicle Data + +Vehicle data is provided by the U.S. National Highway Traffic Safety Administration (NHTSA) [Product Information Catalog and Vehicle Listing (vPIC)](https://vpic.nhtsa.dot.gov). + +## License -More \ No newline at end of file +`VIN` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. \ No newline at end of file diff --git a/docs.sphinx/Makefile b/docs.sphinx/Makefile deleted file mode 100644 index 401d9a3..0000000 --- a/docs.sphinx/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = source -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs.sphinx/requirements.txt b/docs.sphinx/requirements.txt deleted file mode 100644 index a95ae18..0000000 --- a/docs.sphinx/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -furo diff --git a/docs.sphinx/source/api.rst b/docs.sphinx/source/api.rst deleted file mode 100644 index 74eb28b..0000000 --- a/docs.sphinx/source/api.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _api: - -Developer Interface -=================== - -.. module:: vin - - -VIN ---- - -.. autoclass:: VIN - :members: diff --git a/docs.sphinx/source/changelog.rst b/docs.sphinx/source/changelog.rst deleted file mode 100644 index 09929fe..0000000 --- a/docs.sphinx/source/changelog.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../../CHANGELOG.rst diff --git a/docs.sphinx/source/conf.py b/docs.sphinx/source/conf.py deleted file mode 100644 index 83343fd..0000000 --- a/docs.sphinx/source/conf.py +++ /dev/null @@ -1,87 +0,0 @@ -import os -import sys -from datetime import datetime - - -sys.path.insert(0, os.path.abspath("../..")) - -from vin import __about__ # noqa - - -copyright = f"{datetime.now().year}, David Peckham" -author = "David Peckham" -master_doc = "index" -source_suffix = [".rst", ".md"] - -# The full version, including alpha/beta/rc tags -release = __about__.__version__ -version = release.rsplit(".", 1)[0] - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.napoleon", - "sphinx.ext.viewcode", -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_logo = "../logo.png" -html_theme = "furo" -html_theme_options = { - "github_user": "davidpeckham", - "github_repo": "vin", - "description": "A library for working with VINs", - "sidebar_collapse": False, - "logo_text_align": "center", -} - -html_title = "VIN docs" - -# If false, no index is generated. -html_use_index = True - -# If true, the index is split into individual pages for each letter. -html_split_index = False - -# If true, links to the reST sources are added to the pages. -html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -html_show_copyright = True - -html_sidebars = { - "**": [ - "sidebar/scroll-start.html", - "sidebar/brand.html", - "sidebar/search.html", - "sidebar/navigation.html", - "sidebar/ethical-ads.html", - "sidebar/scroll-end.html", - ] -} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] -pygments_style = "sphinx" - -autodoc_member_order = "groupwise" diff --git a/docs.sphinx/source/index.rst b/docs.sphinx/source/index.rst deleted file mode 100644 index ff0ca45..0000000 --- a/docs.sphinx/source/index.rst +++ /dev/null @@ -1,31 +0,0 @@ -VIN -=== - -Release v\ |release| (:ref:`What's new `) - - -.. include:: ../../README.rst - :start-after: teaser-begin - :end-before: teaser-end - -.. include:: ../../README.rst - :start-after: installation-begin - :end-before: installation-end - -.. include:: ../../README.rst - :start-after: usage-begin - :end-before: usage-end - - -API documentation ------------------ - -.. toctree:: - :maxdepth: 2 - - api - -.. toctree:: - :maxdepth: 1 - - changelog diff --git a/docs/.snippets/abbrs.txt b/docs/.snippets/abbrs.txt deleted file mode 100644 index 2e3bb9e..0000000 --- a/docs/.snippets/abbrs.txt +++ /dev/null @@ -1 +0,0 @@ -*[PyPI]: Python Package Index diff --git a/docs/.snippets/links.txt b/docs/.snippets/links.txt deleted file mode 100644 index e0d4e1e..0000000 --- a/docs/.snippets/links.txt +++ /dev/null @@ -1,6 +0,0 @@ -[PEP 440 version specifiers]: https://peps.python.org/pep-0440/#version-specifiers -[PEP 508]: https://peps.python.org/pep-0508/ -[PEP 517]: https://peps.python.org/pep-0517/ -[PEP 639]: https://peps.python.org/pep-0639/ -[PEP 660]: https://peps.python.org/pep-0660/ -[project metadata standard]: https://packaging.python.org/en/latest/specifications/pyproject-toml/#declaring-project-metadata-the-project-table diff --git a/docs/api.md b/docs/api.md index 60ee907..9674f26 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,4 +1,4 @@ -# VPIC API +# API ::: vin.VIN options: diff --git a/docs/help.md b/docs/help.md new file mode 100644 index 0000000..3a20545 --- /dev/null +++ b/docs/help.md @@ -0,0 +1,9 @@ +# Help + +## :material-api: API Documentation + +The [API documentation](api.md) is the best way to learn what VIN can do for you. + +## :simple-github: GitHub Issues + +Use [GitHub issues](https://github.com/davidpeckham/vin/issues) to ask questions, report issues, and suggest changes. diff --git a/docs/hooks.py b/docs/hooks.py new file mode 100644 index 0000000..38a0e01 --- /dev/null +++ b/docs/hooks.py @@ -0,0 +1,5 @@ +import shutil + + +def copy_history(*args, **kwargs): + shutil.copy("CHANGELOG.md", "docs/changelog.md") diff --git a/docs/index.md b/docs/index.md index 5c35f23..4b7ee6a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,10 +2,32 @@ [![PyPI - Version](https://img.shields.io/pypi/v/vin.svg)](https://pypi.org/project/vin) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/vin.svg)](https://pypi.org/project/vin) +[![license](https://img.shields.io/github/license/davidpeckham/vin.svg)](https://github.com/davidpeckham/vin/blob/main/LICENSE) + ----- -A ``VIN`` is a *unique 17-character Vehicle Identification Number*. +VIN validates Vehicle Identification Numbers and decodes the vehicle's manufacturer, make, model, and model year. + + >>> from vin import VIN + + >>> vin("5FNYF5H59HB011946").manufacturer + Honda + + >>> vin("YT9NN1U14KA007175").manufacturer + Koenigsegg + + >>> vin("5FNYF5H59HB011946").model_year + 2017 + +## Why use VIN? + +- **Accurate** — Vehicle information is provided by the National Highway Traffic Safety Administration. +- **Fast** — Vehicle data is included and periodically updated, so validation and decoding are fast. + +## Vehicle Identification Number + +A ``VIN`` is a unique 17-character Vehicle Identification Number. * Assigned by vehicle manufacturers * Uniquely identifies vehicles manufactured for sale or use in the United States since 1980 @@ -33,14 +55,7 @@ and the weight class (for trucks and multi-purpose vehicles). The Vehicle Identification Section (VIS) identifies the model year, plant where the vehicle was made, and the vehicle's serial number. -Use :class:`VIN`-object by calling the default constructor with the -17-character VIN string. To encode the VIN, convert it to a string: - - >>> vin = VIN("4T1BE46K19U856421") - >>> str(vin) - '4T1BE46K19U856421' - -For more information, see the [specification](https://www.ecfr.gov/current/title-49/subtitle-B/chapter-V/part-565). +For more information, see the [VIN specification](https://www.ecfr.gov/current/title-49/subtitle-B/chapter-V/part-565). Installation ------------ @@ -49,22 +64,9 @@ Use ``pip`` to install the library: $ pip install vin -Basic Usage ------------ - -Create a new ``VIN`` object from a 17-character string: - - >>> from vin import VIN - >>> VIN('4T1BE46K19U856421') - VIN(4T1BE46K19U856421) - -## API - -See [API](api.md) - -## Vehicle Information +## Vehicle Data -Vehicle information is provided by the U.S. National Highway Traffic Safety Administration (NHTSA) Product Information Catalog and Vehicle Listing (vPIC). See [About VPIC](vpic/about.md) for more information. +Vehicle data is provided by the U.S. National Highway Traffic Safety Administration (NHTSA) [Product Information Catalog and Vehicle Listing (vPIC)](https://vpic.nhtsa.dot.gov). ## License diff --git a/docs/vpic/about.md b/docs/vpic/about.md deleted file mode 100644 index 5187614..0000000 --- a/docs/vpic/about.md +++ /dev/null @@ -1,6 +0,0 @@ -# About VPIC - ------ - - -[Product Information Catalog and Vehicle Listing](https://vpic.nhtsa.dot.gov) \ No newline at end of file diff --git a/hatch.toml b/hatch.toml index 0527c5d..49e35ad 100644 --- a/hatch.toml +++ b/hatch.toml @@ -1,8 +1,8 @@ [envs.default] dependencies = [ - "freezegun==1.2.*", - "pytest-cov==4.1.*", - "pytest==7.3.*", + "freezegun==1.2.*", + "pytest-cov==4.1.*", + "pytest==7.3.*", ] [envs.default.scripts] @@ -11,62 +11,50 @@ cov-test = "pytest --cov {args:vin} --cov-report=term-missing --cov-report=xml" [envs.lint] dependencies = [ - "ruff==0.0.290", - "black==23.9.*", - "mypy==1.5.*", + "ruff==0.0.290", + "black==23.9.*", + "mypy==1.5.*", ] [envs.lint.scripts] typing = "mypy --install-types --non-interactive {args:src/vin}" style = [ - "black --check --diff {args:.}", - "ruff {args:.}", + "black --check --diff {args:.}", + "ruff {args:.}", ] fmt = [ - "black {args:.}", - "ruff --fix {args:.}", + "black {args:.}", + "ruff --fix {args:.}", ] [envs.docs] dependencies = [ - "mkdocs~=1.5.3", - "mkdocs-material~=9.5.1", - # Plugins - "mkdocs-minify-plugin~=0.7.1", - "mkdocs-git-revision-date-localized-plugin~=1.2.1", - "mkdocs-git-committers-plugin-2~=2.2.2", - "mkdocstrings-python~=1.8.0", - "mkdocs-redirects~=1.2.1", - "mkdocs-glightbox~=0.3.5", - "mike~=2.0.0", - # Extensions - "mkdocs-click~=0.8.1", - "pymdown-extensions~=10.5.0", - # Necessary for syntax highlighting in code blocks - "pygments~=2.17.2", - # Validation - # https://github.com/linkchecker/linkchecker/pull/669#issuecomment-1267236287 - "linkchecker @ git+https://github.com/linkchecker/linkchecker.git@d9265bb71c2054bf57b8c5734a4825d62505c779", + "mkdocs~=1.5.3", + "mkdocs-material~=9.5.1", + # Plugins + "mkdocs-minify-plugin~=0.7.1", + "mkdocs-git-revision-date-localized-plugin~=1.2.1", + "mkdocs-git-committers-plugin-2~=2.2.2", + "mkdocs-simple-hooks", + "mkdocstrings-python~=1.8.0", + "mkdocs-redirects~=1.2.1", + "mkdocs-glightbox~=0.3.5", + "mike~=2.0.0", + # Extensions + "mkdocs-click~=0.8.1", + "pymdown-extensions~=10.5.0", + # Necessary for syntax highlighting in code blocks + "pygments~=2.17.2", + # Validation + # https://github.com/linkchecker/linkchecker/pull/669#issuecomment-1267236287 + "linkchecker @ git+https://github.com/linkchecker/linkchecker.git@d9265bb71c2054bf57b8c5734a4825d62505c779", ] -# pre-install-commands = [ -# "python scripts/install_mkdocs_material_insiders.py", -# ] -# [envs.docs.overrides] -# env.GH_TOKEN_MKDOCS_MATERIAL_INSIDERS.env-vars = [ -# { key = "MKDOCS_CONFIG", value = "mkdocs.insiders.yml" }, -# { key = "MKDOCS_CONFIG", value = "mkdocs.yml", if = [""] }, -# { key = "MKDOCS_IMAGE_PROCESSING", value = "true" }, -# ] -# [envs.docs.env-vars] -# SOURCE_DATE_EPOCH = "1580601600" -# PYTHONUNBUFFERED = "1" -# MKDOCS_CONFIG = "mkdocs.yml" [envs.docs.scripts] build = "mkdocs build --clean --strict" serve = "mkdocs serve --dev-addr localhost:8000" validate = "linkchecker --config .linkcheckerrc site" # https://github.com/linkchecker/linkchecker/issues/678 build-check = [ - "build --no-directory-urls", - "validate", + "build --no-directory-urls", + "validate", ] diff --git a/mkdocs.yml b/mkdocs.yml index cfa3086..bb36660 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -4,39 +4,20 @@ site_author: David Peckham site_url: https://vin.readthedocs.io repo_name: davidpeckham/vin repo_url: https://github.com/davidpeckham/vin -# edit_uri: blob/master/docs copyright: 'Copyright © David Peckham 2024' +# https://www.mkdocs.org/user-guide/configuration/#validation +validation: + omitted_files: warn + absolute_links: warn + unrecognized_links: warn + docs_dir: docs site_dir: site theme: name: material - # custom_dir: docs/.overrides language: en - # favicon: assets/images/logo.svg - # icon: - # repo: fontawesome/brands/github-alt - # logo: material/egg - # font: - # text: Roboto - # code: Roboto Mono - # palette: - # - media: "(prefers-color-scheme: dark)" - # scheme: slate - # primary: indigo - # accent: indigo - # toggle: - # icon: material/weather-night - # name: Switch to light mode - # - media: "(prefers-color-scheme: light)" - # scheme: default - # primary: indigo - # accent: indigo - # toggle: - # icon: material/weather-sunny - # name: Switch to dark mode features: - - content.action.edit - content.code.copy - content.tabs.link - content.tooltips @@ -48,22 +29,17 @@ theme: - navigation.tabs.sticky nav: - - Home: + - Get Started: - About: index.md - API: api.md - - VPIC: - - About: vpic/about.md + - Help: help.md + - Changelog: changelog.md watch: - src/vin -# hooks: -# - docs/.hooks/plugin_register.py - plugins: - # Built-in search: {} - # Extra glightbox: {} minify: minify_html: true @@ -76,89 +52,44 @@ plugins: python: paths: [src] options: - show_root_heading: true - show_root_full_path: false + filters: ["!^_", "^__init__$"] + members_order: source + group_by_category: true + separate_signature: true + show_bases: false + show_category_heading: true show_if_no_docstring: true + show_root_full_path: false + show_root_heading: true show_signature_annotations: true - show_bases: false + docstring_options: + ignore_init_summary: true + merge_init_into_class: true + mkdocs-simple-hooks: + hooks: + on_pre_build: 'docs.hooks:copy_history' markdown_extensions: - admonition - pymdownx.details - pymdownx.superfences - -# markdown_extensions: -# # Built-in -# - markdown.extensions.abbr: -# - markdown.extensions.admonition: -# - markdown.extensions.attr_list: -# - markdown.extensions.footnotes: -# - markdown.extensions.md_in_html: -# - markdown.extensions.meta: -# - markdown.extensions.tables: -# - markdown.extensions.toc: -# permalink: true -# # Extra -# - mkdocs-click: -# - pymdownx.arithmatex: -# - pymdownx.betterem: -# smart_enable: all -# - pymdownx.caret: -# - pymdownx.critic: -# - pymdownx.details: -# - pymdownx.emoji: -# # https://github.com/twitter/twemoji -# # https://raw.githubusercontent.com/facelessuser/pymdown-extensions/master/pymdownx/twemoji_db.py -# emoji_index: !!python/name:material.extensions.emoji.twemoji -# emoji_generator: !!python/name:material.extensions.emoji.to_svg -# - pymdownx.highlight: -# guess_lang: false -# linenums_style: pymdownx-inline -# use_pygments: true -# - pymdownx.inlinehilite: -# - pymdownx.keys: -# - pymdownx.magiclink: -# repo_url_shortener: true -# repo_url_shorthand: true -# social_url_shortener: true -# social_url_shorthand: true -# normalize_issue_symbols: true -# provider: github -# user: pypa -# repo: hatch -# - pymdownx.mark: -# - pymdownx.progressbar: -# - pymdownx.saneheaders: -# - pymdownx.smartsymbols: -# - pymdownx.snippets: -# check_paths: true -# base_path: -# - docs/.snippets -# auto_append: -# - links.txt -# - abbrs.txt -# - pymdownx.superfences: -# - pymdownx.tabbed: -# alternate_style: true -# slugify: !!python/object/apply:pymdownx.slugs.slugify -# kwds: -# case: lower -# - pymdownx.tasklist: -# custom_checkbox: true -# - pymdownx.tilde: - -# extra: -# version: -# provider: mike -# social: -# - icon: fontawesome/brands/github-alt -# link: https://github.com/ofek -# - icon: fontawesome/solid/blog -# link: https://ofek.dev/words/ -# - icon: fontawesome/brands/twitter -# link: https://twitter.com/Ofekmeister -# - icon: fontawesome/brands/linkedin -# link: https://www.linkedin.com/in/ofeklev/ -# extra_css: -# - assets/css/custom.css -# - https://cdn.jsdelivr.net/npm/firacode@6.2.0/distr/fira_code.css + - attr_list + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + - pymdownx.highlight: + guess_lang: false + linenums_style: pymdownx-inline + use_pygments: true + - pymdownx.inlinehilite: + - pymdownx.keys: + - pymdownx.magiclink: + repo_url_shortener: true + repo_url_shorthand: true + social_url_shortener: true + social_url_shorthand: true + normalize_issue_symbols: true + provider: github + user: davidpeckham + repo: vin + - pymdownx.mark: diff --git a/src/vin/scripts/get-manufacturers.py b/scripts/get-manufacturers.py similarity index 97% rename from src/vin/scripts/get-manufacturers.py rename to scripts/get-manufacturers.py index 874bfaa..c3f9c2d 100644 --- a/src/vin/scripts/get-manufacturers.py +++ b/scripts/get-manufacturers.py @@ -8,6 +8,7 @@ import json import time from pathlib import Path +from typing import Any import requests @@ -15,7 +16,7 @@ BASE_URL = "https://vpic.nhtsa.dot.gov/api/vehicles" -def get_wmi(manufacturer_id: int) -> list[str]: +def get_wmi(manufacturer_id: int) -> list[dict[str, Any]]: """get the WMI details for a manufacturer""" params = {"format": "json"} diff --git a/src/vin/__about__.py b/src/vin/__about__.py index 22fd1fd..2b141a2 100644 --- a/src/vin/__about__.py +++ b/src/vin/__about__.py @@ -1,4 +1,4 @@ # SPDX-FileCopyrightText: 2024-present David Peckham # # SPDX-License-Identifier: MIT -__version__ = "0.1.2" +__version__ = "0.2.0" diff --git a/src/vin/__init__.py b/src/vin/__init__.py index 1999d33..ad586f1 100644 --- a/src/vin/__init__.py +++ b/src/vin/__init__.py @@ -76,7 +76,7 @@ def __init__(self, vin: str, fix_check_digit: bool = False) -> None: Raises: TypeError: `vin` is not a string. ValueError: `vin` is not 17 characters - ValueError: `vin` has characters that aren't allowed in a VIN + ValueError: `vin` has characters that aren't allowed in a Vehicle Identification Number ValueError: `vin` check digit isn't correct """