pythonpython-requestsgrequestsvolttron

grequests not having the attributes response.status_code


Can I get a tip for how to do an http request via a VOLTTRON agent using grequests? From what I know about VOLTTRON I think grequests are required for an asynchronous methods.

Small snip from my VOLTTRON agent code, line 140 on git gist:

    _log.info(f'*** [grequests INFO] *** -  onstart sucess!')
    self.core.schedule(cron('*/1 * * * *'), self.cron_function)


def cron_function(self):
    def simple_request(url):
        page = requests.get(url)
        return page

    _log.info(f'*** [grequests INFO] *** - starting CRON Processes')


    request = grequests.get(simple_request(self.url1))
    _log.info(f'*** [grequests INFO] *** - sending out {request}')

    response = grequests.send(request)

    _log.info(f'*** [grequests INFO] *** - response code {response.status_code}')
    _log.info(f'*** [grequests INFO] *** - response text {response.content}')

The traceback when I install the agent has to do with grequests not having the attributes response.status_code. Should I be wrapping the requests library with a grequests to make my script asynchronous? Sorry not a lot wisdom here but I only need to do one http request and I think I realize my agent code needs to asynchronous to accommodate what all else is happening on the VOLTTRON platform.

2021-05-02 10:28:10,875 (grequesteragent-0.1 39693) <stderr> ERROR: Traceback (most recent call last):
2021-05-02 10:28:10,875 (grequesteragent-0.1 39693) <stderr> ERROR:   File "src/gevent/greenlet.py", line 854, in gevent._gevent_cgreenlet.Greenlet.run
2021-05-02 10:28:10,875 (grequesteragent-0.1 39693) <stderr> ERROR:   File "/home/dan/Desktop/volttron/volttron/platform/vip/agent/core.py", line 455, in wrapper
2021-05-02 10:28:10,875 (grequesteragent-0.1 39693) <stderr> ERROR:     event.function(*event.args, **event.kwargs)
2021-05-02 10:28:10,876 (grequesteragent-0.1 39693) <stderr> ERROR:   File "/home/dan/.volttron/agents/5b1bc704-ff6e-4d3d-a186-d5ec6b348686/grequesteragent-0.1/grequester/agent.py", line 153, in cron_function
2021-05-02 10:28:10,877 (grequesteragent-0.1 39693) <stderr> ERROR:     _log.info(f'*** [grequests INFO] *** - response code {response.status_code}')
2021-05-02 10:28:10,877 (grequesteragent-0.1 39693) <stderr> ERROR: AttributeError: 'gevent._gevent_cgreenlet.Greenlet' object has no attribute 'status_code'
2021-05-02 10:28:10,877 (grequesteragent-0.1 39693) <stderr> ERROR: 2021-05-02T15:20:00Z <Greenlet at 0x7f44a146c9d0: wrapper> failed with AttributeError
2021-05-02 10:28:10,877 (grequesteragent-0.1 39693) <stderr> ERROR:

Solution

  • Example code from the Ecobee driver that I've annotated:

    def call_grequest(method_name, url, **kwargs):
        """
        Make grequest calls to remote api
        :param method_name: method type - put/get/delete
        :param url: http URL suffix
        :param kwargs: Additional arguments for http request
        :return: grequest response
        """
        try:
            # Get the correct grequests method for the HTTP request type
            fn = getattr(grequests, method_name)
            # use that method with the url and any args to send the request
            request = fn(url, **kwargs)
            # use map to pull out the response
            response = grequests.map([request])[0]
            if response and isinstance(response, list):
                response = response[0]
            # handle cases were we receive a non-200 response
            response.raise_for_status()
            return response
        # handle specific error cases by logging a message and re-raising so we can handle it elsewhere
        except (ConnectionError, NewConnectionError) as e:
            _log.error(f"Error connecting to {url} with args {kwargs}: {e}")
            raise e
    
    
    def make_ecobee_request(request_type, url, **kwargs):
        """
        Wrapper around making arbitrary GET and POST requests to remote Ecobee API
        :return: Ecobee API response using provided request content
        """
        # Generate appropriate grequests object
        # HTTP request method
        if request_type.lower() in ["get", "post"]:
            # we use verify so we can work with SSL
            response = call_grequest(request_type.lower(), url, verify=requests.certs.where(), timeout=30, **kwargs)
        else:
            raise ValueError(f"Unsupported request type {request_type} for Ecobee driver.")
        # Send request and extract data from response
        headers = response.headers
        # parse JSON content
        if "json" in headers.get("Content-Type"):
            return response.json()
        else:
            # parse Text content
            content = response.content
            if isinstance(content, bytes):
                content = jsonapi.loads(response.decode("UTF-8"))
            return content