pyrjson 0.2.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.
- pyrjson-0.2.0/.cargo/config.toml +15 -0
- pyrjson-0.2.0/.github/dependabot.yml +24 -0
- pyrjson-0.2.0/.github/instructions/leader.instructions.md +62 -0
- pyrjson-0.2.0/.github/instructions/python-enginner.instructions.md +39 -0
- pyrjson-0.2.0/.github/instructions/rust-engineer.instructions.md +60 -0
- pyrjson-0.2.0/.github/workflows/ci.yml +160 -0
- pyrjson-0.2.0/.github/workflows/perf.yml +96 -0
- pyrjson-0.2.0/.github/workflows/wheels.yml +236 -0
- pyrjson-0.2.0/.gitignore +17 -0
- pyrjson-0.2.0/.python-version +1 -0
- pyrjson-0.2.0/.vscode/settings.json +7 -0
- pyrjson-0.2.0/AGENTS.md +29 -0
- pyrjson-0.2.0/CHANGELOG.md +97 -0
- pyrjson-0.2.0/CLAUDE.md +130 -0
- pyrjson-0.2.0/CONTRIBUTING.md +118 -0
- pyrjson-0.2.0/Cargo.lock +162 -0
- pyrjson-0.2.0/Cargo.toml +45 -0
- pyrjson-0.2.0/LICENSE +21 -0
- pyrjson-0.2.0/Makefile +59 -0
- pyrjson-0.2.0/PKG-INFO +419 -0
- pyrjson-0.2.0/README.md +383 -0
- pyrjson-0.2.0/SECURITY.md +45 -0
- pyrjson-0.2.0/benches/bulk_benchmark.py +113 -0
- pyrjson-0.2.0/benches/comprehensive_benchmark.py +227 -0
- pyrjson-0.2.0/benches/corpus_benchmark.py +160 -0
- pyrjson-0.2.0/benches/demo.py +74 -0
- pyrjson-0.2.0/benches/examples_benchmark.py +595 -0
- pyrjson-0.2.0/benches/fetch_corpus.sh +38 -0
- pyrjson-0.2.0/benches/large_benchmark.py +23 -0
- pyrjson-0.2.0/benches/make_charts.py +216 -0
- pyrjson-0.2.0/benches/make_demo_svg.py +75 -0
- pyrjson-0.2.0/benches/make_showcase_charts.py +261 -0
- pyrjson-0.2.0/benches/perf_gate.py +82 -0
- pyrjson-0.2.0/benches/prod_workloads.py +337 -0
- pyrjson-0.2.0/benches/production_benchmark.py +939 -0
- pyrjson-0.2.0/benches/profile_overhead.py +179 -0
- pyrjson-0.2.0/benches/python_benchmark.py +133 -0
- pyrjson-0.2.0/benches/showcase.py +367 -0
- pyrjson-0.2.0/build.rs +43 -0
- pyrjson-0.2.0/docs/ASYNC.md +190 -0
- pyrjson-0.2.0/docs/PERFORMANCE_REVIEW.md +267 -0
- pyrjson-0.2.0/docs/PRODUCTION_READINESS.md +404 -0
- pyrjson-0.2.0/docs/SHOWCASE.md +140 -0
- pyrjson-0.2.0/docs/examples-benchmark-results.json +1028 -0
- pyrjson-0.2.0/docs/guides/faster-json-in-python-services.md +120 -0
- pyrjson-0.2.0/docs/img/architecture-dark.svg +80 -0
- pyrjson-0.2.0/docs/img/architecture-light.svg +80 -0
- pyrjson-0.2.0/docs/img/benchmark-results.json +580 -0
- pyrjson-0.2.0/docs/img/demo.svg +15 -0
- pyrjson-0.2.0/docs/img/headline-dark.svg +19 -0
- pyrjson-0.2.0/docs/img/headline-light.svg +19 -0
- pyrjson-0.2.0/docs/img/showcase-dark.svg +86 -0
- pyrjson-0.2.0/docs/img/showcase-light.svg +86 -0
- pyrjson-0.2.0/docs/img/social-preview.png +0 -0
- pyrjson-0.2.0/docs/img/social-preview.svg +15 -0
- pyrjson-0.2.0/docs/img/vs-orjson-dark.svg +83 -0
- pyrjson-0.2.0/docs/img/vs-orjson-light.svg +83 -0
- pyrjson-0.2.0/docs/showcase-results.json +271 -0
- pyrjson-0.2.0/examples/README.md +122 -0
- pyrjson-0.2.0/examples/codec.py +545 -0
- pyrjson-0.2.0/examples/django_json.py +208 -0
- pyrjson-0.2.0/examples/fastapi_app.py +261 -0
- pyrjson-0.2.0/examples/flask_json.py +146 -0
- pyrjson-0.2.0/examples/json_logging.py +367 -0
- pyrjson-0.2.0/llms.txt +40 -0
- pyrjson-0.2.0/pyproject.toml +66 -0
- pyrjson-0.2.0/rjson.pyi +88 -0
- pyrjson-0.2.0/scripts/build_pgo.sh +87 -0
- pyrjson-0.2.0/scripts/ci_build_pgo_container.sh +41 -0
- pyrjson-0.2.0/scripts/pgo_train.py +253 -0
- pyrjson-0.2.0/scripts/run_tests.sh +21 -0
- pyrjson-0.2.0/scripts/test_aarch64_qemu.sh +33 -0
- pyrjson-0.2.0/src/compat.rs +100 -0
- pyrjson-0.2.0/src/entry.rs +523 -0
- pyrjson-0.2.0/src/lemire.rs +791 -0
- pyrjson-0.2.0/src/lib.rs +32 -0
- pyrjson-0.2.0/src/native.rs +805 -0
- pyrjson-0.2.0/src/parser.rs +2226 -0
- pyrjson-0.2.0/src/ser.rs +3761 -0
- pyrjson-0.2.0/src/stack.rs +164 -0
- pyrjson-0.2.0/tests/test_dumps.py +969 -0
- pyrjson-0.2.0/tests/test_examples.py +919 -0
- pyrjson-0.2.0/tests/test_format.py +175 -0
- pyrjson-0.2.0/tests/test_fuzz.py +246 -0
- pyrjson-0.2.0/tests/test_hardening.py +349 -0
- pyrjson-0.2.0/tests/test_keys.py +290 -0
- pyrjson-0.2.0/tests/test_lenient.py +303 -0
- pyrjson-0.2.0/tests/test_native.py +745 -0
- pyrjson-0.2.0/tests/test_rjson.py +1285 -0
- pyrjson-0.2.0/uv.lock +150 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Build configuration.
|
|
2
|
+
#
|
|
3
|
+
# x86_64 targets x86-64-v2 (SSE4.2/SSSE3/POPCNT, every x86_64 CPU since ~2009),
|
|
4
|
+
# like orjson's wheels. Do NOT use target-cpu=native: wheels built that way
|
|
5
|
+
# crash with SIGILL on older CPUs, and on the benchmark Xeon, BMI2 codegen
|
|
6
|
+
# (enabled by native / x86-64-v3) made zmij float formatting ~1.6x slower
|
|
7
|
+
# (33ns vs 20ns per float in dumps).
|
|
8
|
+
#
|
|
9
|
+
# One cfg entry covers every x86_64 target (linux-gnu, linux-musl, darwin,
|
|
10
|
+
# windows-msvc/gnu). Setting RUSTFLAGS in the environment silently replaces
|
|
11
|
+
# these flags; pass extra flags via CARGO_TARGET_<TRIPLE>_RUSTFLAGS, which
|
|
12
|
+
# cargo merges with them (scripts/build_pgo.sh does this).
|
|
13
|
+
|
|
14
|
+
[target.'cfg(target_arch = "x86_64")']
|
|
15
|
+
rustflags = ["-C", "target-cpu=x86-64-v2"]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Weekly update PRs. Actions are pinned to commit SHAs; Dependabot bumps the
|
|
2
|
+
# SHA and the version comment together. Grouped to keep the PR count low.
|
|
3
|
+
version: 2
|
|
4
|
+
updates:
|
|
5
|
+
- package-ecosystem: github-actions
|
|
6
|
+
directory: /
|
|
7
|
+
schedule:
|
|
8
|
+
interval: weekly
|
|
9
|
+
groups:
|
|
10
|
+
actions:
|
|
11
|
+
patterns: ["*"]
|
|
12
|
+
|
|
13
|
+
- package-ecosystem: cargo
|
|
14
|
+
directory: /
|
|
15
|
+
schedule:
|
|
16
|
+
interval: weekly
|
|
17
|
+
groups:
|
|
18
|
+
cargo-minor:
|
|
19
|
+
update-types: [minor, patch]
|
|
20
|
+
# PyO3 upgrades touch the doc-hidden trampoline API (src/entry.rs) and
|
|
21
|
+
# need the version checks in CLAUDE.md; take them deliberately.
|
|
22
|
+
ignore:
|
|
23
|
+
- dependency-name: pyo3
|
|
24
|
+
update-types: [version-update:semver-major, version-update:semver-minor]
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: '**/*.md'
|
|
3
|
+
---
|
|
4
|
+
# AI Coding Leader Instructions for Python-Rust Integration Project
|
|
5
|
+
|
|
6
|
+
## Project Overview
|
|
7
|
+
This project integrates Python and Rust, using Rust for performance-critical operations while providing Pythonic interfaces. The core functionality is implemented in Rust with Python bindings.
|
|
8
|
+
|
|
9
|
+
## Technology Stack
|
|
10
|
+
- **Rust**: Core implementation
|
|
11
|
+
- **Python**: User-facing API
|
|
12
|
+
- **PyO3**: Python bindings for Rust
|
|
13
|
+
- **Maturin**: Build system for Python extensions written in Rust
|
|
14
|
+
|
|
15
|
+
## Coding Standards
|
|
16
|
+
|
|
17
|
+
### Rust Standards
|
|
18
|
+
- Follow Rust idiomatic practices and official style guide
|
|
19
|
+
- Use 2021 edition of Rust
|
|
20
|
+
- Leverage strong typing and ownership system
|
|
21
|
+
- Use Result/Option for error handling, avoid panics
|
|
22
|
+
- Format code with `rustfmt` and lint with `clippy`
|
|
23
|
+
- Document all public APIs with rustdoc
|
|
24
|
+
|
|
25
|
+
### Python Standards
|
|
26
|
+
- Follow PEP 8 style guidelines
|
|
27
|
+
- Use type hints (PEP 484)
|
|
28
|
+
- Format with Black and check with flake8
|
|
29
|
+
- Write docstrings in Google style
|
|
30
|
+
- Target Python 3.7+ compatibility
|
|
31
|
+
|
|
32
|
+
## Integration Guidelines
|
|
33
|
+
- Create Pythonic APIs that hide Rust implementation details
|
|
34
|
+
- Handle memory management carefully at language boundaries
|
|
35
|
+
- Convert between Rust and Python types safely
|
|
36
|
+
- Propagate errors appropriately between languages
|
|
37
|
+
|
|
38
|
+
## Project Structure
|
|
39
|
+
- `/src/`: Rust implementation
|
|
40
|
+
- `/python/`: Python interface code
|
|
41
|
+
- `/tests/`: Test suites for both languages
|
|
42
|
+
- `/examples/`: Example usage
|
|
43
|
+
- `/docs/`: Documentation
|
|
44
|
+
- `/benches/`: Performance benchmarks
|
|
45
|
+
|
|
46
|
+
## Testing Requirements
|
|
47
|
+
- Unit tests for both languages
|
|
48
|
+
- Integration tests across language boundary
|
|
49
|
+
- Benchmark performance-critical code
|
|
50
|
+
- Test in CI pipeline
|
|
51
|
+
|
|
52
|
+
## Documentation
|
|
53
|
+
- Document APIs in both languages
|
|
54
|
+
- Include examples and usage patterns
|
|
55
|
+
- Explain performance characteristics
|
|
56
|
+
- Keep README updated with installation and usage instructions
|
|
57
|
+
|
|
58
|
+
## Performance Considerations
|
|
59
|
+
- Profile code to identify bottlenecks
|
|
60
|
+
- Minimize Python/Rust boundary crossings
|
|
61
|
+
- Consider parallelism for CPU-bound operations
|
|
62
|
+
- Benchmark critical paths
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: '**/*.py'
|
|
3
|
+
---
|
|
4
|
+
# Senior Python Engineer Coding Standards
|
|
5
|
+
|
|
6
|
+
## General Approach
|
|
7
|
+
- Write code as a senior Python engineer would: prioritize readability, maintainability, and efficiency
|
|
8
|
+
- Use Python 3.11+ syntax and features when appropriate
|
|
9
|
+
- Follow a systematic problem-solving approach: understand requirements, plan implementation, code, test, and refactor
|
|
10
|
+
- Before coding, outline a clear strategy considering edge cases, performance implications, and maintainability
|
|
11
|
+
|
|
12
|
+
## Code Style & Organization
|
|
13
|
+
- Follow PEP 8 style guidelines consistently
|
|
14
|
+
- Use meaningful variable/function names that convey purpose
|
|
15
|
+
- Organize code into logical modules and packages
|
|
16
|
+
- Limit function/method size to maintain readability (typically under 50 lines)
|
|
17
|
+
- Use appropriate design patterns where they simplify the code
|
|
18
|
+
|
|
19
|
+
## Documentation & Type Hints
|
|
20
|
+
- Write clear, concise docstrings for all public functions, classes, and modules
|
|
21
|
+
- Use type hints consistently to improve code understanding and static analysis
|
|
22
|
+
- Include examples in docstrings for complex functions
|
|
23
|
+
- Add inline comments only for non-obvious code sections
|
|
24
|
+
|
|
25
|
+
## Best Practices
|
|
26
|
+
- Prefer built-in functions and standard library modules over third-party packages for simple tasks
|
|
27
|
+
- Write idiomatic Python (e.g., use list comprehensions, generators where appropriate)
|
|
28
|
+
- Handle errors gracefully using try/except with specific exception types
|
|
29
|
+
- Use context managers for resource management
|
|
30
|
+
- Implement appropriate logging rather than print statements
|
|
31
|
+
- Write modular, reusable, and testable code
|
|
32
|
+
- Practice defensive programming for robust error handling
|
|
33
|
+
|
|
34
|
+
## Performance & Security
|
|
35
|
+
- Avoid premature optimization; focus on correct and clear code first
|
|
36
|
+
- Be mindful of performance implications for large datasets
|
|
37
|
+
- Use appropriate data structures for the problem at hand
|
|
38
|
+
- Validate all inputs, especially in public-facing interfaces
|
|
39
|
+
- Follow security best practices when handling sensitive data
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: '**/*.rs'
|
|
3
|
+
---
|
|
4
|
+
# Senior Rust Engineering Guidelines
|
|
5
|
+
|
|
6
|
+
## Code Style and Organization
|
|
7
|
+
- Follow the official Rust style guide and idioms from rust-lang/rust
|
|
8
|
+
- Use snake_case for variables and functions, CamelCase for types and traits
|
|
9
|
+
- Organize code with clear module hierarchy and separation of concerns
|
|
10
|
+
- Keep functions focused and concise (ideally under 50 lines)
|
|
11
|
+
- Use meaningful variable and function names that convey intent
|
|
12
|
+
|
|
13
|
+
## Safety and Error Handling
|
|
14
|
+
- Prefer Result over panic/unwrap in public APIs
|
|
15
|
+
- Use `?` operator for clean error propagation
|
|
16
|
+
- Implement custom error types for libraries using thiserror or similar
|
|
17
|
+
- Handle all error cases explicitly; avoid ignoring errors
|
|
18
|
+
- Use `#[must_use]` for Results that shouldn't be ignored
|
|
19
|
+
|
|
20
|
+
## Performance
|
|
21
|
+
- Minimize heap allocations where possible
|
|
22
|
+
- Consider using Cow<T> for flexible ownership
|
|
23
|
+
- Use iterators and avoid unnecessary collecting
|
|
24
|
+
- Profile before optimizing; focus on measured bottlenecks
|
|
25
|
+
- Use appropriate data structures for the task (e.g., HashMap vs BTreeMap)
|
|
26
|
+
|
|
27
|
+
## Memory Management
|
|
28
|
+
- Leverage Rust's ownership system; avoid unnecessary Rc/Arc
|
|
29
|
+
- Minimize use of unsafe code; document and isolate when necessary
|
|
30
|
+
- Prefer references over smart pointers when appropriate
|
|
31
|
+
- Be explicit about lifetimes when necessary for clarity
|
|
32
|
+
|
|
33
|
+
## Documentation
|
|
34
|
+
- Document all public APIs with rustdoc comments
|
|
35
|
+
- Include examples in documentation for complex functionality
|
|
36
|
+
- Document SAFETY considerations for unsafe functions
|
|
37
|
+
- Add comments for complex algorithms or non-obvious implementations
|
|
38
|
+
|
|
39
|
+
## Testing
|
|
40
|
+
- Write unit tests for all public functions
|
|
41
|
+
- Include integration tests for major features
|
|
42
|
+
- Use property-based testing where appropriate (e.g., proptest)
|
|
43
|
+
- Test edge cases and error paths explicitly
|
|
44
|
+
|
|
45
|
+
## Dependencies
|
|
46
|
+
- Be conservative with dependencies; evaluate their maintenance status
|
|
47
|
+
- Prefer crates from the rust-lang org when available
|
|
48
|
+
- Consider MSRV (Minimum Supported Rust Version) requirements
|
|
49
|
+
|
|
50
|
+
## Async Code
|
|
51
|
+
- Use appropriate executor for the project (tokio, async-std, etc.)
|
|
52
|
+
- Follow structured concurrency patterns
|
|
53
|
+
- Be mindful of cancellation and resource cleanup
|
|
54
|
+
- Document blocking operations clearly
|
|
55
|
+
|
|
56
|
+
## General
|
|
57
|
+
- Prefer immutable variables (let vs let mut)
|
|
58
|
+
- Use strong typing over runtime checks
|
|
59
|
+
- Leverage the type system to prevent invalid states
|
|
60
|
+
- Follow API design principles from the Rust API Guidelines
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
env:
|
|
17
|
+
CARGO_TERM_COLOR: always
|
|
18
|
+
PIP_DISABLE_PIP_VERSION_CHECK: "1"
|
|
19
|
+
|
|
20
|
+
# Never set RUSTFLAGS in this workflow: it replaces the target rustflags from
|
|
21
|
+
# .cargo/config.toml (target-cpu=x86-64-v2). Extra flags go into
|
|
22
|
+
# CARGO_TARGET_<TRIPLE>_RUSTFLAGS, which cargo merges with the config.
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
lint:
|
|
26
|
+
runs-on: ubuntu-24.04
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
29
|
+
with:
|
|
30
|
+
persist-credentials: false
|
|
31
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
32
|
+
with:
|
|
33
|
+
python-version: "3.13"
|
|
34
|
+
- uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de # master
|
|
35
|
+
with:
|
|
36
|
+
toolchain: stable
|
|
37
|
+
components: clippy
|
|
38
|
+
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
|
39
|
+
# Warnings are reported, not fatal (two pre-existing clippy warnings in
|
|
40
|
+
# src/ser.rs). `cargo fmt --check` is not enforced yet: src/entry.rs and
|
|
41
|
+
# src/parser.rs are not rustfmt-clean.
|
|
42
|
+
- run: cargo clippy --release --all-targets
|
|
43
|
+
env:
|
|
44
|
+
PYO3_PYTHON: python
|
|
45
|
+
|
|
46
|
+
test:
|
|
47
|
+
name: test ${{ matrix.os }} py${{ matrix.python }}${{ matrix.no-avx512 && ' no-avx512' || '' }}
|
|
48
|
+
runs-on: ${{ matrix.os }}
|
|
49
|
+
strategy:
|
|
50
|
+
fail-fast: false
|
|
51
|
+
matrix:
|
|
52
|
+
# requires-python >=3.10; PyO3 0.29 supports up to 3.14 (3.14 str access: src/compat.rs).
|
|
53
|
+
os: [ubuntu-24.04]
|
|
54
|
+
python: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
55
|
+
include:
|
|
56
|
+
# SSE2/AVX2 escape kernels (the runner may have AVX-512).
|
|
57
|
+
- os: ubuntu-24.04
|
|
58
|
+
python: "3.13"
|
|
59
|
+
no-avx512: true
|
|
60
|
+
# aarch64: SWAR / non-x86 fallback paths, pre- and post-3.12 layouts.
|
|
61
|
+
- os: ubuntu-24.04-arm
|
|
62
|
+
python: "3.10"
|
|
63
|
+
- os: ubuntu-24.04-arm
|
|
64
|
+
python: "3.13"
|
|
65
|
+
- os: macos-14
|
|
66
|
+
python: "3.13"
|
|
67
|
+
# Windows links private C-API symbols via the full import library
|
|
68
|
+
# (build.rs); 3.10 covers _PyDict_NewPresized, 3.13 _PyDict_FromItems.
|
|
69
|
+
- os: windows-2022
|
|
70
|
+
python: "3.10"
|
|
71
|
+
- os: windows-2022
|
|
72
|
+
python: "3.13"
|
|
73
|
+
defaults:
|
|
74
|
+
run:
|
|
75
|
+
shell: bash
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
78
|
+
with:
|
|
79
|
+
persist-credentials: false
|
|
80
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
81
|
+
with:
|
|
82
|
+
python-version: ${{ matrix.python }}
|
|
83
|
+
- uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de # master
|
|
84
|
+
with:
|
|
85
|
+
toolchain: stable
|
|
86
|
+
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
|
87
|
+
with:
|
|
88
|
+
key: py${{ matrix.python }}-${{ matrix.no-avx512 && 'no-avx512' || 'default' }}
|
|
89
|
+
- name: Disable the AVX-512 kernel
|
|
90
|
+
if: matrix.no-avx512
|
|
91
|
+
run: echo "CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS=--cfg rjson_no_avx512" >> "$GITHUB_ENV"
|
|
92
|
+
# fastapi + httpx: tests/test_examples.py skips its FastAPI tests without them;
|
|
93
|
+
# orjson: tests/test_native.py compares against it (skipped without it);
|
|
94
|
+
# tzdata: ZoneInfo zones on Windows, which has no system tz database.
|
|
95
|
+
- run: python -m pip install "maturin>=1.8,<2" pytest fastapi httpx orjson tzdata
|
|
96
|
+
- name: Build wheel
|
|
97
|
+
run: maturin build --release -i python -o dist
|
|
98
|
+
- name: Install wheel
|
|
99
|
+
run: python -m pip install --no-index --find-links dist pyrjson
|
|
100
|
+
- run: python -m pytest tests -q
|
|
101
|
+
|
|
102
|
+
# musl (Alpine): the musllinux wheels' platform. Its pthread_getattr_np
|
|
103
|
+
# reports the main thread's stack differently from glibc (src/stack.rs);
|
|
104
|
+
# rjson 0.1.0's musllinux wheel builds failed on exactly that.
|
|
105
|
+
musl:
|
|
106
|
+
name: test musl (Alpine) py3.13
|
|
107
|
+
runs-on: ubuntu-24.04
|
|
108
|
+
container: python:3.13-alpine
|
|
109
|
+
steps:
|
|
110
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
111
|
+
with:
|
|
112
|
+
persist-credentials: false
|
|
113
|
+
- name: Install the Rust toolchain
|
|
114
|
+
run: |
|
|
115
|
+
apk add --no-cache build-base curl patchelf
|
|
116
|
+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal
|
|
117
|
+
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
118
|
+
- run: python -m pip install "maturin>=1.8,<2" pytest orjson
|
|
119
|
+
- name: Build wheel
|
|
120
|
+
run: maturin build --release -i python -o dist
|
|
121
|
+
- name: Install wheel
|
|
122
|
+
run: python -m pip install --no-index --find-links dist pyrjson
|
|
123
|
+
- run: python -m pytest tests -q
|
|
124
|
+
|
|
125
|
+
# The extension built with AddressSanitizer (nightly rustc), run under a
|
|
126
|
+
# normal CPython with the ASan runtime preloaded. PYTHONMALLOC=malloc puts
|
|
127
|
+
# Python objects (the bytes/str rjson writes into) on ASan's allocator, so
|
|
128
|
+
# out-of-bounds reads/writes and use-after-free are reported; leak checking
|
|
129
|
+
# is off because CPython keeps objects alive until exit on purpose. The fuzz
|
|
130
|
+
# suite runs with more cases here.
|
|
131
|
+
sanitize:
|
|
132
|
+
name: sanitize (ASan) py3.13
|
|
133
|
+
runs-on: ubuntu-24.04
|
|
134
|
+
steps:
|
|
135
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
136
|
+
with:
|
|
137
|
+
persist-credentials: false
|
|
138
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
139
|
+
with:
|
|
140
|
+
python-version: "3.13"
|
|
141
|
+
- uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de # master
|
|
142
|
+
with:
|
|
143
|
+
toolchain: nightly
|
|
144
|
+
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
|
145
|
+
with:
|
|
146
|
+
key: asan
|
|
147
|
+
- run: python -m pip install "maturin>=1.8,<2" pytest fastapi httpx orjson
|
|
148
|
+
- name: Build wheel with AddressSanitizer
|
|
149
|
+
env:
|
|
150
|
+
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS: -Zsanitizer=address
|
|
151
|
+
run: maturin build --release --target x86_64-unknown-linux-gnu -i python -o dist
|
|
152
|
+
- name: Install wheel
|
|
153
|
+
run: python -m pip install --no-index --find-links dist pyrjson
|
|
154
|
+
- name: Tests and fuzzing under ASan
|
|
155
|
+
env:
|
|
156
|
+
ASAN_OPTIONS: detect_leaks=0:abort_on_error=1
|
|
157
|
+
PYTHONMALLOC: malloc
|
|
158
|
+
RJSON_SANITIZER: asan
|
|
159
|
+
RJSON_FUZZ_ITERS: "5000"
|
|
160
|
+
run: LD_PRELOAD="$(gcc -print-file-name=libasan.so)" python -m pytest tests -q
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
name: Perf gate
|
|
2
|
+
|
|
3
|
+
# Opt-in performance regression gate (shared runners are noisy): runs on PRs
|
|
4
|
+
# labelled `perf` and on manual dispatch. Builds base and head (plain release,
|
|
5
|
+
# no PGO) in the same job, runs benches/corpus_benchmark.py for both,
|
|
6
|
+
# interleaved, and fails if any rjson/orjson geomean (loads, dumps,
|
|
7
|
+
# dumps_str) is more than 5% worse than base. The ratio to orjson, timed in
|
|
8
|
+
# the same process, cancels most runner speed drift.
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
pull_request:
|
|
12
|
+
types: [opened, synchronize, reopened, labeled]
|
|
13
|
+
workflow_dispatch:
|
|
14
|
+
inputs:
|
|
15
|
+
base:
|
|
16
|
+
description: Base ref to compare against
|
|
17
|
+
default: main
|
|
18
|
+
threshold:
|
|
19
|
+
description: Max allowed geomean regression (0.05 = 5%)
|
|
20
|
+
default: "0.05"
|
|
21
|
+
|
|
22
|
+
permissions:
|
|
23
|
+
contents: read
|
|
24
|
+
|
|
25
|
+
concurrency:
|
|
26
|
+
group: perf-${{ github.ref }}
|
|
27
|
+
cancel-in-progress: true
|
|
28
|
+
|
|
29
|
+
env:
|
|
30
|
+
CARGO_TERM_COLOR: always
|
|
31
|
+
PIP_DISABLE_PIP_VERSION_CHECK: "1"
|
|
32
|
+
ROUNDS: "5"
|
|
33
|
+
|
|
34
|
+
jobs:
|
|
35
|
+
perf:
|
|
36
|
+
if: github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'perf')
|
|
37
|
+
name: perf py${{ matrix.python }}
|
|
38
|
+
runs-on: ubuntu-24.04
|
|
39
|
+
timeout-minutes: 60
|
|
40
|
+
strategy:
|
|
41
|
+
fail-fast: false
|
|
42
|
+
matrix:
|
|
43
|
+
# 3.11 exercises the GC-pause path, 3.13 the like-for-like parser.
|
|
44
|
+
python: ["3.11", "3.13"]
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
47
|
+
with:
|
|
48
|
+
persist-credentials: false
|
|
49
|
+
path: head
|
|
50
|
+
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
|
|
51
|
+
with:
|
|
52
|
+
persist-credentials: false
|
|
53
|
+
path: base
|
|
54
|
+
ref: ${{ github.event.pull_request.base.sha || inputs.base }}
|
|
55
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
56
|
+
with:
|
|
57
|
+
python-version: ${{ matrix.python }}
|
|
58
|
+
- uses: dtolnay/rust-toolchain@02cb101ec7c40f2c49e1d9714d64511d8e1b74de # master
|
|
59
|
+
with:
|
|
60
|
+
toolchain: stable
|
|
61
|
+
- name: Fetch corpora (sha256-pinned)
|
|
62
|
+
run: head/benches/fetch_corpus.sh "$RUNNER_TEMP/corpus"
|
|
63
|
+
- name: Build base and head
|
|
64
|
+
run: |
|
|
65
|
+
python -m pip install "maturin>=1.8,<2"
|
|
66
|
+
for side in base head; do
|
|
67
|
+
(cd "$side" && maturin build --release -i python -o "$RUNNER_TEMP/wheels-$side")
|
|
68
|
+
python -m venv "$RUNNER_TEMP/venv-$side"
|
|
69
|
+
"$RUNNER_TEMP/venv-$side/bin/python" -m pip install -q orjson
|
|
70
|
+
# Install by file, not by name: the base ref may predate the
|
|
71
|
+
# rjson -> pyrjson distribution rename (one wheel per directory).
|
|
72
|
+
"$RUNNER_TEMP/venv-$side/bin/python" -m pip install -q --no-index "$RUNNER_TEMP/wheels-$side"/*.whl
|
|
73
|
+
done
|
|
74
|
+
- name: Benchmark (interleaved)
|
|
75
|
+
run: |
|
|
76
|
+
mkdir -p results
|
|
77
|
+
for r in $(seq 1 "$ROUNDS"); do
|
|
78
|
+
# alternate which side goes first to cancel ordering effects
|
|
79
|
+
if (( r % 2 )); then order="base head"; else order="head base"; fi
|
|
80
|
+
for side in $order; do
|
|
81
|
+
# head's benchmark script for both sides (base may predate --output-json)
|
|
82
|
+
"$RUNNER_TEMP/venv-$side/bin/python" head/benches/corpus_benchmark.py \
|
|
83
|
+
--data "$RUNNER_TEMP/corpus" --repeat 5 --output-json "results/$side-$r.json" > /dev/null
|
|
84
|
+
done
|
|
85
|
+
done
|
|
86
|
+
- name: Compare
|
|
87
|
+
run: |
|
|
88
|
+
python head/benches/perf_gate.py --threshold "${THRESHOLD}" \
|
|
89
|
+
--base results/base-*.json --head results/head-*.json
|
|
90
|
+
env:
|
|
91
|
+
THRESHOLD: ${{ inputs.threshold || '0.05' }}
|
|
92
|
+
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
93
|
+
if: always()
|
|
94
|
+
with:
|
|
95
|
+
name: perf-results-py${{ matrix.python }}
|
|
96
|
+
path: results/
|