From 90aac14f6827225ac98495c20e4558bfa90fcfbe Mon Sep 17 00:00:00 2001 From: Nils <> Date: Tue, 16 Jun 2020 21:51:59 +0200 Subject: [PATCH] fix the fix --- template/qtgui/nsmclient.py | 120 ++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/template/qtgui/nsmclient.py b/template/qtgui/nsmclient.py index 2e91268..b0518db 100644 --- a/template/qtgui/nsmclient.py +++ b/template/qtgui/nsmclient.py @@ -12,24 +12,24 @@ MIT License Copyright 2014-2020 Nils Hilbricht https://www.laborejo.org -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, -sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import logging; -logger = None #filled by init with prettyName +logger = None #filled by init with prettyName import struct import socket @@ -51,9 +51,9 @@ class _IncomingMessage(object): def __init__(self, dgram): #NSM Broadcasts are bundles, but very simple ones. We only need to care about the single message it contains. - #Therefore we can strip the bundle prefix and handle it as normal message. - if b"#bundle" in dgram: - bundlePrefix, singleMessage = dgram.split(b"/", maxsplit=1) + #Therefore we can strip the bundle prefix and handle it as normal message. + if b"#bundle" in dgram: + bundlePrefix, singleMessage = dgram.split(b"/", maxsplit=1) dgram = b"/" + singleMessage # / eaten by split self.isBroadcast = True else: @@ -108,13 +108,13 @@ class _IncomingMessage(object): ValueError if the datagram could not be parsed. """ #First test for empty string, which is nothing, followed by a terminating \x00 padded by three additional \x00. - if dgram[start_index:].startswith(b"\x00\x00\x00\x00"): + if dgram[start_index:].startswith(b"\x00\x00\x00\x00"): return "", start_index + 4 - + #Otherwise we have a non-empty string that must follow the rules of the docstring. - - offset = 0 - try: + + offset = 0 + try: while dgram[start_index + offset] != 0: offset += 1 if offset == 0: @@ -128,7 +128,7 @@ class _IncomingMessage(object): # do it ourselves. if offset > len(dgram[start_index:]): raise ValueError('Datagram is too short') - data_str = dgram[start_index:start_index + offset] + data_str = dgram[start_index:start_index + offset] return data_str.replace(b'\x00', b'').decode('utf-8'), start_index + offset except IndexError as ie: raise ValueError('Could not parse datagram %s' % ie) @@ -155,7 +155,7 @@ class _IncomingMessage(object): def parse_datagram(self): try: - self._address_regexp, index = self.get_string(self._dgram, 0) + self._address_regexp, index = self.get_string(self._dgram, 0) if not self._dgram[index:]: # No params is legit, just return now. return @@ -269,7 +269,7 @@ class NSMClient(object): global logger logger = logging.getLogger(prettyName) - logger.info("import") + logger.info("import") if loggingLevel == "info" or loggingLevel == 20: logging.basicConfig(level=logging.INFO) #development logger.info("Starting PyNSM2 Client with logging level INFO. Switch to 'error' for a release!") #the NSM name is not ready yet so we just use the pretty name @@ -277,7 +277,7 @@ class NSMClient(object): logging.basicConfig(level=logging.ERROR) #production else: logging.warning("Unknown logging level: {}. Choose 'info' or 'error'".format(loggingLevel)) - logging.basicConfig(level=logging.INFO) #development + logging.basicConfig(level=logging.INFO) #development #given parameters, self.prettyName = prettyName #keep this consistent! Settle for one name. @@ -298,15 +298,15 @@ class NSMClient(object): "/nsm/client/hide_optional_gui" : lambda msg: self.hideGUICallback(), "/nsm/client/session_is_loaded" : self._sessionIsLoadedCallback, #Hello source-code reader. You can add your own reactions here by nsmClient.reactions[oscpath]=func, where func gets the raw _IncomingMessage OSC object as argument. - #broadcast is handled directly by the function because it has more parameters + #broadcast is handled directly by the function because it has more parameters } #self.discardReactions = set(["/nsm/client/session_is_loaded"]) self.discardReactions = set() - + #Networking and Init self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #internet, udp self.sock.bind(('', 0)) #pick a free port on localhost. - ip, port = self.sock.getsockname() + ip, port = self.sock.getsockname() self.ourOscUrl = f"osc.udp://{ip}:{port}/" self.executableName = self.getExecutableName() @@ -325,7 +325,7 @@ class NSMClient(object): self.saveStatus = True # true is clean. false means we need saving. self.announceOurselves() - + assert self.serverFeatures, self.serverFeatures assert self.sessionName, self.sessionName assert self.ourPath, self.ourPath @@ -334,7 +334,7 @@ class NSMClient(object): self.sock.setblocking(False) #We have waited for tha handshake. Now switch blocking off because we expect sock.recvfrom to be empty in 99.99...% of the time so we shouldn't wait for the answer. #After this point the host must include self.reactToMessage in its event loop - #We assume we are save at startup. + #We assume we are save at startup. self.announceSaveStatus(isClean = True) logger.info("NSMClient client init complete. Going into listening mode.") @@ -343,11 +343,11 @@ class NSMClient(object): def reactToMessage(self): """This is the main loop message. It is added to the clients event loop.""" try: - data, addr = self.sock.recvfrom(4096) #4096 is quite big. We don't expect nsm messages this big. Better safe than sorry. However, messages will crash the program if they are bigger than 4096. + data, addr = self.sock.recvfrom(4096) #4096 is quite big. We don't expect nsm messages this big. Better safe than sorry. However, messages will crash the program if they are bigger than 4096. except BlockingIOError: #happens while no data is received. Has nothing to do with blocking or not. return None - msg = _IncomingMessage(data) + msg = _IncomingMessage(data) if msg.oscpath in self.reactions: self.reactions[msg.oscpath](msg) elif msg.oscpath in self.discardReactions: @@ -358,13 +358,13 @@ class NSMClient(object): logger.info ("Got /reply Saved from NSM Server") elif msg.isBroadcast: if self.broadcastCallback: - logger.info (f"Got broadcast with messagePath {msg.oscpath} and listOfArguments {msg.params}") + logger.info (f"Got broadcast with messagePath {msg.oscpath} and listOfArguments {msg.params}") self.broadcastCallback(self.ourPath, self.sessionName, self.ourClientNameUnderNSM, msg.oscpath, msg.params) else: - logger.info (f"No callback for broadcast! Got messagePath {msg.oscpath} and listOfArguments {msg.params}") + logger.info (f"No callback for broadcast! Got messagePath {msg.oscpath} and listOfArguments {msg.params}") elif msg.oscpath == "/error": logger.warning("Got /error from NSM Server. Path: {} , Parameter: {}".format(msg.oscpath, msg.params)) - else: + else: logger.warning("Reaction not implemented:. Path: {} , Parameter: {}".format(msg.oscpath, msg.params)) @@ -374,11 +374,11 @@ class NSMClient(object): if host and port: url = (host, port) else: - url = self.nsmOSCUrl + url = self.nsmOSCUrl msg = _OutgoingMessage(path) for arg in listOfParameters: msg.add_arg(arg) #type is auto-determined by outgoing message - self.sock.sendto(msg.build(), url) + self.sock.sendto(msg.build(), url) def getNsmOSCUrl(self): """Return and save the nsm osc url or raise an error""" @@ -429,22 +429,22 @@ class NSMClient(object): announce.add_arg(self.executableName) #s:executable_name announce.add_arg(1) #i:api_version_major announce.add_arg(2) #i:api_version_minor - announce.add_arg(int(getpid())) #i:pid + announce.add_arg(int(getpid())) #i:pid hostname, port = self.nsmOSCUrl assert hostname, self.nsmOSCUrl assert port, self.nsmOSCUrl self.sock.sendto(announce.build(), self.nsmOSCUrl) - + #Wait for /reply (aka 'Howdy, what took you so long?) - data, addr = self.sock.recvfrom(1024) + data, addr = self.sock.recvfrom(1024) msg = _IncomingMessage(data) if msg.oscpath == "/error": - originalMessage, errorCode, reason = msg.params + originalMessage, errorCode, reason = msg.params logger.error("Code {}: {}".format(errorCode, reason)) quit() - elif msg.oscpath == "/reply": + elif msg.oscpath == "/reply": nsmAnnouncePath, welcomeMessage, managerName, self.serverFeatures = msg.params assert nsmAnnouncePath == "/nsm/server/announce", nsmAnnouncePath logger.info("Got /reply " + welcomeMessage) @@ -463,7 +463,7 @@ class NSMClient(object): replyToOpen.add_arg("{} is opened or created".format(self.prettyName)) self.sock.sendto(replyToOpen.build(), self.nsmOSCUrl) else: - raise ValueError("Unexpected message path after announce: {} {}".format((msg.oscpath, msg.params))) + raise ValueError("Unexpected message path after announce: {}".format((msg.oscpath, msg.params))) def announceGuiVisibility(self, isVisible): message = "/nsm/client/gui_is_shown" if isVisible else "/nsm/client/gui_is_hidden" @@ -484,7 +484,7 @@ class NSMClient(object): logger.info("Telling NSM that our clients save state is now: {}".format(message)) self.sock.sendto(saveStatus.build(), self.nsmOSCUrl) - def _saveCallback(self, msg): + def _saveCallback(self, msg): logger.info("Telling our client to save as {}".format(self.ourPath)) self.saveCallback(self.ourPath, self.sessionName, self.ourClientNameUnderNSM) replyToSave = _OutgoingMessage("/reply") @@ -495,7 +495,7 @@ class NSMClient(object): self.announceSaveStatus(isClean = True) - def _sessionIsLoadedCallback(self, msg): + def _sessionIsLoadedCallback(self, msg): if self.sessionIsLoadedCallback: logger.info("Received 'Session is Loaded'. Our client supports it. Forwarding message...") self.sessionIsLoadedCallback() @@ -567,15 +567,15 @@ class NSMClient(object): We offer a clean solution in calling this function which will trigger a round trip over the NSM server so our client thinks it received a Save instruction. This leads to a clean state with a good saveStatus and no required extra functionality in the client.""" - + logger.info("instructing the NSM-Server to send Save to ourselves.") if "server-control" in self.serverFeatures: #message = _OutgoingMessage("/nsm/server/save") # "Save All" Command. - message = _OutgoingMessage("/nsm/gui/client/save") - message.add_arg("{}".format(self.ourClientId)) + message = _OutgoingMessage("/nsm/gui/client/save") + message.add_arg("{}".format(self.ourClientId)) self.sock.sendto(message.build(), self.nsmOSCUrl) else: - logger.warning("...but the NSM-Server does not support server control. Server only supports: {}".format(self.serverFeatures)) + logger.warning("...but the NSM-Server does not support server control. Server only supports: {}".format(self.serverFeatures)) def changeLabel(self, label:str): """This function is implemented because it is provided by NSM. However, it does not much. @@ -585,9 +585,9 @@ class NSMClient(object): This is fine for us as clients, but you need to provide a GUI field to enter that label.""" logger.info("Telling the NSM-Server that our label is now " + label) - message = _OutgoingMessage("/nsm/client/label") - message.add_arg(label) #s:label - self.sock.sendto(message.build(), self.nsmOSCUrl) + message = _OutgoingMessage("/nsm/client/label") + message.add_arg(label) #s:label + self.sock.sendto(message.build(), self.nsmOSCUrl) def broadcast(self, path:str, arguments:list): """/nsm/server/broadcast s:path [arguments...] @@ -597,27 +597,27 @@ class NSMClient(object): """ if path.startswith("/nsm"): logger.warning("Attempted broadbast starting with /nsm. Not allwoed") - else: + else: logger.info("Sending broadcast " + path + repr(arguments)) - message = _OutgoingMessage("/nsm/server/broadcast") + message = _OutgoingMessage("/nsm/server/broadcast") message.add_arg(path) for arg in arguments: message.add_arg(arg) #type autodetect - self.sock.sendto(message.build(), self.nsmOSCUrl) + self.sock.sendto(message.build(), self.nsmOSCUrl) def importResource(self, filePath): """aka. import into session - + ATTENTION! You will still receive an absolute path from this function. You need to make sure yourself that this path will not be saved in your save file, but rather use a place- - holder that gets replaced by the actual session path each time. A good point is after + holder that gets replaced by the actual session path each time. A good point is after serialisation. search&replace for the session prefix ("ourPath") and replace it with a tag e.g. . The opposite during load. Only such a behaviour will make your session portable. - - Do not use the following pattern: An alternative that comes to mind is to only work with - relative paths and force your programs workdir to the session directory. Better work with - absolute paths internally . + + Do not use the following pattern: An alternative that comes to mind is to only work with + relative paths and force your programs workdir to the session directory. Better work with + absolute paths internally . Symlinks given path into session dir and returns the linked path relative to the ourPath. It can handles single files as well as whole directories. @@ -640,11 +640,11 @@ class NSMClient(object): or of our client program. We do not provide any means to unlink or delete files from the session directory. """ - + #Even if the project was not saved yet now it is time to make our directory in the NSM dir. if not os.path.exists(self.ourPath): os.makedirs(self.ourPath) - + filePath = os.path.abspath(filePath) #includes normalisation if not os.path.exists(self.ourPath):raise FileNotFoundError(self.ourPath) if not os.path.isdir(self.ourPath): raise NotADirectoryError(self.ourPath)