diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7104fec..d02f139 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ on: pull_request: jobs: - quality: + lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -26,5 +26,38 @@ jobs: - name: mypy run: mypy src + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10", "3.11", "3.12", "3.13"] + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: pip install -e ".[dev]" + - name: pytest - run: pytest + run: pytest --cov=visor --cov-report=term-missing + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install build tools + run: pip install build twine + + - name: Build + run: python -m build + + - name: Check dist + run: python -m twine check dist/* diff --git a/.gitignore b/.gitignore index 3409ff2..721137a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,9 @@ env/ .pytest_cache/ .ruff_cache/ .mypy_cache/ +.coverage +coverage.xml +htmlcov/ # Build artifacts dist/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 17a96e1..d384dd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] @@ -19,9 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Auto-pagination helpers: `paginate_listings`, `paginate_dealers` (async generators), `iter_listings`, `iter_dealers` (sync iterators) - Public export audit and test suite (`tests/test_exports.py`) +- `src/visor/py.typed` PEP 561 marker for typed-package distribution +- Publish-grade `pyproject.toml` metadata: authors, classifiers, keywords, license files, URLs +- CI matrix across Python 3.10–3.13 with separate lint, test, and build jobs -## Future Work - -- Retry-with-backoff strategy -- MCP server wrapper -- Response caching +[Unreleased]: https://github.com/whitewalls86/visor-python/commits/master/ diff --git a/pyproject.toml b/pyproject.toml index e25220f..fea2df1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["hatchling"] +requires = ["hatchling>=1.27"] build-backend = "hatchling.build" [project] @@ -8,7 +8,23 @@ version = "0.1.0" description = "Python SDK for the Visor Public API" readme = "README.md" license = "MIT" +license-files = ["LICENSE"] requires-python = ">=3.10" +authors = [ + { name = "Andrew Miller", email = "miller.andrew.preston@gmail.com" }, +] +keywords = ["visor", "automotive", "car", "vehicle", "listings", "inventory", "api", "sdk"] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Typing :: Typed", +] dependencies = [ "httpx>=0.27", "pydantic>=2.0", @@ -18,19 +34,36 @@ dependencies = [ dev = [ "pytest>=8.0", "pytest-asyncio>=0.23", + "pytest-cov>=5.0", "respx>=0.21", "ruff>=0.4", "mypy>=1.10", "pre-commit>=3.0", + "build>=1.0", + "twine>=5.0", ] [project.urls] Homepage = "https://visor.vin" -Repository = "https://github.com/drewpearce/visor-python" +Repository = "https://github.com/whitewalls86/visor-python" +Issues = "https://github.com/whitewalls86/visor-python/issues" +Changelog = "https://github.com/whitewalls86/visor-python/blob/master/CHANGELOG.md" [tool.hatch.build.targets.wheel] packages = ["src/visor"] +[tool.hatch.build.targets.sdist] +exclude = [ + "Visor API Docs/", + "Design Docs/", + ".coverage", + ".pytest_cache/", + "dist/", + "*.egg-info/", + "__pycache__/", + "tests/", +] + [tool.ruff] line-length = 88 target-version = "py310" diff --git a/src/visor/models/listings.py b/src/visor/models/listings.py index f4e2684..4db8c15 100644 --- a/src/visor/models/listings.py +++ b/src/visor/models/listings.py @@ -104,8 +104,9 @@ def to_params(self) -> dict[str, str]: class ListingSummary(VisorResponseModel): """Returned by filter_listings() and dealer_inventory(). - Fields are optional because the caller controls projection via - ListingsFilter.fields. + id and vin are always present; the API returns them regardless of fields + projection. All other fields are optional because the caller controls which + fields are returned via ListingsFilter.fields. """ id: str diff --git a/src/visor/py.typed b/src/visor/py.typed new file mode 100644 index 0000000..e69de29