Sunday, 29 August 2021

Different Python packages use different versions of the same DLL

For my project I use pyenchant library for spell checking and win32com.client.dispatch to call SAPI.SpVoice for automatic voiceovers. I have some Speech2Go packages installed and all worked fine until I decided it's time to move my project to Python 3.9. The only thing preventing me to do it is the new version of pyenchant, which uses the same DLL as Speech2Go, but the different version. By trial and error I deduced that the DLL in question is libstdc++-6.dll and I cannot force one of these 2 programs to use the same version neither I can rename the DLL obviously. But it turned out that this code does not work in Python 3.9:

import win32com.client
speaker = win32com.client.Dispatch("SAPI.SpVoice")

def speaktest(text):
    voices = speaker.GetVoices("Language = 409", 'Gender = Male')
    for voice in voices:
        desc = voice.GetDescription()
        if "Eric" in desc:
            speaker.Voice = voice
            speaker.Speak(text)
            break

if __name__ == '__main__':
    speaktest("Hello")
    import enchant
    from enchant.checker import SpellChecker

After speaker.Speak(text) is called, provided a Speech2Go voice is selected (named "Eric" in my case, because that's what I have installed), I cannot load pyenchant because of this error:

Traceback (most recent call last):
  File "C:\Users\User\AppData\Roaming\JetBrains\PyCharm2021.2\scratches\scratch_9.py", line 21, in <module>
    import enchant
  File "C:\Plan Z Editor XXL\venv\lib\site-packages\enchant\__init__.py", line 81, in <module>
    from enchant import _enchant as _e
  File "C:\Plan Z Editor XXL\venv\lib\site-packages\enchant\_enchant.py", line 162, in <module>
    e = ctypes.cdll.LoadLibrary(enchant_lib_path)
  File "C:\python39\lib\ctypes\__init__.py", line 452, in LoadLibrary
    return self._dlltype(name)
  File "C:\python39\lib\ctypes\__init__.py", line 374, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: [WinError 127] The specified procedure could not be found

Process finished with exit code 1

If I call speaktest after importing pyenchant, this happens:

Traceback (most recent call last):
  File "C:\Users\User\AppData\Roaming\JetBrains\PyCharm2021.2\scratches\scratch_9.py", line 22, in <module>
    speaktest("Hello")
  File "C:\Users\User\AppData\Roaming\JetBrains\PyCharm2021.2\scratches\scratch_9.py", line 16, in speaktest
    speaker.Speak(text)
  File "C:\Users\USER\AppData\Local\Temp\gen_py\3.9\C866CA3A-32F7-11D2-9602-00C04F8EE628x0x5x4\ISpeechVoice.py", line 70, in Speak
    return self._oleobj_.InvokeTypes(12, LCID, 1, (3, 0), ((8, 1), (3, 49)),Text
pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2147221164), None)

Process finished with exit code 1

What CAN be a working solution is to use multiprocessing to handle the speaking and making sure multiprocessing.freeze_support() is called before importing pyenchant, thus making different Python instances load different versions of libstdc++-6.dll, but since I already have a code base around both pyEnchant and SAPI.SpVoice and it works the way it is, it's not a good idea to rewrite all the code just because these 2 libraries conflict with each other.

That's why I'm here asking whether there is a much simpler method of resolving this conflict without rewriting a large chunk of code?



from Different Python packages use different versions of the same DLL

What result codes can be returned from BillingClient.launchBillingFlow()?

I'm using using Google's Billing Library 4. The documentation for BillingClient.launchBillingFlow says:

Initiates the billing flow for an in-app purchase or subscription.

It will show the Google Play purchase screen. The result will be delivered via the PurchasesUpdatedListener interface implementation set by BillingClient.Builder.setListener(PurchasesUpdatedListener).

The PurchasesUpdatedListener is passed a BillingResult object that contains a response code. However, the launchBillingFlow method also returns a BillingResult object. My question is, what response codes do I need to deal with in each place?

The documentation is, shall we say, less than clear, and also seems wrong. For launchBillingFlow, it says that the method returns a BillingResult with a code of BillingResponseCode.BILLING_CANCELED if the user cancels the purchase flow. However, my experiments show that code is actually delivered in a call to PurchasesUpdatedListener.onPurchasesUpdated.

Unfortunately, the source code for BillingClient isn't available, but I did decompile the library module. As far as I can tell, the call to launchBillingFlow can return the following result codes:

  • OK - when the flow successfully starts (and the user is shown Google's purchase screen).
  • SERVICE_DISCONNECTED - when the BillingClient isn't currently connected to Google Play on the device.
  • SERVICE_TIMEOUT - if the connection breaks during the attempt to launch the flow. (I think I read somewhere that this can happen if Google Play is being updated in the background.)
  • FEATURE_NOT_SUPPORTED - under various conditions where the BillingFlowParams don't match the current configuration of the BillingClient object.

There also seems to be a place in the decompiled code where certain error responses from the Google Play billing service are captured and returned here. Does anyone know what other response codes can be returned from a call to launchBillingFlow?



from What result codes can be returned from BillingClient.launchBillingFlow()?

Slide finger vertically to open fragments

I would like to achieve fragments navigation like this: enter image description here

When I slide finger down somewhere on fragment A, fragment C will be opened (as slide movement), to go back to A slide finger up from C and so on...

I thought about custom ViewPager with vertical swipe direction but I have problem with "Open .." triangles, it should be shared between fragments end rotated during slide movement to change from for example "Open C" to "Open A". Any suggestions ? :)



from Slide finger vertically to open fragments