Skip to content

Commit 49996e4

Browse files
brentyiClaude
and
Claude
authored
Add transpiler for Python 3.10 (#41)
* Add type stripping utility that preserves Any Added script to strip type annotations from Python source files while replacing them with `Any` instead of removing them entirely. This approach maintains dataclass compatibility and proper generic class behavior. Key features: - Replace type annotations with `Any` instead of removing them - Preserve `ClassVar` and `jdc.Static` wrappers (replace inner types) - Add `__class_getitem__` methods to generic classes for subscriptability - Handle PEP 695 type parameters - Remove overloaded function definitions - Process TYPE_CHECKING blocks correctly 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Update strip_types.py to remove comments and docstrings - Add functionality to remove all comments from Python files - Remove function/class/module docstrings - Remove dataclass field documentation strings - Preserve Any imports and handle imports correctly - All types still replaced with Any as required for dataclasses 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Refactor and rename to transpile_py310.py - Clean up and refactor code for better maintainability - Rename from strip_types.py to transpile_py310.py to better reflect purpose - Organize code into logical sections with clear separation of concerns - Remove debug comments and improve documentation - Maintain all functionality: type stripping, comment removal, docstring removal - Add comprehensive error handling and better function naming 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Fix type errors in transpile_py310.py - Fix return type incompatibility for leave_Call method - Initialize new_body variable to avoid unbound variable errors - Handle SimpleStatementSuite body conversion properly - Remove assert_never special handling to fix type issues - All pyright type errors now resolved 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Enhance comment removal in transpiler - Add leave_Comment method to remove standalone comments - Add leave_EmptyLine method to remove empty lines with comments - Ensure trailing comments are removed from SimpleStatementLine - Now removes all types of comments: trailing, standalone, and on empty lines - Tested and working correctly 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Add Python 3.10 support via transpilation Implements automatic transpilation to support Python 3.10 while maintaining modern type annotations in the source code: - Update required Python version to >=3.10 - Add transpile_py310.py script that replaces type annotations with Any - Create _py310 directory with transpiled code for older Python versions - Add conditional imports in __init__.py based on Python version - Add pytest tests for core functionality - Add GitHub Actions CI for Python 3.10, 3.11, and 3.12 - Configure pyright to exclude transpiled files 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Add Python version compatibility matrix to README Clearly documents which Python versions are supported and how: - Python 3.12-3.13: Fully supported and recommended - Python 3.10-3.11: Supported via transpiled compatibility layer - Python <3.10: Not supported 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Add Python 3.13 to CI test matrix 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Exclude _py310 directory from ruff checks Prevents ruff from checking/formatting transpiled code 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Update pytest workflow with SuiteSparse dependencies - Add libsuitesparse-dev installation for CHOLMOD support - Install both dev and examples extras for complete test coverage 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Add workflow to verify transpiled code is up to date Ensures that the _py310 directory contains the latest transpiled version of the source code. Fails CI if transpilation would produce different output than what's committed. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Fix transpilation check workflow - Combined libcst and ruff install into single pip command - Added cleanup of test directory before running transpiler - Improved diff comparison with better flags and output - Exclude __pycache__ from comparison - Added always() condition to cleanup step 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Add libcst to dev dependencies Required for running the transpile_py310.py script 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Simplify transpiler script and workflow - Remove command-line arguments from transpile_py310.py - Use hardcoded paths for source and output directories - Update workflow to use git diff for checking changes - Simplify workflow by removing temporary directories 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Apply ruff formatting to transpiler 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1bdd3c8 commit 49996e4

24 files changed

+4886
-17
lines changed

.github/workflows/check_transpile.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: Check Transpilation
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
check-transpilation:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v2
14+
15+
- name: Set up Python 3.12
16+
uses: actions/setup-python@v4
17+
with:
18+
python-version: "3.12"
19+
20+
- name: Install dependencies
21+
run: |
22+
pip install uv
23+
uv pip install --system -e ".[dev]"
24+
25+
- name: Run transpiler
26+
run: |
27+
python transpile_py310.py
28+
29+
- name: Check for changes
30+
run: |
31+
# Check if there are any changes to the transpiled files
32+
if ! git diff --quiet src/jaxls/_py310/; then
33+
echo "Error: Transpiled code is out of date!"
34+
echo "Changes detected:"
35+
git diff --stat src/jaxls/_py310/
36+
echo ""
37+
echo "Differences:"
38+
git diff src/jaxls/_py310/
39+
echo ""
40+
echo "Please run: python transpile_py310.py"
41+
exit 1
42+
fi
43+
echo "Transpiled code is up to date!"

.github/workflows/pytest.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: pytest
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.10", "3.11", "3.12", "3.13"]
15+
16+
steps:
17+
- uses: actions/checkout@v2
18+
- name: Set up Python ${{ matrix.python-version }}
19+
uses: actions/setup-python@v4
20+
with:
21+
python-version: ${{ matrix.python-version }}
22+
- name: Install dependencies
23+
run: |
24+
sudo apt update
25+
sudo apt install -y libsuitesparse-dev
26+
pip install uv
27+
uv pip install --system -e ".[dev,examples]"
28+
- name: Test with pytest
29+
run: |
30+
pytest

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,22 @@
55
**`jaxls`** is a library for solving sparse [NLLS](https://en.wikipedia.org/wiki/Non-linear_least_squares) and [IRLS](https://en.wikipedia.org/wiki/Iteratively_reweighted_least_squares) problems in JAX.
66
These are common in classical robotics and computer vision.
77

8-
To install on Python 3.12+:
8+
To install:
99

1010
```bash
1111
pip install "git+https://github.com/brentyi/jaxls.git[examples]"
1212
```
1313

14+
### Python Version Compatibility
15+
16+
| Python Version | Support Status | Notes |
17+
|---------------|---------------|--------|
18+
| 3.13 | ✅ Supported | Recommended |
19+
| 3.12 | ✅ Supported | Recommended |
20+
| 3.11 | ⚠️ Supported | Transpiled compatibility layer |
21+
| 3.10 | ⚠️ Supported | Transpiled compatibility layer |
22+
| <3.10 | ❌ Not supported | |
23+
1424
### Overviews
1525

1626
We provide a factor graph interface for specifying and solving least squares

pyproject.toml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ dynamic = ["version"]
1717
description = "Nonlinear least squares with JAX"
1818
readme = "README.md"
1919
license = { text="MIT" }
20-
requires-python = ">=3.12"
20+
requires-python = ">=3.10"
2121
classifiers = [
22+
"Programming Language :: Python :: 3.10",
23+
"Programming Language :: Python :: 3.11",
2224
"Programming Language :: Python :: 3.12",
2325
"License :: OSI Approved :: MIT License",
2426
"Operating System :: OS Independent"
@@ -37,7 +39,9 @@ dependencies = [
3739
[project.optional-dependencies]
3840
dev = [
3941
"pyright>=1.1.308",
42+
"pytest",
4043
"ruff",
44+
"libcst",
4145
]
4246
examples = [
4347
"tyro",
@@ -49,5 +53,11 @@ examples = [
4953
[project.urls]
5054
"GitHub" = "https://github.com/brentyi/jaxls"
5155

56+
[tool.ruff]
57+
exclude = ["_py310"]
58+
5259
[tool.ruff.lint]
5360
ignore = ["E731"] # Ignore lambda assignment warnings
61+
62+
[tool.pyright]
63+
exclude = ["transpile_py310.py", "**/_py310/**"]

src/jaxls/__init__.py

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,39 @@
1+
import sys
12
from typing import TYPE_CHECKING
23

3-
from . import utils as utils
4-
from ._core import AnalyzedLeastSquaresProblem as AnalyzedLeastSquaresProblem
5-
from ._core import Cost as Cost
6-
from ._core import LeastSquaresProblem as LeastSquaresProblem
7-
from ._lie_group_variables import SE2Var as SE2Var
8-
from ._lie_group_variables import SE3Var as SE3Var
9-
from ._lie_group_variables import SO2Var as SO2Var
10-
from ._lie_group_variables import SO3Var as SO3Var
11-
from ._solvers import ConjugateGradientConfig as ConjugateGradientConfig
12-
from ._solvers import SolveSummary as SolveSummary
13-
from ._solvers import TerminationConfig as TerminationConfig
14-
from ._solvers import TrustRegionConfig as TrustRegionConfig
15-
from ._variables import Var as Var
16-
from ._variables import VarValues as VarValues
4+
if TYPE_CHECKING or sys.version_info >= (3, 12):
5+
from . import utils as utils
6+
from ._core import AnalyzedLeastSquaresProblem as AnalyzedLeastSquaresProblem
7+
from ._core import Cost as Cost
8+
from ._core import LeastSquaresProblem as LeastSquaresProblem
9+
from ._lie_group_variables import SE2Var as SE2Var
10+
from ._lie_group_variables import SE3Var as SE3Var
11+
from ._lie_group_variables import SO2Var as SO2Var
12+
from ._lie_group_variables import SO3Var as SO3Var
13+
from ._solvers import ConjugateGradientConfig as ConjugateGradientConfig
14+
from ._solvers import SolveSummary as SolveSummary
15+
from ._solvers import TerminationConfig as TerminationConfig
16+
from ._solvers import TrustRegionConfig as TrustRegionConfig
17+
from ._variables import Var as Var
18+
from ._variables import VarValues as VarValues
19+
elif sys.version_info >= (3, 10):
20+
from ._py310 import utils as utils
21+
from ._py310._core import AnalyzedLeastSquaresProblem as AnalyzedLeastSquaresProblem
22+
from ._py310._core import Cost as Cost
23+
from ._py310._core import LeastSquaresProblem as LeastSquaresProblem
24+
from ._py310._lie_group_variables import SE2Var as SE2Var
25+
from ._py310._lie_group_variables import SE3Var as SE3Var
26+
from ._py310._lie_group_variables import SO2Var as SO2Var
27+
from ._py310._lie_group_variables import SO3Var as SO3Var
28+
from ._py310._solvers import ConjugateGradientConfig as ConjugateGradientConfig
29+
from ._py310._solvers import SolveSummary as SolveSummary
30+
from ._py310._solvers import TerminationConfig as TerminationConfig
31+
from ._py310._solvers import TrustRegionConfig as TrustRegionConfig
32+
from ._py310._variables import Var as Var
33+
from ._py310._variables import VarValues as VarValues
34+
else:
35+
assert False
36+
1737

1838
__version__ = "0.0.0"
1939

src/jaxls/_py310/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)