Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

R server #10

Merged
merged 29 commits into from
Nov 7, 2024
Merged

R server #10

merged 29 commits into from
Nov 7, 2024

Conversation

sverhoeven
Copy link
Member

@sverhoeven sverhoeven commented Sep 25, 2024

TODO

  • add missing methods to https://github.com/eWaterCycle/bmi-r/blob/master/R/abstract-bmi.R
  • add route handlers for missing methods
  • rename package from R/remotebmi to /remotebmir
  • look at other usethis commands that can be used to improve the remotebmi R package
    • coverage
    • publish
    • docs site
    • styler / tidy
  • validate against openapi.yaml, aka use Python client or swagger ui to test all methods
  • use under_score instead of camelCase for bmi methods. Which is default for lintr. Only C++ and Java use camelCase

@sverhoeven
Copy link
Member Author

Merged eWaterCycle/bmi-r#3

@sverhoeven sverhoeven marked this pull request as ready for review September 25, 2024 12:59
@BSchilperoort
Copy link
Member

Cool, I'm looking forward to trying it out! 🚀


``` r
# install.packages("pak")
pak::pak("github::eWaterCycle/remotebmi/R/remotebmi")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This command does not work,

> pak::pak("github::eWaterCycle/remotebmi/R/remotebmi")
! Using bundled GitHub PAT. Please add your own PAT using `gitcreds::gitcreds_set()`.
Error:                                                                          
! error in pak subprocess
Caused by error: 
! Could not solve package dependencies:
* github::eWaterCycle/remotebmi/R/remotebmi: ! pkgdepends resolution error for
github::eWaterCycle/remotebmi/R/remotebmi.
Caused by error: 
! Can't find R package in GitHub repo eWaterCycle/remotebmi in directory 'R/remotebmi'

Is that because of this:?

rename package from R/remotebmi to /remotebmir

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, the path is according to https://pak.r-lib.org/reference/pak_package_sources.html#github-packages-github-

I was using

cd R/remotebmi
R
devtools::load_all()
# and/or
devtools::install_local()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah that uses main branch, try pak::pak("github::eWaterCycle/remotebmi/R/remotebmi#10")

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I git cloned the repo and installed it from a local path. That worked fine

## basic example code

pak::pak("github::ClaudiaBrauer/WALRUS")
pak::pak("github::eWaterCycle/bmi-r")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get stuck on building the stringi package. install.package("stringi") fails to build, and R binaries are only available for Windows and MacOS, not linux 🙄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Managed to finally build it with install.packages("stringi", configure.args="--disable-pkg-config")

array([0.0044])
client.get_var_nbytes('Q')
'mm/h'
# TODO get_var_nbytes should return int not str
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting error! Seems like a mismapping of a function (get_var_units vs. get_var_nbytes)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, seems I fixed it in https://github.com/eWaterCycle/grpc4bmi-examples/pull/10/files, but never completed it.

Copy link
Member

@BSchilperoort BSchilperoort left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't fully follow the readme, but got past the errors and have WALRUS running 🥳

@BSchilperoort
Copy link
Member

BSchilperoort commented Sep 26, 2024

Something still seems to be going wrong. I can do the first update, but after the second .update() I can't use get_value anymore:

>>> client.update()
>>> client.get_value("Q", dest=np.array([0.]))
array([0.0044])
>>> client.update()
>>> client.get_value("Q", dest=np.array([0.]))
Traceback (most recent call last):
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_transports/default.py", line 72, in map_httpcore_exceptions
    yield
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_transports/default.py", line 236, in handle_request
    resp = self._pool.handle_request(req)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py", line 216, in handle_request
    raise exc from None
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py", line 196, in handle_request
    response = connection.handle_request(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpcore/_sync/connection.py", line 101, in handle_request
    return self._connection.handle_request(request)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpcore/_sync/http11.py", line 143, in handle_request
    raise exc
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpcore/_sync/http11.py", line 113, in handle_request
    ) = self._receive_response_headers(**kwargs)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpcore/_sync/http11.py", line 186, in _receive_response_headers
    event = self._receive_event(timeout=timeout)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpcore/_sync/http11.py", line 220, in _receive_event
    with map_exceptions({h11.RemoteProtocolError: RemoteProtocolError}):
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/contextlib.py", line 158, in __exit__
    self.gen.throw(value)
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpcore/_exceptions.py", line 14, in map_exceptions
    raise to_exc(exc) from exc
httpcore.RemoteProtocolError: illegal status line: bytearray(b'14')

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/bart/git/remotebmi/python/remotebmi/client/client.py", line 118, in get_value
    response = self.client.get(f"/get_value/{name}")
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_client.py", line 1066, in get
    return self.request(
           ^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_client.py", line 837, in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_client.py", line 926, in send
    response = self._send_handling_auth(
               ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_client.py", line 954, in _send_handling_auth
    response = self._send_handling_redirects(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_client.py", line 991, in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_client.py", line 1027, in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_transports/default.py", line 235, in handle_request
    with map_httpcore_exceptions():
         ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/contextlib.py", line 158, in __exit__
    self.gen.throw(value)
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_transports/default.py", line 89, in map_httpcore_exceptions
    raise mapped_exc(message) from exc
httpx.RemoteProtocolError: illegal status line: bytearray(b'14')

The running R server shows no errors

@sverhoeven
Copy link
Member Author

Something still seems to be going wrong. I can do the first update, but after the second .update() I can't use get_value anymore:

>>> client.update()
>>> client.get_value("Q", dest=np.array([0.]))
array([0.0044])
>>> client.update()
>>> client.get_value("Q", dest=np.array([0.]))
Traceback (most recent call last):
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_transports/default.py", line 72, in map_httpcore_exceptions
    yield
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_transports/default.py", line 236, in handle_request
    resp = self._pool.handle_request(req)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py", line 216, in handle_request
    raise exc from None
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py", line 196, in handle_request
    response = connection.handle_request(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpcore/_sync/connection.py", line 101, in handle_request
    return self._connection.handle_request(request)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpcore/_sync/http11.py", line 143, in handle_request
    raise exc
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpcore/_sync/http11.py", line 113, in handle_request
    ) = self._receive_response_headers(**kwargs)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpcore/_sync/http11.py", line 186, in _receive_response_headers
    event = self._receive_event(timeout=timeout)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpcore/_sync/http11.py", line 220, in _receive_event
    with map_exceptions({h11.RemoteProtocolError: RemoteProtocolError}):
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/contextlib.py", line 158, in __exit__
    self.gen.throw(value)
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpcore/_exceptions.py", line 14, in map_exceptions
    raise to_exc(exc) from exc
httpcore.RemoteProtocolError: illegal status line: bytearray(b'14')

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/bart/git/remotebmi/python/remotebmi/client/client.py", line 118, in get_value
    response = self.client.get(f"/get_value/{name}")
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_client.py", line 1066, in get
    return self.request(
           ^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_client.py", line 837, in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_client.py", line 926, in send
    response = self._send_handling_auth(
               ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_client.py", line 954, in _send_handling_auth
    response = self._send_handling_redirects(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_client.py", line 991, in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_client.py", line 1027, in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_transports/default.py", line 235, in handle_request
    with map_httpcore_exceptions():
         ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/contextlib.py", line 158, in __exit__
    self.gen.throw(value)
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_transports/default.py", line 89, in map_httpcore_exceptions
    raise mapped_exc(message) from exc
httpx.RemoteProtocolError: illegal status line: bytearray(b'14')

The running R server shows no errors

What does R server say and what does curl do

curl http://localhost:50051/get_value/Q 

@BSchilperoort
Copy link
Member

What does R server say and what does curl do

curl http://localhost:50051/get_value/Q 

Before the first update it returns nothing.

After one update it returns
[0.0044]
and after the second update
[0.0044]
and after a third:
[0.0047]

So that looks OK. Running the curl command and then using get_value also works fine 😕 .

  1. start remote bmi R server
  2. connect in python
  3. initialize
  4. update
  5. get value
  6. update
  7. get value --> breaks!
  8. use curl command
  9. get value works OK.

@sverhoeven
Copy link
Member Author

With httpx trace I see it fails when reusing connections
from httpx import Client
client = Client(base_url='http://localhost:50051')

def log(event_name, info):
    print(event_name, info)

In [326]: try:
     ...:   client.post('/update', extensions={"trace": log})
     ...: except Exception as e:
     ...:   print(e)
     ...: 
connection.connect_tcp.started {'host': 'localhost', 'port': 50051, 'local_address': None, 'timeout': 5.0, 'socket_options': None}
connection.connect_tcp.complete {'return_value': <httpcore._backends.sync.SyncStream object at 0x7f6f19125910>}
http11.send_request_headers.started {'request': <Request [b'POST']>}
http11.send_request_headers.complete {'return_value': None}
http11.send_request_body.started {'request': <Request [b'POST']>}
http11.send_request_body.complete {'return_value': None}
http11.receive_response_headers.started {'request': <Request [b'POST']>}
http11.receive_response_headers.complete {'return_value': (b'HTTP/1.1', 204, b'No Content', [(b'Date', b'Thu, 03 Oct 2024 14:50:36 GMT'), (b'Content-Type', b'text/plain'), (b'Content-Encoding', b'gzip'), (b'Transfer-Encoding', b'chunked')])}
http11.receive_response_body.started {'request': <Request [b'POST']>}
http11.receive_response_body.complete {'return_value': None}
http11.response_closed.started {}
http11.response_closed.complete {'return_value': None}

In [327]: try:
     ...:   client.post('/update', extensions={"trace": log})
     ...: except Exception as e:
     ...:   print(e)
     ...: 
http11.send_request_headers.started {'request': <Request [b'POST']>}
http11.send_request_headers.complete {'return_value': None}
http11.send_request_body.started {'request': <Request [b'POST']>}
http11.send_request_body.complete {'return_value': None}
http11.receive_response_headers.started {'request': <Request [b'POST']>}
http11.receive_response_headers.failed {'exception': RemoteProtocolError(RemoteProtocolError("illegal status line: bytearray(b'14')"))}
http11.response_closed.started {}
http11.response_closed.complete {'return_value': None}
illegal status line: bytearray(b'14')

Each time it fails it is not making a connection.connect_tcp.started message.

@sverhoeven
Copy link
Member Author

sverhoeven commented Oct 7, 2024

With httpx trace I see it fails when reusing connections

Error happens sometimes on POST /update on n Ubuntu 22.04 in wsl with R 44.1, Python 3.11rc0, Python 3.12.6, but not with

urllib
import http.client
from urllib.parse import urlparse

# Parse the URL
url = urlparse('http://localhost:50051/update')

# Create an HTTP connection
conn = http.client.HTTPConnection(url.netloc)

# Make the first request
conn.request('POST', url.path)
response1 = conn.getresponse()
print(response1.status, response1.reason)
print([response1.read().decode()])

# Make the second request on the same connection
conn.request('POST', url.path)
response2 = conn.getresponse()
print(response2.status, response2.reason)
print([response2.read().decode()])
or
telnet
 telnet localhost 50051
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
POST /update HTTP/1.1
Host: localhost:50051
Content-Length: 0


HTTP/1.1 204 No Content
Date: Mon, 07 Oct 2024 09:13:05 GMT
Content-Type: text/plain
Content-Length: 0

POST /update HTTP/1.1
Host: localhost:50051
Content-Length: 0


HTTP/1.1 204 No Content
Date: Mon, 07 Oct 2024 09:13:07 GMT
Content-Type: text/plain
Content-Length: 0

POST /update HTTP/1.1
Host: localhost:50051
Content-Length: 0


HTTP/1.1 204 No Content
Date: Mon, 07 Oct 2024 09:13:09 GMT
Content-Type: text/plain
Content-Length: 0

POST /update HTTP/1.1
Host: localhost:50051
Content-Length: 0


HTTP/1.1 204 No Content
Date: Mon, 07 Oct 2024 09:13:10 GMT
Content-Type: text/plain
Content-Length: 0

POST /update HTTP/1.1
Host: localhost:50051
Content-Length: 0


HTTP/1.1 204 No Content
Date: Mon, 07 Oct 2024 09:13:10 GMT
Content-Type: text/plain
Content-Length: 0
.

Unable to replicate on Ubuntu 24.04 with R 4.4.1, Python 3.12.3, httpx 0.27.2, fiery 1.2.1, routr 0.4.1, reqres 0.2.5, httpuv 1.6.15 (installed via mamba)

@sverhoeven
Copy link
Member Author

I stopped the reusing of the same connection. @BSchilperoort could you try again

@BSchilperoort
Copy link
Member

I stopped the reusing of the same connection. @BSchilperoort could you try again

Seems to work properly now!

I did notice that client.get_input_var_names() returns an error. I guess that's not implemented by walrus?

Details

>>> client.get_input_var_names()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/bart/git/remotebmi/python/remotebmi/client/client.py", line 41, in get_input_var_names
    return response.json()
           ^^^^^^^^^^^^^^^
  File "/home/bart/git/remotebmi/.venv/lib/python3.12/site-packages/httpx/_models.py", line 766, in json
    return jsonlib.loads(self.content, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

@sverhoeven
Copy link
Member Author

I stopped the reusing of the same connection. @BSchilperoort could you try again

Seems to work properly now!

I did notice that client.get_input_var_names() returns an error. I guess that's not implemented by walrus?

Details

Tested all methods exposed by walrus, get_input_var_names is still giving error due to R return empty list as empty string somehow.

Copy link

codecov bot commented Oct 7, 2024

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

Thanks for integrating Codecov - We've got you covered ☂️

@sverhoeven
Copy link
Member Author

@sverhoeven
Copy link
Member Author

Need to raise coverage of https://app.codecov.io/gh/eWaterCycle/remotebmi/blob/r-server/R%2Fremotebmi%2FR%2Froute.R

Coverage is now 97.36%

@sverhoeven
Copy link
Member Author

I stopped the reusing of the same connection. @BSchilperoort could you try again

Seems to work properly now!
I did notice that client.get_input_var_names() returns an error. I guess that's not implemented by walrus?
Details

Tested all methods exposed by walrus, get_input_var_names is still giving error due to R return empty list as empty string somehow.

Added workaround in 27a583f

@sverhoeven sverhoeven merged commit 379058f into main Nov 7, 2024
@sverhoeven sverhoeven deleted the r-server branch November 7, 2024 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants