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 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[object]:
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, App, AppOutput
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
- # Ensure response text decoding uses UTF-8 by default
1371
- try:
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.querys[k] = v
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.text
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
- uuid: str = ""
10
- request_uri: str = ""
11
- http_method: str = ""
12
- client_addr: str = ""
13
- body: bytes = b""
14
- headers: dict = {}
15
- querys: dict = {}
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
- # http://www.cnitblog.com/luckydmz/archive/2019/11/20/91959.html
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 = 0
32
- body_msg_type: str = ""
33
- body: str = ""
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 response message"""
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.2
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,,
@@ -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,,