Ejemplo n.º 1
0
 def __init__(self, sock):
     asynchat.async_chat.__init__(self, sock=sock)
     self._createTime = TimeUtils.getNowSeconds()
     self._data = None
     self._message = None
     self.handling = False
     self._requestFlags = 0
     self._responseFlags = 0
     self._chunk = ByteChunk(endianess=ByteChunk.BIG_ENDIAN)
     self._resetRouterState()
Ejemplo n.º 2
0
    def __init__(self):
        """
        Creates a new instance of NimbleConnection and opens the communication
        socket to the corresponding NimbleServer instance. NimbleEnvironment
        is used to determine whether the connection should be to a Maya or
        external application NimbleServer instance.
        """

        self._active = False
        self._socket = None
        self._activatedTime = None
        self._chunk = ByteChunk(endianess=ByteChunk.BIG_ENDIAN)
Ejemplo n.º 3
0
    def __init__(self):
        """ Creates a new instance of NimbleConnection and opens the communication socket to the
            corresponding NimbleServer instance. NimbleEnvironment is used to determine whether the
            connection should be to a Maya or external application NimbleServer instance. """

        self._active            = False
        self._socket            = None
        self._activatedTime     = None
        self._chunk             = ByteChunk(endianess=ByteChunk.BIG_ENDIAN)
Ejemplo n.º 4
0
 def __init__(self, sock):
     asynchat.async_chat.__init__(self, sock=sock)
     self._createTime    = TimeUtils.getNowSeconds()
     self._data          = None
     self._message       = None
     self.handling       = False
     self._requestFlags  = 0
     self._responseFlags = 0
     self._chunk         = ByteChunk(endianess=ByteChunk.BIG_ENDIAN)
     self._resetRouterState()
Ejemplo n.º 5
0
class NimbleRouter(asynchat.async_chat):

#===================================================================================================
#                                                                                       C L A S S


    def __init__(self, sock):
        asynchat.async_chat.__init__(self, sock=sock)
        self._createTime    = TimeUtils.getNowSeconds()
        self._data          = None
        self._message       = None
        self.handling       = False
        self._requestFlags  = 0
        self._responseFlags = 0
        self._chunk         = ByteChunk(endianess=ByteChunk.BIG_ENDIAN)
        self._resetRouterState()

#===================================================================================================
#                                                                                   G E T / S E T


    @property
    def keepAlive(self):
        return self._requestFlags & ConnectionFlags.KEEP_ALIVE \
            and (TimeUtils.getNowSeconds() - self._createTime < NimbleEnvironment.CONNECTION_LIFETIME)

#===================================================================================================
#                                                                                     P U B L I C


    def collect_incoming_data(self, data):
        """Buffer the data until the terminator is found"""
        if data:
            self._data.append(data)


    def found_terminator(self):
        if self.handling:
            return

        self.set_terminator(None) # connections sometimes over-send
        self.handling = True
        # noinspection PyTypeChecker
        self._chunk.writeString(''.join(self._data))
        self._chunk.position = 0
        self._requestFlags    = self._chunk.readUint32()
        self._message        = StringUtils.strToUnicode(str(self._chunk.read(-1)))
        self.handle_request()


    def handle_request(self):
        message = self._message
        if not message:
            return

        logLevel = NimbleEnvironment.getServerLogLevel()

        try:
            data = self._parseData(message, logLevel)
        except Exception as err:
            self._sendResponse(
                NimbleResponseData(
                    kind=DataKindEnum.GENERAL,
                    error=DataErrorEnum.PARSE_FAILURE,
                    response=NimbleResponseData.FAILED_RESPONSE,
                    payload={'error':str(err)} ),
                logLevel )
            return

        if data.kind == DataKindEnum.PING:
            reply = NimbleResponseData(
                kind=DataKindEnum.PING,
                response=NimbleResponseData.SUCCESS_RESPONSE)
        else:
            reply = self._routeMessage(data)

        if not reply:
            reply = NimbleResponseData(
                kind=DataKindEnum.GENERAL,
                error=DataErrorEnum.UNRECOGNIZED_REQUEST,
                response=NimbleResponseData.FAILED_RESPONSE )

        self._sendResponse(reply, logLevel)

#===================================================================================================
#                                                                               P R O T E C T E D


    def _resetRouterState(self):
        self.set_terminator(NimbleEnvironment.TERMINATION_IDENTIFIER)
        self._data          = []
        self._message       = None
        self.handling       = False
        self._responseFlags = 0
        self._chunk.clear()


    # noinspection PyMethodMayBeStatic
    def _logData(self, data, logLevel):
        if logLevel == -1:
            return

        # Assume successful for data unless the data object specifies otherwise, which only exists
        # in certain cases
        success = True
        try:
            success = data.success
        except Exception:
            pass

        if logLevel > 1 or not success:
            NimbleEnvironment.logger.write(data.echo(verbose=True, pretty=True))
        elif logLevel > 0:
            NimbleEnvironment.logger.write(data.echo(pretty=True))


    def _sendResponse(self, responseData, logLevel):
        self._logData(responseData, logLevel)

        flags = self._responseFlags
        if self.keepAlive:
            flags = flags | ConnectionFlags.KEEP_ALIVE

        self._chunk.clear()
        self._chunk.writeUint32(flags)
        self._chunk.writeString(responseData.serialize() + NimbleEnvironment.TERMINATION_IDENTIFIER)
        print('CHUNK:', self._chunk)
        reply     = bytes(self._chunk.byteArray)
        keepAlive = self.keepAlive

        # Clear state for future use before sending response
        self._resetRouterState()

        self.push(reply)

        time.sleep(0.0001)
        if not keepAlive:
            self.close_when_done()


    def _routeMessage(self, data):
        if data.kind == DataKindEnum.ECHO:
            return NimbleResponseData(
                kind=DataKindEnum.ECHO,
                response=NimbleResponseData.SUCCESS_RESPONSE,
                payload={'echo':data.payload['echo']} )
        elif data.kind == DataKindEnum.ADD_SYSTEM_PATH:
            path  = data.payload['path']
            doAdd = path not in sys.path
            if doAdd:
                sys.path.append(path)
            return NimbleResponseData(
                kind=DataKindEnum.ADD_SYSTEM_PATH,
                response=NimbleResponseData.SUCCESS_RESPONSE,
                payload={'added':doAdd} )
        else:
            result = self._routeMessageImpl(data)
            if result is not None:
                return result

        return NimbleResponseData(
            kind=DataKindEnum.GENERAL,
            error=DataErrorEnum.UNRECOGNIZED_REQUEST,
            response=NimbleResponseData.FAILED_RESPONSE )


    def _routeMessageImpl(self, data):
        return None


    def _parseData(self, message, logLevel):
        data = NimbleData.fromMessage(message)
        self._logData(data, logLevel)
        return data
Ejemplo n.º 6
0
class NimbleRouter(asynchat.async_chat):

    #===================================================================================================
    #                                                                                       C L A S S

    def __init__(self, sock):
        asynchat.async_chat.__init__(self, sock=sock)
        self._createTime = TimeUtils.getNowSeconds()
        self._data = None
        self._message = None
        self.handling = False
        self._requestFlags = 0
        self._responseFlags = 0
        self._chunk = ByteChunk(endianess=ByteChunk.BIG_ENDIAN)
        self._resetRouterState()

#===================================================================================================
#                                                                                   G E T / S E T

    @property
    def keepAlive(self):
        return self._requestFlags & ConnectionFlags.KEEP_ALIVE \
            and (TimeUtils.getNowSeconds() - self._createTime < NimbleEnvironment.CONNECTION_LIFETIME)

#===================================================================================================
#                                                                                     P U B L I C

    def collect_incoming_data(self, data):
        """Buffer the data until the terminator is found"""
        if data:
            self._data.append(data)

    def found_terminator(self):
        if self.handling:
            return

        self.set_terminator(None)  # connections sometimes over-send
        self.handling = True
        # noinspection PyTypeChecker
        self._chunk.writeString(''.join(self._data))
        self._chunk.position = 0
        self._requestFlags = self._chunk.readUint32()
        self._message = StringUtils.strToUnicode(str(self._chunk.read(-1)))
        self.handle_request()

    def handle_request(self):
        message = self._message
        if not message:
            return

        logLevel = NimbleEnvironment.getServerLogLevel()

        try:
            data = self._parseData(message, logLevel)
        except Exception as err:
            self._sendResponse(
                NimbleResponseData(kind=DataKindEnum.GENERAL,
                                   error=DataErrorEnum.PARSE_FAILURE,
                                   response=NimbleResponseData.FAILED_RESPONSE,
                                   payload={'error': str(err)}), logLevel)
            return

        if data.kind == DataKindEnum.PING:
            reply = NimbleResponseData(
                kind=DataKindEnum.PING,
                response=NimbleResponseData.SUCCESS_RESPONSE)
        else:
            reply = self._routeMessage(data)

        if not reply:
            reply = NimbleResponseData(
                kind=DataKindEnum.GENERAL,
                error=DataErrorEnum.UNRECOGNIZED_REQUEST,
                response=NimbleResponseData.FAILED_RESPONSE)

        self._sendResponse(reply, logLevel)

#===================================================================================================
#                                                                               P R O T E C T E D

    def _resetRouterState(self):
        self.set_terminator(NimbleEnvironment.TERMINATION_IDENTIFIER)
        self._data = []
        self._message = None
        self.handling = False
        self._responseFlags = 0
        self._chunk.clear()

    # noinspection PyMethodMayBeStatic
    def _logData(self, data, logLevel):
        if logLevel == -1:
            return

        # Assume successful for data unless the data object specifies otherwise, which only exists
        # in certain cases
        success = True
        try:
            success = data.success
        except Exception:
            pass

        if logLevel > 1 or not success:
            NimbleEnvironment.logger.write(data.echo(verbose=True,
                                                     pretty=True))
        elif logLevel > 0:
            NimbleEnvironment.logger.write(data.echo(pretty=True))

    def _sendResponse(self, responseData, logLevel):
        self._logData(responseData, logLevel)

        flags = self._responseFlags
        if self.keepAlive:
            flags = flags | ConnectionFlags.KEEP_ALIVE

        self._chunk.clear()
        self._chunk.writeUint32(flags)
        self._chunk.writeString(responseData.serialize() +
                                NimbleEnvironment.TERMINATION_IDENTIFIER)
        print('CHUNK:', self._chunk)
        reply = bytes(self._chunk.byteArray)
        keepAlive = self.keepAlive

        # Clear state for future use before sending response
        self._resetRouterState()

        self.push(reply)

        time.sleep(0.0001)
        if not keepAlive:
            self.close_when_done()

    def _routeMessage(self, data):
        if data.kind == DataKindEnum.ECHO:
            return NimbleResponseData(
                kind=DataKindEnum.ECHO,
                response=NimbleResponseData.SUCCESS_RESPONSE,
                payload={'echo': data.payload['echo']})
        elif data.kind == DataKindEnum.ADD_SYSTEM_PATH:
            path = data.payload['path']
            doAdd = path not in sys.path
            if doAdd:
                sys.path.append(path)
            return NimbleResponseData(
                kind=DataKindEnum.ADD_SYSTEM_PATH,
                response=NimbleResponseData.SUCCESS_RESPONSE,
                payload={'added': doAdd})
        else:
            result = self._routeMessageImpl(data)
            if result is not None:
                return result

        return NimbleResponseData(kind=DataKindEnum.GENERAL,
                                  error=DataErrorEnum.UNRECOGNIZED_REQUEST,
                                  response=NimbleResponseData.FAILED_RESPONSE)

    def _routeMessageImpl(self, data):
        return None

    def _parseData(self, message, logLevel):
        data = NimbleData.fromMessage(message)
        self._logData(data, logLevel)
        return data
Ejemplo n.º 7
0
class NimbleConnection(object):
    """
    Establishes a socket connection with a NimbleServer instance for
    communication
    """

    _CONNECTION_POOL = []

    def __init__(self):
        """
        Creates a new instance of NimbleConnection and opens the communication
        socket to the corresponding NimbleServer instance. NimbleEnvironment
        is used to determine whether the connection should be to a Maya or
        external application NimbleServer instance.
        """

        self._active = False
        self._socket = None
        self._activatedTime = None
        self._chunk = ByteChunk(endianess=ByteChunk.BIG_ENDIAN)

    @property
    def active(self):
        """
        Specifies whether or not the NimbleConnection instance is active. When
        active the instance can communicate with its remote NimbleServer
        counterpart. NimbleConnection instances are active by default and only
        become inactive if they are closed after which point they will have to
        be reopened in order to allow further communication.
        """

        return self._active

    def echo(self, message):
        return self._send(
            NimbleData(kind=DataKindEnum.ECHO, payload={'echo': message}))

    def ping(self, message=None):
        """Doc..."""
        return self._send(
            NimbleData(kind=DataKindEnum.PING, payload={'msg': message}))

    def addToMayaPythonPath(self, path):
        return self._send(
            NimbleData(kind=DataKindEnum.ADD_SYSTEM_PATH,
                       payload={'path': path}))

    def runMelScript(self, script):
        return self._send(
            NimbleData(kind=DataKindEnum.MEL_SCRIPT,
                       payload={'script': script}))

    def runPythonScript(self, script, **kwargs):
        return self._send(
            NimbleData(kind=DataKindEnum.PYTHON_SCRIPT,
                       payload={
                           'script': script,
                           'kwargs': kwargs
                       }))

    def runPythonScriptFile(self, path, **kwargs):
        return self._send(
            NimbleData(kind=DataKindEnum.PYTHON_SCRIPT_FILE,
                       payload={
                           'path': path,
                           'kwargs': kwargs
                       }))

    def runPythonImport(self,
                        modulePackage,
                        methodName=None,
                        className=None,
                        runInMaya=None,
                        **kwargs):
        """ Executes the specified import through Nimble in the specified run mode.

            modulePackage:  (String) An absolute (dot-syntax) formatted import to the module you
                            wish to be executed. This module will be imported by Maya and must be
                            on its sys.path.

            [methodName]:   (String) An optional function name to be executed within the module. If
                            a class name is specified this method will be called on an instance of
                            the specified class. If no class name is specified the method will be
                            called directly on the module.

            [className]:    (String) An optional class name of a class to import within the
                            specified module. The class will be imported from the module and
                            instantiated.

            [runInMaya]:    If True the import will be executed within Maya. If False the import
                            will be executed outside of Maya on the remote end of the Nimble
                            connection. The default value of None will use the current global
                            setting, which can be set by the nimble.enablePythonTestMode()
                            top-level function and defaults to runInMaya = True, i.e. test mode
                            is disabled.

            Returns a NimbleResponseData object with the results of the script execution. """

        payload = {
            'module': modulePackage,
            'method': methodName,
            'class': className,
            'kwargs': kwargs
        }

        if NimbleEnvironment.inMaya():
            return MayaRouter.runPythonImport(payload)

        if (not NimbleEnvironment.TEST_REMOTE_MODE
            ) if runInMaya is None else runInMaya:
            return self._send(
                NimbleData(kind=DataKindEnum.PYTHON_IMPORT, payload=payload))
        else:
            return MayaRouter.runPythonImport(payload)

    def runPythonClass(self,
                       targetClass,
                       methodName=None,
                       runInMaya=None,
                       **kwargs):
        """ Convenience method that wraps runPythonImport where the targetClass is a Class object
            that is parsed into a modulePackage and className.

            targetClass:    (Class) A class object that should be executed in Nimble.

            For additional information see NimbleConnection.runPythonImport """
        return self.runPythonImport(modulePackage=targetClass.__module__,
                                    methodName=methodName,
                                    className=targetClass.__name__,
                                    runInMaya=runInMaya,
                                    **kwargs)

    def runPythonModule(self,
                        module,
                        methodName=None,
                        className=None,
                        runInMaya=None,
                        **kwargs):
        """ Convenience method that wraps runPythonImport where the module is an imported module
            object instead of the string to the modulePackage.

            module:    (Module) A module object that is parsed into a package name.

            For additional information see NimbleConnection.runPythonImport """
        return self.runPythonImport(modulePackage=module.__name__,
                                    methodName=methodName,
                                    className=className,
                                    runInMaya=runInMaya,
                                    **kwargs)

    def mel(self, command):
        result = self.runMelScript(command)
        if not result or not result.success:
            raise MayaCommandException(
                'Failed execution of Maya MEL command: ' + str(command),
                response=result)
        return result.payload['result']

    def maya(self, command, *args, **kwargs):
        result = self.runMayaCommand(command, *args, **kwargs)
        if not result or not result.success:
            raise MayaCommandException('Failed execution of Maya command: ' +
                                       str(command),
                                       response=result)
        return result.payload['result']

    def runMayaCommand(self, command, *args, **kwargs):
        return self._send(
            NimbleData(kind=DataKindEnum.MAYA_COMMAND,
                       payload={
                           'command': str(command),
                           'kwargs': kwargs,
                           'args': args
                       }))

    def runMayaCommandBatch(self, commandList):
        return self._send(
            NimbleData(kind=DataKindEnum.MAYA_COMMAND_BATCH,
                       payload={'commands': commandList}))

    def mayaBatch(self, commandList):
        result = self.runMayaCommandBatch(commandList)
        if not result or not result.success:
            raise MayaCommandException(
                'Failed execution during Maya command batch execution',
                response=result)
        return result.payload['result']

    def command(self, command, *args, **kwargs):
        result = self.runCommand(command, *args, **kwargs)
        if not result or not result.success:
            raise MayaCommandException('Failed execution of command: ' +
                                       str(command),
                                       response=result)
        return result.payload['result']

    def runCommand(self, command, *args, **kwargs):
        return self._send(
            NimbleData(kind=DataKindEnum.COMMAND,
                       payload={
                           'command':
                           command.toDict() if isinstance(
                               command, ImportedCommand) else str(command),
                           'kwargs':
                           kwargs,
                           'args':
                           args
                       }))

    def close(self):
        if not self._active:
            return False

        self._active = False
        self._socket.close()

        if self in NimbleConnection._CONNECTION_POOL:
            NimbleConnection._CONNECTION_POOL.remove(self)

        return True

    def open(self):
        if self._active:
            nowTime = TimeUtils.getNowSeconds()
            if nowTime - self._activatedTime > NimbleEnvironment.CONNECTION_LIFETIME:
                self.close()
            else:
                return False

        self._activatedTime = TimeUtils.getNowSeconds()
        try:
            target = (NimbleEnvironment.getConnectionHost(),
                      NimbleEnvironment.getConnectionPort())
            self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

            # Sets socket option to prevent connection being refused by TCP reconnecting
            # to the same socket after a recent closure.
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self._socket.setblocking(1)
            self._socket.connect(target)
        except Exception as err:
            NimbleEnvironment.logError(
                '[ERROR | NIMBLE COMMUNICATION] Failed to open Nimble connection',
                err)
            return False

        if self not in NimbleConnection._CONNECTION_POOL:
            NimbleConnection._CONNECTION_POOL.append(self)

        self._active = True
        return True

    @classmethod
    def getConnection(cls, forceCreate=False):
        if forceCreate or not NimbleConnection._CONNECTION_POOL:
            return NimbleConnection()

        return NimbleConnection._CONNECTION_POOL[-1]

    @classmethod
    def closeConnectionPool(cls):
        while NimbleConnection._CONNECTION_POOL:
            NimbleConnection._CONNECTION_POOL.pop().close()

#===================================================================================================
#                                                                               P R O T E C T E D

    def _send(self, nimbleData):
        """Doc..."""

        if NimbleEnvironment.inMaya():
            return MayaRouter.processRequest(nimbleData)

        result = self._sendRemote(nimbleData)
        time.sleep(0.0001)
        return result

    def _sendRemote(self, nimbleData):
        responseFlags = 0
        message = u''
        retry = NimbleEnvironment.REMOTE_RETRY_COUNT

        while retry > 0:
            try:
                self.open()
            except Exception as err:
                failure = [
                    '[ERROR | NIMBLE COMMUNICATION] Unable to open connection',
                    err
                ]
                retry -= 1
                if retry == 0:
                    if not nimble.quietFailure:
                        NimbleEnvironment.logError(failure[0], failure[1])
                    return None
                continue

            try:
                serialData = nimbleData.serialize()
            except Exception as err:
                failure = [
                    '[ERROR | NIMBLE COMMUNICATION] Unable to serialize data for transmission',
                    err
                ]
                if not nimble.quietFailure:
                    NimbleEnvironment.logError(failure[0], failure[1])
                return None

            try:
                self._chunk.clear()
                self._chunk.writeUint32(NimbleEnvironment.CONNECTION_FLAGS)
                self._chunk.writeString(
                    serialData + NimbleEnvironment.TERMINATION_IDENTIFIER)
                self._socket.sendall(self._chunk.byteArray)
            except Exception as err:
                failure = [
                    '[ERROR | NIMBLE COMMUNICATION] Unable to send data', err
                ]
                self.close()
                retry -= 1
                if retry == 0:
                    if not nimble.quietFailure:
                        NimbleEnvironment.logError(failure[0], failure[1])
                    return None
                continue

            try:
                self._chunk.clear()
                b = SocketUtils.receiveInChunks(
                    self._socket,
                    chunkSize=NimbleEnvironment.SOCKET_RESPONSE_CHUNK_SIZE)
                self._chunk.writeString(b)
                self._chunk.position = 0
                responseFlags = self._chunk.readUint32()
                message = StringUtils.strToUnicode(self._chunk.read(-1))

                # Break while loop on successful reading of the result
                if message is not None:
                    break

            except Exception as err:
                if not nimble.quietFailure:
                    NimbleEnvironment.logError(
                        '[ERROR | NIMBLE COMMUNICATION] Unable to read response',
                        err)
                self.close()
                return None

        try:
            if not (responseFlags & ConnectionFlags.KEEP_ALIVE):
                self.close()
        except Exception as err:
            if not nimble.quietFailure:
                NimbleEnvironment.logError(
                    '[ERROR | NIMBLE COMMUNICATION] Unable to close connection',
                    err)

        try:
            return NimbleData.fromMessage(message)
        except Exception as err:
            if not nimble.quietFailure:
                NimbleEnvironment.logError(
                    '[ERROR | NIMBLE COMMUNICATION] Response data parsing failure',
                    err)
            return None

#===================================================================================================
#                                                                               I N T R I N S I C

    def __del__(self):
        try:
            self._socket.close()
        except Exception:
            pass

    def __repr__(self):
        return self.__str__()

    def __unicode__(self):
        return StringUtils.toUnicode(self.__str__())

    def __str__(self):
        return '<%s>' % self.__class__.__name__
Ejemplo n.º 8
0
class NimbleConnection(object):
    """
    Establishes a socket connection with a NimbleServer instance for
    communication
    """

    _CONNECTION_POOL = []

    def __init__(self):
        """
        Creates a new instance of NimbleConnection and opens the communication
        socket to the corresponding NimbleServer instance. NimbleEnvironment
        is used to determine whether the connection should be to a Maya or
        external application NimbleServer instance.
        """

        self._active = False
        self._socket = None
        self._activatedTime = None
        self._chunk = ByteChunk(endianess=ByteChunk.BIG_ENDIAN)

    @property
    def active(self):
        """
        Specifies whether or not the NimbleConnection instance is active. When
        active the instance can communicate with its remote NimbleServer
        counterpart. NimbleConnection instances are active by default and only
        become inactive if they are closed after which point they will have to
        be reopened in order to allow further communication.
        """

        return self._active

    def echo(self, message):
        return self._send(NimbleData(kind=DataKindEnum.ECHO, payload={'echo':message}))

    def ping(self, message =None):
        """Doc..."""
        return self._send(NimbleData(kind=DataKindEnum.PING, payload={'msg':message}))


    def addToMayaPythonPath(self, path):
        return self._send(NimbleData(
            kind=DataKindEnum.ADD_SYSTEM_PATH,
            payload={'path':path} ))


    def runMelScript(self, script):
        return self._send(NimbleData(
            kind=DataKindEnum.MEL_SCRIPT,
            payload={'script':script} ))


    def runPythonScript(self, script, **kwargs):
        return self._send(NimbleData(
            kind=DataKindEnum.PYTHON_SCRIPT,
            payload={'script':script, 'kwargs':kwargs} ))


    def runPythonScriptFile(self, path, **kwargs):
        return self._send(NimbleData(
            kind=DataKindEnum.PYTHON_SCRIPT_FILE,
            payload={'path':path, 'kwargs':kwargs} ))


    def runPythonImport(self, modulePackage, methodName =None, className=None, runInMaya =None, **kwargs):
        """ Executes the specified import through Nimble in the specified run mode.

            modulePackage:  (String) An absolute (dot-syntax) formatted import to the module you
                            wish to be executed. This module will be imported by Maya and must be
                            on its sys.path.

            [methodName]:   (String) An optional function name to be executed within the module. If
                            a class name is specified this method will be called on an instance of
                            the specified class. If no class name is specified the method will be
                            called directly on the module.

            [className]:    (String) An optional class name of a class to import within the
                            specified module. The class will be imported from the module and
                            instantiated.

            [runInMaya]:    If True the import will be executed within Maya. If False the import
                            will be executed outside of Maya on the remote end of the Nimble
                            connection. The default value of None will use the current global
                            setting, which can be set by the nimble.enablePythonTestMode()
                            top-level function and defaults to runInMaya = True, i.e. test mode
                            is disabled.

            Returns a NimbleResponseData object with the results of the script execution. """

        payload = {
            'module':modulePackage,
            'method':methodName,
            'class':className,
            'kwargs':kwargs}

        if NimbleEnvironment.inMaya():
            return MayaRouter.runPythonImport(payload)

        if (not NimbleEnvironment.TEST_REMOTE_MODE) if runInMaya is None else runInMaya:
            return self._send(NimbleData(kind=DataKindEnum.PYTHON_IMPORT, payload=payload))
        else:
            return MayaRouter.runPythonImport(payload)


    def runPythonClass(self, targetClass, methodName =None, runInMaya =None, **kwargs):
        """ Convenience method that wraps runPythonImport where the targetClass is a Class object
            that is parsed into a modulePackage and className.

            targetClass:    (Class) A class object that should be executed in Nimble.

            For additional information see NimbleConnection.runPythonImport """
        return self.runPythonImport(
            modulePackage=targetClass.__module__,
            methodName=methodName,
            className=targetClass.__name__,
            runInMaya=runInMaya, **kwargs)


    def runPythonModule(self, module, methodName =None, className =None, runInMaya =None, **kwargs):
        """ Convenience method that wraps runPythonImport where the module is an imported module
            object instead of the string to the modulePackage.

            module:    (Module) A module object that is parsed into a package name.

            For additional information see NimbleConnection.runPythonImport """
        return self.runPythonImport(
            modulePackage=module.__name__,
            methodName=methodName,
            className=className,
            runInMaya=runInMaya, **kwargs)


    def mel(self, command):
        result = self.runMelScript(command)
        if not result or not result.success:
            raise MayaCommandException(
                'Failed execution of Maya MEL command: ' + str(command),
                response=result)
        return result.payload['result']


    def maya(self, command, *args, **kwargs):
        result = self.runMayaCommand(command, *args, **kwargs)
        if not result or not result.success:
            raise MayaCommandException(
                'Failed execution of Maya command: ' + str(command),
                response=result)
        return result.payload['result']


    def runMayaCommand(self, command, *args, **kwargs):
        return self._send(NimbleData(
            kind=DataKindEnum.MAYA_COMMAND,
            payload={
                'command':str(command),
                'kwargs':kwargs,
                'args':args} ))


    def runMayaCommandBatch(self, commandList):
        return self._send(NimbleData(
            kind=DataKindEnum.MAYA_COMMAND_BATCH,
            payload={'commands':commandList} ))


    def mayaBatch(self, commandList):
        result = self.runMayaCommandBatch(commandList)
        if not result or not result.success:
            raise MayaCommandException(
                'Failed execution during Maya command batch execution',
                response=result)
        return result.payload['result']


    def command(self, command, *args, **kwargs):
        result = self.runCommand(command, *args, **kwargs)
        if not result or not result.success:
            raise MayaCommandException(
                'Failed execution of command: ' + str(command),
                response=result)
        return result.payload['result']


    def runCommand(self, command, *args, **kwargs):
        return self._send(NimbleData(
            kind=DataKindEnum.COMMAND,
            payload={
                'command':command.toDict() if isinstance(command, ImportedCommand) else str(command),
                'kwargs':kwargs,
                'args':args } ))


    def close(self):
        if not self._active:
            return False

        self._active = False
        self._socket.close()

        if self in NimbleConnection._CONNECTION_POOL:
            NimbleConnection._CONNECTION_POOL.remove(self)

        return True


    def open(self):
        if self._active:
            nowTime = TimeUtils.getNowSeconds()
            if nowTime - self._activatedTime > NimbleEnvironment.CONNECTION_LIFETIME:
                self.close()
            else:
                return False

        self._activatedTime = TimeUtils.getNowSeconds()
        try:
            target = (
                NimbleEnvironment.getConnectionHost(),
                NimbleEnvironment.getConnectionPort()
            )
            self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

            # Sets socket option to prevent connection being refused by TCP reconnecting
            # to the same socket after a recent closure.
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self._socket.setblocking(1)
            self._socket.connect(target)
        except Exception as err:
            NimbleEnvironment.logError(
                '[ERROR | NIMBLE COMMUNICATION] Failed to open Nimble connection', err)
            return False

        if self not in NimbleConnection._CONNECTION_POOL:
            NimbleConnection._CONNECTION_POOL.append(self)

        self._active = True
        return True


    @classmethod
    def getConnection(cls, forceCreate =False):
        if forceCreate or not NimbleConnection._CONNECTION_POOL:
            return NimbleConnection()

        return NimbleConnection._CONNECTION_POOL[-1]


    @classmethod
    def closeConnectionPool(cls):
        while NimbleConnection._CONNECTION_POOL:
            NimbleConnection._CONNECTION_POOL.pop().close()

#===================================================================================================
#                                                                               P R O T E C T E D


    def _send(self, nimbleData):
        """Doc..."""

        if NimbleEnvironment.inMaya():
            return MayaRouter.processRequest(nimbleData)

        result = self._sendRemote(nimbleData)
        time.sleep(0.0001)
        return result


    def _sendRemote(self, nimbleData):
        responseFlags = 0
        message       = u''
        retry         = NimbleEnvironment.REMOTE_RETRY_COUNT

        while retry > 0:
            try:
                self.open()
            except Exception as err:
                failure = [
                    '[ERROR | NIMBLE COMMUNICATION] Unable to open connection',
                    err ]
                retry -= 1
                if retry == 0:
                    if not nimble.quietFailure:
                        NimbleEnvironment.logError(failure[0], failure[1])
                    return None
                continue

            try:
                serialData = nimbleData.serialize()
            except Exception as err:
                failure = [
                    '[ERROR | NIMBLE COMMUNICATION] Unable to serialize data for transmission',
                    err ]
                if not nimble.quietFailure:
                    NimbleEnvironment.logError(failure[0], failure[1])
                return None

            try:
                self._chunk.clear()
                self._chunk.writeUint32(NimbleEnvironment.CONNECTION_FLAGS)
                self._chunk.writeString(serialData + NimbleEnvironment.TERMINATION_IDENTIFIER)
                self._socket.sendall(self._chunk.byteArray)
            except Exception as err:
                failure = [
                    '[ERROR | NIMBLE COMMUNICATION] Unable to send data',
                    err ]
                self.close()
                retry -= 1
                if retry == 0:
                    if not nimble.quietFailure:
                        NimbleEnvironment.logError(failure[0], failure[1])
                    return None
                continue

            try:
                self._chunk.clear()
                b = SocketUtils.receiveInChunks(
                    self._socket,
                    chunkSize=NimbleEnvironment.SOCKET_RESPONSE_CHUNK_SIZE)
                self._chunk.writeString(b)
                self._chunk.position = 0
                responseFlags  = self._chunk.readUint32()
                message        = StringUtils.strToUnicode(self._chunk.read(-1))

                # Break while loop on successful reading of the result
                if message is not None:
                    break

            except Exception as err:
                if not nimble.quietFailure:
                    NimbleEnvironment.logError(
                        '[ERROR | NIMBLE COMMUNICATION] Unable to read response', err)
                self.close()
                return None

        try:
            if not (responseFlags & ConnectionFlags.KEEP_ALIVE):
                self.close()
        except Exception as err:
            if not nimble.quietFailure:
                NimbleEnvironment.logError(
                    '[ERROR | NIMBLE COMMUNICATION] Unable to close connection', err)

        try:
            return NimbleData.fromMessage(message)
        except Exception as err:
            if not nimble.quietFailure:
                NimbleEnvironment.logError(
                    '[ERROR | NIMBLE COMMUNICATION] Response data parsing failure', err)
            return None

#===================================================================================================
#                                                                               I N T R I N S I C


    def __del__(self):
        try:
            self._socket.close()
        except Exception:
            pass


    def __repr__(self):
        return self.__str__()


    def __unicode__(self):
        return StringUtils.toUnicode(self.__str__())


    def __str__(self):
        return '<%s>' % self.__class__.__name__
Ejemplo n.º 9
0
from pyaid.string.ByteChunk import ByteChunk
from array import array

import random

a = array('d')
for i in range(100):
    a.append(10000.0 * (random.random() - 0.5))

bc = ByteChunk()
bc.writeArrayChunk(a)
bc.position = 0
out = bc.readArrayChunk()
print('[TEST] Array Chunk I/O: %s' % ('PASSED' if a == out else 'FAILED'))