iris-pex-embedded-python 3.5.5b4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- grongier/__init__.py +0 -0
- grongier/cls/Grongier/PEX/BusinessOperation.cls +8 -0
- grongier/cls/Grongier/PEX/BusinessProcess.cls +13 -0
- grongier/cls/Grongier/PEX/BusinessService.cls +8 -0
- grongier/cls/Grongier/PEX/Common.cls +10 -0
- grongier/cls/Grongier/PEX/Director.cls +10 -0
- grongier/cls/Grongier/PEX/Duplex/Operation.cls +4 -0
- grongier/cls/Grongier/PEX/Duplex/Process.cls +13 -0
- grongier/cls/Grongier/PEX/Duplex/Service.cls +4 -0
- grongier/cls/Grongier/PEX/InboundAdapter.cls +8 -0
- grongier/cls/Grongier/PEX/Message.cls +13 -0
- grongier/cls/Grongier/PEX/OutboundAdapter.cls +8 -0
- grongier/cls/Grongier/PEX/PickleMessage.cls +13 -0
- grongier/cls/Grongier/PEX/PrivateSession/Duplex.cls +8 -0
- grongier/cls/Grongier/PEX/PrivateSession/Message/Ack.cls +14 -0
- grongier/cls/Grongier/PEX/PrivateSession/Message/Poll.cls +14 -0
- grongier/cls/Grongier/PEX/PrivateSession/Message/Start.cls +14 -0
- grongier/cls/Grongier/PEX/PrivateSession/Message/Stop.cls +14 -0
- grongier/cls/Grongier/PEX/Test.cls +10 -0
- grongier/cls/Grongier/PEX/Utils.cls +10 -0
- grongier/cls/Grongier/Service/WSGI.cls +4 -0
- grongier/pex/__init__.py +24 -0
- grongier/pex/__main__.py +4 -0
- grongier/pex/_business_host.py +1 -0
- grongier/pex/_cli.py +4 -0
- grongier/pex/_common.py +1 -0
- grongier/pex/_director.py +1 -0
- grongier/pex/_utils.py +1 -0
- grongier/pex/wsgi/handlers.py +104 -0
- iop/__init__.py +25 -0
- iop/__main__.py +4 -0
- iop/_async_request.py +67 -0
- iop/_business_host.py +256 -0
- iop/_business_operation.py +75 -0
- iop/_business_process.py +224 -0
- iop/_business_service.py +63 -0
- iop/_cli.py +247 -0
- iop/_common.py +334 -0
- iop/_debugpy.py +187 -0
- iop/_decorators.py +49 -0
- iop/_director.py +301 -0
- iop/_dispatch.py +136 -0
- iop/_generator_request.py +30 -0
- iop/_inbound_adapter.py +34 -0
- iop/_iris.py +8 -0
- iop/_log_manager.py +100 -0
- iop/_message.py +40 -0
- iop/_message_validator.py +49 -0
- iop/_outbound_adapter.py +23 -0
- iop/_private_session_duplex.py +103 -0
- iop/_private_session_process.py +41 -0
- iop/_remote.py +91 -0
- iop/_serialization.py +199 -0
- iop/_utils.py +671 -0
- iop/cls/IOP/BusinessOperation.cls +35 -0
- iop/cls/IOP/BusinessProcess.cls +156 -0
- iop/cls/IOP/BusinessService.cls +40 -0
- iop/cls/IOP/Common.cls +569 -0
- iop/cls/IOP/Director.cls +70 -0
- iop/cls/IOP/Duplex/Operation.cls +29 -0
- iop/cls/IOP/Duplex/Process.cls +229 -0
- iop/cls/IOP/Duplex/Service.cls +9 -0
- iop/cls/IOP/Generator/Message/Ack.cls +31 -0
- iop/cls/IOP/Generator/Message/Poll.cls +31 -0
- iop/cls/IOP/Generator/Message/Start.cls +15 -0
- iop/cls/IOP/Generator/Message/StartPickle.cls +15 -0
- iop/cls/IOP/Generator/Message/Stop.cls +32 -0
- iop/cls/IOP/InboundAdapter.cls +22 -0
- iop/cls/IOP/Message/JSONSchema.cls +125 -0
- iop/cls/IOP/Message.cls +754 -0
- iop/cls/IOP/OutboundAdapter.cls +36 -0
- iop/cls/IOP/PickleMessage.cls +58 -0
- iop/cls/IOP/PrivateSession/Duplex.cls +260 -0
- iop/cls/IOP/PrivateSession/Message/Ack.cls +32 -0
- iop/cls/IOP/PrivateSession/Message/Poll.cls +32 -0
- iop/cls/IOP/PrivateSession/Message/Start.cls +31 -0
- iop/cls/IOP/PrivateSession/Message/Stop.cls +48 -0
- iop/cls/IOP/Projection.cls +49 -0
- iop/cls/IOP/Service/Remote/Handler.cls +30 -0
- iop/cls/IOP/Service/Remote/Rest/v1.cls +97 -0
- iop/cls/IOP/Service/WSGI.cls +310 -0
- iop/cls/IOP/Test.cls +85 -0
- iop/cls/IOP/Utils.cls +503 -0
- iop/cls/IOP/Wrapper.cls +58 -0
- iop/wsgi/handlers.py +104 -0
- iris_pex_embedded_python-3.5.5b4.dist-info/METADATA +91 -0
- iris_pex_embedded_python-3.5.5b4.dist-info/RECORD +91 -0
- iris_pex_embedded_python-3.5.5b4.dist-info/WHEEL +5 -0
- iris_pex_embedded_python-3.5.5b4.dist-info/entry_points.txt +2 -0
- iris_pex_embedded_python-3.5.5b4.dist-info/licenses/LICENSE +21 -0
- iris_pex_embedded_python-3.5.5b4.dist-info/top_level.txt +2 -0
iop/_serialization.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import codecs
|
|
3
|
+
import importlib
|
|
4
|
+
import inspect
|
|
5
|
+
import pickle
|
|
6
|
+
import json
|
|
7
|
+
from dataclasses import is_dataclass
|
|
8
|
+
from typing import Any, Dict, Type
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, TypeAdapter, ValidationError
|
|
11
|
+
|
|
12
|
+
from . import _iris
|
|
13
|
+
from ._message import _PydanticPickleMessage, _Message
|
|
14
|
+
from ._utils import _Utils
|
|
15
|
+
|
|
16
|
+
class SerializationError(Exception):
|
|
17
|
+
"""Exception raised for serialization errors."""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
class TempPydanticModel(BaseModel):
|
|
21
|
+
model_config = {
|
|
22
|
+
'arbitrary_types_allowed' : True,
|
|
23
|
+
'extra' : 'allow'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class MessageSerializer:
|
|
27
|
+
"""Handles message serialization and deserialization."""
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def serialize(message: Any, use_pickle: bool = False, is_generator:bool = False) -> Any:
|
|
31
|
+
"""Serializes a message to IRIS format."""
|
|
32
|
+
message = remove_iris_id(message)
|
|
33
|
+
if use_pickle:
|
|
34
|
+
return MessageSerializer._serialize_pickle(message, is_generator)
|
|
35
|
+
return MessageSerializer._serialize_json(message, is_generator)
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def _serialize_json(message: Any, is_generator: bool = False) -> Any:
|
|
39
|
+
json_string = MessageSerializer._convert_to_json_safe(message)
|
|
40
|
+
|
|
41
|
+
if is_generator:
|
|
42
|
+
msg = _iris.get_iris().cls('IOP.Generator.Message.Start')._New()
|
|
43
|
+
else:
|
|
44
|
+
msg = _iris.get_iris().cls('IOP.Message')._New()
|
|
45
|
+
msg.classname = f"{message.__class__.__module__}.{message.__class__.__name__}"
|
|
46
|
+
|
|
47
|
+
if hasattr(msg, 'buffer') and len(json_string) > msg.buffer:
|
|
48
|
+
msg.json = _Utils.string_to_stream(json_string, msg.buffer)
|
|
49
|
+
else:
|
|
50
|
+
msg.json = json_string
|
|
51
|
+
return msg
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def _serialize_pickle(message: Any, is_generator: bool = False) -> Any:
|
|
55
|
+
"""Serializes a message to IRIS format using pickle."""
|
|
56
|
+
message = remove_iris_id(message)
|
|
57
|
+
pickle_string = codecs.encode(pickle.dumps(message), "base64").decode()
|
|
58
|
+
if is_generator:
|
|
59
|
+
msg = _iris.get_iris().cls('IOP.Generator.Message.StartPickle')._New()
|
|
60
|
+
else:
|
|
61
|
+
msg = _iris.get_iris().cls('IOP.PickleMessage')._New()
|
|
62
|
+
msg.classname = f"{message.__class__.__module__}.{message.__class__.__name__}"
|
|
63
|
+
msg.jstr = _Utils.string_to_stream(pickle_string)
|
|
64
|
+
return msg
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def deserialize(serial: Any, use_pickle: bool = False) -> Any:
|
|
68
|
+
if use_pickle:
|
|
69
|
+
msg=MessageSerializer._deserialize_pickle(serial)
|
|
70
|
+
else:
|
|
71
|
+
msg=MessageSerializer._deserialize_json(serial)
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
iris_id = serial._Id()
|
|
75
|
+
msg._iris_id = iris_id if iris_id else None
|
|
76
|
+
except Exception as e:
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
return msg
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def _deserialize_json(serial: Any) -> Any:
|
|
83
|
+
if not serial.classname:
|
|
84
|
+
raise SerializationError("JSON message malformed, must include classname")
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
module_name, class_name = MessageSerializer._parse_classname(serial.classname)
|
|
88
|
+
module = importlib.import_module(module_name)
|
|
89
|
+
msg_class = getattr(module, class_name)
|
|
90
|
+
except Exception as e:
|
|
91
|
+
raise SerializationError(f"Failed to load class {serial.classname}: {str(e)}")
|
|
92
|
+
|
|
93
|
+
json_string = (_Utils.stream_to_string(serial.json)
|
|
94
|
+
if serial.type == 'Stream' else serial.json)
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
if issubclass(msg_class, BaseModel):
|
|
98
|
+
return msg_class.model_validate_json(json_string)
|
|
99
|
+
elif is_dataclass(msg_class):
|
|
100
|
+
return dataclass_from_dict(msg_class, json.loads(json_string))
|
|
101
|
+
else:
|
|
102
|
+
raise SerializationError(f"Class {msg_class} must be a Pydantic model or dataclass")
|
|
103
|
+
except Exception as e:
|
|
104
|
+
raise SerializationError(f"Failed to deserialize JSON: {str(e)}")
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def _deserialize_pickle(serial: Any) -> Any:
|
|
108
|
+
string = _Utils.stream_to_string(serial.jstr)
|
|
109
|
+
return pickle.loads(codecs.decode(string.encode(), "base64"))
|
|
110
|
+
|
|
111
|
+
@staticmethod
|
|
112
|
+
def _parse_classname(classname: str) -> tuple[str, str]:
|
|
113
|
+
j = classname.rindex(".")
|
|
114
|
+
if j <= 0:
|
|
115
|
+
raise SerializationError(f"Classname must include a module: {classname}")
|
|
116
|
+
return classname[:j], classname[j+1:]
|
|
117
|
+
|
|
118
|
+
@staticmethod
|
|
119
|
+
def _convert_to_json_safe(obj: Any) -> Any:
|
|
120
|
+
"""Convert objects to JSON-safe format."""
|
|
121
|
+
if isinstance(obj, BaseModel):
|
|
122
|
+
return obj.model_dump_json()
|
|
123
|
+
elif is_dataclass(obj) and isinstance(obj, _Message):
|
|
124
|
+
return TempPydanticModel.model_validate(dataclass_to_dict(obj)).model_dump_json()
|
|
125
|
+
else:
|
|
126
|
+
raise SerializationError(f"Object {obj} must be a Pydantic model or dataclass Message")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def remove_iris_id(message: Any) -> Any:
|
|
130
|
+
try:
|
|
131
|
+
del message._iris_id
|
|
132
|
+
except AttributeError:
|
|
133
|
+
pass
|
|
134
|
+
return message
|
|
135
|
+
|
|
136
|
+
def dataclass_from_dict(klass: Type | Any, dikt: Dict) -> Any:
|
|
137
|
+
"""Converts a dictionary to a dataclass instance.
|
|
138
|
+
Handles non attended fields and nested dataclasses."""
|
|
139
|
+
|
|
140
|
+
def process_field(value: Any, field_type: Type) -> Any:
|
|
141
|
+
if value is None:
|
|
142
|
+
return None
|
|
143
|
+
if is_dataclass(field_type):
|
|
144
|
+
return dataclass_from_dict(field_type, value)
|
|
145
|
+
if field_type != inspect.Parameter.empty:
|
|
146
|
+
try:
|
|
147
|
+
return TypeAdapter(field_type).validate_python(value)
|
|
148
|
+
except ValidationError:
|
|
149
|
+
return value
|
|
150
|
+
return value
|
|
151
|
+
|
|
152
|
+
# Get field definitions from class signature
|
|
153
|
+
fields = inspect.signature(klass).parameters
|
|
154
|
+
field_dict = {}
|
|
155
|
+
|
|
156
|
+
# Process each field
|
|
157
|
+
for field_name, field_info in fields.items():
|
|
158
|
+
if field_name not in dikt:
|
|
159
|
+
if field_info.default != field_info.empty:
|
|
160
|
+
field_dict[field_name] = field_info.default
|
|
161
|
+
continue
|
|
162
|
+
|
|
163
|
+
field_dict[field_name] = process_field(dikt[field_name], field_info.annotation)
|
|
164
|
+
|
|
165
|
+
# Create instance
|
|
166
|
+
instance = klass(**field_dict)
|
|
167
|
+
|
|
168
|
+
# Add any extra fields not in the dataclass definition
|
|
169
|
+
for key, value in dikt.items():
|
|
170
|
+
if key not in field_dict:
|
|
171
|
+
setattr(instance, key, value)
|
|
172
|
+
|
|
173
|
+
return instance
|
|
174
|
+
|
|
175
|
+
def dataclass_to_dict(instance: Any) -> Dict:
|
|
176
|
+
"""Converts a class instance to a dictionary.
|
|
177
|
+
Handles non attended fields."""
|
|
178
|
+
result = {}
|
|
179
|
+
for field in instance.__dict__:
|
|
180
|
+
value = getattr(instance, field)
|
|
181
|
+
if is_dataclass(value):
|
|
182
|
+
result[field] = dataclass_to_dict(value)
|
|
183
|
+
elif isinstance(value, list):
|
|
184
|
+
result[field] = [dataclass_to_dict(i) if is_dataclass(i) else i for i in value]
|
|
185
|
+
elif isinstance(value, dict):
|
|
186
|
+
result[field] = {k: dataclass_to_dict(v) if is_dataclass(v) else v for k, v in value.items()}
|
|
187
|
+
elif hasattr(value, '__dict__'):
|
|
188
|
+
result[field] = dataclass_to_dict(value)
|
|
189
|
+
else:
|
|
190
|
+
result[field] = value
|
|
191
|
+
return result
|
|
192
|
+
|
|
193
|
+
# Maintain backwards compatibility
|
|
194
|
+
serialize_pickle_message = lambda msg: MessageSerializer.serialize(msg, use_pickle=True, is_generator=False)
|
|
195
|
+
serialize_pickle_message_generator = lambda msg: MessageSerializer.serialize(msg, use_pickle=True, is_generator=True)
|
|
196
|
+
serialize_message = lambda msg: MessageSerializer.serialize(msg, use_pickle=False, is_generator=False)
|
|
197
|
+
serialize_message_generator = lambda msg: MessageSerializer.serialize(msg, use_pickle=False, is_generator=True)
|
|
198
|
+
deserialize_pickle_message = lambda serial: MessageSerializer.deserialize(serial, use_pickle=True)
|
|
199
|
+
deserialize_message = lambda serial: MessageSerializer.deserialize(serial, use_pickle=False)
|