2

I think it’s best if I explain what I want to do, first: I am currently developing a testing framework using Python, pytest and Pyro4. It is indented to test software running on hardware prototypes. The developer/tester is executing the test script on his PC locally using pytest. In the test script, actions are executed using Pyro4 proxy objects which connect to a Pyro4 server (daemon) running on a Raspberry Pi. The implementation on the Raspberry Pi adapts various sensors and relays that are connected to the device under test, represented as objects, which are then passed to the Pyro4 daemon.

Here is a simplified example of how a test script could look like. What it does: Press button x and measure if LED y lights up.

@pytest.fixture(scope="module")
def test_components():
    # initialize a dict of pyro proxy objects that represent a hardware component, controlled by the Raspberry Pi
    a_dict = {}
    a_dict['button_x'] = Pyro4.Proxy("PYRO:button_x@192.168.1.2:1234")
    a_dict['status_led'] = Pyro4.Proxy("PYRO:status_led@192.168.1.2:1234")
    return a_dict

def test_functionality_button_led(test_components):
    test_components['button_x'].set_for(0.5)    # presses button x / enables relay x for 0.5 seconds
    time.sleep(1)
    assert test_compontents['status_led'].get_level() > 0    # assert that led is on

Now, to my initial question: After each test run, i want to have a log file that shows the function calls, their input values and their return values. So it should look roughly like this:

0.1:    test_components('button_x').set_for(0.5) [None]
0.2:    time.sleep(1) [None]
1.2:    assert test_compontents('status_led').get_level() [1] > 0

I had two options in mind but was not able to archieve this:

Leveraging pytest

I was hoping to find some switch or plugin (for example pytest-logging) which would help me do this, but i simply could not find anything to archieve this.

Leveraging Pyro

This is where i got a bit farer. I was hoping to be able to subclass Pyro4 Proxy (the client) stuff to get this done. I subclassed Pyro4.Proxy and reimplemented the __getattr__ method, which is called to get the methods from the remote object at runtime. Using the name attribute, i can now at least detect when and which method is called, but i can still not get the return and input values.

class ImprovedProxy(Pyro4.Proxy):

    def __getattr__(self, name):

        print(name)
        return = Pyro4.Proxy.__getattr__(self, name)

To get the input and output values i would need to subclass and replace the _RemoteMethod class, which is returned by Pyro4.Proxy.__getattr__(self, name) but i couldn't even run the original code from Pyro4.Proxy.__getattr__(self, name) in my ImprovedProxy.__getattr__(self, name) without loads of errors, see for yourself:

class ImprovedProxy(Pyro4.Proxy):


    def __getattr__(self, name):
        if name in Pyro4.Proxy.__pyroAttributes:
            # allows it to be safely pickled
            raise AttributeError(name)
        if config.METADATA:
            # get metadata if it's not there yet
            if not self._pyroMethods and not self._pyroAttrs:
                self._pyroGetMetadata()
        if name in self._pyroAttrs:
            return self._pyroInvoke("__getattr__", (name,), None)
        if config.METADATA and name not in self._pyroMethods:
            # client side check if the requested attr actually exists
            raise AttributeError("remote object '%s' has no exposed attribute or method '%s'" % (self._pyroUri, name))
        if self.__async:
            return _AsyncRemoteMethod(self, name, self._pyroMaxRetries)
        return _RemoteMethod(self._pyroInvoke, name, self._pyroMaxRetries)

The Traceback was:

  File "C:\...\client.py", line 38, in __getattr__
    if name in Pyro4.Proxy.__pyroAttributes:
AttributeError: type object 'Proxy' has no attribute '_ImprovedProxy__pyroAttributes'

Not sure why the name of my subclass is prepended here and after fiddling with the code for a while, i am not sure if this is the right way.

Is there a better way to solve this? As a last resort i could log actions on the server and send it to the client, but i would like to avoid this, one reason being that i have multiple of these test adapters running which would then require precise time syncronisation.

I hope i could explain this well enought. Looking forward to your replies.

4

1 回答 1

1

您在回溯中看到 ' _ImprovedProxy__pyroAttributes' 的原因是您的代码正在尝试访问基本代理上的私有属性。__getattr__您方法中的第一行。Python 正在对其标准名称进行修改,就像对所有以双下划线开头的属性所做的那样,以使其成为“私有”属性。您不应该访问此属性。

现在,我认为解决您的问题的实际解决方案很简单:_pyroInvoke改为覆盖代理的方法。这是最终被调用的,它是执行实际远程方法调用的逻辑。我认为它提供了您需要的所有论点:

def _pyroInvoke(self, methodname, vargs, kwargs, flags=0, objectId=None):
    """perform the remote method call communication"""
    ...
于 2017-06-27T18:33:10.247 回答