Skip to content

Three NameErrors in src/scenic, and nothing checks for undefined names #493

Description

@Arthur031221

System Details

  1. Python Version: Python 3.12.3
  2. Scenic Version: 3.2.0b1, source checkout at 2fc1634. Also reproduced on 3.1.1 installed from PyPI.
  3. Operating System / Platform: Ubuntu 24.04.4 LTS, Linux 7.0.0-28-generic x86_64
  4. Simulator Version: not applicable

Detailed Description

  1. Description

Nothing in this project checks for undefined names. .pre-commit-config.yaml pins black and isort. No workflow, pyproject.toml, tox.ini or toxfile.py runs flake8, ruff or pyflakes. pyflakes 3.4.0 reports 15 undefined names across 10 files in src/scenic. Three of them raise NameError when the line runs, and I ran all three.

src/scenic/core/distributions.py:822:33: undefined name 'arg'
src/scenic/core/object_types.py:313:50: undefined name 'name'
src/scenic/core/object_types.py:361:57: undefined name 'name'
src/scenic/core/regions.py:2563:23: undefined name 'other'
src/scenic/core/regions.py:2566:23: undefined name 'other'
src/scenic/core/sensors.py:92:15: undefined name 'Optional'
src/scenic/core/utils.py:115:16: undefined name 'oldHandler'
src/scenic/core/vectors.py:561:19: undefined name 'bz'
src/scenic/core/vectors.py:562:29: undefined name 'bz'
src/scenic/core/visibility.py:569:17: undefined name 'SpheroidRegion'
src/scenic/domains/driving/roads.py:590:25: undefined name 'Dict'
src/scenic/domains/driving/roads.py:848:15: undefined name 'Dict'
src/scenic/simulators/carla/utils/visuals.py:241:19: undefined name 'collections'
src/scenic/simulators/carla/utils/visuals.py:311:9: undefined name 'set_transform'
src/scenic/simulators/webots/simulator.py:139:19: undefined name 'SimulationCreationError'

syntax/veneer.py produces 18 more of the form undefined name 'Left' in __all__. Those are names Scenic injects itself and are not counted above. ruff classifies them F822 rather than F821, so an F821 check does not fire on them.

The three that run:

  • OperatorDistribution.evaluateInner, distributions.py:822. Line 820 binds arg in its generator expression and uses it. The dict comprehension two lines below binds kwarg and uses arg. Reached whenever a random value is called with a keyword argument whose value needs lazy evaluation.
  • Vector.cross, vectors.py:559, unpacks into bx, by, ba while lines 561 and 562 use bz. Vector.dot immediately above it uses ox, oy, oz and is correct.
  • PolygonalFootprintRegion.containsRegionInner(self, reg, tolerance), regions.py:2560, tests isinstance(other, ...) at 2563 and 2566. The parameter is reg. Region.containsRegion ends with return self.containsRegionInner(reg, tolerance).

Those three account for five of the fifteen lines. The other ten I have not run, so please read them as places where a name is used with no binding pyflakes can see, not as ten more failures. utils.py:115 is at least dead: noNesting=True is passed nowhere in src or tests.

The suite is green at this commit. pytest --no-graphics -q -p no:randomly, after pip install -e ".[test-full]", gives 1761 passed, 391 skipped, 1 xfailed, 0 failed in 669s. Three lines that raise NameError when executed, and nothing red. The skip count depends on which optional simulators are installed.

This class of defect has reached a user once already. Issue #189, October 2023: someone hit NameError: name 'cubic_elem' is not defined in xodr_parser.py. The reply was "Unfortunately it wasn't detected by our test suite because we don't have an XODR file with that kind of curve in it." Three days later ea74a672, a one line change whose message is "Attempt bugfix, but new error pops up", corrected the name on the OpendriveParser branch, and that branch never reached main. On main the name survived until 62da37a1 deleted the surrounding block on 2026-05-15 as part of #467, which is about inconsistent planView lengths. That name is in v3.0.0, v3.1.0 and v3.1.1, and v3.1.1 went out eight days before it disappeared from main. Running the same pyflakes command over the 3.1.1 sdist reports 16 rather than 15, and the extra one is:

src/scenic/formats/opendrive/xodr_parser.py:1450:25: undefined name 'cubic_elem'

A check like this pays for itself on unfinished work, which is where it would run. PR #487, "[WIP] Various Improvements", is an open draft, head cefd7ec3, last commit dated 2026-07-27. Nothing there is finished and I am not treating it as a bug report. Sweeping it with the same command is just what a commit hook would have done: that branch fixes containsRegionInner (line 2741 there uses reg), still carries the cross one, and has two new ones:

src/scenic/core/regions.py:4195:26: undefined name 'point'
src/scenic/core/simulators.py:1089:42: undefined name 'collections'

PointSetRegion.closestPointTo(self, target) there begins point = toVector(point).coordinates, which is the first line of distanceTo directly above it. closestPointTo appears nowhere in src on main, so that method is new. A hook would have flagged both at the commit that wrote them rather than at review or after a merge.

A check that catches all 15:

  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.16.1
    hooks:
      - id: ruff-check
        files: ^src/
        args: [--select, F821, --target-version, py311]

pre-commit run --all-files with that config, on a clean checkout of 2fc1634, ends with Found 15 errors. and they are the same 15.

Both extra arguments are load bearing, and I only found that out by running it. All four combinations, same command, same clean checkout:

files: ^src/ --target-version py311 errors
yes yes 15
no yes 43
yes no 16
no no 359

The 359 is 44 F821 plus 315 invalid-syntax, the latter from match statements in tests/ that ruff refuses to parse because pyproject.toml:20 sets requires-python = ">=3.8". Of the 43, 27 are in tests/ and 26 of those are in tests/syntax/polychrome.py.

--target-version py311 is doing two separate jobs, and I would not suggest it as a project setting since it contradicts tox.ini's py38 through py314. Without files: ^src/ it is what lets ruff parse your test files at all. With files: ^src/ it is still what keeps the count at 15 rather than 16: the extra one is core/errors.py:158 BaseExceptionGroup, which ruff flags correctly under requires-python = ">=3.8" even though the line is guarded by sys.version_info >= (3, 11) at runtime. If you would rather not pin a version in the hook, # noqa: F821 on that line gets the same 15. I checked that: files: ^src/, args: [--select, F821], noqa added, Found 15 errors.

The existing 15 need a decision either way. pre-commit runs on changed files, so the hook does not report them by itself. What it does is hand the next person who touches regions.py or roads.py a failing commit for a line they did not write.

If you would rather have three separate bug reports and no process argument, say so and close this.

  1. Command
scenic --2d -S -b lazykw.scenic
  1. Error log

Tail of the -b traceback, which is 95 lines in full. Only the path prefix up to the checkout root is cut:

  File ".../src/scenic/core/lazy_eval.py", line 226, in valueInContext
    return value.evaluateIn(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../src/scenic/core/distributions.py", line 180, in evaluateIn
    value = super().evaluateIn(context)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../src/scenic/core/lazy_eval.py", line 57, in evaluateIn
    value = self.evaluateInner(context)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../src/scenic/core/distributions.py", line 822, in evaluateInner
    key: valueInContext(arg, context) for key, kwarg in self.kwoperands.items()
                        ^^^
NameError: name 'arg' is not defined

Steps To Reproduce

The list of 15:

git clone https://github.com/BerkeleyLearnVerify/Scenic && cd Scenic
python -m pip install -e . pyflakes
python -m pyflakes src/scenic | grep "undefined name '" | grep -v "in __all__" | sort

That prints the block above and takes a second or two here. The grep -v drops the 18 veneer.py __all__ entries.

lazykw.scenic, for the traceback above:

def scaled(value, factor=1):
    return value * factor

def shifted(value, factor=1):
    return value + factor

vf = VectorField("Foo", lambda pos: 2 * pos.x)
x = (0 relative to vf).yaw
ego = new Object at 0.5 @ 0, with output Uniform(scaled, shifted)(10, factor=x)

The other two, in Python:

from scenic.core.vectors import Vector
Vector(1, 0, 0).dot(Vector(0, 1, 0))     # 0
Vector(1, 0, 0).cross(Vector(0, 1, 0))   # NameError: name 'bz' is not defined

from scenic.core.regions import PolygonalRegion
import shapely.geometry as sg
big = PolygonalRegion(polygon=sg.box(-10, -10, 10, 10)).footprint
small = PolygonalRegion(polygon=sg.box(-1, -1, 1, 1)).footprint
big.containsRegion(small)                # NameError: name 'other' is not defined

All three behave the same on 3.1.1 from PyPI, at the same line numbers. No seed is needed, since all three fail before anything is sampled.

Nothing calls Vector.cross: git grep -n '\.cross(' -- src tests returns one hit and it is numpy.cross at regions.py:2810.

Issue Submission Checklist

  • I am reporting an issue, not asking a question
  • I checked the open and closed issues, forum, etc. and have not found any solution
  • I have provided all necessary code, etc. to reproduce the issue

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: validIssue is valid and will be worked ontype: bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions