Tuesday, August 5, 2014

Unreliable paths

Finished coding paths.py which (big surprise) turned out to be more complicated than planned. Basically this is just a module that has a pair of static globals _EXTRAS and _DICTS that contain path strings, and offers get- and set- functions for them. Simple.

The complications come in during initialization. Like its Preference-related buddies colors.py, fonts.py, and dictionaries.py, it has an initialize(settings) function that receives a QSettings, and is supposed to recover from it, the last user-set values for what it manages, namely those two paths. Still simple, like this?

    global _EXTRAS
    _EXTRAS = settings.value('paths/extras_path','some default here')

Not simple. First, what is the default if there is no entry in the settings? Presumably that would indicate a brand-new installation, one where PPQT has never run and shut-down to write some settings. Or the settings were erased. (Or PPQT is on a thumb-drive and was just plugged into a different computer.)

Then, supposing that there is a value in the settings, there is no guarantee that it still represents a valid path. Maybe the path has been removed, or renamed, since the settings were written. It can't be trusted.

Plus, the path to extras and the path to dicts need different default assumptions. It's OK for the dicts path to be a null string, the spellcheck dicts for a book might be in the book's folder.

And the more I fiddled with this initialize(settings) function, the more scenarios came to mind. Finally I settled on this set of policies.

First, there is a function paths.check_path(path) that reads like this:

def check_path(path):
    return os.access( path ,os.F_OK ) and os.access( path, os.R_OK )

It returns False for anything not a valid path to a readable end-point. In particular, it returns False for a null string. So the null string is the appropriate default for the settings.value() calls.

If the "extras" path value from settings passes check_path(), use it. This is the usual and expected case.

If it fails check_path() (either because it wasn't set or is not valid in the present system), look for a folder "extras" in the same location as the app executable, and set that as extras.

If that doesn't exist, set the CWD as the extras path.

If the "dicts" path from settings passes check_path(), as normal and expected, use it.

If it fails check_path(), look for a "dicts" folder on the above-set "extras" path and use that.

Otherwise, leave the dicts path as a null string.

With that all coded up and a test driver written and executing, I could then modify dictionaries.py to use it, and modify its test driver accordingly and run it. That was it for today.

Tomorrow, modify mainwindow.py to use the paths module, and finally check and update the spreadsheet of functions-by-module for all the above. Then I can continue with my easy code review.

No comments: