Skip to content

Check args more thoroghly in sense_hat.py and mangle traceback for sense_hat.py errors#1348

Merged
patch0 merged 11 commits intomainfrom
fix-sense-hat-error-messages
Feb 27, 2026
Merged

Check args more thoroghly in sense_hat.py and mangle traceback for sense_hat.py errors#1348
patch0 merged 11 commits intomainfrom
fix-sense-hat-error-messages

Conversation

@patch0
Copy link
Contributor

@patch0 patch0 commented Feb 26, 2026

Closes https://github.com/RaspberryPiFoundation/digital-maintenance-team/issues/7

This commit specifically fixes the issue triggered by using incorrect arguments in the set_pixel and set_pixels functions.

TypeError: object of type 'int' has no len() on line 1081 of sense_hat.py

I've added an explicit check to see if the pixel object has a __len__ attribute.

I've opened an identical PR on the Sense Hat library, and here and here!

Traceback mangling

I changed the error message when the filename is ./sense_hat.py to point to the next file in the stack, hopefully the user's code.

The issue with this is that it effectively masks errors in the sense_hat library, blaming the user instead. At the moment it only catches specific errors: ValueError, RuntimeError which are specifically mentioned in the sense hat shim.

Package update

This also updates the package version to 0.34.7.

Example python

# Import the libraries
from sense_hat import SenseHat
from time import sleep

# Set up the Sense HAT
sense = SenseHat()

sense.set_pixel(1, 2, 3)

# Or:
sense.set_pixels(1)

# Or:
# sense.set_pixels([1]*64)

Before

image

After

Tested on https://staging-editor-static.raspberrypi.org/branches/1348_merge/web-component.html

image

This commit specifically fixes the issue

`TypeError: object of type 'int' has no len() on line 1030 of
sense_hat.py`

by a more explicit check to see if the pixel object has a `__len__`
attribute.

Also, the I mangled traceback when the filename is `./sense_hat.py` to
skip that first file, hopefully pointing to the line in the user's code
instead.
Copilot AI review requested due to automatic review settings February 26, 2026 17:55
@patch0 patch0 temporarily deployed to previews/1348/merge February 26, 2026 17:55 — with GitHub Actions Inactive
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request improves error handling for the sense_hat.py shim by making two key changes: (1) implementing safer pixel validation that explicitly checks for the __len__ attribute before calling len(), preventing TypeErrors when non-iterable objects are passed; and (2) manipulating the error traceback to hide internal shim frames and show users the line in their own code that caused the error, making debugging easier.

Changes:

  • Enhanced pixel validation in set_pixel() to use hasattr() check before calling len(), fixing "TypeError: object of type 'int' has no len()"
  • Added traceback manipulation logic to remove sense_hat.py frames from error messages
  • Improved error message to suggest checking for missing x,y coordinates

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.jsx Adds logic to strip sense_hat.py frames from error tracebacks to show user code lines
public/shims/sense_hat/sense_hat_blob.py Improves pixel validation with hasattr check and enhanced error message

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@patch0 patch0 temporarily deployed to previews/1348/merge February 26, 2026 18:09 — with GitHub Actions Inactive
@patch0 patch0 temporarily deployed to previews/1348/merge February 26, 2026 18:12 — with GitHub Actions Inactive
@patch0 patch0 temporarily deployed to previews/1348/merge February 26, 2026 21:50 — with GitHub Actions Inactive
@patch0 patch0 temporarily deployed to previews/1348/merge February 26, 2026 21:55 — with GitHub Actions Inactive
@patch0 patch0 changed the title Specifically mangle traceback for sense_hat.py errors Check args more thoroghly in sense_hat.py and mangle traceback for sense_hat.py errors Feb 26, 2026
@patch0 patch0 requested a review from Copilot February 26, 2026 22:02
@patch0 patch0 temporarily deployed to previews/1348/merge February 26, 2026 22:06 — with GitHub Actions Inactive
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@patch0 patch0 temporarily deployed to previews/1348/merge February 27, 2026 07:58 — with GitHub Actions Inactive
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +230 to +263
// This initialState sets up a file `sense_hat.py` which contains code that
// will raise an error if the set_pixel function is called with an x
// value greater than 7 or less than 0. The `main.py` file then calls this
// function with an x value of 255, which will cause the error to be raised.
//
// This file matches the name looked for in the SkulptRunner
// (`./sense_hat.py`), so the SkulptRunner should attribute the error to the
// user's `main.py` file and not the `sense_hat.py` shim file, and set the
// errorDetails accordingly.
//
// This test has to be run this way because the sense hat libray is loaded
// via `fetch()` from a remote URL, which is hard to do in the test
// environment.
beforeEach(() => {
const middlewares = [];
const mockStore = configureStore(middlewares);
const initialState = {
editor: {
project: {
components: [
{
name: "main",
extension: "py",
content:
"from sense_hat import set_pixel\nset_pixel(255, 0, 0, 0, 0)",
},
{
name: "sense_hat",
extension: "py",
content:
"def set_pixel(x, y, r, g, b):\n" +
" if x > 7 or x < 0:\n" +
" raise ValueError('X position must be between 0 and 7')\n",
},
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test sets up a local sense_hat.py project component, so the import resolves to user code (because builtinRead prioritizes local project files) rather than the external Sense HAT shim loaded via fetch(). That means the test isn't actually validating the shim traceback-mangling behavior, and it can also lock in incorrect behavior for projects that legitimately define their own sense_hat.py. Consider mocking the external library fetch (or asserting mangling only when there is no local sense_hat.py component) so the test matches the real shim scenario.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mocking the fetch is really complicated, and I think this will do for now.

Comment on lines +346 to +357
// If this is an error in the sense_hat.py shim, use the next entry in
// the traceback as this will be the line in the shim which we don't want
// to show to users, so that the error message will instead point to the
// line in the user's code which caused the error.
if (
err.traceback.length > 1 &&
fileName === "./sense_hat.py" &&
["RuntimeError", "ValueError"].includes(errorType)
) {
lineNumber = err.traceback[1].lineno;
fileName = err.traceback[1].filename;
}
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The traceback mangling triggers for any exception whose top frame filename is "./sense_hat.py", even if the user has created their own local sense_hat.py file. Since builtinRead prefers local project files over external libraries, this can incorrectly attribute real user-code errors in sense_hat.py to main.py. Consider guarding this logic by checking whether a sense_hat.py component exists in the current project (and skipping mangling if it does), or otherwise positively identifying the external shim source before rewriting the traceback frame.

Suggested change
// If this is an error in the sense_hat.py shim, use the next entry in
// the traceback as this will be the line in the shim which we don't want
// to show to users, so that the error message will instead point to the
// line in the user's code which caused the error.
if (
err.traceback.length > 1 &&
fileName === "./sense_hat.py" &&
["RuntimeError", "ValueError"].includes(errorType)
) {
lineNumber = err.traceback[1].lineno;
fileName = err.traceback[1].filename;
}
// Note: we intentionally avoid rewriting tracebacks originating from
// sense_hat.py, because a user may define their own sense_hat.py file.
// Blindly remapping those errors to another frame can misattribute
// real user-code errors to a different file (e.g. main.py).

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good idea, but I think it is vanishingly small that someone would write a sense_hat.py file. It would make testing much more complex, as we'd have to stub the fetch(), which is tricky.

@patch0 patch0 temporarily deployed to previews/1348/merge February 27, 2026 08:21 — with GitHub Actions Inactive
I've opened a PR in the sense_hat library to match the changes I've made
here, and to reduce noise there, I've kept the error messages the same
as they were.
@patch0 patch0 force-pushed the fix-sense-hat-error-messages branch from 5ea4777 to 5e8a655 Compare February 27, 2026 08:21
@patch0 patch0 temporarily deployed to previews/1348/merge February 27, 2026 08:22 — with GitHub Actions Inactive
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@patch0 patch0 temporarily deployed to previews/1348/merge February 27, 2026 08:30 — with GitHub Actions Inactive
@patch0 patch0 merged commit 02f6a7d into main Feb 27, 2026
7 checks passed
@patch0 patch0 deleted the fix-sense-hat-error-messages branch February 27, 2026 08:58
jamiebenstead pushed a commit that referenced this pull request Mar 3, 2026
…`sense_hat.py` errors (#1348)

Closes
RaspberryPiFoundation/digital-maintenance-team#7

This commit specifically fixes the issue triggered by using incorrect
arguments in the `set_pixel` and `set_pixels` functions.

`TypeError: object of type 'int' has no len() on line 1081 of
sense_hat.py`

I've added an explicit check to see if the pixel object has a `__len__`
attribute.

I've opened an identical PR on the [Sense Hat
library](RaspberryPiFoundation/sense_hat#1), and
[here](RaspberryPiFoundation/python-sense-hat#2)
and [here](astro-pi/python-sense-hat#151)!

## Traceback mangling

I changed the error message when the filename is `./sense_hat.py` to
point to the next file in the stack, hopefully the user's code.

The issue with this is that it effectively masks errors in the
`sense_hat` library, blaming the user instead. At the moment it only
catches specific errors: `ValueError`, `RuntimeError` which are
specifically mentioned in the sense hat shim.

## Package update

This also updates the package version to 0.34.7.

## Example python

```python
# Import the libraries
from sense_hat import SenseHat
from time import sleep

# Set up the Sense HAT
sense = SenseHat()

sense.set_pixel(1, 2, 3)

# Or:
sense.set_pixels(1)

# Or:
# sense.set_pixels([1]*64)
```

## Before

<img width="1473" height="347" alt="image"
src="https://github.com/user-attachments/assets/0f8f6e1d-42bc-454f-88b5-a7c22568c012"
/>

## After

Tested on
https://staging-editor-static.raspberrypi.org/branches/1348_merge/web-component.html

<img width="1200" height="390" alt="image"
src="https://github.com/user-attachments/assets/f30ff39c-0c47-4dbf-a7ae-d1aed3951259"
/>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants