├── lib └── rpyc │ ├── utils │ ├── __init__.py │ ├── logger.py │ ├── authenticators.py │ ├── helpers.py │ ├── twisted_integration.py │ ├── lib.py │ ├── factory.py │ ├── server.py │ ├── classic.py │ └── registry.py │ ├── servers │ ├── __init__.py │ ├── registry_server.py │ ├── vdbconf.py │ └── classic_server.py │ ├── core │ ├── __init__.py │ ├── consts.py │ ├── channel.py │ ├── async.py │ ├── vinegar.py │ ├── service.py │ ├── netref.py │ ├── brine.py │ ├── stream.py │ └── protocol.py │ ├── license.py │ └── __init__.py ├── HComNuke ├── HCom_History │ └── hinfo.inf ├── HCom_Icons │ ├── bgeo.png │ ├── close.png │ ├── gnm.wav │ ├── hcom.png │ ├── help.png │ ├── nuke.png │ ├── obj.png │ ├── alembic.png │ ├── connect.png │ ├── folder.png │ ├── houdini.png │ ├── picture.png │ ├── clearmsg.png │ ├── disconnect.png │ ├── settings.png │ ├── unreadmsg.png │ ├── digitalasset.png │ ├── folder_hist.png │ ├── maya_hengine.png │ ├── maya_no_hengine.png │ ├── sendoutputnode.png │ └── digitalasset_settings.png ├── HCom_Received_Files │ └── info.inf ├── _globals.py ├── menu │ └── menu.py ├── HCom.ini ├── install.txt ├── HComNukeUtils.py └── HComNukeClient.py ├── HComHoudini ├── HCom_History │ └── hinfo.inf ├── HCom_Icons │ ├── bgeo.png │ ├── gnm.wav │ ├── hcom.png │ ├── help.png │ ├── nuke.png │ ├── obj.png │ ├── alembic.png │ ├── close.png │ ├── connect.png │ ├── folder.png │ ├── houdini.png │ ├── picture.png │ ├── clearmsg.png │ ├── settings.png │ ├── unreadmsg.png │ ├── digitalasset.png │ ├── disconnect.png │ ├── folder_hist.png │ ├── maya_hengine.png │ ├── maya_no_hengine.png │ └── digitalasset_settings.png ├── HCom_Received_Files │ └── info.inf ├── HCom.ini ├── install.txt ├── PyPanel │ └── HCom.pypanel ├── HComHoudiniClient.py └── HComHoudiniUtils.py ├── hCom_help.pdf ├── .gitignore ├── HComMaya ├── HCom_Icons │ ├── bgeo.png │ ├── close.png │ ├── gnm.wav │ ├── hcom.png │ ├── help.png │ ├── nuke.png │ ├── obj.png │ ├── alembic.png │ ├── connect.png │ ├── folder.png │ ├── houdini.png │ ├── picture.png │ ├── clearmsg.png │ ├── disconnect.png │ ├── settings.png │ ├── unreadmsg.png │ ├── digitalasset.png │ ├── folder_hist.png │ ├── maya_hengine.png │ ├── maya_no_hengine.png │ └── digitalasset_settings.png ├── shelf │ ├── hcom_shelf.png │ └── shelf_HCom.mel ├── HCom_Received_Files │ └── info.inf ├── _globals.py ├── install.txt ├── HCom.ini ├── HComMayaUtils.py └── HComMayaClient.py ├── README.md └── HComServer.py /lib/rpyc/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /HComNuke/HCom_History/hinfo.inf: -------------------------------------------------------------------------------- 1 | all history files will be saved here. -------------------------------------------------------------------------------- /HComHoudini/HCom_History/hinfo.inf: -------------------------------------------------------------------------------- 1 | all history files will be saved here. -------------------------------------------------------------------------------- /hCom_help.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/hCom_help.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | HComClient.pyc 3 | *.pyc 4 | *.project 5 | *.pydevproject 6 | -------------------------------------------------------------------------------- /lib/rpyc/servers/__init__.py: -------------------------------------------------------------------------------- 1 | __import__("pkg_resources").declare_namespace(__name__) -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/bgeo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/bgeo.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/close.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/gnm.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/gnm.wav -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/hcom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/hcom.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/help.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/nuke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/nuke.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/obj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/obj.png -------------------------------------------------------------------------------- /HComMaya/shelf/hcom_shelf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/shelf/hcom_shelf.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/bgeo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/bgeo.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/close.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/gnm.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/gnm.wav -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/hcom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/hcom.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/help.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/nuke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/nuke.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/obj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/obj.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/bgeo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/bgeo.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/gnm.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/gnm.wav -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/hcom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/hcom.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/help.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/nuke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/nuke.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/obj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/obj.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/alembic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/alembic.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/connect.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/folder.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/houdini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/houdini.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/picture.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/alembic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/alembic.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/connect.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/folder.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/houdini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/houdini.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/picture.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/alembic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/alembic.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/close.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/connect.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/folder.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/houdini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/houdini.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/picture.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/clearmsg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/clearmsg.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/disconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/disconnect.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/settings.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/unreadmsg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/unreadmsg.png -------------------------------------------------------------------------------- /HComMaya/HCom_Received_Files/info.inf: -------------------------------------------------------------------------------- 1 | all files: otls, mesh ( obj, bgeo ) and pictures will be saved by default here. -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/clearmsg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/clearmsg.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/disconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/disconnect.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/settings.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/unreadmsg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/unreadmsg.png -------------------------------------------------------------------------------- /HComNuke/HCom_Received_Files/info.inf: -------------------------------------------------------------------------------- 1 | all files: otls, mesh ( obj, bgeo ) and pictures will be saved by default here. -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/clearmsg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/clearmsg.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/settings.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/unreadmsg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/unreadmsg.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Received_Files/info.inf: -------------------------------------------------------------------------------- 1 | all files: otls, mesh ( obj, bgeo ) and pictures will be saved by default here. -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/digitalasset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/digitalasset.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/folder_hist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/folder_hist.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/maya_hengine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/maya_hengine.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/digitalasset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/digitalasset.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/folder_hist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/folder_hist.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/maya_hengine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/maya_hengine.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/digitalasset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/digitalasset.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/disconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/disconnect.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/folder_hist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/folder_hist.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/maya_hengine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/maya_hengine.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/maya_no_hengine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/maya_no_hengine.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/maya_no_hengine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/maya_no_hengine.png -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/sendoutputnode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/sendoutputnode.png -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/maya_no_hengine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/maya_no_hengine.png -------------------------------------------------------------------------------- /HComMaya/HCom_Icons/digitalasset_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComMaya/HCom_Icons/digitalasset_settings.png -------------------------------------------------------------------------------- /HComMaya/_globals.py: -------------------------------------------------------------------------------- 1 | class MayaGlobals(): 2 | 3 | MAIN_UI = None 4 | HCOM_TABS = {} 5 | CUR_ID = None 6 | HCOMCLIENT = None -------------------------------------------------------------------------------- /HComNuke/HCom_Icons/digitalasset_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComNuke/HCom_Icons/digitalasset_settings.png -------------------------------------------------------------------------------- /HComNuke/_globals.py: -------------------------------------------------------------------------------- 1 | class NukeGlobals(): 2 | 3 | MAIN_UI = None 4 | HCOM_TABS = {} 5 | CUR_ID = None 6 | HCOMCLIENT = None -------------------------------------------------------------------------------- /HComNuke/menu/menu.py: -------------------------------------------------------------------------------- 1 | import HComNukeUi 2 | toolbar = nuke.menu("Nodes") 3 | toolbar.addCommand("HCom", "HComNukeUi.main()", icon="hcom.png") -------------------------------------------------------------------------------- /HComHoudini/HCom_Icons/digitalasset_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgtoolbox/HCom/HEAD/HComHoudini/HCom_Icons/digitalasset_settings.png -------------------------------------------------------------------------------- /HComMaya/install.txt: -------------------------------------------------------------------------------- 1 | copy the HComMaya in your $PYTHONPATH 2 | add $HOME/houdini14.0/scripts/python/HComHoudini in your $PYTHONPATH ( using userSetup.py) 3 | -------------------------------------------------------------------------------- /HComMaya/HCom.ini: -------------------------------------------------------------------------------- 1 | #HCom info file -MAYA CLIENT- 2 | 3 | PLAY_SOUND=False 4 | MY_RECEIVED_FILES=DEFAULT 5 | PORT=5000 6 | SAVE_HISTORY=True 7 | SERVER=127.0.0.1 8 | -------------------------------------------------------------------------------- /HComHoudini/HCom.ini: -------------------------------------------------------------------------------- 1 | #HCom info file 2 | SWITCH_TO_MANUAL_UPDATE=True 3 | SAVE_HISTORY=True 4 | SERVER=127.0.0.1 5 | PLAY_SOUND=False 6 | MY_RECEIVED_FILES=DEFAULT 7 | PORT=5000 8 | -------------------------------------------------------------------------------- /HComNuke/HCom.ini: -------------------------------------------------------------------------------- 1 | #HCom info file -MAYA CLIENT- 2 | 3 | SAVE_HISTORY=True 4 | OUTPUT_IMAGE_FORMAT=png 5 | SERVER=127.0.0.1 6 | PLAY_SOUND=False 7 | MY_RECEIVED_FILES=DEFAULT 8 | PORT=5000 9 | -------------------------------------------------------------------------------- /HComHoudini/install.txt: -------------------------------------------------------------------------------- 1 | copy the HComHoudini to $HOME/houdini14.0/scripts/python or in your $PYTHONPATH 2 | add $HOME/houdini14.0/scripts/python/HComHoudini in your $PYTHONPATH (using 123.py for houdinifx, hescape.py for other versions) 3 | -------------------------------------------------------------------------------- /HComNuke/install.txt: -------------------------------------------------------------------------------- 1 | copy the HComNuke on your hardrive as well as the lib folder 2 | 3 | in the file C:\Users\%USER_NAME%\.nuke\init.py 4 | 5 | add these lines: 6 | nuke.pluginAddPath(PATH_TO_HCOM:HComNuke) 7 | nuke.pluginAddPath(PATH_TO_HCOM:\lib) 8 | nuke.pluginAddPath(PATH_TO_HCOM:\HComNuke\HCom_Icons) -------------------------------------------------------------------------------- /lib/rpyc/core/__init__.py: -------------------------------------------------------------------------------- 1 | from rpyc.core.stream import SocketStream, PipeStream 2 | from rpyc.core.channel import Channel 3 | from rpyc.core.protocol import Connection 4 | from rpyc.core.netref import BaseNetref 5 | from rpyc.core.async import AsyncResult, AsyncResultTimeout 6 | from rpyc.core.service import Service, VoidService, SlaveService 7 | from rpyc.core.vinegar import GenericException, install_rpyc_excepthook 8 | 9 | 10 | install_rpyc_excepthook() 11 | 12 | -------------------------------------------------------------------------------- /HComHoudini/PyPanel/HCom.pypanel: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /lib/rpyc/core/consts.py: -------------------------------------------------------------------------------- 1 | """ 2 | constants used by the protocol 3 | """ 4 | 5 | # messages 6 | MSG_REQUEST = 1 7 | MSG_REPLY = 2 8 | MSG_EXCEPTION = 3 9 | 10 | # boxing 11 | LABEL_VALUE = 1 12 | LABEL_TUPLE = 2 13 | LABEL_LOCAL_REF = 3 14 | LABEL_REMOTE_REF = 4 15 | 16 | # action handlers 17 | HANDLE_PING = 1 18 | HANDLE_CLOSE = 2 19 | HANDLE_GETROOT = 3 20 | HANDLE_GETATTR = 4 21 | HANDLE_DELATTR = 5 22 | HANDLE_SETATTR = 6 23 | HANDLE_CALL = 7 24 | HANDLE_CALLATTR = 8 25 | HANDLE_REPR = 9 26 | HANDLE_STR = 10 27 | HANDLE_CMP = 11 28 | HANDLE_HASH = 12 29 | HANDLE_DIR = 13 30 | HANDLE_PICKLE = 14 31 | HANDLE_DEL = 15 32 | HANDLE_INSPECT = 16 33 | HANDLE_BUFFITER = 17 34 | 35 | # optimized exceptions 36 | EXC_STOP_ITERATION = 1 37 | 38 | # DEBUG 39 | #for k in globals().keys(): 40 | # globals()[k] = k 41 | 42 | 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HCom 2 | HCom is a client / server communication system which allows the user to send data between software like Houdini and Maya through local network. It allows to send meshes, bitmaps, alembic cache data and houdini digital assets ( more to come soon ... ). 3 | 4 | It is based on Python 2.7 and the rpyc library, the UI is written with PySide ( shipped with Houdini and Maya ). 5 | 6 | How it works: 7 | 8 | - A hCom python server runs on a machine on the network 9 | - On each user machine you can connect an hCom client ( from Maya or/and Houdini ) to the server. 10 | - you can send data to any user connected to hCom, for houdini digital assets, only maya with Houdini Engine installed can receive them. 11 | 12 | Demo on vimeo: 13 | 14 | Help : http://guillaumejobst.blogspot.fr/p/hcom.html 15 | 16 | https://vimeo.com/127091487 ( Houdini to Houdini ) 17 | 18 | https://vimeo.com/127655675 ( Houdini to Maya ) 19 | 20 | https://vimeo.com/128033450 ( Alembic support ) 21 | 22 | 23 | -------------------------------------------------------------------------------- /lib/rpyc/license.py: -------------------------------------------------------------------------------- 1 | """\ 2 | Copyright (c) 2005-2009 3 | Tomer Filiba (tomerfiliba@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | """ 23 | -------------------------------------------------------------------------------- /HComMaya/shelf/shelf_HCom.mel: -------------------------------------------------------------------------------- 1 | global proc shelf_HCom () { 2 | global string $gBuffStr; 3 | global string $gBuffStr0; 4 | global string $gBuffStr1; 5 | 6 | 7 | shelfButton 8 | -enableCommandRepeat 1 9 | -enable 1 10 | -width 35 11 | -height 35 12 | -manage 1 13 | -visible 1 14 | -preventOverride 0 15 | -annotation "HCom Maya Client" 16 | -enableBackground 0 17 | -align "center" 18 | -label "HCom Maya Client" 19 | -labelOffset 0 20 | -font "plainLabelFont" 21 | -imageOverlayLabel "HCom" 22 | -overlayLabelColor 0.8 0.8 0.8 23 | -overlayLabelBackColor 0 0 0 0.2 24 | -image "hcom_shelf.png" 25 | -image1 "hcom_shelf.png" 26 | -style "iconOnly" 27 | -marginWidth 1 28 | -marginHeight 1 29 | -command "from maya import OpenMayaUI as omui \nfrom shiboken import wrapInstance \nfrom PySide import QtGui\nfrom HComMayaClient import HComMayaUi\nreload(HComMayaUi)\n\nmayaMainWindowPtr = omui.MQtUtil.mainWindow() \nmayaMainWindow= wrapInstance(long(mayaMainWindowPtr), QtGui.QMainWindow)\n\nui = HComMayaUi.main()\nui.show(dockable=True)\n" 30 | -sourceType "python" 31 | -commandRepeatable 1 32 | -flat 1 33 | ; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /lib/rpyc/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | . 3 | ##### ##### #### 4 | ## ## ## ## ## #### 5 | ## ## ## ## ## # 6 | ##### ##### ## ## ## ## 7 | ## ## ## ## ## ## # 8 | ## ## ## ### ## ### 9 | ## ## ## ## ##### 10 | -------------------- ## ------------------------------------------ 11 | ## 12 | 13 | Remote Python Call (RPyC) v 3.0.7 (22 Sep 2009) 14 | Licensed under the MIT license (see license.py) 15 | 16 | A transparent, symmetric and light-weight RPC and distributed computing 17 | library for python. 18 | 19 | Usage: 20 | import rpyc 21 | c = rpyc.connect_by_service("SERVICENAME") 22 | print c.root.some_function(1, 2, 3) 23 | 24 | Classic-style usage: 25 | import rpyc 26 | # `hostname` is assumed to be running a slave-service server 27 | c = rpyc.classic.connect("hostname") 28 | print c.execute("x = 5") 29 | print c.eval("x + 2") 30 | print c.modules.os.listdir(".") 31 | print c.modules["xml.dom.minidom"].parseString("") 32 | f = c.builtin.open("foobar.txt", "rb") 33 | print f.read(100) 34 | """ 35 | from rpyc.core import (SocketStream, PipeStream, Channel, Connection, Service, 36 | BaseNetref, AsyncResult, GenericException, AsyncResultTimeout, VoidService, 37 | SlaveService) 38 | from rpyc.utils.factory import (connect_stream, connect_channel, connect_pipes, 39 | connect_stdpipes, connect, tls_connect, discover, connect_by_service, 40 | connect_subproc, connect_thread) 41 | from rpyc.utils.helpers import async, timed, buffiter, BgServingThread 42 | from rpyc.utils import classic 43 | from rpyc.license import __doc__ as license 44 | 45 | 46 | __author__ = "Tomer Filiba (tomerfiliba@gmail.com)" 47 | version = __version__ = (3, 0, 7) 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /lib/rpyc/core/channel.py: -------------------------------------------------------------------------------- 1 | """ 2 | channel - an abstraction layer over streams that works with data frames 3 | (rather than bytes) and supports compression. 4 | Note: in order to avoid problems with all sorts of line-buffered transports, 5 | we deliberately add \\n at the end of each frame. 6 | 7 | note: unlike previous versions, this is no longer thread safe 8 | """ 9 | import zlib 10 | from rpyc.utils.lib import Struct 11 | 12 | # * 64 bit length field? 13 | # * separate \n into a FlushingChannel subclass? 14 | # * add thread safety as a subclass? 15 | 16 | class Channel(object): 17 | COMPRESSION_THRESHOLD = 3000 18 | COMPRESSION_LEVEL = 1 19 | FRAME_HEADER = Struct("!LB") 20 | FLUSHER = "\n" # cause any line-buffered layers below us to flush 21 | __slots__ = ["stream", "compress"] 22 | 23 | def __init__(self, stream, compress = True): 24 | self.stream = stream 25 | self.compress = compress 26 | def close(self): 27 | self.stream.close() 28 | @property 29 | def closed(self): 30 | return self.stream.closed 31 | def fileno(self): 32 | return self.stream.fileno() 33 | def poll(self, timeout): 34 | return self.stream.poll(timeout) 35 | def recv(self): 36 | header = self.stream.read(self.FRAME_HEADER.size) 37 | length, compressed = self.FRAME_HEADER.unpack(header) 38 | data = self.stream.read(length + len(self.FLUSHER))[:-len(self.FLUSHER)] 39 | if compressed: 40 | data = zlib.decompress(data) 41 | return data 42 | def send(self, data): 43 | if self.compress and len(data) > self.COMPRESSION_THRESHOLD: 44 | compressed = 1 45 | data = zlib.compress(data, self.COMPRESSION_LEVEL) 46 | else: 47 | compressed = 0 48 | header = self.FRAME_HEADER.pack(len(data), compressed) 49 | buf = header + data + self.FLUSHER 50 | self.stream.write(buf) 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /lib/rpyc/servers/registry_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | The registry server listens to broadcasts on UDP port 18812, answering to 4 | discovery queries by clients and registering keepalives from all running 5 | servers. In order for clients to use discovery, a registry service must 6 | be running somewhere on their local network. 7 | """ 8 | from optparse import OptionParser 9 | from rpyc.utils.registry import REGISTRY_PORT, DEFAULT_PRUNING_TIMEOUT 10 | from rpyc.utils.registry import UDPRegistryServer, TCPRegistryServer 11 | 12 | 13 | parser = OptionParser() 14 | parser.add_option("-m", "--mode", action="store", dest="mode", metavar="MODE", 15 | default="udp", type="string", help="mode can be 'udp' or 'tcp'") 16 | parser.add_option("-p", "--port", action="store", dest="port", type="int", 17 | metavar="PORT", default=REGISTRY_PORT, help="specify a different UDP/TCP listener port") 18 | parser.add_option("-f", "--file", action="store", dest="logfile", type="str", 19 | metavar="FILE", default=None, help="specify the log file to use; the default is stderr") 20 | parser.add_option("-q", "--quiet", action="store_true", dest="quiet", 21 | default=False, help="quiet mode (no logging)") 22 | parser.add_option("-t", "--timeout", action="store", dest="pruning_timeout", 23 | type="int", default=DEFAULT_PRUNING_TIMEOUT, help="sets a custom pruning timeout") 24 | 25 | options, args = parser.parse_args() 26 | if args: 27 | raise ValueError("does not take positional arguments: %r" % (args,)) 28 | 29 | if options.port < 1 or options.port > 65535: 30 | raise ValueError("invalid TCP/UDP port %r" % (options.port,)) 31 | 32 | if options.mode.lower() == "udp": 33 | server = UDPRegistryServer(port = options.port, 34 | pruning_timeout = options.pruning_timeout) 35 | elif options.mode.lower() == "tcp": 36 | server = TCPRegistryServer(port = options.port, 37 | pruning_timeout = options.pruning_timeout) 38 | else: 39 | raise ValueError("invalid mode %r" % (options.mode,)) 40 | 41 | server.logger.quiet = options.quiet 42 | if options.logfile: 43 | server.logger.console = open(options.logfile) 44 | 45 | server.start() 46 | 47 | 48 | -------------------------------------------------------------------------------- /lib/rpyc/utils/logger.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import thread 4 | import time 5 | import traceback 6 | 7 | 8 | class Logger(object): 9 | def __init__(self, name, console = sys.stderr, file = None, show_name = True, 10 | show_pid = False, show_tid = False, show_date = False, show_time = True, 11 | show_label = True, quiet = False): 12 | self.name = name 13 | self.console = console 14 | self.file = file 15 | self.show_name = show_name 16 | self.show_pid = show_pid 17 | self.show_tid = show_tid 18 | self.show_date = show_date 19 | self.show_time = show_time 20 | self.show_label = show_label 21 | self.quiet = quiet 22 | self.filter = set() 23 | 24 | def log(self, label, msg): 25 | if label in self.filter: 26 | return 27 | header = [] 28 | if self.show_name: 29 | header.append("%-10s" % (self.name,)) 30 | if self.show_label: 31 | header.append("%-10s" % (label,)) 32 | if self.show_date: 33 | header.append(time.strftime("%Y-%m-%d")) 34 | if self.show_time: 35 | header.append(time.strftime("%H:%M:%S")) 36 | if self.show_pid: 37 | header.append("pid=%d" % (os.getpid(),)) 38 | if self.show_tid: 39 | header.append("tid=%d" % (thread.get_ident(),)) 40 | if header: 41 | header = "[" + " ".join(header) + "] " 42 | sep = "\n...." + " " * (len(header) - 4) 43 | text = header + sep.join(msg.splitlines()) + "\n" 44 | if self.console: 45 | self.console.write(text) 46 | if self.file: 47 | self.file.write(text) 48 | 49 | def debug(self, msg, *args, **kwargs): 50 | if self.quiet: return 51 | if args: msg %= args 52 | self.log("DEBUG", msg) 53 | def info(self, msg, *args, **kwargs): 54 | if self.quiet: return 55 | if args: msg %= args 56 | self.log("INFO", msg) 57 | def warn(self, msg, *args, **kwargs): 58 | if self.quiet: return 59 | if args: msg %= args 60 | self.log("WARNING", msg) 61 | def error(self, msg, *args, **kwargs): 62 | if args: msg %= args 63 | self.log("ERROR", msg) 64 | def traceback(self, excinfo = None): 65 | if not excinfo: 66 | excinfo = sys.exc_info() 67 | self.log("TRACEBACK", "".join(traceback.format_exception(*excinfo))) 68 | 69 | 70 | logger = Logger("root", show_name = False) 71 | 72 | 73 | if __name__ == "__main__": 74 | try: 75 | logger.info("hello") 76 | 1/0 77 | except: 78 | logger.traceback() 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /lib/rpyc/servers/vdbconf.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """%prog [options] 3 | 4 | A simple configurator for tlslite's verifier databases (VDB), which allows you to: 5 | 1) list the usernames in a given vdb file 6 | 2) add / modify a username in the given vdb file 7 | 3) delete an existing username from the vdb file 8 | 9 | Examples: 10 | vdbconf -l : list all users in `filename` 11 | vdbconf -a : add/replace `username` in `filename` 12 | vdbconf -d : delete `username` from `filename` 13 | 14 | SECURITY NOTE: 15 | Make sure the vdb file has the correct write permissions! 16 | """ 17 | import sys 18 | import getpass 19 | from optparse import OptionParser 20 | from rpyc.utils.authenticators import VdbAuthenticator 21 | 22 | 23 | parser = OptionParser(usage = __doc__) 24 | parser.add_option("-l", "--list", action="store_true", dest="listonly", 25 | default=False, help="List usernames and exit") 26 | parser.add_option("-a", "--add", action="store", dest="add", metavar="USERNAME", 27 | default=None, help="Set the given username (required for -d or adding)") 28 | parser.add_option("-d", "--delete", action="store", dest="delete", metavar="USERNAME", 29 | default=None, help="Deletes the given username") 30 | 31 | def get_options(): 32 | options, args = parser.parse_args() 33 | if len(args) != 1: 34 | parser.error("Missing filename!") 35 | if options.add and options.delete: 36 | parser.error("Options -a and -d are mutually exclusive!") 37 | 38 | options.filename = args[0] 39 | return options 40 | 41 | def list_users(vdb, options): 42 | users = sorted(vdb.list_users()) 43 | if not users: 44 | print "No users defined in %s:" % (options.filename,) 45 | else: 46 | print "Existing users in %s:" % (options.filename,) 47 | for user in users: 48 | print " %s" % (user,) 49 | 50 | def del_user(vdb, options): 51 | username = options.delete 52 | if username not in vdb.list_users(): 53 | sys.exit("User %s doesn't exist in %s" % (username, options.filename)) 54 | 55 | print "Removing user %s from %s" % (username, options.filename) 56 | vdb.del_user(username) 57 | vdb.sync() 58 | 59 | def set_user(vdb, options): 60 | username = options.add 61 | if username in vdb.list_users(): 62 | print "Adding user %s to %s" % (username, options.filename) 63 | else: 64 | print "Changing user %s in %s" % (username, options.filename) 65 | 66 | password1 = getpass.getpass("Password: ") 67 | password2 = getpass.getpass("Retype password: ") 68 | 69 | if password1 != password2: 70 | sys.exit("Passwords do not match!") 71 | if not password1: 72 | sys.exit("Password cannot be empty!") 73 | 74 | vdb.set_user(username, password1) 75 | vdb.sync() 76 | 77 | def main(): 78 | options = get_options() 79 | vdb = VdbAuthenticator.from_file(options.filename) 80 | 81 | if options.listonly: 82 | list_users(vdb, options) 83 | elif options.delete: 84 | del_user(vdb, options) 85 | print "OK" 86 | elif options.add: 87 | set_user(vdb, options) 88 | print "OK" 89 | else: 90 | parser.error("No action specified") 91 | 92 | 93 | if __name__ == "__main__": 94 | main() 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /lib/rpyc/utils/authenticators.py: -------------------------------------------------------------------------------- 1 | """ 2 | authenticators: the server instance accepts an authenticator object, 3 | which is basically any callable (i.e., a function) that takes the newly 4 | connected socket and "authenticates" it. 5 | 6 | the authenticator should return a socket-like object with its associated 7 | credentials (a tuple), or raise AuthenticationError if it fails. 8 | 9 | a very trivial authenticator might be 10 | 11 | def magic_word_authenticator(sock): 12 | if sock.recv(5) != "Ma6ik": 13 | raise AuthenticationError("wrong magic word") 14 | return sock, None 15 | 16 | s = ThreadedServer(...., authenticator = magic_word_authenticator) 17 | 18 | your authenticator can return any socket-like object. for instance, it may 19 | authenticate the client and return a TLS/SSL-wrapped socket object that 20 | encrypts the transport. 21 | 22 | the credentials returned alongside with the new socket can be any object. 23 | it will be stored in the rpyc connection configruation under the key 24 | "credentials", and may be used later by the service logic. if no credentials 25 | are applicable, just return None as in the example above. 26 | 27 | rpyc includes integration with tlslite, a TLS/SSL library: 28 | the VdbAuthenticator class authenticates clients based on username-password 29 | pairs. 30 | """ 31 | import os 32 | import anydbm 33 | from rpyc.utils.lib import safe_import 34 | tlsapi = safe_import("tlslite.api") 35 | 36 | 37 | class AuthenticationError(Exception): 38 | pass 39 | 40 | 41 | def _load_vdb_with_mode(vdb, mode): 42 | """taken from tlslite/BaseDB.py -- patched for file mode""" 43 | # {{ 44 | db = anydbm.open(vdb.filename, mode) 45 | try: 46 | if db["--Reserved--type"] != vdb.type: 47 | raise ValueError("Not a %s database" % (vdb.type,)) 48 | except KeyError: 49 | raise ValueError("Not a recognized database") 50 | vdb.db = db 51 | # }} 52 | 53 | class VdbAuthenticator(object): 54 | __slots__ = ["vdb"] 55 | BITS = 2048 56 | 57 | def __init__(self, vdb): 58 | self.vdb = vdb 59 | 60 | @classmethod 61 | def from_dict(cls, users): 62 | inst = cls(tlsapi.VerifierDB()) 63 | for username, password in users.iteritems(): 64 | inst.set_user(username, password) 65 | return inst 66 | 67 | @classmethod 68 | def from_file(cls, filename, mode = "w"): 69 | vdb = tlsapi.VerifierDB(filename) 70 | if os.path.exists(filename): 71 | _load_vdb_with_mode(vdb, mode) 72 | else: 73 | if mode not in "ncw": 74 | raise ValueError("%s does not exist but mode does not allow " 75 | "writing (%r)" % (filename, mode)) 76 | vdb.create() 77 | return cls(vdb) 78 | 79 | def sync(self): 80 | self.vdb.db.sync() 81 | 82 | def set_user(self, username, password): 83 | self.vdb[username] = self.vdb.makeVerifier(username, password, self.BITS) 84 | 85 | def del_user(self, username): 86 | del self.vdb[username] 87 | 88 | def list_users(self): 89 | return self.vdb.keys() 90 | 91 | def __call__(self, sock): 92 | sock2 = tlsapi.TLSConnection(sock) 93 | sock2.fileno = lambda fd=sock.fileno(): fd # tlslite omitted fileno 94 | try: 95 | sock2.handshakeServer(verifierDB = self.vdb) 96 | except Exception, ex: 97 | raise AuthenticationError(str(ex)) 98 | return sock2, sock2.allegedSrpUsername 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /lib/rpyc/utils/helpers.py: -------------------------------------------------------------------------------- 1 | """ 2 | helpers and wrappers for common rpyc tasks 3 | """ 4 | import threading 5 | from rpyc.utils.lib import WeakValueDict, callable 6 | from rpyc.core.consts import HANDLE_BUFFITER, HANDLE_CALL 7 | from rpyc.core.netref import BaseNetref, syncreq, asyncreq 8 | 9 | 10 | def buffiter(obj, chunk = 10, max_chunk = 1000, factor = 2): 11 | """buffering iterator - reads the remote iterator in chunks starting with 12 | `chunk` up to `max_chunk`, multiplying by `factor` as an exponential 13 | backoff""" 14 | if factor < 1: 15 | raise ValueError("factor must be >= 1, got %r" % (factor,)) 16 | it = iter(obj) 17 | count = chunk 18 | while True: 19 | items = syncreq(it, HANDLE_BUFFITER, count) 20 | count = min(count * factor, max_chunk) 21 | if not items: 22 | break 23 | for elem in items: 24 | yield elem 25 | 26 | class _Async(object): 27 | """creates an async proxy wrapper over an existing proxy. async proxies 28 | are cached. invoking an async proxy will return an AsyncResult instead of 29 | blocking""" 30 | 31 | __slots__ = ("proxy", "__weakref__") 32 | def __init__(self, proxy): 33 | self.proxy = proxy 34 | def __call__(self, *args, **kwargs): 35 | return asyncreq(self.proxy, HANDLE_CALL, args, tuple(kwargs.items())) 36 | def __repr__(self): 37 | return "async(%r)" % (self.proxy,) 38 | 39 | _async_proxies_cache = WeakValueDict() 40 | def async(proxy): 41 | pid = id(proxy) 42 | if pid in _async_proxies_cache: 43 | return _async_proxies_cache[pid] 44 | if not hasattr(proxy, "____conn__") or not hasattr(proxy, "____oid__"): 45 | raise TypeError("'proxy' must be a Netref: %r", (proxy,)) 46 | if not callable(proxy): 47 | raise TypeError("'proxy' must be callable: %r" % (proxy,)) 48 | caller = _Async(proxy) 49 | _async_proxies_cache[id(caller)] = _async_proxies_cache[pid] = caller 50 | return caller 51 | 52 | async.__doc__ = _Async.__doc__ 53 | 54 | class timed(object): 55 | """creates a timed asynchronous proxy. invoking the timed proxy will 56 | run in the background and will raise an AsyncResultTimeout exception 57 | if the computation does not terminate within the given timeout""" 58 | 59 | __slots__ = ("__weakref__", "proxy", "timeout") 60 | def __init__(self, proxy, timeout): 61 | self.proxy = async(proxy) 62 | self.timeout = timeout 63 | def __call__(self, *args, **kwargs): 64 | res = self.proxy(*args, **kwargs) 65 | res.set_expiry(self.timeout) 66 | return res 67 | def __repr__(self): 68 | return "timed(%r, %r)" % (self.proxy.proxy, self.timeout) 69 | 70 | class BgServingThread(object): 71 | """runs an RPyC server in the background to serve all requests and replies 72 | that arrive on the given RPyC connection. the thread is created along with 73 | the object; you can use the stop() method to stop the server thread""" 74 | INTERVAL = 0.1 75 | def __init__(self, conn): 76 | self._conn = conn 77 | self._thread = threading.Thread(target = self._bg_server) 78 | self._thread.setDaemon(True) 79 | self._active = True 80 | self._thread.start() 81 | def __del__(self): 82 | if self._active: 83 | self.stop() 84 | def _bg_server(self): 85 | try: 86 | while self._active: 87 | self._conn.serve(self.INTERVAL) 88 | except Exception: 89 | if self._active: 90 | raise 91 | def stop(self): 92 | """stop the server thread. once stopped, it cannot be resumed. you will 93 | have to create a new BgServingThread object later.""" 94 | self._active = False 95 | self._thread.join() 96 | self._conn = None 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /lib/rpyc/core/async.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | class AsyncResultTimeout(Exception): 5 | pass 6 | 7 | class AsyncResult(object): 8 | """AsyncResult is an object that represent a computation that occurs in 9 | the background and will eventually have a result. Use the .value property 10 | to access the result (which will block if the result has not yet arrived) 11 | """ 12 | __slots__ = ["_conn", "_is_ready", "_is_exc", "_callbacks", "_obj", "_ttl"] 13 | def __init__(self, conn): 14 | self._conn = conn 15 | self._is_ready = False 16 | self._is_exc = None 17 | self._obj = None 18 | self._callbacks = [] 19 | self._ttl = None 20 | def __repr__(self): 21 | if self._is_ready: 22 | state = "ready" 23 | elif self._is_exc: 24 | state = "error" 25 | elif self.expired: 26 | state = "expired" 27 | else: 28 | state = "pending" 29 | return "" % (state, id(self)) 30 | def __call__(self, is_exc, obj): 31 | if self.expired: 32 | return 33 | self._is_ready = True 34 | self._is_exc = is_exc 35 | self._obj = obj 36 | for cb in self._callbacks: 37 | cb(self) 38 | del self._callbacks[:] 39 | 40 | def wait(self): 41 | """wait for the result to arrive. if the AsyncResult object has an 42 | expiry set, and the result does not arrive within that timeout, 43 | an AsyncResultTimeout exception is raised""" 44 | if self._is_ready: 45 | return 46 | if self._ttl is None: 47 | while not self._is_ready: 48 | self._conn.serve() 49 | else: 50 | while True: 51 | timeout = self._ttl - time.time() 52 | self._conn.poll(timeout = max(timeout, 0)) 53 | if self._is_ready: 54 | break 55 | if timeout <= 0: 56 | raise AsyncResultTimeout("result expired") 57 | def add_callback(self, func): 58 | """adds a callback to be invoked when the result arrives. the 59 | callback function takes a single argument, which is the current 60 | AsyncResult (self)""" 61 | if self._is_ready: 62 | func(self) 63 | else: 64 | self._callbacks.append(func) 65 | def set_expiry(self, timeout): 66 | """set the expiry time (in seconds, relative to now) or None for 67 | unlimited time""" 68 | if timeout is None: 69 | self._ttl = None 70 | else: 71 | self._ttl = time.time() + timeout 72 | 73 | @property 74 | def ready(self): 75 | """a predicate of whether the result has arrived""" 76 | if self.expired: 77 | return False 78 | if not self._is_ready: 79 | self._conn.poll_all() 80 | return self._is_ready 81 | @property 82 | def error(self): 83 | """a predicate of whether the returned result is an exception""" 84 | if self.ready: 85 | return self._is_exc 86 | return False 87 | @property 88 | def expired(self): 89 | """a predicate of whether the async result has expired""" 90 | if self._is_ready or self._ttl is None: 91 | return False 92 | else: 93 | return time.time() > self._ttl 94 | 95 | @property 96 | def value(self): 97 | """returns the result of the operation. if the result has not yet 98 | arrived, accessing this property will wait for it. if the result does 99 | not arrive before the expiry time elapses, AsyncResultTimeout is 100 | raised. if the returned result is an exception, it will be raised here. 101 | otherwise, the result is returned directly.""" 102 | self.wait() 103 | if self._is_exc: 104 | raise self._obj 105 | else: 106 | return self._obj 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /lib/rpyc/utils/twisted_integration.py: -------------------------------------------------------------------------------- 1 | """ 2 | rpyc-twisted integration, based on code originally contributed by noam raphael 3 | 4 | Note: rpyc normally works in blocking (synchornous) fashion, for instance, 5 | getting an attribute of an object (foo.bar.baz). the twistedish solution 6 | would be using @inlineCallbacks and `yield (yield foo.bar).baz`... which is 7 | rather less pythonistic. 8 | 9 | function calls, however, can be made asynchronous easily with the async() 10 | wrapper, so these will play nicely with twisted. 11 | 12 | all in all, the integration with twisted is limited and rather fake. 13 | working with rpyc might block the reactor -- a bad thing -- but a necessary 14 | evil if we wish to combine the two methodologies. 15 | 16 | if you find a better solution, please tell me. 17 | """ 18 | import socket 19 | import rpyc 20 | from rpyc.core import SocketStream, Channel 21 | import twisted.internet.protocol as tip 22 | from twisted.internet import reactor 23 | from twisted.python import log 24 | 25 | 26 | class TwistedSocketStream(SocketStream): 27 | def __init__(self, transport): 28 | SocketStream.__init__(self, transport.socket) 29 | self.transport = transport 30 | self._buffer = "" 31 | def push(self, data): 32 | self._buffer += data 33 | 34 | def poll(self, timeout): 35 | if self._buffer: 36 | return True 37 | self.sock.setblocking(True) 38 | try: 39 | return SocketStream.poll(self, timeout) 40 | finally: 41 | try: 42 | self.sock.setblocking(False) 43 | except socket.error: 44 | pass 45 | 46 | def read(self, count): 47 | if count <= len(self._buffer): 48 | data = self._buffer[:count] 49 | self._buffer = self._buffer[count:] 50 | else: 51 | self.sock.setblocking(True) 52 | try: 53 | data2 = SocketStream.read(self, count - len(self._buffer)) 54 | finally: 55 | try: 56 | self.sock.setblocking(False) 57 | except socket.error: 58 | pass 59 | data = self._buffer + data2 60 | self._buffer = "" 61 | #log.msg("%s.read(%r)" % (self, data)) 62 | return data 63 | 64 | def write(self, data): 65 | #log.msg("%s.write(%r)" % (self, data)) 66 | self.sock.setblocking(True) 67 | try: 68 | SocketStream.write(self, data) 69 | finally: 70 | self.sock.setblocking(False) 71 | 72 | 73 | class TwistedRpycProtocol(tip.Protocol): 74 | def __init__(self): 75 | self.stream = None 76 | self.conn = None 77 | def connectionMade(self): 78 | self.stream = TwistedSocketStream(self.transport) 79 | self.conn = rpyc.Connection(self.factory.service, Channel(self.stream), 80 | config = self.factory.config, _lazy = True) 81 | self.conn._init_service() 82 | if self.factory.logging: 83 | log.msg("%s: connected %s" % (self, self.conn)) 84 | if self.factory.on_connected is not None: 85 | reactor.callLater(0, self.factory.on_connected, self.conn) 86 | def connectionLost(self, reason=None): 87 | if self.conn: 88 | if self.factory.logging: 89 | log.msg("%s: closing connection %s" % (self, self.conn)) 90 | c = self.conn 91 | self.conn = None 92 | c.close(_catchall = True) 93 | def dataReceived(self, data): 94 | self.stream.push(data) 95 | self.conn.poll_all() 96 | 97 | 98 | class RpycClientFactory(tip.ClientFactory): 99 | protocol = TwistedRpycProtocol 100 | def __init__(self, service, on_connected = None, config = {}, logging = False): 101 | self.service = service 102 | self.config = config 103 | self.on_connected = on_connected 104 | self.logging = logging 105 | 106 | 107 | RpycServerFactory = RpycClientFactory 108 | 109 | 110 | -------------------------------------------------------------------------------- /lib/rpyc/utils/lib.py: -------------------------------------------------------------------------------- 1 | """ 2 | various library utilities (also for compatibility with python2.4) 3 | """ 4 | try: 5 | from struct import Struct 6 | except ImportError: 7 | import struct 8 | class Struct(object): 9 | __slots__ = ["format", "size"] 10 | def __init__(self, format): 11 | self.format = format 12 | self.size = struct.calcsize(format) 13 | def pack(self, *args): 14 | return struct.pack(self.format, *args) 15 | def unpack(self, data): 16 | return struct.unpack(self.format, data) 17 | 18 | try: 19 | all = all 20 | except NameError: 21 | def all(seq): 22 | for elem in seq: 23 | if not elem: 24 | return False 25 | return True 26 | 27 | try: 28 | callable = callable 29 | except NameError: 30 | def callable(obj): 31 | return hasattr(obj, "__call__") 32 | 33 | from threading import Lock, RLock, Event 34 | 35 | import weakref 36 | #from weakref import WeakValueDictionary as WeakValueDict 37 | 38 | class WeakValueDict(object): 39 | """a light-weight version of weakref.WeakValueDictionary""" 40 | __slots__ = ("_dict",) 41 | def __init__(self): 42 | self._dict = {} 43 | def __repr__(self): 44 | return repr(self._dict) 45 | def __iter__(self): 46 | return self.iterkeys() 47 | def __len__(self): 48 | return len(self._dict) 49 | def __contains__(self, key): 50 | try: 51 | self[key] 52 | except KeyError: 53 | return False 54 | else: 55 | return True 56 | def get(self, key, default = None): 57 | try: 58 | return self[key] 59 | except KeyError: 60 | return default 61 | def __getitem__(self, key): 62 | obj = self._dict[key]() 63 | if obj is None: 64 | raise KeyError(key) 65 | return obj 66 | def __setitem__(self, key, value): 67 | def remover(wr, _dict = self._dict, key = key): 68 | _dict.pop(key, None) 69 | self._dict[key] = weakref.ref(value, remover) 70 | def __delitem__(self, key): 71 | del self._dict[key] 72 | def iterkeys(self): 73 | return self._dict.iterkeys() 74 | def keys(self): 75 | return self._dict.keys() 76 | def itervalues(self): 77 | for k in self: 78 | yield self[k] 79 | def values(self): 80 | return list(self.itervalues()) 81 | def iteritems(self): 82 | for k in self: 83 | yield k, self[k] 84 | def items(self): 85 | return list(self.iteritems()) 86 | def clear(self): 87 | self._dict.clear() 88 | 89 | class RefCountingColl(object): 90 | """a set-like object that implements refcounting on its contained objects""" 91 | __slots__ = ("_lock", "_dict") 92 | def __init__(self): 93 | self._lock = Lock() 94 | self._dict = {} 95 | def __repr__(self): 96 | return repr(self._dict) 97 | def add(self, obj): 98 | self._lock.acquire() 99 | try: 100 | key = id(obj) 101 | slot = self._dict.get(key, None) 102 | if slot is None: 103 | slot = [obj, 0] 104 | else: 105 | slot[1] += 1 106 | self._dict[key] = slot 107 | finally: 108 | self._lock.release() 109 | def clear(self): 110 | self._lock.acquire() 111 | try: 112 | self._dict.clear() 113 | finally: 114 | self._lock.release() 115 | def decref(self, key): 116 | self._lock.acquire() 117 | try: 118 | slot = self._dict[key] 119 | if slot[1] <= 1: 120 | del self._dict[key] 121 | else: 122 | slot[1] -= 1 123 | self._dict[key] = slot 124 | finally: 125 | self._lock.release() 126 | def __getitem__(self, key): 127 | self._lock.acquire() 128 | try: 129 | return self._dict[key][0] 130 | finally: 131 | self._lock.release() 132 | 133 | 134 | class MissingModule(object): 135 | __slots__ = ["__name"] 136 | def __init__(self, name): 137 | self.__name = name 138 | def __getattr__(self, name): 139 | raise ImportError("module %r not found" % (self.__name,)) 140 | 141 | def safe_import(name): 142 | try: 143 | mod = __import__(name, None, None, "*") 144 | except ImportError: 145 | mod = MissingModule(name) 146 | return mod 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /lib/rpyc/core/vinegar.py: -------------------------------------------------------------------------------- 1 | """ 2 | vinegar ('when things go sour'): safe serialization of exceptions. 3 | 4 | note that by changing the configuration parameters, this module can be 5 | made non-secure 6 | """ 7 | import sys 8 | import exceptions 9 | import traceback 10 | from types import InstanceType, ClassType 11 | from rpyc.core import brine 12 | from rpyc.core import consts 13 | 14 | 15 | class GenericException(Exception): 16 | pass 17 | 18 | _generic_exceptions_cache = {} 19 | 20 | STOP_ITERATION_MAGIC = 0 21 | 22 | def dump(typ, val, tb, include_local_traceback): 23 | if type(typ) is str: 24 | return typ 25 | if typ is StopIteration: 26 | return consts.EXC_STOP_ITERATION # optimization 27 | 28 | if include_local_traceback: 29 | tbtext = "".join(traceback.format_exception(typ, val, tb)) 30 | else: 31 | tbtext = "" 32 | attrs = [] 33 | args = [] 34 | for name in dir(val): 35 | if name == "args": 36 | for a in val.args: 37 | if brine.dumpable(a): 38 | args.append(a) 39 | else: 40 | args.append(repr(a)) 41 | elif not name.startswith("_") or name == "_remote_tb": 42 | attrval = getattr(val, name) 43 | if not brine.dumpable(attrval): 44 | attrval = repr(attrval) 45 | attrs.append((name, attrval)) 46 | return (typ.__module__, typ.__name__), tuple(args), tuple(attrs), tbtext 47 | 48 | try: 49 | BaseException 50 | except NameError: 51 | # python 2.4 compatible 52 | BaseException = Exception 53 | 54 | def load(val, import_custom_exceptions, instantiate_custom_exceptions, instantiate_oldstyle_exceptions): 55 | if val == consts.EXC_STOP_ITERATION: 56 | return StopIteration # optimization 57 | if type(val) is str: 58 | return val # deprecated string exceptions 59 | 60 | (modname, clsname), args, attrs, tbtext = val 61 | if import_custom_exceptions and modname not in sys.modules: 62 | try: 63 | mod = __import__(modname, None, None, "*") 64 | except ImportError: 65 | pass 66 | if instantiate_custom_exceptions: 67 | cls = getattr(sys.modules[modname], clsname, None) 68 | elif modname == "exceptions": 69 | cls = getattr(exceptions, clsname, None) 70 | else: 71 | cls = None 72 | 73 | if not isinstance(cls, (type, ClassType)): 74 | cls = None 75 | elif issubclass(cls, ClassType) and not instantiate_oldstyle_exceptions: 76 | cls = None 77 | elif not issubclass(cls, BaseException): 78 | cls = None 79 | 80 | if cls is None: 81 | fullname = "%s.%s" % (modname, clsname) 82 | if fullname not in _generic_exceptions_cache: 83 | fakemodule = {"__module__" : "%s.%s" % (__name__, modname)} 84 | if isinstance(GenericException, ClassType): 85 | _generic_exceptions_cache[fullname] = ClassType(fullname, (GenericException,), fakemodule) 86 | else: 87 | _generic_exceptions_cache[fullname] = type(fullname, (GenericException,), fakemodule) 88 | cls = _generic_exceptions_cache[fullname] 89 | 90 | # support old-style exception classes 91 | if isinstance(cls, ClassType): 92 | exc = InstanceType(cls) 93 | else: 94 | exc = cls.__new__(cls) 95 | 96 | exc.args = args 97 | for name, attrval in attrs: 98 | setattr(exc, name, attrval) 99 | if hasattr(exc, "_remote_tb"): 100 | exc._remote_tb += (tbtext,) 101 | else: 102 | exc._remote_tb = (tbtext,) 103 | return exc 104 | 105 | 106 | #=============================================================================== 107 | # customized except hook 108 | #=============================================================================== 109 | if hasattr(sys, "excepthook"): 110 | _orig_excepthook = sys.excepthook 111 | else: 112 | # ironpython forgot to implement excepthook, scheisse 113 | _orig_excepthook = None 114 | 115 | def rpyc_excepthook(typ, val, tb): 116 | if hasattr(val, "_remote_tb"): 117 | sys.stderr.write("======= Remote traceback =======\n") 118 | tbtext = "\n--------------------------------\n\n".join(val._remote_tb) 119 | sys.stderr.write(tbtext) 120 | sys.stderr.write("\n======= Local exception ========\n") 121 | _orig_excepthook(typ, val, tb) 122 | 123 | def install_rpyc_excepthook(): 124 | if _orig_excepthook is not None: 125 | sys.excepthook = rpyc_excepthook 126 | 127 | def uninstall_rpyc_excepthook(): 128 | if _orig_excepthook is not None: 129 | sys.excepthook = _orig_excepthook 130 | 131 | 132 | -------------------------------------------------------------------------------- /lib/rpyc/core/service.py: -------------------------------------------------------------------------------- 1 | class Service(object): 2 | """The service base-class. Subclass this class to implement custom RPyC 3 | services: 4 | * The name of the class implementing the 'Foo' service should match the 5 | pattern 'FooService' (suffixed by the word 'Service'): 6 | class FooService(Service): 7 | pass 8 | FooService.get_service_name() # 'FOO' 9 | FooService.get_service_aliases() # ['FOO'] 10 | * To supply a different name or aliases, use the ALIASES class attribute: 11 | class Foobar(Service): 12 | ALIASES = ["foo", "bar", "lalaland"] 13 | Foobar.get_service_name() # 'FOO' 14 | Foobar.get_service_aliases() # ['FOO', 'BAR', 'LALALAND'] 15 | * Override on_connect to perform custom initialization 16 | * Override on_disconnect to perform custom finilization 17 | * To add exposed methods or attributes, simply define them as normally, 18 | but make sure their name is prefixed by 'exposed_', e.g.: 19 | class FooService(Service): 20 | def exposed_foo(self, x, y): 21 | return x + y 22 | * All other names (not prefixed by 'exposed_') are local (not accessible 23 | by the other party) 24 | """ 25 | __slots__ = ["_conn"] 26 | ALIASES = () 27 | 28 | def __init__(self, conn): 29 | self._conn = conn 30 | def on_connect(self): 31 | """called when the connection is established""" 32 | pass 33 | def on_disconnect(self): 34 | """called when the connection had already terminated for cleanup 35 | (must not perform any IO on the connection)""" 36 | pass 37 | 38 | def _rpyc_getattr(self, name): 39 | if name.startswith("exposed_"): 40 | name = name 41 | else: 42 | name = "exposed_" + name 43 | return getattr(self, name) 44 | def _rpyc_delattr(self, name): 45 | raise AttributeError("access denied") 46 | def _rpyc_setattr(self, name, value): 47 | raise AttributeError("access denied") 48 | 49 | @classmethod 50 | def get_service_aliases(cls): 51 | if cls.ALIASES: 52 | return tuple(str(n).upper() for n in cls.ALIASES) 53 | name = cls.__name__.upper() 54 | if name.endswith("SERVICE"): 55 | name = name[:-7] 56 | return (name,) 57 | @classmethod 58 | def get_service_name(cls): 59 | return cls.get_service_aliases()[0] 60 | 61 | exposed_get_service_aliases = get_service_aliases 62 | exposed_get_service_name = get_service_name 63 | 64 | 65 | class VoidService(Service): 66 | """void service - an empty service""" 67 | __slots__ = () 68 | 69 | 70 | class ModuleNamespace(object): 71 | """used by the SlaveService to implement the magic 'module namespace'""" 72 | __slots__ = ["__getmodule", "__cache", "__weakref__"] 73 | def __init__(self, getmodule): 74 | self.__getmodule = getmodule 75 | self.__cache = {} 76 | def __getitem__(self, name): 77 | if type(name) is tuple: 78 | name = ".".join(name) 79 | if name not in self.__cache: 80 | self.__cache[name] = self.__getmodule(name) 81 | return self.__cache[name] 82 | def __getattr__(self, name): 83 | return self[name] 84 | 85 | class SlaveService(Service): 86 | """The SlaveService allows the other side to perform arbitrary imports and 87 | code execution on the server. This is provided for compatibility with 88 | the classic RPyC (2.6) modus operandi. 89 | This service is very useful in local, secured networks, but it exposes 90 | a major security risk otherwise.""" 91 | __slots__ = ["exposed_namespace"] 92 | 93 | def on_connect(self): 94 | self.exposed_namespace = {} 95 | self._conn._config.update(dict( 96 | allow_all_attrs = True, 97 | allow_pickle = True, 98 | allow_getattr = True, 99 | allow_setattr = True, 100 | allow_delattr = True, 101 | import_custom_exceptions = True, 102 | instantiate_custom_exceptions = True, 103 | instantiate_oldstyle_exceptions = True, 104 | )) 105 | # shortcuts 106 | self._conn.modules = ModuleNamespace(self._conn.root.getmodule) 107 | self._conn.eval = self._conn.root.eval 108 | self._conn.execute = self._conn.root.execute 109 | self._conn.namespace = self._conn.root.namespace 110 | self._conn.builtin = self._conn.modules.__builtin__ 111 | 112 | def exposed_execute(self, text): 113 | exec text in self.exposed_namespace 114 | def exposed_eval(self, text): 115 | return eval(text, self.exposed_namespace) 116 | def exposed_getmodule(self, name): 117 | return __import__(name, None, None, "*") 118 | def exposed_getconn(self): 119 | return self._conn 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /HComServer.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import rpyc 3 | import copy 4 | from rpyc.utils.server import ThreadedServer 5 | 6 | 7 | class HCom_Server(rpyc.Service): 8 | 9 | ''' 10 | Main server, CLIENTS is a dict of clients registered on the server 11 | ''' 12 | CLIENTS = {} 13 | CLIENTS_TYPE = {} 14 | 15 | def on_disconnect(self): 16 | ''' 17 | Remove the client when is disconnected from the registred dict 18 | ''' 19 | clientDisconnected = "" 20 | clientDisconnected_type = "None" 21 | for k in self.CLIENTS.keys(): 22 | try: 23 | if self.CLIENTS[k] == self._conn: 24 | clientDisconnected = k 25 | break 26 | except: 27 | continue 28 | 29 | 30 | if clientDisconnected: 31 | del(self.CLIENTS[clientDisconnected]) 32 | clientDisconnected_type = self.CLIENTS_TYPE[clientDisconnected] 33 | del(self.CLIENTS_TYPE[clientDisconnected]) 34 | sys.stdout.write("=> HCOM_INFO: Client " + str(clientDisconnected) + " left the server !\n") 35 | 36 | # Send update to clients 37 | for k in self.CLIENTS.keys(): 38 | if k == clientDisconnected: continue 39 | self.CLIENTS[k].root.exposed_sendIDUpdate(clientDisconnected, "left", clientDisconnected_type) 40 | 41 | def exposed_registerClient(self, clientID, clientType): 42 | ''' 43 | Save the given client ( from _conn ) to the CLIENTS dict, using the client ID as key 44 | ''' 45 | if not clientID in self.CLIENTS.keys(): 46 | self.CLIENTS[clientID] = self._conn 47 | self.CLIENTS_TYPE[clientID] = clientType 48 | sys.stdout.write("=> HCOM_INFO: Client " + str(clientID) + " registered !\n") 49 | 50 | # Send update to clients 51 | for k in self.CLIENTS.keys(): 52 | self.CLIENTS[k].root.exposed_sendIDUpdate(clientID, "join", clientType) 53 | 54 | print("=> HCOM_INFO: Registered clients: " + str(self.CLIENTS.keys()).replace("[", "").replace("]", "")) 55 | 56 | return True 57 | else: 58 | return False 59 | 60 | def exposed_removeClient(self, clientID): 61 | ''' 62 | Remove client from server registered clients. 63 | ''' 64 | if clientID in self.CLIENTS.keys(): 65 | del(self.CLIENTS[clientID]) 66 | del(self.CLIENTS_TYPE[clientID]) 67 | sys.stdout.write("=> HCOM_INFO: Client " + str(clientID) + " removed from server !\n") 68 | sys.stdout.write("=> HCOM_INFO: Registered clients: " + str(self.CLIENTS.keys()).replace("[", "").replace("]", "")) 69 | 70 | def exposed_getClient(self, clientID): 71 | ''' 72 | return given client. 73 | ''' 74 | if not clientID in self.CLIENTS.keys(): 75 | return None 76 | 77 | return self.CLIENTS[clientID] 78 | 79 | def exposed_getClientType(self, clientID): 80 | 81 | if not clientID in self.CLIENTS_TYPE.keys(): 82 | return None 83 | 84 | return self.CLIENTS_TYPE[clientID] 85 | 86 | def exposed_getAllClientTypes(self): 87 | 88 | return self.CLIENTS_TYPE 89 | 90 | def exposed_getAllClients(self): 91 | ''' 92 | return all clients registered on the server. 93 | ''' 94 | return self.CLIENTS 95 | 96 | def exposed_getAllCientInfos(self): 97 | return [self.CLIENTS, self.CLIENTS_TYPE] 98 | 99 | def exposed_sendDataToClient(self, clientID, dataType, sender, data, tabTarget): 100 | ''' 101 | Invoke the 'catchData' of the client(s) from the given ID(s). 102 | ''' 103 | 104 | c_data = copy.copy(data) 105 | 106 | notReached = [] 107 | if not isinstance(clientID, list): 108 | clientID = [clientID,] 109 | 110 | if 'OPEN_CHAT_ROOM' in clientID or tabTarget == 'OPEN_CHAT_ROOM': 111 | 112 | for k in self.CLIENTS.keys(): 113 | if k == sender: 114 | continue 115 | self.CLIENTS[k].root.exposed_catchData(dataType, sender, c_data, tabTarget, [None, None]) 116 | else: 117 | for client in clientID: 118 | if not client in self.CLIENTS.keys(): 119 | notReached.append(client) 120 | continue 121 | self.CLIENTS[client].root.exposed_catchData(dataType, sender, c_data, tabTarget, self.CLIENTS_TYPE[client]) 122 | 123 | if not notReached: 124 | return True 125 | else: 126 | return notReached 127 | 128 | 129 | if __name__ == "__main__": 130 | 131 | print("LAUNCHING HCOM SERVER ...") 132 | print("SERVER VERSION: 1.0") 133 | 134 | t = ThreadedServer(HCom_Server, port = 5000, protocol_config={"allow_public_attrs" : True, "allow_pickle" : True}) 135 | t.start() -------------------------------------------------------------------------------- /lib/rpyc/servers/classic_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | classic rpyc server (threaded, forking or std) running a SlaveService 4 | usage: 5 | classic_server.py # default settings 6 | classic_server.py -m forking -p 12345 # custom settings 7 | classic_server.py --vdb file.vdb # tlslite-authenticated server 8 | """ 9 | import sys 10 | import os 11 | import rpyc 12 | from optparse import OptionParser 13 | from rpyc.utils.server import ThreadedServer, ForkingServer 14 | from rpyc.utils.classic import DEFAULT_SERVER_PORT 15 | from rpyc.utils.registry import REGISTRY_PORT 16 | from rpyc.utils.registry import UDPRegistryClient, TCPRegistryClient 17 | from rpyc.utils.authenticators import VdbAuthenticator 18 | from rpyc.core import SlaveService 19 | 20 | 21 | parser = OptionParser() 22 | parser.add_option("-m", "--mode", action="store", dest="mode", metavar="MODE", 23 | default="threaded", type="string", help="mode can be 'threaded', 'forking', " 24 | "or 'stdio' to operate over the standard IO pipes (for inetd, etc.). " 25 | "Default is 'threaded'") 26 | parser.add_option("-p", "--port", action="store", dest="port", type="int", 27 | metavar="PORT", default=DEFAULT_SERVER_PORT, help="specify a different " 28 | "TCP listener port. Default is 18812") 29 | parser.add_option("--host", action="store", dest="host", type="str", 30 | metavar="HOST", default="0.0.0.0", help="specify a different " 31 | "host to bind to. Default is 0.0.0.0") 32 | parser.add_option("--logfile", action="store", dest="logfile", type="str", 33 | metavar="FILE", default=None, help="specify the log file to use; the " 34 | "default is stderr") 35 | parser.add_option("-q", "--quiet", action="store_true", dest="quiet", 36 | default=False, help="quiet mode (no logging). in stdio mode, " 37 | "writes to /dev/null") 38 | parser.add_option("--vdb", action="store", dest="vdbfile", metavar="FILENAME", 39 | default=None, help="starts an TLS/SSL authenticated server (using tlslite);" 40 | "the credentials are loaded from the vdb file. if not given, the server" 41 | "is not secure (unauthenticated). use vdbconf.py to manage vdb files" 42 | ) 43 | parser.add_option("--dont-register", action="store_false", dest="auto_register", 44 | default=True, help="disables this server from registering at all. " 45 | "By default, the server will attempt to register") 46 | parser.add_option("--registry-type", action="store", dest="regtype", type="str", 47 | default="udp", help="can be 'udp' or 'tcp', default is 'udp'") 48 | parser.add_option("--registry-port", action="store", dest="regport", type="int", 49 | default=REGISTRY_PORT, help="the UDP/TCP port. default is %s" % (REGISTRY_PORT,)) 50 | parser.add_option("--registry-host", action="store", dest="reghost", type="str", 51 | default=None, help="the registry host machine. for UDP, the default is " 52 | "255.255.255.255; for TCP, a value is required") 53 | 54 | 55 | def get_options(): 56 | options, args = parser.parse_args() 57 | if args: 58 | parser.error("does not take positional arguments: %r" % (args,)) 59 | 60 | options.mode = options.mode.lower() 61 | 62 | if options.regtype.lower() == "udp": 63 | if options.reghost is None: 64 | options.reghost = "255.255.255.255" 65 | options.registrar = UDPRegistryClient(ip = options.reghost, port = options.regport) 66 | elif options.regtype.lower() == "tcp": 67 | if options.reghost is None: 68 | parser.error("must specific --registry-host") 69 | options.registrar = TCPRegistryClient(ip = options.reghost, port = options.regport) 70 | else: 71 | parse.error("invalid registry type %r" % (options.regtype,)) 72 | 73 | if options.vdbfile: 74 | if not os.path.exists(options.vdbfile): 75 | parser.error("vdb file does not exist") 76 | options.authenticator = VdbAuthenticator.from_file(options.vdbfile, mode = "r") 77 | else: 78 | options.authenticator = None 79 | 80 | options.handler = "serve_%s" % (options.mode,) 81 | if options.handler not in globals(): 82 | parser.error("invalid mode %r" % (options.mode,)) 83 | 84 | return options 85 | 86 | def serve_threaded(options): 87 | t = ThreadedServer(SlaveService, hostname = options.host, 88 | port = options.port, reuse_addr = True, 89 | authenticator = options.authenticator, registrar = options.registrar, 90 | auto_register = options.auto_register) 91 | t.logger.quiet = options.quiet 92 | if options.logfile: 93 | t.logger.console = open(options.logfile, "w") 94 | t.start() 95 | 96 | def serve_forking(options): 97 | t = ForkingServer(SlaveService, hostname = options.host, 98 | port = options.port, reuse_addr = True, 99 | authenticator = options.authenticator, registrar = options.registrar, 100 | auto_register = options.auto_register) 101 | t.logger.quiet = options.quiet 102 | if options.logfile: 103 | t.logger.console = open(options.logfile, "w") 104 | t.start() 105 | 106 | def serve_stdio(options): 107 | origstdin = sys.stdin 108 | origstdout = sys.stdout 109 | if options.quiet: 110 | dev = os.devnull 111 | elif sys.platform == "win32": 112 | dev = "con:" 113 | else: 114 | dev = "/dev/tty" 115 | try: 116 | sys.stdin = open(dev, "r") 117 | sys.stdout = open(dev, "w") 118 | except (IOError, OSError): 119 | sys.stdin = open(os.devnull, "r") 120 | sys.stdout = open(os.devnull, "w") 121 | conn = rpyc.classic.connect_pipes(origstdin, origstdout) 122 | try: 123 | try: 124 | conn.serve_all() 125 | except KeyboardInterrupt: 126 | print "User interrupt!" 127 | finally: 128 | conn.close() 129 | 130 | 131 | def main(): 132 | options = get_options() 133 | handler = globals()[options.handler] 134 | handler(options) 135 | 136 | 137 | if __name__ == "__main__": 138 | main() 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /lib/rpyc/utils/factory.py: -------------------------------------------------------------------------------- 1 | """ 2 | rpyc connection factories 3 | """ 4 | import socket 5 | import thread, threading 6 | import rpyc 7 | from rpyc import Connection, Channel, SocketStream, PipeStream, VoidService 8 | from rpyc.utils.registry import UDPRegistryClient, TCPRegistryClient 9 | 10 | 11 | class DiscoveryError(Exception): 12 | pass 13 | 14 | 15 | #------------------------------------------------------------------------------ 16 | # API 17 | #------------------------------------------------------------------------------ 18 | def connect_channel(channel, service = VoidService, config = {}): 19 | """creates a connection over a given channel 20 | channel - the channel to use 21 | service - the local service to expose (defaults to Void) 22 | config - configuration dict""" 23 | return Connection(service, channel, config = config) 24 | 25 | def connect_stream(stream, service = VoidService, config = {}): 26 | """creates a connection over a given stream 27 | stream - the stream to use 28 | service - the local service to expose (defaults to Void) 29 | config - configuration dict""" 30 | return connect_channel(Channel(stream), service = service, config = config) 31 | 32 | def connect_pipes(input, output, service = VoidService, config = {}): 33 | """creates a connection over the given input/output pipes 34 | input - the input pipe 35 | output - the output pipe 36 | service - the local service to expose (defaults to Void) 37 | config - configuration dict""" 38 | return connect_stream(PipeStream(input, output), service = service, config = config) 39 | 40 | def connect_stdpipes(service = VoidService, config = {}): 41 | """creates a connection over the standard input/output pipes 42 | service - the local service to expose (defaults to Void) 43 | config - configuration dict""" 44 | return connect_stream(PipeStream.from_std(), service = service, config = config) 45 | 46 | def connect(host, port, service = VoidService, config = {}): 47 | """creates a socket-connection to the given host 48 | host - the hostname to connect to 49 | port - the TCP port to use 50 | service - the local service to expose (defaults to Void) 51 | config - configuration dict""" 52 | return Connection(service, Channel(SocketStream.connect(host, port)), config = config) 53 | 54 | def tls_connect(host, port, username, password, service = VoidService, config = {}): 55 | """creates a TLS-connection to the given host (encrypted and authenticated) 56 | username - the username used to authenticate the client 57 | password - the password used to authenticate the client 58 | host - the hostname to connect to 59 | port - the TCP port to use 60 | service - the local service to expose (defaults to Void) 61 | config - configuration dict""" 62 | s = SocketStream.tls_connect(host, port, username, password) 63 | return Connection(service, Channel(s), config = config) 64 | 65 | def discover(service_name, host = None, registrar = None, timeout = 2): 66 | """discovers hosts running the given service 67 | service_name - the service to look for 68 | host - limit the discovery to the given host only (None means any host) 69 | registrar - use this registry client to discover services. if None, 70 | use the default UDPRegistryClient with the default settings. 71 | timeout - the number of seconds to wait for a reply from the registry 72 | if no hosts are found, raises DiscoveryError 73 | returns a list of (ip, port) pairs 74 | """ 75 | if registrar is None: 76 | registrar = UDPRegistryClient(timeout = timeout) 77 | addrs = registrar.discover(service_name) 78 | if not addrs: 79 | raise DiscoveryError("no servers exposing %r were found" % (service_name,)) 80 | if host: 81 | ips = socket.gethostbyname_ex(host)[2] 82 | addrs = [(h, p) for h, p in addrs if h in ips] 83 | if not addrs: 84 | raise DiscoveryError("no servers exposing %r were found on %r" % (service_name, host)) 85 | return addrs 86 | 87 | def connect_by_service(service_name, host = None, service = VoidService, config = {}): 88 | """create a connection to an arbitrary server that exposes the requested service 89 | service_name - the service to discover 90 | host - limit discovery to the given host only (None means any host) 91 | service - the local service to expose (defaults to Void) 92 | config - configuration dict""" 93 | host, port = discover(service_name, host = host)[0] 94 | return connect(host, port, service, config = config) 95 | 96 | def connect_subproc(args, service = VoidService, config = {}): 97 | """runs an rpyc server on a child process that and connects to it over 98 | the stdio pipes. uses the subprocess module. 99 | args - the args to Popen, e.g., ["python", "-u", "myfile.py"] 100 | service - the local service to expose (defaults to Void) 101 | config - configuration dict""" 102 | from subprocess import Popen, PIPE 103 | proc = Popen(args, stdin = PIPE, stdout = PIPE) 104 | conn = connect_pipes(proc.stdout, proc.stdin, service = service, config = config) 105 | conn.proc = proc # just so you can have control over the processs 106 | return conn 107 | 108 | def connect_thread(service = VoidService, config = {}, remote_service = VoidService, remote_config = {}): 109 | """starts an rpyc server on a thread and connects to it over a socket. 110 | service - the local service to expose (defaults to Void) 111 | config - configuration dict 112 | server_service - the remote service to expose (of the server; defaults to Void) 113 | server_config - remote configuration dict (of the server) 114 | """ 115 | listener = socket.socket() 116 | listener.bind(("localhost", 0)) 117 | listener.listen(1) 118 | 119 | def server(listener = listener): 120 | client = listener.accept()[0] 121 | listener.close() 122 | conn = connect_stream(SocketStream(client), service = remote_service, 123 | config = remote_config) 124 | try: 125 | conn.serve_all() 126 | except KeyboardInterrupt: 127 | thread.interrupt_main() 128 | 129 | t = threading.Thread(target = server) 130 | t.setDaemon(True) 131 | t.start() 132 | host, port = listener.getsockname() 133 | return connect(host, port, service = service, config = config) 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /HComNuke/HComNukeUtils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import subprocess 4 | import random 5 | import threading 6 | import nuke 7 | 8 | HISTORY_FOLDER = os.path.dirname(__file__) + "\\HCom_History\\" 9 | ICONPATH = os.path.dirname(__file__) + "\\HCom_Icons\\" 10 | 11 | def readIni(): 12 | 13 | iniValues = {} 14 | 15 | ini = os.path.dirname(__file__) + "\\HCom.ini" 16 | with open(ini, 'r') as f: 17 | for i in f.readlines(): 18 | if i.startswith("#"): 19 | continue 20 | elif i == "\n": 21 | continue 22 | else: 23 | data = i.split("=") 24 | if len(data) <= 1: 25 | continue 26 | 27 | val = data[1].replace("\n", "").replace("\r", "") 28 | if val.isdigit() and "." in val: 29 | val = float(val) 30 | elif val.isdigit() and not "." in val: 31 | val = int(val) 32 | elif val.lower() == "true": 33 | val = True 34 | elif val.lower() == "false": 35 | val = False 36 | else: 37 | val = str(val) 38 | 39 | iniValues[str(data[0]).replace("\r", "")] = val 40 | 41 | return iniValues 42 | 43 | def writeIni(settings): 44 | 45 | ini = os.path.dirname(__file__) + "\\HCom.ini" 46 | with open(ini, 'w') as f: 47 | f.write('') 48 | 49 | data = ["#HCom info file -MAYA CLIENT-\r\n"] 50 | for k in settings.keys(): 51 | data.append(k + "=" + str(settings[k])) 52 | 53 | with open(ini, 'a') as f: 54 | for d in data: f.write(d + "\r\n") 55 | 56 | 57 | def writeHistory(sender, timeStamp, data): 58 | 59 | historyFile = HISTORY_FOLDER + sender.lower() + "_history.txt" 60 | with open(historyFile, 'a') as f: 61 | f.write(timeStamp + "\n") 62 | f.write(" " + str(data) + "\n") 63 | 64 | def coloredString(string, hexColor=None, rgb=None, italic=False, bold=False): 65 | 66 | in_italic_tag = "" 67 | out_italic_tag = "" 68 | if italic: 69 | in_italic_tag = "" 70 | out_italic_tag = "" 71 | 72 | in_bold_tag = "" 73 | out_bold_tag = "" 74 | if bold: 75 | in_bold_tag = "" 76 | out_bold_tag = "" 77 | 78 | if not hexColor and not rgb: 79 | return "{0}{1}{2}{3}{4}".format(in_italic_tag, in_bold_tag, string, out_bold_tag, out_italic_tag) 80 | 81 | if hexColor: 82 | if hexColor.startswith("#"): 83 | hexColor = hexColor.replace("#", "") 84 | return "{0}{1}{2}{3}{4}".format(in_italic_tag, in_bold_tag, string, out_bold_tag, out_italic_tag, hexColor) 85 | else: 86 | return "{0}{1}{2}{3}{4}".format(in_italic_tag, in_bold_tag, string, out_bold_tag, out_italic_tag, rgb[0], rgb[1], rgb[2]) 87 | 88 | def incrementFile(filePath): 89 | 90 | if not os.path.exists(filePath): 91 | return filePath 92 | 93 | baseName = os.path.basename(filePath) 94 | name = baseName.split(".")[0] 95 | fileType = baseName.split(".")[1] 96 | dirName = os.path.dirname(filePath) 97 | 98 | fileInc = dirName + os.sep + name + "_1." + fileType 99 | i = 2 100 | while os.path.exists(fileInc): 101 | fileInc = dirName + os.sep + name + "_" + str(i) + "." + fileType 102 | 103 | return fileInc 104 | 105 | def createAlembic(data, sender = "", settings=None): 106 | 107 | name = data["NAME"] 108 | binary = data["DATA"] 109 | 110 | abcFile = fetchMyReceivedFilesFolder() + os.sep + name + ".abc" 111 | abcFile = incrementFile(abcFile) 112 | 113 | with open(abcFile, 'wb') as f: 114 | f.write(binary) 115 | 116 | fileName = name = name + "_from_" + sender 117 | 118 | nuke.executeInMainThread(_importAlembic, args=(fileName, abcFile)) 119 | 120 | return True 121 | 122 | def _importAlembic(fileName, filePath): 123 | 124 | n = nuke.createNode('ReadGeo2') 125 | n["file"].setValue(filePath.replace("\\", "/")) 126 | n.setName(fileName) 127 | 128 | def createOtl(data, sender="", settings=None): 129 | return False 130 | 131 | def setOtlSettings(data, sender="", settings=None): 132 | return False 133 | 134 | def createMesh(data, sender="", settings=None): 135 | 136 | meshType = data["MESH_TYPE"] 137 | if meshType != ".obj": 138 | print("ERROR: Mesh type not supported (" + meshType + ")") 139 | return False 140 | 141 | meshName = data["MESH_NAME"] 142 | meshData = data["MESH_DATA"] 143 | 144 | obj = fetchMyReceivedFilesFolder() + os.sep + meshName + meshType 145 | obj = incrementFile(obj) 146 | with open(obj, 'wb') as f: 147 | f.write(meshData) 148 | 149 | nuke.nodes.ReadGeo(name = meshName + "_from_" + sender, 150 | file = obj.replace("\\", "/")) 151 | 152 | 153 | def createPic(data, sender="", settings=None): 154 | 155 | imageName = data["IMAGE_NAME"] 156 | imageData = data["BINARY_DATA"] 157 | 158 | imageNameAndFile = imageName.split(".") 159 | 160 | outFile = fetchMyReceivedFilesFolder() + os.sep + imageNameAndFile[0] + "." + imageNameAndFile[1] 161 | outFile = incrementFile(outFile) 162 | 163 | with open(outFile, 'wb') as f: 164 | f.write(imageData) 165 | 166 | nuke.nodes.Read(name = imageName + "_from_" + sender, 167 | file = outFile.replace("\\", "/")) 168 | return True 169 | 170 | 171 | def fetchMyReceivedFilesFolder(): 172 | 173 | p = readIni()["MY_RECEIVED_FILES"].replace("\r", "") 174 | if p == "DEFAULT": 175 | p = os.path.dirname(__file__) + "\\HCom_Received_Files" 176 | 177 | if not os.path.exists(p): 178 | os.makedirs(p) 179 | 180 | return p 181 | 182 | def rdnname(): 183 | names = ["Motoko", "Bato", "Kusanagi", "Frodon", "Sheldon", "Pipo", "Sam", "Gandalf", "Fitz", "Henry"] 184 | names += ["Leonard", "Batman", "Bobleponge", "rincewind", "carrot", "HelloWorld", "Python", "Houdini"] 185 | return names[random.randint(0, len(names)-1)] 186 | 187 | class CLIENT_TYPE(): 188 | 189 | NONE = "NONE" 190 | HOUDINI = "Houdini" 191 | MAYA_NO_HENGINE = "Maya_no_hengine" 192 | MAYA_HENGINE = "Maya_hengine" 193 | NUKE = "nuke" 194 | -------------------------------------------------------------------------------- /lib/rpyc/core/netref.py: -------------------------------------------------------------------------------- 1 | """ 2 | NetRef - transparent network references implementation. 3 | 4 | SURGEON GENERAL'S WARNING: Black magaic is known to causes Lung Cancer, 5 | Heart Disease, Emphysema, and May Complicate Pregnancy. Close your eyes! 6 | """ 7 | import sys 8 | import inspect 9 | import types 10 | import cPickle as pickle 11 | from rpyc.core import consts 12 | 13 | 14 | _local_netref_attrs = frozenset([ 15 | '____conn__', '____oid__', '__class__', '__cmp__', '__del__', '__delattr__', 16 | '__dir__', '__doc__', '__getattr__', '__getattribute__', '__hash__', 17 | '__init__', '__metaclass__', '__module__', '__new__', '__reduce__', 18 | '__reduce_ex__', '__repr__', '__setattr__', '__slots__', '__str__', 19 | '__weakref__', '__dict__', '__members__', '__methods__', 20 | ]) 21 | 22 | _builtin_types = [ 23 | type, object, types.InstanceType, types.ClassType, bool, complex, dict, 24 | file, float, int, list, long, slice, str, basestring, tuple, unicode, 25 | str, set, frozenset, Exception, types.NoneType, types.DictProxyType, 26 | types.BuiltinFunctionType, types.GeneratorType, types.MethodType, 27 | types.CodeType, types.FrameType, types.TracebackType, xrange, 28 | types.ModuleType, types.FunctionType, 29 | 30 | type(int.__add__), # wrapper_descriptor 31 | type((1).__add__), # method-wrapper 32 | type(iter([])), # listiterator 33 | type(iter(())), # tupleiterator 34 | type(iter(xrange(10))), # rangeiterator 35 | type(iter(set())), # setiterator 36 | ] 37 | 38 | _normalized_builtin_types = dict(((t.__name__, t.__module__), t) 39 | for t in _builtin_types) 40 | 41 | def syncreq(proxy, handler, *args): 42 | """performs a synchronous request on the given proxy object""" 43 | conn = object.__getattribute__(proxy, "____conn__") 44 | oid = object.__getattribute__(proxy, "____oid__") 45 | return conn().sync_request(handler, oid, *args) 46 | 47 | def asyncreq(proxy, handler, *args): 48 | """performs an asynchronous request on the given proxy object, 49 | retuning an AsyncResult""" 50 | conn = object.__getattribute__(proxy, "____conn__") 51 | oid = object.__getattribute__(proxy, "____oid__") 52 | return conn().async_request(handler, oid, *args) 53 | 54 | class NetrefMetaclass(type): 55 | """a metaclass just to customize the __repr__ of netref classes""" 56 | __slots__ = () 57 | def __repr__(self): 58 | if self.__module__: 59 | return "" % (self.__module__, self.__name__) 60 | else: 61 | return "" % (self.__name__,) 62 | 63 | class BaseNetref(object): 64 | """the base netref object, from which all netref classes derive""" 65 | __metaclass__ = NetrefMetaclass 66 | __slots__ = ["____conn__", "____oid__", "__weakref__"] 67 | def __init__(self, conn, oid): 68 | self.____conn__ = conn 69 | self.____oid__ = oid 70 | def __del__(self): 71 | try: 72 | asyncreq(self, consts.HANDLE_DEL) 73 | except: 74 | pass 75 | 76 | def __getattribute__(self, name): 77 | if name in _local_netref_attrs: 78 | if name == "__class__": 79 | cls = object.__getattribute__(self, "__class__") 80 | if cls is None: 81 | cls = self.__getattr__("__class__") 82 | return cls 83 | elif name == "__doc__": 84 | return self.__getattr__("__doc__") 85 | elif name == "__members__": # sys.version_info < (2, 6) 86 | return self.__dir__() 87 | else: 88 | return object.__getattribute__(self, name) 89 | else: 90 | return syncreq(self, consts.HANDLE_GETATTR, name) 91 | def __getattr__(self, name): 92 | return syncreq(self, consts.HANDLE_GETATTR, name) 93 | def __delattr__(self, name): 94 | if name in _local_netref_attrs: 95 | object.__delattr__(self, name) 96 | else: 97 | syncreq(self, consts.HANDLE_DELATTR, name) 98 | def __setattr__(self, name, value): 99 | if name in _local_netref_attrs: 100 | object.__setattr__(self, name, value) 101 | else: 102 | syncreq(self, consts.HANDLE_SETATTR, name, value) 103 | def __dir__(self): 104 | return list(syncreq(self, consts.HANDLE_DIR)) 105 | 106 | # support for metaclasses 107 | def __hash__(self): 108 | return syncreq(self, consts.HANDLE_HASH) 109 | def __cmp__(self, other): 110 | return syncreq(self, consts.HANDLE_CMP, other) 111 | def __repr__(self): 112 | return syncreq(self, consts.HANDLE_REPR) 113 | def __str__(self): 114 | return syncreq(self, consts.HANDLE_STR) 115 | # support for pickle 116 | def __reduce_ex__(self, proto): 117 | return pickle.loads, (syncreq(self, consts.HANDLE_PICKLE, proto),) 118 | 119 | def _make_method(name, doc): 120 | if name == "__call__": 121 | def __call__(_self, *args, **kwargs): 122 | kwargs = tuple(kwargs.items()) 123 | return syncreq(_self, consts.HANDLE_CALL, args, kwargs) 124 | __call__.__doc__ = doc 125 | return __call__ 126 | else: 127 | def method(_self, *args, **kwargs): 128 | kwargs = tuple(kwargs.items()) 129 | return syncreq(_self, consts.HANDLE_CALLATTR, name, args, kwargs) 130 | method.__name__ = name 131 | method.__doc__ = doc 132 | return method 133 | 134 | def inspect_methods(obj): 135 | """returns a list of (method name, docstring) tuples of all the methods of 136 | the given object""" 137 | methods = {} 138 | attrs = {} 139 | if isinstance(obj, type): 140 | # don't forget the darn metaclass 141 | mros = list(reversed(type(obj).__mro__)) + list(reversed(obj.__mro__)) 142 | else: 143 | mros = reversed(type(obj).__mro__) 144 | for basecls in mros: 145 | attrs.update(basecls.__dict__) 146 | for name, attr in attrs.iteritems(): 147 | if name not in _local_netref_attrs and hasattr(attr, "__call__"): 148 | methods[name] = inspect.getdoc(attr) 149 | return methods.items() 150 | 151 | def class_factory(clsname, modname, methods): 152 | ns = {"__slots__" : ()} 153 | for name, doc in methods: 154 | if name not in _local_netref_attrs: 155 | ns[name] = _make_method(name, doc) 156 | ns["__module__"] = modname 157 | if modname in sys.modules and hasattr(sys.modules[modname], clsname): 158 | ns["__class__"] = getattr(sys.modules[modname], clsname) 159 | elif (clsname, modname) in _normalized_builtin_types: 160 | ns["__class__"] = _normalized_builtin_types[clsname, modname] 161 | else: 162 | ns["__class__"] = None # to be resolved by the instance 163 | return type(clsname, (BaseNetref,), ns) 164 | 165 | builtin_classes_cache = {} 166 | for cls in _builtin_types: 167 | builtin_classes_cache[cls.__name__, cls.__module__] = class_factory( 168 | cls.__name__, cls.__module__, inspect_methods(cls)) 169 | 170 | 171 | -------------------------------------------------------------------------------- /HComMaya/HComMayaUtils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import subprocess 4 | import random 5 | import threading 6 | 7 | import maya.mel as mel 8 | import maya.cmds as cmds 9 | import maya.utils as mUtils 10 | 11 | HISTORY_FOLDER = os.path.dirname(__file__) + "\\HCom_History\\" 12 | ICONPATH = os.path.dirname(__file__) + "\\HCom_Icons\\" 13 | 14 | def readIni(): 15 | 16 | iniValues = {} 17 | 18 | ini = os.path.dirname(__file__) + "\\HCom.ini" 19 | with open(ini, 'r') as f: 20 | for i in f.readlines(): 21 | if i.startswith("#"): 22 | continue 23 | elif i == "\n": 24 | continue 25 | else: 26 | data = i.split("=") 27 | if len(data) <= 1: 28 | continue 29 | 30 | val = data[1].replace("\n", "").replace("\r", "") 31 | if val.isdigit() and "." in val: 32 | val = float(val) 33 | elif val.isdigit() and not "." in val: 34 | val = int(val) 35 | elif val.lower() == "true": 36 | val = True 37 | elif val.lower() == "false": 38 | val = False 39 | else: 40 | val = str(val) 41 | 42 | iniValues[str(data[0]).replace("\r", "")] = val 43 | 44 | return iniValues 45 | 46 | def writeIni(settings): 47 | 48 | ini = os.path.dirname(__file__) + "\\HCom.ini" 49 | with open(ini, 'w') as f: 50 | f.write('') 51 | 52 | data = ["#HCom info file -MAYA CLIENT-\r\n"] 53 | for k in settings.keys(): 54 | data.append(k + "=" + str(settings[k])) 55 | 56 | with open(ini, 'a') as f: 57 | for d in data: f.write(d + "\r\n") 58 | 59 | 60 | def writeHistory(sender, timeStamp, data): 61 | 62 | historyFile = HISTORY_FOLDER + sender.lower() + "_history.txt" 63 | with open(historyFile, 'a') as f: 64 | f.write(timeStamp + "\n") 65 | f.write(" " + str(data) + "\n") 66 | 67 | def coloredString(string, hexColor=None, rgb=None, italic=False, bold=False): 68 | 69 | in_italic_tag = "" 70 | out_italic_tag = "" 71 | if italic: 72 | in_italic_tag = "" 73 | out_italic_tag = "" 74 | 75 | in_bold_tag = "" 76 | out_bold_tag = "" 77 | if bold: 78 | in_bold_tag = "" 79 | out_bold_tag = "" 80 | 81 | if not hexColor and not rgb: 82 | return "{0}{1}{2}{3}{4}".format(in_italic_tag, in_bold_tag, string, out_bold_tag, out_italic_tag) 83 | 84 | if hexColor: 85 | if hexColor.startswith("#"): 86 | hexColor = hexColor.replace("#", "") 87 | return "{0}{1}{2}{3}{4}".format(in_italic_tag, in_bold_tag, string, out_bold_tag, out_italic_tag, hexColor) 88 | else: 89 | return "{0}{1}{2}{3}{4}".format(in_italic_tag, in_bold_tag, string, out_bold_tag, out_italic_tag, rgb[0], rgb[1], rgb[2]) 90 | 91 | def incrementFile(filePath): 92 | 93 | if not os.path.exists(filePath): 94 | return filePath 95 | 96 | baseName = os.path.basename(filePath) 97 | name = baseName.split(".")[0] 98 | fileType = baseName.split(".")[1] 99 | dirName = os.path.dirname(filePath) 100 | 101 | fileInc = dirName + os.sep + name + "_1." + fileType 102 | i = 2 103 | while os.path.exists(fileInc): 104 | fileInc = dirName + os.sep + name + "_" + str(i) + "." + fileType 105 | 106 | return fileInc 107 | 108 | def createAlembic(data, sender = "", settings=None): 109 | 110 | name = data["NAME"] 111 | binary = data["DATA"] 112 | 113 | abcFile = fetchMyReceivedFilesFolder() + os.sep + name + ".abc" 114 | abcFile = incrementFile(abcFile) 115 | 116 | with open(abcFile, 'wb') as f: 117 | f.write(binary) 118 | 119 | try: 120 | mUtils.executeInMainThreadWithResult(lambda: cmds.AbcImport(fetchMyReceivedFilesFolder() + os.sep + name + ".abc")) 121 | return True 122 | except AttributeError: 123 | print("ERROR: ALEMBIC PLUGIN NOT LOADED") 124 | return False 125 | 126 | def createOtl(data, sender="", settings=None): 127 | 128 | 129 | nodeType = data["OTL_TYPE"] 130 | subOtlLibs = data["OTL_ALL_LIBS"] 131 | 132 | libPath = None 133 | otlToAdd = None 134 | 135 | # Check otl libs 136 | if subOtlLibs: 137 | 138 | for e in subOtlLibs: 139 | libName = e[0] 140 | libData = e[1] 141 | 142 | otlLibToInstall = str(fetchMyReceivedFilesFolder() + os.sep).replace("\\","/") + libName 143 | 144 | with open(otlLibToInstall, 'wb') as f: 145 | f.write(libData) 146 | 147 | libs = mel.eval('houdiniAsset -listAssets "' + otlLibToInstall + '"') 148 | if libs: 149 | for lib in libs: 150 | if nodeType in str(lib): 151 | libPath = otlLibToInstall 152 | otlToAdd = lib 153 | 154 | if libPath and otlToAdd: 155 | melcmd = 'houdiniAsset -loadAsset "' + libPath + '" "' + otlToAdd + '"' 156 | 157 | try: 158 | mUtils.executeInMainThreadWithResult(lambda: mel.eval(melcmd)) 159 | return True 160 | except Exception as e: 161 | print str(e) 162 | return False 163 | 164 | else: 165 | print("ERROR: Incoming object is not a valid digital asset") 166 | return None 167 | 168 | 169 | def setOtlSettings(data, sender="", settings=None): 170 | return False 171 | 172 | def createMesh(data, sender="", settings=None): 173 | 174 | meshType = data["MESH_TYPE"] 175 | if meshType != ".obj": 176 | print("ERROR: Mesh type not supported (" + meshType + ")") 177 | return False 178 | 179 | meshName = data["MESH_NAME"] 180 | meshData = data["MESH_DATA"] 181 | 182 | obj = fetchMyReceivedFilesFolder() + os.sep + meshName + meshType 183 | obj = incrementFile(obj) 184 | with open(obj, 'wb') as f: 185 | f.write(meshData) 186 | 187 | try: 188 | mUtils.executeInMainThreadWithResult(lambda: cmds.file(obj,i=True,dns=True)) 189 | return True 190 | except Exception as e: 191 | print str(e) 192 | return False 193 | 194 | 195 | def createPic(data, sender="", settings=None): 196 | 197 | imageName = data["IMAGE_NAME"] 198 | imageData = data["BINARY_DATA"] 199 | 200 | imageNameAndFile = imageName.split(".") 201 | 202 | outFile = fetchMyReceivedFilesFolder() + os.sep + imageNameAndFile[0] + "." + imageNameAndFile[1] 203 | outFile = incrementFile(outFile) 204 | 205 | with open(outFile, 'wb') as f: 206 | f.write(imageData) 207 | 208 | openPicFile(outFile) 209 | return True 210 | 211 | def openPicFile(picFile): 212 | 213 | try: 214 | subprocess.Popen("fcheck " + picFile, shell=True) 215 | 216 | except Exception as e: 217 | 218 | print "FCHECK ERROR: " + str(e) 219 | 220 | try: 221 | subprocess.Popen("explorer " + picFile, shell=True) 222 | 223 | except Exception as e: 224 | print "EXPLORER ERROR: " + str(e) 225 | 226 | 227 | def fetchMyReceivedFilesFolder(): 228 | 229 | p = readIni()["MY_RECEIVED_FILES"].replace("\r", "") 230 | if p == "DEFAULT": 231 | p = os.path.dirname(__file__) + "\\HCom_Received_Files" 232 | 233 | if not os.path.exists(p): 234 | os.makedirs(p) 235 | 236 | return p 237 | 238 | def rdnname(): 239 | names = ["Motoko", "Bato", "Kusanagi", "Frodon", "Sheldon", "Pipo", "Sam", "Gandalf", "Fitz", "Henry"] 240 | names += ["Leonard", "Batman", "Bobleponge", "rincewind", "carrot", "HelloWorld", "Python", "Houdini"] 241 | return names[random.randint(0, len(names)-1)] 242 | 243 | class CLIENT_TYPE(): 244 | 245 | NONE = "NONE" 246 | HOUDINI = "Houdini" 247 | MAYA_NO_HENGINE = "Maya_no_hengine" 248 | MAYA_HENGINE = "Maya_hengine" 249 | NUKE = "nuke" 250 | -------------------------------------------------------------------------------- /lib/rpyc/utils/server.py: -------------------------------------------------------------------------------- 1 | """ 2 | rpyc plug-in server (threaded or forking) 3 | """ 4 | import sys 5 | import os 6 | import socket 7 | import time 8 | import threading 9 | import select 10 | import signal 11 | import errno 12 | from rpyc.core import brine, SocketStream, Channel, Connection 13 | from rpyc.utils.logger import Logger 14 | from rpyc.utils.registry import UDPRegistryClient, TCPRegistryClient 15 | from rpyc.utils.authenticators import AuthenticationError 16 | 17 | 18 | class Server(object): 19 | def __init__(self, service, hostname = "0.0.0.0", port = 0, backlog = 10, 20 | reuse_addr = True, authenticator = None, registrar = None, 21 | auto_register = True, protocol_config = {}, logger = None): 22 | self.active = False 23 | self._closed = False 24 | self.service = service 25 | self.authenticator = authenticator 26 | self.backlog = backlog 27 | self.auto_register = auto_register 28 | self.protocol_config = protocol_config 29 | self.clients = set() 30 | if logger is None: 31 | logger = self._get_logger() 32 | self.logger = logger 33 | if registrar is None: 34 | registrar = UDPRegistryClient(logger = self.logger) 35 | self.registrar = registrar 36 | 37 | self.listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 38 | if reuse_addr and sys.platform != "win32": 39 | # warning: reuseaddr is not what you expect on windows! 40 | self.listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 41 | 42 | self.listener.bind((hostname, port)) 43 | self.port = self.listener.getsockname()[1] 44 | 45 | def _get_logger(self): 46 | raise NotImplementedError() 47 | 48 | def close(self): 49 | if self._closed: 50 | return 51 | self._closed = True 52 | self.active = False 53 | if self.auto_register: 54 | try: 55 | self.registrar.unregister(self.port) 56 | except Exception: 57 | self.logger.traceback() 58 | self.listener.close() 59 | self.logger.info("listener closed") 60 | for c in set(self.clients): 61 | try: 62 | c.shutdown(socket.SHUT_RDWR) 63 | except Exception: 64 | pass 65 | c.close() 66 | self.clients.clear() 67 | 68 | def fileno(self): 69 | return self.listener.fileno() 70 | 71 | def accept(self): 72 | while True: 73 | try: 74 | sock, (h, p) = self.listener.accept() 75 | except socket.timeout: 76 | pass 77 | except socket.error, ex: 78 | if ex[0] == errno.EINTR: 79 | pass 80 | else: 81 | raise EOFError() 82 | else: 83 | break 84 | 85 | sock.setblocking(True) 86 | self.logger.info("accepted %s:%s", h, p) 87 | self.clients.add(sock) 88 | self._accept_method(sock) 89 | 90 | def _accept_method(self, sock): 91 | """this method should start a thread, fork a child process, or 92 | anything else in order to serve the client. once the mechanism has 93 | been created, it should invoke _authenticate_and_serve_client with 94 | `sock` as the argument""" 95 | raise NotImplementedError 96 | 97 | def _authenticate_and_serve_client(self, sock): 98 | try: 99 | if self.authenticator: 100 | h, p = sock.getpeername() 101 | try: 102 | sock, credentials = self.authenticator(sock) 103 | except AuthenticationError: 104 | self.logger.info("%s:%s failed to authenticate, rejecting connection", h, p) 105 | return 106 | else: 107 | self.logger.info("%s:%s authenticated successfully", h, p) 108 | else: 109 | credentials = None 110 | self._serve_client(sock, credentials) 111 | finally: 112 | try: 113 | sock.shutdown(socket.SHUT_RDWR) 114 | except Exception: 115 | pass 116 | sock.close() 117 | self.clients.discard(sock) 118 | 119 | def _serve_client(self, sock, credentials): 120 | h, p = sock.getpeername() 121 | self.logger.info("welcome %s:%s", h, p) 122 | try: 123 | config = dict(self.protocol_config, credentials = credentials) 124 | conn = Connection(self.service, Channel(SocketStream(sock)), 125 | config = config, _lazy = True) 126 | conn._init_service() 127 | conn.serve_all() 128 | finally: 129 | self.logger.info("goodbye %s:%s", h, p) 130 | 131 | def _bg_register(self): 132 | interval = self.registrar.REREGISTER_INTERVAL 133 | self.logger.info("started background auto-register thread " 134 | "(interval = %s)", interval) 135 | tnext = 0 136 | try: 137 | while self.active: 138 | t = time.time() 139 | if t >= tnext: 140 | tnext = t + interval 141 | try: 142 | self.registrar.register(self.service.get_service_aliases(), 143 | self.port) 144 | except Exception: 145 | self.logger.traceback() 146 | time.sleep(1) 147 | finally: 148 | if not self._closed: 149 | self.logger.info("background auto-register thread finished") 150 | 151 | def start(self): 152 | """starts the server. use close() to stop""" 153 | self.listener.listen(self.backlog) 154 | h, p = self.listener.getsockname() 155 | self.logger.info("server started on %s:%s", h, p) 156 | self.active = True 157 | if self.auto_register: 158 | t = threading.Thread(target = self._bg_register) 159 | t.setDaemon(True) 160 | t.start() 161 | #if sys.platform == "win32": 162 | # hack so we can receive Ctrl+C on windows 163 | self.listener.settimeout(0.5) 164 | try: 165 | try: 166 | while True: 167 | self.accept() 168 | except EOFError: 169 | pass # server closed by another thread 170 | except KeyboardInterrupt: 171 | print 172 | self.logger.warn("keyboard interrupt!") 173 | finally: 174 | self.logger.info("server has terminated") 175 | self.close() 176 | 177 | 178 | class ThreadedServer(Server): 179 | def _get_logger(self): 180 | return Logger(self.service.get_service_name(), show_tid = True) 181 | 182 | def _accept_method(self, sock): 183 | t = threading.Thread(target = self._authenticate_and_serve_client, args = (sock,)) 184 | t.setDaemon(True) 185 | t.start() 186 | 187 | 188 | class ForkingServer(Server): 189 | def __init__(self, *args, **kwargs): 190 | Server.__init__(self, *args, **kwargs) 191 | # setup sigchld handler 192 | self._prevhandler = signal.signal(signal.SIGCHLD, self._handle_sigchld) 193 | 194 | def close(self): 195 | Server.close(self) 196 | signal.signal(signal.SIGCHLD, self._prevhandler) 197 | 198 | def _get_logger(self): 199 | return Logger(self.service.get_service_name(), show_pid = True) 200 | 201 | @staticmethod 202 | def _handle_sigchld(signum, unused): 203 | try: 204 | while True: 205 | os.waitpid(-1, os.WNOHANG) 206 | except OSError: 207 | pass 208 | # re-register signal handler (see man signal(2), under Portability) 209 | signal.signal(signal.SIGCHLD, self._handle_sigchld) 210 | 211 | def _accept_method(self, sock): 212 | pid = os.fork() 213 | if pid == 0: 214 | # child 215 | try: 216 | try: 217 | self.logger.info("child process created") 218 | signal.signal(signal.SIGCHLD, self._prevhandler) 219 | self.listener.close() 220 | self.clients.clear() 221 | self._authenticate_and_serve_client(sock) 222 | except: 223 | self.logger.traceback() 224 | finally: 225 | self.logger.info("child terminated") 226 | os._exit(0) 227 | else: 228 | # parent 229 | sock.close() 230 | 231 | 232 | 233 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /lib/rpyc/utils/classic.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import inspect 4 | import pdb 5 | import cPickle as pickle 6 | import rpyc 7 | from rpyc import SlaveService 8 | from rpyc.utils import factory 9 | 10 | 11 | SERVER_FILE = os.path.join(os.path.dirname(rpyc.__file__), "servers", "classic_server.py") 12 | DEFAULT_SERVER_PORT = 18812 13 | 14 | 15 | #=============================================================================== 16 | # connecting 17 | #=============================================================================== 18 | def connect_channel(channel): 19 | return factory.connect_channel(channel, SlaveService) 20 | 21 | def connect_stream(stream): 22 | return factory.connect_stream(stream, SlaveService) 23 | 24 | def connect_stdpipes(): 25 | return factory.connect_stdpipes(SlaveService) 26 | 27 | def connect_pipes(input, output): 28 | return factory.connect_pipes(input, output, SlaveService) 29 | 30 | def connect(host, port = DEFAULT_SERVER_PORT): 31 | """creates a socket connection to the given host and port""" 32 | return factory.connect(host, port, SlaveService) 33 | 34 | def tls_connect(host, username, password, port = DEFAULT_SERVER_PORT): 35 | """creates a secure (TLS) socket connection to the given host and port, 36 | authenticating with the given username and password""" 37 | return factory.tls_connect(host, port, username, password, SlaveService) 38 | 39 | def connect_subproc(): 40 | """runs an rpyc classic server as a subprocess and return an rpyc 41 | connection to it""" 42 | return factory.connect_subproc(["python", "-u", SERVER_FILE, "-q", "-m", "stdio"], 43 | SlaveService) 44 | 45 | def connect_thread(): 46 | """starts a SlaveService on a thread and connects to it""" 47 | return factory.connect_thread(SlaveService, remote_service = SlaveService) 48 | 49 | 50 | #=============================================================================== 51 | # remoting utilities 52 | #=============================================================================== 53 | def upload(conn, localpath, remotepath, filter = None, ignore_invalid = False): 54 | """uploads a file or a directory to the given remote path 55 | localpath - the local file or directory 56 | remotepath - the remote path 57 | filter - a predicate that accepts the filename and determines whether 58 | it should be uploaded; None means any file""" 59 | if os.path.isdir(localpath): 60 | upload_dir(conn, localpath, remotepath, filter) 61 | elif os.path.isfile(localpath): 62 | upload_file(conn, localpath, remotepath) 63 | else: 64 | if not ignore_invalid: 65 | raise ValueError("cannot upload %r" % (localpath,)) 66 | 67 | def upload_file(conn, localpath, remotepath): 68 | lf = open(localpath, "rb") 69 | rf = conn.modules.__builtin__.open(remotepath, "wb") 70 | while True: 71 | buf = lf.read(16000) 72 | if not buf: 73 | break 74 | rf.write(buf) 75 | lf.close() 76 | rf.close() 77 | 78 | def upload_dir(conn, localpath, remotepath, filter = None): 79 | if not conn.modules.os.path.isdir(remotepath): 80 | conn.modules.os.makedirs(remotepath) 81 | for fn in os.listdir(localpath): 82 | if not filter or filter(fn): 83 | lfn = os.path.join(localpath, fn) 84 | rfn = conn.modules.os.path.join(remotepath, fn) 85 | upload(conn, lfn, rfn, filter = filter, ignore_invalid = True) 86 | 87 | def download(conn, remotepath, localpath, filter = None, ignore_invalid = False): 88 | """download a file or a directory to the given remote path 89 | localpath - the local file or directory 90 | remotepath - the remote path 91 | filter - a predicate that accepts the filename and determines whether 92 | it should be downloaded; None means any file""" 93 | if conn.modules.os.path.isdir(remotepath): 94 | download_dir(conn, remotepath, localpath, filter) 95 | elif conn.modules.os.path.isfile(remotepath): 96 | download_file(conn, remotepath, localpath) 97 | else: 98 | if not ignore_invalid: 99 | raise ValueError("cannot download %r" % (remotepath,)) 100 | 101 | def download_file(conn, remotepath, localpath): 102 | rf = conn.modules.__builtin__.open(remotepath, "rb") 103 | lf = open(localpath, "wb") 104 | while True: 105 | buf = rf.read(16000) 106 | if not buf: 107 | break 108 | lf.write(buf) 109 | lf.close() 110 | rf.close() 111 | 112 | def download_dir(conn, remotepath, localpath, filter = None): 113 | if not os.path.isdir(localpath): 114 | os.makedirs(localpath) 115 | for fn in conn.modules.os.listdir(remotepath): 116 | if not filter or filter(fn): 117 | rfn = conn.modules.os.path.join(remotepath, fn) 118 | lfn = os.path.join(localpath, fn) 119 | download(conn, rfn, lfn, filter = filter, ignore_invalid = True) 120 | 121 | def upload_package(conn, module, remotepath = None): 122 | """uploads a module or a package to the remote party""" 123 | if remotepath is None: 124 | site = conn.modules["distutils.sysconfig"].get_python_lib() 125 | remotepath = conn.modules.os.path.join(site, module.__name__) 126 | localpath = os.path.dirname(inspect.getsourcefile(module)) 127 | upload(conn, localpath, remotepath) 128 | 129 | upload_module = upload_package 130 | 131 | def update_module(conn, module): 132 | """replaces a module on the remote party""" 133 | rmodule = conn.modules[module.__name__] 134 | lf = inspect.getsourcefile(module) 135 | rf = conn.modules.inspect.getsourcefile(rmodule) 136 | upload_file(conn, lf, rf) 137 | c.modules.__builtin__.reload(rmodule) 138 | 139 | def obtain(proxy): 140 | """obtains (recreates) a remote object proxy from the other party. 141 | the object is moved by *value*, so changes made to it will not reflect 142 | on the remote object""" 143 | return pickle.loads(pickle.dumps(proxy)) 144 | 145 | def deliver(conn, localobj): 146 | """delivers (recreates) a local object on the other party. the object is 147 | moved by *value*, so changes made to it will not reflect on the local 148 | object. returns a proxy to the remote object""" 149 | return conn.modules.cPickle.loads(pickle.dumps(localobj)) 150 | 151 | class redirected_stdio(object): 152 | """redirects the other party's stdin, stdout and stderr to those of the 153 | local party, so remote STDIO will occur locally""" 154 | def __init__(self, conn): 155 | self._restored = True 156 | self.conn = conn 157 | self.orig_stdin = self.conn.modules.sys.stdin 158 | self.orig_stdout = self.conn.modules.sys.stdout 159 | self.orig_stderr = self.conn.modules.sys.stderr 160 | self.conn.modules.sys.stdin = sys.stdin 161 | self.conn.modules.sys.stdout = sys.stdout 162 | self.conn.modules.sys.stderr = sys.stderr 163 | self._restored = False 164 | def __del__(self): 165 | self.restore() 166 | def restore(self): 167 | if self._restored: 168 | return 169 | self._restored = True 170 | self.conn.modules.sys.stdin = self.orig_stdin 171 | self.conn.modules.sys.stdout = self.orig_stdout 172 | self.conn.modules.sys.stderr = self.orig_stderr 173 | def __enter__(self): 174 | return self 175 | def __exit__(self, t, v, tb): 176 | self.restore() 177 | 178 | #== compatibility with python 2.4 == 179 | #@contextmanager 180 | #def redirected_stdio(conn): 181 | # orig_stdin = conn.modules.sys.stdin 182 | # orig_stdout = conn.modules.sys.stdout 183 | # orig_stderr = conn.modules.sys.stderr 184 | # try: 185 | # conn.modules.sys.stdin = sys.stdin 186 | # conn.modules.sys.stdout = sys.stdout 187 | # conn.modules.sys.stderr = sys.stderr 188 | # yield 189 | # finally: 190 | # conn.modules.sys.stdin = orig_stdin 191 | # conn.modules.sys.stdout = orig_stdout 192 | # conn.modules.sys.stderr = orig_stderr 193 | 194 | def pm(conn): 195 | """pdb.pm on a remote exception""" 196 | #pdb.post_mortem(conn.root.getconn()._last_traceback) 197 | redir = redirected_stdio(conn) 198 | try: 199 | conn.modules.pdb.post_mortem(conn.root.getconn()._last_traceback) 200 | finally: 201 | redir.restore() 202 | 203 | def interact(conn, namespace = None): 204 | """remote interactive interpreter""" 205 | if namespace is None: 206 | namespace = {} 207 | namespace["conn"] = conn 208 | redir = redirected_stdio(conn) 209 | try: 210 | conn.execute("""def _rinteract(ns): 211 | import code 212 | code.interact(local = dict(ns))""") 213 | conn.namespace["_rinteract"](namespace) 214 | finally: 215 | redir.restore() 216 | 217 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /HComMaya/HComMayaClient.py: -------------------------------------------------------------------------------- 1 | import rpyc 2 | import sys 3 | import getpass 4 | import os 5 | 6 | import maya.cmds as cmds 7 | import maya.utils as mUtils 8 | 9 | from PySide import QtGui 10 | 11 | import threading 12 | 13 | from _globals import MayaGlobals 14 | 15 | import HComMayaUi 16 | import HComMayaUtils 17 | import HComMayaWidgets 18 | 19 | global server_conn 20 | server_conn = None 21 | 22 | global server_id 23 | server_id = None 24 | 25 | global bgsrv 26 | bgsrv = None 27 | 28 | # Decorator tor send new data to client 29 | # into a new thread 30 | def threaded_sendata(func): 31 | 32 | def wrapper(*args, **kwargs): 33 | t = threading.Thread(target=func, args=args, kwargs=kwargs) 34 | t.start() 35 | 36 | return wrapper 37 | 38 | class HCom_ClientService(rpyc.Service): 39 | ''' 40 | This is the client service called to fetch data from server. 41 | ''' 42 | 43 | def on_disconnect(self): 44 | server_is_disconnected() 45 | 46 | def exposed_catchData(self, dataType, sender, data, tabTarget, clientType): 47 | 48 | HComMayaUi.receiveData(sender, data, dataType, tabTarget, clientType) 49 | 50 | def exposed_sendIDUpdate(self, ID, action, clientType): 51 | 52 | HComMayaUi.receiveIDUpdate(ID, action, clientType) 53 | 54 | 55 | def connectToServer(ID=None, clientType="NONE"): 56 | ''' 57 | Try to connect to the server and launch the BG thread service 58 | ''' 59 | 60 | if not ID: 61 | ID = getpass.getuser() 62 | 63 | global server_conn 64 | global bgsrv 65 | 66 | try: 67 | server_conn = rpyc.connect(HComMayaUtils.readIni()["SERVER"].replace(" ",""), int(str(HComMayaUtils.readIni()["PORT"]).replace(" ","")), service=HCom_ClientService, config={"allow_pickle":True}) 68 | except Exception as e: 69 | print("ERROR: Can not connect to server: " + str(e)) 70 | return False, False 71 | else: 72 | if ID in server_conn.root.getAllClients().keys(): 73 | ask = QtGui.QMessageBox() 74 | ask.setText("User ID already registered on the server") 75 | ask.setIcon(QtGui.QMessageBox.Critical) 76 | ask.exec_() 77 | server_conn.close() 78 | return False 79 | 80 | MayaGlobals.HCOMCLIENT = [server_conn, ID] 81 | 82 | global server_id 83 | server_id = ID 84 | 85 | bgsrv = rpyc.BgServingThread(server_conn) 86 | result = server_conn.root.registerClient(ID, clientType) 87 | 88 | if result: 89 | return ID 90 | else: 91 | return False 92 | 93 | def _sendData(target_clientID, sender, data, datatype, tabTarget): 94 | ''' 95 | Send data the to target client, could be message, otl or settings. 96 | ''' 97 | 98 | global server_conn 99 | 100 | if not server_conn: 101 | print("ERROR: Client is not connected.") 102 | return False 103 | 104 | try: 105 | setDataAsync = rpyc.async(server_conn.root.sendDataToClient) 106 | result = setDataAsync(target_clientID, datatype, sender, data, tabTarget) 107 | return result 108 | except AttributeError: 109 | return False 110 | print("ERROR: client " + target_clientID + " not found.") 111 | 112 | @threaded_sendata 113 | def sendMessage(target_clientID, sender, message, tabTarget): 114 | 115 | result = _sendData(target_clientID, sender, message, "msg", tabTarget) 116 | return result 117 | 118 | def sendAlembic(target_clientID, sender, tabTarget): 119 | 120 | result = mUtils.executeInMainThreadWithResult(_exportAlembic) 121 | if not result: 122 | return False 123 | 124 | fileName = result[0] 125 | filePath = result[1] 126 | frameRange = result[2] 127 | 128 | with open(filePath, 'rb') as f: 129 | data = f.read() 130 | 131 | outData = {} 132 | outData["TYPE"] = "alembic_cache" 133 | outData["NAME"] = fileName 134 | outData["FRAME_RANGE"] = frameRange 135 | outData["DATA"] = data 136 | 137 | try: 138 | os.remove(filePath) 139 | except: 140 | pass 141 | 142 | result = _sendData(target_clientID, sender, outData, "alembic", tabTarget) 143 | 144 | return result 145 | 146 | def sendSettings(target_clientID, sender, tabTarget): 147 | return 148 | 149 | def sendOtl(target_clientID, sender, tabTarget): 150 | return 151 | 152 | def sendBgeo(target_clientID, sender, tabTarget, isObj=False): 153 | return 154 | 155 | def sendObjMesh(target_clientID, sender, tabTarget): 156 | 157 | meshOut = {} 158 | meshOut["MESH_TYPE"] = ".obj" 159 | 160 | meshName, objtmp = mUtils.executeInMainThreadWithResult(_exportObj) 161 | 162 | if not meshName: 163 | return False 164 | 165 | if not objtmp: 166 | return False 167 | 168 | with open(objtmp, 'rb') as f: 169 | meshOut["MESH_DATA"] = f.read() 170 | 171 | try: 172 | os.remove(objtmp) 173 | except: 174 | pass 175 | 176 | meshOut["MESH_TYPE"] = ".obj" 177 | meshOut["MESH_NAME"] = meshName 178 | 179 | result = _sendData(target_clientID, sender, meshOut, "mesh", tabTarget) 180 | 181 | return result 182 | 183 | def _exportAlembic(): 184 | 185 | if not "AbcExport" in cmds.pluginInfo( query=True, listPlugins=True ): 186 | ask = QtGui.QMessageBox() 187 | ask.setText("Error: Alembic export plugin not loaded (AbcExport) !") 188 | ask.setIcon(QtGui.QMessageBox.Warning) 189 | ask.exec_() 190 | return False 191 | 192 | selection = cmds.ls(sl=True) 193 | if not selection: 194 | ask = QtGui.QMessageBox() 195 | ask.setText("Error: Nothing selected !") 196 | ask.setIcon(QtGui.QMessageBox.Warning) 197 | ask.exec_() 198 | return False 199 | 200 | name = str(selection[0]).replace("|", "_") + ".abc" 201 | abcFile = HComMayaUtils.fetchMyReceivedFilesFolder() + os.sep + name 202 | abcFile = HComMayaUtils.incrementFile(abcFile) 203 | start = cmds.playbackOptions(query=True, minTime=True) 204 | end = cmds.playbackOptions(query=True, maxTime=True) 205 | 206 | frameRangeUi = HComMayaWidgets.FrameRangeSelection(start=start, end=end) 207 | frameRangeUi.exec_() 208 | 209 | if not frameRangeUi.VALID: 210 | return False 211 | 212 | else: 213 | frames = frameRangeUi.frameRange 214 | 215 | cmd = "-fr {0} {1} -f {2}".format(frames[0], frames[1], abcFile) 216 | cmds.AbcExport(sl=True, j=cmd) 217 | 218 | if os.path.exists(abcFile): 219 | return name, abcFile, frames 220 | 221 | return False 222 | 223 | def _exportObj(): 224 | 225 | selection = cmds.ls(sl=True) 226 | if not selection: 227 | ask = QtGui.QMessageBox() 228 | ask.setText("Error: Nothing selected !") 229 | ask.setIcon(QtGui.QMessageBox.Warning) 230 | ask.exec_() 231 | return False, False 232 | 233 | meshName = str(selection[0]).replace("|", "_") 234 | 235 | objtmp = HComMayaUtils.fetchMyReceivedFilesFolder() + os.sep + meshName + "_tmp.obj" 236 | objtmp = HComMayaUtils.incrementFile(objtmp) 237 | 238 | try: 239 | cmds.file(objtmp, force=True, type="OBJexport", es=True, shader=False, ) 240 | except Exception as e: 241 | print("ERROR: " + str(e)) 242 | return False, False 243 | 244 | return meshName, objtmp 245 | 246 | 247 | 248 | def sendPic(target_clientID, _sender, tabTarget, imagePath): 249 | 250 | with open(imagePath, 'rb') as f: 251 | imageData = f.read() 252 | 253 | outImdageData = {} 254 | outImdageData["IMAGE_NAME"] = os.path.basename(imagePath) 255 | outImdageData["BINARY_DATA"] = imageData 256 | 257 | result = _sendData(target_clientID, _sender, outImdageData, "pic", tabTarget) 258 | if result: 259 | return True 260 | else: 261 | return False 262 | 263 | def sendDataReceivedInfo(targetClient, sender, data, tabTarget): 264 | 265 | _sendData(targetClient, sender, data, "dataReceivedUpdate", tabTarget) 266 | 267 | 268 | def getAllClientRegistred(): 269 | 270 | global server_conn 271 | 272 | if not server_conn: 273 | print("ERROR: Client is not connected.") 274 | return False 275 | 276 | return server_conn.root.getAllClients().keys() 277 | 278 | def disconnect(): 279 | ''' 280 | Disconect client and stop BG thread 281 | ''' 282 | 283 | global server_conn 284 | global bgsrv 285 | global server_id 286 | 287 | if not server_conn: 288 | return 289 | try: 290 | bgsrv.stop() 291 | bgsrv = None 292 | except: 293 | pass 294 | 295 | try: 296 | server_conn.root.removeClient(server_id) 297 | except EOFError: 298 | pass 299 | 300 | try: 301 | server_conn.close() 302 | 303 | except: 304 | pass 305 | server_conn = None 306 | MayaGlobals.MAIN_UI.updateUiThread.data = {"ACTION":"server_disconnect", "ID":None, "CLIENT_TYPE":None} 307 | 308 | 309 | def server_is_disconnected(): 310 | disconnect() -------------------------------------------------------------------------------- /HComNuke/HComNukeClient.py: -------------------------------------------------------------------------------- 1 | import rpyc 2 | import sys 3 | import getpass 4 | import os 5 | import random 6 | import tempfile 7 | 8 | import nuke 9 | 10 | 11 | from PySide import QtGui 12 | 13 | import threading 14 | 15 | from _globals import NukeGlobals 16 | import HComNukeUtils 17 | import HComNukeUi 18 | 19 | global server_conn 20 | server_conn = None 21 | 22 | global server_id 23 | server_id = None 24 | 25 | global bgsrv 26 | bgsrv = None 27 | 28 | # Decorator tor send new data to client 29 | # into a new thread 30 | def threaded_sendata(func): 31 | 32 | def wrapper(*args, **kwargs): 33 | t = threading.Thread(target=func, args=args, kwargs=kwargs) 34 | t.start() 35 | 36 | return wrapper 37 | 38 | class HCom_ClientService(rpyc.Service): 39 | ''' 40 | This is the client service called to fetch data from server. 41 | ''' 42 | 43 | def on_disconnect(self): 44 | server_is_disconnected() 45 | 46 | def exposed_catchData(self, dataType, sender, data, tabTarget, clientType): 47 | 48 | HComNukeUi.receiveData(sender, data, dataType, tabTarget, clientType) 49 | 50 | def exposed_sendIDUpdate(self, ID, action, clientType): 51 | 52 | HComNukeUi.receiveIDUpdate(ID, action, clientType) 53 | 54 | 55 | def connectToServer(ID=None, clientType="NONE"): 56 | ''' 57 | Try to connect to the server and launch the BG thread service 58 | ''' 59 | 60 | if not ID: 61 | ID = getpass.getuser() 62 | 63 | global server_conn 64 | global bgsrv 65 | 66 | try: 67 | server_conn = rpyc.connect(HComNukeUtils.readIni()["SERVER"].replace(" ",""), int(str(HComNukeUtils.readIni()["PORT"]).replace(" ","")), service=HCom_ClientService, config={"allow_pickle":True}) 68 | except Exception as e: 69 | print("ERROR: Can not connect to server: " + str(e)) 70 | return False, False 71 | else: 72 | if ID in server_conn.root.getAllClients().keys(): 73 | ask = QtGui.QMessageBox() 74 | ask.setText("User ID already registered on the server") 75 | ask.setIcon(QtGui.QMessageBox.Critical) 76 | ask.exec_() 77 | server_conn.close() 78 | return False 79 | 80 | NukeGlobals.HCOMCLIENT = [server_conn, ID] 81 | 82 | global server_id 83 | server_id = ID 84 | 85 | bgsrv = rpyc.BgServingThread(server_conn) 86 | result = server_conn.root.registerClient(ID, clientType) 87 | 88 | if result: 89 | return ID 90 | else: 91 | return False 92 | 93 | def _sendData(target_clientID, sender, data, datatype, tabTarget): 94 | ''' 95 | Send data the to target client, could be message, otl or settings. 96 | ''' 97 | 98 | global server_conn 99 | 100 | if not server_conn: 101 | print("ERROR: Client is not connected.") 102 | return False 103 | 104 | try: 105 | setDataAsync = rpyc.async(server_conn.root.sendDataToClient) 106 | result = setDataAsync(target_clientID, datatype, sender, data, tabTarget) 107 | return result 108 | except AttributeError: 109 | return False 110 | print("ERROR: client " + target_clientID + " not found.") 111 | 112 | @threaded_sendata 113 | def sendMessage(target_clientID, sender, message, tabTarget): 114 | 115 | result = _sendData(target_clientID, sender, message, "msg", tabTarget) 116 | return result 117 | 118 | def sendNodeOuput(target_clientID, sender, tabTarget): 119 | 120 | fileType = HComNukeUtils.readIni()["OUTPUT_IMAGE_FORMAT"] 121 | 122 | try: 123 | selectedNode = nuke.selectedNode() 124 | except ValueError: 125 | return False 126 | 127 | curFrame = int(nuke.knob("frame")) 128 | tmpfilePath = tempfile.gettempdir() + "\\hcom_tmp_" + ''.join([random.choice("abcdef0123456789") for n in xrange(5)]) + "." + fileType 129 | tmpWriterName = "tmp_writer_" + ''.join([random.choice("abcdef0123456789") for n in xrange(5)]) 130 | 131 | result = nuke.executeInMainThreadWithResult(_createWriter, args=(tmpWriterName, tmpfilePath, selectedNode, curFrame)) 132 | 133 | if not result: 134 | return False 135 | 136 | with open(tmpfilePath, 'rb') as f: 137 | data = f.read() 138 | 139 | outImdageData = {} 140 | outImdageData["IMAGE_NAME"] = os.path.basename(selectedNode.name()) + "." + fileType 141 | outImdageData["BINARY_DATA"] = data 142 | 143 | try: 144 | os.remove(tmpfilePath) 145 | except: 146 | pass 147 | 148 | w = nuke.toNode(tmpWriterName) 149 | if w: 150 | nuke.delete(w) 151 | 152 | result = _sendData(target_clientID, sender, outImdageData, "pic", tabTarget) 153 | 154 | return result 155 | 156 | def _createWriter(name, filePath, selectedNode, curFrame): 157 | 158 | n = nuke.createNode('Write', inpanel=False) 159 | n.setName(name) 160 | n["file"].setValue(filePath.replace("\\", "/")) 161 | n.setInput(0, selectedNode) 162 | 163 | try: 164 | nuke.execute(name, curFrame, curFrame, 1) 165 | except RuntimeError as e: 166 | print str(e) 167 | return False 168 | 169 | return True 170 | 171 | def sendSettings(target_clientID, sender, tabTarget): 172 | return 173 | 174 | 175 | def sendObjMesh(target_clientID, sender, tabTarget, alembic=False, frames=[0,0]): 176 | 177 | try: 178 | selectedNode = nuke.selectedNode() 179 | except ValueError: 180 | return False 181 | 182 | meshOut = {} 183 | 184 | geoType = ".obj" 185 | if alembic: 186 | geoType = ".abc" 187 | 188 | curFrame = int(nuke.knob("frame")) 189 | tmpfilePath = tempfile.gettempdir() + "\\hcom_tmp_" + ''.join([random.choice("abcdef0123456789") for n in xrange(5)]) + geoType 190 | tmpWriterName = "tmp_geoWriter_" + ''.join([random.choice("abcdef0123456789") for n in xrange(5)]) 191 | 192 | result = nuke.executeInMainThreadWithResult(_createObjWriter, args=(tmpWriterName, tmpfilePath, selectedNode, curFrame, alembic, frames)) 193 | 194 | if not result: 195 | return False 196 | 197 | 198 | with open(tmpfilePath, 'rb') as f: 199 | data = f.read() 200 | 201 | 202 | try: 203 | os.remove(tmpfilePath) 204 | except: 205 | pass 206 | 207 | meshOut["MESH_TYPE"] = geoType 208 | meshOut["MESH_NAME"] = selectedNode.name() 209 | meshOut["MESH_DATA"] = data 210 | 211 | w = nuke.toNode(tmpWriterName) 212 | if w: 213 | nuke.delete(w) 214 | 215 | outType = "mesh" 216 | if alembic: 217 | outType = "alembic" 218 | meshOut["NAME"]= selectedNode.name() 219 | meshOut["FRAME_RANGE"] = frames 220 | meshOut["DATA"]= data 221 | 222 | result = _sendData(target_clientID, sender, meshOut, outType, tabTarget) 223 | 224 | return result 225 | 226 | def _createObjWriter(tmpWriterName, tmpfilePath, selectedNode, curFrame, alembic, frames): 227 | 228 | w = nuke.createNode('WriteGeo', inpanel=False) 229 | w.setName(tmpWriterName) 230 | w["file"].setValue(tmpfilePath.replace("\\", "/")) 231 | w.setInput(0, selectedNode) 232 | 233 | try: 234 | 235 | if alembic: 236 | w["use_limit"].setValue(True) 237 | w["first"].setValue(int(frames[0])) 238 | w["last"].setValue(int(frames[1])) 239 | w["file_type"].setValue(1) 240 | nuke.execute(tmpWriterName, int(frames[0]), int(frames[1]), 1) 241 | else: 242 | w["file_type"].setValue(3) 243 | nuke.execute(tmpWriterName, curFrame, curFrame, 1) 244 | 245 | except RuntimeError as e: 246 | 247 | print str(e) 248 | return False 249 | 250 | return True 251 | 252 | 253 | def sendPic(target_clientID, sender, tabTarget, imagePath): 254 | 255 | with open(imagePath, 'rb') as f: 256 | imageData = f.read() 257 | 258 | outImdageData = {} 259 | outImdageData["IMAGE_NAME"] = os.path.basename(imagePath) 260 | outImdageData["BINARY_DATA"] = imageData 261 | 262 | result = _sendData(target_clientID, sender, outImdageData, "pic", tabTarget) 263 | if result: 264 | return True 265 | else: 266 | return False 267 | 268 | def sendDataReceivedInfo(targetClient, sender, data, tabTarget): 269 | 270 | _sendData(targetClient, sender, data, "dataReceivedUpdate", tabTarget) 271 | 272 | 273 | def getAllClientRegistred(): 274 | 275 | global server_conn 276 | 277 | if not server_conn: 278 | print("ERROR: Client is not connected.") 279 | return False 280 | 281 | return server_conn.root.getAllClients().keys() 282 | 283 | def disconnect(): 284 | ''' 285 | Disconect client and stop BG thread 286 | ''' 287 | 288 | global server_conn 289 | global bgsrv 290 | global server_id 291 | 292 | if not server_conn: 293 | return 294 | try: 295 | bgsrv.stop() 296 | bgsrv = None 297 | except: 298 | pass 299 | 300 | try: 301 | server_conn.root.removeClient(server_id) 302 | except EOFError: 303 | pass 304 | 305 | try: 306 | server_conn.close() 307 | 308 | except: 309 | pass 310 | server_conn = None 311 | NukeGlobals.MAIN_UI.updateUiThread.data = {"ACTION":"server_disconnect", "ID":None, "CLIENT_TYPE":None} 312 | 313 | 314 | def server_is_disconnected(): 315 | disconnect() -------------------------------------------------------------------------------- /lib/rpyc/core/brine.py: -------------------------------------------------------------------------------- 1 | """ 2 | brine - a simple, fast and secure object serializer for immutable objects, 3 | optimized for small integers [-48..160). 4 | the following types are supported: int, long, bool, str, float, unicode, 5 | slice, complex, tuple(of simple types), forzenset(of simple types) 6 | as well as the following singletons: None, NotImplemented, Ellipsis 7 | """ 8 | from cStringIO import StringIO 9 | from rpyc.utils.lib import Struct, all 10 | 11 | 12 | # singletons 13 | TAG_NONE = "\x00" 14 | TAG_EMPTY_STR = "\x01" 15 | TAG_EMPTY_TUPLE = "\x02" 16 | TAG_TRUE = "\x03" 17 | TAG_FALSE = "\x04" 18 | TAG_NOT_IMPLEMENTED = "\x05" 19 | TAG_ELLIPSIS = "\x06" 20 | # types 21 | TAG_UNICODE = "\x08" 22 | TAG_LONG = "\x09" 23 | TAG_STR1 = "\x0a" 24 | TAG_STR2 = "\x0b" 25 | TAG_STR3 = "\x0c" 26 | TAG_STR4 = "\x0d" 27 | TAG_STR_L1 = "\x0e" 28 | TAG_STR_L4 = "\x0f" 29 | TAG_TUP1 = "\x10" 30 | TAG_TUP2 = "\x11" 31 | TAG_TUP3 = "\x12" 32 | TAG_TUP4 = "\x13" 33 | TAG_TUP_L1 = "\x14" 34 | TAG_TUP_L4 = "\x15" 35 | TAG_INT_L1 = "\x16" 36 | TAG_INT_L4 = "\x17" 37 | TAG_FLOAT = "\x18" 38 | TAG_SLICE = "\x19" 39 | TAG_FSET = "\x1a" 40 | TAG_COMPLEX = "\x1b" 41 | IMM_INTS = dict((i, chr(i + 0x50)) for i in range(-0x30, 0xa0)) 42 | 43 | I1 = Struct("!B") 44 | I4 = Struct("!L") 45 | F8 = Struct("!d") 46 | C16 = Struct("!dd") 47 | 48 | _dump_registry = {} 49 | _load_registry = {} 50 | IMM_INTS_LOADER = dict((v, k) for k, v in IMM_INTS.iteritems()) 51 | 52 | def register(coll, key): 53 | def deco(func): 54 | coll[key] = func 55 | return func 56 | return deco 57 | 58 | #=============================================================================== 59 | # dumping 60 | #=============================================================================== 61 | @register(_dump_registry, type(None)) 62 | def _dump_none(obj, stream): 63 | stream.append(TAG_NONE) 64 | 65 | @register(_dump_registry, type(NotImplemented)) 66 | def _dump_notimplemeted(obj, stream): 67 | stream.append(TAG_NOT_IMPLEMENTED) 68 | 69 | @register(_dump_registry, type(Ellipsis)) 70 | def _dump_ellipsis(obj, stream): 71 | stream.append(TAG_ELLIPSIS) 72 | 73 | @register(_dump_registry, bool) 74 | def _dump_bool(obj, stream): 75 | if obj: 76 | stream.append(TAG_TRUE) 77 | else: 78 | stream.append(TAG_FALSE) 79 | 80 | @register(_dump_registry, slice) 81 | def _dump_slice(obj, stream): 82 | stream.append(TAG_SLICE) 83 | _dump((obj.start, obj.stop, obj.step), stream) 84 | 85 | @register(_dump_registry, frozenset) 86 | def _dump_frozenset(obj, stream): 87 | stream.append(TAG_FSET) 88 | _dump(tuple(obj), stream) 89 | 90 | @register(_dump_registry, int) 91 | def _dump_int(obj, stream): 92 | if obj in IMM_INTS: 93 | stream.append(IMM_INTS[obj]) 94 | else: 95 | obj = str(obj) 96 | l = len(obj) 97 | if l < 256: 98 | stream.append(TAG_INT_L1 + I1.pack(l) + obj) 99 | else: 100 | stream.append(TAG_INT_L4 + I4.pack(l) + obj) 101 | 102 | @register(_dump_registry, long) 103 | def _dump_long(obj, stream): 104 | stream.append(TAG_LONG) 105 | _dump_int(obj, stream) 106 | 107 | @register(_dump_registry, str) 108 | def _dump_str(obj, stream): 109 | l = len(obj) 110 | if l == 0: 111 | stream.append(TAG_EMPTY_STR) 112 | elif l == 1: 113 | stream.append(TAG_STR1 + obj) 114 | elif l == 2: 115 | stream.append(TAG_STR2 + obj) 116 | elif l == 3: 117 | stream.append(TAG_STR3 + obj) 118 | elif l == 4: 119 | stream.append(TAG_STR4 + obj) 120 | elif l < 256: 121 | stream.append(TAG_STR_L1 + I1.pack(l) + obj) 122 | else: 123 | stream.append(TAG_STR_L4 + I4.pack(l) + obj) 124 | 125 | @register(_dump_registry, float) 126 | def _dump_float(obj, stream): 127 | stream.append(TAG_FLOAT + F8.pack(obj)) 128 | 129 | @register(_dump_registry, complex) 130 | def _dump_complex(obj, stream): 131 | stream.append(TAG_COMPLEX + C16.pack(obj.real, obj.imag)) 132 | 133 | @register(_dump_registry, unicode) 134 | def _dump_unicode(obj, stream): 135 | stream.append(TAG_UNICODE) 136 | _dump_str(obj.encode("utf8"), stream) 137 | 138 | @register(_dump_registry, tuple) 139 | def _dump_tuple(obj, stream): 140 | l = len(obj) 141 | if l == 0: 142 | stream.append(TAG_EMPTY_TUPLE) 143 | elif l == 1: 144 | stream.append(TAG_TUP1) 145 | elif l == 2: 146 | stream.append(TAG_TUP2) 147 | elif l == 3: 148 | stream.append(TAG_TUP3) 149 | elif l == 4: 150 | stream.append(TAG_TUP4) 151 | elif l < 256: 152 | stream.append(TAG_TUP_L1 + I1.pack(l)) 153 | else: 154 | stream.append(TAG_TUP_L4 + I4.pack(l)) 155 | for item in obj: 156 | _dump(item, stream) 157 | 158 | def _undumpable(obj, stream): 159 | raise TypeError("cannot dump %r" % (obj,)) 160 | 161 | def _dump(obj, stream): 162 | _dump_registry.get(type(obj), _undumpable)(obj, stream) 163 | 164 | #=============================================================================== 165 | # loading 166 | #=============================================================================== 167 | @register(_load_registry, TAG_NONE) 168 | def _load_none(stream): 169 | return None 170 | @register(_load_registry, TAG_NOT_IMPLEMENTED) 171 | def _load_nonimp(stream): 172 | return NotImplemented 173 | @register(_load_registry, TAG_ELLIPSIS) 174 | def _load_elipsis(stream): 175 | return Ellipsis 176 | @register(_load_registry, TAG_TRUE) 177 | def _load_true(stream): 178 | return True 179 | @register(_load_registry, TAG_FALSE) 180 | def _load_false(stream): 181 | return False 182 | @register(_load_registry, TAG_EMPTY_TUPLE) 183 | def _load_empty_tuple(stream): 184 | return () 185 | @register(_load_registry, TAG_EMPTY_STR) 186 | def _load_empty_str(stream): 187 | return "" 188 | @register(_load_registry, TAG_UNICODE) 189 | def _load_unicode(stream): 190 | obj = _load(stream) 191 | return obj.decode("utf-8") 192 | @register(_load_registry, TAG_LONG) 193 | def _load_long(stream): 194 | obj = _load(stream) 195 | return long(obj) 196 | 197 | @register(_load_registry, TAG_FLOAT) 198 | def _load_float(stream): 199 | return F8.unpack(stream.read(8))[0] 200 | @register(_load_registry, TAG_COMPLEX) 201 | def _load_complex(stream): 202 | real, imag = C16.unpack(stream.read(16)) 203 | return complex(real, imag) 204 | 205 | @register(_load_registry, TAG_STR1) 206 | def _load_str1(stream): 207 | return stream.read(1) 208 | @register(_load_registry, TAG_STR2) 209 | def _load_str2(stream): 210 | return stream.read(2) 211 | @register(_load_registry, TAG_STR3) 212 | def _load_str3(stream): 213 | return stream.read(3) 214 | @register(_load_registry, TAG_STR4) 215 | def _load_str4(stream): 216 | return stream.read(4) 217 | @register(_load_registry, TAG_STR_L1) 218 | def _load_str_l1(stream): 219 | l, = I1.unpack(stream.read(1)) 220 | return stream.read(l) 221 | @register(_load_registry, TAG_STR_L4) 222 | def _load_str_l4(stream): 223 | l, = I4.unpack(stream.read(4)) 224 | return stream.read(l) 225 | 226 | @register(_load_registry, TAG_TUP1) 227 | def _load_tup1(stream): 228 | return (_load(stream),) 229 | @register(_load_registry, TAG_TUP2) 230 | def _load_tup2(stream): 231 | return (_load(stream), _load(stream)) 232 | @register(_load_registry, TAG_TUP3) 233 | def _load_tup3(stream): 234 | return (_load(stream), _load(stream), _load(stream)) 235 | @register(_load_registry, TAG_TUP4) 236 | def _load_tup4(stream): 237 | return (_load(stream), _load(stream), _load(stream), _load(stream)) 238 | @register(_load_registry, TAG_TUP_L1) 239 | def _load_tup_l1(stream): 240 | l, = I1.unpack(stream.read(1)) 241 | return tuple(_load(stream) for i in range(l)) 242 | @register(_load_registry, TAG_TUP_L4) 243 | def _load_tup_l4(stream): 244 | l, = I4.unpack(stream.read(4)) 245 | return tuple(_load(stream) for i in xrange(l)) 246 | 247 | @register(_load_registry, TAG_SLICE) 248 | def _load_slice(stream): 249 | start, stop, step = _load(stream) 250 | return slice(start, stop, step) 251 | @register(_load_registry, TAG_FSET) 252 | def _load_frozenset(stream): 253 | return frozenset(_load(stream)) 254 | 255 | @register(_load_registry, TAG_INT_L1) 256 | def _load_int_l1(stream): 257 | l, = I1.unpack(stream.read(1)) 258 | return int(stream.read(l)) 259 | @register(_load_registry, TAG_INT_L4) 260 | def _load_int_l4(stream): 261 | l, = I4.unpack(stream.read(4)) 262 | return int(stream.read(l)) 263 | 264 | def _load(stream): 265 | tag = stream.read(1) 266 | if tag in IMM_INTS_LOADER: 267 | return IMM_INTS_LOADER[tag] 268 | return _load_registry.get(tag)(stream) 269 | 270 | #=============================================================================== 271 | # API 272 | #=============================================================================== 273 | def dump(obj): 274 | """dumps the given object to a byte-string representation""" 275 | stream = [] 276 | _dump(obj, stream) 277 | return "".join(stream) 278 | 279 | def load(data): 280 | """loads the given byte-string representation to an object""" 281 | stream = StringIO(data) 282 | return _load(stream) 283 | 284 | simple_types = frozenset([type(None), int, long, bool, str, float, unicode, 285 | slice, complex, type(NotImplemented), type(Ellipsis)]) 286 | def dumpable(obj): 287 | """indicates whether the object is dumpable by brine""" 288 | if type(obj) in simple_types: 289 | return True 290 | if type(obj) in (tuple, frozenset): 291 | return all(dumpable(item) for item in obj) 292 | return False 293 | 294 | 295 | if __name__ == "__main__": 296 | x = ("he", 7, u"llo", 8, (), 900, None, True, Ellipsis, 18.2, 18.2j + 13, 297 | slice(1,2,3), frozenset([5,6,7]), NotImplemented) 298 | assert dumpable(x) 299 | y = dump(x) 300 | z = load(y) 301 | assert x == z 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | -------------------------------------------------------------------------------- /lib/rpyc/core/stream.py: -------------------------------------------------------------------------------- 1 | """ 2 | abstraction layer over OS-depenedent byte streams 3 | """ 4 | import sys 5 | import os 6 | import socket 7 | import time 8 | import errno 9 | from select import select 10 | from rpyc.utils.lib import safe_import 11 | win32file = safe_import("win32file") 12 | win32pipe = safe_import("win32pipe") 13 | msvcrt = safe_import("msvcrt") 14 | 15 | 16 | retry_errnos = set([errno.EAGAIN]) 17 | if hasattr(errno, "WSAEWOULDBLOCK"): 18 | retry_errnos.add(errno.WSAEWOULDBLOCK) 19 | 20 | 21 | class Stream(object): 22 | __slots__ = () 23 | def close(self): 24 | raise NotImplementedError() 25 | @property 26 | def closed(self): 27 | raise NotImplementedError() 28 | def fileno(self): 29 | raise NotImplementedError() 30 | def poll(self, timeout): 31 | """indicate whether the stream has data to read""" 32 | rl, wl, xl = select([self], [], [], timeout) 33 | return bool(rl) 34 | def read(self, count): 35 | """read exactly `count` bytes, or raise EOFError""" 36 | raise NotImplementedError() 37 | def write(self, data): 38 | """write the entire `data`, or raise EOFError""" 39 | raise NotImplementedError() 40 | 41 | 42 | class ClosedFile(object): 43 | """represents a closed file object (singleton)""" 44 | __slots__ = () 45 | def __getattr__(self, name): 46 | raise EOFError("stream has been closed") 47 | def close(self): 48 | pass 49 | @property 50 | def closed(self): 51 | return True 52 | def fileno(self): 53 | raise EOFError("stream has been closed") 54 | ClosedFile = ClosedFile() 55 | 56 | 57 | class SocketStream(Stream): 58 | __slots__ = ("sock",) 59 | MAX_IO_CHUNK = 8000 60 | def __init__(self, sock): 61 | self.sock = sock 62 | @classmethod 63 | def _connect(cls, host, port, family = socket.AF_INET, type = socket.SOCK_STREAM, 64 | proto = 0, timeout = 3): 65 | s = socket.socket(family, type, proto) 66 | s.settimeout(timeout) 67 | s.connect((host, port)) 68 | return s 69 | @classmethod 70 | def connect(cls, host, port, **kwargs): 71 | return cls(cls._connect(host, port, **kwargs)) 72 | @classmethod 73 | def tls_connect(cls, host, port, username, password, **kwargs): 74 | from tlslite.api import TLSConnection 75 | s = cls._connect(host, port, **kwargs) 76 | s2 = TLSConnection(s) 77 | s2.fileno = lambda fd=s.fileno(): fd 78 | s2.handshakeClientSRP(username, password) 79 | return cls(s2) 80 | @property 81 | def closed(self): 82 | return self.sock is ClosedFile 83 | def close(self): 84 | if not self.closed: 85 | try: 86 | self.sock.shutdown(socket.SHUT_RDWR) 87 | except Exception: 88 | pass 89 | self.sock.close() 90 | self.sock = ClosedFile 91 | def fileno(self): 92 | return self.sock.fileno() 93 | def read(self, count): 94 | data = [] 95 | while count > 0: 96 | try: 97 | buf = self.sock.recv(min(self.MAX_IO_CHUNK, count)) 98 | except socket.timeout: 99 | continue 100 | except socket.error, ex: 101 | if ex[0] in retry_errnos: 102 | # windows just has to be a bitch 103 | continue 104 | self.close() 105 | raise EOFError(ex) 106 | if not buf: 107 | self.close() 108 | raise EOFError("connection closed by peer") 109 | data.append(buf) 110 | count -= len(buf) 111 | return "".join(data) 112 | def write(self, data): 113 | try: 114 | while data: 115 | count = self.sock.send(data[:self.MAX_IO_CHUNK]) 116 | data = data[count:] 117 | except socket.error, ex: 118 | self.close() 119 | raise EOFError(ex) 120 | 121 | class PipeStream(Stream): 122 | __slots__ = ("incoming", "outgoing") 123 | MAX_IO_CHUNK = 32000 124 | def __init__(self, incoming, outgoing): 125 | outgoing.flush() 126 | self.incoming = incoming 127 | self.outgoing = outgoing 128 | @classmethod 129 | def from_std(cls): 130 | return cls(sys.stdin, sys.stdout) 131 | @classmethod 132 | def create_pair(cls): 133 | r1, w1 = os.pipe() 134 | r2, w2 = os.pipe() 135 | side1 = cls(os.fdopen(r1, "rb"), os.fdopen(w2, "wb")) 136 | side2 = cls(os.fdopen(r2, "rb"), os.fdopen(w1, "wb")) 137 | return side1, side2 138 | @property 139 | def closed(self): 140 | return self.incoming is ClosedFile 141 | def close(self): 142 | self.incoming.close() 143 | self.outgoing.close() 144 | self.incoming = ClosedFile 145 | self.outgoing = ClosedFile 146 | def fileno(self): 147 | return self.incoming.fileno() 148 | def read(self, count): 149 | data = [] 150 | try: 151 | while count > 0: 152 | buf = os.read(self.incoming.fileno(), min(self.MAX_IO_CHUNK, count)) 153 | if not buf: 154 | raise EOFError("connection closed by peer") 155 | data.append(buf) 156 | count -= len(buf) 157 | except EOFError: 158 | self.close() 159 | raise 160 | except EnvironmentError, ex: 161 | self.close() 162 | raise EOFError(ex) 163 | return "".join(data) 164 | def write(self, data): 165 | try: 166 | while data: 167 | chunk = data[:self.MAX_IO_CHUNK] 168 | written = os.write(self.outgoing.fileno(), chunk) 169 | data = data[written:] 170 | except EnvironmentError, ex: 171 | self.close() 172 | raise EOFError(ex) 173 | 174 | 175 | class Win32PipeStream(Stream): 176 | """win32 has to suck""" 177 | __slots__ = ("incoming", "outgoing", "_fileno") 178 | PIPE_BUFFER_SIZE = 130000 179 | MAX_IO_CHUNK = 32000 180 | 181 | def __init__(self, incoming, outgoing): 182 | if hasattr(incoming, "fileno"): 183 | self._fileno = incoming.fileno() 184 | incoming = msvcrt.get_osfhandle(incoming.fileno()) 185 | if hasattr(outgoing, "fileno"): 186 | outgoing = msvcrt.get_osfhandle(outgoing.fileno()) 187 | self.incoming = incoming 188 | self.outgoing = outgoing 189 | @classmethod 190 | def from_std(cls): 191 | return cls(sys.stdin, sys.stdout) 192 | @classmethod 193 | def create_pair(cls): 194 | r1, w1 = win32pipe.CreatePipe(None, cls.PIPE_BUFFER_SIZE) 195 | r2, w2 = win32pipe.CreatePipe(None, cls.PIPE_BUFFER_SIZE) 196 | return cls(r1, w2), cls(r2, w1) 197 | 198 | def fileno(self): 199 | return self._fileno 200 | @property 201 | def closed(self): 202 | return self.incoming is ClosedFile 203 | def close(self): 204 | if self.closed: 205 | return 206 | win32file.CloseHandle(self.incoming) 207 | win32file.CloseHandle(self.outgoing) 208 | self.incoming = ClosedFile 209 | self.outgoing = ClosedFile 210 | def read(self, count): 211 | try: 212 | data = [] 213 | while count > 0: 214 | dummy, buf = win32file.ReadFile(self.incoming, min(self.MAX_IO_CHUNK, count)) 215 | count -= len(buf) 216 | data.append(buf) 217 | except TypeError, ex: 218 | if not self.closed: 219 | raise 220 | raise EOFError(ex) 221 | except win32file.error, ex: 222 | self.close() 223 | raise EOFError(ex) 224 | return "".join(data) 225 | def write(self, data): 226 | try: 227 | while data: 228 | dummy, count = win32file.WriteFile(self.outgoing, data[:self.MAX_IO_CHUNK]) 229 | data = data[count:] 230 | except TypeError, ex: 231 | if not self.closed: 232 | raise 233 | raise EOFError(ex) 234 | except win32file.error, ex: 235 | self.close() 236 | raise EOFError(ex) 237 | 238 | def poll(self, timeout, interval = 0.1): 239 | """a poor man's version of select()""" 240 | if timeout is None: 241 | timeout = sys.maxint 242 | length = 0 243 | tmax = time.time() + timeout 244 | try: 245 | while length == 0: 246 | length = win32pipe.PeekNamedPipe(self.incoming, 0)[1] 247 | if time.time() >= tmax: 248 | break 249 | time.sleep(interval) 250 | except TypeError, ex: 251 | if not self.closed: 252 | raise 253 | raise EOFError(ex) 254 | return length != 0 255 | 256 | 257 | class NamedPipeStream(Win32PipeStream): 258 | NAMED_PIPE_PREFIX = r'\\.\pipe\rpyc_' 259 | PIPE_IO_TIMEOUT = 3 260 | CONNECT_TIMEOUT = 3 261 | __slots__ = ("is_server_side",) 262 | 263 | def __init__(self, handle, is_server_side): 264 | Win32PipeStream.__init__(self, handle, handle) 265 | self.is_server_side = is_server_side 266 | @classmethod 267 | def from_std(cls): 268 | raise NotImplementedError() 269 | @classmethod 270 | def create_pair(cls): 271 | raise NotImplementedError() 272 | 273 | @classmethod 274 | def create_server(cls, pipename, connect = True): 275 | if not pipename.startswith("\\\\."): 276 | pipename = cls.NAMED_PIPE_PREFIX + pipename 277 | handle = win32pipe.CreateNamedPipe( 278 | pipename, 279 | win32pipe.PIPE_ACCESS_DUPLEX, 280 | win32pipe.PIPE_TYPE_BYTE | win32pipe.PIPE_READMODE_BYTE | win32pipe.PIPE_WAIT, 281 | 1, 282 | cls.PIPE_BUFFER_SIZE, 283 | cls.PIPE_BUFFER_SIZE, 284 | cls.PIPE_IO_TIMEOUT * 1000, 285 | None 286 | ) 287 | inst = cls(handle, True) 288 | if connect: 289 | inst.connect_server() 290 | return inst 291 | 292 | def connect_server(self): 293 | if not self.is_server_side: 294 | raise ValueError("this must be the server side") 295 | win32pipe.ConnectNamedPipe(self.incoming, None) 296 | 297 | @classmethod 298 | def create_client(cls, pipename, timeout = CONNECT_TIMEOUT): 299 | if not pipename.startswith("\\\\."): 300 | pipename = cls.NAMED_PIPE_PREFIX + pipename 301 | handle = win32file.CreateFile( 302 | pipename, 303 | win32file.GENERIC_READ | win32file.GENERIC_WRITE, 304 | 0, 305 | None, 306 | win32file.OPEN_EXISTING, 307 | 0, 308 | None 309 | ) 310 | return cls(handle, False) 311 | 312 | def close(self): 313 | if self.closed: 314 | return 315 | if self.is_server_side: 316 | win32file.FlushFileBuffers(self.outgoing) 317 | win32pipe.DisconnectNamedPipe(self.outgoing) 318 | Win32PipeStream.close(self) 319 | 320 | 321 | if sys.platform == "win32": 322 | PipeStream = Win32PipeStream 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | -------------------------------------------------------------------------------- /HComHoudini/HComHoudiniClient.py: -------------------------------------------------------------------------------- 1 | import hou 2 | 3 | import rpyc 4 | import sys 5 | import getpass 6 | import os 7 | 8 | import threading 9 | import HComHoudiniUi 10 | import HComHoudiniUtils 11 | 12 | pysidePath = os.environ["PYTHONHOME"] + r"lib\site-packages-forced" 13 | if not pysidePath in sys.path: 14 | sys.path.append(pysidePath) 15 | 16 | global server_conn 17 | server_conn = None 18 | 19 | global server_id 20 | server_id = None 21 | 22 | global bgsrv 23 | bgsrv = None 24 | 25 | # Decorator tor send new data to client 26 | # into a new thread 27 | def threaded_sendata(func): 28 | 29 | def wrapper(*args, **kwargs): 30 | t = threading.Thread(target=func, args=args, kwargs=kwargs) 31 | t.start() 32 | 33 | return wrapper 34 | 35 | class HCom_ClientService(rpyc.Service): 36 | ''' 37 | This is the client service called to fetch data from server. 38 | ''' 39 | 40 | def on_disconnect(self): 41 | server_is_disconnected() 42 | 43 | def exposed_catchData(self, dataType, _sender, data, tabTarget, clientType): 44 | 45 | HComHoudiniUi.receiveData(_sender, data, dataType, tabTarget, clientType) 46 | 47 | def exposed_sendIDUpdate(self, ID, action, clientType): 48 | 49 | HComHoudiniUi.receiveIDUpdate(ID, action, clientType) 50 | 51 | 52 | def connectToServer(ID=None, clientType="NONE"): 53 | ''' 54 | Try to connect to the server and launch the BG thread service 55 | ''' 56 | 57 | if not ID: 58 | ID = getpass.getuser() 59 | 60 | global server_conn 61 | global bgsrv 62 | 63 | try: 64 | server_conn = rpyc.connect(HComHoudiniUtils.readIni()["SERVER"], int(HComHoudiniUtils.readIni()["PORT"]), service=HCom_ClientService, config={"allow_pickle":True}) 65 | except Exception as e: 66 | print("ERROR: Can not connect to server: " + str(e)) 67 | return False, False 68 | else: 69 | if ID in server_conn.root.getAllClients().keys(): 70 | hou.ui.displayMessage("User ID already registered on the server") 71 | server_conn.close() 72 | return False 73 | 74 | hou.session.HCOMCLIENT = [server_conn, ID] 75 | 76 | global server_id 77 | server_id = ID 78 | 79 | bgsrv = rpyc.BgServingThread(server_conn) 80 | result = server_conn.root.registerClient(ID, clientType) 81 | 82 | if result: 83 | return ID 84 | else: 85 | return False 86 | 87 | def _sendData(target_clientID, sender, data, datatype, tabTarget): 88 | ''' 89 | Send data the to target client, could be message, otl or settings. 90 | ''' 91 | 92 | global server_conn 93 | 94 | if not server_conn: 95 | print("ERROR: Client is not connected.") 96 | return False 97 | 98 | try: 99 | setDataAsync = rpyc.async(server_conn.root.sendDataToClient) 100 | result = setDataAsync(target_clientID, datatype, sender, data, tabTarget) 101 | return result 102 | except AttributeError: 103 | return False 104 | print("ERROR: client " + target_clientID + " not found.") 105 | 106 | @threaded_sendata 107 | def sendMessage(target_clientID, sender, message, tabTarget): 108 | 109 | result = _sendData(target_clientID, sender, message, "msg", tabTarget) 110 | return result 111 | 112 | 113 | def sendSettings(target_clientID, sender, tabTarget, tabClientType=None): 114 | 115 | settingsData = {} 116 | 117 | n = hou.selectedNodes() 118 | if not n: 119 | hou.ui.displayMessage("Nothing is selected") 120 | return False 121 | sel = n[0] 122 | 123 | settingsData["OTL_TYPE"] = sel.type().name() 124 | 125 | parmDict = {} 126 | parms = sel.parms() 127 | for p in parms: 128 | parmDict[p.name()] = p.eval() 129 | 130 | settingsData["OTL_PARMS"] = parmDict 131 | 132 | result = _sendData(target_clientID, sender, settingsData, "settings", tabTarget) 133 | return [result, settingsData["OTL_TYPE"] + " settings"] 134 | 135 | 136 | def sendOtl(target_clientID, sender, tabTarget, tabClientType=None): 137 | 138 | n = hou.selectedNodes() 139 | if not n: 140 | hou.ui.displayMessage("Nothing is selected") 141 | return False 142 | sel = n[0] 143 | 144 | if tabClientType[0] != HComHoudiniUtils.CLIENT_TYPE.HOUDINI: 145 | if not sel.type().definition(): 146 | hou.ui.displayMessage("Invalid node") 147 | return False 148 | 149 | else: 150 | if hou.expandString("$HH") in sel.type().definition().libraryFilePath(): 151 | hou.ui.displayMessage("Invalid node") 152 | return False 153 | 154 | parentType = "" 155 | if sel.__class__ == hou.SopNode: 156 | parentType = "geo" 157 | 158 | elif sel.__class__ == hou.ObjNode: 159 | parentType = "obj" 160 | 161 | elif sel.__class__ == hou.ShopNode: 162 | parentType = "shop" 163 | 164 | elif sel.__class__ == hou.CopNode: 165 | parentType = "cop" 166 | 167 | elif sel.__class__ == hou.DopNode: 168 | parentType = "dop" 169 | 170 | elif sel.__class__ == hou.RopNode: 171 | parentType = "rop" 172 | 173 | elif sel.__class__ == hou.VopNode: 174 | 175 | parentType = "vop;" 176 | parentType += sel.parent().type().name() + ";" 177 | 178 | if sel.parent().__class__ == hou.SopNode: 179 | parentType += "sop" 180 | 181 | elif sel.parent().__class__ == hou.ShopNode: 182 | parentType += "material" 183 | 184 | elif sel.parent().__class__ == hou.CopNode: 185 | parentType += "cop" 186 | 187 | else: 188 | hou.ui.displayMessage("The current node type is not supported by HCom yet.") 189 | return False 190 | 191 | else: 192 | hou.ui.displayMessage("The current node type is not supported by HCom yet.") 193 | return False 194 | 195 | otlData = {} 196 | otlData["OTL_PARENT_TYPE"] = parentType 197 | otlData["PY_CODE"] = sel.asCode(recurse=True) 198 | otlData["OTL_NAME"] = sel.name() 199 | otlData["OTL_TYPE"] = sel.type().name() 200 | otlData["OTL_ALL_LIBS"] = HComHoudiniUtils.getAllLib(sel) 201 | 202 | result = _sendData(target_clientID, sender, otlData, "otl", tabTarget) 203 | if result: 204 | return True 205 | else: 206 | return False 207 | 208 | def sendAlembic(target_clientID, sender, tabTarget, tabClientType=None): 209 | 210 | selection = hou.selectedNodes() 211 | if not selection: 212 | hou.ui.displayMessage("Nothing is selected") 213 | return False 214 | 215 | selection = selection[0] 216 | if not selection.__class__ == hou.ObjNode: 217 | hou.ui.displayMessage("Selection must be a geo node") 218 | return False 219 | 220 | start = 1 221 | end = 100 222 | cancelled = False 223 | inputValid = False 224 | while not inputValid: 225 | 226 | pickFrameUI = hou.ui.readMultiInput("Enter a frame range:", 227 | ["Start Frame", "End Frame"], 228 | buttons = ["Ok", "Cancel"], 229 | initial_contents=(hou.expandString("$FSTART"), hou.expandString("$FEND")), 230 | title="Pick Frame Range", 231 | help="Must be two integers") 232 | if pickFrameUI[0] == 1: 233 | cancelled = True 234 | inputValid = True 235 | else: 236 | start = pickFrameUI[1][0] 237 | end = pickFrameUI[1][1] 238 | 239 | if not start.isdigit() or not end.isdigit(): 240 | inputValid = False 241 | 242 | else: 243 | if int(start) > int(end): 244 | inputValid = False 245 | else: 246 | start = int(start) 247 | end = int(end) 248 | inputValid = True 249 | 250 | if cancelled: 251 | return False 252 | 253 | name = selection.name() 254 | frames = [start, end] 255 | 256 | nodePath = selection.path() 257 | tmpGeo = hou.node("/obj").createNode("geo", run_init_scripts=False) 258 | tmpGeo.setName('alembic_tmp_exporter', unique_name=True) 259 | 260 | objectMerge = tmpGeo.createNode("object_merge") 261 | objectMerge.parm("objpath1").set(nodePath) 262 | 263 | alembicExport = tmpGeo.createNode("rop_alembic") 264 | alembicExport.parm("trange").set(1) 265 | alembicExport.parm("f1").deleteAllKeyframes() 266 | alembicExport.parm("f1").set(int(frames[0])) 267 | alembicExport.parm("f2").deleteAllKeyframes() 268 | alembicExport.parm("f2").set(int(frames[1])) 269 | 270 | alembicExport.parm("save_attributes").set(0) 271 | 272 | alembicExport.setInput(0, objectMerge) 273 | 274 | outFile = HComHoudiniUtils.fetchMyReceivedFilesFolder() + os.sep + name + "_tmpCacheAlembic.abc" 275 | 276 | alembicExport.parm("filename").set(outFile) 277 | alembicExport.render() 278 | 279 | tmpGeo.destroy() 280 | 281 | with open(outFile, 'rb') as f: 282 | data = f.read() 283 | 284 | # Clean tmp file 285 | try: 286 | os.remove(outFile) 287 | except: 288 | pass 289 | 290 | outDic = {} 291 | outDic["NAME"] = name 292 | outDic["FRAME_RANGE"] = frames 293 | outDic["DATA"]= data 294 | 295 | result = _sendData(target_clientID, sender, outDic, "alembic", tabTarget) 296 | if result: 297 | return True 298 | else: 299 | return False 300 | 301 | 302 | def sendBgeo(target_clientID, sender, tabTarget, isObj=False, tabClientType=None): 303 | 304 | n = hou.selectedNodes() 305 | if not n: 306 | hou.ui.displayMessage("Nothing is selected") 307 | return False 308 | sel = n[0] 309 | 310 | if sel.__class__ != hou.SopNode: 311 | hou.ui.displayMessage("Node selected is not a sop node") 312 | return False 313 | 314 | geo = sel.geometry() 315 | 316 | if isObj: 317 | if not geo.points(): 318 | hou.ui.displayMessage("No points found on geometry") 319 | return False 320 | 321 | fileType = ".bgeo" 322 | if isObj: 323 | fileType = ".obj" 324 | 325 | # Dump geo on disk in a tmp file if data() not supported by houdini's version 326 | # If it is an obj file it must pass by saveToFile() methode 327 | if hasattr(geo, "data") and not isObj: 328 | binaryData = geo.data() 329 | else: 330 | tmpFile = hou.expandString("$HOME") + "/" + "tmphcom__" + fileType 331 | geo.saveToFile(tmpFile) 332 | binaryData = "" 333 | with open(tmpFile, 'rb') as f: 334 | binaryData = f.read() 335 | os.remove(tmpFile) 336 | 337 | meshData = {} 338 | meshData["MESH_TYPE"] = fileType 339 | meshData["MESH_NAME"] = sel.name() 340 | meshData["MESH_DATA"] = binaryData 341 | 342 | result = _sendData(target_clientID, sender, meshData, "mesh", tabTarget) 343 | if result: 344 | return True 345 | else: 346 | return False 347 | 348 | 349 | def sendObjMesh(target_clientID, sender, tabTarget, tabClientType=None): 350 | result = sendBgeo(target_clientID, sender, tabTarget, isObj=True) 351 | return result 352 | 353 | 354 | def sendPic(target_clientID, _sender, tabTarget, imagePath, tabClientType=None): 355 | 356 | with open(imagePath, 'rb') as f: 357 | imageData = f.read() 358 | 359 | outImdageData = {} 360 | outImdageData["IMAGE_NAME"] = os.path.basename(imagePath) 361 | outImdageData["BINARY_DATA"] = imageData 362 | 363 | result = _sendData(target_clientID, _sender, outImdageData, "pic", tabTarget) 364 | if result: 365 | return True 366 | else: 367 | return False 368 | 369 | def sendDataReceivedInfo(targetClient, sender, data, tabTarget): 370 | 371 | _sendData(targetClient, sender, data, "dataReceivedUpdate", tabTarget) 372 | 373 | 374 | def getAllClientRegistred(): 375 | 376 | global server_conn 377 | 378 | if not server_conn: 379 | print("ERROR: Client is not connected.") 380 | return False 381 | 382 | return server_conn.root.getAllClients().keys() 383 | 384 | def disconnect(): 385 | ''' 386 | Disconect client and stop BG thread 387 | ''' 388 | 389 | global server_conn 390 | global bgsrv 391 | global server_id 392 | 393 | if not server_conn: 394 | return 395 | try: 396 | bgsrv.stop() 397 | bgsrv = None 398 | except: 399 | pass 400 | 401 | try: 402 | server_conn.root.removeClient(server_id) 403 | except EOFError: 404 | pass 405 | 406 | try: 407 | server_conn.close() 408 | 409 | except: 410 | pass 411 | server_conn = None 412 | 413 | HComHoudiniUi.HComMainUi.updateUiThread.data = {"ACTION":"server_disconnect", "ID":None, "CLIENT_TYPE":None} 414 | 415 | 416 | def server_is_disconnected(): 417 | disconnect() -------------------------------------------------------------------------------- /lib/rpyc/utils/registry.py: -------------------------------------------------------------------------------- 1 | """ 2 | rpyc registry server implementation 3 | """ 4 | import sys 5 | import socket 6 | import time 7 | from rpyc.core import brine 8 | from rpyc.utils.logger import Logger 9 | 10 | 11 | DEFAULT_PRUNING_TIMEOUT = 4 * 60 12 | MAX_DGRAM_SIZE = 1500 13 | REGISTRY_PORT = 18811 14 | 15 | 16 | #------------------------------------------------------------------------------ 17 | # servers 18 | #------------------------------------------------------------------------------ 19 | 20 | class RegistryServer(object): 21 | def __init__(self, listenersock, pruning_timeout = None, logger = None): 22 | self.sock = listenersock 23 | self.active = False 24 | self.services = {} 25 | if pruning_timeout is None: 26 | pruning_timeout = DEFAULT_PRUNING_TIMEOUT 27 | self.pruning_timeout = pruning_timeout 28 | if logger is None: 29 | logger = Logger("REGSRV") 30 | self.logger = logger 31 | 32 | def on_service_added(self, name, addrinfo): 33 | """called when a new service joins the registry (but on keepalives). 34 | override this to add custom logic""" 35 | 36 | def on_service_removed(self, name, addrinfo): 37 | """called when a service unregisters or is pruned. 38 | override this to add custom logic""" 39 | 40 | def add_service(self, name, addrinfo): 41 | if name not in self.services: 42 | self.services[name] = {} 43 | is_new = addrinfo not in self.services 44 | self.services[name][addrinfo] = time.time() 45 | if is_new: 46 | try: 47 | self.on_service_added(name, addrinfo) 48 | except Exception: 49 | self.logger.traceback() 50 | 51 | def remove_service(self, name, addrinfo): 52 | self.services[name].pop(addrinfo, None) 53 | if not self.services[name]: 54 | del self.services[name] 55 | try: 56 | self.on_service_removed(name, addrinfo) 57 | except Exception: 58 | self.logger.traceback() 59 | 60 | def cmd_query(self, host, name): 61 | name = name.upper() 62 | self.logger.debug("querying for %r", name) 63 | if name not in self.services: 64 | self.logger.debug("no such service") 65 | return () 66 | 67 | oldest = time.time() - self.pruning_timeout 68 | all_servers = sorted(self.services[name].items(), key = lambda x: x[1]) 69 | servers = [] 70 | for addrinfo, t in all_servers: 71 | if t < oldest: 72 | self.logger.debug("discarding stale %s:%s", *addrinfo) 73 | self.remove_service(name, addrinfo) 74 | else: 75 | servers.append(addrinfo) 76 | 77 | self.logger.debug("replying with %r", servers) 78 | return tuple(servers) 79 | 80 | def cmd_register(self, host, names, port): 81 | self.logger.debug("registering %s:%s as %s", host, port, ", ".join(names)) 82 | for name in names: 83 | self.add_service(name.upper(), (host, port)) 84 | return "OK" 85 | 86 | def cmd_unregister(self, host, port): 87 | self.logger.debug("unregistering %s:%s", host, port) 88 | for name in self.services.keys(): 89 | self.remove_service(name, (host, port)) 90 | return "OK" 91 | 92 | def _recv(self): 93 | raise NotImplementedError() 94 | 95 | def _send(self, data, addrinfo): 96 | raise NotImplementedError() 97 | 98 | def _work(self): 99 | while self.active: 100 | try: 101 | data, addrinfo = self._recv() 102 | except (socket.error, socket.timeout): 103 | continue 104 | try: 105 | magic, cmd, args = brine.load(data) 106 | except Exception: 107 | continue 108 | if magic != "RPYC": 109 | self.logger.warn("invalid magic: %r", magic) 110 | continue 111 | cmdfunc = getattr(self, "cmd_%s" % (cmd.lower(),), None) 112 | if not cmdfunc: 113 | self.logger.warn("unknown command: %r", cmd) 114 | continue 115 | 116 | try: 117 | reply = cmdfunc(addrinfo[0], *args) 118 | except Exception: 119 | self.logger.traceback() 120 | else: 121 | self._send(brine.dump(reply), addrinfo) 122 | 123 | def start(self): 124 | if self.active: 125 | raise ValueError("server is already running") 126 | if self.sock is None: 127 | raise ValueError("object disposed") 128 | self.logger.debug("server started on %s:%s", *self.sock.getsockname()) 129 | try: 130 | try: 131 | self.active = True 132 | self._work() 133 | except KeyboardInterrupt: 134 | self.logger.warn("User interrupt!") 135 | finally: 136 | self.active = False 137 | self.logger.debug("server closed") 138 | self.sock.close() 139 | self.sock = None 140 | 141 | def close(self): 142 | if not self.active: 143 | raise ValueError("server is not running") 144 | self.logger.debug("stopping server...") 145 | self.active = False 146 | 147 | class UDPRegistryServer(RegistryServer): 148 | def __init__(self, host = "0.0.0.0", port = REGISTRY_PORT, 149 | pruning_timeout = None, logger = None): 150 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 151 | sock.bind((host, port)) 152 | sock.settimeout(0.5) 153 | super(UDPRegistryServer, self).__init__(sock, 154 | pruning_timeout = pruning_timeout, logger = logger) 155 | 156 | def _recv(self): 157 | return self.sock.recvfrom(MAX_DGRAM_SIZE) 158 | 159 | def _send(self, data, addrinfo): 160 | try: 161 | self.sock.sendto(data, addrinfo) 162 | except (socket.error, socket.timeout): 163 | pass 164 | 165 | class TCPRegistryServer(RegistryServer): 166 | def __init__(self, host = "0.0.0.0", port = REGISTRY_PORT, 167 | pruning_timeout = None, logger = None, reuse_addr = True): 168 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 169 | if reuse_addr and sys.platform != "win32": 170 | # warning: reuseaddr is not what you expect on windows! 171 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 172 | sock.bind((host, port)) 173 | sock.listen(10) 174 | sock.settimeout(0.5) 175 | super(TCPRegistryServer, self).__init__(sock, 176 | pruning_timeout = pruning_timeout, logger = logger) 177 | self._connected_sockets = {} 178 | 179 | def _recv(self): 180 | sock2 = self.sock.accept()[0] 181 | addrinfo = sock2.getpeername() 182 | data = sock2.recv(MAX_DGRAM_SIZE) 183 | self._connected_sockets[addrinfo] = sock2 184 | return data, addrinfo 185 | 186 | def _send(self, data, addrinfo): 187 | sock2 = self._connected_sockets.pop(addrinfo) 188 | try: 189 | sock2.send(data) 190 | except (socket.error, socket.timeout): 191 | pass 192 | 193 | #------------------------------------------------------------------------------ 194 | # clients (registrars) 195 | #------------------------------------------------------------------------------ 196 | class RegistryClient(object): 197 | REREGISTER_INTERVAL = 60 198 | 199 | def __init__(self, ip, port, timeout, logger = None): 200 | self.ip = ip 201 | self.port = port 202 | self.timeout = timeout 203 | if logger is None: 204 | logger = Logger("REGCLNT") 205 | self.logger = logger 206 | 207 | def discover(self, name): 208 | raise NotImplementedError 209 | 210 | def register(self, aliases, port): 211 | raise NotImplementedError 212 | 213 | def unregister(self, port): 214 | raise NotImplementedError 215 | 216 | class UDPRegistryClient(RegistryClient): 217 | def __init__(self, ip = "255.255.255.255", port = REGISTRY_PORT, timeout = 2, 218 | bcast = None, logger = None): 219 | super(UDPRegistryClient, self).__init__(ip = ip, port = port, 220 | timeout = timeout, logger = logger) 221 | if bcast is None: 222 | bcast = "255" in ip.split(".") 223 | self.bcast = bcast 224 | 225 | def discover(self, name): 226 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 227 | if self.bcast: 228 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True) 229 | data = brine.dump(("RPYC", "QUERY", (name,))) 230 | sock.sendto(data, (self.ip, self.port)) 231 | sock.settimeout(self.timeout) 232 | 233 | try: 234 | try: 235 | data, addrinfo = sock.recvfrom(MAX_DGRAM_SIZE) 236 | except (socket.error, socket.timeout): 237 | servers = () 238 | else: 239 | servers = brine.load(data) 240 | finally: 241 | sock.close() 242 | return servers 243 | 244 | def register(self, aliases, port): 245 | self.logger.info("registering on %s:%s", self.ip, self.port) 246 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 247 | if self.bcast: 248 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True) 249 | data = brine.dump(("RPYC", "REGISTER", (aliases, port))) 250 | sock.sendto(data, (self.ip, self.port)) 251 | 252 | tmax = time.time() + self.timeout 253 | while time.time() < tmax: 254 | sock.settimeout(tmax - time.time()) 255 | try: 256 | data, (rip, rport) = sock.recvfrom(MAX_DGRAM_SIZE) 257 | except socket.timeout: 258 | self.logger.warn("no registry acknowledged") 259 | break 260 | if rport != self.port: 261 | continue 262 | try: 263 | reply = brine.load(data) 264 | except Exception: 265 | continue 266 | if reply == "OK": 267 | self.logger.info("registry %s:%s acknowledged", rip, rport) 268 | break 269 | else: 270 | self.logger.warn("no registry acknowledged") 271 | sock.close() 272 | 273 | def unregister(self, port): 274 | self.logger.info("unregistering from %s:%s", self.ip, self.port) 275 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 276 | if self.bcast: 277 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True) 278 | data = brine.dump(("RPYC", "UNREGISTER", (port,))) 279 | sock.sendto(data, (self.ip, self.port)) 280 | sock.close() 281 | 282 | 283 | class TCPRegistryClient(RegistryClient): 284 | def __init__(self, ip, port = REGISTRY_PORT, timeout = 2, logger = None): 285 | super(TCPRegistryClient, self).__init__(ip = ip, port = port, 286 | timeout = timeout, logger = logger) 287 | 288 | def discover(self, name): 289 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 290 | sock.settimeout(self.timeout) 291 | data = brine.dump(("RPYC", "QUERY", (name,))) 292 | sock.connect((self.ip, self.port)) 293 | sock.send(data) 294 | 295 | try: 296 | try: 297 | data = sock.recv(MAX_DGRAM_SIZE) 298 | except (socket.error, socket.timeout): 299 | servers = () 300 | else: 301 | servers = brine.load(data) 302 | finally: 303 | sock.close() 304 | return servers 305 | 306 | def register(self, aliases, port): 307 | self.logger.info("registering on %s:%s", self.ip, self.port) 308 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 309 | sock.settimeout(self.timeout) 310 | data = brine.dump(("RPYC", "REGISTER", (aliases, port))) 311 | 312 | try: 313 | try: 314 | sock.connect((self.ip, self.port)) 315 | sock.send(data) 316 | except (socket.error, socket.timeout): 317 | self.logger.warn("could not connect to registry") 318 | return 319 | try: 320 | data = sock.recv(MAX_DGRAM_SIZE) 321 | except socket.timeout: 322 | self.logger.warn("registry did not acknowledge") 323 | return 324 | try: 325 | reply = brine.load(data) 326 | except Exception: 327 | self.logger.warn("received corrupted data from registry") 328 | return 329 | if reply == "OK": 330 | self.logger.info("registry %s:%s acknowledged", self.ip, self.port) 331 | finally: 332 | sock.close() 333 | 334 | def unregister(self, port): 335 | self.logger.info("unregistering from %s:%s", self.ip, self.port) 336 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 337 | sock.settimeout(self.timeout) 338 | data = brine.dump(("RPYC", "UNREGISTER", (port,))) 339 | try: 340 | sock.connect((self.ip, self.port)) 341 | sock.send(data) 342 | except (socket.error, socket.timeout): 343 | self.logger.warn("could not connect to registry") 344 | sock.close() 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | -------------------------------------------------------------------------------- /HComHoudini/HComHoudiniUtils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import hou 4 | import subprocess 5 | import random 6 | import threading 7 | 8 | HISTORY_FOLDER = os.path.dirname(__file__) + "\\HCom_History\\" 9 | ICONPATH = os.path.dirname(__file__) + "\\HCom_Icons\\" 10 | 11 | def readIni(): 12 | 13 | iniValues = {} 14 | 15 | ini = os.path.dirname(__file__) + "\\HCom.ini" 16 | with open(ini, 'r') as f: 17 | for i in f.readlines(): 18 | if i.startswith("#"): 19 | continue 20 | elif i == "\n": 21 | continue 22 | else: 23 | data = i.split("=") 24 | 25 | val = data[1].replace("\n", "") 26 | if val.isdigit() and "." in val: 27 | val = float(val) 28 | elif val.isdigit() and not "." in val: 29 | val = int(val) 30 | elif val.lower() == "true": 31 | val = True 32 | elif val.lower() == "false": 33 | val = False 34 | else: 35 | val = str(val) 36 | 37 | iniValues[data[0]] = val 38 | 39 | return iniValues 40 | 41 | def writeIni(settings): 42 | ini = os.path.dirname(__file__) + "\\HCom.ini" 43 | with open(ini, 'w') as f: 44 | 45 | f.write("#HCom info file\n") 46 | 47 | for k in settings.keys(): 48 | f.write(k + "=" + str(settings[k]) + "\n") 49 | 50 | def getAllLib(node): 51 | ''' 52 | Fetch all library path of sub otls and selected otl. 53 | returns a dict {lib_file:binary_data} 54 | ''' 55 | 56 | libFound = False 57 | libData = [] 58 | libNameList = [] 59 | 60 | # All sub children nodes 61 | allChildrens = list(node.allSubChildren()) 62 | allChildrens.append(node) 63 | 64 | for childrenNode in allChildrens: 65 | 66 | nodeDefinition = childrenNode.type().definition() 67 | if not nodeDefinition: 68 | continue 69 | 70 | libPath = nodeDefinition.libraryFilePath() 71 | libName = os.path.basename(libPath) 72 | 73 | if libName in libNameList: 74 | continue 75 | 76 | # Skip built-in otl 77 | if libPath.startswith(hou.expandString("$HH")): 78 | continue 79 | 80 | if not os.path.exists(libPath): 81 | continue 82 | 83 | binaryData = "" 84 | with open(libPath, 'rb') as f: 85 | binaryData = f.read() 86 | 87 | libData.append([libName, binaryData]) 88 | libNameList.append(libName) 89 | libFound = True 90 | 91 | if libFound: 92 | return libData 93 | return False 94 | 95 | def writeHistory(sender, timeStamp, data): 96 | 97 | historyFile = HISTORY_FOLDER + sender.lower() + "_history.txt" 98 | with open(historyFile, 'a') as f: 99 | f.write(timeStamp + "\n") 100 | f.write(" " + str(data) + "\n") 101 | 102 | def coloredString(string, hexColor=None, rgb=None, italic=False, bold=False): 103 | 104 | in_italic_tag = "" 105 | out_italic_tag = "" 106 | if italic: 107 | in_italic_tag = "" 108 | out_italic_tag = "" 109 | 110 | in_bold_tag = "" 111 | out_bold_tag = "" 112 | if bold: 113 | in_bold_tag = "" 114 | out_bold_tag = "" 115 | 116 | if not hexColor and not rgb: 117 | return "{0}{1}{2}{3}{4}".format(in_italic_tag, in_bold_tag, string, out_bold_tag, out_italic_tag) 118 | 119 | if hexColor: 120 | if hexColor.startswith("#"): 121 | hexColor = hexColor.replace("#", "") 122 | return "{0}{1}{2}{3}{4}".format(in_italic_tag, in_bold_tag, string, out_bold_tag, out_italic_tag, hexColor) 123 | else: 124 | return "{0}{1}{2}{3}{4}".format(in_italic_tag, in_bold_tag, string, out_bold_tag, out_italic_tag, rgb[0], rgb[1], rgb[2]) 125 | 126 | def incrementFile(filePath): 127 | 128 | if not os.path.exists(filePath): 129 | return filePath 130 | 131 | baseName = os.path.basename(filePath) 132 | name = baseName.split(".")[0] 133 | fileType = baseName.split(".")[1] 134 | dirName = os.path.dirname(filePath) 135 | 136 | fileInc = dirName + os.sep + name + "_1." + fileType 137 | i = 2 138 | while os.path.exists(fileInc): 139 | fileInc = dirName + os.sep + name + "_" + str(i) + "." + fileType 140 | 141 | return fileInc 142 | 143 | def createAlembic(data, sender="", settings=None): 144 | 145 | hou.setUpdateMode(hou.updateMode.Manual) 146 | 147 | fileName = data["NAME"] 148 | filePath = fetchMyReceivedFilesFolder() + os.sep + fileName 149 | filePath = incrementFile(filePath) 150 | 151 | with open(filePath, 'wb') as f: 152 | f.write(data["DATA"]) 153 | 154 | geo = hou.node("/obj").createNode("geo", run_init_scripts=False) 155 | geo.setName("Alembic_container_from_" + sender, unique_name=True) 156 | geo.appendComment("Alembic_container_from_" + sender) 157 | 158 | alembicNode = geo.createNode("alembic") 159 | alembicNode.parm("fileName").set(filePath) 160 | 161 | return True 162 | 163 | def createOtl(data, sender="", settings=None): 164 | 165 | nodeName = data["OTL_NAME"] 166 | parentType = data["OTL_PARENT_TYPE"] 167 | subOtlLibs = data["OTL_ALL_LIBS"] 168 | 169 | # Switch houdini to manual update according to settings 170 | if settings["SWITCH_TO_MANUAL_UPDATE"]: 171 | hou.setUpdateMode(hou.updateMode.Manual) 172 | 173 | # Check otl libs 174 | if subOtlLibs: 175 | 176 | allLoadedFiles = [os.path.basename(n) for n in hou.hda.loadedFiles()] 177 | 178 | for e in subOtlLibs: 179 | libName = e[0] 180 | libData = e[1] 181 | 182 | if libName in allLoadedFiles: 183 | continue 184 | 185 | otlLibToInstall = fetchMyReceivedFilesFolder() + os.sep + libName 186 | 187 | with open(otlLibToInstall, 'wb') as f: 188 | f.write(libData) 189 | 190 | hou.hda.installFile(otlLibToInstall) 191 | 192 | comment = " Node sent by {0} ({1})".format(str(sender), time.ctime()) 193 | pyCode = data["PY_CODE"].split("\n") 194 | 195 | outPyCode = "" 196 | houNodeFound = False 197 | for c in pyCode: 198 | 199 | # Change code to create a geo container for sop node 200 | if parentType == "geo": 201 | if c.replace(" ", "").startswith("hou_parent") and not houNodeFound: 202 | houNodeFound = True 203 | outPyCode += c + "\n" 204 | outPyCode += "hou_parent = hou.node('/obj').createNode('geo', run_init_scripts=False)\n" 205 | outPyCode += "hou_parent.setName('{0}_container_' + '_from_{1}', unique_name=True)\n".format(nodeName, str(sender)) 206 | outPyCode += "hou_parent.appendComment('HCom:')\n" 207 | outPyCode += "hou_parent.appendComment('{0}')\n".format(comment) 208 | else: 209 | outPyCode += c + "\n" 210 | 211 | # Change parent to shopnet for shop nodes 212 | elif parentType == "shop": 213 | if c.replace(" ", "").startswith("hou_parent") and not houNodeFound: 214 | houNodeFound = True 215 | outPyCode += c + "\n" 216 | outPyCode += "container = hou.node('/obj').createNode('geo', run_init_scripts=False)\n" 217 | outPyCode += "container.setName('{0}_container_' + '_from_{1}', unique_name=True)\n".format(nodeName, str(sender)) 218 | outPyCode += "container.appendComment('HCom:')\n" 219 | outPyCode += "container.appendComment('{0}')\n".format(comment) 220 | outPyCode += "hou_parent = container.createNode('shopnet', run_init_scripts=False)\n" 221 | else: 222 | outPyCode += c + "\n" 223 | 224 | # Change parent to dopnet for dop nodes 225 | elif parentType == "dop": 226 | if c.replace(" ", "").startswith("hou_parent") and not houNodeFound: 227 | houNodeFound = True 228 | outPyCode += c + "\n" 229 | outPyCode += "container = hou.node('/obj').createNode('geo', run_init_scripts=False)\n" 230 | outPyCode += "container.setName('{0}_container_' + '_from_{1}', unique_name=True)\n".format(nodeName, str(sender)) 231 | outPyCode += "container.appendComment('HCom:')\n" 232 | outPyCode += "container.appendComment('{0}')\n".format(comment) 233 | outPyCode += "hou_parent = container.createNode('dopnet', run_init_scripts=False)\n" 234 | else: 235 | outPyCode += c + "\n" 236 | 237 | # Change parent to copnet for cop nodes 238 | elif parentType == "cop": 239 | if c.replace(" ", "").startswith("hou_parent") and not houNodeFound: 240 | houNodeFound = True 241 | outPyCode += c + "\n" 242 | outPyCode += "container = hou.node('/obj').createNode('geo', run_init_scripts=False)\n" 243 | outPyCode += "container.setName('{0}_container_' + '_from_{1}', unique_name=True)\n".format(nodeName, str(sender)) 244 | outPyCode += "container.appendComment('HCom:')\n" 245 | outPyCode += "container.appendComment('{0}')\n".format(comment) 246 | outPyCode += "hou_parent = container.createNode('cop2net', run_init_scripts=False)\n" 247 | else: 248 | outPyCode += c + "\n" 249 | 250 | # Change parent to ropnet for rop nodes 251 | elif parentType == "rop": 252 | if c.replace(" ", "").startswith("hou_parent") and not houNodeFound: 253 | houNodeFound = True 254 | outPyCode += c + "\n" 255 | outPyCode += "container = hou.node('/obj').createNode('geo', run_init_scripts=False)\n" 256 | outPyCode += "container.setName('{0}_container_' + '_from_{1}', unique_name=True)\n".format(nodeName, str(sender)) 257 | outPyCode += "container.appendComment('HCom:')\n" 258 | outPyCode += "container.appendComment('{0}')\n".format(comment) 259 | outPyCode += "hou_parent = container.createNode('ropnet', run_init_scripts=False)\n" 260 | else: 261 | outPyCode += c + "\n" 262 | 263 | # Change code to change node name 264 | elif parentType == "obj": 265 | if c.startswith("hou_node") and not houNodeFound: 266 | houNodeFound = True 267 | outPyCode += c + "\n" 268 | outPyCode += "hou_node.setName('{0}' + '_from_{1}', unique_name=True)\n".format(nodeName, str(sender)) 269 | outPyCode += "hou_node.appendComment('HCom:')\n" 270 | outPyCode += "hou_node.appendComment('{0}')\n".format(comment) 271 | else: 272 | outPyCode += c + "\n" 273 | 274 | # Change parent to vopsop for vex nodes 275 | elif "vop" in parentType: 276 | 277 | parentTypeContainer = parentType.split(";")[1] 278 | parentClass = parentType.split(";")[2] 279 | 280 | # is a sop vop network 281 | if parentClass == "sop": 282 | if c.replace(" ", "").startswith("hou_parent") and not houNodeFound: 283 | houNodeFound = True 284 | outPyCode += c + "\n" 285 | outPyCode += "container = hou.node('/obj').createNode('geo', run_init_scripts=False)\n" 286 | outPyCode += "container.setName('{0}_container_' + '_from_{1}', unique_name=True)\n".format(nodeName, str(sender)) 287 | outPyCode += "container.appendComment('HCom:')\n" 288 | outPyCode += "container.appendComment('{0}')\n".format(comment) 289 | outPyCode += "hou_parent = container.createNode('{0}', run_init_scripts=False)\n".format(parentTypeContainer) 290 | else: 291 | outPyCode += c + "\n" 292 | 293 | # it's a material network 294 | elif parentClass == "material": 295 | if c.replace(" ", "").startswith("hou_parent") and not houNodeFound: 296 | houNodeFound = True 297 | outPyCode += c + "\n" 298 | outPyCode += "container = hou.node('/obj').createNode('geo', run_init_scripts=False)\n" 299 | outPyCode += "container.setName('{0}_container_' + '_from_{1}', unique_name=True)\n".format(nodeName, str(sender)) 300 | outPyCode += "container.appendComment('HCom:')\n" 301 | outPyCode += "container.appendComment('{0}')\n".format(comment) 302 | outPyCode += "shopnet = container.createNode('shopnet', run_init_scripts=False)\n" 303 | outPyCode += "hou_parent = shopnet.createNode('{0}', run_init_scripts=False)\n".format(parentTypeContainer) 304 | else: 305 | outPyCode += c + "\n" 306 | 307 | # it's a cop network 308 | elif parentClass == "cop": 309 | if c.replace(" ", "").startswith("hou_parent") and not houNodeFound: 310 | houNodeFound = True 311 | outPyCode += c + "\n" 312 | outPyCode += "container = hou.node('/obj').createNode('geo', run_init_scripts=False)\n" 313 | outPyCode += "container.setName('{0}_container_' + '_from_{1}', unique_name=True)\n".format(nodeName, str(sender)) 314 | outPyCode += "container.appendComment('HCom:')\n" 315 | outPyCode += "container.appendComment('{0}')\n".format(comment) 316 | outPyCode += "copnet = container.createNode('cop2net', run_init_scripts=False)\n" 317 | outPyCode += "hou_parent = copnet.createNode('{0}', run_init_scripts=False)\n".format(parentTypeContainer) 318 | else: 319 | outPyCode += c + "\n" 320 | try: 321 | 322 | exec(outPyCode) 323 | except Exception as e: 324 | print("ERROR: exec pyCode " + str(e)) 325 | 326 | def setOtlSettings(data, sender="", settings=None): 327 | 328 | nodeType = data["OTL_TYPE"] 329 | 330 | selNode = hou.ui.selectNode() 331 | if not selNode: 332 | return False 333 | else: 334 | selection = hou.node(selNode) 335 | 336 | if selection.type().name() != nodeType: 337 | hou.ui.displayMessage("You must select a node of type: " + nodeType) 338 | return 339 | 340 | parms = data["OTL_PARMS"] 341 | for p in parms: 342 | 343 | parm = selection.parm(p) 344 | if parm: 345 | parm.set(parms[p]) 346 | else: 347 | print("Parm '{0}' not found, skipped.".format(p)) 348 | 349 | def createMesh(data, sender="", settings=None): 350 | 351 | meshType = data["MESH_TYPE"] 352 | meshName = data["MESH_NAME"] 353 | meshData = data["MESH_DATA"] 354 | 355 | bgeoFile = fetchMyReceivedFilesFolder() + os.sep + meshName + meshType 356 | bgeoFile = incrementFile(bgeoFile) 357 | with open(bgeoFile, 'wb') as f: 358 | f.write(meshData) 359 | 360 | comment = " Node sent by {0} ({1})".format(str(sender), time.ctime()) 361 | container = hou.node("/obj").createNode("geo", run_init_scripts=False) 362 | container.setName(meshName + "_{0}_from_".format(meshType.replace(".", "")) + sender, unique_name=True) 363 | container.appendComment(comment) 364 | 365 | fileNode = container.createNode("file") 366 | fileNode.setName(meshName + "_{0}_model".format(meshType.replace(".", ""))) 367 | fileNode.parm("file").set(bgeoFile) 368 | 369 | def createPic(data, sender="", settings=None): 370 | 371 | imageName = data["IMAGE_NAME"] 372 | imageData = data["BINARY_DATA"] 373 | 374 | imageNameAndFile = imageName.split(".") 375 | 376 | outFile = fetchMyReceivedFilesFolder() + os.sep + imageNameAndFile[0] + "." + imageNameAndFile[1] 377 | outFile = incrementFile(outFile) 378 | 379 | with open(outFile, 'wb') as f: 380 | f.write(imageData) 381 | 382 | t = threading.Thread(target = openPicFile, args=(outFile,)) 383 | t.start() 384 | 385 | def openPicFile(picFile): 386 | 387 | try: 388 | subprocess.Popen(["mplay.exe", picFile]) 389 | except Exception as e: 390 | print "MPLAY ERROR: " + str(e) 391 | try: 392 | subprocess.Popen(["explorer", picFile]) 393 | except Exception as e: 394 | print "EXPLORER ERROR: " + str(e) 395 | 396 | 397 | def fetchMyReceivedFilesFolder(): 398 | 399 | p = readIni()["MY_RECEIVED_FILES"] 400 | if p == "DEFAULT": 401 | p = os.path.dirname(__file__) + "\\HCom_Received_Files" 402 | 403 | if not os.path.exists(p): 404 | os.makedirs(p) 405 | 406 | return p 407 | 408 | def rdnname(): 409 | names = ["Motoko", "Bato", "Kusanagi", "Frodon", "Sheldon", "Pipo", "Sam", "Gandalf", "Fitz", "Henry"] 410 | names += ["Leonard", "Batman", "Bobleponge", "rincewind", "carrot", "HelloWorld", "Python", "Houdini"] 411 | return names[random.randint(0, len(names)-1)] 412 | 413 | class CLIENT_TYPE(): 414 | 415 | NONE = "NONE" 416 | HOUDINI = "Houdini" 417 | MAYA_NO_HENGINE = "Maya_no_hengine" 418 | MAYA_HENGINE = "Maya_hengine" 419 | NUKE = "nuke" 420 | -------------------------------------------------------------------------------- /lib/rpyc/core/protocol.py: -------------------------------------------------------------------------------- 1 | """ 2 | The RPyC protocol 3 | """ 4 | import sys 5 | import select 6 | import weakref 7 | import itertools 8 | import cPickle as pickle 9 | from threading import Lock 10 | from rpyc.utils.lib import WeakValueDict, RefCountingColl 11 | from rpyc.core import consts, brine, vinegar, netref 12 | from rpyc.core.async import AsyncResult 13 | 14 | 15 | class PingError(Exception): 16 | pass 17 | 18 | DEFAULT_CONFIG = dict( 19 | # ATTRIBUTES 20 | allow_safe_attrs = True, 21 | allow_exposed_attrs = True, 22 | allow_public_attrs = False, 23 | allow_all_attrs = False, 24 | safe_attrs = set(['__abs__', '__add__', '__and__', '__cmp__', '__contains__', 25 | '__delitem__', '__delslice__', '__div__', '__divmod__', '__doc__', 26 | '__eq__', '__float__', '__floordiv__', '__ge__', '__getitem__', 27 | '__getslice__', '__gt__', '__hash__', '__hex__', '__iadd__', '__iand__', 28 | '__idiv__', '__ifloordiv__', '__ilshift__', '__imod__', '__imul__', 29 | '__index__', '__int__', '__invert__', '__ior__', '__ipow__', '__irshift__', 30 | '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', '__len__', 31 | '__long__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', 32 | '__neg__', '__new__', '__nonzero__', '__oct__', '__or__', '__pos__', 33 | '__pow__', '__radd__', '__rand__', '__rdiv__', '__rdivmod__', '__repr__', 34 | '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', 35 | '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', 36 | '__rxor__', '__setitem__', '__setslice__', '__str__', '__sub__', 37 | '__truediv__', '__xor__', 'next', '__length_hint__', '__enter__', 38 | '__exit__', ]), 39 | exposed_prefix = "exposed_", 40 | allow_getattr = True, 41 | allow_setattr = False, 42 | allow_delattr = False, 43 | # EXCEPTIONS 44 | include_local_traceback = True, 45 | instantiate_custom_exceptions = False, 46 | import_custom_exceptions = False, 47 | instantiate_oldstyle_exceptions = False, # which don't derive from Exception 48 | propagate_SystemExit_locally = False, # whether to propagate SystemExit locally or to the other party 49 | # MISC 50 | allow_pickle = False, 51 | connid = None, 52 | credentials = None, 53 | ) 54 | 55 | _connection_id_generator = itertools.count(1) 56 | 57 | class Connection(object): 58 | """The RPyC connection (also know as the RPyC protocol). 59 | * service: the service to expose 60 | * channel: the channcel over which messages are passed 61 | * config: this connection's config dict (overriding parameters from the 62 | default config dict) 63 | * _lazy: whether or not to initialize the service with the creation of the 64 | connection. default is True. if set to False, you will need to call 65 | _init_service manually later 66 | """ 67 | def __init__(self, service, channel, config = {}, _lazy = False): 68 | self._closed = True 69 | self._config = DEFAULT_CONFIG.copy() 70 | self._config.update(config) 71 | if self._config["connid"] is None: 72 | self._config["connid"] = "conn%d" % (_connection_id_generator.next(),) 73 | 74 | self._channel = channel 75 | self._seqcounter = itertools.count() 76 | self._recvlock = Lock() 77 | self._sendlock = Lock() 78 | self._sync_replies = {} 79 | self._async_callbacks = {} 80 | self._local_objects = RefCountingColl() 81 | self._last_traceback = None 82 | self._proxy_cache = WeakValueDict() 83 | self._netref_classes_cache = {} 84 | self._remote_root = None 85 | self._local_root = service(weakref.proxy(self)) 86 | if not _lazy: 87 | self._init_service() 88 | self._closed = False 89 | def _init_service(self): 90 | self._local_root.on_connect() 91 | 92 | def __del__(self): 93 | self.close() 94 | def __enter__(self): 95 | return self 96 | def __exit__(self, t, v, tb): 97 | self.close() 98 | def __repr__(self): 99 | a, b = object.__repr__(self).split(" object ") 100 | return "%s %r object %s" % (a, self._config["connid"], b) 101 | 102 | # 103 | # IO 104 | # 105 | def _cleanup(self, _anyway = True): 106 | if self._closed and not _anyway: 107 | return 108 | self._closed = True 109 | self._channel.close() 110 | self._local_root.on_disconnect() 111 | self._sync_replies.clear() 112 | self._async_callbacks.clear() 113 | self._local_objects.clear() 114 | self._proxy_cache.clear() 115 | self._netref_classes_cache.clear() 116 | self._last_traceback = None 117 | self._last_traceback = None 118 | self._remote_root = None 119 | self._local_root = None 120 | #self._seqcounter = None 121 | #self._config.clear() 122 | def close(self, _catchall = True): 123 | if self._closed: 124 | return 125 | self._closed = True 126 | try: 127 | try: 128 | self._async_request(consts.HANDLE_CLOSE) 129 | except EOFError: 130 | pass 131 | except Exception: 132 | if not _catchall: 133 | raise 134 | finally: 135 | self._cleanup(_anyway = True) 136 | 137 | @property 138 | def closed(self): 139 | return self._closed 140 | def fileno(self): 141 | return self._channel.fileno() 142 | 143 | def ping(self, data = "the world is a vampire!" * 20, timeout = 3): 144 | """assert that the other party is functioning properly""" 145 | res = self.async_request(consts.HANDLE_PING, data, timeout = timeout) 146 | if res.value != data: 147 | raise PingError("echo mismatches sent data") 148 | 149 | def _send(self, msg, seq, args): 150 | data = brine.dump((msg, seq, args)) 151 | self._sendlock.acquire() 152 | try: 153 | self._channel.send(data) 154 | finally: 155 | self._sendlock.release() 156 | def _send_request(self, handler, args): 157 | seq = self._seqcounter.next() 158 | self._send(consts.MSG_REQUEST, seq, (handler, self._box(args))) 159 | return seq 160 | def _send_reply(self, seq, obj): 161 | self._send(consts.MSG_REPLY, seq, self._box(obj)) 162 | def _send_exception(self, seq, exctype, excval, exctb): 163 | exc = vinegar.dump(exctype, excval, exctb, 164 | include_local_traceback = self._config["include_local_traceback"]) 165 | self._send(consts.MSG_EXCEPTION, seq, exc) 166 | 167 | # 168 | # boxing 169 | # 170 | def _box(self, obj): 171 | """store a local object in such a way that it could be recreated on 172 | the remote party either by-value or by-reference""" 173 | if brine.dumpable(obj): 174 | return consts.LABEL_VALUE, obj 175 | if type(obj) is tuple: 176 | return consts.LABEL_TUPLE, tuple(self._box(item) for item in obj) 177 | elif isinstance(obj, netref.BaseNetref) and obj.____conn__() is self: 178 | return consts.LABEL_LOCAL_REF, obj.____oid__ 179 | else: 180 | self._local_objects.add(obj) 181 | cls = getattr(obj, "__class__", type(obj)) 182 | return consts.LABEL_REMOTE_REF, (id(obj), cls.__name__, cls.__module__) 183 | 184 | def _unbox(self, package): 185 | """recreate a local object representation of the remote object: if the 186 | object is passed by value, just return it; if the object is passed by 187 | reference, create a netref to it""" 188 | label, value = package 189 | if label == consts.LABEL_VALUE: 190 | return value 191 | if label == consts.LABEL_TUPLE: 192 | return tuple(self._unbox(item) for item in value) 193 | if label == consts.LABEL_LOCAL_REF: 194 | return self._local_objects[value] 195 | if label == consts.LABEL_REMOTE_REF: 196 | oid, clsname, modname = value 197 | if oid in self._proxy_cache: 198 | return self._proxy_cache[oid] 199 | proxy = self._netref_factory(oid, clsname, modname) 200 | self._proxy_cache[oid] = proxy 201 | return proxy 202 | raise ValueError("invalid label %r" % (label,)) 203 | 204 | def _netref_factory(self, oid, clsname, modname): 205 | typeinfo = (clsname, modname) 206 | if typeinfo in self._netref_classes_cache: 207 | cls = self._netref_classes_cache[typeinfo] 208 | elif typeinfo in netref.builtin_classes_cache: 209 | cls = netref.builtin_classes_cache[typeinfo] 210 | else: 211 | info = self.sync_request(consts.HANDLE_INSPECT, oid) 212 | cls = netref.class_factory(clsname, modname, info) 213 | self._netref_classes_cache[typeinfo] = cls 214 | return cls(weakref.ref(self), oid) 215 | 216 | # 217 | # dispatching 218 | # 219 | def _dispatch_request(self, seq, raw_args): 220 | try: 221 | handler, args = raw_args 222 | args = self._unbox(args) 223 | res = self._HANDLERS[handler](self, *args) 224 | except KeyboardInterrupt: 225 | raise 226 | except: 227 | t, v, tb = sys.exc_info() 228 | self._last_traceback = tb 229 | if t is SystemExit and self._config["propagate_SystemExit_locally"]: 230 | raise 231 | self._send_exception(seq, t, v, tb) 232 | else: 233 | self._send_reply(seq, res) 234 | 235 | def _dispatch_reply(self, seq, raw): 236 | obj = self._unbox(raw) 237 | if seq in self._async_callbacks: 238 | self._async_callbacks.pop(seq)(False, obj) 239 | else: 240 | self._sync_replies[seq] = (False, obj) 241 | 242 | def _dispatch_exception(self, seq, raw): 243 | obj = vinegar.load(raw, 244 | import_custom_exceptions = self._config["import_custom_exceptions"], 245 | instantiate_custom_exceptions = self._config["instantiate_custom_exceptions"], 246 | instantiate_oldstyle_exceptions = self._config["instantiate_oldstyle_exceptions"]) 247 | if seq in self._async_callbacks: 248 | self._async_callbacks.pop(seq)(True, obj) 249 | else: 250 | self._sync_replies[seq] = (True, obj) 251 | 252 | # 253 | # serving 254 | # 255 | def _recv(self, timeout, wait_for_lock): 256 | if not self._recvlock.acquire(wait_for_lock): 257 | return None 258 | try: 259 | try: 260 | if self._channel.poll(timeout): 261 | data = self._channel.recv() 262 | else: 263 | data = None 264 | except EOFError: 265 | self.close() 266 | raise 267 | finally: 268 | self._recvlock.release() 269 | return data 270 | 271 | def _dispatch(self, data): 272 | msg, seq, args = brine.load(data) 273 | if msg == consts.MSG_REQUEST: 274 | self._dispatch_request(seq, args) 275 | elif msg == consts.MSG_REPLY: 276 | self._dispatch_reply(seq, args) 277 | elif msg == consts.MSG_EXCEPTION: 278 | self._dispatch_exception(seq, args) 279 | else: 280 | raise ValueError("invalid message type: %r" % (msg,)) 281 | 282 | def poll(self, timeout = 0): 283 | """serve a single transaction, should one arrives in the given 284 | interval. note that handling a request/reply may trigger nested 285 | requests, which are all part of the transaction. 286 | 287 | returns True if one was served, False otherwise""" 288 | data = self._recv(timeout, wait_for_lock = False) 289 | if not data: 290 | return False 291 | self._dispatch(data) 292 | return True 293 | 294 | def serve(self, timeout = 1): 295 | """serve a single request or reply that arrives within the given 296 | time frame (default is 1 sec). note that the dispatching of a request 297 | might trigger multiple (nested) requests, thus this function may be 298 | reentrant. returns True if a request or reply were received, False 299 | otherwise.""" 300 | 301 | data = self._recv(timeout, wait_for_lock = True) 302 | if not data: 303 | return False 304 | self._dispatch(data) 305 | return True 306 | 307 | def serve_all(self): 308 | """serve all requests and replies while the connection is alive""" 309 | try: 310 | try: 311 | while True: 312 | self.serve(0.1) 313 | except select.error: 314 | if not self.closed: 315 | raise e 316 | except EOFError: 317 | pass 318 | finally: 319 | self.close() 320 | 321 | def poll_all(self, timeout = 0): 322 | """serve all requests and replies that arrive within the given interval. 323 | returns True if at least one was served, False otherwise""" 324 | at_least_once = False 325 | try: 326 | while self.poll(timeout): 327 | at_least_once = True 328 | except EOFError: 329 | pass 330 | return at_least_once 331 | 332 | # 333 | # requests 334 | # 335 | def sync_request(self, handler, *args): 336 | """send a request and wait for the reply to arrive""" 337 | seq = self._send_request(handler, args) 338 | while seq not in self._sync_replies: 339 | self.serve(0.1) 340 | isexc, obj = self._sync_replies.pop(seq) 341 | if isexc: 342 | raise obj 343 | else: 344 | return obj 345 | 346 | def _async_request(self, handler, args = (), callback = (lambda a, b: None)): 347 | seq = self._send_request(handler, args) 348 | self._async_callbacks[seq] = callback 349 | def async_request(self, handler, *args, **kwargs): 350 | """send a request and return an AsyncResult object, which will 351 | eventually hold the reply""" 352 | timeout = kwargs.pop("timeout", None) 353 | if kwargs: 354 | raise TypeError("got unexpected keyword argument %r" % (kwargs.keys()[0],)) 355 | res = AsyncResult(weakref.proxy(self)) 356 | self._async_request(handler, args, res) 357 | if timeout is not None: 358 | res.set_expiry(timeout) 359 | return res 360 | 361 | @property 362 | def root(self): 363 | """fetch the root object of the other party""" 364 | if self._remote_root is None: 365 | self._remote_root = self.sync_request(consts.HANDLE_GETROOT) 366 | return self._remote_root 367 | 368 | # 369 | # attribute access 370 | # 371 | def _check_attr(self, obj, name): 372 | if self._config["allow_exposed_attrs"]: 373 | if name.startswith(self._config["exposed_prefix"]): 374 | name2 = name 375 | else: 376 | name2 = self._config["exposed_prefix"] + name 377 | if hasattr(obj, name2): 378 | return name2 379 | if self._config["allow_all_attrs"]: 380 | return name 381 | if self._config["allow_safe_attrs"] and name in self._config["safe_attrs"]: 382 | return name 383 | if self._config["allow_public_attrs"] and not name.startswith("_"): 384 | return name 385 | return False 386 | 387 | def _access_attr(self, oid, name, args, overrider, param, default): 388 | if type(name) is not str: 389 | raise TypeError("attr name must be a string") 390 | obj = self._local_objects[oid] 391 | accessor = getattr(type(obj), overrider, None) 392 | if accessor is None: 393 | name2 = self._check_attr(obj, name) 394 | if not self._config[param] or not name2: 395 | raise AttributeError("cannot access %r" % (name,)) 396 | accessor = default 397 | name = name2 398 | return accessor(obj, name, *args) 399 | 400 | # 401 | # handlers 402 | # 403 | def _handle_ping(self, data): 404 | return data 405 | def _handle_close(self): 406 | self._cleanup() 407 | def _handle_getroot(self): 408 | return self._local_root 409 | def _handle_del(self, oid): 410 | self._local_objects.decref(oid) 411 | def _handle_repr(self, oid): 412 | return repr(self._local_objects[oid]) 413 | def _handle_str(self, oid): 414 | return str(self._local_objects[oid]) 415 | def _handle_cmp(self, oid, other): 416 | # cmp() might enter recursive resonance... yet another workaround 417 | #return cmp(self._local_objects[oid], other) 418 | obj = self._local_objects[oid] 419 | try: 420 | return type(obj).__cmp__(obj, other) 421 | except TypeError: 422 | return NotImplemented 423 | def _handle_hash(self, oid): 424 | return hash(self._local_objects[oid]) 425 | def _handle_call(self, oid, args, kwargs): 426 | return self._local_objects[oid](*args, **dict(kwargs)) 427 | def _handle_dir(self, oid): 428 | return tuple(dir(self._local_objects[oid])) 429 | def _handle_inspect(self, oid): 430 | return tuple(netref.inspect_methods(self._local_objects[oid])) 431 | def _handle_getattr(self, oid, name): 432 | return self._access_attr(oid, name, (), "_rpyc_getattr", "allow_getattr", getattr) 433 | def _handle_delattr(self, oid, name): 434 | return self._access_attr(oid, name, (), "_rpyc_delattr", "allow_delattr", delattr) 435 | def _handle_setattr(self, oid, name, value): 436 | return self._access_attr(oid, name, (value,), "_rpyc_setattr", "allow_setattr", setattr) 437 | def _handle_callattr(self, oid, name, args, kwargs): 438 | return self._handle_getattr(oid, name)(*args, **dict(kwargs)) 439 | def _handle_pickle(self, oid, proto): 440 | if not self._config["allow_pickle"]: 441 | raise ValueError("pickling is disabled") 442 | return pickle.dumps(self._local_objects[oid], proto) 443 | def _handle_buffiter(self, oid, count): 444 | items = [] 445 | obj = self._local_objects[oid] 446 | for i in xrange(count): 447 | try: 448 | items.append(obj.next()) 449 | except StopIteration: 450 | break 451 | return tuple(items) 452 | 453 | # collect handlers 454 | _HANDLERS = {} 455 | for name, obj in locals().items(): 456 | if name.startswith("_handle_"): 457 | name2 = "HANDLE_" + name[8:].upper() 458 | if hasattr(consts, name2): 459 | _HANDLERS[getattr(consts, name2)] = obj 460 | else: 461 | raise NameError("no constant defined for %r", name) 462 | del name, name2, obj 463 | 464 | 465 | --------------------------------------------------------------------------------