Developing Scenic
This page covers information useful if you will be developing Scenic, either changing the language itself or adding new built-in libraries or simulator interfaces.
To find documentation (and code) for specific parts of Scenic’s implementation, see our page on Scenic Internals.
Getting Started
Start by cloning our repository on GitHub and setting up your virtual environment. Then to install Scenic and its development dependencies in your virtual environment run:
$ python -m pip install -e ".[dev]"
This will perform an “editable” install, so that any changes you make to Scenic’s code will take effect immediately when running Scenic in your virtual environment.
Scenic uses the isort and black tools to automatically sort import
statements and enforce a consistent code style.
Run the command pre-commit install to set up hooks which will run every time you commit and correct any formatting problems (you can then inspect the files and try committing again).
You can also manually run the formatters on the files changed since the last commit with pre-commit run. [1]
Running the Test Suite
Scenic has an extensive test suite exercising most of the features of the language. We use the pytest Python testing tool. To run the entire test suite, run the command pytest inside the virtual environment from the root directory of the repository.
Some of the tests are quite slow, e.g. those which test the parsing and construction of
road networks. We add a --fast
option to pytest which skips such tests, while
still covering all of the core features of the language. So it is convenient to often run
pytest --fast as a quick check, remembering to run the full pytest
before making any final commits. You can also run specific parts of the test suite with a
command like pytest tests/syntax/test_specifiers.py, or use pytest’s -k
option to filter by test name, e.g. pytest -k specifiers.
Note that many of Scenic’s tests are probabilistic, so in order to reproduce a test
failure you may need to set the random seed. We use the
pytest-randomly plugin to help with
this: at the beginning of each run of pytest
, it prints out a line like:
Using --randomly-seed=344295085
Adding this as an option, i.e. running pytest --randomly-seed=344295085, will reproduce the same sequence of tests with the same Python/Scenic random seed. As a shortcut, you can use --randomly-seed=last to use the seed from the previous testing run.
If you’re running the test suite on a headless server or just want to stop windows from popping up during testing, use the --no-graphics option to skip graphical tests.
Prior to finalizing a PR or other substantial changes, it’s a good idea to run the test suite under all major versions of Python that Scenic supports, in fresh virtual environments. You can do this automatically with the command tox, which by default will test all supported major versions both with and without optional dependencies (this will take a long time). Some variations:
tox -p will run the various combinations in parallel.
tox -m basic skips testing installations with the optional dependencies.
tox -- --fast only runs the “fast” tests. In general, any arguments after the -- will get passed to
pytest
. For example,tox -- tests/syntax/test_specifiers.py only runs the tests in the given file.
See the Tox website for more information about the available options and how to configure Tox.
Debugging
You can use Python’s built-in debugger pdb
to debug the parsing, compilation, sampling,
and simulation of Scenic programs. The Scenic command-line option -b
will cause the
backtraces printed from uncaught exceptions to include Scenic’s internals; you can also
use the --pdb
option to automatically enter the debugger on such exceptions.
If you’re trying to figure out why a scenario is taking many iterations of rejection
sampling, first use the --verbosity
option to print out the reason for each
rejection. If the problem doesn’t become clear, you can use the --pdb-on-reject
option to automatically enter the debugger when a scene or simulation is rejected.
If you’re using the Python API instead of invoking Scenic from the command line, these
debugging features can be enabled using the following function from the scenic
module:
- setDebuggingOptions(*, verbosity=0, fullBacktrace=False, debugExceptions=False, debugRejections=False)[source]
Configure Scenic’s debugging options.
- Parameters:
verbosity (int) – Verbosity level. Zero by default, although the command-line interface uses 1 by default. See the
--verbosity
option for the allowed values.fullBacktrace (bool) – Whether to include Scenic’s innards in backtraces (like the
-b
command-line option).debugExceptions (bool) – Whether to use
pdb
for post-mortem debugging of uncaught exceptions (like the--pdb
option).debugRejections (bool) – Whether to enter
pdb
when a scene or simulation is rejected (like the--pdb-on-reject
option).
It is possible to put breakpoints into a Scenic program using the Python built-in
function breakpoint
. Note however that since code in a Scenic program is not always
executed the way you might expect (e.g. top-level code is only run once, whereas code in
requirements can run every time we generate a sample: see How Scenic is Compiled), some care is needed when
interpreting what you see in the debugger. The same consideration applies when adding
print
statements to a Scenic program. For example, a top-level print(x)
will
not print out the actual value of x
every time a sample is generated: instead,
you will get a single print at compile time, showing the Distribution
object which
represents the distribution of x
(and which is bound to x
in the Python
namespace used internally for the Scenic module).
Building the Documentation
Scenic’s documentation is built using Sphinx. The
freestanding documentation pages (like this one) are found under the docs
folder, written in the reStructuredText format.
The detailed documentation of Scenic’s internal classes, functions, etc. is largely
auto-generated from their docstrings, which are written in a variant of Google’s style
understood by the Napoleon
Sphinx extension (see the docstring of Scenario.generate
for a simple example: click
the [source]
link to the right of the function signature to see the code).
If you modify the documentation, you should build a copy of it locally to make sure
everything looks good before you push your changes to GitHub (where they will be picked
up automatically by ReadTheDocs). To compile the
documentation, enter the docs
folder and run make html. The output
will be placed in the docs/_build/html
folder, so the root page will be at
docs/_build/html/index.html
. If your changes do not appear, it’s possible that Sphinx
has not detected them; you can run make clean to delete all the files from the
last compilation and start from a clean slate.
Scenic extends Sphinx in a number of ways to improve the presentation of Scenic code and
add various useful features: see docs/conf.py
for full details. Some of the most
commonly-used features are:
a
scenic
role which extends the standard Sphinxsamp
role with Scenic syntax highlighting;a
sampref
role which makes a cross-reference likekeyword
but allows emphasizing variables likesamp
;the
term
role for glossary terms is extended so that the cross-reference will work even if the link is plural but the glossary entry is singular or vice versa.
Footnotes