salestax-python 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. salestax_python-0.1.0/.gitignore +54 -0
  2. salestax_python-0.1.0/CHANGELOG.md +21 -0
  3. salestax_python-0.1.0/LICENSE +21 -0
  4. salestax_python-0.1.0/PKG-INFO +158 -0
  5. salestax_python-0.1.0/README.md +120 -0
  6. salestax_python-0.1.0/examples/async_usage.py +20 -0
  7. salestax_python-0.1.0/examples/basic_calculation.py +13 -0
  8. salestax_python-0.1.0/examples/batch_calculation.py +17 -0
  9. salestax_python-0.1.0/examples/error_handling.py +18 -0
  10. salestax_python-0.1.0/pyproject.toml +111 -0
  11. salestax_python-0.1.0/salestax/__init__.py +61 -0
  12. salestax_python-0.1.0/salestax/_config.py +53 -0
  13. salestax_python-0.1.0/salestax/_transport/__init__.py +18 -0
  14. salestax_python-0.1.0/salestax/_transport/http_client.py +287 -0
  15. salestax_python-0.1.0/salestax/_transport/httpx_transport.py +54 -0
  16. salestax_python-0.1.0/salestax/_transport/retry.py +49 -0
  17. salestax_python-0.1.0/salestax/_transport/types.py +91 -0
  18. salestax_python-0.1.0/salestax/_transport/urllib_transport.py +55 -0
  19. salestax_python-0.1.0/salestax/_transport/user_agent.py +15 -0
  20. salestax_python-0.1.0/salestax/_version.py +3 -0
  21. salestax_python-0.1.0/salestax/client.py +215 -0
  22. salestax_python-0.1.0/salestax/errors.py +202 -0
  23. salestax_python-0.1.0/salestax/models.py +80 -0
  24. salestax_python-0.1.0/salestax/py.typed +1 -0
  25. salestax_python-0.1.0/salestax/resources/__init__.py +5 -0
  26. salestax_python-0.1.0/salestax/resources/jurisdictions.py +27 -0
  27. salestax_python-0.1.0/salestax/resources/rates.py +25 -0
  28. salestax_python-0.1.0/salestax/resources/tax.py +146 -0
  29. salestax_python-0.1.0/salestax/utils.py +22 -0
  30. salestax_python-0.1.0/tests/__init__.py +0 -0
  31. salestax_python-0.1.0/tests/conftest.py +100 -0
  32. salestax_python-0.1.0/tests/integration/__init__.py +0 -0
  33. salestax_python-0.1.0/tests/integration/test_live.py +39 -0
  34. salestax_python-0.1.0/tests/unit/__init__.py +0 -0
  35. salestax_python-0.1.0/tests/unit/test_async.py +117 -0
  36. salestax_python-0.1.0/tests/unit/test_client.py +30 -0
  37. salestax_python-0.1.0/tests/unit/test_client_extras.py +151 -0
  38. salestax_python-0.1.0/tests/unit/test_errors.py +50 -0
  39. salestax_python-0.1.0/tests/unit/test_hooks.py +52 -0
  40. salestax_python-0.1.0/tests/unit/test_httpx_transport.py +79 -0
  41. salestax_python-0.1.0/tests/unit/test_retry.py +44 -0
  42. salestax_python-0.1.0/tests/unit/test_tax.py +72 -0
  43. salestax_python-0.1.0/tests/unit/test_transport.py +110 -0
  44. salestax_python-0.1.0/tests/unit/test_urllib_transport.py +142 -0
  45. salestax_python-0.1.0/tests/unit/test_utils.py +58 -0
@@ -0,0 +1,54 @@
1
+ # -- Python bytecode / caches --
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+
7
+ # -- Distribution / packaging --
8
+ build/
9
+ dist/
10
+ *.egg-info/
11
+ *.egg
12
+ MANIFEST
13
+
14
+ # -- Virtual environments --
15
+ .venv/
16
+ venv/
17
+ env/
18
+ ENV/
19
+ .python-version
20
+
21
+ # -- Test / coverage artifacts --
22
+ .pytest_cache/
23
+ .coverage
24
+ .coverage.*
25
+ coverage.xml
26
+ htmlcov/
27
+ .tox/
28
+ .nox/
29
+
30
+ # -- Type checker / linter caches --
31
+ .mypy_cache/
32
+ .dmypy.json
33
+ dmypy.json
34
+ .ruff_cache/
35
+
36
+ # -- Build backends --
37
+ .hatch/
38
+
39
+ # -- Editors / IDEs --
40
+ .idea/
41
+ .vscode/
42
+ *.swp
43
+ *.swo
44
+ *~
45
+
46
+ # -- OS noise --
47
+ .DS_Store
48
+ Thumbs.db
49
+ desktop.ini
50
+
51
+ # -- Secrets / local config (never commit) --
52
+ .env
53
+ .env.*
54
+ !.env.example
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
+ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-09-22
10
+
11
+ ### Added
12
+
13
+ - Initial release.
14
+ - `SalesTaxClient` (sync) with zero runtime dependencies.
15
+ - `AsyncSalesTaxClient` (async, requires `[async]` extra).
16
+ - Resources: `client.tax`, `client.rates`, `client.jurisdictions`.
17
+ - Automatic retries with exponential backoff, jitter, and `Retry-After` support.
18
+ - Full error hierarchy: `RateLimitError`, `ValidationError`, `TimeoutError`, and more.
19
+ - Lifecycle hooks: `on_request`, `on_response`, `on_retry`.
20
+ - Batch chunking via `calculate_batch_chunked()`.
21
+ - PEP 561 `py.typed` marker; `mypy --strict` clean.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 InstaBlick-USA
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,158 @@
1
+ Metadata-Version: 2.5
2
+ Name: salestax-python
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for Sales Tax Calculator API — real-time sales tax across 70+ countries, 51 US jurisdictions, and batch processing.
5
+ Project-URL: Homepage, https://salestaxcalculatorapi.com
6
+ Project-URL: Documentation, https://salestaxcalculatorapi.com/docs
7
+ Project-URL: Repository, https://github.com/InstaBlick-USA/salestax-python
8
+ Project-URL: Issues, https://github.com/InstaBlick-USA/salestax-python/issues
9
+ Project-URL: Changelog, https://github.com/InstaBlick-USA/salestax-python/blob/main/CHANGELOG.md
10
+ Author-email: InstaBlick USA <dev@salestaxcalculatorapi.com>
11
+ License: MIT
12
+ License-File: LICENSE
13
+ Keywords: api-client,async,checkout,ecommerce,gst,python,sales-tax,tax-api,vat
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Office/Business :: Financial :: Accounting
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.10
26
+ Provides-Extra: async
27
+ Requires-Dist: httpx>=0.27; extra == 'async'
28
+ Provides-Extra: dev
29
+ Requires-Dist: build>=1.2; extra == 'dev'
30
+ Requires-Dist: mypy>=1.9; extra == 'dev'
31
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
32
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
33
+ Requires-Dist: pytest>=8.0; extra == 'dev'
34
+ Requires-Dist: respx>=0.21; extra == 'dev'
35
+ Requires-Dist: ruff>=0.4; extra == 'dev'
36
+ Requires-Dist: twine>=5.0; extra == 'dev'
37
+ Description-Content-Type: text/markdown
38
+
39
+ # salestax-python
40
+
41
+ [![PyPI](https://img.shields.io/pypi/v/salestax-python)](https://pypi.org/project/salestax-python/)
42
+ [![Python versions](https://img.shields.io/pypi/pyversions/salestax-python)](https://pypi.org/project/salestax-python/)
43
+ [![CI](https://github.com/InstaBlick-USA/salestax-python/actions/workflows/ci.yml/badge.svg)](https://github.com/InstaBlick-USA/salestax-python/actions)
44
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
45
+
46
+ Official Python SDK for the **[Sales Tax Calculator API](https://salestaxcalculatorapi.com)** —
47
+ real-time sales tax for 70+ countries, 51 US jurisdictions, and 13 Canadian provinces.
48
+ Batch up to 100 transactions. Zero runtime dependencies (sync). Full type hints.
49
+
50
+ ## Install
51
+
52
+ ```bash
53
+ pip install salestax-python
54
+ ```
55
+
56
+ Async support:
57
+
58
+ ```bash
59
+ pip install salestax-python[async]
60
+ ```
61
+
62
+ ## Quickstart
63
+
64
+ ```python
65
+ from salestax import SalesTaxClient
66
+
67
+ with SalesTaxClient() as client: # reads SALESTAX_API_KEY
68
+ tax = client.tax.calculate(zip_code="90210", amount=100)
69
+
70
+ print(tax["taxAmount"]) # 9.75
71
+ print(tax["totalAmount"]) # 109.75
72
+ ```
73
+
74
+ Get your API key → **[salestaxcalculatorapi.com](https://salestaxcalculatorapi.com)**
75
+
76
+ ## Features
77
+
78
+ - **Zero runtime dependencies** for the sync client — stdlib `urllib` only.
79
+ - **Optional async client** via `httpx` (`pip install salestax-python[async]`).
80
+ - **Automatic retries** with exponential backoff + jitter, honors `Retry-After`.
81
+ - **Typed errors** — `RateLimitError`, `ValidationError`, `TimeoutError`, and more.
82
+ No string parsing. Every error carries `code`, `request_id`, and (where applicable) `param`.
83
+ - **Lifecycle hooks** — `on_request`, `on_response`, `on_retry` for logging and telemetry.
84
+ - **Batch chunking** — `calculate_batch_chunked()` splits >100 automatically.
85
+ - **Fully typed** — ships `py.typed`, `mypy --strict` clean.
86
+
87
+ ## Async
88
+
89
+ ```python
90
+ import asyncio
91
+ from salestax import AsyncSalesTaxClient
92
+
93
+ async def main():
94
+ async with AsyncSalesTaxClient() as client:
95
+ tax = await client.tax.calculate(zip_code="90210", amount=100)
96
+ print(tax["taxAmount"])
97
+
98
+ asyncio.run(main())
99
+ ```
100
+
101
+ ## Batch
102
+
103
+ ```python
104
+ with SalesTaxClient() as client:
105
+ result = client.tax.calculate_batch_chunked(
106
+ transactions, # any length
107
+ idempotency_key="order-12345",
108
+ )
109
+ ```
110
+
111
+ ## Error Handling
112
+
113
+ ```python
114
+ from salestax import RateLimitError, ValidationError
115
+
116
+ try:
117
+ client.tax.calculate(zip_code="90210", amount=100)
118
+ except RateLimitError as err:
119
+ # err.retry_after_ms — back off
120
+ ...
121
+ except ValidationError as err:
122
+ # err.param — which field failed
123
+ ...
124
+ ```
125
+
126
+ ## Configuration
127
+
128
+ ```python
129
+ from salestax import SalesTaxClient, RetryPolicy
130
+
131
+ client = SalesTaxClient(
132
+ api_key="sk_live_...",
133
+ timeout_ms=20_000,
134
+ chunk_batch=True,
135
+ retry=RetryPolicy(max_retries=4),
136
+ )
137
+ ```
138
+
139
+ ## Hooks
140
+
141
+ ```python
142
+ client = SalesTaxClient(hooks={
143
+ "on_retry": lambda info: print(f"retry {info['attempt']} in {info['delay_ms']}ms"),
144
+ })
145
+ ```
146
+
147
+ ## Also Available For
148
+
149
+ - [salestax-node](https://github.com/InstaBlick-USA/salestax-node) — Node.js / TypeScript
150
+ - [salestax-ruby](https://github.com/InstaBlick-USA/salestax-ruby) — Ruby
151
+
152
+ ## Documentation
153
+
154
+ Full API reference → **[salestaxcalculatorapi.com/docs](https://salestaxcalculatorapi.com/docs)**
155
+
156
+ ## License
157
+
158
+ MIT © [InstaBlick USA](https://github.com/InstaBlick-USA)
@@ -0,0 +1,120 @@
1
+ # salestax-python
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/salestax-python)](https://pypi.org/project/salestax-python/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/salestax-python)](https://pypi.org/project/salestax-python/)
5
+ [![CI](https://github.com/InstaBlick-USA/salestax-python/actions/workflows/ci.yml/badge.svg)](https://github.com/InstaBlick-USA/salestax-python/actions)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
7
+
8
+ Official Python SDK for the **[Sales Tax Calculator API](https://salestaxcalculatorapi.com)** —
9
+ real-time sales tax for 70+ countries, 51 US jurisdictions, and 13 Canadian provinces.
10
+ Batch up to 100 transactions. Zero runtime dependencies (sync). Full type hints.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pip install salestax-python
16
+ ```
17
+
18
+ Async support:
19
+
20
+ ```bash
21
+ pip install salestax-python[async]
22
+ ```
23
+
24
+ ## Quickstart
25
+
26
+ ```python
27
+ from salestax import SalesTaxClient
28
+
29
+ with SalesTaxClient() as client: # reads SALESTAX_API_KEY
30
+ tax = client.tax.calculate(zip_code="90210", amount=100)
31
+
32
+ print(tax["taxAmount"]) # 9.75
33
+ print(tax["totalAmount"]) # 109.75
34
+ ```
35
+
36
+ Get your API key → **[salestaxcalculatorapi.com](https://salestaxcalculatorapi.com)**
37
+
38
+ ## Features
39
+
40
+ - **Zero runtime dependencies** for the sync client — stdlib `urllib` only.
41
+ - **Optional async client** via `httpx` (`pip install salestax-python[async]`).
42
+ - **Automatic retries** with exponential backoff + jitter, honors `Retry-After`.
43
+ - **Typed errors** — `RateLimitError`, `ValidationError`, `TimeoutError`, and more.
44
+ No string parsing. Every error carries `code`, `request_id`, and (where applicable) `param`.
45
+ - **Lifecycle hooks** — `on_request`, `on_response`, `on_retry` for logging and telemetry.
46
+ - **Batch chunking** — `calculate_batch_chunked()` splits >100 automatically.
47
+ - **Fully typed** — ships `py.typed`, `mypy --strict` clean.
48
+
49
+ ## Async
50
+
51
+ ```python
52
+ import asyncio
53
+ from salestax import AsyncSalesTaxClient
54
+
55
+ async def main():
56
+ async with AsyncSalesTaxClient() as client:
57
+ tax = await client.tax.calculate(zip_code="90210", amount=100)
58
+ print(tax["taxAmount"])
59
+
60
+ asyncio.run(main())
61
+ ```
62
+
63
+ ## Batch
64
+
65
+ ```python
66
+ with SalesTaxClient() as client:
67
+ result = client.tax.calculate_batch_chunked(
68
+ transactions, # any length
69
+ idempotency_key="order-12345",
70
+ )
71
+ ```
72
+
73
+ ## Error Handling
74
+
75
+ ```python
76
+ from salestax import RateLimitError, ValidationError
77
+
78
+ try:
79
+ client.tax.calculate(zip_code="90210", amount=100)
80
+ except RateLimitError as err:
81
+ # err.retry_after_ms — back off
82
+ ...
83
+ except ValidationError as err:
84
+ # err.param — which field failed
85
+ ...
86
+ ```
87
+
88
+ ## Configuration
89
+
90
+ ```python
91
+ from salestax import SalesTaxClient, RetryPolicy
92
+
93
+ client = SalesTaxClient(
94
+ api_key="sk_live_...",
95
+ timeout_ms=20_000,
96
+ chunk_batch=True,
97
+ retry=RetryPolicy(max_retries=4),
98
+ )
99
+ ```
100
+
101
+ ## Hooks
102
+
103
+ ```python
104
+ client = SalesTaxClient(hooks={
105
+ "on_retry": lambda info: print(f"retry {info['attempt']} in {info['delay_ms']}ms"),
106
+ })
107
+ ```
108
+
109
+ ## Also Available For
110
+
111
+ - [salestax-node](https://github.com/InstaBlick-USA/salestax-node) — Node.js / TypeScript
112
+ - [salestax-ruby](https://github.com/InstaBlick-USA/salestax-ruby) — Ruby
113
+
114
+ ## Documentation
115
+
116
+ Full API reference → **[salestaxcalculatorapi.com/docs](https://salestaxcalculatorapi.com/docs)**
117
+
118
+ ## License
119
+
120
+ MIT © [InstaBlick USA](https://github.com/InstaBlick-USA)
@@ -0,0 +1,20 @@
1
+ """Async client example. Requires: pip install salestax-python[async]"""
2
+
3
+ import asyncio
4
+
5
+ from salestax import AsyncSalesTaxClient
6
+
7
+
8
+ async def main() -> None:
9
+ async with AsyncSalesTaxClient() as client:
10
+ results = await asyncio.gather(
11
+ client.tax.calculate(zip_code="90210", amount=100),
12
+ client.tax.calculate(zip_code="10001", amount=200),
13
+ client.rates.get("60601"),
14
+ )
15
+ for r in results:
16
+ print(r)
17
+
18
+
19
+ if __name__ == "__main__":
20
+ asyncio.run(main())
@@ -0,0 +1,13 @@
1
+ """Minimal single-calculation example.
2
+
3
+ Run:
4
+ SALESTAX_API_KEY=sk_live_... python examples/basic_calculation.py
5
+ """
6
+
7
+ from salestax import SalesTaxClient
8
+
9
+ with SalesTaxClient() as client:
10
+ tax = client.tax.calculate(zip_code="90210", amount=100)
11
+
12
+ print(f"Tax: ${tax['taxAmount']:.2f}")
13
+ print(f"Total: ${tax['totalAmount']:.2f}")
@@ -0,0 +1,17 @@
1
+ """Batch example with automatic chunking for >100 transactions."""
2
+
3
+ from salestax import SalesTaxClient
4
+
5
+ transactions = [
6
+ {"zipCode": "90210", "amount": 100},
7
+ {"zipCode": "10001", "amount": 250},
8
+ {"zipCode": "60601", "amount": 75.50},
9
+ ]
10
+
11
+ with SalesTaxClient() as client:
12
+ result = client.tax.calculate_batch(
13
+ transactions, idempotency_key="order-2026-0922-001"
14
+ )
15
+
16
+ for i, calc in enumerate(result["results"]):
17
+ print(f"#{i}: tax={calc['taxAmount']:.2f} total={calc['totalAmount']:.2f}")
@@ -0,0 +1,18 @@
1
+ """Catch and branch on specific error types."""
2
+
3
+ from salestax import (
4
+ RateLimitError,
5
+ SalesTaxClient,
6
+ TimeoutError,
7
+ ValidationError,
8
+ )
9
+
10
+ with SalesTaxClient() as client:
11
+ try:
12
+ client.tax.calculate(zip_code="90210", amount=100)
13
+ except RateLimitError as err:
14
+ print(f"Rate limited. Retry in {err.retry_after_ms}ms. req={err.request_id}")
15
+ except ValidationError as err:
16
+ print(f"Bad input on `{err.param}`: {err.message}")
17
+ except TimeoutError:
18
+ print("Request timed out — safe to retry.")
@@ -0,0 +1,111 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.21"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "salestax-python"
7
+ version = "0.1.0"
8
+ description = "Official Python SDK for Sales Tax Calculator API — real-time sales tax across 70+ countries, 51 US jurisdictions, and batch processing."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "InstaBlick USA", email = "dev@salestaxcalculatorapi.com" }]
13
+ keywords = [
14
+ "sales-tax", "tax-api", "ecommerce", "checkout",
15
+ "python", "api-client", "vat", "gst", "async"
16
+ ]
17
+ classifiers = [
18
+ "Development Status :: 4 - Beta",
19
+ "Intended Audience :: Developers",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Topic :: Office/Business :: Financial :: Accounting",
27
+ "Topic :: Software Development :: Libraries :: Python Modules",
28
+ "Typing :: Typed",
29
+ ]
30
+
31
+ [project.urls]
32
+ Homepage = "https://salestaxcalculatorapi.com"
33
+ Documentation = "https://salestaxcalculatorapi.com/docs"
34
+ Repository = "https://github.com/InstaBlick-USA/salestax-python"
35
+ Issues = "https://github.com/InstaBlick-USA/salestax-python/issues"
36
+ Changelog = "https://github.com/InstaBlick-USA/salestax-python/blob/main/CHANGELOG.md"
37
+
38
+ [project.optional-dependencies]
39
+ async = ["httpx>=0.27"]
40
+ dev = [
41
+ "pytest>=8.0",
42
+ "pytest-asyncio>=0.23",
43
+ "pytest-cov>=5.0",
44
+ "respx>=0.21",
45
+ "mypy>=1.9",
46
+ "ruff>=0.4",
47
+ "build>=1.2",
48
+ "twine>=5.0",
49
+ ]
50
+
51
+ [tool.hatch.build.targets.wheel]
52
+ packages = ["salestax"]
53
+
54
+ [tool.hatch.build.targets.sdist]
55
+ include = [
56
+ "salestax",
57
+ "tests",
58
+ "examples",
59
+ "README.md",
60
+ "CHANGELOG.md",
61
+ "LICENSE",
62
+ ]
63
+
64
+ [tool.ruff]
65
+ line-length = 100
66
+ target-version = "py39"
67
+ src = ["salestax", "tests"]
68
+
69
+ [tool.ruff.lint]
70
+ select = ["E", "F", "I", "N", "UP", "B", "C4", "SIM", "RUF"]
71
+ ignore = ["E501"]
72
+
73
+ [tool.ruff.lint.isort]
74
+ known-first-party = ["salestax"]
75
+
76
+ [tool.mypy]
77
+ python_version = "3.10"
78
+ strict = true
79
+ warn_unused_ignores = true
80
+ warn_redundant_casts = true
81
+ disallow_untyped_defs = true
82
+ disallow_incomplete_defs = true
83
+ no_implicit_optional = true
84
+ show_error_codes = true
85
+ files = ["salestax"]
86
+
87
+ [[tool.mypy.overrides]]
88
+ module = ["httpx.*"]
89
+ ignore_missing_imports = true
90
+
91
+ [tool.pytest.ini_options]
92
+ testpaths = ["tests"]
93
+ addopts = "-ra --strict-markers"
94
+ asyncio_mode = "auto"
95
+ markers = [
96
+ "integration: marks tests that hit the live API (deselect with '-m \"not integration\"')",
97
+ ]
98
+
99
+ [tool.coverage.run]
100
+ source = ["salestax"]
101
+ branch = true
102
+ omit = ["salestax/_version.py"]
103
+
104
+ [tool.coverage.report]
105
+ fail_under = 90
106
+ show_missing = true
107
+ exclude_lines = [
108
+ "pragma: no cover",
109
+ "if TYPE_CHECKING:",
110
+ "raise NotImplementedError",
111
+ ]
@@ -0,0 +1,61 @@
1
+ """Official Python SDK for the Sales Tax Calculator API.
2
+
3
+ Real-time sales tax for 70+ countries, 51 US jurisdictions, and 13 Canadian
4
+ provinces. Batch up to 100 transactions per call.
5
+
6
+ Docs: https://salestaxcalculatorapi.com/docs
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from ._config import ClientOptions, RetryPolicy
12
+ from ._version import __version__
13
+ from .client import AsyncSalesTaxClient, SalesTaxClient
14
+ from .errors import (
15
+ ApiError,
16
+ AuthenticationError,
17
+ ConflictError,
18
+ ConnectionError,
19
+ NotFoundError,
20
+ PermissionError,
21
+ RateLimitError,
22
+ SalesTaxError,
23
+ ServerError,
24
+ TimeoutError,
25
+ ValidationError,
26
+ )
27
+ from .models import (
28
+ BatchResult,
29
+ CalculateTaxParams,
30
+ Jurisdiction,
31
+ JurisdictionQuery,
32
+ TaxBreakdownEntry,
33
+ TaxCalculation,
34
+ TaxRate,
35
+ )
36
+
37
+ __all__ = [
38
+ "ApiError",
39
+ "AsyncSalesTaxClient",
40
+ "AuthenticationError",
41
+ "BatchResult",
42
+ "CalculateTaxParams",
43
+ "ClientOptions",
44
+ "ConflictError",
45
+ "ConnectionError",
46
+ "Jurisdiction",
47
+ "JurisdictionQuery",
48
+ "NotFoundError",
49
+ "PermissionError",
50
+ "RateLimitError",
51
+ "RetryPolicy",
52
+ "SalesTaxClient",
53
+ "SalesTaxError",
54
+ "ServerError",
55
+ "TaxBreakdownEntry",
56
+ "TaxCalculation",
57
+ "TaxRate",
58
+ "TimeoutError",
59
+ "ValidationError",
60
+ "__version__",
61
+ ]
@@ -0,0 +1,53 @@
1
+ """Configuration and defaults for the SDK."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from dataclasses import dataclass, field, replace
7
+
8
+ DEFAULT_BASE_URL = "https://api.salestaxcalculatorapi.com/v1"
9
+ DEFAULT_TIMEOUT_MS = 30_000
10
+ MAX_BATCH_SIZE = 100
11
+ ENV_API_KEY = "SALESTAX_API_KEY"
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class RetryPolicy:
16
+ """Retry behavior for transient failures.
17
+
18
+ Attributes:
19
+ max_retries: Number of retries *after* the initial attempt.
20
+ initial_delay_ms: Base delay for the first retry.
21
+ max_delay_ms: Ceiling for any single delay.
22
+ backoff_factor: Exponential multiplier applied per attempt.
23
+ jitter: Apply full jitter to each computed delay.
24
+ """
25
+
26
+ max_retries: int = 2
27
+ initial_delay_ms: int = 250
28
+ max_delay_ms: int = 8_000
29
+ backoff_factor: float = 2.0
30
+ jitter: bool = True
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class ClientOptions:
35
+ """Immutable options container. Prefer passing keyword args to the client."""
36
+
37
+ api_key: str | None = None
38
+ base_url: str = DEFAULT_BASE_URL
39
+ timeout_ms: int = DEFAULT_TIMEOUT_MS
40
+ retry: RetryPolicy = field(default_factory=RetryPolicy)
41
+ default_headers: dict[str, str] = field(default_factory=dict)
42
+ chunk_batch: bool = False
43
+
44
+ def resolved_api_key(self) -> str:
45
+ key = self.api_key or os.environ.get(ENV_API_KEY)
46
+ if not key:
47
+ raise ValueError(
48
+ f"Missing API key. Pass api_key=... or set the {ENV_API_KEY} environment variable."
49
+ )
50
+ return key
51
+
52
+ def with_overrides(self, **kwargs: object) -> ClientOptions:
53
+ return replace(self, **kwargs) # type: ignore[arg-type]
@@ -0,0 +1,18 @@
1
+ from .http_client import AsyncHttpClient, HttpClient
2
+ from .retry import compute_delay_ms, parse_retry_after
3
+ from .types import AsyncTransport, Hooks, RequestOptions, SyncTransport
4
+ from .urllib_transport import UrllibTransport
5
+ from .user_agent import build_user_agent
6
+
7
+ __all__ = [
8
+ "AsyncHttpClient",
9
+ "AsyncTransport",
10
+ "Hooks",
11
+ "HttpClient",
12
+ "RequestOptions",
13
+ "SyncTransport",
14
+ "UrllibTransport",
15
+ "build_user_agent",
16
+ "compute_delay_ms",
17
+ "parse_retry_after",
18
+ ]