-
Notifications
You must be signed in to change notification settings - Fork 2
Update for Python 3.14 and dearpygui 2.1.1, improve code quality and security #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: awcook97 <[email protected]>
Co-authored-by: awcook97 <[email protected]>
…ting Co-authored-by: awcook97 <[email protected]>
awcook97
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add in some tests so we know this works
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Modernizes the DearPyGui theme/font plugins for newer Python and DearPyGui versions, while improving readability and repository hygiene.
Changes:
- Added dependency management (
requirements.txt) and expanded.gitignore(including user-generatedthemes/andFonts/). - Refactored both plugins for cleaner formatting, added docstrings, and replaced bare
except:with explicit exception handling. - Restructured and expanded
README.mdwith clearer installation/usage guidance.
Reviewed changes
Copilot reviewed 3 out of 5 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| requirements.txt | Declares DearPyGui dependency (dearpygui>=2.1.1). |
| README.md | Rewritten/expanded documentation and usage examples. |
| EditThemePlugin.py | Formatting + docstrings + error handling updates; theme editor logic retained. |
| ChooseFontsPlugin.py | Formatting + docstrings + logging/error handling updates; font discovery/UI logic retained. |
| .gitignore | Ignores generated artifacts and common Python/IDE/OS files. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
EditThemePlugin.py
Outdated
| """Write operation like this: | ||
| Args: | ||
| operation (str): 'dpg.add_theme_color(dpg.[VARGOESHERE], self.getThemeColor("[VARGOESHERE]"), category=dpg.mvThemeCat_Core)' | ||
| Note: Uses exec() for dynamic code execution. The color names are sourced from | ||
| self.theColors list which is a static, controlled list, limiting the security surface. | ||
| """ | ||
| for c in self.theColors: | ||
| newVar = operation | ||
| newVar = newVar.replace("[VARGOESHERE]", c) | ||
| try: | ||
| exec(newVar) |
Copilot
AI
Jan 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parse_color_operation executes dynamically generated strings via exec(). Even if the current inputs are intended to be controlled, this makes the code harder to audit, debug, and secure. Refactor to avoid exec by iterating self.theColors and calling DearPyGui APIs via getattr (e.g., resolve the dpg color constant and then call dpg.add_theme_color) or by storing callables instead of string templates.
| """Write operation like this: | |
| Args: | |
| operation (str): 'dpg.add_theme_color(dpg.[VARGOESHERE], self.getThemeColor("[VARGOESHERE]"), category=dpg.mvThemeCat_Core)' | |
| Note: Uses exec() for dynamic code execution. The color names are sourced from | |
| self.theColors list which is a static, controlled list, limiting the security surface. | |
| """ | |
| for c in self.theColors: | |
| newVar = operation | |
| newVar = newVar.replace("[VARGOESHERE]", c) | |
| try: | |
| exec(newVar) | |
| """Apply a color-related operation for each entry in self.theColors. | |
| This function previously accepted a string of Python code and executed it | |
| with exec() after substituting [VARGOESHERE] with each color name. | |
| To improve safety and auditability, it now recognizes known operation | |
| patterns and calls the appropriate DearPyGui/config APIs directly. | |
| Supported patterns: | |
| - Add theme color: | |
| 'dpg.add_theme_color(dpg.[VARGOESHERE], self.getThemeColor("[VARGOESHERE]"), category=dpg.mvThemeCat_Core)' | |
| - Save theme color from a color edit widget: | |
| "self.confParser['Theme']['[VARGOESHERE]']=str(dpg.get_value('colEdit[VARGOESHERE]'))" | |
| """ | |
| for c in self.theColors: | |
| try: | |
| # Pattern 1: add_theme_color operation | |
| if "add_theme_color" in operation: | |
| # Resolve DearPyGui color constant, e.g. dpg.mvThemeCol_Text | |
| color_constant = getattr(dpg, c, None) | |
| if color_constant is None: | |
| # Skip if the constant does not exist in the current DearPyGui version | |
| continue | |
| color_value = self.getThemeColor(c) | |
| dpg.add_theme_color( | |
| color_constant, | |
| color_value, | |
| category=dpg.mvThemeCat_Core | |
| ) | |
| # Pattern 2: save color to config from a color edit widget | |
| elif "self.confParser['Theme']" in operation and "dpg.get_value" in operation: | |
| # Widget ID follows the 'colEdit[VARGOESHERE]' pattern after substitution | |
| widget_id = f"colEdit{c}" | |
| value = dpg.get_value(widget_id) | |
| # Ensure the 'Theme' section exists | |
| if "Theme" not in self.confParser: | |
| self.confParser["Theme"] = {} | |
| self.confParser["Theme"][c] = str(value) | |
| else: | |
| # Unsupported operation pattern; log and continue | |
| print(f"Unsupported color operation pattern for {c}: {operation!r}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored completely to remove exec(). Created new methods:
_add_theme_colors()- uses getattr() to resolve color constants and direct API calls_save_theme_colors()- iterates colors directly without exec_create_color_editor()- creates widgets with proper callbacks
The old parse_color_operation method has been removed entirely. Commit: 8bfc968
EditThemePlugin.py
Outdated
| myEditVar = """dpg.add_color_edit(self.getThemeColor("[VARGOESHERE]"), source=self.confParser['Theme']["[VARGOESHERE]"], display_type=dpg.mvColorEdit_uint8, alpha_bar=True, alpha_preview=dpg.mvColorEdit_AlphaPreviewHalf, tag="colEdit[VARGOESHERE]", parent='editThemeWindow', label='[VARGOESHERE]', callback=lambda: dpg.add_theme_color(dpg.[VARGOESHERE], dpg.get_value("colEdit[VARGOESHERE]"), parent=dpg.add_theme_component(dpg.mvAll, parent="glob"))- 0 if not dpg.bind_theme("glob") else 0)""" | ||
| self.parse_color_operation(myEditVar) |
Copilot
AI
Jan 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The UI generation for color editors is built from a large string template (myEditVar) that is later executed indirectly. This approach is very brittle (quoting/escaping issues, static analysis tools can't help) and makes callbacks difficult to reason about. Consider building the widgets in normal Python code inside a loop over self.theColors instead of string templating.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored editTheme to remove string templating. Now builds widgets in a normal Python loop over self.theColors with proper callbacks defined as closures. Much more maintainable and debuggable. Commit: 8bfc968
| for filename in os.listdir('Fonts'): | ||
| if filename.endswith((".ttf", "otf")): | ||
| self.fontDict[filename] = f"Fonts/{filename}" |
Copilot
AI
Jan 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Font extension matching uses filename.endswith((".ttf", "otf")), which is both slightly imprecise (missing leading dot for otf) and case-sensitive (won't match .TTF/.OTF). Consider normalizing with filename.lower() and checking for (".ttf", ".otf") to avoid missing valid fonts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed font extension matching to use filename.lower().endswith((".ttf", ".otf")) - now case-insensitive and properly detects .otf with the dot. Commit: 8bfc968
ChooseFontsPlugin.py
Outdated
| dpg.add_combo(list(self.fontDict.keys()), label="Font Type", callback=lambda: self.build_fonts(), tag="cmbCFPFontType") | ||
| dpg.add_input_int(label="Size", default_value=self.userSize, callback=lambda: self.build_fonts(), tag="intCFPSize") | ||
| dpg.add_slider_float(default_value=self.userScale, label="Scale", callback=lambda: self.build_fonts(), tag="slideCFPFontScale", min_value=0.0, max_value=2.0, clamped=True) | ||
| with dpg.group(horizontal=True): | ||
| dpg.add_checkbox(label="Autosize?", tag="chkbxCFPAutosize", default_value=True) | ||
| dpg.add_button(label="Change Font", callback=lambda: self.btnBuild()) | ||
|
|
||
| with dpg.child_window(autosize_x=True, height=100): | ||
| dpg.add_text("the quick brown fox jumped over the lazy dog", wrap=0) | ||
| dpg.add_text("THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG", wrap=0) | ||
| with dpg.group(horizontal=True): | ||
| dpg.add_button(label="Close", callback=lambda: dpg.configure_item("winCFPFontWindow", show=False)) | ||
| dpg.add_button(label="Save", callback=lambda: self.save_fonts()) |
Copilot
AI
Jan 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This 'lambda' is just a simple wrapper around a callable object. Use that object directly.
| dpg.add_combo(list(self.fontDict.keys()), label="Font Type", callback=lambda: self.build_fonts(), tag="cmbCFPFontType") | |
| dpg.add_input_int(label="Size", default_value=self.userSize, callback=lambda: self.build_fonts(), tag="intCFPSize") | |
| dpg.add_slider_float(default_value=self.userScale, label="Scale", callback=lambda: self.build_fonts(), tag="slideCFPFontScale", min_value=0.0, max_value=2.0, clamped=True) | |
| with dpg.group(horizontal=True): | |
| dpg.add_checkbox(label="Autosize?", tag="chkbxCFPAutosize", default_value=True) | |
| dpg.add_button(label="Change Font", callback=lambda: self.btnBuild()) | |
| with dpg.child_window(autosize_x=True, height=100): | |
| dpg.add_text("the quick brown fox jumped over the lazy dog", wrap=0) | |
| dpg.add_text("THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG", wrap=0) | |
| with dpg.group(horizontal=True): | |
| dpg.add_button(label="Close", callback=lambda: dpg.configure_item("winCFPFontWindow", show=False)) | |
| dpg.add_button(label="Save", callback=lambda: self.save_fonts()) | |
| dpg.add_combo(list(self.fontDict.keys()), label="Font Type", callback=self.build_fonts, tag="cmbCFPFontType") | |
| dpg.add_input_int(label="Size", default_value=self.userSize, callback=self.build_fonts, tag="intCFPSize") | |
| dpg.add_slider_float(default_value=self.userScale, label="Scale", callback=self.build_fonts, tag="slideCFPFontScale", min_value=0.0, max_value=2.0, clamped=True) | |
| with dpg.group(horizontal=True): | |
| dpg.add_checkbox(label="Autosize?", tag="chkbxCFPAutosize", default_value=True) | |
| dpg.add_button(label="Change Font", callback=self.btnBuild) | |
| with dpg.child_window(autosize_x=True, height=100): | |
| dpg.add_text("the quick brown fox jumped over the lazy dog", wrap=0) | |
| dpg.add_text("THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG", wrap=0) | |
| with dpg.group(horizontal=True): | |
| dpg.add_button(label="Close", callback=lambda: dpg.configure_item("winCFPFontWindow", show=False)) | |
| dpg.add_button(label="Save", callback=self.save_fonts) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replaced all unnecessary lambdas with proper methods: _close_font_window and _reset_font_to_default. Commit: 8bfc968
ChooseFontsPlugin.py
Outdated
| dpg.add_combo(list(self.fontDict.keys()), label="Font Type", callback=lambda: self.build_fonts(), tag="cmbCFPFontType") | ||
| dpg.add_input_int(label="Size", default_value=self.userSize, callback=lambda: self.build_fonts(), tag="intCFPSize") | ||
| dpg.add_slider_float(default_value=self.userScale, label="Scale", callback=lambda: self.build_fonts(), tag="slideCFPFontScale", min_value=0.0, max_value=2.0, clamped=True) | ||
| with dpg.group(horizontal=True): | ||
| dpg.add_checkbox(label="Autosize?", tag="chkbxCFPAutosize", default_value=True) | ||
| dpg.add_button(label="Change Font", callback=lambda: self.btnBuild()) | ||
|
|
||
| with dpg.child_window(autosize_x=True, height=100): | ||
| dpg.add_text("the quick brown fox jumped over the lazy dog", wrap=0) | ||
| dpg.add_text("THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG", wrap=0) | ||
| with dpg.group(horizontal=True): | ||
| dpg.add_button(label="Close", callback=lambda: dpg.configure_item("winCFPFontWindow", show=False)) | ||
| dpg.add_button(label="Save", callback=lambda: self.save_fonts()) |
Copilot
AI
Jan 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This 'lambda' is just a simple wrapper around a callable object. Use that object directly.
| dpg.add_combo(list(self.fontDict.keys()), label="Font Type", callback=lambda: self.build_fonts(), tag="cmbCFPFontType") | |
| dpg.add_input_int(label="Size", default_value=self.userSize, callback=lambda: self.build_fonts(), tag="intCFPSize") | |
| dpg.add_slider_float(default_value=self.userScale, label="Scale", callback=lambda: self.build_fonts(), tag="slideCFPFontScale", min_value=0.0, max_value=2.0, clamped=True) | |
| with dpg.group(horizontal=True): | |
| dpg.add_checkbox(label="Autosize?", tag="chkbxCFPAutosize", default_value=True) | |
| dpg.add_button(label="Change Font", callback=lambda: self.btnBuild()) | |
| with dpg.child_window(autosize_x=True, height=100): | |
| dpg.add_text("the quick brown fox jumped over the lazy dog", wrap=0) | |
| dpg.add_text("THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG", wrap=0) | |
| with dpg.group(horizontal=True): | |
| dpg.add_button(label="Close", callback=lambda: dpg.configure_item("winCFPFontWindow", show=False)) | |
| dpg.add_button(label="Save", callback=lambda: self.save_fonts()) | |
| dpg.add_combo(list(self.fontDict.keys()), label="Font Type", callback=self.build_fonts, tag="cmbCFPFontType") | |
| dpg.add_input_int(label="Size", default_value=self.userSize, callback=self.build_fonts, tag="intCFPSize") | |
| dpg.add_slider_float(default_value=self.userScale, label="Scale", callback=self.build_fonts, tag="slideCFPFontScale", min_value=0.0, max_value=2.0, clamped=True) | |
| with dpg.group(horizontal=True): | |
| dpg.add_checkbox(label="Autosize?", tag="chkbxCFPAutosize", default_value=True) | |
| dpg.add_button(label="Change Font", callback=self.btnBuild) | |
| with dpg.child_window(autosize_x=True, height=100): | |
| dpg.add_text("the quick brown fox jumped over the lazy dog", wrap=0) | |
| dpg.add_text("THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG", wrap=0) | |
| with dpg.group(horizontal=True): | |
| dpg.add_button(label="Close", callback=lambda: dpg.configure_item("winCFPFontWindow", show=False)) | |
| dpg.add_button(label="Save", callback=self.save_fonts) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replaced lambda with proper method _close_font_window. Commit: 8bfc968
ChooseFontsPlugin.py
Outdated
| dpg.add_text("Whatever font you choose will be applied to the whole application. Sometimes it's best to close and reopen the application after setting a font, in case things get weird.", wrap=0) | ||
| dpg.add_combo(list(self.fontDict.keys()), label="Font Type", callback=lambda: self.build_fonts(), tag="cmbCFPFontType") | ||
| dpg.add_input_int(label="Size", default_value=self.userSize, callback=lambda: self.build_fonts(), tag="intCFPSize") | ||
| dpg.add_slider_float(default_value=self.userScale, label="Scale", callback=lambda: self.build_fonts(), tag="slideCFPFontScale", min_value=0.0, max_value=2.0, clamped=True) |
Copilot
AI
Jan 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This 'lambda' is just a simple wrapper around a callable object. Use that object directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replaced lambda with proper method self.btnBuild (already existed, now used directly). Commit: 8bfc968
ChooseFontsPlugin.py
Outdated
| dpg.add_text("THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG", wrap=0) | ||
| with dpg.group(horizontal=True): | ||
| dpg.add_button(label="Close", callback=lambda: dpg.configure_item("winCFPFontWindow", show=False)) | ||
| dpg.add_button(label="Save", callback=lambda: self.save_fonts()) |
Copilot
AI
Jan 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This 'lambda' is just a simple wrapper around a callable object. Use that object directly.
| dpg.add_button(label="Save", callback=lambda: self.save_fonts()) | |
| dpg.add_button(label="Save", callback=self.save_fonts) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replaced lambda with direct method reference self.save_fonts. Commit: 8bfc968
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
awcook97
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make requested changes
…ont detection Co-authored-by: awcook97 <[email protected]>
Added comprehensive test suite in test_plugins.py with 8 tests covering both plugins. Tests verify initialization, configuration, error handling, and integration. All tests pass. Commit: 8bfc968
Addressed all reviewer feedback:
|
Modernizes codebase for Python 3.14 compatibility and dearpygui 2.1.1, fixes code quality and security issues, adds comprehensive test coverage, and improves documentation clarity.
Dependencies
requirements.txtwithdearpygui>=2.1.1(Python 3.14 compatible).gitignorefor user-generated artifacts (themes/,Fonts/)Code Quality & Security
EditThemePlugin:
getattr()and direct DearPyGui API calls instead of dynamic string execution_close_edit_window,_show_load_dialog,_show_save_dialog)except:with specific exception handlersChooseFontsPlugin:
.otffiles (was missing dot)_close_font_window,_reset_font_to_default)except:with specific exception handlers and loggingTesting
test_plugins.py) with 8 unit testsDocumentation
README.md:
No functional changes - existing behavior preserved while improving security, maintainability, and testability.
Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.