Debugging & Troubleshooting in PyMAD-NG
This guide explains how to diagnose and fix common issues in PyMAD-NG’s communication with MAD-NG. You’ll learn how to enable debug output, inspect logs, redirect streams, and avoid typical pitfalls (like deadlocks or type mismatches).
1. Enable Debug Mode
When you initialise the MAD object with debug=True, MAD-NG runs in a verbose mode. This prints extra information about each command you send, as well as diagnostic messages from the Lua side.
from pymadng import MAD
mad = MAD(debug=True)
1.1 Redirecting Output
By default, PyMAD-NG writes MAD-NG’s standard output to Python’s sys.stdout. To redirect it:
# Send MAD-NG stdout to a file
mad = MAD(debug=True, stdout="mad_debug.log")
If you need to redirect standard error as well:
mad = MAD(debug=True, stdout="mad_debug.log", redirect_stderr=True)
This helps keep logs organised, especially when running long scripts.
2. Inspecting Command History
PyMAD-NG keeps track of all string-based commands sent to MAD-NG in a history buffer. To review them:
print(mad.history())
This is invaluable for tracing unexpected behaviour. For instance, if you suspect a weird command or an incorrect syntax was sent, you can look up the last lines in the history.
Note
Binary data (like large NumPy arrays) won’t appear in mad.history(). Only textual commands are recorded.
3. Communication Rules
3.1 Send Before Receive
PyMAD-NG uses pipes for first-in-first-out communication. If you call:
mad.recv() # This will block forever if there's nothing to read!
without telling MAD-NG to py:send(...) first, your script will hang.
Correct Sequence:
mad.send(...)mad.recv()
Any mismatch in these calls can lead to deadlocks.
3.2 Matching Data Transfers
If you instruct MAD-NG to receive data (arr = py:recv()), you must ensure Python actually sends that data:
mad.send("arr = py:recv()")
mad.send(my_array) # Actually transmit the data
Failing to do so can cause indefinite blocking or partial reads.
4. Handling Errors
4.1 Protected Sends
All sends in PyMAD-NG are automatically “protected” by default. If MAD-NG issues an error (err_), PyMAD-NG raises a RuntimeError on the Python side.
try:
mad.send("invalid_lua_code").recv()
except RuntimeError as e:
print("Caught MAD-NG error:", e)
If you need to ignore an error and continue, you can instantiate:
mad = MAD(raise_on_madng_error=False)
and manually check for failures.
5. Debugging Subprocess Behavior
5.1 Starting MAD-NG
During initialisation, PyMAD-NG calls:
mad_binary -q -e "MAD.pymad 'py' {_dbg = true} :__ini(fd)"
If
mad_binaryis missing or not executable, you’ll getFileNotFoundError.If it fails to run, an
OSErroris raised.
5.2 Checking Streams
stdout: By default prints to Python’s standard output unless you pass
stdout=....stderr: Remains attached to Python’s own stderr, unless you specify
redirect_sterr=True.
6. Common Pitfalls & Solutions
Issue |
Possible Cause |
Recommended Fix |
|---|---|---|
Hang / Deadlock |
Called |
Always pair |
BrokenPipeError |
MAD-NG crashed or closed unexpectedly |
Re-initialise |
“Unsupported data type” error |
Attempted to |
Limit data to |
AttributeError / KeyError accessing a field |
Tried to read a reference property without evaluating it first |
Call |
Exceeding |
Too many temp variables stored in |
Manually name them in MAD, or increase |
7. Cleaning Up
If you’re done using MAD-NG, close the session:
mad.close()
or use Python’s context manager:
with MAD(debug=True) as mad:
mad.send("a = 1 + 2").recv()
...
# Subprocess automatically ends here
8. Summary
Enable
debug=Trueto see more logs.Check
mad.history()to identify incorrect or unexpected commands.Balance each
mad.send()with amad.recv()to avoid deadlocks.Catch
RuntimeErrorto handle failures gracefully.Evaluate references (
.eval()) if you need real values from objects in MAD.
If you still have trouble:
Look at the Architecture Overview for the internal design.
See Contributing for details on how to extend or fix PyMAD-NG’s internals.
Open an issue on GitHub if you suspect a bug in the code.
Happy debugging!