⚠️ Clean-Room / Educational

This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.

Hotfix os Shadowing + Anti-Regression Test

Date:2026-01-03 StepID:0462 State: VERIFIED

Summary

Critical bug fixUnboundLocalError: cannot access local variable 'os'which prevented the emulator from starting. The bug was introduced in Step 0461 by aimport youredundant withinrun()that shadowed theyouimported at module level. Solution: Elimination of redundant import. Anti-regression test added to detect this type of bugs before the push.

Hardware Concept

This Step is not about Game Boy hardware, but about a fundamental Python concept:variable shadowingand the behavior of the interpreter when parsing functions.

Variable Shadowing in Python: When Python parses a function, it identifies all assignments to a variable (includingimport). If you find an assignment toyouanywhere in the function, markyouas variablelocalfor the entire function, even before the assignment is executed.

The Problem: Insrc/viboy.py:

  • Line 28:import youat module level (correct)
  • Line 765: Use ofos.environ.get()withinrun()
  • Line 777:import youwithinrun()(shadowing)

Python detects theimport youof line 777 and markyouas a local variable for the entire functionrun(). By executing line 765,youis not yet defined locally (the import on line 777 has not been executed), causingUnboundLocalError.

The Solution: Remove redundant import. Heyouimported at the module level (line 28) is accessible from any function in the module, so the local import is unnecessary and causes the bug.

Implementation

The fix is ​​minimal and direct: eliminate theimport youredundant withinrun().

Modified components

  • src/viboy.py- Removedimport youredundant online 777
  • tests/test_runtime_flags_no_crash_0462.py- New anti-regression test

Change applied

Before (line 777):

# --- Step 0393: Toggle traces via environment variable ---
# VBC_TRACE=1 enables traces, by default disabled for performance
import you
ENABLE_DEBUG_LOGS = os.getenv('VBC_TRACE', '0') == '1'
# ------------------------------------------------------------------------------------

After:

# --- Step 0393: Toggle traces via environment variable ---
# VBC_TRACE=1 enables traces, by default disabled for performance
# Step 0462: Removed redundant imports (already imported at module level line 28)
ENABLE_DEBUG_LOGS = os.getenv('VBC_TRACE', '0') == '1'
# ------------------------------------------------------------------------------------

Anti-Regression Test

was createdtests/test_runtime_flags_no_crash_0462.pywith 4 tests that verify:

  • test_env_flag_helper_importable()- Verify that the module can be imported without shadowing
  • test_env_flag_helper_with_env_on()- Check environment flags with value '1'
  • test_env_flag_helper_with_env_off()- Checks environment flags with value '0' or absent
  • test_viboy_module_imports_without_crash()- DetectUnboundLocalErrorwhen importing the module

These tests are cheap (they do not require Pygame or complete initialization of the emulator) and they detect the bug before the push.

Affected Files

  • src/viboy.py- Removedimport youredundant (line 777)
  • tests/test_runtime_flags_no_crash_0462.py- New anti-regression test (4 tests)

Tests and Verification

Implementation validation:

  • Anti-regression test:4/4 tests pass
  • Import verification: python3 -c "import src.viboy"runs without errors
  • Boot Verification: python3 main.py roms/mario.gbcstarts withoutUnboundLocalError

Command executed

pytest -v tests/test_runtime_flags_no_crash_0462.py

Result

============================== test session starts ==============================
platform linux -- Python 3.12.3, pytest-9.0.2, pluggy-1.6.0
collected 4 items

tests/test_runtime_flags_no_crash_0462.py::test_env_flag_helper_importable PASSED [ 25%]
tests/test_runtime_flags_no_crash_0462.py::test_env_flag_helper_with_env_on PASSED [ 50%]
tests/test_runtime_flags_no_crash_0462.py::test_env_flag_helper_with_env_off PASSED [ 75%]
tests/test_runtime_flags_no_crash_0462.py::test_viboy_module_imports_without_crash PASSED [100%]

============================== 4 passed in 0.32s ==============================

Test Code

def test_viboy_module_imports_without_crash():
    """Verify that the viboy module can be imported without crashes.
    
    This test detects UnboundLocalError and other import errors.
    """
    try:
        import src.viboy
        # If we get here, the import was successful
        assert True
    except UnboundLocalError as e:
        pytest.fail(f"UnboundLocalError importing viboy (possible shadowing): {e}")
    except Exception as e:
        # Other errors are acceptable (ex: pygame not available in CI)
        # but UnboundLocalError NOT
        if "UnboundLocalError" in str(type(e)):
            pytest.fail(f"UnboundLocalUnexpectedError: {e}")

Native Validation: C++ compiled module validation not applicable (this fix is ​​Python only).

Sources consulted

Educational Integrity

What I Understand Now

  • Variable Shadowing in Python: Python parses the entire function before executing it. If you encounter an assignment to a variable (includingimport), marks that variable as local to the entire function, even before the assignment is executed.
  • UnboundLocalError: Occurs when an attempt is made to access a local variable before a value has been assigned. In this case, access toos.environ.get()on line 765 occurs before theimport youof line 777.
  • Module Level Imports: Module-level imports are accessible from any module function. It is not necessary to re-import within functions unless there is a specific reason (e.g. conditional import).

What remains to be confirmed

  • Nothing pending - the fix is ​​direct and verified.

Hypotheses and Assumptions

There are no assumptions. Python's behavior is well documented and the fix is ​​standard.

Next Steps

  • [ ] Continue with normal emulator development
  • [ ] Consider adding more anti-regression tests for other types of common bugs