appmesh 1.6.2__py3-none-any.whl → 1.6.3__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.
- appmesh/app.py +2 -2
- appmesh/app_run.py +2 -1
- appmesh/appmesh_client.py +3 -1
- appmesh/client_http.py +78 -8
- appmesh/client_tcp.py +5 -5
- appmesh/server_http.py +6 -6
- appmesh/tcp_messages.py +48 -20
- {appmesh-1.6.2.dist-info → appmesh-1.6.3.dist-info}/METADATA +2 -2
- appmesh-1.6.3.dist-info/RECORD +16 -0
- appmesh-1.6.2.dist-info/RECORD +0 -16
- {appmesh-1.6.2.dist-info → appmesh-1.6.3.dist-info}/WHEEL +0 -0
- {appmesh-1.6.2.dist-info → appmesh-1.6.3.dist-info}/top_level.txt +0 -0
appmesh/app.py
CHANGED
@@ -5,7 +5,7 @@ import json
|
|
5
5
|
import copy
|
6
6
|
|
7
7
|
from datetime import datetime
|
8
|
-
from typing import Optional
|
8
|
+
from typing import Optional, Any
|
9
9
|
from enum import Enum, unique
|
10
10
|
|
11
11
|
# pylint: disable=line-too-long
|
@@ -39,7 +39,7 @@ class App:
|
|
39
39
|
return None
|
40
40
|
|
41
41
|
@staticmethod
|
42
|
-
def _get_native_item(data: dict, key: str) -> Optional[
|
42
|
+
def _get_native_item(data: dict, key: str) -> Optional[Any]:
|
43
43
|
"""Retrieve a deep copy of a value from a dictionary by key, if it exists."""
|
44
44
|
return copy.deepcopy(data[key]) if (data and key in data and data[key] is not None) else None
|
45
45
|
|
appmesh/app_run.py
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
"""Application run object for remote application execution."""
|
3
3
|
|
4
4
|
from contextlib import contextmanager
|
5
|
+
from typing import Optional
|
5
6
|
|
6
7
|
# pylint: disable=line-too-long
|
7
8
|
|
@@ -42,7 +43,7 @@ class AppRun:
|
|
42
43
|
finally:
|
43
44
|
self._client.forward_to = original_value
|
44
45
|
|
45
|
-
def wait(self, stdout_print: bool = True, timeout: int = 0) -> int:
|
46
|
+
def wait(self, stdout_print: bool = True, timeout: int = 0) -> Optional[int]:
|
46
47
|
"""Wait for the asynchronous run to complete.
|
47
48
|
|
48
49
|
Args:
|
appmesh/appmesh_client.py
CHANGED
@@ -5,4 +5,6 @@
|
|
5
5
|
# AppMeshClient, App, and AppOutput classes. The updated implementation can be found
|
6
6
|
# in client_http.py, where these classes are now primarily maintained.
|
7
7
|
|
8
|
-
from .client_http import AppMeshClient
|
8
|
+
from .client_http import AppMeshClient
|
9
|
+
from .app import App
|
10
|
+
from .app_output import AppOutput
|
appmesh/client_http.py
CHANGED
@@ -3,8 +3,10 @@
|
|
3
3
|
import abc
|
4
4
|
import base64
|
5
5
|
import json
|
6
|
+
import locale
|
6
7
|
import logging
|
7
8
|
import os
|
9
|
+
import sys
|
8
10
|
import time
|
9
11
|
from datetime import datetime
|
10
12
|
from enum import Enum, unique
|
@@ -20,6 +22,64 @@ from .app_run import AppRun
|
|
20
22
|
from .app_output import AppOutput
|
21
23
|
|
22
24
|
|
25
|
+
class EncodingResponse:
|
26
|
+
"""Wrapper for requests.Response that handles encoding conversion on Windows."""
|
27
|
+
|
28
|
+
def __init__(self, response: requests.Response):
|
29
|
+
self._response = response
|
30
|
+
self._converted_text = None
|
31
|
+
self._should_convert = False
|
32
|
+
|
33
|
+
# Check if we need to convert encoding on Windows
|
34
|
+
if sys.platform == "win32":
|
35
|
+
content_type = response.headers.get("Content-Type", "").lower()
|
36
|
+
if response.status_code == HTTPStatus.OK and "text/plain" in content_type and "utf-8" in content_type:
|
37
|
+
try:
|
38
|
+
local_encoding = locale.getpreferredencoding()
|
39
|
+
|
40
|
+
if local_encoding.lower() not in ["utf-8", "utf8"]:
|
41
|
+
# Ensure response is decoded as UTF-8 first
|
42
|
+
response.encoding = "utf-8"
|
43
|
+
utf8_text = response.text # This gives us proper Unicode string
|
44
|
+
|
45
|
+
# Convert Unicode to local encoding, then back to Unicode
|
46
|
+
# This simulates how text would appear in local encoding
|
47
|
+
try:
|
48
|
+
local_bytes = utf8_text.encode(local_encoding, errors="replace")
|
49
|
+
self._converted_text = local_bytes.decode(local_encoding)
|
50
|
+
self._should_convert = True
|
51
|
+
except (UnicodeEncodeError, LookupError):
|
52
|
+
# If local encoding can't handle the characters, fall back to UTF-8
|
53
|
+
self._converted_text = utf8_text
|
54
|
+
self._should_convert = True
|
55
|
+
|
56
|
+
except (UnicodeError, LookupError):
|
57
|
+
# If any conversion fails, keep original UTF-8
|
58
|
+
response.encoding = "utf-8"
|
59
|
+
|
60
|
+
@property
|
61
|
+
def text(self):
|
62
|
+
"""Return converted text if needed, otherwise original text."""
|
63
|
+
if self._should_convert and self._converted_text is not None:
|
64
|
+
return self._converted_text
|
65
|
+
# return the original text from _response without modification
|
66
|
+
return self._response.text
|
67
|
+
|
68
|
+
def __getattr__(self, name):
|
69
|
+
"""Dynamically delegate attribute access to the original response"""
|
70
|
+
return getattr(self._response, name)
|
71
|
+
|
72
|
+
def __dir__(self):
|
73
|
+
"""Optional: allow dir() to show attributes of both wrapper and response"""
|
74
|
+
return list(set(dir(self._response) + list(self.__dict__.keys())))
|
75
|
+
|
76
|
+
@property
|
77
|
+
def __class__(self):
|
78
|
+
"""Optional: allow isinstance checks for requests.Response"""
|
79
|
+
# Pretend to be a requests.Response for isinstance checks
|
80
|
+
return requests.Response
|
81
|
+
|
82
|
+
|
23
83
|
class AppMeshClient(metaclass=abc.ABCMeta):
|
24
84
|
"""
|
25
85
|
Client SDK for interacting with the App Mesh service via REST API.
|
@@ -1186,6 +1246,22 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1186
1246
|
|
1187
1247
|
return resp.text
|
1188
1248
|
|
1249
|
+
def cancle_task(self, app_name: str) -> bool:
|
1250
|
+
"""Client cancle a running task to a App Mesh application.
|
1251
|
+
|
1252
|
+
Args:
|
1253
|
+
app_name (str): Name of the target application (as registered in App Mesh).
|
1254
|
+
|
1255
|
+
Returns:
|
1256
|
+
bool: Task exist and cancled status.
|
1257
|
+
"""
|
1258
|
+
path = f"/appmesh/app/{app_name}/task"
|
1259
|
+
resp = self._request_http(
|
1260
|
+
AppMeshClient.Method.DELETE,
|
1261
|
+
path=path,
|
1262
|
+
)
|
1263
|
+
return resp.status_code == HTTPStatus.OK
|
1264
|
+
|
1189
1265
|
def run_app_async(
|
1190
1266
|
self,
|
1191
1267
|
app: Union[App, str],
|
@@ -1367,13 +1443,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1367
1443
|
else:
|
1368
1444
|
raise Exception("Invalid http method", method)
|
1369
1445
|
|
1370
|
-
#
|
1371
|
-
|
1372
|
-
resp.encoding = "utf-8"
|
1373
|
-
except Exception:
|
1374
|
-
# If setting encoding fails for any reason, ignore and return the response as-is
|
1375
|
-
pass
|
1376
|
-
|
1377
|
-
return resp
|
1446
|
+
# Wrap the response for encoding handling
|
1447
|
+
return EncodingResponse(resp)
|
1378
1448
|
except requests.exceptions.RequestException as e:
|
1379
1449
|
raise Exception(f"HTTP request failed: {str(e)}")
|
appmesh/client_tcp.py
CHANGED
@@ -6,7 +6,7 @@ import os
|
|
6
6
|
import socket
|
7
7
|
import uuid
|
8
8
|
import requests
|
9
|
-
from .client_http import AppMeshClient
|
9
|
+
from .client_http import AppMeshClient, EncodingResponse
|
10
10
|
from .tcp_transport import TCPTransport
|
11
11
|
from .tcp_messages import RequestMessage, ResponseMessage
|
12
12
|
|
@@ -140,7 +140,7 @@ class AppMeshClientTCP(AppMeshClient):
|
|
140
140
|
appmesh_request.headers[k] = v
|
141
141
|
if query:
|
142
142
|
for k, v in query.items():
|
143
|
-
appmesh_request.
|
143
|
+
appmesh_request.query[k] = v
|
144
144
|
|
145
145
|
data = appmesh_request.serialize()
|
146
146
|
self.tcp_transport.send_message(data)
|
@@ -153,13 +153,13 @@ class AppMeshClientTCP(AppMeshClient):
|
|
153
153
|
appmesh_resp = ResponseMessage().deserialize(resp_data)
|
154
154
|
response = requests.Response()
|
155
155
|
response.status_code = appmesh_resp.http_status
|
156
|
-
response.encoding = self.ENCODING_UTF8
|
157
|
-
response._content = appmesh_resp.body.encode(self.ENCODING_UTF8)
|
156
|
+
# response.encoding = self.ENCODING_UTF8 # only need when charset not in appmesh_resp.body_msg_type
|
157
|
+
response._content = appmesh_resp.body if isinstance(appmesh_resp.body, bytes) else str(appmesh_resp.body).encode(self.ENCODING_UTF8)
|
158
158
|
response.headers = appmesh_resp.headers
|
159
159
|
if appmesh_resp.body_msg_type:
|
160
160
|
response.headers["Content-Type"] = appmesh_resp.body_msg_type
|
161
161
|
|
162
|
-
return response
|
162
|
+
return EncodingResponse(response)
|
163
163
|
|
164
164
|
########################################
|
165
165
|
# File management
|
appmesh/server_http.py
CHANGED
@@ -5,7 +5,7 @@ import abc
|
|
5
5
|
import logging
|
6
6
|
import os
|
7
7
|
import time
|
8
|
-
from typing import Optional, Tuple
|
8
|
+
from typing import Optional, Tuple, Union
|
9
9
|
from http import HTTPStatus
|
10
10
|
from .client_http import AppMeshClient
|
11
11
|
|
@@ -66,7 +66,7 @@ class AppMeshServer(metaclass=abc.ABCMeta):
|
|
66
66
|
raise Exception("Missing environment variable: APP_MESH_APPLICATION_NAME. This must be set by App Mesh service.")
|
67
67
|
return process_id, app_name
|
68
68
|
|
69
|
-
def task_fetch(self) -> str:
|
69
|
+
def task_fetch(self) -> Union[str, bytes]:
|
70
70
|
"""Fetch task data in the currently running App Mesh application process.
|
71
71
|
|
72
72
|
Used by App Mesh application process to obtain the payload from App Mesh service
|
@@ -74,7 +74,7 @@ class AppMeshServer(metaclass=abc.ABCMeta):
|
|
74
74
|
|
75
75
|
|
76
76
|
Returns:
|
77
|
-
str: The payload provided by the client as returned by the service.
|
77
|
+
Union[str, bytes]: The payload provided by the client as returned by the service.
|
78
78
|
"""
|
79
79
|
process_id, app_name = self._get_runtime_env()
|
80
80
|
path = f"/appmesh/app/{app_name}/task"
|
@@ -91,16 +91,16 @@ class AppMeshServer(metaclass=abc.ABCMeta):
|
|
91
91
|
time.sleep(0.1)
|
92
92
|
continue
|
93
93
|
|
94
|
-
return resp.
|
94
|
+
return resp.content
|
95
95
|
|
96
|
-
def task_return(self, result: str) -> None:
|
96
|
+
def task_return(self, result: Union[str, bytes]) -> None:
|
97
97
|
"""Return the result of a server-side invocation back to the original client.
|
98
98
|
|
99
99
|
Used by App Mesh application process to posts the `result` to App Mesh service
|
100
100
|
after processed payload data so the invoking client can retrieve it.
|
101
101
|
|
102
102
|
Args:
|
103
|
-
result (str): Result payload to be delivered back to the client.
|
103
|
+
result (Union[str, bytes]): Result payload to be delivered back to the client.
|
104
104
|
"""
|
105
105
|
process_id, app_name = self._get_runtime_env()
|
106
106
|
path = f"/appmesh/app/{app_name}/task"
|
appmesh/tcp_messages.py
CHANGED
@@ -1,41 +1,69 @@
|
|
1
1
|
# tcp_messages.py
|
2
2
|
|
3
|
+
from typing import get_type_hints
|
3
4
|
import msgpack
|
4
5
|
|
5
6
|
|
6
7
|
class RequestMessage:
|
7
8
|
"""TCP request message for HTTP-like communication"""
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
def __init__(self):
|
11
|
+
self.uuid: str = ""
|
12
|
+
self.request_uri: str = ""
|
13
|
+
self.http_method: str = ""
|
14
|
+
self.client_addr: str = ""
|
15
|
+
self.body: bytes = b""
|
16
|
+
self.headers: dict = {}
|
17
|
+
self.query: dict = {}
|
16
18
|
|
17
19
|
def serialize(self) -> bytes:
|
18
20
|
"""Serialize request message to bytes"""
|
19
|
-
|
20
|
-
self_dict = vars(self)
|
21
|
-
self_dict["headers"] = self.headers
|
22
|
-
self_dict["querys"] = self.querys
|
23
|
-
return msgpack.dumps(self_dict)
|
21
|
+
return msgpack.dumps(vars(self), use_bin_type=True)
|
24
22
|
|
25
23
|
|
26
24
|
class ResponseMessage:
|
27
25
|
"""TCP response message for HTTP-like communication"""
|
28
26
|
|
29
|
-
uuid: str
|
30
|
-
request_uri: str
|
31
|
-
http_status: int
|
32
|
-
body_msg_type: str
|
33
|
-
body:
|
34
|
-
headers: dict
|
27
|
+
uuid: str
|
28
|
+
request_uri: str
|
29
|
+
http_status: int
|
30
|
+
body_msg_type: str
|
31
|
+
body: bytes
|
32
|
+
headers: dict
|
33
|
+
|
34
|
+
def __init__(self):
|
35
|
+
self.uuid = ""
|
36
|
+
self.request_uri = ""
|
37
|
+
self.http_status = 0
|
38
|
+
self.body_msg_type = ""
|
39
|
+
self.body = b""
|
40
|
+
self.headers = {}
|
35
41
|
|
36
42
|
def deserialize(self, buf: bytes):
|
37
|
-
"""Deserialize
|
38
|
-
dic = msgpack.unpackb(buf)
|
43
|
+
"""Deserialize TCP msgpack buffer with proper type conversion."""
|
44
|
+
dic = msgpack.unpackb(buf, raw=False)
|
45
|
+
hints = get_type_hints(self.__class__)
|
46
|
+
|
39
47
|
for k, v in dic.items():
|
48
|
+
if k not in hints:
|
49
|
+
continue # Skip unknown fields
|
50
|
+
|
51
|
+
# handle all types (int, bytes, dict, str)
|
52
|
+
t = hints[k]
|
53
|
+
if t is str:
|
54
|
+
if isinstance(v, bytes):
|
55
|
+
v = v.decode("utf-8", errors="replace")
|
56
|
+
elif v is None:
|
57
|
+
v = ""
|
58
|
+
else:
|
59
|
+
v = str(v)
|
60
|
+
elif t is bytes:
|
61
|
+
if isinstance(v, str):
|
62
|
+
v = v.encode("utf-8") # handle accidental str
|
63
|
+
elif v is None:
|
64
|
+
v = b""
|
65
|
+
elif t is int:
|
66
|
+
v = int(v or 0)
|
40
67
|
setattr(self, k, v)
|
68
|
+
|
41
69
|
return self
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: appmesh
|
3
|
-
Version: 1.6.
|
3
|
+
Version: 1.6.3
|
4
4
|
Summary: Client SDK for App Mesh
|
5
5
|
Home-page: https://github.com/laoshanxi/app-mesh
|
6
6
|
Author: laoshanxi
|
@@ -40,9 +40,9 @@ Dynamic: summary
|
|
40
40
|
|
41
41
|
# App Mesh: Advanced Application Management Platform
|
42
42
|
|
43
|
+
<div align=center><img src="https://github.com/laoshanxi/picture/raw/master/appmesh/whatis.gif" align=center /></div>
|
43
44
|
App Mesh is an open-source, multi-tenant application management platform designed for cloud-native environments. It efficiently manages, schedules, and monitors both microservices and traditional applications, offering a lightweight alternative to Kubernetes. App Mesh bridges the gap between simple process managers and complex container orchestration systems, making it ideal for organizations seeking to modernize their infrastructure without adopting full container-native complexity. Supporting both containerized and native applications, it provides a versatile solution for diverse enterprise needs.
|
44
45
|
|
45
|
-
<div align=center><img src="https://github.com/laoshanxi/picture/raw/master/appmesh/whatis.gif" align=center /></div>
|
46
46
|
<div align=center><img src="https://github.com/laoshanxi/picture/raw/master/appmesh/diagram.png" align=center /></div>
|
47
47
|
|
48
48
|
## Features
|
@@ -0,0 +1,16 @@
|
|
1
|
+
appmesh/__init__.py,sha256=TY1y5B5cE57uhraEzCFOZRWuo9SY1R-fYNRan8hCZOM,1670
|
2
|
+
appmesh/app.py,sha256=crD4DRFZJuHtZMfSsz7C-EwvjPmGZbFXYXvA_wCdvdI,10734
|
3
|
+
appmesh/app_output.py,sha256=vfn322AyixblI8DbXds08h6L_ybObiaRSifsA1-Xcoo,1035
|
4
|
+
appmesh/app_run.py,sha256=aYq852a29OThIi32Xtx5s0sTXZ97T0lHD5WXH8yfPoc,2018
|
5
|
+
appmesh/appmesh_client.py,sha256=ywB2222PtJUffdfdxZcBfdhZs1KYyc7JvzMxwuK2qyI,378
|
6
|
+
appmesh/client_http.py,sha256=WLdHmi2ryzCiXFyIIAbKH3arfcs__rxt4kwehpO_w6s,57711
|
7
|
+
appmesh/client_http_oauth.py,sha256=1d51o0JX_xtB8d2bEuM7_XJHcwMnhcjkbIq7GE1Zxm8,6120
|
8
|
+
appmesh/client_tcp.py,sha256=OrKmJ-SMSmCPXj5WVhWwBXiK8Z_WyCrDmTim0pe1pc0,11407
|
9
|
+
appmesh/server_http.py,sha256=vf_Kh7ZIyEuBijZp8I2Rv-Fy9gxFdPFn5Pp2rUNCT1U,4319
|
10
|
+
appmesh/server_tcp.py,sha256=biBFF5IGWFOw2ru831cfmzn1DVXcBm9e-W6CP2VkfzE,1444
|
11
|
+
appmesh/tcp_messages.py,sha256=H9S_iCy0IuufY2v50_SUgRvcyQmJsySG65tBe_xb3Ko,1878
|
12
|
+
appmesh/tcp_transport.py,sha256=f28zfZNH46tUHfT8F1PrCM1wUXiSBIW7R3ipMsXJqIU,8946
|
13
|
+
appmesh-1.6.3.dist-info/METADATA,sha256=Hcymb5xi90fgxLHqHNWHzicT2KLZzGPo4DonII-IQBQ,11828
|
14
|
+
appmesh-1.6.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
15
|
+
appmesh-1.6.3.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
|
16
|
+
appmesh-1.6.3.dist-info/RECORD,,
|
appmesh-1.6.2.dist-info/RECORD
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
appmesh/__init__.py,sha256=TY1y5B5cE57uhraEzCFOZRWuo9SY1R-fYNRan8hCZOM,1670
|
2
|
-
appmesh/app.py,sha256=4lo66ob1ZFDgL8VYKxH4_sh-vbHcGkZNThURE0go-d4,10732
|
3
|
-
appmesh/app_output.py,sha256=vfn322AyixblI8DbXds08h6L_ybObiaRSifsA1-Xcoo,1035
|
4
|
-
appmesh/app_run.py,sha256=TJ9xVX8xqUN9-OOmr_8JlgXoyg3hTmDFNKvGf4w_oR4,1980
|
5
|
-
appmesh/appmesh_client.py,sha256=7yy9c_ygJbqMekrUsHWmRObwHLK82qP65xQLXxfGd8U,339
|
6
|
-
appmesh/client_http.py,sha256=rVmGAc_nWkntoZBL7BCaQZtH9QOZXUis38tW9OhT2ps,54707
|
7
|
-
appmesh/client_http_oauth.py,sha256=1d51o0JX_xtB8d2bEuM7_XJHcwMnhcjkbIq7GE1Zxm8,6120
|
8
|
-
appmesh/client_tcp.py,sha256=hE0T_2Z0OQZdF5zi--iuvu2_-ka0DfSSQ4BP3oHlg44,11243
|
9
|
-
appmesh/server_http.py,sha256=zr9sfS8g_mtP2kuAfmz-qU7rLSFTVt3srbFXaCbl8Y0,4253
|
10
|
-
appmesh/server_tcp.py,sha256=biBFF5IGWFOw2ru831cfmzn1DVXcBm9e-W6CP2VkfzE,1444
|
11
|
-
appmesh/tcp_messages.py,sha256=4XRv5lSm_8ElMg_37SuyIRrfxI7XFNNP_7SdO7us3PA,1023
|
12
|
-
appmesh/tcp_transport.py,sha256=f28zfZNH46tUHfT8F1PrCM1wUXiSBIW7R3ipMsXJqIU,8946
|
13
|
-
appmesh-1.6.2.dist-info/METADATA,sha256=NljlBJBJxpevRRrgxFPyjNpxsYeu3FuUk7_klAhJCcs,11828
|
14
|
-
appmesh-1.6.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
15
|
-
appmesh-1.6.2.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
|
16
|
-
appmesh-1.6.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|