Skip to content
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

Migrate NVDA to Python 3 #9974

Merged
merged 142 commits into from
Jul 25, 2019
Merged

Migrate NVDA to Python 3 #9974

merged 142 commits into from
Jul 25, 2019

Conversation

feerrenrut
Copy link
Contributor

Link to issue number:

#7105

Summary of the issue:

Support for Python 2 is ending, security and bug fix updates will cease. For the longevity of the NVDA project, we must migrate to Python 3.

Description of how this pull request fixes the issue:

This PR merges in many other changes that incrementally brought Python 3 to the NVDA code base.

There are likely yet to be discovered issues, but we have reached a level of stability that we think is acceptable for alpha testing.

Testing performed:

Much testing has been completed, however much more is required. Please see https://github.com/nvaccess/nvda/projects/1 for an overview of this project.

Any testing you can perform would be appreciated, please file bugs as usual (not comments on this PR, which will be closed shortly).

In particular, please test any braille devices you have access to.

Change log entry:

The change log is still being compiled, please see #9942

jcsteh and others added 30 commits May 15, 2019 18:34
…ches and prioritized queuing (#7599)

* Enhance nvwave to simplify accurate indexing for speech synthesizers.

1. Add an onDone argument to WavePlayer.feed which accepts a function to be called when the provided chunk of audio has finished playing. Speech synths can simply feed audio up to an index and use the onDone callback to be accurately notified when the index is reached.
2. Add a buffered argument to the WavePlayer constructor. If True, small chunks of audio will be buffered to prevent audio glitches. This avoids the need for tricky buffering across calls in the synth driver if the synth provides fixed size chunks and an index lands near the end of a previous chunk. It is also useful for synths which always provide very small chunks.

* Enhancements to config profile triggers needed for profile switching within speech sequences.

1. Allow triggers to specify that handlers watching for config profile switches should not be notified. In the case of profile switches during speech sequences, we only want to apply speech settings, not switch braille displays.
2. Add some debug logging for when profiles are activated and deactivated.

* Add support for callbacks, beeps, sounds, profile switches and utterance splits during speech sequences, as well as prioritized queuing.

Changes for synth drivers:

- SynthDrivers must now accurately notify when the synth reaches an index or finishes speaking using the new `synthIndexReached` and `synthDoneSpeaking` extension points in the `synthDriverHandler` module. The `lastIndex` property is deprecated. See below regarding backwards compatibility for SynthDrivers which do not support these notifications.
- SynthDrivers must now support `PitchCommand` if they which to support capital pitch change.
- SynthDrivers now have `supportedCommands` and `supportedNotifications` attributes which specify what they support.
- Because there are some speech commands which trigger behaviour unrelated to synthesizers (e.g. beeps, callbacks and profile switches), commands which are passed to synthesizers are now subclasses of `speech.SynthCommand`.

Central speech manager:

- The core of this new functionality is the `speech._SpeechManager` class. It is intended for internal use only. It is called by higher level functions such as `speech.speak`.
- It manages queuing of speech utterances, calling callbacks at desired points in the speech, profile switching, prioritization, etc. It relies heavily on index reached and done speaking notifications from synths. These notifications alone trigger the next task in the flow.
- It maintains separate queues (`speech._ManagerPriorityQueue`) for each priority. As well as holding the pending speech sequences for that priority, each queue holds other information necessary to restore state (profiles, etc.) when that queue is preempted by a higher priority queue.
- See the docstring for the `speech._SpeechManager` class for a high level summary of the flow of control.

New/enhanced speech commands:

- `EndUtteranceCommand` ends the current utterance at this point in the speech. This allows you to have two utterances in a single speech sequence.
- `CallbackCommand` calls a function when speech reaches the command.
- `BeepCommand` produces a beep when speech reaches the command.
- `WaveFileCommand` plays a wave file when speech reaches the command.
- The above three commands are all subclasses of `BaseCallbackCommand`. You can subclass this to implement other commands which run a pre-defined function.
- `ConfigProfileTriggerCommand` applies (or stops applying) a configuration profile trigger to subsequent speech. This is the basis for switching profiles (and thus synthesizers, speech rates, etc.) for specific languages, math, etc.
- `PitchCommand`, `RateCommand` and `VolumeCommand` can now take either a multiplier or an offset. In addition, they can convert between the two on demand, which makes it easier to handle these commands in synth drivers based on the synth's requirements. They also have an `isDefault` attribute which specifies whether this is returning to the default value (as configured by the user).

Speech priorities:

`speech.speak` now accepts a `priority` argument specifying one of three priorities: `SPRI_NORMAL` (normal priority), `SPRI_NEXT` (speak after next utterance of lower priority)or `SPRI_NOW` (speech is very important and should be spoken right now, interrupting lower priority speech). Interrupted lower priority speech resumes after any higher priority speech is complete.

Refactored functionality to use the new framework:

- Rather than using a polling generator, spelling is now sent as a single speech sequence, including `EndUtteranceCommand`s, `BeepCommand`s and `PitchCommand`s as appropriate. This can be created and incorporated elsewhere using the `speech.getSpeechForSpelling` function.
- Say all has been completely rewritten to use `CallbackCommand`s instead of a polling generator. The code should also be a lot more readable now, as it is now classes with methods for the various stages in the process.

Backwards compatibility for old synths:

- For synths that don't support index and done speaking notifications, we don't use the speech manager at all. This means none of the new functionality (callbacks, profile switching, etc.) will work.
- This means we must fall back to the old code for speak spelling, say all, etc. This code is in the `speechCompat` module.
- This compatibility fallback is considered deprecated and will be removed eventually. Synth drivers should be updated ASAP.

Deprecated/removed:

- `speech.getLastIndex` is deprecated and will simply return None.
- `IndexCommand` should no longer be used in speech sequences passed to `speech.speak`. Use a subclass of `speech.BaseCallbackCommand` instead.
- In the `speech` module, `speakMessage`, `speakText`, `speakTextInfo`, `speakObjectProperties` and `speakObject` no longer take an `index` argument. No add-ons in the official repository use this, so I figured it was safe to just remove it rather than having it do nothing.
- `speech.SpeakWithoutPausesBreakCommand` has been removed. Use `speech.EndUtteranceCommand` instead. No add-ons in the official repository use this.
- `speech.speakWithoutPauses.lastSentIndex` has been removed. Use a subclass of `speech.BaseCallbackCommand` instead. No add-ons in the official repository use this.

* Update the espeak synth driver to support the new speech framework.

* Update the oneCore synth driver to support the new speech framework.

* Update comtypes to version 1.1.3.

This is necessary to handle events from SAPI 5, as one of the parameters is a decimal which is not supported by our existing (very outdated) version of comtypes .
comtypes has now been added as a separate git submodule.

* Update the sapi5 synth driver to support the new speech framework.

* Fix submodule URL for comtypes. Oops!

* Ensure eSpeak emits index callbacks even if the espeak event chunk containing the index is  0 samples in length. This allows sayAll to function with eSpeak. this may have broken due to a recent change in eSpeak I'm guessing.

* Remove some debug print statements

* Ensure eSpeak sets its speech parameters back to user-configured values if interupted while speaking ssml that changes those parameters.

* Add a 'priority' keyword argument to  all speech.speak* functions allowing  the caller to provide a speech priority.

* Alerts in Chrome and Firefox now are spoken with a higher speech priority, and no longer cancel speech beforehand. This means that the alert text will be spoken straight away, but will not interrupt  other speech such as the new focus.

* Unit tests: fake speech functions now must take a 'priority' keyword argument to match the real functions.

* Remove speech compat

* synthDriverHandler.handleConfigProfileSwitch was renamed to handlePostConfigProfileSwitch. Ensure that speech  uses this name now.

* synthDriverHandler.handlePostConfigProfileSwitch: reset speech queues if switching synths, so that the new synth has entirely new speech state.
This behaviour can however be disabled by providing resetSpeechIfNeeded to false on this function. Speechmanager internally does this when dealing with configProfileSwitch commands in speech sequences.

 # Please enter the commit message for your changes. Lines starting

* Remove the audioLogic synthesizer due to its extremely low usage.  If someone does require this, it could be updated and provided as an add-on.

* Speech: correct indentation, which allows  profile switching in a speech eequence to work again.

* Sapi4 synthDriver: very basic conversion to speechRefactor supporting synthIndexReached and synthDoneSpeaking, though for some sapi4 synths, indexing could be slightly early.

* Convert system test synthDriver to speechRefactor so that indexing and doneSpeaking notifications are fired.

* sayAllhandler: move trigger handling into _readText as recommended.

* Remove some more speech compat stuff.

* Speech: BaseCallbackCommand's run method is now abstract. Note that CallbackCommand now has been changed slightly so it implements its own run method which executes the callback passed to the command rather than run being overridden directly. BeepCommand also now calls playWaveFile with async set to True, not False so that it does not block. finally, BaseCallbackCommand's run method docstring notes that the main thread is blocked.

* Speech: fix up some docstrings.
…mpat to the same.

We already know there will be 2019.2 before threshold, but this may need to be bumpt to 2019.4.0 if we end up doing a 2019.3 off master as well.
* No longer clear curWordChars in speech.speak, rather clear it on focus and caret movement.

* Break up docstring.
The version number for threshold is currently unknown, I have added a new section at the start of this file as a place holder for this version number.

Merges from master with modifications to this file should not be too hard, the changes from master should always come after the changes on the threshold branch. Because they are in different parts of the file there should be very few conflicts.
Change iter.next() to next(iter)
closes #9086
* SayAll: avoid error when reading to the end of a Microsoft word document where self.reader is set to None.

* Move speech.py into its own package ready for splitting out into separate modules.

* Move speech command objects into a speech.commands module.

* Split out speechManager into speech.manager module and priority constants into  speech.priorities module.

* espeak: provide constants for callback return codes.

* espeak: use named constants for converting ms to bytes.

* nvWave: use named constants for buffer size calculation.

* speech.getSpeechForSpelling: use a named constant for the idiographic comma and rename char to speakCharAs.

* espeak: ensure that onIndexReached is set before espeak_setSynthCallback is called.

* removed accidental file speech./commands.py

* rename constant with caps
…or now as master is already heading toward a 2019.3.
* Remove deprecated functionality from braille

* Remove validate module

* Remove deprecated functions from synthDriverHandler

* Replace textInfos.Rect and textInfos.Point with locationHelper

* Fix tethering

* Remove old sound recorder appModule

* Removed old pre vista code

* Remove getConfigValidationParameter

* Remove deprecated synthesizer setting classes

* Remove deprecated logIdentifier

* Remove validate from setup.py

* Remove pythonMonkeyPatches

* Remove support for Skype 7

* Remove Outlook pre2003 class

* Remove support for abandoned klango player

* Remove legacy code from Explorer appmodule

* Remove Vista specific check from installer

* Revert "Remove Vista specific check from installer"

This reverts commit dfb2dbc.

* Revert accidental change of configobj module

* Fix typo in explorer appModule

* espeak synthesizer, do not import BooleanSynthSetting

* synthDriverHandler: no longer mention supportedSettings in the doc string on SynthDriver, as it is already part of the Driver class

* Update what's new.
* Remove dependency on pyWin32, including pythoncom, win32clipboard and win32con.

* nvdaHelperLocal's oleUtils.cpp: fix syntax error.

* Address review comments.

* No longer forceably include win32api via setup.py. Also update what's new.
* nvWave.playWaveFile: async > asynchronous. Re #8607.

Python 3.5 introduces 'async' and 'await' keywords to deal with asynchronous generators and other possibiliites. Since Python 3.7, use of these keywords as variable names is no longer allowed. In NVDA code, nvWave.playWaveFile is affected, so rename 'async' to 'asynchronous'.

* nvwave.playWaveFile: document 'asynchronous' keyword.

Reviewed by Leonard de Ruijter (Babbage): document the renamed keyword arg.

* Speech commands: async -> asynchronous.

* nvwave.playWaveFile: correct argument name in docstring.
* IAccessible objects: relative imports. Re #8712.

* Excel window objects: relative imports. Re #8712.

* GUI: relative imports. Re #8712.

* Settings dialogs: relative imports. Re #8712.
LeonarddeR and others added 24 commits July 9, 2019 14:27
…that surrogate characters are handled properly (PR #9897)

Before this change, in virtual buffers, emoji are shown as two surrogate characters instead of the actual emoji character.
This is because virtual buffers output in UTF-16, and surrogate characters are not allowed in xml. Therefore, the XML contains integer values for the surrogate characters, and they are converted to real characters in the xmlFormatting module. As surrogate characters are perfectly valid within Python 3 and even a surrogate pair is allowed, Python 3 does not collapse two surrogate characters into one 32-bit character.

If a low surrogate character is handled, add it to the currently buffered text, and quickly encode and decode the text to/from UTF-16. This ensures that surrogate pairs are properly decoded to the associated 32-bit character. The decision to do this for low surrogates only is intentional. A high surrogate is usually followed by a low surrogate. Therefore it doesn't make sense to re-encode if processing a high surrogate. We also want to avoid just re-encoding anything.
Fixes #9768

The VARIANT out-param was not being initialised correctly. Calling VariantClear on an uninitialised VARIANT is an error.

Comtypes was not initialising VARIANT.vt to VT_EMPTY, and attempting to call VariantClear (oleauto.h) before assigning it the value returned from getProp.
Instead, we implement a low-level getProp method by requiring an unused "this" param. This low-levl getProp method is provided pointers to the out-params, allowing it to manually initialise the VARIANT correctly before assigning a value.
…9890)

These comments are no longer required, they have now been reviewed.
… other identified cases (PR: #9881)

Reading documents in Adobe Reader was raising a TypeError
Fixed this int to NoneType comparison, identified some others while at it.
…ility with Audio Themes add-on" (PR #9907)

This reverts commit 7f986ca from PR #9826
Fixes an issue in #9847: catch WindowsError because WinError is not an exception type.
Speech refactor introduced a new way to clear the current words buffer for speech. However, in some cases, this buffer wasn't cleared, particularly in the following cases:

- In legacy windows consoles, when typing a word and pressing enter, pressing space still reported the previous word that was already sent to the console.
- When moving by sentence in Word, the buffer wasn't cleared.

To fix this:
- in EditableText, the logic to clear the buffer was moved from _caretMovementScriptHelper to _caretScriptPostMovedHelper. This ensures that the buffer is also cleared when navigating by sentence, while not clearing it when the cart hasn't moved, such as when trying sentence nav in an application that doesn't implement it.
- I copied the flush_queuedChars from winConsoleUIA to winConsole.

Fixes #9769
Our Python 2 code contained some workarounds  and code paths that are no longer necessary or confusing when on Python 3.

1. Most notably, this is related to functions with `*args` and `**kwargs` catch all handlers which also required a specific keyword argument. Once of these was part of the `hwIo` module and has already been removed in an earlier state.
   - Code where we pop a particular argument from `kwargs` now contains that argument as a keyword only argument in the signature of the function.
2. Furthermore, there was a workaround in the `gui` code to register support for the `cp65001` codec in Python 2, which is a known codec in Python 3.
   - This is removed
3. The `scriptHandler.script` decorator checked whether the decorated script was a routine. This also returned `True` for bound instance methods, and therefore no warning was raised when trying to decorate a bound instance method (which is unsupported). Now, such a warning will be raised. See also #9884 
   - When decorating a script, we now use `isinstance(script, types.FunctionType)`
4. `config.AggregatedSection` still had an `iteritems` method.
   - `config.AggregatedSection.iteritems` has been renamed to items.
NVDA slave module was using the raw_input function to scrape stdin, which raises NameError in Python 3.
In Python 3 raw_input has been replaced with the functional equal input. For more information see:
https://www.python.org/dev/peps/pep-3111/
We were using range as a name of a local attribute or function parameter. In at least one case (uia _getBoundingRectsFromUIARange), this fails, as were also calling the range function in that method.

Looked for every instance of range as a local attribute. In most cases, rename it to textRange.
…on in Installer GUI (PR #9924)

Comparison function found in installer can return an integer (-1, 0, 1) or None. Because of this, when comparing the return value from installer GUI, it assumed None was an integer when in fact in Python 3, it is a different type.

Fixes #9923
…aging

 Conflicts:
	source/gui/settingsDialogs.py
In the Python console, the tab-completion is smarter with Python 3.
Auto complete does not suggest attribute names starting with underscore until an underscore is typed, or a double underscore for attributes with two.

Fixes #9918
In outlook and powerpoint, we create a waiting for xx window while pumping com events. However, comtypes.client.PumpEvents now always raises an error due to a bug in comtypes, see enthought/comtypes#187

Until the bug is fixed in comtypes, this will also log a debug warning for time outs, though that's not very problematic IMO.

Fixes #9921
… behind (PR #9946)

From the speech manager docs:

> For every index reached, L{_handleIndex} is called.
> 		The completed sequence is removed from L{_pendingSequences}.
> 		If there is an associated callback, it is run.
> 		If the index marks the end of an utterance, L{_pushNextSpeech} is called to push more speech.

`_handleIndex` calls `_removeCompletedFromQueue` for the specified index. However, that code did check whether `currentIndex >= lastCommand.index`. In the case where the speech synthesizer sometimes doesn't send a notification for an index, this means the following:

1. Speech synth misses an index, calls the extension point for the index after the missing one
2. `_removeCompletedFromQueue` removes the sequence from the queue that is associated with the missed index, thereby ignoring the current index.
3. More importantly, `_removeCompletedFromQueue` checks the missed index to see whether it is the index for the end of an utterance.
4. If it is not, it expects the sequence to continue. However, if the current index was actually the end of the current utterance, the current index would be handled as soon as the next utterance starts as it is still in the queue, but as the end of the utterance is never handled, speech simply ceases.

Now:
Log cases of indexes we missed, and clean up the sequences for the missed indexes.
When fetching java text ranges, NVDA now provides a character buffer (created with `create_string_buffer` instead of `create_unicode_buffer`) to getAccessibleTextRange (see commit 15d8374). This no longer matches the ctypes prototype applied to the function.

Change the prototype applied to the function to expect c_char instead of _wchar.

Fixes #9939
When updating NVDA, old *.pyc and *.pyo files were remaining. This is problematic for Python 2 pyc files that Python 3 tries to open. Furthermore, it causes issues such as reported in #9960, where the logHandler gets really mad if it tries to log a traceback for a file in library.zip which is also available in the installation directory.

When removing old program files, check for *.pyc and *.pyo files and get rid of them.
SAPI5 was not working on installed copies NVDA because dllImportTableHooks_hookSingle was getting wide strings, not byte strings.
Changing from SAPI5 to another synthesiser still showed the SAPI5 voices in the list of voices.

Now expect Python 3 (unicode) strings, and encode to mbcs/ANSI. This is required because in nvdaHelper/local/dllImportTableHooks.cpp we are using ANSI functions, which should probably change in the future, but for now we need to ensure that these strings are no more than ANSI strings. If the encode fails, which we don't currently expect, the error is logged.

Also add protections in __del__ method self._hook is not set, if there is some failure in the initialisation.

Also fix a circular reference with the SAPI5 event sink.
## Summary of the issue:

In text fields, when moving the caret with caret movement commands, such as the arrows, NVDA relies on bookmarks to find out whether the caret has moved.

In short:

1. You press left arrow
2. NVDA creates a bookmark of the current caret position
3. NVDA executes left arrow
4. For up to the caret movement timeout, NVDA tries to find out whether the caret has moved by creating new bookmarks and comparing them against the old.

However, for UIA, this comparison fails, as creating a bookmark at the end of a range and then removing one character from the end of the range results in a new bookmark that is equal to the former.

## Description of how this fixes the issue:

This PR introduces a different approach based on #9773. In first instance, this code caused #9786 to occur (i.e. editors in Chrome reporting the wrong caret position).

Compared to #9773, flow is now as follows:

-  In a loop in EditableText._hasCaretMoved, NVDA processes pedning events
-   If there is a focus event pending, the loop is exited
-   NVDA tries to find out whether the caret position has changed by creating a textInfo at the caret position
-   new code: only when this succeeds, NVDA checks for pending caret and textChange events. If the object that contains the caret has caretMovementDetectionUsesEvents set to False, it still ignores the caret events. This is what we do in IA2Web objects.

## Testing performed:

- Tested that deleted characters/words are again reported in UIA controls, such as MS Word
- Tested Notepad, Wordpad, Word Without UIA, Thunderbird, LibreOffice, start menu edit field, legacy command consoles and Skype Electron. Made sure that the caret was still correctly reported in all of them when moving with arrow keys or deleting.

## Known issues with pull request:

We can't set the caretMovementDetectionUsesEvents attribute to True on editableText.EditableText and then override it in other places, such as IA2Web. This is because EditableText precedes the IA2Web class as well as other classes in the mro Setting it on EditableText directly therefore makes it impossible for other classes to override the attribute. I solved this by making it a magic property on EditableText that first tries to call super before returning the default.

Fixes #9928
Follow up of  PR #9773
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants