-
Notifications
You must be signed in to change notification settings - Fork 14
Assess Userdata Folders (new feature, off by default) #330
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
Merged
yujiahu415
merged 31 commits into
umyelab:master
from
ruck94301:check-for-datafolders-within-tree
Dec 15, 2025
Merged
Changes from all commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
6c0ad9d
refactor bring_to_foreground to mywx module
ruck94301 98fc602
In probes.probes, Stub in a call to userdata_survey.survey.
ruck94301 fded217
Implement two custom wx.Dialogs with left-aligned msg.
ruck94301 3a23ca2
Implement verify_userdata_dir_separation.
ruck94301 79d3fe4
Implement offer_mkdir_userdata.
ruck94301 b8141df
Stub in check_for_internal_userdata_dirs.
ruck94301 0adcc92
Partially implement advise_on_internal_userdata_dirs.
ruck94301 255e703
Stub in warn_on_orphaned_userdata.
ruck94301 1386f4d
Enforce the expectation that path args are "full" not relative.
ruck94301 28640ca
Implement "advise" with two approaches to displaying instructions.
ruck94301 4a448b1
Implement warn_on_orphaned userdata
ruck94301 0b7be36
Merge branch 'master' into check-for-datafolders-within-tree
ruck94301 7a2ff79
Merge branch 'master' into check-for-datafolders-within-tree
ruck94301 993177b
Clean userdata_survey.py
ruck94301 48b5163
Resuscitate the single call to wx.App().
ruck94301 1fe87df
Replace mywx.App function with patch to wx.App.
ruck94301 7c00776
Implement userdata_survey.survey with warnings instead of gui dialogs…
ruck94301 92b722d
Fix unit tests.
ruck94301 3afe940
Use logger.warning() instead of logger.warn().
ruck94301 012d7a6
Move bring_wxapp_to_foreground to instantiation in __main__.py
ruck94301 8f8bdb9
Be robust to mymx being loaded twice.
ruck94301 d071263
Show warning msgs with wx Dialog.
ruck94301 f69568c
Mock userdata_survey.survey in test_probes.py.
ruck94301 ea33d91
Change usage comment in mywx.py
ruck94301 458bde9
Refactor module mywx.py into package mywx
ruck94301 2cdb9c1
Wordsmith comments. Small & benign.
ruck94301 6e46137
Add unit tests for userdata_survey.py
ruck94301 65be890
Scrub unused functions from userdata_survey.py
ruck94301 ce6ed83
Implement assess_userdata_folders with feature off, by default.
ruck94301 86d53f1
Bugfixed the unittest
ruck94301 db3da2f
remove an unused function and its unittest
ruck94301 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| """ | ||
| Monkeypatch wx.App, and provide wx utility functions and wx.Dialog subclasses. | ||
|
|
||
| Import this package before the first import of wx, to patch wx.App to be | ||
| a strict-singleton before an unpatched instance of wx.App can possibly | ||
| be created. | ||
|
|
||
| Example -- Display a modal dialog. In this example, mywx is a | ||
| subpackage of mypkg (not in a dir in sys.path). | ||
| from mypkg import mywx # on load, monkeypatch wx.App to be a strict-singleton | ||
| import wx # wxPython, Cross platform GUI toolkit for Python, "Phoenix" version | ||
|
|
||
| # Dialog obj requires that the wx.App obj exists already. | ||
| if not wx.GetApp(): | ||
| wx.App() | ||
| mywx.bring_wxapp_to_foreground() | ||
|
|
||
| # Show modal dialog. | ||
| title = 'My Title' | ||
| msg = 'My message' | ||
| with mywx.OK_Dialog(None, title=title, msg=msg) as dlg: | ||
| result = dlg.ShowModal() # will return wx.ID_OK upon OK or dismiss | ||
|
|
||
| Notes | ||
| * Why patch wx.App? | ||
| Because it's easy to misuse, producing delayed consequences that may | ||
| be difficult to diagnose. | ||
|
|
||
| Scraped on 2025-12-10 from wxPython 4.2.3 documentation | ||
| https://docs.wxpython.org/wx.App.html | ||
| The wx.App class represents the application and is used to: | ||
| * bootstrap the wxPython system and initialize the underlying | ||
| gui toolkit | ||
| * set and get application-wide properties | ||
| * implement the native windowing system main message or event | ||
| loop, and to dispatch events to window instances | ||
| * etc. | ||
|
|
||
| Every wx application must have a single wx.App instance, and all | ||
| creation of UI objects should be delayed until after the wx.App | ||
| object has been created in order to ensure that the gui platform | ||
| and wxWidgets have been fully initialized. | ||
|
|
||
| Normally you would derive from this class and implement an | ||
| OnInit method that creates a frame and then calls | ||
| self.SetTopWindow(frame), however wx.App is also usable on its | ||
| own without derivation. | ||
|
|
||
| Scraped on 2025-12-10 from wxPython discussion | ||
| https://discuss.wxpython.org/t/two-wx-app-instances-one-appx-mainloop-runs-both-app-instances/24934 | ||
| wx.App is supposed to be used as a singleton. It doesn't own | ||
| the frames that are created in its OnInit, but instead assumes | ||
| that it is supposed to manage and deliver events to all the | ||
| windows that exist in the application. | ||
|
|
||
| * Why patch wx.App to be a "strict singleton" (that raises an | ||
| exception on a second instantiation attempt) instead of a singleton | ||
| that returns the existing instance? | ||
| Because the intention is to prevent the misuse of wx (according to | ||
| the lib documentation), not to accommodate the misuse of wx. | ||
|
|
||
| * During development of a feature, more wx-related functions and | ||
| classes are being parked in mywx.custom out of convenience, but | ||
| there may be a better way to reorganize them after the feature code | ||
| is stable. | ||
| """ | ||
|
|
||
| # Standard library imports. | ||
| import logging | ||
|
|
||
| # Log the load of this module (by the module loader, on first import). | ||
| # Intentionally positioning these statements before other imports, against the | ||
| # guidance of PEP 8, to log the load before other imports log messages. | ||
| logger = logging.getLogger(__name__) | ||
| logger.debug('%s', f'loading {__name__}') | ||
|
|
||
| # Related third party imports. | ||
| # None | ||
|
|
||
| # Local application/library specific imports. | ||
| # On load of this package, import/load .patch, which monkeypatches wx.App. | ||
| from . import patch | ||
|
|
||
| # Expose custom functions and classes as attributes of this package. | ||
| from .custom import ( | ||
| bring_wxapp_to_foreground, | ||
| OK_Dialog, | ||
| OK_Cancel_Dialog, | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| """Provide wx utility functions and wx.Dialog subclasses. | ||
|
|
||
| Public Functions | ||
| bring_wxapp_to_foreground -- Bring the wx app to the foreground. | ||
|
|
||
| Public Classes | ||
| OK_Dialog -- A wx.Dialog with left-aligned msg, and centered OK button. | ||
| OK_Cancel_Dialog -- A wx.Dialog with left-aligned msg, and centered | ||
| OK and Cancel buttons. | ||
| """ | ||
|
|
||
| # Allow use of newer syntax Python 3.10 type hints in Python 3.9. | ||
| from __future__ import annotations | ||
|
|
||
| # Standard library imports. | ||
| import logging | ||
| import sys | ||
|
|
||
| # Log the load of this module (by the module loader, on first import). | ||
| # Intentionally positioning these statements before other imports, against the | ||
| # guidance of PEP 8, to log the load before other imports log messages. | ||
| logger = logging.getLogger(__name__) | ||
| logger.debug('%s', f'loading {__name__}') | ||
|
|
||
| # Related third party imports. | ||
| if sys.platform == 'darwin': # macOS | ||
| # AppKit is from package pyobjc-framework-Cocoa, "Wrappers for the | ||
| # Cocoa frameworks on macOS". | ||
| from AppKit import NSApp, NSApplication | ||
|
|
||
| import wx # wxPython, Cross platform GUI toolkit for Python, "Phoenix" version | ||
|
|
||
| # Local application/library specific imports. | ||
| # None | ||
|
|
||
|
|
||
| def bring_wxapp_to_foreground() -> None: | ||
| """Bring the wx app to the foreground. | ||
|
|
||
| We want the wx app displayed to the user, not initially obscured by | ||
| windows from other apps. | ||
|
|
||
| On macOS 12.7, with LabGym started from terminal, I'm seeing: | ||
| * the registration dialog is displayed, but doesn't have focus | ||
| and is under the windows of the active app (terminology?), | ||
| potentially hidden completely. | ||
| * a bouncing python rocketship icon in the tray. The bouncing | ||
| stops when you mouseover the icon. Also some other actions | ||
| can stop the bouncing or just pause the bouncing... weird. | ||
|
|
||
| I wasn't able to resolve this undesirable behavior on macOS using | ||
| the wx.Dialog object's Raise, SetFocus, and Restore methods. | ||
| Instead, this approach worked for me... | ||
| "Calling NSApp().activateIgnoringOtherApps_(True) via AppKit: | ||
| This macOS workaround uses the AppKit module to explicitly | ||
| activate the application, ignoring other running applications. | ||
| This is typically done after your wxPython application has | ||
| started and its main window is shown." | ||
| """ | ||
|
|
||
| if sys.platform == 'darwin': # macOS | ||
| NSApplication.sharedApplication() | ||
| NSApp().activateIgnoringOtherApps_(True) | ||
|
|
||
|
|
||
| class OK_Dialog(wx.Dialog): | ||
| """An OK dialog object, with the message text left-aligned. | ||
|
|
||
| Why use a custom class instead of using wx.MessageDialog? | ||
| Because left-alignment of the message is preferred, and a | ||
| wx.MessageDialog cannot be customized to control the alignment of | ||
| its message text or buttons. | ||
|
|
||
| (class purpose and functionality, and optionally its attributes and methods) | ||
| """ | ||
|
|
||
| def __init__(self, parent, title='', msg=''): | ||
| super().__init__(parent, title=title) | ||
|
|
||
| panel = wx.Panel(self) | ||
| main_sizer = wx.BoxSizer(wx.VERTICAL) | ||
|
|
||
| # Add the msg | ||
| main_sizer.Add(wx.StaticText(panel, label=msg), | ||
| 0, # proportion (int). 0 means the item won't expand | ||
| # beyond its minimal size. | ||
|
|
||
| # border on all sides, and align left | ||
| wx.ALL | wx.LEFT, | ||
|
|
||
| 10, # width (in pixels) of the borders specified | ||
| ) | ||
|
|
||
| # Create sizers for layout. | ||
| button_sizer = wx.StdDialogButtonSizer() | ||
|
|
||
| # Create buttons, Add buttons to sizers, and Bind event handlers | ||
| self.add_buttons(panel, button_sizer) | ||
|
|
||
| # Realize the sizer to apply platform-specific layout | ||
| button_sizer.Realize() | ||
|
|
||
| # Add the button sizer to the main sizer | ||
| main_sizer.Add(button_sizer, 0, wx.ALL | wx.EXPAND, 10) | ||
|
|
||
| panel.SetSizer(main_sizer) | ||
| main_sizer.Fit(self) | ||
| # self.SetSizerAndFit(main_sizer) # is this equivalent?? | ||
|
|
||
| def add_buttons(self, panel, button_sizer): | ||
| """Create and add buttons to sizers, and bind event handlers. | ||
|
|
||
| Create standard buttons with their respective IDs. | ||
| Add buttons to the StdDialogButtonSizer. | ||
| Bind event handlers for the buttons. | ||
| """ | ||
| # Create/Add/Bind for the OK button | ||
| ok_button = wx.Button(panel, wx.ID_OK) | ||
| button_sizer.AddButton(ok_button) | ||
| self.Bind(wx.EVT_BUTTON, self.on_ok, id=wx.ID_OK) | ||
|
|
||
| def on_ok(self, event): | ||
| self.EndModal(wx.ID_OK) | ||
|
|
||
|
|
||
| class OK_Cancel_Dialog(OK_Dialog): | ||
| """An OK/Cancel dialog object, with the message text left-aligned.""" | ||
|
|
||
| def add_buttons(self, panel, button_sizer): | ||
| # Create/Add/Bind for the OK button | ||
| super().add_buttons(panel, button_sizer) | ||
|
|
||
| # Create/Add/Bind for the Cancel button | ||
| cancel_button = wx.Button(panel, wx.ID_CANCEL) | ||
| button_sizer.AddButton(cancel_button) | ||
| self.Bind(wx.EVT_BUTTON, self.on_cancel, id=wx.ID_CANCEL) | ||
|
|
||
| def on_cancel(self, event): | ||
| self.EndModal(wx.ID_CANCEL) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
mywx.py is replaced by mywx package