Knowing that an error occurred is the first step to preventing and solving errors. There are many types of errors that PyScript applications will experience. Network failures, resources not being available and programming mistakes are just a few problems to manage.
I try to cover several important items when writing PyScript applications:
- Program Structure
- Example Application Template
- JavaScript Debuggers
- Python Debuggers
- JavaScript Error Management
- Python Error Management
- Uncaught Python Exceptions
- Strange PyScript Error Messages
- PyScript Error Handling Feature Improvements
Program structure
I do not recommend mixing HTML with Python. Python error messages often contain a line number where the error occurred. That line number is relative to the start of the Python code and not the actual line number when mixed with HTML.
Separate the HTML from the Python code to make testing easier. By splitting the Python code into multiple files, you can test the Python code independently of the browser. Only the parts that interface with the browser need to be tested with the browser.
Load your main Python source file:
1 |
<py-script src="application.py"></py-script> |
Load additional Python source files:
1 2 3 4 5 |
<py-env> - paths: - /app/file1.py - /app/file2.py </py-env> |
And import the required functions from those files:
1 2 |
from file1 import myfunction from file2 import myotherfunction |
Load required Python packages:
1 2 3 |
<py-env> - numpy </py-env> |
By structuring your application in a consistent manner and with functionality split into different modules, you will improve your ability to detect and solve problems that occur. I prefer that a customer has a useful error box with detailed information that he can report over “hey – your program does not work and there are no error messages“.
If you use separate development and QA teams, your QA team will appreciate the time you spent structuring the source code and providing informative messages for all error conditions. Your development team will appreciate bug reports that include source code file and line details.
Example Template to use for Applications
This example includes the file error-handler.js
that I will explain later in this article.
Notice the onerror=scriptLoadFailure('pyscript.js')
added to the script tag for loading pyscript.js
. The function scriptLoadFailure()
is located in error-handler.js
. That function will catch network problems loading the PyScript JavaScript file, which I see once in a while developing.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<!DOCTYPE html> <html lang="en"> <head> <title>Example Application</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <!-- Load error-handler.js script before other scripts --> <script src="error-handler.js"></script> <!-- Load required PyScript packages --> <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" /> <script defer src="https://pyscript.net/alpha/pyscript.js"onerror=scriptLoadFailure('pyscr ipt.js')></script> <!-- Load required Python packages --> <py-env> </py-env> </head> <body> <!-- Load the Python main application file --> <py-script src="application.py"></py-script> </body> </html> |
JavaScript Debuggers
Most web browsers include a very good HTML, CSS, and JavaScript debugger. I will not cover how to use the web browser debugger as there are many excellent resources on the Internet.
Shortcut to open the debugger: CTRL + Shift + J.
Python Debuggers
At this time, there are no Python debuggers for PyScript. I write my Python code as independent layers. One that interfaces with the browser and another layer that can be executed independently. That way I can use traditional code testing and debugging tools such as Visual Studio Code or PyCharm.
A great tool to get into the habit of running against your code often is Pylint. This is a static code analyzer that can catch many mistakes and ensure code compliance. I create Makefiles that run Pylint every time I deploy my application for testing. There are a number of other useful tools listed on the Pylint page.
JavaScript Error Management
PyScript depends upon JavaScript to load and interface with the web browser. Catching network and JavaScript errors will be your first defense. Next, write solid Python code that catches its own errors. How you structure the main components (HTML, CSS, JavaScript, Python) will also impact your ability to manage errors.
There are several types of errors that you can catch. I wrote handlers for these error types and placed them in the file error-handler.js
.
To catch errors, add an event listener for the error
event.
The error event is fired on a Window object when a resource failed to load or couldn’t be used — for example, if a script has an execution error.
This handler displays an alert dialog box with source file information::
1 2 3 4 5 6 7 8 9 10 11 12 |
window.addEventListener("error", function (e) { msg = 'Error Handler\n'; msg += e.error.message + '\n'; msg += 'File: ' + e.filename + '\n'; msg += 'Line: ' + e.lineno + '\n'; msg += 'Error: ' + e.error + '\n'; console.error(msg) alert(msg); return false; }) |
To catch script load failures, such as pyscript.js not loading, add an onerror=scriptLoadFailure('pyscript.js')
to the script tag. Example:
1 |
<script defer src="https://pyscript.net/alpha/pyscript.js" onerror=scriptLoadFailure('pyscript.js')></script> |
The function scriptLoadFailure()
then displays an alert dialog box.
1 2 3 4 |
function scriptLoadFailure(msg) { console.error('Script Load Failure', msg) alert('Script Load Failure\n' + msg) } |
To catch unhandled promise rejections, add an event listener for the unhandledrejection
event.
1 2 3 4 5 |
window.addEventListener('unhandledrejection', event => { console.error('Unhandled Promise Rejection:', event.reason); alert("Unandled Promise Rejection: " + event.reason) return false; }); |
The following code is the error-handler.js
that I use for my applications during development:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
wndledrejectionindow.addEventListener("error", function (e) { msg = 'Error Handler\n'; msg += e.error.message + '\n'; msg += 'File: ' + e.filename + '\n'; msg += 'Line: ' + e.lineno + '\n'; msg += 'Error: ' + e.error + '\n'; console.error(msg) alert(msg); return false; }) function scriptLoadFailure(msg) { console.error('Script Load Failure', msg) alert('Script Load Failure\n' + msg) } window.addEventListener('unhandledrejection', event => { console.error('Unhandled Promise Rejection:', event.reason); alert("Unandled Promise Rejection: " + event.reason) return false; }); |
Python Error Management
Since there are no Python debuggers available for the browser, you must be creative. Python errors often result in a poorly formed pink error message from PyScript. Then you must use the browser debugger to analyze the actual error. A better strategy is to use exception handling that catches errors for your entire application.
I put my application code into the old fashioned main()
function and wrap that with a try/except block.
If you see the filename as <exec>
, that means the file specified by the PyScript tag. For some reason, that is not preserved by the PyScript loader.
Example application.py template with error handler:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import sys from js import alert def errorHandler(e): exception_type, exception_object, exception_traceback = sys.exc_info() filename = exception_traceback.tb_frame.f_code.co_filename lineno = exception_traceback.tb_lineno msg = 'Exception Type: ' + str(exception_type) + '\n' msg += 'File: ' + filename + '\n' msg += 'Line: ' + str(lineno) + '\n' msg += 'Error: ' + str(e) console.error(msg) alert(msg) def main(): # Write your main application here try: main() except Exception as e: errorHandler(e) |
Uncaught Python Exceptions
SyntaxError
Pyodide parses the entire Python source file before execution. The Exception SyntaxError
can only be caught in the following cases:
- eval()
- exec()
- import
Strange PyScript Error Messages
Missing Python Source File
If you declare a Python file via the <py-script src="file">
tag and the file does not exist, PyScript displays a strange error:
1 |
_parse_and_compile_gen mod = compile(source, filename, mode, flags | ast.PyCF_ONLY_AST) File "", line 1 |
Complete error message:
1 |
JsException(PythonError: Traceback (most recent call last): File "/lib/python3.10/site-packages/_pyodide/_base.py", line 421, in eval_code CodeRunner( File "/lib/python3.10/site-packages/_pyodide/_base.py", line 237, in __init__ self.ast = next(self._gen) File "/lib/python3.10/site-packages/_pyodide/_base.py", line 141, in _parse_and_compile_gen mod = compile(source, filename, mode, flags | ast.PyCF_ONLY_AST) File "", line 1 |
PyScript Error Handling Feature Improvements
- If a Python source file is specified via the
<py-script src="file">
, the propertyexception_traceback.tb_frame.f_code.co_filenamefilename
has the value<exec>
instead of the filename specified by thesrc
attribute.
Summary
If you implement one or more of my strategies for PyScript application development, you might find that you will have an improved development and testing experience. PyScript is new and will become very popular. As that popularity grows, more options will become available for IDEs, debuggers, and libraries.
More Information
Photography Credit
I write free articles about technology. Recently, I learned about Pexels.com which provides free images. The image in this article is courtesy of Pixabay at Pexels.
I design software for enterprise-class systems and data centers. My background is 30+ years in storage (SCSI, FC, iSCSI, disk arrays, imaging) virtualization. 20+ years in identity, security, and forensics.
For the past 14+ years, I have been working in the cloud (AWS, Azure, Google, Alibaba, IBM, Oracle) designing hybrid and multi-cloud software solutions. I am an MVP/GDE with several.
Leave a Reply