diff --git a/.github/workflows/lint-and-test.yaml b/.github/workflows/lint-and-test.yml similarity index 69% rename from .github/workflows/lint-and-test.yaml rename to .github/workflows/lint-and-test.yml index 33249a9..63291cc 100644 --- a/.github/workflows/lint-and-test.yaml +++ b/.github/workflows/lint-and-test.yml @@ -23,21 +23,21 @@ jobs: - name: Lint code run: hatch run lint:style - # lint-typing: - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@vv4.1.1 - # - name: Set up Python - # uses: actions/setup-python@v5 - # with: - # python-version: "3.11" - # - name: Install dependencies - # run: | - # pip install hatch - # pip install hatch-vcs - # pip install hatch-fancy-pypi-readme - # - name: Lint typing - # run: hatch run lint:typing + lint-typing: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.1.1 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install dependencies + run: | + pip install hatch + pip install hatch-vcs + pip install hatch-fancy-pypi-readme + - name: Lint typing + run: hatch run lint:typing test: runs-on: ubuntu-latest diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yml similarity index 100% rename from .github/workflows/release.yaml rename to .github/workflows/release.yml diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..eea5bd6 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,15 @@ +version: 2 + +build: + os: "ubuntu-20.04" + tools: + python: "3.10" + +sphinx: + configuration: docs/source/conf.py + +python: + install: + - requirements: docs/requirements.txt + - method: pip + path: . diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 0000000..9a7b6c6 --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,26 @@ +.. _changelog: + +Changelog +========= + +Versions follow `Semantic Versioning `_ + +`0.0.2`_ - 2024-02-01 +--------------------- + +Added +~~~~~ +* Documentation + +Changed +~~~~~~~ +* Renamed GitHub workflows as .yml (from .yaml) +* The method :meth:`.VIN.is_vin_character` now returns False instead of raising ValueError +* Fixed VIN_CHARACTERS assignment in constants.py + +`0.0.1`_ - 2024-01-31 +--------------------- + +Added +~~~~~ +* First public release diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..81a768a --- /dev/null +++ b/README.rst @@ -0,0 +1,104 @@ +.. begin-html-header + +.. raw:: html + +

+
+ + vin + +
+
+
+

+

+ + + + + + + + + + + + + + + +

+ +.. end-html-header + +.. teaser-begin + +A ``VIN`` is a *unique 17-character Vehicle Identification Number*. + +* Assigned by vehicle manufacturers +* Governed by the U.S. National Highway Traffic Safety Administration (NHTSA) +* Uniquely identifies vehicles manufacture for sale or use in the United States since 1980 + +The structure of the VIN is: + +.. 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 + +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 `_. + +.. teaser-end + +.. installation-begin + +Installation +------------ + +Use ``pip`` to install the library + +.. code-block:: bash + + $ pip install vin + +.. installation-end + +.. usage-begin + +Basic Usage +----------- + +Create a new ``VIN`` object from a 17-character string + +.. code-block:: pycon + + >>> from vin import VIN + >>> VIN('4T1BE46K19U856421') + VIN(4T1BE46K19U856421) + +.. usage-end diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..401d9a3 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# 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/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..a95ae18 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1 @@ +furo diff --git a/docs/source/api.rst b/docs/source/api.rst new file mode 100644 index 0000000..74eb28b --- /dev/null +++ b/docs/source/api.rst @@ -0,0 +1,13 @@ +.. _api: + +Developer Interface +=================== + +.. module:: vin + + +VIN +--- + +.. autoclass:: VIN + :members: diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst new file mode 100644 index 0000000..09929fe --- /dev/null +++ b/docs/source/changelog.rst @@ -0,0 +1 @@ +.. include:: ../../CHANGELOG.rst diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..e59ffc1 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,87 @@ +import os +import sys +from datetime import datetime + + +sys.path.insert(0, os.path.abspath("../..")) + +import vin # 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 = vin.__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/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..ab02e06 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,35 @@ +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 + +.. include:: ../../README.rst + :start-after: cli-begin + :end-before: cli-end + +API documentation +----------------- + +.. toctree:: + :maxdepth: 2 + + api + +.. toctree:: + :maxdepth: 1 + + changelog + diff --git a/hatch.toml b/hatch.toml index 8fafc22..4c7bac0 100644 --- a/hatch.toml +++ b/hatch.toml @@ -7,7 +7,7 @@ dependencies = [ [envs.default.scripts] test = "pytest {args:.}" -cov-test = "pytest --cov {args:ulid} --cov-report=term-missing --cov-report=xml" +cov-test = "pytest --cov {args:vin} --cov-report=term-missing --cov-report=xml" [envs.lint] dependencies = [ @@ -17,7 +17,7 @@ dependencies = [ ] [envs.lint.scripts] -typing = "mypy --install-types --non-interactive {args:ulid}" +typing = "mypy --install-types --non-interactive {args:src/vin}" style = [ "black --check --diff {args:.}", "ruff {args:.}", diff --git a/pyproject.toml b/pyproject.toml index b8da832..b7f8d49 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["hatchling"] +requires = ["hatchling", "hatch-vcs", "hatch-fancy-pypi-readme"] build-backend = "hatchling.build" [project] @@ -89,7 +89,6 @@ exclude_lines = [ line-length = 100 [tool.ruff] -target-version = "py39" line-length = 100 select = ["A", "B", "C", "C4", "E", "F", "I", "N", "PT", "Q", "RUF", "S", "SIM", "T10", "UP", "W", "YTT"] fixable = ["RUF100", "I001"] @@ -116,3 +115,10 @@ line_length = 100 [tool.doc8] max-line-length = 100 + +[tool.hatch.metadata.hooks.fancy-pypi-readme] +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 diff --git a/src/vin/__about__.py b/src/vin/__about__.py index 91343fc..d4743f4 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.0.1" +__version__ = "0.0.2" diff --git a/src/vin/__init__.py b/src/vin/__init__.py index 6acce92..2485694 100644 --- a/src/vin/__init__.py +++ b/src/vin/__init__.py @@ -65,7 +65,7 @@ class VIN: """ def __init__(self, vehicle_identification_number: str) -> None: - """return a VIN""" + """create a VIN""" if not isinstance(vehicle_identification_number, str): raise TypeError("VIN must be a string") self.vin: str = vehicle_identification_number @@ -75,12 +75,11 @@ def __init__(self, vehicle_identification_number: str) -> None: raise ValueError(f"VIN must have only these characters {VIN_CHARACTERS}") if self.vin[8:9] != self.check_digit: raise ValueError("VIN check digit is incorrect") + return def is_vin_character(self, vin) -> bool: """ "return True if vin only has VIN characters""" - for c in vin: - if c not in VIN_CHARACTERS: - raise ValueError("VIN contains non-VIN characters") + return all(c in VIN_CHARACTERS for c in vin) @property def wmi(self) -> str: diff --git a/src/vin/constants.py b/src/vin/constants.py index fc038ff..a8cd9d9 100644 --- a/src/vin/constants.py +++ b/src/vin/constants.py @@ -37,7 +37,7 @@ "Z": 9, } -VIN_CHARACTERS: list[str] = VIN_CHARACTER_VALUES.keys() +VIN_CHARACTERS: list[str] = list(VIN_CHARACTER_VALUES.keys()) VIN_POSITION_WEIGHTS: list[int] = [8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2]