Skip to content

Commit 5f23c3f

Browse files
committed
cli: add RPCServer commands
1 parent b904bb7 commit 5f23c3f

File tree

2 files changed

+117
-1
lines changed

2 files changed

+117
-1
lines changed

invenio_app/cli.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,128 @@
22
#
33
# This file is part of Invenio.
44
# Copyright (C) 2017-2018 CERN.
5+
# Copyright (C) 2025 Graz University of Technology.
56
#
67
# Invenio is free software; you can redistribute it and/or modify it
78
# under the terms of the MIT License; see LICENSE file for more details.
89

910
"""CLI application for Invenio flavours."""
1011

12+
import contextlib
13+
import io
14+
import pickle
15+
import socket
16+
import socketserver
17+
18+
from click import group, option, pass_context, secho
19+
from flask.cli import with_appcontext
1120
from invenio_base.app import create_cli
1221

1322
from .factory import create_app
1423

1524
#: Invenio CLI application.
1625
cli = create_cli(create_app=create_app)
26+
27+
28+
class RPCRequestHandler(socketserver.BaseRequestHandler):
29+
"""RPCRequestHandler."""
30+
31+
def handle(self):
32+
"""Handles the requests to the RPCServer."""
33+
data = self.request.recv(4096)
34+
if not data:
35+
return
36+
37+
try:
38+
command_parts = pickle.loads(data)
39+
40+
if not isinstance(command_parts, list) or len(command_parts) == 0:
41+
raise ValueError("Invalid command format should be a list.")
42+
43+
if command_parts[0] == "ping":
44+
result = "pong"
45+
else:
46+
output_buffer = io.StringIO()
47+
with contextlib.redirect_stdout(output_buffer):
48+
cli.main(args=command_parts, standalone_mode=False)
49+
result = output_buffer.getvalue().strip()
50+
51+
response = pickle.dumps({"success": True, "result": result})
52+
except Exception as e:
53+
response = pickle.dumps({"success": False, "error": str(e)})
54+
55+
self.request.sendall(response)
56+
57+
58+
class RPCServer(socketserver.TCPServer):
59+
"""RPCServer implementation."""
60+
61+
allow_reuse_address = True
62+
63+
def shutdown_server(self):
64+
"""Shutdown the server."""
65+
secho("Shutting down RPC Server...", fg="green")
66+
self.shutdown()
67+
self.server_close()
68+
69+
70+
def send(host, port, args):
71+
"""Send."""
72+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
73+
s.connect((host, port))
74+
s.sendall(pickle.dumps(list(args)))
75+
76+
return pickle.loads(s.recv(4096))
77+
78+
79+
@group()
80+
@option("--port", default=5000)
81+
@option("--host", default="localhost")
82+
def rpc_server(port, host):
83+
"""RPC server."""
84+
# TODO pass in the context
85+
86+
87+
@rpc_server.command("start")
88+
@with_appcontext
89+
def rpc_server_start():
90+
"""Start rpc server."""
91+
host = "localhost"
92+
port = 5000
93+
server = RPCServer((host, port), RPCRequestHandler)
94+
95+
secho(
96+
f"RPC Server is running on port {host}:{port}... (Press Ctrl+C to stop)",
97+
fg="green",
98+
)
99+
100+
try:
101+
server.serve_forever()
102+
except KeyboardInterrupt:
103+
server.shutdown()
104+
105+
106+
@rpc_server.command(
107+
"send",
108+
context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
109+
)
110+
@pass_context
111+
def rpc_server_send(ctx):
112+
"""Send."""
113+
host = "localhost"
114+
port = 5000
115+
116+
response = send(host, port, ctx.args)
117+
if response["success"]:
118+
secho(f"Response: {response['result']}", fg="green")
119+
else:
120+
secho(f"Error: {response['error']}", fg="red")
121+
122+
123+
@rpc_server.command("ping")
124+
def rpc_server_ping():
125+
"""Ping."""
126+
host = "localhost"
127+
port = 5000
128+
response = send(host, port, ["ping"])
129+
secho(response["result"])

setup.cfg

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# This file is part of Invenio.
44
# Copyright (C) 2017-2022 CERN.
55
# Copyright (C) 2021 TU Wien.
6-
# Copyright (C) 2022-2024 Graz University of Technology.
6+
# Copyright (C) 2022-2025 Graz University of Technology.
77
#
88
# Invenio is free software; you can redistribute it and/or modify it
99
# under the terms of the MIT License; see LICENSE file for more details.
@@ -48,6 +48,9 @@ tests =
4848
[options.entry_points]
4949
console_scripts =
5050
invenio = invenio_app.cli:cli
51+
rpc-server = invenio_app.cli:rpc_server
52+
flask.commands =
53+
rpc-server = invenio_app.cli:rpc_server
5154
invenio_base.api_apps =
5255
invenio_app = invenio_app:InvenioApp
5356
invenio_base.apps =

0 commit comments

Comments
 (0)