From 68b23437bb98163d46fa72fa9ece09c7051a5c7b Mon Sep 17 00:00:00 2001 From: Martin Urban Date: Tue, 9 Sep 2025 23:14:55 +0200 Subject: [PATCH 1/2] Replace XML-RPC with gRPC for PyMOL RPC services Introduced a new gRPC-based implementation to replace the deprecated XML-RPC framework used in PyMOL. This migration addresses security vulnerabilities in XML-RPC (e.g., CVE-2016-5004) and provides a modern, efficient, and extensible framework for remote procedure calls. Updated associated protobuf definitions and Python gRPC bindings accordingly. --- modules/pymol/invocation.py | 12 +- modules/pymol/pml_grpc.py | 294 ++++++++ modules/pymol/pymol_rpc_pb2.py | 86 +++ modules/pymol/pymol_rpc_pb2_grpc.py | 1044 +++++++++++++++++++++++++++ proto/pymol_rpc.proto | 183 +++++ 5 files changed, 1618 insertions(+), 1 deletion(-) create mode 100644 modules/pymol/pml_grpc.py create mode 100644 modules/pymol/pymol_rpc_pb2.py create mode 100644 modules/pymol/pymol_rpc_pb2_grpc.py create mode 100644 proto/pymol_rpc.proto diff --git a/modules/pymol/invocation.py b/modules/pymol/invocation.py index 2dc6bcf38..5bced2c79 100644 --- a/modules/pymol/invocation.py +++ b/modules/pymol/invocation.py @@ -519,7 +519,17 @@ def parse_args(argv, _pymol=None, options=None, restricted=0): for a in pymolrc] + options.deferred options.pymolrc = pymolrc if options.rpcServer: - options.deferred.append('_do__ /import pymol.rpc;pymol.rpc.launch_XMLRPC()') + # Replaced old XML-RPC implementation with modern gRPC implementation. + # However, to enforce this change, it uses the same command line argument. + # This enforcement comes from clear security concerns, like vulnerabilities in the + # Apache XML RPC Client Library (https://mvnrepository.com/artifact/org.apache.xmlrpc/xmlrpc-client/3.1.3) + # that has its last official release in 2010. + # It contains two Vulnerabilities from dependencies: + # CVE-2016-5004: https://www.cve.org/CVERecord?id=CVE-2016-5004 + # CVE-2012-5783: https://www.cve.org/CVERecord?id=CVE-2012-5783 + # + # options.deferred.append('_do__ /import pymol.rpc;pymol.rpc.launch_XMLRPC()') + options.deferred.append('_do__ /import pymol.pml_grpc;pymol.pml_grpc.launch_gRPC()') if options.plugins == 1: # Load plugins independent of PMGApp (will not add menu items) options.deferred.append('_do__ /import pymol.plugins;pymol.plugins.initialize(-1)') diff --git a/modules/pymol/pml_grpc.py b/modules/pymol/pml_grpc.py new file mode 100644 index 000000000..712313419 --- /dev/null +++ b/modules/pymol/pml_grpc.py @@ -0,0 +1,294 @@ +""" +A gRPC server to allow remote control of PyMol. + +Replaces the legacy XML-RPC implementation with a modern, +high-performance gRPC service. + +Author: Greg Landrum (original XML-RPC), Martin Urban (updated for gRPC) +Date: September 2025 +License: PyMOL +Requires: + - grpcio, grpcio-tools, protobuf + - Python with threading enabled +""" + +import os +import tempfile +from concurrent import futures + +import grpc +from pymol import cmd, cgo + +import pymol_rpc_pb2 +import pymol_rpc_pb2_grpc + +# --- Global state --- +cgoDict = {} # stores CGO objects by id +_server = None + + +# --- Helper functions --- + +def _color_obj(obj_name, color_scheme: str): + """Applies a color scheme to a molecular object.""" + if not color_scheme: + return + try: + if color_scheme == 'std': + cmd.color("magenta", f"({obj_name})", quiet=1) + cmd.color("oxygen", f"(elem O and {obj_name})", quiet=1) + cmd.color("nitrogen", f"(elem N and {obj_name})", quiet=1) + cmd.color("sulfur", f"(elem S and {obj_name})", quiet=1) + cmd.color("hydrogen", f"(elem H and {obj_name})", quiet=1) + cmd.color("gray", f"(elem C and {obj_name})", quiet=1) + else: + cmd.color(color_scheme, obj_name, quiet=1) + except Exception as e: + print(f"Error applying color scheme '{color_scheme}': {e}") + + +def _make_alpha_section(transparent: bool, transparency: float): + return [] if not transparent else [cgo.ALPHA, 1.0 - transparency] + + +# --- gRPC Servicer Implementation --- + +class PyMolRPCServicer(pymol_rpc_pb2_grpc.PyMolRPCServicer): + """Implements the PyMolRPC gRPC service.""" + + def Ping(self, request, context): + return pymol_rpc_pb2.StatusResponse(success=True, message="Pong") + + def Do(self, request, context): + try: + result = cmd.do(request.command) + return pymol_rpc_pb2.CommandResponse(result=str(result) if result else "") + except Exception as e: + return pymol_rpc_pb2.CommandResponse(result=f"Error: {e}") + + def Label(self, request, context): + try: + pos = (request.pos.x, request.pos.y, request.pos.z) + color = (request.color.r, request.color.g, request.color.b) + obj_id = request.id or 'lab1' + color_id = f"{obj_id}-color" + + cmd.pseudoatom(obj_id, label=repr(request.text), elem='C', pos=pos) + cmd.set_color(color_id, color) + cmd.color(color_id, obj_id) + return pymol_rpc_pb2.StatusResponse(success=True) + except Exception as e: + return pymol_rpc_pb2.StatusResponse(success=False, message=str(e)) + + def Sphere(self, request, context): + try: + pos = (request.pos.x, request.pos.y, request.pos.z) + color = (request.color.r, request.color.g, request.color.b) + obj_id = request.id or 'cgo' + + obj = cgoDict.get(obj_id, []).copy() if request.extend else [] + obj.extend(_make_alpha_section(request.transparent, request.transparency)) + obj.extend([cgo.COLOR, *color, cgo.SPHERE, *pos, request.radius]) + + cgoDict[obj_id] = obj + cmd.load_cgo(obj, obj_id, 1) + return pymol_rpc_pb2.StatusResponse(success=True) + except Exception as e: + return pymol_rpc_pb2.StatusResponse(success=False, message=str(e)) + + def Cylinder(self, request, context): + try: + p1 = (request.end1.x, request.end1.y, request.end1.z) + p2 = (request.end2.x, request.end2.y, request.end2.z) + c1 = (request.color1.r, request.color1.g, request.color1.b) + + if request.HasField("color2"): + c2 = (request.color2.r, request.color2.g, request.color2.b) + else: + c2 = c1 + + obj_id = request.id or 'cgo' + obj = cgoDict.get(obj_id, []).copy() if request.extend else [] + obj.extend(_make_alpha_section(request.transparent, request.transparency)) + obj.extend([cgo.CYLINDER, *p1, *p2, request.radius, *c1, *c2]) + + cgoDict[obj_id] = obj + cmd.load_cgo(obj, obj_id, 1) + return pymol_rpc_pb2.StatusResponse(success=True) + except Exception as e: + return pymol_rpc_pb2.StatusResponse(success=False, message=str(e)) + + def LoadPDB(self, request, context): + if request.replace: + cmd.delete(request.obj_name) + result = cmd.read_pdbstr(request.data, request.obj_name) + if request.HasField("color_scheme"): + _color_obj(request.obj_name, request.color_scheme) + return pymol_rpc_pb2.CommandResponse(result=str(result) if result else "") + + def LoadMolBlock(self, request, context): + if request.replace: + cmd.delete(request.obj_name) + result = cmd.read_molstr(request.data, request.obj_name) + if request.HasField("color_scheme"): + _color_obj(request.obj_name, request.color_scheme) + return pymol_rpc_pb2.CommandResponse(result=str(result) if result else "") + + def LoadFile(self, request, context): + obj_name = request.obj_name or os.path.splitext(os.path.basename(request.file_name))[0] + if request.replace: + cmd.delete(obj_name) + fmt = request.format if request.HasField("format") else '' + result = cmd.load(request.file_name, obj_name, format=fmt) + if request.HasField("color_scheme"): + _color_obj(obj_name, request.color_scheme) + return pymol_rpc_pb2.CommandResponse(result=str(result) if result else "") + + def LoadSurface(self, request, context): + grid_name = f"grid-{request.obj_name}" + file_to_load = None + + try: + if request.WhichOneof("source") == "data": + with tempfile.NamedTemporaryFile(delete=False, mode='w', suffix='.grd') as tmp: + tmp.write(request.data) + file_to_load = tmp.name + elif request.WhichOneof("source") == "file_name": + file_to_load = request.file_name + else: + return pymol_rpc_pb2.CommandResponse(result="Error: no source specified") + + fmt = request.format if request.HasField("format") else '' + result = cmd.load(file_to_load, grid_name, format=fmt) + cmd.isosurface(request.obj_name, grid_name, level=request.surface_level) + return pymol_rpc_pb2.CommandResponse(result=str(result) if result else "") + finally: + if file_to_load and file_to_load.endswith(".grd") and os.path.exists(file_to_load): + os.unlink(file_to_load) + + def GetNames(self, request, context): + names = cmd.get_names(request.what, enabled_only=request.enabled_only) + return pymol_rpc_pb2.GetNamesResponse(names=names) + + def GetAtomCoords(self, request, context): + coords = cmd.get_atom_coords(request.selection, state=request.state) + if coords is None: + return pymol_rpc_pb2.GetAtomCoordsResponse(coordinates=[]) + response_coords = [pymol_rpc_pb2.Vector3(x=c[0], y=c[1], z=c[2]) for c in coords] + return pymol_rpc_pb2.GetAtomCoordsResponse(coordinates=response_coords) + + def DeleteObject(self, request, context): + try: + cmd.delete(request.name) + return pymol_rpc_pb2.StatusResponse(success=True) + except Exception as e: + return pymol_rpc_pb2.StatusResponse(success=False, message=str(e)) + + def DeleteAll(self, request, context): + try: + cmd.delete("all") + return pymol_rpc_pb2.StatusResponse(success=True) + except Exception as e: + return pymol_rpc_pb2.StatusResponse(success=False, message=str(e)) + + def Center(self, request, context): + try: + cmd.center(request.selection, animate=int(request.animate)) + return pymol_rpc_pb2.StatusResponse(success=True) + except Exception as e: + return pymol_rpc_pb2.StatusResponse(success=False, message=str(e)) + + def Zoom(self, request, context): + try: + cmd.zoom(request.selection, buffer=request.buffer, + state=request.state, animate=int(request.animate)) + return pymol_rpc_pb2.StatusResponse(success=True) + except Exception as e: + return pymol_rpc_pb2.StatusResponse(success=False, message=str(e)) + + def Rotate(self, request, context): + try: + cmd.rotate(request.axis, request.angle, request.selection) + return pymol_rpc_pb2.StatusResponse(success=True) + except Exception as e: + return pymol_rpc_pb2.StatusResponse(success=False, message=str(e)) + + def Move(self, request, context): + try: + cmd.move(request.axis, request.distance, request.selection) + return pymol_rpc_pb2.StatusResponse(success=True) + except Exception as e: + return pymol_rpc_pb2.StatusResponse(success=False, message=str(e)) + + def Hide(self, request, context): + try: + cmd.hide(request.representation, request.selection) + return pymol_rpc_pb2.StatusResponse(success=True) + except Exception as e: + return pymol_rpc_pb2.StatusResponse(success=False, message=str(e)) + + def Show(self, request, context): + try: + cmd.show(request.representation, request.selection) + return pymol_rpc_pb2.StatusResponse(success=True) + except Exception as e: + return pymol_rpc_pb2.StatusResponse(success=False, message=str(e)) + + def Color(self, request, context): + try: + cmd.color(request.color, request.selection) + return pymol_rpc_pb2.StatusResponse(success=True) + except Exception as e: + return pymol_rpc_pb2.StatusResponse(success=False, message=str(e)) + + def SetTransparency(self, request, context): + try: + cmd.set("transparency", request.transparency, request.selection) + return pymol_rpc_pb2.StatusResponse(success=True) + except Exception as e: + return pymol_rpc_pb2.StatusResponse(success=False, message=str(e)) + + def Select(self, request, context): + try: + cmd.select("sele", request.selection) + return pymol_rpc_pb2.StatusResponse(success=True) + except Exception as e: + return pymol_rpc_pb2.StatusResponse(success=False, message=str(e)) + + def DeleteSelection(self, request, context): + try: + cmd.delete(request.selection) + return pymol_rpc_pb2.StatusResponse(success=True) + except Exception as e: + return pymol_rpc_pb2.StatusResponse(success=False, message=str(e)) + +# --- Server Launching Logic --- + +def launch_gRPC(hostname='', port=50051, n_to_try=5): + """Launches the gRPC server in a background thread.""" + global _server + if _server is not None: + print("gRPC server already running.") + return + + if not hostname: + hostname = os.environ.get('PYMOL_RPCHOST', 'localhost') + + server_port = None + for i in range(n_to_try): + current_port = port + i + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + pymol_rpc_pb2_grpc.add_PyMolRPCServicer_to_server(PyMolRPCServicer(), server) + bound = server.add_insecure_port(f"{hostname}:{current_port}") + if bound == 0: + print(f"Port {current_port} unavailable; trying next") + continue + server.start() + _server = server + server_port = current_port + break + + if _server: + print(f"gRPC server running on host {hostname}, port {server_port}") + else: + print("gRPC server could not be started") diff --git a/modules/pymol/pymol_rpc_pb2.py b/modules/pymol/pymol_rpc_pb2.py new file mode 100644 index 000000000..c07fdd739 --- /dev/null +++ b/modules/pymol/pymol_rpc_pb2.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: pymol_rpc.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'pymol_rpc.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fpymol_rpc.proto\"\x07\n\x05\x45mpty\"2\n\x0eStatusResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"*\n\x07Vector3\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\"+\n\x08\x43olorRGB\x12\t\n\x01r\x18\x01 \x01(\x02\x12\t\n\x01g\x18\x02 \x01(\x02\x12\t\n\x01\x62\x18\x03 \x01(\x02\"!\n\x0e\x43ommandRequest\x12\x0f\n\x07\x63ommand\x18\x01 \x01(\t\"!\n\x0f\x43ommandResponse\x12\x0e\n\x06result\x18\x01 \x01(\t\"Y\n\x0cLabelRequest\x12\x15\n\x03pos\x18\x01 \x01(\x0b\x32\x08.Vector3\x12\x0c\n\x04text\x18\x02 \x01(\t\x12\n\n\x02id\x18\x03 \x01(\t\x12\x18\n\x05\x63olor\x18\x04 \x01(\x0b\x32\t.ColorRGB\"\x97\x01\n\rSphereRequest\x12\x15\n\x03pos\x18\x01 \x01(\x0b\x32\x08.Vector3\x12\x0e\n\x06radius\x18\x02 \x01(\x02\x12\x18\n\x05\x63olor\x18\x03 \x01(\x0b\x32\t.ColorRGB\x12\n\n\x02id\x18\x04 \x01(\t\x12\x0e\n\x06\x65xtend\x18\x05 \x01(\x08\x12\x13\n\x0btransparent\x18\x06 \x01(\x08\x12\x14\n\x0ctransparency\x18\x07 \x01(\x02\"\xde\x01\n\x0f\x43ylinderRequest\x12\x16\n\x04\x65nd1\x18\x01 \x01(\x0b\x32\x08.Vector3\x12\x16\n\x04\x65nd2\x18\x02 \x01(\x0b\x32\x08.Vector3\x12\x0e\n\x06radius\x18\x03 \x01(\x02\x12\x19\n\x06\x63olor1\x18\x04 \x01(\x0b\x32\t.ColorRGB\x12\x1e\n\x06\x63olor2\x18\x05 \x01(\x0b\x32\t.ColorRGBH\x00\x88\x01\x01\x12\n\n\x02id\x18\x06 \x01(\t\x12\x0e\n\x06\x65xtend\x18\x07 \x01(\x08\x12\x13\n\x0btransparent\x18\x08 \x01(\x08\x12\x14\n\x0ctransparency\x18\t \x01(\x02\x42\t\n\x07_color2\"m\n\x0eLoadMolRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\t\x12\x10\n\x08obj_name\x18\x02 \x01(\t\x12\x19\n\x0c\x63olor_scheme\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x0f\n\x07replace\x18\x04 \x01(\x08\x42\x0f\n\r_color_scheme\"\x93\x01\n\x0fLoadFileRequest\x12\x11\n\tfile_name\x18\x01 \x01(\t\x12\x10\n\x08obj_name\x18\x02 \x01(\t\x12\x13\n\x06\x66ormat\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0c\x63olor_scheme\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x0f\n\x07replace\x18\x05 \x01(\x08\x42\t\n\x07_formatB\x0f\n\r_color_scheme\"\x8c\x01\n\x12LoadSurfaceRequest\x12\x0e\n\x04\x64\x61ta\x18\x01 \x01(\tH\x00\x12\x13\n\tfile_name\x18\x02 \x01(\tH\x00\x12\x10\n\x08obj_name\x18\x03 \x01(\t\x12\x13\n\x06\x66ormat\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x15\n\rsurface_level\x18\x05 \x01(\x02\x42\x08\n\x06sourceB\t\n\x07_format\"5\n\x0fGetNamesRequest\x12\x0c\n\x04what\x18\x01 \x01(\t\x12\x14\n\x0c\x65nabled_only\x18\x02 \x01(\x08\"!\n\x10GetNamesResponse\x12\r\n\x05names\x18\x01 \x03(\t\"8\n\x14GetAtomCoordsRequest\x12\x11\n\tselection\x18\x01 \x01(\t\x12\r\n\x05state\x18\x02 \x01(\x05\"6\n\x15GetAtomCoordsResponse\x12\x1d\n\x0b\x63oordinates\x18\x01 \x03(\x0b\x32\x08.Vector3\"\x1d\n\rObjectRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"%\n\x10SelectionRequest\x12\x11\n\tselection\x18\x01 \x01(\t\"3\n\rCenterRequest\x12\x11\n\tselection\x18\x01 \x01(\t\x12\x0f\n\x07\x61nimate\x18\x02 \x01(\x08\"P\n\x0bZoomRequest\x12\x11\n\tselection\x18\x01 \x01(\t\x12\x0e\n\x06\x62uffer\x18\x02 \x01(\x02\x12\r\n\x05state\x18\x03 \x01(\x05\x12\x0f\n\x07\x61nimate\x18\x04 \x01(\x08\"?\n\rRotateRequest\x12\x0c\n\x04\x61xis\x18\x01 \x01(\t\x12\r\n\x05\x61ngle\x18\x02 \x01(\x02\x12\x11\n\tselection\x18\x03 \x01(\t\"@\n\x0bMoveRequest\x12\x0c\n\x04\x61xis\x18\x01 \x01(\t\x12\x10\n\x08\x64istance\x18\x02 \x01(\x02\x12\x11\n\tselection\x18\x03 \x01(\t\"<\n\x0fShowHideRequest\x12\x16\n\x0erepresentation\x18\x01 \x01(\t\x12\x11\n\tselection\x18\x02 \x01(\t\"0\n\x0c\x43olorRequest\x12\r\n\x05\x63olor\x18\x01 \x01(\t\x12\x11\n\tselection\x18\x02 \x01(\t\">\n\x13TransparencyRequest\x12\x14\n\x0ctransparency\x18\x01 \x01(\x02\x12\x11\n\tselection\x18\x02 \x01(\t2\xa8\x08\n\x08PyMolRPC\x12\x1f\n\x04Ping\x12\x06.Empty\x1a\x0f.StatusResponse\x12\'\n\x02\x44o\x12\x0f.CommandRequest\x1a\x10.CommandResponse\x12\'\n\x05Label\x12\r.LabelRequest\x1a\x0f.StatusResponse\x12)\n\x06Sphere\x12\x0e.SphereRequest\x1a\x0f.StatusResponse\x12-\n\x08\x43ylinder\x12\x10.CylinderRequest\x1a\x0f.StatusResponse\x12,\n\x07LoadPDB\x12\x0f.LoadMolRequest\x1a\x10.CommandResponse\x12\x31\n\x0cLoadMolBlock\x12\x0f.LoadMolRequest\x1a\x10.CommandResponse\x12.\n\x08LoadFile\x12\x10.LoadFileRequest\x1a\x10.CommandResponse\x12\x34\n\x0bLoadSurface\x12\x13.LoadSurfaceRequest\x1a\x10.CommandResponse\x12/\n\x08GetNames\x12\x10.GetNamesRequest\x1a\x11.GetNamesResponse\x12>\n\rGetAtomCoords\x12\x15.GetAtomCoordsRequest\x1a\x16.GetAtomCoordsResponse\x12/\n\x0c\x44\x65leteObject\x12\x0e.ObjectRequest\x1a\x0f.StatusResponse\x12$\n\tDeleteAll\x12\x06.Empty\x1a\x0f.StatusResponse\x12)\n\x06\x43\x65nter\x12\x0e.CenterRequest\x1a\x0f.StatusResponse\x12%\n\x04Zoom\x12\x0c.ZoomRequest\x1a\x0f.StatusResponse\x12)\n\x06Rotate\x12\x0e.RotateRequest\x1a\x0f.StatusResponse\x12%\n\x04Move\x12\x0c.MoveRequest\x1a\x0f.StatusResponse\x12)\n\x04Hide\x12\x10.ShowHideRequest\x1a\x0f.StatusResponse\x12)\n\x04Show\x12\x10.ShowHideRequest\x1a\x0f.StatusResponse\x12\'\n\x05\x43olor\x12\r.ColorRequest\x1a\x0f.StatusResponse\x12\x38\n\x0fSetTransparency\x12\x14.TransparencyRequest\x1a\x0f.StatusResponse\x12,\n\x06Select\x12\x11.SelectionRequest\x1a\x0f.StatusResponse\x12\x35\n\x0f\x44\x65leteSelection\x12\x11.SelectionRequest\x1a\x0f.StatusResponseb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'pymol_rpc_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_EMPTY']._serialized_start=19 + _globals['_EMPTY']._serialized_end=26 + _globals['_STATUSRESPONSE']._serialized_start=28 + _globals['_STATUSRESPONSE']._serialized_end=78 + _globals['_VECTOR3']._serialized_start=80 + _globals['_VECTOR3']._serialized_end=122 + _globals['_COLORRGB']._serialized_start=124 + _globals['_COLORRGB']._serialized_end=167 + _globals['_COMMANDREQUEST']._serialized_start=169 + _globals['_COMMANDREQUEST']._serialized_end=202 + _globals['_COMMANDRESPONSE']._serialized_start=204 + _globals['_COMMANDRESPONSE']._serialized_end=237 + _globals['_LABELREQUEST']._serialized_start=239 + _globals['_LABELREQUEST']._serialized_end=328 + _globals['_SPHEREREQUEST']._serialized_start=331 + _globals['_SPHEREREQUEST']._serialized_end=482 + _globals['_CYLINDERREQUEST']._serialized_start=485 + _globals['_CYLINDERREQUEST']._serialized_end=707 + _globals['_LOADMOLREQUEST']._serialized_start=709 + _globals['_LOADMOLREQUEST']._serialized_end=818 + _globals['_LOADFILEREQUEST']._serialized_start=821 + _globals['_LOADFILEREQUEST']._serialized_end=968 + _globals['_LOADSURFACEREQUEST']._serialized_start=971 + _globals['_LOADSURFACEREQUEST']._serialized_end=1111 + _globals['_GETNAMESREQUEST']._serialized_start=1113 + _globals['_GETNAMESREQUEST']._serialized_end=1166 + _globals['_GETNAMESRESPONSE']._serialized_start=1168 + _globals['_GETNAMESRESPONSE']._serialized_end=1201 + _globals['_GETATOMCOORDSREQUEST']._serialized_start=1203 + _globals['_GETATOMCOORDSREQUEST']._serialized_end=1259 + _globals['_GETATOMCOORDSRESPONSE']._serialized_start=1261 + _globals['_GETATOMCOORDSRESPONSE']._serialized_end=1315 + _globals['_OBJECTREQUEST']._serialized_start=1317 + _globals['_OBJECTREQUEST']._serialized_end=1346 + _globals['_SELECTIONREQUEST']._serialized_start=1348 + _globals['_SELECTIONREQUEST']._serialized_end=1385 + _globals['_CENTERREQUEST']._serialized_start=1387 + _globals['_CENTERREQUEST']._serialized_end=1438 + _globals['_ZOOMREQUEST']._serialized_start=1440 + _globals['_ZOOMREQUEST']._serialized_end=1520 + _globals['_ROTATEREQUEST']._serialized_start=1522 + _globals['_ROTATEREQUEST']._serialized_end=1585 + _globals['_MOVEREQUEST']._serialized_start=1587 + _globals['_MOVEREQUEST']._serialized_end=1651 + _globals['_SHOWHIDEREQUEST']._serialized_start=1653 + _globals['_SHOWHIDEREQUEST']._serialized_end=1713 + _globals['_COLORREQUEST']._serialized_start=1715 + _globals['_COLORREQUEST']._serialized_end=1763 + _globals['_TRANSPARENCYREQUEST']._serialized_start=1765 + _globals['_TRANSPARENCYREQUEST']._serialized_end=1827 + _globals['_PYMOLRPC']._serialized_start=1830 + _globals['_PYMOLRPC']._serialized_end=2894 +# @@protoc_insertion_point(module_scope) diff --git a/modules/pymol/pymol_rpc_pb2_grpc.py b/modules/pymol/pymol_rpc_pb2_grpc.py new file mode 100644 index 000000000..ce2ca7054 --- /dev/null +++ b/modules/pymol/pymol_rpc_pb2_grpc.py @@ -0,0 +1,1044 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + +import pymol_rpc_pb2 as pymol__rpc__pb2 + +GRPC_GENERATED_VERSION = '1.74.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in pymol_rpc_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + + +class PyMolRPCStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Ping = channel.unary_unary( + '/PyMolRPC/Ping', + request_serializer=pymol__rpc__pb2.Empty.SerializeToString, + response_deserializer=pymol__rpc__pb2.StatusResponse.FromString, + _registered_method=True) + self.Do = channel.unary_unary( + '/PyMolRPC/Do', + request_serializer=pymol__rpc__pb2.CommandRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.CommandResponse.FromString, + _registered_method=True) + self.Label = channel.unary_unary( + '/PyMolRPC/Label', + request_serializer=pymol__rpc__pb2.LabelRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.StatusResponse.FromString, + _registered_method=True) + self.Sphere = channel.unary_unary( + '/PyMolRPC/Sphere', + request_serializer=pymol__rpc__pb2.SphereRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.StatusResponse.FromString, + _registered_method=True) + self.Cylinder = channel.unary_unary( + '/PyMolRPC/Cylinder', + request_serializer=pymol__rpc__pb2.CylinderRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.StatusResponse.FromString, + _registered_method=True) + self.LoadPDB = channel.unary_unary( + '/PyMolRPC/LoadPDB', + request_serializer=pymol__rpc__pb2.LoadMolRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.CommandResponse.FromString, + _registered_method=True) + self.LoadMolBlock = channel.unary_unary( + '/PyMolRPC/LoadMolBlock', + request_serializer=pymol__rpc__pb2.LoadMolRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.CommandResponse.FromString, + _registered_method=True) + self.LoadFile = channel.unary_unary( + '/PyMolRPC/LoadFile', + request_serializer=pymol__rpc__pb2.LoadFileRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.CommandResponse.FromString, + _registered_method=True) + self.LoadSurface = channel.unary_unary( + '/PyMolRPC/LoadSurface', + request_serializer=pymol__rpc__pb2.LoadSurfaceRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.CommandResponse.FromString, + _registered_method=True) + self.GetNames = channel.unary_unary( + '/PyMolRPC/GetNames', + request_serializer=pymol__rpc__pb2.GetNamesRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.GetNamesResponse.FromString, + _registered_method=True) + self.GetAtomCoords = channel.unary_unary( + '/PyMolRPC/GetAtomCoords', + request_serializer=pymol__rpc__pb2.GetAtomCoordsRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.GetAtomCoordsResponse.FromString, + _registered_method=True) + self.DeleteObject = channel.unary_unary( + '/PyMolRPC/DeleteObject', + request_serializer=pymol__rpc__pb2.ObjectRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.StatusResponse.FromString, + _registered_method=True) + self.DeleteAll = channel.unary_unary( + '/PyMolRPC/DeleteAll', + request_serializer=pymol__rpc__pb2.Empty.SerializeToString, + response_deserializer=pymol__rpc__pb2.StatusResponse.FromString, + _registered_method=True) + self.Center = channel.unary_unary( + '/PyMolRPC/Center', + request_serializer=pymol__rpc__pb2.CenterRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.StatusResponse.FromString, + _registered_method=True) + self.Zoom = channel.unary_unary( + '/PyMolRPC/Zoom', + request_serializer=pymol__rpc__pb2.ZoomRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.StatusResponse.FromString, + _registered_method=True) + self.Rotate = channel.unary_unary( + '/PyMolRPC/Rotate', + request_serializer=pymol__rpc__pb2.RotateRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.StatusResponse.FromString, + _registered_method=True) + self.Move = channel.unary_unary( + '/PyMolRPC/Move', + request_serializer=pymol__rpc__pb2.MoveRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.StatusResponse.FromString, + _registered_method=True) + self.Hide = channel.unary_unary( + '/PyMolRPC/Hide', + request_serializer=pymol__rpc__pb2.ShowHideRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.StatusResponse.FromString, + _registered_method=True) + self.Show = channel.unary_unary( + '/PyMolRPC/Show', + request_serializer=pymol__rpc__pb2.ShowHideRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.StatusResponse.FromString, + _registered_method=True) + self.Color = channel.unary_unary( + '/PyMolRPC/Color', + request_serializer=pymol__rpc__pb2.ColorRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.StatusResponse.FromString, + _registered_method=True) + self.SetTransparency = channel.unary_unary( + '/PyMolRPC/SetTransparency', + request_serializer=pymol__rpc__pb2.TransparencyRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.StatusResponse.FromString, + _registered_method=True) + self.Select = channel.unary_unary( + '/PyMolRPC/Select', + request_serializer=pymol__rpc__pb2.SelectionRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.StatusResponse.FromString, + _registered_method=True) + self.DeleteSelection = channel.unary_unary( + '/PyMolRPC/DeleteSelection', + request_serializer=pymol__rpc__pb2.SelectionRequest.SerializeToString, + response_deserializer=pymol__rpc__pb2.StatusResponse.FromString, + _registered_method=True) + + +class PyMolRPCServicer(object): + """Missing associated documentation comment in .proto file.""" + + def Ping(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Do(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Label(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Sphere(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Cylinder(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def LoadPDB(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def LoadMolBlock(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def LoadFile(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def LoadSurface(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetNames(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetAtomCoords(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DeleteObject(self, request, context): + """Backward compatibility methods + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DeleteAll(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Center(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Zoom(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Rotate(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Move(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Hide(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Show(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Color(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def SetTransparency(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Select(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DeleteSelection(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_PyMolRPCServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Ping': grpc.unary_unary_rpc_method_handler( + servicer.Ping, + request_deserializer=pymol__rpc__pb2.Empty.FromString, + response_serializer=pymol__rpc__pb2.StatusResponse.SerializeToString, + ), + 'Do': grpc.unary_unary_rpc_method_handler( + servicer.Do, + request_deserializer=pymol__rpc__pb2.CommandRequest.FromString, + response_serializer=pymol__rpc__pb2.CommandResponse.SerializeToString, + ), + 'Label': grpc.unary_unary_rpc_method_handler( + servicer.Label, + request_deserializer=pymol__rpc__pb2.LabelRequest.FromString, + response_serializer=pymol__rpc__pb2.StatusResponse.SerializeToString, + ), + 'Sphere': grpc.unary_unary_rpc_method_handler( + servicer.Sphere, + request_deserializer=pymol__rpc__pb2.SphereRequest.FromString, + response_serializer=pymol__rpc__pb2.StatusResponse.SerializeToString, + ), + 'Cylinder': grpc.unary_unary_rpc_method_handler( + servicer.Cylinder, + request_deserializer=pymol__rpc__pb2.CylinderRequest.FromString, + response_serializer=pymol__rpc__pb2.StatusResponse.SerializeToString, + ), + 'LoadPDB': grpc.unary_unary_rpc_method_handler( + servicer.LoadPDB, + request_deserializer=pymol__rpc__pb2.LoadMolRequest.FromString, + response_serializer=pymol__rpc__pb2.CommandResponse.SerializeToString, + ), + 'LoadMolBlock': grpc.unary_unary_rpc_method_handler( + servicer.LoadMolBlock, + request_deserializer=pymol__rpc__pb2.LoadMolRequest.FromString, + response_serializer=pymol__rpc__pb2.CommandResponse.SerializeToString, + ), + 'LoadFile': grpc.unary_unary_rpc_method_handler( + servicer.LoadFile, + request_deserializer=pymol__rpc__pb2.LoadFileRequest.FromString, + response_serializer=pymol__rpc__pb2.CommandResponse.SerializeToString, + ), + 'LoadSurface': grpc.unary_unary_rpc_method_handler( + servicer.LoadSurface, + request_deserializer=pymol__rpc__pb2.LoadSurfaceRequest.FromString, + response_serializer=pymol__rpc__pb2.CommandResponse.SerializeToString, + ), + 'GetNames': grpc.unary_unary_rpc_method_handler( + servicer.GetNames, + request_deserializer=pymol__rpc__pb2.GetNamesRequest.FromString, + response_serializer=pymol__rpc__pb2.GetNamesResponse.SerializeToString, + ), + 'GetAtomCoords': grpc.unary_unary_rpc_method_handler( + servicer.GetAtomCoords, + request_deserializer=pymol__rpc__pb2.GetAtomCoordsRequest.FromString, + response_serializer=pymol__rpc__pb2.GetAtomCoordsResponse.SerializeToString, + ), + 'DeleteObject': grpc.unary_unary_rpc_method_handler( + servicer.DeleteObject, + request_deserializer=pymol__rpc__pb2.ObjectRequest.FromString, + response_serializer=pymol__rpc__pb2.StatusResponse.SerializeToString, + ), + 'DeleteAll': grpc.unary_unary_rpc_method_handler( + servicer.DeleteAll, + request_deserializer=pymol__rpc__pb2.Empty.FromString, + response_serializer=pymol__rpc__pb2.StatusResponse.SerializeToString, + ), + 'Center': grpc.unary_unary_rpc_method_handler( + servicer.Center, + request_deserializer=pymol__rpc__pb2.CenterRequest.FromString, + response_serializer=pymol__rpc__pb2.StatusResponse.SerializeToString, + ), + 'Zoom': grpc.unary_unary_rpc_method_handler( + servicer.Zoom, + request_deserializer=pymol__rpc__pb2.ZoomRequest.FromString, + response_serializer=pymol__rpc__pb2.StatusResponse.SerializeToString, + ), + 'Rotate': grpc.unary_unary_rpc_method_handler( + servicer.Rotate, + request_deserializer=pymol__rpc__pb2.RotateRequest.FromString, + response_serializer=pymol__rpc__pb2.StatusResponse.SerializeToString, + ), + 'Move': grpc.unary_unary_rpc_method_handler( + servicer.Move, + request_deserializer=pymol__rpc__pb2.MoveRequest.FromString, + response_serializer=pymol__rpc__pb2.StatusResponse.SerializeToString, + ), + 'Hide': grpc.unary_unary_rpc_method_handler( + servicer.Hide, + request_deserializer=pymol__rpc__pb2.ShowHideRequest.FromString, + response_serializer=pymol__rpc__pb2.StatusResponse.SerializeToString, + ), + 'Show': grpc.unary_unary_rpc_method_handler( + servicer.Show, + request_deserializer=pymol__rpc__pb2.ShowHideRequest.FromString, + response_serializer=pymol__rpc__pb2.StatusResponse.SerializeToString, + ), + 'Color': grpc.unary_unary_rpc_method_handler( + servicer.Color, + request_deserializer=pymol__rpc__pb2.ColorRequest.FromString, + response_serializer=pymol__rpc__pb2.StatusResponse.SerializeToString, + ), + 'SetTransparency': grpc.unary_unary_rpc_method_handler( + servicer.SetTransparency, + request_deserializer=pymol__rpc__pb2.TransparencyRequest.FromString, + response_serializer=pymol__rpc__pb2.StatusResponse.SerializeToString, + ), + 'Select': grpc.unary_unary_rpc_method_handler( + servicer.Select, + request_deserializer=pymol__rpc__pb2.SelectionRequest.FromString, + response_serializer=pymol__rpc__pb2.StatusResponse.SerializeToString, + ), + 'DeleteSelection': grpc.unary_unary_rpc_method_handler( + servicer.DeleteSelection, + request_deserializer=pymol__rpc__pb2.SelectionRequest.FromString, + response_serializer=pymol__rpc__pb2.StatusResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'PyMolRPC', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('PyMolRPC', rpc_method_handlers) + + + # This class is part of an EXPERIMENTAL API. +class PyMolRPC(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def Ping(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/Ping', + pymol__rpc__pb2.Empty.SerializeToString, + pymol__rpc__pb2.StatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Do(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/Do', + pymol__rpc__pb2.CommandRequest.SerializeToString, + pymol__rpc__pb2.CommandResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Label(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/Label', + pymol__rpc__pb2.LabelRequest.SerializeToString, + pymol__rpc__pb2.StatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Sphere(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/Sphere', + pymol__rpc__pb2.SphereRequest.SerializeToString, + pymol__rpc__pb2.StatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Cylinder(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/Cylinder', + pymol__rpc__pb2.CylinderRequest.SerializeToString, + pymol__rpc__pb2.StatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def LoadPDB(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/LoadPDB', + pymol__rpc__pb2.LoadMolRequest.SerializeToString, + pymol__rpc__pb2.CommandResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def LoadMolBlock(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/LoadMolBlock', + pymol__rpc__pb2.LoadMolRequest.SerializeToString, + pymol__rpc__pb2.CommandResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def LoadFile(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/LoadFile', + pymol__rpc__pb2.LoadFileRequest.SerializeToString, + pymol__rpc__pb2.CommandResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def LoadSurface(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/LoadSurface', + pymol__rpc__pb2.LoadSurfaceRequest.SerializeToString, + pymol__rpc__pb2.CommandResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def GetNames(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/GetNames', + pymol__rpc__pb2.GetNamesRequest.SerializeToString, + pymol__rpc__pb2.GetNamesResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def GetAtomCoords(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/GetAtomCoords', + pymol__rpc__pb2.GetAtomCoordsRequest.SerializeToString, + pymol__rpc__pb2.GetAtomCoordsResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def DeleteObject(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/DeleteObject', + pymol__rpc__pb2.ObjectRequest.SerializeToString, + pymol__rpc__pb2.StatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def DeleteAll(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/DeleteAll', + pymol__rpc__pb2.Empty.SerializeToString, + pymol__rpc__pb2.StatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Center(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/Center', + pymol__rpc__pb2.CenterRequest.SerializeToString, + pymol__rpc__pb2.StatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Zoom(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/Zoom', + pymol__rpc__pb2.ZoomRequest.SerializeToString, + pymol__rpc__pb2.StatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Rotate(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/Rotate', + pymol__rpc__pb2.RotateRequest.SerializeToString, + pymol__rpc__pb2.StatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Move(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/Move', + pymol__rpc__pb2.MoveRequest.SerializeToString, + pymol__rpc__pb2.StatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Hide(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/Hide', + pymol__rpc__pb2.ShowHideRequest.SerializeToString, + pymol__rpc__pb2.StatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Show(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/Show', + pymol__rpc__pb2.ShowHideRequest.SerializeToString, + pymol__rpc__pb2.StatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Color(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/Color', + pymol__rpc__pb2.ColorRequest.SerializeToString, + pymol__rpc__pb2.StatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def SetTransparency(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/SetTransparency', + pymol__rpc__pb2.TransparencyRequest.SerializeToString, + pymol__rpc__pb2.StatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Select(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/Select', + pymol__rpc__pb2.SelectionRequest.SerializeToString, + pymol__rpc__pb2.StatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def DeleteSelection(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/PyMolRPC/DeleteSelection', + pymol__rpc__pb2.SelectionRequest.SerializeToString, + pymol__rpc__pb2.StatusResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/proto/pymol_rpc.proto b/proto/pymol_rpc.proto new file mode 100644 index 000000000..e71dfb525 --- /dev/null +++ b/proto/pymol_rpc.proto @@ -0,0 +1,183 @@ +syntax = "proto3"; + +message Empty {} + +message StatusResponse { + bool success = 1; + string message = 2; +} + +message Vector3 { + float x = 1; + float y = 2; + float z = 3; +} + +message ColorRGB { + float r = 1; + float g = 2; + float b = 3; +} + +message CommandRequest { + string command = 1; +} + +message CommandResponse { + string result = 1; +} + +message LabelRequest { + Vector3 pos = 1; + string text = 2; + string id = 3; + ColorRGB color = 4; +} + +message SphereRequest { + Vector3 pos = 1; + float radius = 2; + ColorRGB color = 3; + string id = 4; + bool extend = 5; + bool transparent = 6; + float transparency = 7; +} + +message CylinderRequest { + Vector3 end1 = 1; + Vector3 end2 = 2; + float radius = 3; + ColorRGB color1 = 4; + optional ColorRGB color2 = 5; + string id = 6; + bool extend = 7; + bool transparent = 8; + float transparency = 9; +} + +message LoadMolRequest { + string data = 1; + string obj_name = 2; + optional string color_scheme = 3; + bool replace = 4; +} + +message LoadFileRequest { + string file_name = 1; + string obj_name = 2; + optional string format = 3; + optional string color_scheme = 4; + bool replace = 5; +} + +message LoadSurfaceRequest { + oneof source { + string data = 1; + string file_name = 2; + } + string obj_name = 3; + optional string format = 4; + float surface_level = 5; +} + +message GetNamesRequest { + string what = 1; + bool enabled_only = 2; +} + +message GetNamesResponse { + repeated string names = 1; +} + +message GetAtomCoordsRequest { + string selection = 1; + int32 state = 2; +} + +message GetAtomCoordsResponse { + repeated Vector3 coordinates = 1; +} + +/* --- Backward compatibility requests --- */ + +message ObjectRequest { + string name = 1; +} + +message SelectionRequest { + string selection = 1; +} + +message CenterRequest { + string selection = 1; + bool animate = 2; +} + +message ZoomRequest { + string selection = 1; + float buffer = 2; + int32 state = 3; + bool animate = 4; +} + +message RotateRequest { + string axis = 1; + float angle = 2; + string selection = 3; +} + +message MoveRequest { + string axis = 1; + float distance = 2; + string selection = 3; +} + +message ShowHideRequest { + string representation = 1; + string selection = 2; +} + +message ColorRequest { + string color = 1; + string selection = 2; +} + +message TransparencyRequest { + float transparency = 1; + string selection = 2; +} + +service PyMolRPC { + rpc Ping(Empty) returns (StatusResponse); + rpc Do(CommandRequest) returns (CommandResponse); + + rpc Label(LabelRequest) returns (StatusResponse); + rpc Sphere(SphereRequest) returns (StatusResponse); + rpc Cylinder(CylinderRequest) returns (StatusResponse); + + rpc LoadPDB(LoadMolRequest) returns (CommandResponse); + rpc LoadMolBlock(LoadMolRequest) returns (CommandResponse); + rpc LoadFile(LoadFileRequest) returns (CommandResponse); + rpc LoadSurface(LoadSurfaceRequest) returns (CommandResponse); + + rpc GetNames(GetNamesRequest) returns (GetNamesResponse); + rpc GetAtomCoords(GetAtomCoordsRequest) returns (GetAtomCoordsResponse); + + // Backward compatibility methods + rpc DeleteObject(ObjectRequest) returns (StatusResponse); + rpc DeleteAll(Empty) returns (StatusResponse); + + rpc Center(CenterRequest) returns (StatusResponse); + rpc Zoom(ZoomRequest) returns (StatusResponse); + rpc Rotate(RotateRequest) returns (StatusResponse); + rpc Move(MoveRequest) returns (StatusResponse); + + rpc Hide(ShowHideRequest) returns (StatusResponse); + rpc Show(ShowHideRequest) returns (StatusResponse); + rpc Color(ColorRequest) returns (StatusResponse); + rpc SetTransparency(TransparencyRequest) returns (StatusResponse); + + rpc Select(SelectionRequest) returns (StatusResponse); + rpc DeleteSelection(SelectionRequest) returns (StatusResponse); +} From e26b4ea5197988ce669a0367c12a840e2290164e Mon Sep 17 00:00:00 2001 From: Martin Urban Date: Tue, 9 Sep 2025 23:18:24 +0200 Subject: [PATCH 2/2] Add PR triggers to CI workflow Updated the CI workflow to trigger on pushes to the "production" branch and pull requests targeting "main," "production," or "incubating." This ensures proper testing coverage for additional branches and improves development workflow consistency. --- .github/workflows/wheels.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/wheels.yaml b/.github/workflows/wheels.yaml index f7a30b8a8..c3804923d 100644 --- a/.github/workflows/wheels.yaml +++ b/.github/workflows/wheels.yaml @@ -17,7 +17,10 @@ name: Build Wheels on: push: branches: + - production - scikit_build_core + pull_request: + branches: [ "main", "production", "incubating" ] env: VCPKG_ROOT: ${{ github.workspace }}/vendor/vcpkg