11import copy
2+ import inspect
23from re import compile
34import time
45import requests
@@ -49,7 +50,9 @@ def _check_endpoint_name(name):
4950
5051
5152class Client :
52- def __init__ (self , endpoint , query_timeout = 1000 ):
53+ def __init__ (
54+ self , endpoint , query_timeout = 1000 , remote_server = False , localhost_endpoint = None
55+ ):
5356 """
5457 Connects to a running server.
5558
@@ -63,10 +66,19 @@ def __init__(self, endpoint, query_timeout=1000):
6366
6467 query_timeout : float, optional
6568 The timeout for query operations.
69+
70+ remote_server : bool, optional
71+ Whether client is a remote TabPy server.
72+
73+ localhost_endpoint : str, optional
74+ The localhost endpoint with potentially different protocol and
75+ port compared to the main endpoint parameter.
6676 """
6777 _check_hostname (endpoint )
6878
6979 self ._endpoint = endpoint
80+ self ._remote_server = remote_server
81+ self ._localhost_endpoint = localhost_endpoint
7082
7183 session = requests .session ()
7284 session .verify = False
@@ -232,6 +244,12 @@ def deploy(self, name, obj, description="", schema=None, override=False, is_publ
232244 --------
233245 remove, get_endpoints
234246 """
247+ if self ._remote_server :
248+ return self ._remote_deploy (
249+ name , obj ,
250+ description = description , schema = schema , override = override , is_public = is_public
251+ )
252+
235253 endpoint = self .get_endpoints ().get (name )
236254 version = 1
237255 if endpoint :
@@ -390,6 +408,7 @@ def _gen_endpoint(self, name, obj, description, version=1, schema=None, is_publi
390408 "methods" : endpoint_object .get_methods (),
391409 "required_files" : [],
392410 "required_packages" : [],
411+ "docstring" : endpoint_object .get_docstring (),
393412 "schema" : copy .copy (schema ),
394413 "is_public" : is_public ,
395414 }
@@ -419,6 +438,7 @@ def _wait_for_endpoint_deployment(
419438 logger .info (
420439 f"Waiting for endpoint { endpoint_name } to deploy to " f"version { version } "
421440 )
441+ time .sleep (interval )
422442 start = time .time ()
423443 while True :
424444 ep_status = self .get_status ()
@@ -447,6 +467,72 @@ def _wait_for_endpoint_deployment(
447467 logger .info (f"Sleeping { interval } ..." )
448468 time .sleep (interval )
449469
470+ def _remote_deploy (
471+ self , name , obj , description = "" , schema = None , override = False , is_public = False
472+ ):
473+ """
474+ Remotely deploy a Python function using the /evaluate endpoint. Takes the same inputs
475+ as deploy.
476+ """
477+ remote_script = self ._gen_remote_script ()
478+ remote_script += f"{ inspect .getsource (obj )} \n "
479+
480+ remote_script += (
481+ f"client.deploy("
482+ f"'{ name } ', { obj .__name__ } , '{ description } ', "
483+ f"override={ override } , is_public={ is_public } , schema={ schema } "
484+ f")"
485+ )
486+
487+ return self ._evaluate_remote_script (remote_script )
488+
489+ def _gen_remote_script (self ):
490+ """
491+ Generates a remote script for TabPy client connection with credential handling.
492+
493+ Returns:
494+ str: A Python script to establish a TabPy client connection
495+ """
496+ remote_script = [
497+ "from tabpy.tabpy_tools.client import Client" ,
498+ f"client = Client('{ self ._localhost_endpoint or self ._endpoint } ')"
499+ ]
500+
501+ remote_script .append (
502+ f"client.set_credentials('{ auth .username } ', '{ auth .password } ')"
503+ ) if (auth := self ._service .service_client .network_wrapper .auth ) else None
504+
505+ return "\n " .join (remote_script ) + "\n "
506+
507+ def _evaluate_remote_script (self , remote_script ):
508+ """
509+ Uses TabPy /evaluate endpoint to execute a remote TabPy client script.
510+
511+ Parameters
512+ ----------
513+ remote_script : str
514+ The script to execute remotely.
515+ """
516+ print (f"Remote script:\n { remote_script } \n " )
517+ url = f"{ self ._endpoint } evaluate"
518+ headers = {"Content-Type" : "application/json" }
519+ payload = {"data" : {}, "script" : remote_script }
520+
521+ response = requests .post (
522+ url ,
523+ headers = headers ,
524+ auth = self ._service .service_client .network_wrapper .auth ,
525+ json = payload
526+ )
527+
528+ msg = response .text .replace ('null' , 'Success' )
529+ if "Ad-hoc scripts have been disabled" in msg :
530+ msg += "\n [Deployment to remote tabpy client not allowed.]"
531+
532+ status_message = (f"{ response .status_code } - { msg } \n " )
533+ print (status_message )
534+ return status_message
535+
450536 def set_credentials (self , username , password ):
451537 """
452538 Set credentials for all the TabPy client-server communication
0 commit comments