shipzil 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.
- shipzil-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +84 -0
- shipzil-0.1.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
- shipzil-0.1.0/.github/ISSUE_TEMPLATE/provider_support.yml +50 -0
- shipzil-0.1.0/.github/dependabot.yml +37 -0
- shipzil-0.1.0/.github/pull_request_template.md +29 -0
- shipzil-0.1.0/.github/workflows/ci.yml +48 -0
- shipzil-0.1.0/.github/workflows/docs.yml +67 -0
- shipzil-0.1.0/.github/workflows/release.yml +78 -0
- shipzil-0.1.0/.gitignore +22 -0
- shipzil-0.1.0/.python-version +1 -0
- shipzil-0.1.0/AGENTS.md +85 -0
- shipzil-0.1.0/CHANGELOG.md +77 -0
- shipzil-0.1.0/CONTRIBUTING.md +115 -0
- shipzil-0.1.0/LICENSE +21 -0
- shipzil-0.1.0/Makefile +48 -0
- shipzil-0.1.0/PKG-INFO +260 -0
- shipzil-0.1.0/PLAN.md +49 -0
- shipzil-0.1.0/README.md +236 -0
- shipzil-0.1.0/SECURITY.md +44 -0
- shipzil-0.1.0/UPGRADE_GUIDE.md +7 -0
- shipzil-0.1.0/assets/shipzil-mark.svg +19 -0
- shipzil-0.1.0/docs-site/.gitignore +31 -0
- shipzil-0.1.0/docs-site/README.md +33 -0
- shipzil-0.1.0/docs-site/app/(home)/layout.tsx +18 -0
- shipzil-0.1.0/docs-site/app/(home)/page.tsx +205 -0
- shipzil-0.1.0/docs-site/app/api/search/route.ts +9 -0
- shipzil-0.1.0/docs-site/app/docs/[[...slug]]/page.tsx +63 -0
- shipzil-0.1.0/docs-site/app/docs/layout.tsx +11 -0
- shipzil-0.1.0/docs-site/app/global.css +37 -0
- shipzil-0.1.0/docs-site/app/icon.svg +21 -0
- shipzil-0.1.0/docs-site/app/layout.tsx +55 -0
- shipzil-0.1.0/docs-site/app/llms-full.txt/route.ts +10 -0
- shipzil-0.1.0/docs-site/app/llms.mdx/docs/[[...slug]]/route.ts +23 -0
- shipzil-0.1.0/docs-site/app/llms.txt/route.ts +12 -0
- shipzil-0.1.0/docs-site/app/og/docs/[...slug]/route.tsx +110 -0
- shipzil-0.1.0/docs-site/components/logo.tsx +39 -0
- shipzil-0.1.0/docs-site/components/mdx.tsx +17 -0
- shipzil-0.1.0/docs-site/components/provider.tsx +8 -0
- shipzil-0.1.0/docs-site/components/search.tsx +38 -0
- shipzil-0.1.0/docs-site/content/docs/concepts.md +162 -0
- shipzil-0.1.0/docs-site/content/docs/errors.md +164 -0
- shipzil-0.1.0/docs-site/content/docs/index.md +99 -0
- shipzil-0.1.0/docs-site/content/docs/international.md +134 -0
- shipzil-0.1.0/docs-site/content/docs/meta.json +16 -0
- shipzil-0.1.0/docs-site/content/docs/provider-sources.md +55 -0
- shipzil-0.1.0/docs-site/content/docs/providers.md +121 -0
- shipzil-0.1.0/docs-site/content/docs/quickstart.md +144 -0
- shipzil-0.1.0/docs-site/content/docs/reference.md +296 -0
- shipzil-0.1.0/docs-site/content/docs/research.md +51 -0
- shipzil-0.1.0/docs-site/content/docs/roadmap.md +56 -0
- shipzil-0.1.0/docs-site/lib/cn.ts +1 -0
- shipzil-0.1.0/docs-site/lib/layout.shared.tsx +17 -0
- shipzil-0.1.0/docs-site/lib/shared.ts +12 -0
- shipzil-0.1.0/docs-site/lib/source.ts +53 -0
- shipzil-0.1.0/docs-site/next.config.mjs +16 -0
- shipzil-0.1.0/docs-site/package-lock.json +7526 -0
- shipzil-0.1.0/docs-site/package.json +38 -0
- shipzil-0.1.0/docs-site/postcss.config.mjs +7 -0
- shipzil-0.1.0/docs-site/scripts/generate-python-api.mjs +201 -0
- shipzil-0.1.0/docs-site/tsconfig.json +34 -0
- shipzil-0.1.0/examples/README.md +16 -0
- shipzil-0.1.0/examples/gateway.py +69 -0
- shipzil-0.1.0/pyproject.toml +68 -0
- shipzil-0.1.0/shipzil/__init__.py +82 -0
- shipzil-0.1.0/shipzil/_client.py +189 -0
- shipzil-0.1.0/shipzil/errors.py +92 -0
- shipzil-0.1.0/shipzil/gateway.py +566 -0
- shipzil-0.1.0/shipzil/http.py +264 -0
- shipzil-0.1.0/shipzil/models.py +656 -0
- shipzil-0.1.0/shipzil/multiparcel.py +170 -0
- shipzil-0.1.0/shipzil/normalize.py +71 -0
- shipzil-0.1.0/shipzil/providers/__init__.py +41 -0
- shipzil-0.1.0/shipzil/providers/base.py +398 -0
- shipzil-0.1.0/shipzil/providers/easyship.py +572 -0
- shipzil-0.1.0/shipzil/providers/shippo.py +457 -0
- shipzil-0.1.0/shipzil/providers/shipstation_v1.py +506 -0
- shipzil-0.1.0/shipzil/providers/shipstation_v2.py +448 -0
- shipzil-0.1.0/shipzil/py.typed +0 -0
- shipzil-0.1.0/shipzil/services.py +357 -0
- shipzil-0.1.0/shipzil/units.py +108 -0
- shipzil-0.1.0/tests/fixtures/README.md +35 -0
- shipzil-0.1.0/tests/fixtures/es_label.json +238 -0
- shipzil-0.1.0/tests/fixtures/es_rates_multi.json +28 -0
- shipzil-0.1.0/tests/fixtures/es_rates_single.json +402 -0
- shipzil-0.1.0/tests/fixtures/shippo_multiparcel.json +181 -0
- shipzil-0.1.0/tests/fixtures/shippo_single.json +482 -0
- shipzil-0.1.0/tests/fixtures/ss1_carriers.json +22 -0
- shipzil-0.1.0/tests/fixtures/ss1_rates_single.json +116 -0
- shipzil-0.1.0/tests/fixtures/ss1_testlabel.json +33 -0
- shipzil-0.1.0/tests/fixtures/ss2_carriers.json +792 -0
- shipzil-0.1.0/tests/fixtures/ss2_rates_multi.json +642 -0
- shipzil-0.1.0/tests/fixtures/ss2_rates_single.json +1720 -0
- shipzil-0.1.0/tests/test_gateway.py +570 -0
- shipzil-0.1.0/tests/test_http.py +233 -0
- shipzil-0.1.0/tests/test_logistics.py +1072 -0
- shipzil-0.1.0/tests/test_real_payloads.py +542 -0
- shipzil-0.1.0/tests/test_shipzil.py +594 -0
- shipzil-0.1.0/tests/transport.py +68 -0
- shipzil-0.1.0/uv.lock +469 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something behaves differently from the documented contract.
|
|
3
|
+
labels: [bug]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: |
|
|
8
|
+
Please do not include live credentials, real customer addresses or real
|
|
9
|
+
label data. Sanitize provider payloads the way `tests/fixtures/README.md`
|
|
10
|
+
describes.
|
|
11
|
+
|
|
12
|
+
For a security issue, use a
|
|
13
|
+
[private advisory](https://github.com/sameerkumar18/shipzil/security/advisories/new)
|
|
14
|
+
instead.
|
|
15
|
+
|
|
16
|
+
- type: input
|
|
17
|
+
id: version
|
|
18
|
+
attributes:
|
|
19
|
+
label: shipzil version or commit
|
|
20
|
+
placeholder: "0.1.0, or the commit SHA from your lockfile"
|
|
21
|
+
validations:
|
|
22
|
+
required: true
|
|
23
|
+
|
|
24
|
+
- type: dropdown
|
|
25
|
+
id: provider
|
|
26
|
+
attributes:
|
|
27
|
+
label: Provider
|
|
28
|
+
options:
|
|
29
|
+
- shippo
|
|
30
|
+
- easyship
|
|
31
|
+
- shipstation_v1
|
|
32
|
+
- shipstation_v2
|
|
33
|
+
- more than one
|
|
34
|
+
- not provider specific
|
|
35
|
+
validations:
|
|
36
|
+
required: true
|
|
37
|
+
|
|
38
|
+
- type: dropdown
|
|
39
|
+
id: operation
|
|
40
|
+
attributes:
|
|
41
|
+
label: Operation
|
|
42
|
+
options:
|
|
43
|
+
- rating
|
|
44
|
+
- purchase
|
|
45
|
+
- void or refund
|
|
46
|
+
- filtering or service keys
|
|
47
|
+
- customs or international
|
|
48
|
+
- other
|
|
49
|
+
validations:
|
|
50
|
+
required: true
|
|
51
|
+
|
|
52
|
+
- type: textarea
|
|
53
|
+
id: expected
|
|
54
|
+
attributes:
|
|
55
|
+
label: What you expected, and where that is documented
|
|
56
|
+
description: Link the docs page or quote the contract, so we can tell a bug from a missing feature.
|
|
57
|
+
validations:
|
|
58
|
+
required: true
|
|
59
|
+
|
|
60
|
+
- type: textarea
|
|
61
|
+
id: actual
|
|
62
|
+
attributes:
|
|
63
|
+
label: What happened
|
|
64
|
+
description: Include `quote.explain()` output where relevant, and the exception type and message.
|
|
65
|
+
render: text
|
|
66
|
+
validations:
|
|
67
|
+
required: true
|
|
68
|
+
|
|
69
|
+
- type: textarea
|
|
70
|
+
id: repro
|
|
71
|
+
attributes:
|
|
72
|
+
label: Reproduction
|
|
73
|
+
description: The smallest snippet that shows it. Replace credentials with placeholders.
|
|
74
|
+
render: python
|
|
75
|
+
validations:
|
|
76
|
+
required: true
|
|
77
|
+
|
|
78
|
+
- type: input
|
|
79
|
+
id: python
|
|
80
|
+
attributes:
|
|
81
|
+
label: Python version
|
|
82
|
+
placeholder: "3.12.4"
|
|
83
|
+
validations:
|
|
84
|
+
required: true
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
blank_issues_enabled: true
|
|
2
|
+
contact_links:
|
|
3
|
+
- name: Documentation
|
|
4
|
+
url: https://sameerkumar18.github.io/shipzil/
|
|
5
|
+
about: Concepts, provider support, errors and the API reference.
|
|
6
|
+
- name: Security issue
|
|
7
|
+
url: https://github.com/sameerkumar18/shipzil/security/advisories/new
|
|
8
|
+
about: Report credential handling, duplicate purchase or declaration issues privately.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Provider support
|
|
2
|
+
description: Add a provider, or report a gap in an existing adapter.
|
|
3
|
+
labels: [provider]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: |
|
|
8
|
+
Adapter changes need evidence from the provider's current schema, not its
|
|
9
|
+
prose documentation. See CONTRIBUTING.md.
|
|
10
|
+
|
|
11
|
+
- type: input
|
|
12
|
+
id: provider
|
|
13
|
+
attributes:
|
|
14
|
+
label: Provider
|
|
15
|
+
placeholder: "Sendcloud, or shipstation_v2"
|
|
16
|
+
validations:
|
|
17
|
+
required: true
|
|
18
|
+
|
|
19
|
+
- type: dropdown
|
|
20
|
+
id: kind
|
|
21
|
+
attributes:
|
|
22
|
+
label: Request
|
|
23
|
+
options:
|
|
24
|
+
- a new provider adapter
|
|
25
|
+
- a field or operation an existing adapter does not send
|
|
26
|
+
- a provider behaviour shipzil reports incorrectly
|
|
27
|
+
validations:
|
|
28
|
+
required: true
|
|
29
|
+
|
|
30
|
+
- type: textarea
|
|
31
|
+
id: evidence
|
|
32
|
+
attributes:
|
|
33
|
+
label: Schema evidence
|
|
34
|
+
description: |
|
|
35
|
+
Link the provider's schema or model page for the fields involved, and quote
|
|
36
|
+
the relevant part. A prose example is not enough; field nesting and
|
|
37
|
+
required values have to come from the schema.
|
|
38
|
+
validations:
|
|
39
|
+
required: true
|
|
40
|
+
|
|
41
|
+
- type: textarea
|
|
42
|
+
id: semantics
|
|
43
|
+
attributes:
|
|
44
|
+
label: Semantics that differ from the current adapters
|
|
45
|
+
description: |
|
|
46
|
+
Especially: is a customs value per unit or a line total, is multi-parcel
|
|
47
|
+
rating native, how is duty liability expressed, and is there a no-charge
|
|
48
|
+
test path for purchases.
|
|
49
|
+
validations:
|
|
50
|
+
required: false
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
updates:
|
|
4
|
+
# Python: runtime has no dependencies, so this tracks the dev toolchain only.
|
|
5
|
+
- package-ecosystem: uv
|
|
6
|
+
directory: /
|
|
7
|
+
schedule:
|
|
8
|
+
interval: monthly
|
|
9
|
+
open-pull-requests-limit: 3
|
|
10
|
+
commit-message:
|
|
11
|
+
prefix: "deps"
|
|
12
|
+
|
|
13
|
+
- package-ecosystem: npm
|
|
14
|
+
directory: /docs-site
|
|
15
|
+
schedule:
|
|
16
|
+
interval: monthly
|
|
17
|
+
open-pull-requests-limit: 3
|
|
18
|
+
commit-message:
|
|
19
|
+
prefix: "deps(docs)"
|
|
20
|
+
groups:
|
|
21
|
+
fumadocs:
|
|
22
|
+
patterns:
|
|
23
|
+
- "fumadocs*"
|
|
24
|
+
next:
|
|
25
|
+
patterns:
|
|
26
|
+
- "next"
|
|
27
|
+
- "react"
|
|
28
|
+
- "react-dom"
|
|
29
|
+
- "@types/react*"
|
|
30
|
+
|
|
31
|
+
# setup-uv has no moving major tag, so a pinned version needs updating here.
|
|
32
|
+
- package-ecosystem: github-actions
|
|
33
|
+
directory: /
|
|
34
|
+
schedule:
|
|
35
|
+
interval: monthly
|
|
36
|
+
commit-message:
|
|
37
|
+
prefix: "ci"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
## What changes
|
|
2
|
+
|
|
3
|
+
<!-- The behaviour a caller can observe, not the diff. -->
|
|
4
|
+
|
|
5
|
+
## Evidence
|
|
6
|
+
|
|
7
|
+
<!-- For a provider change: link the provider's current schema or model page for
|
|
8
|
+
every field involved. A prose example is not sufficient. -->
|
|
9
|
+
|
|
10
|
+
- [ ] Request fields verified against the provider's current schema
|
|
11
|
+
- [ ] Payload test proves the value reaches the outgoing request
|
|
12
|
+
- [ ] Parser test uses a sanitized provider response, or the constructed case is
|
|
13
|
+
marked synthetic
|
|
14
|
+
- [ ] Regression test checked by restoring the bug and watching it fail
|
|
15
|
+
|
|
16
|
+
## Safety
|
|
17
|
+
|
|
18
|
+
- [ ] `retries=0` on anything that spends money
|
|
19
|
+
- [ ] No fabricated dimensions, contents, customs values or company names
|
|
20
|
+
- [ ] `Quote.excluded` explains anything shipzil removed
|
|
21
|
+
- [ ] Docs updated where behaviour changed
|
|
22
|
+
|
|
23
|
+
## Verification
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
make check
|
|
27
|
+
make check-compat
|
|
28
|
+
make test-live # state which live tests ran, and against which provider
|
|
29
|
+
```
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- "shipzil/**"
|
|
8
|
+
- "tests/**"
|
|
9
|
+
- "pyproject.toml"
|
|
10
|
+
- "uv.lock"
|
|
11
|
+
- ".github/workflows/ci.yml"
|
|
12
|
+
pull_request:
|
|
13
|
+
paths:
|
|
14
|
+
- "shipzil/**"
|
|
15
|
+
- "tests/**"
|
|
16
|
+
- "pyproject.toml"
|
|
17
|
+
- "uv.lock"
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
quality:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v7
|
|
27
|
+
# setup-uv publishes no moving major tag, so this is pinned exactly.
|
|
28
|
+
- uses: astral-sh/setup-uv@v10.1.0
|
|
29
|
+
with:
|
|
30
|
+
enable-cache: true
|
|
31
|
+
- run: uv sync --python 3.14
|
|
32
|
+
- run: uv run ruff check shipzil tests
|
|
33
|
+
- run: uv run mypy shipzil
|
|
34
|
+
- run: uv run pytest -m "not live"
|
|
35
|
+
|
|
36
|
+
compatibility:
|
|
37
|
+
strategy:
|
|
38
|
+
fail-fast: false
|
|
39
|
+
matrix:
|
|
40
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v7
|
|
44
|
+
# setup-uv publishes no moving major tag, so this is pinned exactly.
|
|
45
|
+
- uses: astral-sh/setup-uv@v10.1.0
|
|
46
|
+
with:
|
|
47
|
+
enable-cache: true
|
|
48
|
+
- run: uv run --python ${{ matrix.python-version }} --isolated --with pytest pytest -m "not live"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
name: docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- "docs-site/**"
|
|
8
|
+
- "shipzil/**"
|
|
9
|
+
- "AGENTS.md"
|
|
10
|
+
- "pyproject.toml"
|
|
11
|
+
- ".github/workflows/docs.yml"
|
|
12
|
+
pull_request:
|
|
13
|
+
paths:
|
|
14
|
+
- "docs-site/**"
|
|
15
|
+
- "shipzil/**"
|
|
16
|
+
- "AGENTS.md"
|
|
17
|
+
workflow_dispatch:
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
# Always build, on every branch and PR. Type generation, static generation and
|
|
24
|
+
# the agent-output checks all run before anything can be deployed.
|
|
25
|
+
build:
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v7
|
|
29
|
+
- uses: actions/setup-node@v7
|
|
30
|
+
with:
|
|
31
|
+
node-version: 22
|
|
32
|
+
cache: npm
|
|
33
|
+
cache-dependency-path: docs-site/package-lock.json
|
|
34
|
+
# setup-uv publishes no moving major tag, so this is pinned exactly.
|
|
35
|
+
- uses: astral-sh/setup-uv@v10.1.0
|
|
36
|
+
with:
|
|
37
|
+
enable-cache: true
|
|
38
|
+
- run: npm ci
|
|
39
|
+
working-directory: docs-site
|
|
40
|
+
- run: npm run types:check
|
|
41
|
+
working-directory: docs-site
|
|
42
|
+
- run: npm run build
|
|
43
|
+
working-directory: docs-site
|
|
44
|
+
- run: npm run check:agent
|
|
45
|
+
working-directory: docs-site
|
|
46
|
+
- uses: actions/upload-pages-artifact@v5
|
|
47
|
+
if: github.ref == 'refs/heads/main'
|
|
48
|
+
with:
|
|
49
|
+
path: docs-site/out
|
|
50
|
+
|
|
51
|
+
# Deploy only from main, and only once Pages is actually enabled. Pages on a
|
|
52
|
+
# private repository needs a paid plan, and deploy-pages returns 404 until the
|
|
53
|
+
# source is set to "GitHub Actions", which would fail every push.
|
|
54
|
+
# Enable with: gh variable set PAGES_ENABLED --body true
|
|
55
|
+
deploy:
|
|
56
|
+
if: github.ref == 'refs/heads/main' && vars.PAGES_ENABLED == 'true'
|
|
57
|
+
needs: build
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
permissions:
|
|
60
|
+
pages: write
|
|
61
|
+
id-token: write
|
|
62
|
+
environment:
|
|
63
|
+
name: github-pages
|
|
64
|
+
url: ${{ steps.deploy.outputs.page_url }}
|
|
65
|
+
steps:
|
|
66
|
+
- id: deploy
|
|
67
|
+
uses: actions/deploy-pages@v5
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Publishes to PyPI when a version tag is pushed.
|
|
4
|
+
#
|
|
5
|
+
# Uses PyPI Trusted Publishing (OIDC), so there is no API token in this repository.
|
|
6
|
+
# One-time setup on PyPI: add a trusted publisher for this repo with workflow
|
|
7
|
+
# name "release" and environment "pypi".
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
tags: ["v*"]
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
# Never publish something that does not pass its own suite.
|
|
18
|
+
verify:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
strategy:
|
|
21
|
+
fail-fast: true
|
|
22
|
+
matrix:
|
|
23
|
+
python-version: ["3.10", "3.14"]
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v7
|
|
26
|
+
# setup-uv publishes no moving major tag, so this is pinned exactly.
|
|
27
|
+
- uses: astral-sh/setup-uv@v10.1.0
|
|
28
|
+
with:
|
|
29
|
+
enable-cache: true
|
|
30
|
+
- run: uv run --python ${{ matrix.python-version }} --isolated --with pytest pytest -m "not live"
|
|
31
|
+
|
|
32
|
+
build:
|
|
33
|
+
needs: verify
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v7
|
|
37
|
+
# setup-uv publishes no moving major tag, so this is pinned exactly.
|
|
38
|
+
- uses: astral-sh/setup-uv@v10.1.0
|
|
39
|
+
|
|
40
|
+
- name: Fail if the tag does not match the package version
|
|
41
|
+
run: |
|
|
42
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
43
|
+
pkg="$(uv run python -c 'import shipzil; print(shipzil.__version__)')"
|
|
44
|
+
echo "tag=$tag package=$pkg"
|
|
45
|
+
if [ "$tag" != "$pkg" ]; then
|
|
46
|
+
echo "::error::tag $tag does not match shipzil.__version__ $pkg"
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
if: github.event_name == 'push'
|
|
50
|
+
|
|
51
|
+
- run: uv build
|
|
52
|
+
|
|
53
|
+
- name: Check the wheel ships what it must
|
|
54
|
+
run: |
|
|
55
|
+
uv run --with twine twine check dist/*
|
|
56
|
+
python -m zipfile -l dist/*.whl > contents.txt
|
|
57
|
+
for required in shipzil/gateway.py shipzil/services.py shipzil/py.typed; do
|
|
58
|
+
grep -q "$required" contents.txt || {
|
|
59
|
+
echo "::error::wheel is missing $required"; exit 1; }
|
|
60
|
+
done
|
|
61
|
+
|
|
62
|
+
- uses: actions/upload-artifact@v7
|
|
63
|
+
with:
|
|
64
|
+
name: dist
|
|
65
|
+
path: dist/
|
|
66
|
+
|
|
67
|
+
publish:
|
|
68
|
+
needs: build
|
|
69
|
+
runs-on: ubuntu-latest
|
|
70
|
+
environment: pypi
|
|
71
|
+
permissions:
|
|
72
|
+
id-token: write # required for Trusted Publishing
|
|
73
|
+
steps:
|
|
74
|
+
- uses: actions/download-artifact@v8
|
|
75
|
+
with:
|
|
76
|
+
name: dist
|
|
77
|
+
path: dist/
|
|
78
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
shipzil-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
.env
|
|
2
|
+
.venv/
|
|
3
|
+
__pycache__/
|
|
4
|
+
*.pyc
|
|
5
|
+
.pytest_cache/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
*.egg-info/
|
|
9
|
+
.probe/
|
|
10
|
+
|
|
11
|
+
# Scraped provider API docs. Reference only, never committed.
|
|
12
|
+
.apidocs/
|
|
13
|
+
|
|
14
|
+
# Fumadocs build and install output, never committed.
|
|
15
|
+
docs-site/node_modules/
|
|
16
|
+
docs-site/.next/
|
|
17
|
+
docs-site/out/
|
|
18
|
+
|
|
19
|
+
# Local build playbook — tricks, scrape recipes, credential inventory, open
|
|
20
|
+
# operational state. Deliberately never pushed. Must not move into the docs app.
|
|
21
|
+
NOTES.local.md
|
|
22
|
+
*.local.md
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
shipzil-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Agent guide
|
|
2
|
+
|
|
3
|
+
This repository is the Python shipping Gateway, not the separate Shipzil status
|
|
4
|
+
page repository.
|
|
5
|
+
|
|
6
|
+
## Current scope
|
|
7
|
+
|
|
8
|
+
Supported provider adapters:
|
|
9
|
+
|
|
10
|
+
- `shippo`
|
|
11
|
+
- `easyship`
|
|
12
|
+
- `shipstation_v1`
|
|
13
|
+
- `shipstation_v2`
|
|
14
|
+
|
|
15
|
+
`Gateway` sits above the existing single-provider `Client`:
|
|
16
|
+
|
|
17
|
+
- no provider preference means query all configured sources
|
|
18
|
+
- an explicit `fallback=(...)` is caller-authored policy
|
|
19
|
+
- provider, carrier and exact `ServiceKey` filters intersect
|
|
20
|
+
- source failures remain in `GatewayQuote.sources`
|
|
21
|
+
- every Gateway rate carries its configured `source`
|
|
22
|
+
- buying and voiding use that exact source
|
|
23
|
+
|
|
24
|
+
Do not add health-aware routing, cheapest-rate selection, service equivalence,
|
|
25
|
+
manifest generation or automatic purchase fallback. Those are not Gateway MVP
|
|
26
|
+
features.
|
|
27
|
+
|
|
28
|
+
## Code map
|
|
29
|
+
|
|
30
|
+
| Area | File | Role |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| internal source client | `shipzil/_client.py` | rate, buy, void for one adapter |
|
|
33
|
+
| Gateway | `shipzil/gateway.py` | multi-source aggregation and explicit filters |
|
|
34
|
+
| service map | `shipzil/services.py` | `ServiceKey`, `ServiceInfo`, `ServiceMap` |
|
|
35
|
+
| provider contract | `shipzil/providers/base.py` | adapter capabilities and fidelity gaps |
|
|
36
|
+
| provider adapters | `shipzil/providers/` | provider-specific requests/parsers |
|
|
37
|
+
| provider errors | `shipzil/normalize.py` | prose/code to `ExclusionCode` |
|
|
38
|
+
| live-shaped fixtures | `tests/fixtures/` | scrubbed provider responses |
|
|
39
|
+
| provider source guide | `docs-site/content/docs/provider-sources.md` | authoritative documentation URLs and verification rules |
|
|
40
|
+
|
|
41
|
+
## Invariants
|
|
42
|
+
|
|
43
|
+
- `Item.value` and `Item.weight` are per unit.
|
|
44
|
+
- Provider machine keys go into `ServiceKey.service`; display names stay on
|
|
45
|
+
`Rate.service`.
|
|
46
|
+
- `ServiceKey` is provider-namespaced. Do not infer that two providers' keys are
|
|
47
|
+
equivalent.
|
|
48
|
+
- `Rate.source` is the configured account/source that produced the rate. Never
|
|
49
|
+
purchase through another source.
|
|
50
|
+
- `Quote.excluded` retains provider-reported and local exclusions. It cannot explain
|
|
51
|
+
services the provider silently omitted.
|
|
52
|
+
- Purchases are never retried automatically.
|
|
53
|
+
- A fan-out rate is an estimate, not one provider-native multi-parcel quote, and
|
|
54
|
+
cannot be bought as one label.
|
|
55
|
+
- Do not add a provider capability to `hazmat_fields` unless the adapter emits
|
|
56
|
+
that field on the wire.
|
|
57
|
+
|
|
58
|
+
## Verification
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
make check
|
|
62
|
+
make check-compat
|
|
63
|
+
make docs-build
|
|
64
|
+
make test-live
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`make test-live` loads the ignored `.env`. Live calls can buy test labels or
|
|
68
|
+
consume sandbox quota; inspect the marker and provider before running them.
|
|
69
|
+
|
|
70
|
+
When changing a provider parser, use a sanitized provider response plus a
|
|
71
|
+
payload-level test. A constructed fixture is appropriate for an edge case absent
|
|
72
|
+
from the captured response.
|
|
73
|
+
|
|
74
|
+
## Provider documentation
|
|
75
|
+
|
|
76
|
+
Read `docs-site/content/docs/provider-sources.md` before changing provider
|
|
77
|
+
behavior. Verify claims against the provider's current schema or documentation and
|
|
78
|
+
then add a captured-response or payload-level test. Never commit `.env`, raw
|
|
79
|
+
credentials or unsanitized provider payloads.
|
|
80
|
+
|
|
81
|
+
## Style
|
|
82
|
+
|
|
83
|
+
Keep the public API small and Pythonic. Prefer a dataclass, tuple, mapping or
|
|
84
|
+
keyword argument over a hierarchy of policy/configuration classes. Use
|
|
85
|
+
`dataclasses.replace` when copying a model so new fields cannot silently vanish.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 - 2026-09-19
|
|
4
|
+
|
|
5
|
+
First published release. Alpha: the interface may change before 1.0.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Multi-source `Gateway` with direct credential configuration.
|
|
10
|
+
- Concurrent source rating and concurrent per-parcel FANOUT.
|
|
11
|
+
- Source provenance on rates and labels.
|
|
12
|
+
- Provider, carrier and exact-service filtering.
|
|
13
|
+
- `GatewayQuote` sequence operations, diagnostics, messages, `cheapest` and
|
|
14
|
+
`fastest`.
|
|
15
|
+
- Structured exclusions for locally filtered and unaddressable rates.
|
|
16
|
+
- Replaceable HTTP `Transport` protocol.
|
|
17
|
+
- Fumadocs site, generated Python reference and agent-readable Markdown routes.
|
|
18
|
+
- CI checks for Python 3.10 through 3.14 and a trusted-publishing release workflow.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- `get_rates(sources=...)` selects configured source names; `providers=` now selects
|
|
23
|
+
adapter types only. The single overloaded argument matched either, which hid a
|
|
24
|
+
caller's typo as an empty result. An unknown name in either raises
|
|
25
|
+
`ConfigurationError`.
|
|
26
|
+
- `max_spend` accepts `max_spend_currency`. A rate with no currency, or one in a
|
|
27
|
+
different currency, is refused rather than compared, because shipzil does not
|
|
28
|
+
convert money.
|
|
29
|
+
- Removed `Label.parcel_labels` and `TrackingLeg.piece`. No adapter populated
|
|
30
|
+
either, and a documented field that is always empty is a promise the library does
|
|
31
|
+
not keep.
|
|
32
|
+
|
|
33
|
+
- Minimum Python version is 3.10.
|
|
34
|
+
- `Gateway` is the caller entry point; the single-source client is internal.
|
|
35
|
+
- `Rate` and `Label` constructors are keyword-only.
|
|
36
|
+
- `Capabilities` is a frozen dataclass.
|
|
37
|
+
- `SourceResult` exposes rates, exclusions and messages directly.
|
|
38
|
+
- `cheapest` returns `None` for mixed or unknown currencies.
|
|
39
|
+
- Cross-border rating stops before a provider call when any item lacks weight or
|
|
40
|
+
value.
|
|
41
|
+
- Easyship requires explicit items and values; it no longer creates placeholder
|
|
42
|
+
merchandise or a default customs value.
|
|
43
|
+
- ShipStation v1 label bytes are available in `Label.label_data`.
|
|
44
|
+
- Purchase-path `ProviderError` becomes `AmbiguousPurchaseError`.
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- A rate list shortened by a transient carrier failure is now reported. Measured
|
|
49
|
+
against a Shippo test account on a US domestic lane, a rating call returns 11
|
|
50
|
+
rates normally and 3 when UPS answers "Hard: Too Many Requests". The reason was
|
|
51
|
+
present in the response messages and already classifiable, but no exclusion was
|
|
52
|
+
produced because the list was not empty, so a caller could lose 8 of 11 options
|
|
53
|
+
with no signal. Structural account and lane messages are still not promoted;
|
|
54
|
+
they are permanent and would be noise on every quote.
|
|
55
|
+
|
|
56
|
+
- Carrier family filters now match variants such as DHL Express and DHL eCommerce
|
|
57
|
+
without matching unrelated carriers.
|
|
58
|
+
- Rates removed by shipzil filters return an exclusion instead of disappearing.
|
|
59
|
+
- Provider warning messages survive Gateway aggregation.
|
|
60
|
+
- Provider counts, customs value basis and idempotency documentation were reconciled
|
|
61
|
+
with current source and provider schemas.
|
|
62
|
+
|
|
63
|
+
### Verified
|
|
64
|
+
|
|
65
|
+
- Live rating now covers all four adapters. Easyship runs against its sandbox host;
|
|
66
|
+
ShipStation v1 rating is read-only and confirmed live that it returns no currency
|
|
67
|
+
and no delivery estimate.
|
|
68
|
+
- Partial-shortfall reporting was checked in every adapter rather than assumed:
|
|
69
|
+
ShipStation v2 and v1 already reported failures alongside returned rates, and
|
|
70
|
+
Easyship's response carries no failure field to report.
|
|
71
|
+
|
|
72
|
+
- Offline tests cover all four adapters with sanitized responses and payload
|
|
73
|
+
assertions.
|
|
74
|
+
- Live rating covers Shippo and ShipStation v2, including a two-source Gateway
|
|
75
|
+
call with provenance checks.
|
|
76
|
+
- Shippo test purchase and refund are exercised with a `shippo_test_` token.
|
|
77
|
+
- ShipStation v2 purchase and void remain unverified live.
|