سؤال

لدي فصل دراسي أرغب في عرضه كخدمة عن بعد باستخدام pythons SimpleXMLRPCServer.يبدو بدء تشغيل الخادم كما يلي:

server = SimpleXMLRPCServer((serverSettings.LISTEN_IP,serverSettings.LISTEN_PORT))

service = Service()

server.register_instance(service)
server.serve_forever()

لدي بعد ذلك فئة ServiceRemote التي تبدو كما يلي:

def __init__(self,ip,port):
    self.rpcClient = xmlrpclib.Server('http://%s:%d' %(ip,port))

def __getattr__(self, name):
    # forward all calls to the rpc client
    return getattr(self.rpcClient, name)

لذلك ستتم إعادة توجيه كافة الاستدعاءات على كائن ServiceRemote إلى xmlrpclib.Server، والذي يقوم بعد ذلك بإعادة توجيهها إلى الخادم البعيد.المشكلة هي طريقة في الخدمة تأخذ varargs المسماة:

@useDb
def select(self, db, fields, **kwargs):
    pass

يغلف الديكورuseDb الوظيفة، وينشئ قاعدة البيانات قبل الاستدعاء ويفتحها، ثم يغلقها بعد انتهاء الاستدعاء قبل إرجاع النتيجة.

عندما أتصل بهذه الطريقة، أحصل على الخطأ "يتصل() حصلت على وسيطة كلمة رئيسية غير متوقعة "اسم"".إذن، هل من الممكن استدعاء الأساليب التي تأخذ وسيطات مسماة متغيرة عن بعد؟أو هل سأضطر إلى إنشاء تجاوز لكل طريقة مختلفة أحتاجها.


شكرا على الردود.لقد قمت بتغيير الكود الخاص بي قليلاً لذا لم يعد السؤال يمثل مشكلة.لكنني أعرف الآن هذا للرجوع إليه مستقبلاً إذا كنت بحاجة بالفعل إلى تنفيذ الحجج الموضعية ودعم الاستدعاء عن بعد.أعتقد أن الجمع بين نهجي توماس وبرابتاكس سيكون جيدًا.تحويل kwargs إلى وسيطات موضعية على العميل من خلال xmlrpclient، مع وجود غلاف على جانب خادم الأساليب لتفريغ الوسائط الموضعية.

هل كانت مفيدة؟

المحلول

لا يمكنك القيام بذلك باستخدام xmlrpc العادي لأنه لا يحتوي على فكرة عن وسيطات الكلمات الرئيسية.ومع ذلك، يمكنك تركيب هذا كبروتوكول أعلى xmlrpc الذي سيمرر دائمًا قائمة كوسيطة أولى وقاموس كوسيطة ثانية، ثم قم بتوفير رمز الدعم المناسب بحيث يصبح هذا شفافًا لاستخدامك، المثال أدناه:

الخادم

from SimpleXMLRPCServer import SimpleXMLRPCServer

class Server(object):
    def __init__(self, hostport):
        self.server = SimpleXMLRPCServer(hostport)

    def register_function(self, function, name=None):
        def _function(args, kwargs):
            return function(*args, **kwargs)
        _function.__name__ = function.__name__
        self.server.register_function(_function, name)

    def serve_forever(self):
        self.server.serve_forever()

#example usage
server = Server(('localhost', 8000))
def test(arg1, arg2):
    print 'arg1: %s arg2: %s' % (arg1, arg2)
    return 0
server.register_function(test)
server.serve_forever()

عميل

import xmlrpclib

class ServerProxy(object):
    def __init__(self, url):
        self._xmlrpc_server_proxy = xmlrpclib.ServerProxy(url)
    def __getattr__(self, name):
        call_proxy = getattr(self._xmlrpc_server_proxy, name)
        def _call(*args, **kwargs):
            return call_proxy(args, kwargs)
        return _call

#example usage
server = ServerProxy('http://localhost:8000')
server.test(1, 2)
server.test(arg2=2, arg1=1)
server.test(1, arg2=2)
server.test(*[1,2])
server.test(**{'arg1':1, 'arg2':2})

نصائح أخرى

لا يحتوي XML-RPC حقًا على مفهوم "وسيطات الكلمات الرئيسية"، لذلك لا يحاول xmlrpclib دعمها.ستحتاج إلى اختيار اصطلاح، ثم تعديل xmlrpclib._Method لقبول وسيطات الكلمات الرئيسية وتمريرها باستخدام هذا الاصطلاح.

على سبيل المثال، كنت أعمل مع خادم XML-RPC الذي مرر وسيطات الكلمات الرئيسية كوسيطتين، '-KEYWORD' متبوعة بالوسيطة الفعلية، في قائمة مسطحة.لم يعد بإمكاني الوصول إلى الكود الذي كتبته للوصول إلى خادم XML-RPC من Python، ولكنه كان بسيطًا إلى حد ما، على غرار:

import xmlrpclib

_orig_Method = xmlrpclib._Method

class KeywordArgMethod(_orig_Method):     
    def __call__(self, *args, **kwargs):
        if args and kwargs:
            raise TypeError, "Can't pass both positional and keyword args"
        args = list(args) 
        for key in kwargs:
            args.append('-%s' % key.upper())
            args.append(kwargs[key])
       return _orig_Method.__call__(self, *args)     

xmlrpclib._Method = KeywordArgMethod

يستخدم نظام القرد التصحيحي لأن هذه هي الطريقة الأسهل للقيام بذلك، وذلك بسبب بعض الاستخدامات غير التقليدية للوحدات النمطية العالمية والسمات المشوهة بالأسماء (__request، على سبيل المثال) في فئة ServerProxy.

بقدر ما أعرف، فإن البروتوكول الأساسي لا يدعم varargs المسماة (أو أي وسائط مسماة في هذا الشأن).الحل البديل لذلك هو إنشاء غلاف يأخذ **kwargs وتمريره كقاموس عادي إلى الطريقة التي تريد الاتصال بها.شيء من هذا القبيل

جانب الخادم:

def select_wrapper(self, db, fields, kwargs):
    """accepts an ordinary dict which can pass through xmlrpc"""
    return select(self,db,fields, **kwargs)

من ناحية العميل:

def select(self, db, fields, **kwargs):
    """you can call it with keyword arguments and they will be packed into a dict"""
    return self.rpcClient.select_wrapper(self,db,fields,kwargs)

تنصل:يُظهر الكود الفكرة العامة، ويمكنك القيام بذلك بشكل أكثر وضوحًا (على سبيل المثال كتابة مصمم ديكور للقيام بذلك).

كما قال توماس ووترز، لا يحتوي XML-RPC على وسيطات للكلمات الرئيسية.فقط ترتيب الوسائط هو المهم فيما يتعلق بالبروتوكول ويمكن تسميتها بأي شيء في XML:arg0، arg1، arg2 جيد تمامًا، كما هو الحال مع الجبن والحلوى ولحم الخنزير المقدد لنفس الحجج.

ربما يجب عليك ببساطة إعادة التفكير في استخدامك للبروتوكول؟سيكون استخدام شيء مثل المستند/الصابون الحرفي أفضل بكثير من الحل البديل مثل تلك المقدمة في الإجابات الأخرى هنا.وبطبيعة الحال، قد لا يكون هذا ممكنا.

باستخدام النصائح المذكورة أعلاه، قمت بإنشاء بعض أكواد العمل.

غلاف طريقة الخادم:

def unwrap_kwargs(func):
    def wrapper(*args, **kwargs):
        print args
        if args and isinstance(args[-1], list) and len(args[-1]) == 2 and "kwargs" == args[-1][0]:
            func(*args[:-1], **args[-1][1])
        else:
            func(*args, **kwargs)
    return wrapper

إعداد العميل (يفعل مرة واحدة):

_orig_Method = xmlrpclib._Method

class KeywordArgMethod(_orig_Method):     
    def __call__(self, *args, **kwargs):
        args = list(args) 
        if kwargs:
            args.append(("kwargs", kwargs))
        return _orig_Method.__call__(self, *args)

xmlrpclib._Method = KeywordArgMethod

لقد اختبرت هذا، وهو يدعم الطريقة باستخدام الوسائط الثابتة والموضعية والكلمات الرئيسية.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top