wx.FileConfig does not work as indicated in documentation
Operating system: Windows 11 Pro 23H2 wxPython version & source: 4.2.1, PyPi install Python version & source: 3.10.6, stock install
Description of the problem:
Attempting to use wx.FileConfig as a cross-platform way to quickly store some app settings and have discovered several problems. Basically, regardless of what arguments are used when creating a wx.FileConfig object, once the Flush method is called the file is either not created at all, or is created at the root of AppData\Roaming. Code sample shows a few of the problems. More specifically:
1.) On Windows, localFilename and globalFilename should cause a file to be created somewhere within the AppData\Local and AppData\Roaming directories respectively. Regardless of the dozens of combinations I've tried, if the file is created, it is always created in AppData\Roaming.
2.) The test file Phoenix/demo/Main.py shows a path being provided to the wx.FileConfig constructor, but in reality, if any path is provided that doesn't point to a child of the AppData\Roaming directory, no config file will be created when Flush is called.
3.) Setting the config object's path using SetPath inherited from wx.ConfigBase is completely ignored when reading/writing the config file.
4.) The wx.CONFIG_USE_SUBDIR as described in the wx.ConfigBase documentation is completely ignored.
The ultimate desire is to have a cross platform compliant way to create local and global config files in such a way that they can be segregated from the config files of other vendors and applications.
Code Example (click to expand)
import os
import wx
app = wx.App(False)
vendor_name = 'my_company'
app_name = 'my_app'
config_file_name = 'config.ini'
# No config file will be created. Error 267: The directory name is invalid, probably because it doesn't exist...
# why wasn't it created automatically?
cf = wx.FileConfig(style=wx.CONFIG_USE_GLOBAL_FILE | wx.CONFIG_USE_SUBDIR)
cf.Write('foo', 'bar')
cf.Flush()
# Try again with and this time specify the localFilename, but still no config file will be created...
# Same error 267 as before...
config_path = os.path.join(wx.StandardPaths.Get().GetUserConfigDir(), vendor_name + '1', app_name, config_file_name)
cf = wx.FileConfig(localFilename=config_path)
cf.Write('foo', 'bar')
cf.Flush()
# Ok, lets make sure the directory exists prior to creating the FileConfig object. Yet still no config file will be
# created. Error 5: Access is denied. My user is an admin with full permissions to the directory we created, why
# is permission denied?
config_path = os.path.join(wx.StandardPaths.Get().GetUserConfigDir(), vendor_name + '2', app_name)
if not os.path.exists(config_path):
os.makedirs(config_path)
cf = wx.FileConfig(localFilename=config_path)
cf.Write('foo', 'bar')
cf.Flush()
# This will create the config file at "AppData\Roaming\my_company" (no .ini extension), unfortunately this is the wrong
# directory since "localFilename" is provided, file should be created in "AppData\Local"
config_path = os.path.join(wx.StandardPaths.Get().GetUserConfigDir(), vendor_name + '3')
cf = wx.FileConfig(localFilename=config_path)
cf.Write('foo', 'bar')
cf.Flush()
I don't think wx.FileConfig makes any promise that it will create folders for you - so your first two points in the code may be invalid. I may be mistaken and may have missed it from the docs, but what I do is preemptively create the folder I need before using wx.FileConfig.
Your third point in the code is fundamentally incorrect: you are providing a folder path, not a file name. This works better:
config_path = os.path.join(wx.StandardPaths.Get().GetUserConfigDir(), vendor_name + '2', app_name)
os.makedirs(config_path, exist_ok=True)
file_path = os.path.join(config_path, config_file_name)
cf = wx.FileConfig(localFilename=file_path)
cf.Write('foo', 'bar')
cf.Flush()
Similar story for point (4) (of course there is no .ini extension, unless this was your intention all the way). Looking at your code:
config_path = os.path.join(wx.StandardPaths.Get().GetUserConfigDir(), vendor_name + '3')
cf = wx.FileConfig(localFilename=config_path)
cf.Write('foo', 'bar')
cf.Flush()
The documentation for GetUserConfigDir() states this:
If you want your stuff in AppData\Local, use the appropriate method: