Skip to content

Commit

Permalink
Studio app module/annotations: add type annotations for module-level …
Browse files Browse the repository at this point in the history
…functions. Re 3155.

Add type annotations for module-level functions, including argument and return types. The only exception is finally function wrapper in Studio app module main module and config UI opener functions.
  • Loading branch information
josephsl committed Jan 24, 2021
1 parent 00542f7 commit 900b199
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 46 deletions.
2 changes: 1 addition & 1 deletion addon/appModules/splcreator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

# Return a tuple of column headers.
# This is just a thinly disguised indexOf function from Studio's track item class.
def indexOf(creatorVersion):
def indexOf(creatorVersion: str) -> tuple[str, ...]:
# Nine columns per line for each tuple.
if creatorVersion >= "5.31":
return (
Expand Down
2 changes: 1 addition & 1 deletion addon/appModules/splengine/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

# #98: call the one from encoders module directly.
# This is an indirect jump due to SPL Controller's encoder connection status command.
def announceEncoderConnectionStatus():
def announceEncoderConnectionStatus() -> None:
from . import encoders
encoders.announceEncoderConnectionStatus()

Expand Down
12 changes: 6 additions & 6 deletions addon/appModules/splengine/encoders.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@


# Load encoder config (including labels and other goodies) from a file-based database.
def loadEncoderConfig():
def loadEncoderConfig() -> None:
# 20.11 (Flake8 E501): define global variables when first used, not here due to line length.
global encoderConfig
encoderConfigPath = os.path.join(globalVars.appArgs.configPath, "splencoders.ini")
Expand Down Expand Up @@ -95,7 +95,7 @@ def loadEncoderConfig():

# Remove encoder ID from various settings maps and sets.
# This is a private module level function in order for it to be invoked by humans alone.
def _removeEncoderID(encoderType, pos):
def _removeEncoderID(encoderType: str, pos: str) -> None:
encoderID = " ".join([encoderType, pos])
# Go through each feature map/set, remove the encoder ID and manipulate encoder positions.
for encoderSettings in (
Expand Down Expand Up @@ -133,7 +133,7 @@ def _removeEncoderID(encoderType, pos):


# Save encoder labels and flags, called when closing app modules and/or config save command is pressed.
def saveEncoderConfig():
def saveEncoderConfig() -> None:
# Gather stream labels and flags.
# 20.11: dictionaries and sets are global items.
encoderConfig["EncoderLabels"] = dict(SPLEncoderLabels)
Expand All @@ -153,7 +153,7 @@ def saveEncoderConfig():
# Nullify various flag sets, otherwise memory leak occurs.
# 20.04: if told to do so, save encoder settings and unregister config save handler.
# In case this is called as part of a reset, unregister config save handler unconditionally.
def cleanup(appTerminating=False, reset=False):
def cleanup(appTerminating: bool = False, reset: bool = False) -> None:
# 20.11 (Flake8 E501): apart from encoder config, other flag containers are global variables.
global encoderConfig
# #132 (20.05): do not proceed if encoder settings database is None (no encoders were initialized).
Expand Down Expand Up @@ -182,7 +182,7 @@ def cleanup(appTerminating=False, reset=False):
# Reset encoder settings.
# Because simply reloading settings will introduce errors,
# respond only to proper reset signal (Control+NVDA+R three times).
def resetEncoderConfig(factoryDefaults=False):
def resetEncoderConfig(factoryDefaults: bool = False) -> None:
if factoryDefaults:
cleanup(reset=True)

Expand Down Expand Up @@ -297,7 +297,7 @@ def onAppTerminate(self):


# Announce connected encoders if any.
def announceEncoderConnectionStatus():
def announceEncoderConnectionStatus() -> None:
import windowUtils
# For SAM encoders, descend into encoder window after locating the foreground window.
# For others, look for a specific SysListView32 control.
Expand Down
6 changes: 3 additions & 3 deletions addon/appModules/splstudio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ def new(*args, **kwargs):


# Braille and play a sound in response to an alarm or an event.
def messageSound(wavFile, message):
def messageSound(wavFile: str, message: str) -> None:
nvwave.playWaveFile(wavFile)
braille.handler.message(message)


# A special version for microphone alarm (continuous or not).
def _micAlarmAnnouncer():
def _micAlarmAnnouncer() -> None:
if splconfig.SPLConfig["General"]["AlarmAnnounce"] in ("beep", "both"):
nvwave.playWaveFile(os.path.join(os.path.dirname(__file__), "SPL_MicAlarm.wav"))
if splconfig.SPLConfig["General"]["AlarmAnnounce"] in ("message", "both"):
Expand All @@ -92,7 +92,7 @@ def _micAlarmAnnouncer():


# Manage microphone alarm announcement.
def micAlarmManager(micAlarmWav, micAlarmMessage):
def micAlarmManager(micAlarmWav: str, micAlarmMessage: str) -> None:
messageSound(micAlarmWav, micAlarmMessage)
# Play an alarm sound (courtesy of Jerry Mader from Mader Radio).
global micAlarmT2
Expand Down
6 changes: 3 additions & 3 deletions addon/appModules/splstudio/splbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# This is to make sure custom commands for SPL Assistant commands
# and other app module gestures display appropriate error messages.
# 19.02: some checks will need to omit message output.
def studioIsRunning(justChecking=False):
def studioIsRunning(justChecking: bool = False) -> bool:
# Keep the boolean flag handy because of message output.
isStudioAlive = (
(_SPLWin is not None and _SPLWin == user32.FindWindowW("SPLStudio", None))
Expand All @@ -40,7 +40,7 @@ def studioIsRunning(justChecking=False):
# 18.05: strengthen this by checking for the handle once more.
# #92 (19.03): SendMessage function returns something from anything (including from dead window handles),
# so really make sure Studio window handle is alive.
def studioAPI(arg, command):
def studioAPI(arg: int, command: int) -> Optional[int]:
if not studioIsRunning(justChecking=True):
return
log.debug(f"SPL: Studio API wParem is {arg}, lParem is {command}")
Expand All @@ -50,7 +50,7 @@ def studioAPI(arg, command):


# Select a track upon request.
def selectTrack(trackIndex):
def selectTrack(trackIndex: int) -> None:
studioAPI(-1, 121)
log.debug(f"SPL: selecting track index {trackIndex}")
studioAPI(trackIndex, 121)
14 changes: 7 additions & 7 deletions addon/appModules/splstudio/splconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ def swapProfiles(self, prevProfile, newProfile, showSwitchIndex=False):


# Open config database, used mostly from modules other than Studio.
def openConfig(splComponent):
def openConfig(splComponent: str) -> None:
global SPLConfig
# #64 (18.07): skip this step if another SPL component (such as Creator) opened this.
if SPLConfig is None:
Expand All @@ -694,7 +694,7 @@ def openConfig(splComponent):
SPLConfig.splComponents.add(splComponent)


def initialize():
def initialize() -> None:
global SPLConfig, _configLoadStatus, trackComments
# Load the default config from a list of profiles.
# 8.0: All this work will be performed when ConfigHub loads.
Expand Down Expand Up @@ -736,7 +736,7 @@ def initialize():


# Close config database if needed.
def closeConfig(splComponent):
def closeConfig(splComponent: str) -> None:
global SPLConfig, _SPLCache
# #99 (19.06/18.09.9-LTS): if more than one instance of a given SPL component executable is running,
# do not remove the component from the components registry.
Expand All @@ -759,7 +759,7 @@ def closeConfig(splComponent):


# Terminate the config and related subsystems.
def terminate():
def terminate() -> None:
global SPLConfig, _SPLCache
# Dump track comments.
with open(os.path.join(globalVars.appArgs.configPath, "spltrackcomments.pickle"), "wb") as f:
Expand All @@ -771,7 +771,7 @@ def terminate():


# Called from within the app module.
def instantProfileSwitch():
def instantProfileSwitch() -> None:
# 17.10: What if only normal profile is in use?
if SPLConfig.normalProfileOnly:
# Translators: announced when only normal profile is in use.
Expand Down Expand Up @@ -880,7 +880,7 @@ def onAppTerminate(self):

# And to open the above dialog and any other dialogs.
# 18.09: return immediately after opening old ver dialog if minimal flag is set.
def showStartupDialogs(oldVer=False):
def showStartupDialogs(oldVer: bool = False) -> None:
# Old version reminder if this is such a case.
# 17.10: and also used to give people a chance to switch to LTS.
# 20.06: controlled by a temporary flag that can come and go.
Expand All @@ -898,7 +898,7 @@ def showStartupDialogs(oldVer=False):
# This is a multimap, consisting of category, value and message.
# Most of the categories are same as confspec keys,
# hence the below message function is invoked when settings are changed.
def message(category, value):
def message(category: str, value: str) -> None:
verbosityLevels = ("beginner", "advanced")
ui.message(messagePool[category][value][verbosityLevels.index(SPLConfig["General"]["MessageVerbosity"])])

Expand Down
2 changes: 1 addition & 1 deletion addon/appModules/splstudio/splconfui.py
Original file line number Diff line number Diff line change
Expand Up @@ -1469,7 +1469,7 @@ def onAppTerminate(self):

# Centralize error handling for various SPL add-on settings dialogs.
# The below error message came directly from NVDA Core's settings dialog opener method (credit: NV Access)
def _configDialogOpenError():
def _configDialogOpenError() -> None:
gui.messageBox(
translate("An NVDA settings dialog is already open. Please close it first."),
translate("Error"), style=wx.OK | wx.ICON_ERROR
Expand Down
46 changes: 23 additions & 23 deletions addon/appModules/splstudio/splmisc.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

# #155 (21.03): remove __future__ import when NVDA runs under Python 3.10.
from __future__ import annotations
from typing import Optional
from typing import Any, Optional
import weakref
import os
import threading
Expand Down Expand Up @@ -37,7 +37,7 @@ class CustomComboBox(wx.ComboBox, wx.Choice):

# Track Finder error dialog.
# This will be refactored into something else.
def _finderError():
def _finderError() -> None:
global _findDialogOpened
if _findDialogOpened:
gui.messageBox(
Expand Down Expand Up @@ -279,7 +279,7 @@ def onAppTerminate(self):
)


def _populateCarts(carts, cartlst, modifier, standardEdition=False, refresh=False):
def _populateCarts(carts: dict[str, Any], cartlst: list[str], modifier: str, standardEdition: bool = False, refresh: bool = False) -> None:
# The real cart string parser, a helper for cart explorer for building cart entries.
# 5.2: Discard number row if SPL Standard is in use.
if standardEdition:
Expand Down Expand Up @@ -316,7 +316,7 @@ def _populateCarts(carts, cartlst, modifier, standardEdition=False, refresh=Fals
# Cart files list is for future use when custom cart names are used.
# if told to refresh, timestamps will be checked and updated banks will be reassigned.
# Carts dictionary is used if and only if refresh is on, as it'll modify live carts.
def cartExplorerInit(StudioTitle, cartFiles=None, refresh=False, carts=None):
def cartExplorerInit(StudioTitle: str, cartFiles: Optional[list[str]] = None, refresh: bool = False, carts: dict[str, Any] = None) -> dict[str, Any]:
global _cartEditTimestamps
log.debug("SPL: refreshing Cart Explorer" if refresh else "preparing cart Explorer")
# Use cart files in SPL's data folder to build carts dictionary.
Expand Down Expand Up @@ -380,7 +380,7 @@ def cartExplorerInit(StudioTitle, cartFiles=None, refresh=False, carts=None):

# Refresh carts upon request.
# calls cart explorer init with special (internal) flags.
def cartExplorerRefresh(studioTitle, currentCarts):
def cartExplorerRefresh(studioTitle: str, currentCarts: dict[str, Any]) -> dict[str, Any]:
return cartExplorerInit(studioTitle, refresh=True, carts=currentCarts)


Expand All @@ -390,7 +390,7 @@ def cartExplorerRefresh(studioTitle, currentCarts):
# Gather streaming flags into a list.
# 18.04: raise runtime error if list is nothing
# (thankfully the splbase's StudioAPI will return None if Studio handle is not found).
def metadataList():
def metadataList() -> list[int]:
metadata = [splbase.studioAPI(pos, 36) for pos in range(5)]
if metadata == [None, None, None, None, None]:
raise RuntimeError("Studio handle not found, no metadata list to return")
Expand All @@ -400,7 +400,7 @@ def metadataList():
# Metadata server connector, to be utilized from many modules.
# Servers refer to a list of connection flags to pass to Studio API,
# and if not present, will be pulled from add-on settings.
def metadataConnector(servers=None):
def metadataConnector(servers: Optional[list[bool]] = None) -> None:
if servers is None:
from . import splconfig
servers = splconfig.SPLConfig["MetadataStreaming"]["MetadataEnabled"]
Expand All @@ -411,7 +411,7 @@ def metadataConnector(servers=None):

# Metadata status formatter.
# 18.04: say something if Studio handle is not found.
def metadataStatus():
def metadataStatus() -> str:
try:
streams = metadataList()
except RuntimeError:
Expand Down Expand Up @@ -461,7 +461,7 @@ def metadataStatus():
# This is necessary in order to allow extension points to work correctly
# and to not hold up other registered action handlers.
# A special startup flag will be used so other text sequences will not be cut off.
def _metadataAnnouncerInternal(status, startup=False):
def _metadataAnnouncerInternal(status: str, startup: bool = False) -> None:
if not startup:
speech.cancelSpeech()
queueHandler.queueFunction(queueHandler.eventQueue, ui.message, status)
Expand All @@ -471,7 +471,7 @@ def _metadataAnnouncerInternal(status, startup=False):
_earlyMetadataAnnouncer = None


def _earlyMetadataAnnouncerInternal(status, startup=False):
def _earlyMetadataAnnouncerInternal(status: str, startup: bool = False) -> None:
global _earlyMetadataAnnouncer
if _earlyMetadataAnnouncer is not None:
_earlyMetadataAnnouncer.cancel()
Expand All @@ -490,7 +490,7 @@ def _earlyMetadataAnnouncerInternal(status, startup=False):
# The config dialog active flag is only invoked when being notified while add-on settings dialog is focused.
# Settings reset flag is used to prevent metadata server connection
# when settings are reloaded from disk or reset to defaults.
def metadata_actionProfileSwitched(configDialogActive=False, settingsReset=False):
def metadata_actionProfileSwitched(configDialogActive: bool = False, settingsReset: bool = False) -> None:
from . import splconfig
# Only connect if add-on settings is active in order to avoid wasting thread running time.
if configDialogActive:
Expand Down Expand Up @@ -523,7 +523,7 @@ def metadata_actionProfileSwitched(configDialogActive=False, settingsReset=False


# The only job of this action handler is to call profile switch handler above with special flags.
def metadata_actionSettingsReset(factoryDefaults=False):
def metadata_actionSettingsReset(factoryDefaults: bool = False) -> None:
metadata_actionProfileSwitched(settingsReset=True)


Expand All @@ -536,7 +536,7 @@ def metadata_actionSettingsReset(factoryDefaults=False):
# Obtain column presentation order.
# Although this is useful in playlist transcripts,
# it can also be useful for column announcement inclusion and order.
def columnPresentationOrder():
def columnPresentationOrder() -> list[str]:
from . import splconfig
return [
column for column in splconfig.SPLConfig["PlaylistTranscripts"]["ColumnOrder"]
Expand All @@ -548,19 +548,19 @@ def columnPresentationOrder():
# copying to clipboard (text style format only), and saving to a file.


def displayPlaylistTranscripts(transcript, HTMLDecoration=False):
def displayPlaylistTranscripts(transcript: list[str], HTMLDecoration: bool = False) -> None:
ui.browseableMessage("\n".join(transcript), title=_("Playlist Transcripts"), isHtml=HTMLDecoration)


def copyPlaylistTranscriptsToClipboard(playlistTranscripts):
def copyPlaylistTranscriptsToClipboard(playlistTranscripts: list[str]) -> None:
# Only text style transcript such as pure text and Markdown supports copying contents to clipboard.
import api
api.copyToClip("\r\n".join(playlistTranscripts))
# Translators: presented when playlist transcript data was copied to the clipboard.
ui.message(_("Playlist data copied to clipboard"))


def savePlaylistTranscriptsToFile(playlistTranscripts, extension, location=None):
def savePlaylistTranscriptsToFile(playlistTranscripts: list[str], extension: str, location: Optional[str] = None) -> None:
# By default playlist transcripts will be saved to a subfolder in user's Documents folder
# named "nvdasplPlaylistTranscripts".
# Each transcript file will be named yyyymmdd-hhmmss-splPlaylistTranscript.ext.
Expand All @@ -583,7 +583,7 @@ def savePlaylistTranscriptsToFile(playlistTranscripts, extension, location=None)
# For text file 1 and HTML list 1, it expects playlist data in the format presented by MSAA.
# Header will not be included if additional decorations will be done (mostly for HTML and others).
# Prefix and suffix denote text to be added around entries (useful for various additional decoration rules).
def playlist2msaa(start, end, additionalDecorations=False, prefix="", suffix=""):
def playlist2msaa(start: Any, end: Any, additionalDecorations: bool = False, prefix: str = "", suffix: str = "") -> list[str]:
playlistTranscripts = []
# Just pure text, ready for the clipboard or writing to a txt file.
if not additionalDecorations:
Expand All @@ -607,7 +607,7 @@ def playlist2msaa(start, end, additionalDecorations=False, prefix="", suffix="")
return playlistTranscripts


def playlist2txt(start, end, transcriptAction):
def playlist2txt(start: Any, end: Any, transcriptAction: int) -> None:
playlistTranscripts = playlist2msaa(start, end)
if transcriptAction == 0:
displayPlaylistTranscripts(playlistTranscripts)
Expand All @@ -620,7 +620,7 @@ def playlist2txt(start, end, transcriptAction):
SPLPlaylistTranscriptFormats.append(("txt", playlist2txt, "plain text with one line per entry"))


def playlist2htmlTable(start, end, transcriptAction):
def playlist2htmlTable(start: Any, end: Any, transcriptAction: int) -> None:
if transcriptAction == 1:
playlistTranscripts = ["<html><head><title>Playlist Transcripts</title></head>"]
playlistTranscripts.append("<body>")
Expand Down Expand Up @@ -651,7 +651,7 @@ def playlist2htmlTable(start, end, transcriptAction):
SPLPlaylistTranscriptFormats.append(("htmltable", playlist2htmlTable, "Table in HTML format"))


def playlist2htmlList(start, end, transcriptAction):
def playlist2htmlList(start: Any, end: Any, transcriptAction: int) -> None:
if transcriptAction == 1:
playlistTranscripts = ["<html><head><title>Playlist Transcripts</title></head>"]
playlistTranscripts.append("<body>")
Expand All @@ -671,7 +671,7 @@ def playlist2htmlList(start, end, transcriptAction):
SPLPlaylistTranscriptFormats.append(("htmllist", playlist2htmlList, "Data list in HTML format"))


def playlist2mdTable(start, end, transcriptAction):
def playlist2mdTable(start: Any, end: Any, transcriptAction: int) -> None:
playlistTranscripts = []
columnHeaders = columnPresentationOrder()
playlistTranscripts.append("| {headers} |\n".format(headers=" | ".join(columnHeaders)))
Expand All @@ -692,7 +692,7 @@ def playlist2mdTable(start, end, transcriptAction):
SPLPlaylistTranscriptFormats.append(("mdtable", playlist2mdTable, "Table in Markdown format"))


def playlist2csv(start, end, transcriptAction):
def playlist2csv(start: Any, end: Any, transcriptAction: int) -> None:
playlistTranscripts = []
columnHeaders = columnPresentationOrder()
playlistTranscripts.append("\"{0}\"\n".format("\",\"".join([col for col in columnHeaders])))
Expand All @@ -716,7 +716,7 @@ def playlist2csv(start, end, transcriptAction):
_plTranscriptsDialogOpened = False


def plTranscriptsDialogError():
def plTranscriptsDialogError() -> None:
gui.messageBox(
# Translators: Text of the dialog when another playlist transcripts dialog is open.
_("Another playlist transcripts dialog is open."), translate("Error"), style=wx.OK | wx.ICON_ERROR
Expand Down
Loading

0 comments on commit 900b199

Please sign in to comment.