Monday, February 16, 2015

Deploy depression, PPQT work

Yesterday I described how I'd found that QWebEngineWidgets was not mentioned in the config file left by pyqtdeploycli. I put it in manually and to my delight, the make of Cobro now completed. It left an incomplete app structure, which I may have a handle on fixing (tomorrow). But, assuming that the absence of QW.E.W. was a mistake in pyqtdeploycli, I posted the info to the PyQt mailing list.

Here's the reply from Phil:

The configuration files generated by pyqtdeploycli should be considered starting points which you should review and possibly update to meet your specific needs. The reason why those modules are not enabled by default is that they are not available on all platforms supported by Python.

This made me quite angry. First, the documentation says this:

The package configuration files created by the configure action of pyqtdeploycli assume a default Qt installation, i.e. with features that would only be enabled by default on the particular platform. For example, SSL support is disabled for Windows and Android targets. If you have configured your Qt installation differently then you may need to modify the configuration files appropriately.

Is there anything there to suggest that a normal piece of Qt for all desktop platforms would be omitted? And as for "not available", probably QWebEngine is not supported on some mobile platforms, but pyqtdeploycli is told on the command line the target is "osx_64" and should not have any question about whether the feature is supported there.

Well, what's the point of bitching? I got over that hump.

And then I noticed this gem:

pyqtdeploy itself uses rcc to embed all the files that make up the applications and does not support the use of the output of pyrcc5 and pyrcc4 in a deployed application.

So? So, I am using pyrcc5 to embed two monospaced fonts into PPQT. The above means that, if I use pyqtdeploy on PPQT, I have to change the logic of how those fonts are delivered. Yes, the change will affect only one module (fonts.py) and some documentation (I'll have to explain why there are a couple of fonts in the Extras folder, which is I guess where I'll have to put them).

All this left me quite depressed about this whole thing. It's been almost two months I've been fucking around with bundlers, and every fucking one of them has failed. I am not stupid or inexperienced but I have to say I've never butted my head so hard against a problem, for so long, with so little to show for it. I keep thinking I'm close to a breakthrough with pyqtdeploy, but every time I get over one hurdle there's another ahead. It is just an exceedingly complex, poorly-documented affair based on many hidden assumptions that I don't know about. And just a bugger to work with.

For a while I thought seriously about tossing the whole project, both projects, just saying, fuck it, here's the github source, enjoy yourself. And I thought seriously about going on elance and finding a contractor and solving the problem by paying somebody to solve it. I did that when I just could not get hunspell to work on Windows; it was $150 well spent.

Well, so. In the course of writing my PPQT2 help file (which is done) I found several things that needed fixing. And this afternoon I fixed all but one of them. That remaining one is a bit of a poser.

In V1 and also V2, the Find panel can be used for regex search and replace. When the Regex switch is checked, every time the user edits the Find string I quickly do a regex.compile() of it, inside a try/except block. The only exception would be a regex error; and if one is triggered, I turn the background of the string pink and put the regex diagnostic message in the toolTip string for the field.

While testing things I was writing into the help, I happened to trigger a different error. I put something invalid in a Replace field, and did a Replace. When the Regex switch is on, a Replace means doing match.expand() on the match object resulting from the most recent Find. Turns out, if the replace string is invalid, that can throw an exception. Down in the guts of the do_replace where no exceptions were anticipated.

So clearly, I thought, I need to also syntax-check the Replace string as the user edits it, similar to how I'm checking the Find expression. Well, not so simple. First, the main error that a replace can have is a reference to a non-existent match group: \2 when the regex didn't have two paren groups, say. But that means the correctness of a replace expression depends on the content of the match expression. Which means (a) the replace expression can't be valid if the find expression is invalid, (b) if the replace expression is valid now, but the user changes the find expression in a valid way, the replace expression can become invalid.

And then, my scheme for checking replace validity fell apart. I'd supposed I could test it in this way. Suppose that the current, valid Find expression is compiled as find_rx. I supposed I could do something like,

try:
    find_rx.sub(rep_text, '' )
except:
    handle the error

Nope. Because the find_rx matches itself against the target string (the null string), says, "I don't match, thanks, finished, bye." And never gets around to compiling the rep_text. The only way to get it to actually look at the rep_text is to have a valid match. But no match is available at the time the user is still editing the replace string.

All this says that there is no way to pre-validate the replace expression. I will have to put the actual replace operation in a try block, and when it throws an error, then I can turn the replace string pink and put an error text in its toolTip. But then, when will I know to clear the error indication? Whenever the user edits that field or the Find expression, I suppose.

No comments: