cborx 0.1.1__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.
- cborx-0.1.1/.github/CODEOWNERS +3 -0
- cborx-0.1.1/.github/dependabot.yml +16 -0
- cborx-0.1.1/.github/workflows/ci.yml +59 -0
- cborx-0.1.1/.github/workflows/codeql.yml +35 -0
- cborx-0.1.1/.github/workflows/dependency-review.yml +22 -0
- cborx-0.1.1/.github/workflows/release.yml +63 -0
- cborx-0.1.1/.github/workflows/scorecard.yml +42 -0
- cborx-0.1.1/.github/workflows/zizmor.yml +28 -0
- cborx-0.1.1/.gitignore +11 -0
- cborx-0.1.1/CHANGELOG.md +31 -0
- cborx-0.1.1/CONTRIBUTING.md +19 -0
- cborx-0.1.1/LICENSE +12 -0
- cborx-0.1.1/Makefile +16 -0
- cborx-0.1.1/PKG-INFO +86 -0
- cborx-0.1.1/README.md +65 -0
- cborx-0.1.1/SECURITY.md +6 -0
- cborx-0.1.1/pyproject.toml +108 -0
- cborx-0.1.1/src/cborx/__init__.py +28 -0
- cborx-0.1.1/src/cborx/_util.py +29 -0
- cborx-0.1.1/src/cborx/decoder.py +456 -0
- cborx-0.1.1/src/cborx/encoder.py +335 -0
- cborx-0.1.1/src/cborx/exceptions.py +14 -0
- cborx-0.1.1/src/cborx/py.typed +0 -0
- cborx-0.1.1/src/cborx/types.py +58 -0
- cborx-0.1.1/tests/__init__.py +1 -0
- cborx-0.1.1/tests/test_adversarial.py +285 -0
- cborx-0.1.1/tests/test_api.py +366 -0
- cborx-0.1.1/tests/test_fuzz.py +95 -0
- cborx-0.1.1/tests/test_readme_smoke.py +28 -0
- cborx-0.1.1/tests/test_rfc8949.py +217 -0
- cborx-0.1.1/tests/test_roundtrip.py +141 -0
- cborx-0.1.1/tests/util.py +50 -0
- cborx-0.1.1/uv.lock +863 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: github-actions
|
|
4
|
+
directory: /
|
|
5
|
+
schedule:
|
|
6
|
+
interval: weekly
|
|
7
|
+
cooldown:
|
|
8
|
+
default-days: 7
|
|
9
|
+
open-pull-requests-limit: 5
|
|
10
|
+
- package-ecosystem: uv
|
|
11
|
+
directory: /
|
|
12
|
+
schedule:
|
|
13
|
+
interval: weekly
|
|
14
|
+
cooldown:
|
|
15
|
+
default-days: 7
|
|
16
|
+
open-pull-requests-limit: 5
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
schedule:
|
|
8
|
+
- cron: "9 7 * * 1"
|
|
9
|
+
|
|
10
|
+
permissions: {}
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
17
|
+
with:
|
|
18
|
+
egress-policy: audit
|
|
19
|
+
|
|
20
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
21
|
+
with:
|
|
22
|
+
persist-credentials: false
|
|
23
|
+
|
|
24
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
25
|
+
with:
|
|
26
|
+
version: "0.11.32"
|
|
27
|
+
enable-cache: true
|
|
28
|
+
|
|
29
|
+
- run: uv sync --locked
|
|
30
|
+
|
|
31
|
+
- run: uv run ruff check .
|
|
32
|
+
- run: uv run ruff format --check .
|
|
33
|
+
- run: uv run bandit -r src/
|
|
34
|
+
- run: uv run mypy
|
|
35
|
+
- run: uvx ty check src tests
|
|
36
|
+
|
|
37
|
+
test:
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
strategy:
|
|
40
|
+
fail-fast: false
|
|
41
|
+
matrix:
|
|
42
|
+
python: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
43
|
+
steps:
|
|
44
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
45
|
+
with:
|
|
46
|
+
egress-policy: audit
|
|
47
|
+
|
|
48
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
49
|
+
with:
|
|
50
|
+
persist-credentials: false
|
|
51
|
+
|
|
52
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
53
|
+
with:
|
|
54
|
+
version: "0.11.32"
|
|
55
|
+
enable-cache: true
|
|
56
|
+
|
|
57
|
+
- run: uv python install ${{ matrix.python }}
|
|
58
|
+
- run: uv sync --locked --python ${{ matrix.python }}
|
|
59
|
+
- run: uv run pytest --cov
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: CodeQL
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
schedule:
|
|
8
|
+
- cron: "17 3 * * 4"
|
|
9
|
+
|
|
10
|
+
permissions: {}
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
analyze:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
actions: read
|
|
17
|
+
contents: read
|
|
18
|
+
security-events: write
|
|
19
|
+
steps:
|
|
20
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
21
|
+
with:
|
|
22
|
+
egress-policy: audit
|
|
23
|
+
|
|
24
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
25
|
+
with:
|
|
26
|
+
persist-credentials: false
|
|
27
|
+
|
|
28
|
+
- uses: github/codeql-action/init@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
|
|
29
|
+
with:
|
|
30
|
+
languages: python
|
|
31
|
+
queries: security-and-quality
|
|
32
|
+
|
|
33
|
+
- uses: github/codeql-action/analyze@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
|
|
34
|
+
with:
|
|
35
|
+
category: "/language:python"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Dependency Review
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
|
|
6
|
+
permissions: {}
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
dependency-review:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
steps:
|
|
14
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
15
|
+
with:
|
|
16
|
+
egress-policy: audit
|
|
17
|
+
|
|
18
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
19
|
+
with:
|
|
20
|
+
persist-credentials: false
|
|
21
|
+
|
|
22
|
+
- uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
permissions: {}
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
release:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
environment:
|
|
13
|
+
name: pypi
|
|
14
|
+
url: https://pypi.org/p/cborx
|
|
15
|
+
permissions:
|
|
16
|
+
contents: write
|
|
17
|
+
id-token: write
|
|
18
|
+
attestations: write
|
|
19
|
+
steps:
|
|
20
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
21
|
+
with:
|
|
22
|
+
egress-policy: audit
|
|
23
|
+
|
|
24
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
25
|
+
with:
|
|
26
|
+
persist-credentials: false
|
|
27
|
+
|
|
28
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
29
|
+
with:
|
|
30
|
+
version: "0.11.32"
|
|
31
|
+
|
|
32
|
+
- run: uv sync --locked
|
|
33
|
+
|
|
34
|
+
- name: Verify tag matches package version
|
|
35
|
+
run: |
|
|
36
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
37
|
+
version="$(uv run python -c 'import cborx; print(cborx.__version__)')"
|
|
38
|
+
if [ "$tag" != "$version" ]; then
|
|
39
|
+
echo "tag $tag does not match package version $version" >&2
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
- run: uv build
|
|
44
|
+
|
|
45
|
+
- name: Attest build provenance
|
|
46
|
+
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
|
|
47
|
+
with:
|
|
48
|
+
subject-path: dist/*
|
|
49
|
+
|
|
50
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
|
|
51
|
+
with:
|
|
52
|
+
attestations: true
|
|
53
|
+
|
|
54
|
+
- name: Create GitHub release
|
|
55
|
+
env:
|
|
56
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
57
|
+
run: |
|
|
58
|
+
gh release create "${GITHUB_REF_NAME}" \
|
|
59
|
+
--repo "${GITHUB_REPOSITORY}" \
|
|
60
|
+
--verify-tag \
|
|
61
|
+
--title "${GITHUB_REF_NAME}" \
|
|
62
|
+
--generate-notes \
|
|
63
|
+
dist/*
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: OpenSSF Scorecard
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
branch_protection_rule:
|
|
5
|
+
push:
|
|
6
|
+
branches: [master]
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
schedule:
|
|
9
|
+
- cron: "42 5 * * 6"
|
|
10
|
+
|
|
11
|
+
permissions: read-all
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
analysis:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
permissions:
|
|
17
|
+
security-events: write
|
|
18
|
+
id-token: write
|
|
19
|
+
steps:
|
|
20
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
21
|
+
with:
|
|
22
|
+
egress-policy: audit
|
|
23
|
+
|
|
24
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
25
|
+
with:
|
|
26
|
+
persist-credentials: false
|
|
27
|
+
|
|
28
|
+
- uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
|
|
29
|
+
with:
|
|
30
|
+
results_file: results.sarif
|
|
31
|
+
results_format: sarif
|
|
32
|
+
publish_results: true
|
|
33
|
+
|
|
34
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
35
|
+
with:
|
|
36
|
+
name: SARIF file
|
|
37
|
+
path: results.sarif
|
|
38
|
+
retention-days: 5
|
|
39
|
+
|
|
40
|
+
- uses: github/codeql-action/upload-sarif@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
|
|
41
|
+
with:
|
|
42
|
+
sarif_file: results.sarif
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: zizmor
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
paths: [.github/**]
|
|
7
|
+
pull_request:
|
|
8
|
+
paths: [.github/**]
|
|
9
|
+
|
|
10
|
+
permissions: {}
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
zizmor:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
security-events: write
|
|
17
|
+
steps:
|
|
18
|
+
- uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
|
19
|
+
with:
|
|
20
|
+
egress-policy: audit
|
|
21
|
+
|
|
22
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
23
|
+
with:
|
|
24
|
+
persist-credentials: false
|
|
25
|
+
|
|
26
|
+
- uses: zizmorcore/zizmor-action@cc914d7f3750a2d13d75c7f184a1060aa0e9d482 # v0.6.4
|
|
27
|
+
with:
|
|
28
|
+
advanced-security: false
|
cborx-0.1.1/.gitignore
ADDED
cborx-0.1.1/CHANGELOG.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.1] - Unreleased
|
|
4
|
+
|
|
5
|
+
Fix the release workflow's package-name placeholder. No library
|
|
6
|
+
changes.
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - Unreleased
|
|
9
|
+
|
|
10
|
+
Initial release.
|
|
11
|
+
|
|
12
|
+
- Full RFC 8949 codec: all major types, definite and indefinite
|
|
13
|
+
lengths, half/single/double floats, bignum tags for integers beyond
|
|
14
|
+
the 64-bit range.
|
|
15
|
+
- `dumps`, `loads`, `dump`, `load` plus `CBOREncoder` and
|
|
16
|
+
`CBORDecoder` classes.
|
|
17
|
+
- Deterministic encoding via `canonical=True` (RFC 8949 section 4.2:
|
|
18
|
+
shortest-form integers and floats, map keys ordered by encoded key
|
|
19
|
+
bytes) and strict canonical validation on decode.
|
|
20
|
+
- Built-in semantic tags 0, 1, 2, 3, 32, 1004 and 55799. `tag_hook`
|
|
21
|
+
for custom handling, `CBORTag` for unhandled tags.
|
|
22
|
+
- `CBORSimpleValue` for unassigned simple values and an `undefined`
|
|
23
|
+
sentinel.
|
|
24
|
+
- `default` callback for encoding unknown types, mirroring cbor2.
|
|
25
|
+
- Hardened iterative decoder: declared lengths are checked against
|
|
26
|
+
remaining input before use, nesting is bounded by a configurable
|
|
27
|
+
`max_depth`, and truncated or malformed input always raises
|
|
28
|
+
`CBORDecodeError`.
|
|
29
|
+
- Decode options: `strict_utf8`, `allow_indefinite`,
|
|
30
|
+
`duplicate_keys` ("last", "first", "error") and `allow_trailing`.
|
|
31
|
+
- Typed throughout, zero runtime dependencies.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Report bugs through GitHub issues. Security reports go to
|
|
4
|
+
security@quad4.io (see SECURITY.md).
|
|
5
|
+
|
|
6
|
+
## Setup
|
|
7
|
+
|
|
8
|
+
uv sync
|
|
9
|
+
make check
|
|
10
|
+
|
|
11
|
+
`make check` runs the full local gate: ruff lint and format, bandit,
|
|
12
|
+
mypy strict, ty, and pytest with coverage.
|
|
13
|
+
|
|
14
|
+
## Conventions
|
|
15
|
+
|
|
16
|
+
- No runtime dependencies unless the project genuinely needs one.
|
|
17
|
+
- Source files start with the SPDX license identifier.
|
|
18
|
+
- Public API changes need tests.
|
|
19
|
+
- Commit messages: short, imperative, formal.
|
cborx-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Copyright (c) 2026 Quad4
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
4
|
+
with or without fee is hereby granted.
|
|
5
|
+
|
|
6
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
7
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
8
|
+
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
9
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
10
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
11
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
12
|
+
PERFORMANCE OF THIS SOFTWARE.
|
cborx-0.1.1/Makefile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.PHONY: check lint test build
|
|
2
|
+
|
|
3
|
+
check: lint test
|
|
4
|
+
|
|
5
|
+
lint:
|
|
6
|
+
uv run ruff check .
|
|
7
|
+
uv run ruff format --check .
|
|
8
|
+
uv run bandit -r src/
|
|
9
|
+
uv run mypy
|
|
10
|
+
uvx ty check src tests
|
|
11
|
+
|
|
12
|
+
test:
|
|
13
|
+
uv run pytest --cov --cov-report=term-missing
|
|
14
|
+
|
|
15
|
+
build:
|
|
16
|
+
uv build
|
cborx-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: cborx
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Pure-Python CBOR (RFC 8949) encoder/decoder. No dependencies, no Rust.
|
|
5
|
+
Project-URL: Homepage, https://quad4.io
|
|
6
|
+
Project-URL: Repository, https://github.com/Quad4-Software/cborx
|
|
7
|
+
Project-URL: Issues, https://github.com/Quad4-Software/cborx/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/Quad4-Software/cborx/blob/master/CHANGELOG.md
|
|
9
|
+
Author: Quad4
|
|
10
|
+
License-Expression: 0BSD
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: binary,cbor,codec,rfc8949,serialization
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# cborx
|
|
23
|
+
|
|
24
|
+
[](https://github.com/Quad4-Software/cborx/actions/workflows/ci.yml)
|
|
25
|
+
[](https://github.com/Quad4-Software/cborx/actions/workflows/codeql.yml)
|
|
26
|
+
[](https://securityscorecards.dev/viewer/?uri=github.com/Quad4-Software/cborx)
|
|
27
|
+
[](https://pypi.org/project/cborx/)
|
|
28
|
+
[](LICENSE)
|
|
29
|
+
|
|
30
|
+
Pure-Python CBOR (RFC 8949) encoder/decoder. No dependencies, no Rust.
|
|
31
|
+
|
|
32
|
+
A hardened, fully typed replacement for cbor2 without a native
|
|
33
|
+
extension. The decoder is iterative, bounds every claimed length
|
|
34
|
+
against the remaining input, and enforces a configurable nesting
|
|
35
|
+
limit, so hostile input fails fast instead of exhausting memory or
|
|
36
|
+
the call stack.
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
pip install cborx
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import cborx
|
|
48
|
+
|
|
49
|
+
data = cborx.dumps({"a": [1, 2, 3], "b": None})
|
|
50
|
+
assert cborx.loads(data) == {"a": [1, 2, 3], "b": None}
|
|
51
|
+
|
|
52
|
+
# Deterministic encoding: shortest-form integers and floats, map keys
|
|
53
|
+
# sorted by encoded key bytes (RFC 8949 section 4.2).
|
|
54
|
+
canonical = cborx.dumps({"b": 1, "a": 2}, canonical=True)
|
|
55
|
+
|
|
56
|
+
# Strict canonical validation on decode, duplicate-key policy, depth
|
|
57
|
+
# and indefinite-length controls.
|
|
58
|
+
cborx.loads(data, canonical=True, duplicate_keys="error", max_depth=100)
|
|
59
|
+
|
|
60
|
+
# Unhandled semantic tags round-trip as CBORTag. A tag_hook overrides
|
|
61
|
+
# all built-in tag handling.
|
|
62
|
+
cborx.loads(b"\xd8\x2a\x01", tag_hook=lambda decoder, tag: (tag.tag, tag.value))
|
|
63
|
+
assert cborx.loads(cborx.dumps(cborx.CBORTag(42, "x"))) == cborx.CBORTag(42, "x")
|
|
64
|
+
|
|
65
|
+
# A default callback handles otherwise unencodable objects, like cbor2.
|
|
66
|
+
cborx.dumps(object(), default=lambda encoder, obj: {"type": type(obj).__name__})
|
|
67
|
+
|
|
68
|
+
# Simple values and the undefined sentinel.
|
|
69
|
+
assert cborx.loads(b"\xf7") is cborx.undefined
|
|
70
|
+
assert cborx.loads(b"\xf0") == cborx.CBORSimpleValue(16)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Built-in semantic tags: 0 (ISO 8601 datetime), 1 (epoch datetime),
|
|
74
|
+
2/3 (bignum integers beyond the 64-bit range), 32 (URI, decoded to a
|
|
75
|
+
plain str since Python has no URI scalar), 1004 (calendar date) and
|
|
76
|
+
55799 (self-described CBOR, passed through). All other tags decode to
|
|
77
|
+
CBORTag. See RFC 8949: https://www.rfc-editor.org/rfc/rfc8949
|
|
78
|
+
|
|
79
|
+
## Development
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
uv sync --group dev
|
|
83
|
+
make check
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
License: 0BSD. Quad4 Software, https://quad4.io
|
cborx-0.1.1/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# cborx
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Quad4-Software/cborx/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/Quad4-Software/cborx/actions/workflows/codeql.yml)
|
|
5
|
+
[](https://securityscorecards.dev/viewer/?uri=github.com/Quad4-Software/cborx)
|
|
6
|
+
[](https://pypi.org/project/cborx/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
Pure-Python CBOR (RFC 8949) encoder/decoder. No dependencies, no Rust.
|
|
10
|
+
|
|
11
|
+
A hardened, fully typed replacement for cbor2 without a native
|
|
12
|
+
extension. The decoder is iterative, bounds every claimed length
|
|
13
|
+
against the remaining input, and enforces a configurable nesting
|
|
14
|
+
limit, so hostile input fails fast instead of exhausting memory or
|
|
15
|
+
the call stack.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
pip install cborx
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
import cborx
|
|
27
|
+
|
|
28
|
+
data = cborx.dumps({"a": [1, 2, 3], "b": None})
|
|
29
|
+
assert cborx.loads(data) == {"a": [1, 2, 3], "b": None}
|
|
30
|
+
|
|
31
|
+
# Deterministic encoding: shortest-form integers and floats, map keys
|
|
32
|
+
# sorted by encoded key bytes (RFC 8949 section 4.2).
|
|
33
|
+
canonical = cborx.dumps({"b": 1, "a": 2}, canonical=True)
|
|
34
|
+
|
|
35
|
+
# Strict canonical validation on decode, duplicate-key policy, depth
|
|
36
|
+
# and indefinite-length controls.
|
|
37
|
+
cborx.loads(data, canonical=True, duplicate_keys="error", max_depth=100)
|
|
38
|
+
|
|
39
|
+
# Unhandled semantic tags round-trip as CBORTag. A tag_hook overrides
|
|
40
|
+
# all built-in tag handling.
|
|
41
|
+
cborx.loads(b"\xd8\x2a\x01", tag_hook=lambda decoder, tag: (tag.tag, tag.value))
|
|
42
|
+
assert cborx.loads(cborx.dumps(cborx.CBORTag(42, "x"))) == cborx.CBORTag(42, "x")
|
|
43
|
+
|
|
44
|
+
# A default callback handles otherwise unencodable objects, like cbor2.
|
|
45
|
+
cborx.dumps(object(), default=lambda encoder, obj: {"type": type(obj).__name__})
|
|
46
|
+
|
|
47
|
+
# Simple values and the undefined sentinel.
|
|
48
|
+
assert cborx.loads(b"\xf7") is cborx.undefined
|
|
49
|
+
assert cborx.loads(b"\xf0") == cborx.CBORSimpleValue(16)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Built-in semantic tags: 0 (ISO 8601 datetime), 1 (epoch datetime),
|
|
53
|
+
2/3 (bignum integers beyond the 64-bit range), 32 (URI, decoded to a
|
|
54
|
+
plain str since Python has no URI scalar), 1004 (calendar date) and
|
|
55
|
+
55799 (self-described CBOR, passed through). All other tags decode to
|
|
56
|
+
CBORTag. See RFC 8949: https://www.rfc-editor.org/rfc/rfc8949
|
|
57
|
+
|
|
58
|
+
## Development
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
uv sync --group dev
|
|
62
|
+
make check
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
License: 0BSD. Quad4 Software, https://quad4.io
|
cborx-0.1.1/SECURITY.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cborx"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Pure-Python CBOR (RFC 8949) encoder/decoder. No dependencies, no Rust."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "0BSD"
|
|
12
|
+
authors = [{ name = "Quad4" }]
|
|
13
|
+
keywords = ["cbor", "rfc8949", "serialization", "codec", "binary"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: BSD License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Typing :: Typed",
|
|
21
|
+
]
|
|
22
|
+
dependencies = []
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Homepage = "https://quad4.io"
|
|
26
|
+
Repository = "https://github.com/Quad4-Software/cborx"
|
|
27
|
+
Issues = "https://github.com/Quad4-Software/cborx/issues"
|
|
28
|
+
Changelog = "https://github.com/Quad4-Software/cborx/blob/master/CHANGELOG.md"
|
|
29
|
+
|
|
30
|
+
[tool.hatch.version]
|
|
31
|
+
path = "src/cborx/__init__.py"
|
|
32
|
+
|
|
33
|
+
[dependency-groups]
|
|
34
|
+
dev = [
|
|
35
|
+
"pytest>=9.1.1",
|
|
36
|
+
"pytest-cov>=7.1.0",
|
|
37
|
+
"ruff>=0.16.8",
|
|
38
|
+
"bandit>=1.9.4",
|
|
39
|
+
"mypy>=2.3.1",
|
|
40
|
+
"hypothesis>=6.168.0",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[tool.ruff]
|
|
44
|
+
target-version = "py310"
|
|
45
|
+
src = ["src", "tests"]
|
|
46
|
+
|
|
47
|
+
[tool.ruff.lint]
|
|
48
|
+
select = [
|
|
49
|
+
"E", "W", # pycodestyle
|
|
50
|
+
"F", # pyflakes
|
|
51
|
+
"I", # isort
|
|
52
|
+
"N", # pep8-naming
|
|
53
|
+
"UP", # pyupgrade
|
|
54
|
+
"YTT", # sys.version misuse
|
|
55
|
+
"FA", # future annotations
|
|
56
|
+
"A", # builtins shadowing
|
|
57
|
+
"B", # flake8-bugbear
|
|
58
|
+
"BLE", # blind except
|
|
59
|
+
"C4", # comprehensions
|
|
60
|
+
"C90", # mccabe complexity
|
|
61
|
+
"DTZ", # timezone-aware datetimes
|
|
62
|
+
"ERA", # commented-out code
|
|
63
|
+
"FURB", # refurb
|
|
64
|
+
"G", # logging format
|
|
65
|
+
"ICN", # import conventions
|
|
66
|
+
"ISC", # implicit string concat
|
|
67
|
+
"LOG", # logging
|
|
68
|
+
"PERF", # perflint
|
|
69
|
+
"PGH", # pygrep hooks
|
|
70
|
+
"PIE", # misc lints
|
|
71
|
+
"PT", # pytest style
|
|
72
|
+
"PTH", # pathlib
|
|
73
|
+
"RET", # return
|
|
74
|
+
"RSE", # raise/return
|
|
75
|
+
"S", # bandit rules
|
|
76
|
+
"SIM", # simplify
|
|
77
|
+
"SLOT", # __slots__
|
|
78
|
+
"T20", # print
|
|
79
|
+
"TRY", # exception handling
|
|
80
|
+
"RUF", # ruff-specific
|
|
81
|
+
]
|
|
82
|
+
ignore = [
|
|
83
|
+
"ISC001", # single-line implicit concat conflicts with the formatter
|
|
84
|
+
"TRY003", # long messages in raise calls are fine
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
[tool.ruff.lint.per-file-ignores]
|
|
88
|
+
"tests/*" = ["S101", "S603"]
|
|
89
|
+
|
|
90
|
+
[tool.mypy]
|
|
91
|
+
strict = true
|
|
92
|
+
files = ["src", "tests"]
|
|
93
|
+
|
|
94
|
+
[tool.bandit]
|
|
95
|
+
exclude_dirs = ["tests"]
|
|
96
|
+
|
|
97
|
+
[tool.coverage.run]
|
|
98
|
+
branch = true
|
|
99
|
+
source = ["src/cborx"]
|
|
100
|
+
|
|
101
|
+
[tool.coverage.report]
|
|
102
|
+
show_missing = true
|
|
103
|
+
fail_under = 90
|
|
104
|
+
|
|
105
|
+
[tool.pytest.ini_options]
|
|
106
|
+
testpaths = ["tests"]
|
|
107
|
+
xfail_strict = true
|
|
108
|
+
filterwarnings = ["error"]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# SPDX-License-Identifier: 0BSD
|
|
2
|
+
"""Pure-Python CBOR (RFC 8949) encoder/decoder. No dependencies, no Rust."""
|
|
3
|
+
|
|
4
|
+
from .decoder import CBORDecoder, DuplicateMode, TagHook, load, loads
|
|
5
|
+
from .encoder import CBOREncoder, DefaultCallback, dump, dumps
|
|
6
|
+
from .exceptions import CBORDecodeError, CBOREncodeError, CBORError
|
|
7
|
+
from .types import CBORSimpleValue, CBORTag, UndefinedType, undefined
|
|
8
|
+
|
|
9
|
+
__version__ = "0.1.1"
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"CBORDecodeError",
|
|
13
|
+
"CBORDecoder",
|
|
14
|
+
"CBOREncodeError",
|
|
15
|
+
"CBOREncoder",
|
|
16
|
+
"CBORError",
|
|
17
|
+
"CBORSimpleValue",
|
|
18
|
+
"CBORTag",
|
|
19
|
+
"DefaultCallback",
|
|
20
|
+
"DuplicateMode",
|
|
21
|
+
"TagHook",
|
|
22
|
+
"UndefinedType",
|
|
23
|
+
"dump",
|
|
24
|
+
"dumps",
|
|
25
|
+
"load",
|
|
26
|
+
"loads",
|
|
27
|
+
"undefined",
|
|
28
|
+
]
|