Skip to content

Commit e1ded3a

Browse files
committed
Add comprehensive tests, CI/CD configs, and project documentation
- Add edge case and fuzz tests for robust coverage - Add pre-commit hooks (ruff, mypy, trailing whitespace) - Add benchmarks for encode, decode, and training - Add project files: CHANGELOG, CONTRIBUTING, SECURITY, CODE_OF_CONDUCT - Add Chinese README - Add tiktoken conversion utilities (_tiktoken.py) - Add model I/O with versioned file format (_model_io.py, _version.py) - Fix pyproject.toml package discovery configuration - Regenerate test fixtures with v1 model format - Remove legacy build_setup.py and README_en.md
1 parent 71ed55d commit e1ded3a

48 files changed

Lines changed: 2953 additions & 2346 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/lint.yml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ on:
55
branches: [ main ]
66
paths:
77
- "tinybpe/**"
8+
- "src/**"
9+
- ".github/workflows/lint.yml"
810
pull_request:
911

1012
jobs:
@@ -19,9 +21,11 @@ jobs:
1921
uses: actions/setup-python@v5
2022
with:
2123
python-version: ${{ matrix.python-version }}
22-
- name: Install dependency
23-
run: pip install flake8 mypy
24-
- name: Run flake8
25-
run: flake8 tinybpe/ --exclude "__init__.py" --max-line-length=127 --statistics
24+
- name: Install dependencies
25+
run: pip install ruff mypy
26+
- name: Run ruff (lint)
27+
run: ruff check tinybpe/ --output-format=github
28+
- name: Run ruff (format check)
29+
run: ruff format --check tinybpe/
2630
- name: Run mypy
27-
run: mypy tinybpe/
31+
run: mypy tinybpe/ --strict

.github/workflows/python-package.yml

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@ on:
88
- "tinybpe/**"
99
- "tests/**"
1010
- "setup.py"
11-
- "build_setup.py"
11+
- "pyproject.toml"
12+
- "requirements_dev.txt"
1213
- ".github/workflows/python-package.yml"
1314
pull_request:
1415
paths:
1516
- "src/**"
1617
- "tinybpe/**"
1718
- "tests/**"
1819
- "setup.py"
19-
- "build_setup.py"
20+
- "pyproject.toml"
21+
- "requirements_dev.txt"
2022
- ".github/workflows/python-package.yml"
2123
schedule:
2224
- cron: '0 10 * * *'
@@ -39,7 +41,6 @@ jobs:
3941
steps:
4042
- uses: actions/checkout@v4
4143
with:
42-
# This is necessary to get the PR head instead of base
4344
ref: ${{ github.event.pull_request.head.sha }}
4445
- name: Set up Python ${{ matrix.python-version }}
4546
uses: actions/setup-python@v5
@@ -49,25 +50,34 @@ jobs:
4950
run: |
5051
python -m pip install --upgrade pip
5152
pip install -r requirements_dev.txt
52-
- name: Build dist
53-
if: matrix.os != 'windows-latest'
53+
- name: Build and install
5454
run: |
55-
python -m build
56-
pip install dist/*.whl
57-
- name: Build dist on Windows
58-
if: matrix.os == 'windows-latest'
59-
run: |
60-
python -m build
61-
pip install (Get-ChildItem dist/*.whl)
55+
pip install -e .
6256
- name: Test with pytest
6357
run: |
64-
python build_setup.py build_ext
65-
python -m pytest
66-
- name: Generate coverage report
67-
run: |
68-
pytest --cov --cov-branch --cov-report=xml
58+
python -m pytest --cov=tinybpe --cov-branch --cov-report=xml
6959
- name: Upload coverage reports to Codecov
7060
uses: codecov/codecov-action@v5
7161
with:
7262
token: ${{ secrets.CODECOV_TOKEN }}
7363
slug: neluca/tinybpe
64+
65+
build-sdist:
66+
runs-on: ubuntu-latest
67+
steps:
68+
- uses: actions/checkout@v4
69+
- name: Set up Python
70+
uses: actions/setup-python@v5
71+
with:
72+
python-version: '3.11'
73+
- name: Install dependencies
74+
run: |
75+
python -m pip install --upgrade pip
76+
pip install build
77+
- name: Build sdist
78+
run: python -m build --sdist
79+
- name: Install from sdist
80+
run: |
81+
pip install dist/*.tar.gz
82+
- name: Verify import
83+
run: python -c "import tinybpe; print(tinybpe.__version__)"

.gitignore

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,4 +172,12 @@ cython_debug/
172172

173173
# PyPI configuration file
174174
.pypirc
175-
t_*
175+
t_*
176+
177+
# IDE
178+
.idea/
179+
.vscode/
180+
*.swp
181+
*.swo
182+
*~
183+
.DS_Store

.pre-commit-config.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
repos:
2+
- repo: https://github.com/astral-sh/ruff-pre-commit
3+
rev: v0.7.0
4+
hooks:
5+
- id: ruff
6+
args: ["--fix"]
7+
- id: ruff-format
8+
9+
- repo: https://github.com/pre-commit/mypy-mirror
10+
rev: v1.13.0
11+
hooks:
12+
- id: mypy
13+
files: ^tinybpe/
14+
args: ["--strict"]
15+
16+
- repo: https://github.com/pre-commit/pre-commit-hooks
17+
rev: v5.0.0
18+
hooks:
19+
- id: trailing-whitespace
20+
- id: end-of-file-fixer
21+
- id: check-yaml
22+
- id: check-toml
23+
- id: check-added-large-files

CHANGELOG.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Added
11+
- `SECURITY.md` with vulnerability reporting process
12+
- `CONTRIBUTING.md` with development setup guide
13+
- `CODE_OF_CONDUCT.md` adopting Contributor Covenant
14+
- `py.typed` marker for PEP 561 compliance
15+
- Pre-commit hooks configuration (ruff, mypy)
16+
- `_version.py` for lightweight version access without C extension import
17+
- `n_vocab` property as consistent alias for vocabulary size
18+
- `n_merges` property as consistent alias for merges count
19+
- `train()` convenience method on Trainer with optional progress callback
20+
- Bounds checking in `bpe_decode` to prevent segfault on invalid IDs
21+
- Overflow guards on memory allocations in C layer
22+
- Model file format versioning (`TinyBPE Model v1`)
23+
24+
### Changed
25+
- Renamed `Tokenizer.size` to `Tokenizer.n_vocab` for naming consistency
26+
- Renamed `Trainer.merges_size` to `Trainer.n_merges` for naming consistency
27+
- Split `_utils.py` into `_model_io.py` and `_tiktoken.py`
28+
- Improved `save_bpe_vocab` to write tab-separated, machine-parseable format
29+
- Added NULL-safety to `bpe_merges_free` and `bpe_vocab_free`
30+
- Optimized list flattening in `encode_ordinary` to avoid O(n²) behavior
31+
- Updated CI from flake8 to ruff
32+
- Removed `build_setup.py` in favor of standard setuptools build
33+
34+
### Fixed
35+
- Integer truncation bug in pair validation (`(int)` cast on unsigned long)
36+
- Missing NULL checks after `bpe_encode`, `bpe_decode`, `bpe_malloc` calls
37+
- Duplicate `bpe_utf8_head_check` function removed
38+
- C struct fields `_1`/`_2` renamed to `left`/`right` for clarity
39+
40+
### Security
41+
- Added bounds checking in `bpe_decode` to prevent out-of-bounds memory access
42+
- Added overflow guards on all size computations before `malloc`
43+
44+
## [0.1.1] - 2025-07-10
45+
46+
### Added
47+
- Initial public release
48+
- CPython BPE tokenizer (encode, decode, streaming decode)
49+
- CPython BPE trainer with support for continuing training
50+
- AVL-tree based merge index for fast pair lookup
51+
- Byte remapping support (compatible with tiktoken models)
52+
- Regex pre-tokenization support
53+
- Special tokens support
54+
- tiktoken model conversion utilities
55+
- Pre-built wheels for Linux (x86_64, aarch64), macOS (x86_64, arm64), Windows (x86_64)
56+
- CI/CD pipelines for build, test, lint, and PyPI release

CODE_OF_CONDUCT.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Contributor Covenant Code of Conduct
2+
3+
## Our Pledge
4+
5+
We as members, contributors, and leaders pledge to make participation in our
6+
community a harassment-free experience for everyone, regardless of age, body
7+
size, visible or invisible disability, ethnicity, sex characteristics, gender
8+
identity and expression, level of experience, education, socio-economic status,
9+
nationality, personal appearance, race, religion, or sexual identity
10+
and orientation.
11+
12+
We pledge to act and interact in ways that contribute to an open, welcoming,
13+
diverse, inclusive, and healthy community.
14+
15+
## Our Standards
16+
17+
Examples of behavior that contributes to a positive environment for our
18+
community include:
19+
20+
* Demonstrating empathy and kindness toward other people
21+
* Being respectful of differing opinions, viewpoints, and experiences
22+
* Giving and gracefully accepting constructive feedback
23+
* Accepting responsibility and apologizing to those affected by our mistakes,
24+
and learning from the experience
25+
* Focusing on what is best not just for us as individuals, but for the
26+
overall community
27+
28+
Examples of unacceptable behavior include:
29+
30+
* The use of sexualized language or imagery, and sexual attention or
31+
advances of any kind
32+
* Trolling, insulting or derogatory comments, and personal or political attacks
33+
* Public or private harassment
34+
* Publishing others' private information, such as a physical or email
35+
address, without their explicit permission
36+
* Other conduct which could reasonably be considered inappropriate in a
37+
professional setting
38+
39+
## Enforcement Responsibilities
40+
41+
Community leaders are responsible for clarifying and enforcing our standards of
42+
acceptable behavior and will take appropriate and fair corrective action in
43+
response to any behavior that they deem inappropriate, threatening, offensive,
44+
or harmful.
45+
46+
## Scope
47+
48+
This Code of Conduct applies within all community spaces, and also applies when
49+
an individual is officially representing the community in public spaces.
50+
51+
## Enforcement
52+
53+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
54+
reported to the community leaders responsible for enforcement at
55+
myneluca@gmail.com. All complaints will be reviewed and investigated promptly and fairly.
56+
57+
## Attribution
58+
59+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
60+
version 2.1, available at
61+
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
62+
63+
[homepage]: https://www.contributor-covenant.org
64+
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html

CONTRIBUTING.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Contributing to TinyBPE
2+
3+
Thank you for your interest in contributing! TinyBPE is a CPython extension implementing a fast, lightweight BPE tokenizer and trainer.
4+
5+
## Development Setup
6+
7+
### Prerequisites
8+
9+
- Python 3.9+
10+
- A C compiler (GCC on Linux, Clang on macOS, MSVC on Windows)
11+
- Git
12+
13+
### Setting up the development environment
14+
15+
```bash
16+
# Clone the repository
17+
git clone https://github.com/neluca/tinybpe.git
18+
cd tinybpe
19+
20+
# Create a virtual environment
21+
python -m venv .venv
22+
source .venv/bin/activate # Linux/macOS
23+
# .venv\Scripts\activate # Windows
24+
25+
# Install development dependencies
26+
pip install -r requirements_dev.txt
27+
28+
# Build the C extension in-place
29+
pip install -e .
30+
31+
# Run tests
32+
python -m pytest
33+
```
34+
35+
### Building the C extension manually
36+
37+
```bash
38+
# Build the extension module (places .pyd/.so in the source tree)
39+
python setup.py build_ext --inplace
40+
41+
# Or use pip for an editable install
42+
pip install -e .
43+
```
44+
45+
## Coding Conventions
46+
47+
### C code (`src/`)
48+
- **Standard**: C99
49+
- **Style**: Consistent with existing code — 4-space indentation, snake_case for function and variable names
50+
- **Naming**: Use descriptive names. Struct fields use `snake_case`. Functions are prefixed with `bpe_`.
51+
- **Memory**: Use `bpe_malloc` / `bpe_free` wrappers which integrate with Python's memory manager. Always check for NULL returns.
52+
- **Error handling**: Set Python exceptions via `PyErr_SetString` / `PyErr_NoMemory` and return NULL or -1.
53+
54+
### Python code (`tinybpe/`)
55+
- **Standard**: Python 3.9+ compatible
56+
- **Style**: Follow [PEP 8](https://peps.python.org/pep-0008/) — enforced by ruff
57+
- **Type hints**: All public methods must have type annotations
58+
- **Docstrings**: NumPy-style docstrings required for all public classes and methods
59+
60+
### Pre-commit hooks
61+
62+
```bash
63+
# Install pre-commit hooks (run once)
64+
pre-commit install
65+
66+
# Run all hooks manually
67+
pre-commit run --all-files
68+
```
69+
70+
## Running Tests
71+
72+
```bash
73+
# Run all tests
74+
python -m pytest
75+
76+
# Run with coverage
77+
python -m pytest --cov=tinybpe --cov-report=term
78+
79+
# Run a specific test file
80+
python -m pytest tests/test_tinybpe.py
81+
```
82+
83+
## Pull Request Process
84+
85+
1. Fork the repository and create a feature branch from `main`.
86+
2. Make your changes, following the coding conventions above.
87+
3. Add or update tests as needed.
88+
4. Run `pre-commit run --all-files` to ensure style compliance.
89+
5. Run `python -m pytest` to verify all tests pass.
90+
6. Update `CHANGELOG.md` under the `[Unreleased]` section.
91+
7. Submit a pull request with a clear description of the changes.
92+
93+
## Project Structure
94+
95+
```
96+
tinybpe/
97+
├── src/ # C extension source
98+
│ ├── _tree_core.h/c # AVL tree (self-balancing binary search tree)
99+
│ ├── bpe_common.h/c # Common types (pairs, pieces) and utilities
100+
│ ├── bpe_tokenizer.h/c # Encoder/decoder implementation
101+
│ ├── bpe_trainer.h/c # BPE training algorithm
102+
│ └── bpe_module.c # CPython type definitions and method wrappers
103+
├── tinybpe/ # Python package
104+
│ ├── __init__.py # Public API exports
105+
│ ├── _version.py # Single source of truth for version
106+
│ ├── _model_io.py # Model file save/load
107+
│ ├── _tiktoken.py # tiktoken model conversion
108+
│ ├── core.py # CommonTokenizer and Tokenizer wrappers
109+
│ ├── simple.py # SimpleTrainer wrapper
110+
│ └── bpe.pyi # Type stubs for the C extension
111+
├── tests/ # Test suite
112+
├── examples/ # Example scripts
113+
├── benchmarks/ # Performance benchmarks
114+
└── .github/workflows/ # CI/CD pipelines
115+
```
116+
117+
## License
118+
119+
By contributing, you agree that your contributions will be licensed under the MIT License.

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2025 E.T. Romani
3+
Copyright (c) 2025-2026 E.T. Romani
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

0 commit comments

Comments
 (0)