Skip to content

Commit b807b9e

Browse files
committed
Merge branch '1848-DAPS-refactor-authz-library' into 1850-DAPS-refactor-web-server-proto3
2 parents a035467 + c2aa1ba commit b807b9e

File tree

7 files changed

+131
-52
lines changed

7 files changed

+131
-52
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ endif()
199199

200200
if( BUILD_PYTHON_CLIENT )
201201
# make target = pydatafed
202-
file(COPY ${PROJECT_SOURCE_DIR}/external/DataFedDependencies/python/datafed_pkg/requirements.txt DESTINATION ${PROJECT_SOURCE_DIR}/python/datafed_pkg/requirements.txt)
202+
file(COPY ${PROJECT_SOURCE_DIR}/external/DataFedDependencies/python/datafed_pkg/requirements.txt DESTINATION ${PROJECT_SOURCE_DIR}/python/datafed_pkg)
203203
add_subdirectory( python EXCLUDE_FROM_ALL )
204204
endif()
205205

python/datafed_pkg/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,4 @@ endforeach()
3535
add_subdirectory( datafed )
3636

3737
add_custom_target( pydatafed )
38-
add_dependencies( pydatafed pydatafed_src)
38+
add_dependencies( pydatafed pydatafed_proto_src)

python/datafed_pkg/datafed/CMakeLists.txt

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,30 +35,13 @@ set(PROTO_SUBDIRS anon auth enums messages)
3535
# from .anon import ack_reply_pb2 as ...
3636
# from .enums import error_code_pb2 as ...
3737
# from . import envelope_pb2 as ...
38+
39+
# Create the import fixup script
3840
add_custom_command(TARGET pydatafed_src POST_BUILD
39-
COMMAND find ${CMAKE_CURRENT_BINARY_DIR} -name "*_pb2.py" -exec
40-
sed -i -r
41-
-e "s:^from anon import:from .anon import:g"
42-
-e "s:^from anon\\.:from .anon.:g"
43-
-e "s:^from auth import:from .auth import:g"
44-
-e "s:^from auth\\.:from .auth.:g"
45-
-e "s:^from enums import:from .enums import:g"
46-
-e "s:^from enums\\.:from .enums.:g"
47-
-e "s:^from messages import:from .messages import:g"
48-
-e "s:^from messages\\.:from .messages.:g"
49-
-e "s:^import ([a-zA-Z0-9_]*_pb2):from . import \\1:g"
50-
{} +
41+
COMMAND sh ${DataFed_SOURCE_DIR}/python/datafed_pkg/scripts/fix_proto_imports.sh ${CMAKE_CURRENT_BINARY_DIR}
5142
COMMENT "Rewriting protobuf imports to relative"
5243
)
5344

54-
# Ensure __init__.py exists in each generated subdirectory
55-
foreach(subdir ${PROTO_SUBDIRS})
56-
add_custom_command(TARGET pydatafed_src POST_BUILD
57-
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${subdir}
58-
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/${subdir}/__init__.py
59-
)
60-
endforeach()
61-
6245
# Copy generated files back to source tree for testing
6346
add_custom_target(pydatafed_proto_src DEPENDS pydatafed_src)
6447
add_custom_command(TARGET pydatafed_proto_src POST_BUILD

python/datafed_pkg/datafed/CommandLib.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,9 @@
1414
import time
1515
import pathlib
1616
import requests
17-
from . import envelope_pb2 as sdms
1817
from . import MessageLib
1918
from . import Config
20-
19+
from . import envelope_pb2 as sdms
2120

2221
class API:
2322
"""

python/datafed_pkg/datafed/Connection.py

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ def __init__(
6767
self._msg_desc_by_type = {}
6868
self._msg_desc_by_name = {}
6969
self._msg_type_by_desc = {}
70+
self._field_by_msg_desc = {}
71+
72+
self._envelope_class = None
73+
self._envelope_desc = None
7074

7175
self._address = "tcp://{0}:{1}".format(server_host, server_port)
7276
# init zeromq
@@ -130,6 +134,10 @@ def registerEnvelope(self, envelope_module, envelope_class_name="Envelope"):
130134
envelope_class = getattr(envelope_module, envelope_class_name)
131135
envelope_desc = envelope_class.DESCRIPTOR
132136

137+
# Store for envelope wrapping/unwrapping
138+
self._envelope_class = envelope_class
139+
self._envelope_desc = envelope_desc
140+
133141
for field in envelope_desc.fields:
134142
if field.message_type is None:
135143
# Skip non-message fields (e.g. scalars) if any exist
@@ -141,6 +149,7 @@ def registerEnvelope(self, envelope_module, envelope_class_name="Envelope"):
141149
self._msg_desc_by_type[msg_type] = desc
142150
self._msg_desc_by_name[desc.name] = desc
143151
self._msg_type_by_desc[desc] = msg_type
152+
self._field_by_msg_desc[desc] = field
144153

145154
self._logger.debug(
146155
"Registered %d message types from %s",
@@ -177,15 +186,15 @@ def registerProtocol(self, msg_module):
177186
##
178187
# @brief Receive a message
179188
#
180-
# Receive a protobuf message with timeout. This method automatically
181-
# parses and creates a new protobuf message class based on received
182-
# framing. The new message object, the message name (defined in the
183-
# associated proto file), and re-association context are returned as
184-
# a tuple. On timeout, (None,None,None) is returned.
189+
# Receive a protobuf message with timeout. The wire payload is an
190+
# Envelope message; this method deserializes the Envelope and extracts
191+
# the inner message via the oneof payload field. The inner message
192+
# object, its name, and re-association context are returned as a tuple.
193+
# On timeout, (None, None, None) is returned.
185194
#
186195
# @param timeout - Timeout in milliseconds
187-
# @return Tuple of message, message type, and re-association context
188-
# @retval (object,str,int) or (None,None,None) on timeout
196+
# @return Tuple of message, message name, and re-association context
197+
# @retval (object, str, int) or (None, None, None) on timeout
189198
# @exception Exception: if unregistered message type is received.
190199
#
191200
def recv(self, a_timeout=1000):
@@ -219,48 +228,62 @@ def recv(self, a_timeout=1000):
219228
# client
220229
self._socket.recv_string(0)
221230

222-
# receive custom frame header and unpack
231+
# Receive frame: 8 bytes = uint32 size + uint16 msg_type + uint16 context
223232
frame_data = self._socket.recv(0)
224-
frame_values = struct.unpack(">LBBH", frame_data)
225-
msg_type = (frame_values[1] << 8) | frame_values[2]
233+
frame_values = struct.unpack(">LHH", frame_data)
234+
body_size = frame_values[0]
235+
msg_type = frame_values[1]
236+
ctxt = frame_values[2]
226237

227-
# find message descriptor based on type (descriptor index)
228-
229-
if not (msg_type in self._msg_desc_by_type):
238+
if msg_type not in self._msg_desc_by_type:
230239
raise Exception(
231240
"received unregistered message type: {}".format(msg_type)
232241
)
233242

234-
desc = self._msg_desc_by_type[msg_type]
243+
data = self._socket.recv(0)
235244

236-
if frame_values[0] > 0:
237-
# Create message by parsing content
238-
data = self._socket.recv(0)
239-
reply = GetMessageClass(desc)()
240-
reply.ParseFromString(data)
245+
if body_size > 0:
246+
# Deserialize as Envelope
247+
envelope = self._envelope_class()
248+
envelope.ParseFromString(data)
249+
250+
# Extract inner message from the oneof
251+
payload_field = envelope.WhichOneof("payload")
252+
if payload_field is None:
253+
raise Exception("Received Envelope with no payload set")
254+
reply = getattr(envelope, payload_field)
241255
else:
242-
# No content, just create message instance
243-
data = self._socket.recv(0)
256+
# Zero-size body: create empty message instance from type
257+
desc = self._msg_desc_by_type[msg_type]
244258
reply = GetMessageClass(desc)()
245259

246-
return reply, desc.name, frame_values[3]
260+
return reply, reply.DESCRIPTOR.name, ctxt
247261
else:
248262
return None, None, None
249263

250264
##
251265
# @brief Send a message
252266
#
253-
# Serializes and sends framing and message payload over connection.
267+
# Wraps the inner message in an Envelope, serializes it, and sends
268+
# framing and payload over the connection. The frame header carries the
269+
# message type (Envelope field number) for efficient routing on the
270+
# server side.
254271
#
255272
# @param message - The protobuf message object to be sent
256273
# @param ctxt - Reply re-association value (int)
257274
# @exception Exception: if unregistered message type is sent.
258275
#
259276
def send(self, message, ctxt):
260277
# Find msg type by descriptor look-up
261-
if not (message.DESCRIPTOR in self._msg_type_by_desc):
278+
if message.DESCRIPTOR not in self._msg_type_by_desc:
262279
raise Exception("Attempt to send unregistered message type.")
280+
263281
msg_type = self._msg_type_by_desc[message.DESCRIPTOR]
282+
field = self._field_by_msg_desc[message.DESCRIPTOR]
283+
284+
# Wrap inner message in Envelope
285+
envelope = self._envelope_class()
286+
getattr(envelope, field.name).CopyFrom(message)
264287

265288
# Initial Null frame
266289
self._socket.send_string("BEGIN_DATAFED", zmq.SNDMORE)
@@ -274,12 +297,12 @@ def send(self, message, ctxt):
274297
self._socket.send_string(self._pub_key, zmq.SNDMORE)
275298
self._socket.send_string("no_user", zmq.SNDMORE)
276299

277-
# Serialize
278-
data = message.SerializeToString()
300+
# Serialize the Envelope (not the inner message)
301+
data = envelope.SerializeToString()
279302
data_sz = len(data)
280303

281-
# Build the message frame, to match C-struct MessageFrame
282-
frame = struct.pack(">LBBH", data_sz, msg_type >> 8, msg_type & 0xFF, ctxt)
304+
# Build the message frame: uint32 size + uint16 msg_type + uint16 context
305+
frame = struct.pack(">LHH", data_sz, msg_type, ctxt)
283306

284307
if data_sz > 0:
285308
# Send frame and payload
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/bin/sh
2+
set -e
3+
4+
PROTO_DIR="$1"
5+
ROOT_DIR="${2:-$1}"
6+
7+
if [ -z "$PROTO_DIR" ]; then
8+
echo "Usage: fix_proto_imports.sh <proto_output_dir> [root_dir]"
9+
echo " proto_output_dir: directory to find and fix _pb2.py files"
10+
echo " root_dir: package root for computing relative depth (defaults to proto_output_dir)"
11+
exit 1
12+
fi
13+
14+
find "$PROTO_DIR" -name '*_pb2.py' | while read f; do
15+
relpath=$(realpath --relative-to="$ROOT_DIR" "$f")
16+
case "$relpath" in
17+
*/*)
18+
sed -i \
19+
-e 's:^from anon import:from ..anon import:g' \
20+
-e 's:^from anon\.:from ..anon.:g' \
21+
-e 's:^from auth import:from ..auth import:g' \
22+
-e 's:^from auth\.:from ..auth.:g' \
23+
-e 's:^from enums import:from ..enums import:g' \
24+
-e 's:^from enums\.:from ..enums.:g' \
25+
-e 's:^from messages import:from ..messages import:g' \
26+
-e 's:^from messages\.:from ..messages.:g' \
27+
-e 's:^import \(.*_pb2\):from . import \1:g' \
28+
"$f"
29+
;;
30+
*)
31+
sed -i \
32+
-e 's:^from anon import:from .anon import:g' \
33+
-e 's:^from anon\.:from .anon.:g' \
34+
-e 's:^from auth import:from .auth import:g' \
35+
-e 's:^from auth\.:from .auth.:g' \
36+
-e 's:^from enums import:from .enums import:g' \
37+
-e 's:^from enums\.:from .enums.:g' \
38+
-e 's:^from messages import:from .messages import:g' \
39+
-e 's:^from messages\.:from .messages.:g' \
40+
-e 's:^import \(.*_pb2\):from . import \1:g' \
41+
"$f"
42+
;;
43+
esac
44+
done
45+
46+
for subdir in anon auth enums messages; do
47+
if [ -d "$ROOT_DIR/$subdir" ]; then
48+
touch "$ROOT_DIR/$subdir/__init__.py"
49+
fi
50+
done
51+
52+
# Append re-exports to envelope_pb2.py for backward compatibility
53+
# Connection.py uses getattr(envelope_module, class_name) for dynamic dispatch
54+
echo "" >> "$ROOT_DIR/envelope_pb2.py"
55+
echo "# Re-export all message and enum classes for dynamic lookup" >> "$ROOT_DIR/envelope_pb2.py"
56+
57+
for subdir in anon auth enums messages; do
58+
if [ -d "$ROOT_DIR/$subdir" ]; then
59+
for f in "$ROOT_DIR/$subdir"/*_pb2.py; do
60+
[ -f "$f" ] || continue
61+
module=$(basename "$f" .py)
62+
echo "from .$subdir.$module import *" >> "$ROOT_DIR/envelope_pb2.py"
63+
done
64+
fi
65+
done

python/datafed_pkg/setup.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@
2222
long_description_content_type="text/markdown",
2323
url="https://github.com/ORNL/DataFed",
2424
packages=setuptools.find_packages(),
25+
package_data={
26+
"datafed": ["*.py"],
27+
"datafed.anon": ["*.py"],
28+
"datafed.auth": ["*.py"],
29+
"datafed.enums": ["*.py"],
30+
"datafed.messages": ["*.py"],
31+
},
2532
setup_requires=["setuptools"],
2633
install_requires=install_requires,
2734
entry_points={"console_scripts": ["datafed = datafed.CLI:run"]},
@@ -31,3 +38,5 @@
3138
"Operating System :: OS Independent",
3239
],
3340
)
41+
42+

0 commit comments

Comments
 (0)