byteforge 0.0.5__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 (68) hide show
  1. byteforge-0.0.5/.github/workflows/release.yml +67 -0
  2. byteforge-0.0.5/.github/workflows/test.yml +26 -0
  3. byteforge-0.0.5/.gitignore +136 -0
  4. byteforge-0.0.5/CHANGELOG.md +22 -0
  5. byteforge-0.0.5/CLAUDE.md +35 -0
  6. byteforge-0.0.5/MANIFEST.in +1 -0
  7. byteforge-0.0.5/PKG-INFO +105 -0
  8. byteforge-0.0.5/README.md +96 -0
  9. byteforge-0.0.5/pyproject.toml +68 -0
  10. byteforge-0.0.5/setup.cfg +4 -0
  11. byteforge-0.0.5/setup.py +37 -0
  12. byteforge-0.0.5/src/byteforge/__init__.py +42 -0
  13. byteforge-0.0.5/src/byteforge/_base.py +289 -0
  14. byteforge-0.0.5/src/byteforge/_c/__init__.py +0 -0
  15. byteforge-0.0.5/src/byteforge/_c/bcd.c +78 -0
  16. byteforge-0.0.5/src/byteforge/_c/dec.c +238 -0
  17. byteforge-0.0.5/src/byteforge/_c/gray.c +37 -0
  18. byteforge-0.0.5/src/byteforge/_c/ibm.c +177 -0
  19. byteforge-0.0.5/src/byteforge/_c/milstd1750a.c +152 -0
  20. byteforge-0.0.5/src/byteforge/_c/ti.c +166 -0
  21. byteforge-0.0.5/src/byteforge/_c/ufunc.c +194 -0
  22. byteforge-0.0.5/src/byteforge/_c/utils.c +15 -0
  23. byteforge-0.0.5/src/byteforge/_registry.py +68 -0
  24. byteforge-0.0.5/src/byteforge/_validation.py +13 -0
  25. byteforge-0.0.5/src/byteforge/_version.py +34 -0
  26. byteforge-0.0.5/src/byteforge/bcd.py +203 -0
  27. byteforge-0.0.5/src/byteforge/boolean.py +46 -0
  28. byteforge-0.0.5/src/byteforge/dec_float.py +215 -0
  29. byteforge-0.0.5/src/byteforge/gray_code.py +88 -0
  30. byteforge-0.0.5/src/byteforge/ibm_float.py +179 -0
  31. byteforge-0.0.5/src/byteforge/ieee754.py +45 -0
  32. byteforge-0.0.5/src/byteforge/linear_scaled.py +116 -0
  33. byteforge-0.0.5/src/byteforge/mil_std_1750a.py +193 -0
  34. byteforge-0.0.5/src/byteforge/offset_binary.py +73 -0
  35. byteforge-0.0.5/src/byteforge/ones_complement.py +84 -0
  36. byteforge-0.0.5/src/byteforge/py.typed +0 -0
  37. byteforge-0.0.5/src/byteforge/ti_float.py +179 -0
  38. byteforge-0.0.5/src/byteforge/twos_complement.py +84 -0
  39. byteforge-0.0.5/src/byteforge/unsigned.py +53 -0
  40. byteforge-0.0.5/src/byteforge.egg-info/PKG-INFO +105 -0
  41. byteforge-0.0.5/src/byteforge.egg-info/SOURCES.txt +66 -0
  42. byteforge-0.0.5/src/byteforge.egg-info/dependency_links.txt +1 -0
  43. byteforge-0.0.5/src/byteforge.egg-info/requires.txt +1 -0
  44. byteforge-0.0.5/src/byteforge.egg-info/top_level.txt +1 -0
  45. byteforge-0.0.5/tests/__init__.py +0 -0
  46. byteforge-0.0.5/tests/conftest.py +0 -0
  47. byteforge-0.0.5/tests/test_bcd.py +241 -0
  48. byteforge-0.0.5/tests/test_benchmarks.py +88 -0
  49. byteforge-0.0.5/tests/test_boolean.py +78 -0
  50. byteforge-0.0.5/tests/test_bytes.py +143 -0
  51. byteforge-0.0.5/tests/test_c_parity.py +188 -0
  52. byteforge-0.0.5/tests/test_dec_float.py +189 -0
  53. byteforge-0.0.5/tests/test_empty_arrays.py +49 -0
  54. byteforge-0.0.5/tests/test_gray_code.py +100 -0
  55. byteforge-0.0.5/tests/test_ibm_float.py +101 -0
  56. byteforge-0.0.5/tests/test_ieee754.py +151 -0
  57. byteforge-0.0.5/tests/test_linear_scaled.py +100 -0
  58. byteforge-0.0.5/tests/test_mil_std_1750a.py +126 -0
  59. byteforge-0.0.5/tests/test_multidimensional.py +136 -0
  60. byteforge-0.0.5/tests/test_new_features.py +307 -0
  61. byteforge-0.0.5/tests/test_offset_binary.py +71 -0
  62. byteforge-0.0.5/tests/test_ones_complement.py +152 -0
  63. byteforge-0.0.5/tests/test_registry.py +126 -0
  64. byteforge-0.0.5/tests/test_roundtrip_fuzz.py +337 -0
  65. byteforge-0.0.5/tests/test_ti_float.py +146 -0
  66. byteforge-0.0.5/tests/test_twos_complement.py +140 -0
  67. byteforge-0.0.5/tests/test_unsigned.py +82 -0
  68. byteforge-0.0.5/uv.lock +716 -0
@@ -0,0 +1,67 @@
1
+ name: "Publish"
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ build_sdist:
9
+ name: Build sdist
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v6
13
+
14
+ - name: Install uv
15
+ uses: astral-sh/setup-uv@v7
16
+
17
+ - name: Build sdist
18
+ run: uv build --sdist
19
+
20
+ - name: Upload sdist
21
+ uses: actions/upload-artifact@v4
22
+ with:
23
+ name: sdist
24
+ path: dist/*.tar.gz
25
+
26
+ build_wheels:
27
+ name: Build wheels (${{ matrix.os }})
28
+ runs-on: ${{ matrix.os }}
29
+ strategy:
30
+ fail-fast: false
31
+ matrix:
32
+ os: [ubuntu-latest, macos-latest, windows-latest]
33
+ steps:
34
+ - uses: actions/checkout@v6
35
+
36
+ - name: Build wheels
37
+ uses: pypa/cibuildwheel@v3.4.0
38
+ env:
39
+ CIBW_ARCHS_MACOS: "x86_64 arm64"
40
+
41
+ - name: Upload wheels
42
+ uses: actions/upload-artifact@v4
43
+ with:
44
+ name: wheels-${{ matrix.os }}
45
+ path: wheelhouse/*.whl
46
+
47
+ publish:
48
+ name: Publish to PyPI
49
+ needs: [build_sdist, build_wheels]
50
+ runs-on: ubuntu-latest
51
+ environment:
52
+ name: pypi
53
+ permissions:
54
+ id-token: write
55
+ contents: read
56
+ steps:
57
+ - name: Install uv
58
+ uses: astral-sh/setup-uv@v7
59
+
60
+ - name: Download all artifacts
61
+ uses: actions/download-artifact@v4
62
+ with:
63
+ path: dist
64
+ merge-multiple: true
65
+
66
+ - name: Publish to PyPI
67
+ run: uv publish dist/*
@@ -0,0 +1,26 @@
1
+ name: Test
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
12
+
13
+ steps:
14
+ - uses: actions/checkout@v6
15
+
16
+ - name: Install uv and set the Python version
17
+ uses: astral-sh/setup-uv@v7
18
+ with:
19
+ enable-cache: true
20
+ python-version: ${{ matrix.python-version }}
21
+
22
+ - name: Install the project
23
+ run: uv sync --locked --all-extras --dev
24
+
25
+ - name: Run pytest
26
+ run: uv run pytest tests/
@@ -0,0 +1,136 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+ __about__.py
30
+
31
+ # PyInstaller
32
+ # Usually these files are written by a python script from a template
33
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
34
+ *.manifest
35
+ *.spec
36
+
37
+ # Installer logs
38
+ pip-log.txt
39
+ pip-delete-this-directory.txt
40
+
41
+ # Unit test / coverage reports
42
+ htmlcov/
43
+ .tox/
44
+ .nox/
45
+ .coverage
46
+ .coverage.*
47
+ .cache
48
+ nosetests.xml
49
+ coverage.xml
50
+ *.cover
51
+ *.py,cover
52
+ .hypothesis/
53
+ .pytest_cache/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ .python-version
87
+
88
+ # pipenv
89
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
90
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
91
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
92
+ # install all needed dependencies.
93
+ #Pipfile.lock
94
+
95
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
96
+ __pypackages__/
97
+
98
+ # Celery stuff
99
+ celerybeat-schedule
100
+ celerybeat.pid
101
+
102
+ # SageMath parsed files
103
+ *.sage.py
104
+
105
+ # Environments
106
+ .env
107
+ .venv
108
+ env/
109
+ venv/
110
+ ENV/
111
+ env.bak/
112
+ venv.bak/
113
+
114
+ # Spyder project settings
115
+ .spyderproject
116
+ .spyproject
117
+
118
+ # Rope project settings
119
+ .ropeproject
120
+
121
+ # mkdocs documentation
122
+ /site
123
+
124
+ # mypy
125
+ .mypy_cache/
126
+ .dmypy.json
127
+ dmypy.json
128
+
129
+ # Pyre type checker
130
+ .pyre/
131
+
132
+ # hatch-vcs
133
+ _version.py
134
+
135
+ # ruff
136
+ .ruff_cache
@@ -0,0 +1,22 @@
1
+ # Changelog
2
+
3
+ ## [0.0.4] - 2026-03-06
4
+
5
+ ### Added
6
+ - Unified `encode_errors` parameter on all encodings (except IEEE754 and Boolean):
7
+ `"clamp"` (default), `"raise"`, `"nan"`, or numeric sentinel.
8
+ - `BCD` now accepts `decode_errors` for invalid-nibble handling during decode.
9
+ - Float encodings (MilStd1750A, TIFloat, IBMFloat, DECFloat, DECFloatG) now
10
+ support `encode_errors` for out-of-range detection.
11
+ - pytest-benchmark benchmarks for all encoding types.
12
+ - `__version__` attribute powered by setuptools-scm.
13
+ - Docstrings on all `_encode_py()`/`_decode_py()` private helpers.
14
+
15
+ ### Changed
16
+ - **BREAKING**: `errors` parameter renamed to `encode_errors` on all encodings.
17
+ - **BREAKING**: `BCD(errors=...)` renamed to `BCD(decode_errors=...)`.
18
+ - Version management switched from hardcoded to setuptools-scm.
19
+
20
+ ### Fixed
21
+ - Fixed mypy type annotation errors in `_base.py`, `twos_complement.py`,
22
+ `ieee754.py`, and `bcd.py`.
@@ -0,0 +1,35 @@
1
+ # Byteforge
2
+
3
+ Encode and decode numbers as unsigned integer bit patterns (`np.ndarray`).
4
+
5
+ ## Structure
6
+
7
+ - `src/byteforge/` — flat package
8
+ - `_base.py` — abstract `Encoding` base class (encode/decode accept scalars or arrays)
9
+ - `_registry.py` — `@register` decorator and `create_encoding()` factory
10
+ - 13 encodings: Unsigned, TwosComplement, OnesComplement, IEEE754, LinearScaled, MilStd1750A, BCD, GrayCode, OffsetBinary, Boolean, TIFloat, IBMFloat, DECFloat/DECFloatG
11
+
12
+ ## Commands
13
+
14
+ - **Test**: `uv run pytest tests/ -v`
15
+ - **Lint**: `uv run ruff check src/ tests/`
16
+ - **Type check**: `uv run mypy`
17
+
18
+ ## C Extension (`src/byteforge/_c/`)
19
+
20
+ Optional NumPy C ufuncs accelerate encodings where Python has per-element loops or multi-pass numpy operations. Falls back to pure Python when the C extension is unavailable (set `BYTEFORGE_NO_C=1` to force fallback).
21
+
22
+ **Has C ufuncs:** BCD (encode/decode), GrayCode (decode only), MilStd1750A, TIFloat, IBMFloat, DECFloat/DECFloatG (all encode+decode).
23
+
24
+ **No C ufuncs:** Unsigned, TwosComplement, OnesComplement, IEEE754, OffsetBinary, Boolean, LinearScaled — these are already single-pass vectorized numpy ops (`view`, `clip`, `where`, arithmetic) with no loops or multi-pass overhead to eliminate. GrayCode encode is also pure Python since it's a single expression (`n ^ (n >> 1)`).
25
+
26
+ Each encoding module checks `_HAS_C` and dispatches to either the C ufunc or `_encode_py`/`_decode_py` helpers.
27
+
28
+ ## Conventions
29
+
30
+ - Python >=3.9, numpy >=2.0
31
+ - Don't use from __future__ import annotations
32
+ - Google style docstrings; use type annotations, not types in docstrings
33
+ - Ruff: rules E, F, W, I, UP, B, SIM; line-length 100
34
+ - Tests use pytest + hypothesis
35
+ - Keep `CHANGELOG.md` up to date under `## Unreleased` for user-facing changes
@@ -0,0 +1 @@
1
+ recursive-include src/byteforge/_c *.c
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.4
2
+ Name: byteforge
3
+ Version: 0.0.5
4
+ Summary: Encode and decode numbers as unsigned integer bit patterns
5
+ Author-email: Jonathan Olsten <jolsten@gmail.com>
6
+ Requires-Python: >=3.9
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: numpy>=2.0
9
+
10
+ # Byteforge
11
+
12
+ Encode and decode numbers as unsigned integer bit patterns using NumPy arrays.
13
+
14
+ Requires Python >= 3.9 and NumPy >= 2.0.
15
+
16
+ ## Installation
17
+
18
+ ```
19
+ pip install byteforge
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ Every encoding exposes the same interface: `encode()` converts physical values to bit patterns, `decode()` converts back.
25
+
26
+ ```python
27
+ import numpy as np
28
+ from byteforge import TwosComplement, IEEE754, Unsigned
29
+
30
+ # Integer encodings
31
+ tc = TwosComplement(16)
32
+ dns = tc.encode(np.array([-1, 0, 127])) # array([65535, 0, 127], dtype=uint16)
33
+ tc.decode(dns) # array([-1, 0, 127], dtype=int16)
34
+
35
+ # Floating-point encodings
36
+ ieee = IEEE754(32)
37
+ dns = ieee.encode(np.array([3.14])) # uint32 bit pattern
38
+ ieee.decode(dns) # array([3.14], dtype=float32)
39
+
40
+ # Unsigned with automatic bit width
41
+ u = Unsigned.from_range(max_value=1000) # Unsigned(10)
42
+ ```
43
+
44
+ ### Registry factory
45
+
46
+ Create encodings by name using the registry:
47
+
48
+ ```python
49
+ from byteforge import create_encoding
50
+
51
+ enc = create_encoding("twos_complement", 8)
52
+ enc = create_encoding("ieee32") # aliases have default bit widths
53
+ enc = create_encoding("linear_scaled", 16, scale_factor=0.1, offset=10.0)
54
+ ```
55
+
56
+ ### Byte serialization
57
+
58
+ Convert encoded bit patterns to/from raw bytes:
59
+
60
+ ```python
61
+ enc = Unsigned(16)
62
+ dns = enc.encode(np.array([256, 512]))
63
+ raw = enc.to_bytes(dns, byteorder="big") # uint8 array, shape (2, 2)
64
+ enc.decode(enc.from_bytes(raw, byteorder="big")) # roundtrip
65
+ ```
66
+
67
+ For non-byte-aligned widths (e.g. 12-bit), values are zero-padded in the most
68
+ significant bits to fill whole bytes (2 bytes for 12-bit).
69
+
70
+ ## Encodings
71
+
72
+ | Encoding | Registry names | Bit widths |
73
+ |---|---|---|
74
+ | `Unsigned` | `unsigned`, `u` | 1-64 |
75
+ | `TwosComplement` | `twos_complement`, `2c` | 2-64 |
76
+ | `OnesComplement` | `ones_complement`, `1c` | 2-64 |
77
+ | `OffsetBinary` | `offset_binary` | 1-64 |
78
+ | `GrayCode` | `gray_code`, `gray` | 1-64 |
79
+ | `BCD` | `bcd` | 4-64 (multiples of 4) |
80
+ | `Boolean` | `boolean` | 1 |
81
+ | `IEEE754` | `ieee754`, `ieee16`, `ieee32`, `ieee64` | 16, 32, 64 |
82
+ | `LinearScaled` | `linear_scaled` | 1-64 |
83
+ | `MilStd1750A` | `mil_std_1750a`, `1750a32`, `1750a48` | 32, 48 |
84
+ | `TIFloat` | `ti_float`, `ti32`, `ti40` | 32, 40 |
85
+ | `IBMFloat` | `ibm_float`, `ibm32`, `ibm64` | 32, 64 |
86
+ | `DECFloat` | `dec_float`, `dec32`, `dec64` | 32, 64 |
87
+ | `DECFloatG` | `dec_float_g`, `dec64g` | 64 |
88
+
89
+ ## C Extension
90
+
91
+ Optional NumPy C ufuncs accelerate encodings that would otherwise require per-element loops
92
+ or multi-pass numpy operations. The C extension is compiled automatically during install and
93
+ falls back to pure Python when unavailable. Set `BYTEFORGE_NO_C=1` to force the pure-Python
94
+ path.
95
+
96
+ Encodings with C ufuncs: BCD, GrayCode (decode), MilStd1750A, TIFloat, IBMFloat,
97
+ DECFloat, and DECFloatG.
98
+
99
+ ## Development
100
+
101
+ ```
102
+ uv run pytest tests/ -v # tests
103
+ uv run ruff check src/ tests/ # lint
104
+ uv run mypy # type check
105
+ ```
@@ -0,0 +1,96 @@
1
+ # Byteforge
2
+
3
+ Encode and decode numbers as unsigned integer bit patterns using NumPy arrays.
4
+
5
+ Requires Python >= 3.9 and NumPy >= 2.0.
6
+
7
+ ## Installation
8
+
9
+ ```
10
+ pip install byteforge
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Every encoding exposes the same interface: `encode()` converts physical values to bit patterns, `decode()` converts back.
16
+
17
+ ```python
18
+ import numpy as np
19
+ from byteforge import TwosComplement, IEEE754, Unsigned
20
+
21
+ # Integer encodings
22
+ tc = TwosComplement(16)
23
+ dns = tc.encode(np.array([-1, 0, 127])) # array([65535, 0, 127], dtype=uint16)
24
+ tc.decode(dns) # array([-1, 0, 127], dtype=int16)
25
+
26
+ # Floating-point encodings
27
+ ieee = IEEE754(32)
28
+ dns = ieee.encode(np.array([3.14])) # uint32 bit pattern
29
+ ieee.decode(dns) # array([3.14], dtype=float32)
30
+
31
+ # Unsigned with automatic bit width
32
+ u = Unsigned.from_range(max_value=1000) # Unsigned(10)
33
+ ```
34
+
35
+ ### Registry factory
36
+
37
+ Create encodings by name using the registry:
38
+
39
+ ```python
40
+ from byteforge import create_encoding
41
+
42
+ enc = create_encoding("twos_complement", 8)
43
+ enc = create_encoding("ieee32") # aliases have default bit widths
44
+ enc = create_encoding("linear_scaled", 16, scale_factor=0.1, offset=10.0)
45
+ ```
46
+
47
+ ### Byte serialization
48
+
49
+ Convert encoded bit patterns to/from raw bytes:
50
+
51
+ ```python
52
+ enc = Unsigned(16)
53
+ dns = enc.encode(np.array([256, 512]))
54
+ raw = enc.to_bytes(dns, byteorder="big") # uint8 array, shape (2, 2)
55
+ enc.decode(enc.from_bytes(raw, byteorder="big")) # roundtrip
56
+ ```
57
+
58
+ For non-byte-aligned widths (e.g. 12-bit), values are zero-padded in the most
59
+ significant bits to fill whole bytes (2 bytes for 12-bit).
60
+
61
+ ## Encodings
62
+
63
+ | Encoding | Registry names | Bit widths |
64
+ |---|---|---|
65
+ | `Unsigned` | `unsigned`, `u` | 1-64 |
66
+ | `TwosComplement` | `twos_complement`, `2c` | 2-64 |
67
+ | `OnesComplement` | `ones_complement`, `1c` | 2-64 |
68
+ | `OffsetBinary` | `offset_binary` | 1-64 |
69
+ | `GrayCode` | `gray_code`, `gray` | 1-64 |
70
+ | `BCD` | `bcd` | 4-64 (multiples of 4) |
71
+ | `Boolean` | `boolean` | 1 |
72
+ | `IEEE754` | `ieee754`, `ieee16`, `ieee32`, `ieee64` | 16, 32, 64 |
73
+ | `LinearScaled` | `linear_scaled` | 1-64 |
74
+ | `MilStd1750A` | `mil_std_1750a`, `1750a32`, `1750a48` | 32, 48 |
75
+ | `TIFloat` | `ti_float`, `ti32`, `ti40` | 32, 40 |
76
+ | `IBMFloat` | `ibm_float`, `ibm32`, `ibm64` | 32, 64 |
77
+ | `DECFloat` | `dec_float`, `dec32`, `dec64` | 32, 64 |
78
+ | `DECFloatG` | `dec_float_g`, `dec64g` | 64 |
79
+
80
+ ## C Extension
81
+
82
+ Optional NumPy C ufuncs accelerate encodings that would otherwise require per-element loops
83
+ or multi-pass numpy operations. The C extension is compiled automatically during install and
84
+ falls back to pure Python when unavailable. Set `BYTEFORGE_NO_C=1` to force the pure-Python
85
+ path.
86
+
87
+ Encodings with C ufuncs: BCD, GrayCode (decode), MilStd1750A, TIFloat, IBMFloat,
88
+ DECFloat, and DECFloatG.
89
+
90
+ ## Development
91
+
92
+ ```
93
+ uv run pytest tests/ -v # tests
94
+ uv run ruff check src/ tests/ # lint
95
+ uv run mypy # type check
96
+ ```
@@ -0,0 +1,68 @@
1
+ [project]
2
+ name = "byteforge"
3
+ dynamic = ["version"]
4
+ description = "Encode and decode numbers as unsigned integer bit patterns"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Jonathan Olsten", email = "jolsten@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.9"
10
+ dependencies = [
11
+ "numpy>=2.0",
12
+ ]
13
+
14
+ [build-system]
15
+ requires = ["setuptools>=64", "setuptools-scm>=8", "numpy>=2.0"]
16
+ build-backend = "setuptools.build_meta"
17
+
18
+ [tool.setuptools.packages.find]
19
+ where = ["src"]
20
+
21
+ [dependency-groups]
22
+ dev = [
23
+ "hypothesis>=6.141",
24
+ "mypy>=1.19.1",
25
+ "pytest>=8.4",
26
+ "pytest-benchmark>=5.2.3",
27
+ "ruff>=0.15.4",
28
+ "setuptools>=64",
29
+ "setuptools-scm>=8",
30
+ ]
31
+
32
+ [tool.pytest.ini_options]
33
+ testpaths = ["tests"]
34
+ addopts = "--benchmark-disable"
35
+
36
+ [tool.setuptools_scm]
37
+ version_file = "src/byteforge/_version.py"
38
+
39
+ [tool.ruff]
40
+ target-version = "py39"
41
+ src = ["src"]
42
+ line-length = 100
43
+
44
+ [tool.ruff.lint]
45
+ select = ["E", "F", "W", "I", "UP", "B", "SIM"]
46
+ ignore = ["E501"]
47
+
48
+ [tool.ruff.lint.isort]
49
+ known-first-party = ["byteforge"]
50
+
51
+ [tool.mypy]
52
+ python_version = "3.9"
53
+ warn_return_any = false
54
+ warn_unused_configs = true
55
+ disallow_untyped_defs = true
56
+ check_untyped_defs = true
57
+ packages = ["byteforge"]
58
+ mypy_path = "src"
59
+
60
+ [[tool.mypy.overrides]]
61
+ module = "byteforge._c.*"
62
+ ignore_missing_imports = true
63
+
64
+ [tool.cibuildwheel]
65
+ build = "cp39-* cp310-* cp311-* cp312-* cp313-*"
66
+ skip = "*-win32 *-manylinux_i686"
67
+ test-requires = ["pytest", "pytest-benchmark", "hypothesis"]
68
+ test-command = "pytest {project}/tests/"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,37 @@
1
+ import numpy
2
+ from setuptools import Extension, setup
3
+ from setuptools.command.build_ext import build_ext as _build_ext
4
+
5
+
6
+ class optional_build_ext(_build_ext):
7
+ """Allow C extension build to fail gracefully."""
8
+
9
+ def run(self):
10
+ try:
11
+ _build_ext.run(self)
12
+ except Exception:
13
+ self._warn_unavailable()
14
+
15
+ def build_extension(self, ext):
16
+ try:
17
+ _build_ext.build_extension(self, ext)
18
+ except Exception:
19
+ self._warn_unavailable()
20
+
21
+ def _warn_unavailable(self):
22
+ print("*" * 70)
23
+ print("WARNING: byteforge C extension could not be compiled.")
24
+ print("Falling back to pure Python (slower).")
25
+ print("*" * 70)
26
+
27
+
28
+ setup(
29
+ cmdclass={"build_ext": optional_build_ext},
30
+ ext_modules=[
31
+ Extension(
32
+ "byteforge._c.ufunc",
33
+ sources=["src/byteforge/_c/ufunc.c"],
34
+ include_dirs=[numpy.get_include(), "src/byteforge/_c"],
35
+ ),
36
+ ],
37
+ )
@@ -0,0 +1,42 @@
1
+ try:
2
+ from ._version import __version__, __version_tuple__
3
+ except ImportError:
4
+ __version__ = "0.0.0"
5
+ __version_tuple__ = (0, 0, 0)
6
+
7
+ from ._base import Encoding
8
+ from ._registry import create_encoding, register
9
+ from .bcd import BCD
10
+ from .boolean import Boolean
11
+ from .dec_float import DECFloat, DECFloatG
12
+ from .gray_code import GrayCode
13
+ from .ibm_float import IBMFloat
14
+ from .ieee754 import IEEE754
15
+ from .linear_scaled import LinearScaled
16
+ from .mil_std_1750a import MilStd1750A
17
+ from .offset_binary import OffsetBinary
18
+ from .ones_complement import OnesComplement
19
+ from .ti_float import TIFloat
20
+ from .twos_complement import TwosComplement
21
+ from .unsigned import Unsigned
22
+
23
+ __all__ = [
24
+ "__version__",
25
+ "Encoding",
26
+ "register",
27
+ "create_encoding",
28
+ "BCD",
29
+ "Boolean",
30
+ "DECFloat",
31
+ "DECFloatG",
32
+ "GrayCode",
33
+ "IBMFloat",
34
+ "IEEE754",
35
+ "LinearScaled",
36
+ "MilStd1750A",
37
+ "OffsetBinary",
38
+ "OnesComplement",
39
+ "TIFloat",
40
+ "TwosComplement",
41
+ "Unsigned",
42
+ ]