Development#
This document is a guide for developers who want to contribute to the project or understand its internal workings in more detail.
Please refer to CONTRIBUTING.md for how to best contribute to Newton and relevant legal information (CLA).
Installation#
For regular end-user installation, see the Installation guide.
To install Newton from source for development or contribution, first clone the repository:
git clone https://github.com/newton-physics/newton.git
cd newton
Method 1: Using uv (Recommended)#
Install uv if you don’t have it already:
curl -LsSf https://astral.sh/uv/install.sh | sh
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
Then create a local project environment with the dev dependency extras:
uv sync --extra dev
After syncing, the dev extras are available to all uv run commands
without needing to pass --extra dev each time. For example, to list all
available examples:
uv run -m newton.examples
See the Extra Dependencies section of the installation guide for a description of all available extras.
Method 2: Using pip in a Virtual Environment#
To manually manage a virtual environment, create and activate one first:
python -m venv .venv
source .venv/bin/activate
python -m venv .venv
.venv\Scripts\activate.bat
python -m venv .venv
.venv\Scripts\Activate.ps1
Then locally install Newton in editable mode with its development dependencies:
pip install -e ".[dev]" --extra-index-url https://pypi.nvidia.com/
The --extra-index-url flag points pip to the NVIDIA package index, which is
required to find warp-lang versions newer than those available on PyPI.
Python Dependency Management#
uv lockfile management#
When using uv, the lockfile
(uv.lock) is used to resolve project dependencies into exact versions for reproducibility among different machines.
We maintain a lockfile in the root of the repository that pins exact versions of all dependencies and their transitive dependencies.
Sometimes, a dependency in the lockfile needs to be updated to a newer version.
This can be done by running uv lock -P <package-name>:
uv lock -P warp-lang --prerelease allow
uv lock -P mujoco-warp
The --prerelease allow flag is needed for dependencies that use pre-release versions (e.g. warp-lang).
uv also provides a command to update all dependencies in the lockfile:
uv lock -U
Remember to commit uv.lock after running a command that updates the lockfile.
Running the tests#
The Newton test suite supports both uv and standard venv workflows,
and by default runs in up to eight parallel processes. The tests can be run
in a serial manner with --serial-fallback.
Pass --help to either run method below to see all available flags.
# install development extras and run tests
uv run --extra dev -m newton.tests
# install dev extras (including testing & coverage deps)
python -m pip install -e ".[dev]"
# run tests
python -m newton.tests
Most tests run when the dev extras are installed. The tests using PyTorch
to run inference on an RL policy are skipped if the torch dependency is
not installed. In order to run these tests, include the torch-cu12 or
torch-cu13 extras matching your NVIDIA driver’s CUDA support:
# install development extras and run tests
uv run --extra dev --extra torch-cu12 -m newton.tests
# install both dev and torch-cu12 extras (need to pull from PyTorch CUDA 12.8 wheel index)
python -m pip install --extra-index-url https://download.pytorch.org/whl/cu128 -e ".[dev,torch-cu12]"
# run tests
python -m newton.tests
Specific Newton examples can be tested in isolation via the -k argument:
# test the basic_shapes example
uv run --extra dev -m newton.tests.test_examples -k test_basic.example_basic_shapes
# test the basic_shapes example
python -m newton.tests.test_examples -k test_basic.example_basic_shapes
To generate a coverage report:
# append the coverage flags:
uv run --extra dev -m newton.tests --coverage --coverage-html htmlcov
# append the coverage flags and make sure `coverage[toml]` is installed (it comes in `[dev]`)
python -m newton.tests --coverage --coverage-html htmlcov
The file htmlcov/index.html can be opened with a web browser to view the coverage report.
Code formatting and linting#
Ruff is used for Python linting and code formatting. pre-commit can be used to ensure that local code complies with Newton’s checks. From the top of the repository, run:
uvx pre-commit run -a
python -m pip install pre-commit
pre-commit run -a
To automatically run pre-commit hooks with git commit:
uvx pre-commit install
pre-commit install
The hooks can be uninstalled with pre-commit uninstall.
Typos#
To proactively catch spelling mistakes, Newton uses the typos tool. Typos scans source files for common misspellings and is integrated into our pre-commit hooks, so spelling errors in both code and documentation are flagged when you run or install pre-commit (see above). You can also run typos manually if needed. Refer to the typos documentation for more details on usage and configuration options.
Dealing with false positives#
Typos may occasionally flag legitimate project-specific terminology, domain terms, or variable names as misspellings (false positives). To handle these, the Newton codebase configures typos in pyproject.toml at the repository root.
False positives are managed as follows:
File exclusions: The
[tool.typos]section includesfiles.extend-excludeto ignore matching files and directories, such asexamples/assetsand specific model or asset file types (e.g.,*.urdf,*.usd).Word allowlist: Words or acronyms that would otherwise be flagged can be listed in
[tool.typos.default.extend-words](e.g.,ba,HAA).Identifier allowlist: Specific identifiers, such as variable or constant names, can be declared in
[tool.typos.default.extend-identifiers](e.g.,PNGs).
When typos reports a word that is valid within the Newton codebase, you can add it to the appropriate section in pyproject.toml to suppress future warnings. After updating, re-run typos (or pre-commit) to confirm that the word is ignored. Use these options to keep the codebase clean while ensuring needed flexibility for accepted project-specific words and identifiers.
License headers#
Every source file in the repository must carry an SPDX license header.
A CI check (pr_license_check.yml) enforces this on every pull request using
Apache SkyWalking Eyes.
The required headers depend on the file type:
Python files (
.py) — Apache-2.0. See.licenserc.yamlfor the exact template.Documentation files (
.rst) — CC-BY-4.0. See.licenserc-docs.yamlfor the exact template.Jupyter notebooks (
.ipynb) — CC-BY-4.0. Copy the header from an existing notebook.
When adding a new file, copy the header from an existing file of the same type. If the license check fails on your PR, add the appropriate header to the top of each flagged file.
Using a local Warp installation with uv#
Use the following steps to run Newton with a local build of Warp:
uv venv
source .venv/bin/activate
uv sync --extra dev
uv pip install -e "warp-lang @ ../warp"
The Warp initialization message should then properly reflect the local Warp installation instead of the locked version,
e.g. when running python -m newton.examples basic_pendulum.
Building the documentation#
To build the documentation locally, ensure you have the documentation dependencies installed.
rm -rf docs/_build
uv run --extra docs --extra sim sphinx-build -W -b html docs docs/_build/html
python -m pip install -e ".[docs]"
cd path/to/newton/docs && make html
The built documentation will be available in docs/_build/html.
Note
The documentation build requires pandoc for converting Jupyter notebooks.
While pypandoc_binary is included in the [docs] dependencies, some systems may require
pandoc to be installed separately:
Ubuntu/Debian:
sudo apt-get install pandocmacOS:
brew install pandocWindows: Download from https://pandoc.org/installing.html or
choco install pandoc
Serving the documentation locally#
After building the documentation, you can serve it locally using the docs/serve.py script.
This is particularly useful for testing interactive features like the Viser 3D visualizations
in the tutorial notebooks, which require proper MIME types for WebAssembly and JavaScript modules.
uv run docs/serve.py
python docs/serve.py
Then open http://localhost:8000 in your browser. You can specify a custom port with --port:
uv run docs/serve.py --port 8080
python docs/serve.py --port 8080
Note
Using Python’s built-in http.server or simply opening the HTML files directly
will not work correctly for the interactive Viser visualizations, as they require
specific CORS headers and MIME types that serve.py provides.
Documentation Versioning#
Newton’s documentation is versioned and hosted on GitHub Pages. Multiple versions are available simultaneously, with a version switcher dropdown in the navigation bar.
How It Works#
The gh-pages branch contains versioned documentation in subdirectories:
/
├── index.html # Redirects to /stable/
├── switcher.json # Version manifest for dropdown
├── stable/ # Copy of latest release
├── latest/ # Dev docs from main branch
├── 1.1.0/ # Release versions
└── 1.0.0/
Two GitHub Actions workflows manage deployment:
docs-dev.yml: Deploys to
/latest/on every push tomaindocs-release.yml: Deploys to
/X.Y.Z/and updates/stable/on version tags
Deploying Documentation#
Dev docs are deployed automatically when changes are pushed to main.
Release docs are deployed when a version tag is pushed:
git tag v1.0.0
git push origin v1.0.0
Only strict semver tags (vX.Y.Z) trigger release deployments. Pre-release tags
like v1.0.0-rc.1 are ignored.
Manual Operations#
Removing a version (rare):
Check out the
gh-pagesbranchDelete the version directory (e.g.,
rm -rf 1.0.0)Edit
switcher.jsonto remove the entryCommit and push
Rebuilding all docs (disaster recovery): Check out each version tag, build its
docs with Sphinx, and deploy to the corresponding directory on gh-pages. Update
switcher.json after each version using scripts/ci/update_docs_switcher.py.
API documentation#
Newton’s API reference is auto-generated from the __all__ lists of its public modules.
The script docs/generate_api.py produces reStructuredText files under docs/api/ (git-ignored)
that Sphinx processes via autosummary to create individual pages for every public symbol.
Whenever you add, remove, or rename a public symbol in one of the public modules
(newton, newton.geometry, newton.solvers, newton.sensors, etc.),
regenerate the API pages:
uv run python docs/generate_api.py
python docs/generate_api.py
After running the script, rebuild the documentation to verify the result (see Building the documentation above).
Note
Only symbols listed in a module’s __all__ (or, as a fallback, its public
attributes) are included. If a new class or function in newton/_src/ should
be visible to users, re-export it through the appropriate public module first.
Testing documentation code snippets#
The doctest Sphinx builder is used to ensure that code snippets in the documentation remain up-to-date.
The doctests can be run with:
uv run --extra docs --extra sim sphinx-build -W -b doctest docs docs/_build/doctest
python -m sphinx -W -b doctest docs docs/_build/doctest
For more information, see the sphinx.ext.doctest documentation.
Changelog#
Newton maintains a CHANGELOG.md at the repository root.
When a pull request modifies user-facing behavior, add an entry under the
[Unreleased] section in the appropriate category:
Added — new features
Changed — changes to existing functionality (include migration guidance)
Deprecated — features that will be removed (include migration guidance, e.g. “Deprecate
Model.geo_meshesin favor ofModel.shapes”)Removed — removed features (include migration guidance)
Fixed — bug fixes
Use imperative present tense (“Add X”, not “Added X”) and keep entries concise. Internal implementation details (refactors, CI tweaks) that do not affect users should not be listed.
Style Guide#
Follow PEP 8 for Python code.
Use Google-style docstrings (compatible with Napoleon extension).
Write clear, concise commit messages.
Keep pull requests focused on a single feature or bug fix.
Use kebab-case instead of snake_case for command line arguments, e.g.
--use-cuda-graphinstead of--use_cuda_graph.
Writing examples#
Examples live in newton/examples/<category>/example_<category>_<name>.py (e.g.
newton/examples/basic/example_basic_pendulum.py). Each file defines an Example
class with the following interface:
class Example:
def __init__(self, viewer, args):
"""Build the model, create solver/state/control, and set up the viewer."""
...
def step(self):
"""Advance the simulation by one frame (typically with substeps)."""
...
def render(self):
"""Update the viewer with the current state."""
...
def test_final(self):
"""Validate the final simulation state. Required for CI."""
...
def test_post_step(self):
"""Optional per-step validation, called after every step() in test mode."""
...
Every example must implement test_final() (or test_post_step(), or both).
The test harness runs examples with --viewer null --test and calls these methods to
verify simulation correctness. An example that implements neither will raise
NotImplementedError in CI.
Discovery and registration#
Examples are discovered automatically: any file matching
newton/examples/<category>/example_*.py is picked up by newton.examples.get_examples().
The short name used on the command line is the filename without the example_ prefix and
.py extension (e.g. basic_pendulum).
New examples must also be registered in the examples README.md with a
python -m newton.examples <example_name> command and a 320x320 jpg screenshot.
# list all available examples
uv run -m newton.examples
# run an example by short name
uv run -m newton.examples basic_pendulum
# run in headless test mode (used by CI)
uv run -m newton.examples basic_pendulum --viewer null --test
# list all available examples
python -m newton.examples
# run an example by short name
python -m newton.examples basic_pendulum
# run in headless test mode (used by CI)
python -m newton.examples basic_pendulum --viewer null --test
Roadmap and Future Work#
(Placeholder for future roadmap and planned features)
Advanced solver coupling
More comprehensive sensor models
Expanded robotics examples
See the GitHub Discussions and GitHub Roadmap for ongoing feature planning.
Benchmarking with airspeed velocity#
The Newton repository contains a benchmarking suite implemented using the airspeed velocity framework. The full set of benchmarks is intended to be run on a machine with a CUDA-capable GPU.
To get started, install airspeed velocity from PyPI:
python -m pip install asv
Tip
With uv, airspeed velocity can be run without installing it into the
project environment by using uvx:
uvx --with virtualenv asv run --launch-method spawn ...
If airspeed velocity has not been previously run on the machine, it will need to be initialized with:
asv machine --yes
To run the benchmarks, run the following command from the root of the repository:
asv run --launch-method spawn main^!
asv run --launch-method spawn main^^!
Note
On Windows CMD, the ^ character is an escape character, so it must be doubled (^^) to be interpreted literally.
The benchmarks discovered by airspeed velocity are in the asv/benchmarks directory. This command runs the
benchmark code from the asv/benchmarks directory against the code state of the main branch. Note that
the benchmark definitions themselves are not checked out from different branches—only the code being
benchmarked is.
Benchmarks can also be run against a range of commits using the commit1...commit2 syntax.
This is useful for comparing performance across several recent changes:
asv run --launch-method spawn HEAD~4..HEAD
asv run --launch-method spawn HEAD~4..HEAD
Commit hashes can be used instead of relative references:
asv run --launch-method spawn abc1234..def5678
asv run --launch-method spawn abc1234..def5678
Running benchmarks standalone#
Benchmark files can also be run directly as Python scripts, without the airspeed velocity
harness. This is useful for quick iteration during development since it skips the
environment setup that airspeed velocity performs. Each benchmark file under
asv/benchmarks/ supports a --bench flag to select specific benchmark classes:
uv run python asv/benchmarks/simulation/bench_mujoco.py --bench FastAllegro
python asv/benchmarks/simulation/bench_mujoco.py --bench FastAllegro
When --bench is omitted, all benchmarks in the file are run. The --bench flag can
be repeated to select multiple benchmarks:
uv run python asv/benchmarks/simulation/bench_mujoco.py --bench FastAllegro --bench FastG1
python asv/benchmarks/simulation/bench_mujoco.py --bench FastAllegro --bench FastG1
Tips for writing benchmarks#
Rather than running the entire benchmark suite, use the --bench BENCH, -b BENCH flag to filter the benchmarks
to just the ones under development:
asv run --launch-method spawn main^! --bench FastG1
asv run --launch-method spawn main^^! --bench FastG1
The most time-consuming benchmarks are those that measure the time it takes to load and run one frame of the example
starting from an empty kernel cache.
These benchmarks have names ending with time_load. It is sometimes convenient to exclude these benchmarks
from running by using the following command:
asv run --launch-method spawn main^! -b '^(?!.*time_load$).*'
asv run --launch-method spawn main^^! -b "^^(?!.*time_load$).*"
While airspeed velocity has built-in mechanisms to determine automatically how to collect measurements,
it is often useful to manually specify benchmark attributes like repeat and number to control the
number of times a benchmark is run and the number of times a benchmark is repeated.
class PretrainedSimulate:
repeat = 3
number = 1
As the airspeed documentation on benchmark attributes notes,
the setup and teardown methods are not run between the number iterations that make up a sample.
These benchmark attributes should be tuned to ensure that the benchmark runs in a reasonable amount of time while also ensuring that the benchmark is run a sufficient number of times to get a statistically meaningful result.
The --durations all flag can be passed to the asv run command to show the durations of all benchmarks,
which is helpful for ensuring that a single benchmark is not requiring an abnormally long amount of time compared
to the other benchmarks.