appmesh 1.6.13__py3-none-any.whl → 1.6.14__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
@@ -134,75 +134,85 @@ class App:
134
134
 
135
135
  # Application configuration
136
136
  self.name = _get_str(data, "name")
137
- """application name (unique)"""
137
+ """app name (unique)"""
138
138
  self.command = _get_str(data, "command")
139
139
  """full command line with arguments"""
140
140
  self.shell = _get_bool(data, "shell")
141
- """use shell mode, cmd can be more shell commands with string format"""
141
+ """Whether run command in shell mode (enables shell syntax such as pipes and compound commands)"""
142
142
  self.session_login = _get_bool(data, "session_login")
143
- """app run in session login mode"""
143
+ """Whether to run the app in session login mode (inheriting the user's full login environment)"""
144
144
  self.description = _get_str(data, "description")
145
- """application description string"""
145
+ """app description string"""
146
146
  self.metadata = _get_item(data, "metadata")
147
- """metadata string/JSON (input for application, pass to process stdin)"""
147
+ """metadata string/JSON (input for app, pass to process stdin)"""
148
148
  self.working_dir = _get_str(data, "working_dir")
149
149
  """working directory"""
150
150
  self.status = _get_int(data, "status")
151
- """initial application status (true is enable, false is disabled)"""
151
+ """app status: 1 for enabled, 0 for disabled"""
152
152
  self.docker_image = _get_str(data, "docker_image")
153
- """docker image which used to run command line (for docker container application)"""
153
+ """Docker image for containerized execution"""
154
154
  self.stdout_cache_num = _get_int(data, "stdout_cache_num")
155
- """stdout file cache number"""
155
+ """maximum number of stdout log files to retain"""
156
156
  self.start_time = _get_int(data, "start_time")
157
157
  """start date time for app (ISO8601 time format, e.g., '2020-10-11T09:22:05')"""
158
158
  self.end_time = _get_int(data, "end_time")
159
159
  """end date time for app (ISO8601 time format, e.g., '2020-10-11T10:22:05')"""
160
- self.interval = _get_int(data, "interval")
160
+ self.start_interval_seconds = _get_int(data, "start_interval_seconds")
161
161
  """start interval seconds for short running app, support ISO 8601 durations and cron expression (e.g., 'P1Y2M3DT4H5M6S' 'P5W' '* */5 * * * *')"""
162
162
  self.cron = _get_bool(data, "cron")
163
- """indicate interval parameter use cron expression or not"""
163
+ """Whether the interval is specified as a cron expression"""
164
164
  self.daily_limitation = App.DailyLimitation(_get_item(data, "daily_limitation"))
165
165
  self.retention = _get_str(data, "retention")
166
166
  """extra timeout seconds for stopping current process, support ISO 8601 durations (e.g., 'P1Y2M3DT4H5M6S' 'P5W')."""
167
167
  self.health_check_cmd = _get_str(data, "health_check_cmd")
168
168
  """health check script command (e.g., sh -x 'curl host:port/health', return 0 is health)"""
169
169
  self.permission = _get_int(data, "permission")
170
- """application user permission, value is 2 bit integer: [group & other], each bit can be deny:1, read:2, write: 3."""
170
+ """app user permission, value is 2 bit integer: [group & other], each bit can be deny:1, read:2, write: 3."""
171
171
  self.behavior = App.Behavior(_get_item(data, "behavior"))
172
172
 
173
173
  self.env = data.get("env", {}) if data else {}
174
174
  """environment variables (e.g., -e env1=value1 -e env2=value2, APP_DOCKER_OPTS is used to input docker run parameters)"""
175
175
  self.sec_env = data.get("sec_env", {}) if data else {}
176
- """security environment variables, encrypt in server side with application owner's cipher"""
176
+ """security environment variables, encrypt in server side with app owner's cipher"""
177
177
  self.pid = _get_int(data, "pid")
178
178
  """process id used to attach to the running process"""
179
179
  self.resource_limit = App.ResourceLimitation(_get_item(data, "resource_limit"))
180
180
 
181
181
  # Read-only attributes
182
+ self.register_time = _get_int(data, "register_time")
183
+ """app register time"""
184
+ self.starts = _get_int(data, "starts")
185
+ """number of times started"""
182
186
  self.owner = _get_str(data, "owner")
183
- """owner name"""
187
+ """owner name of app mesh user who created the app"""
184
188
  self.user = _get_str(data, "pid_user")
185
- """process user name"""
189
+ """process OS user name"""
186
190
  self.pstree = _get_str(data, "pstree")
187
191
  """process tree"""
188
192
  self.container_id = _get_str(data, "container_id")
189
- """container id"""
193
+ """docker container id"""
190
194
  self.memory = _get_int(data, "memory")
191
195
  """memory usage"""
192
196
  self.cpu = _get_int(data, "cpu")
193
197
  """cpu usage"""
194
198
  self.fd = _get_int(data, "fd")
195
199
  """file descriptor usage"""
200
+ self.stdout_cache_size = _get_int(data, "stdout_cache_size")
201
+ """number of stdout log files currently retained"""
196
202
  self.last_start_time = _get_int(data, "last_start_time")
197
203
  """last start time"""
198
204
  self.last_exit_time = _get_int(data, "last_exit_time")
199
205
  """last exit time"""
206
+ self.last_error = _get_str(data, "last_error")
207
+ """last error message"""
208
+ self.next_start_time = _get_int(data, "next_start_time")
209
+ """next start time"""
200
210
  self.health = _get_int(data, "health")
201
- """health status"""
211
+ """health status: 0 for healthy, 1 for unhealthy"""
202
212
  self.version = _get_int(data, "version")
203
- """version number"""
213
+ """app version"""
204
214
  self.return_code = _get_int(data, "return_code")
205
- """last exit code"""
215
+ """last process exit code"""
206
216
  self.task_id = _get_int(data, "task_id")
207
217
  """current task id"""
208
218
  self.task_status = _get_str(data, "task_status")
appmesh/client_http.py CHANGED
@@ -12,6 +12,7 @@ import os
12
12
  import sys
13
13
  import threading
14
14
  import time
15
+ from pathlib import Path
15
16
  from datetime import datetime
16
17
  from enum import Enum, unique
17
18
  from http import HTTPStatus
@@ -39,17 +40,6 @@ class AppMeshClient(metaclass=abc.ABCMeta):
39
40
 
40
41
  This client is designed for direct usage in applications that require access to App Mesh services over HTTP-based REST.
41
42
 
42
- Usage:
43
- - Install the App Mesh Python package:
44
- python3 -m pip install --upgrade appmesh
45
- - Import the client module:
46
- from appmesh import AppMeshClient
47
-
48
- Example:
49
- client = AppMeshClient()
50
- client.login("your-name", "your-password")
51
- response = client.app_view(app_name='ping')
52
-
53
43
  Attributes:
54
44
  - TLS (Transport Layer Security): Supports secure connections between the client and App Mesh service,
55
45
  ensuring encrypted communication.
@@ -112,6 +102,13 @@ class AppMeshClient(metaclass=abc.ABCMeta):
112
102
  - update_role()
113
103
  - view_roles()
114
104
  - view_groups()
105
+
106
+ Example:
107
+ >>> python -m pip install --upgrade appmesh
108
+ >>> from appmesh import AppMeshClient
109
+ >>> client = AppMeshClient()
110
+ >>> client.login("your-name", "your-password")
111
+ >>> response = client.app_view(app_name='ping')
115
112
  """
116
113
 
117
114
  DURATION_ONE_WEEK_ISO = "P1W"
@@ -121,10 +118,10 @@ class AppMeshClient(metaclass=abc.ABCMeta):
121
118
  TOKEN_REFRESH_OFFSET = 30 # 30s before token expire to refresh token
122
119
 
123
120
  # Platform-aware default SSL paths
124
- _DEFAULT_SSL_DIR = "c:/local/appmesh/ssl" if os.name == "nt" else "/opt/appmesh/ssl"
125
- DEFAULT_SSL_CA_CERT_PATH = os.path.join(_DEFAULT_SSL_DIR, "ca.pem")
126
- DEFAULT_SSL_CLIENT_CERT_PATH = os.path.join(_DEFAULT_SSL_DIR, "client.pem")
127
- DEFAULT_SSL_CLIENT_KEY_PATH = os.path.join(_DEFAULT_SSL_DIR, "client-key.pem")
121
+ _DEFAULT_SSL_DIR = Path("C:/local/appmesh/ssl") if os.name == "nt" else Path("/opt/appmesh/ssl")
122
+ DEFAULT_SSL_CA_CERT_PATH = str(_DEFAULT_SSL_DIR / "ca.pem")
123
+ DEFAULT_SSL_CLIENT_CERT_PATH = str(_DEFAULT_SSL_DIR / "client.pem")
124
+ DEFAULT_SSL_CLIENT_KEY_PATH = str(_DEFAULT_SSL_DIR / "client-key.pem")
128
125
 
129
126
  DEFAULT_JWT_AUDIENCE = "appmesh-service"
130
127
 
@@ -199,39 +196,28 @@ class AppMeshClient(metaclass=abc.ABCMeta):
199
196
  def __init__(
200
197
  self,
201
198
  rest_url: str = "https://127.0.0.1:6060",
202
- rest_ssl_verify=DEFAULT_SSL_CA_CERT_PATH if os.path.exists(DEFAULT_SSL_CA_CERT_PATH) else False,
203
- rest_ssl_client_cert=(DEFAULT_SSL_CLIENT_CERT_PATH, DEFAULT_SSL_CLIENT_KEY_PATH) if os.path.exists(DEFAULT_SSL_CLIENT_CERT_PATH) else None,
204
- rest_timeout=(60, 300),
199
+ rest_ssl_verify: Union[bool, str] = DEFAULT_SSL_CA_CERT_PATH,
200
+ rest_ssl_client_cert: Optional[Union[str, Tuple[str, str]]] = (DEFAULT_SSL_CLIENT_CERT_PATH, DEFAULT_SSL_CLIENT_KEY_PATH),
201
+ rest_timeout: Tuple[float, float] = (60, 300),
205
202
  jwt_token: Optional[str] = None,
206
203
  rest_cookie_file: Optional[str] = None,
207
- auto_refresh_token=False,
204
+ auto_refresh_token: bool = False,
208
205
  ):
209
206
  """Initialize an App Mesh HTTP client for interacting with the App Mesh server via secure HTTPS.
210
207
 
211
208
  Args:
212
- rest_url (str, optional): The server's base URI, including protocol, hostname, and port. Defaults to `"https://127.0.0.1:6060"`.
213
-
214
- rest_ssl_verify (Union[bool, str], optional): Configures SSL certificate verification for HTTPS requests:
215
- - `True`: Uses system CA certificates to verify the server's identity.
216
- - `False`: Disables SSL verification (insecure, use cautiously for development).
217
- - `str`: Path to a custom CA certificate or directory for verification. This option allows custom CA configuration,
218
- which may be necessary in environments requiring specific CA chains that differ from the default system CAs.
219
- To use both a custom CA and the system's default CAs, create a combined CA bundle by concatenating them into a single file. (e.g., `cat custom_ca.pem /etc/ssl/certs/ca-certificates.crt > combined_ca.pem`).
220
-
221
- rest_ssl_client_cert (Union[str, Tuple[str, str]], optional): Path to the SSL client certificate and key. Can be:
222
- - `str`: A path to a single PEM file containing both the client certificate and private key.
223
- - `tuple`: A pair of paths (`cert_file`, `key_file`), where `cert_file` is the client certificate file path and `key_file` is the private key file path.
224
-
225
- rest_timeout (tuple, optional): HTTP connection timeouts for API requests, as `(connect_timeout, read_timeout)`.
226
- The default is `(60, 300)`, where `60` seconds is the maximum time to establish a connection and `300` seconds for the maximum read duration.
227
-
228
- rest_cookie_file (str, optional): Path to a file for storing session cookies.
229
- If provided, cookies will be saved to and loaded from this file to maintain session state across client instances instead of keep jwt_token.
230
-
231
- jwt_token (str, optional): JWT token for API authentication, used in headers to authorize requests where required.
232
- auto_refresh_token (bool, optional): Enable automatic token refresh before expiration.
233
- When enabled, a background timer will monitor token expiration and attempt to refresh
234
- the token before it expires. This works with both native App Mesh tokens and Keycloak tokens.
209
+ rest_url: Base server URI (protocol, host, port). Default: `"https://127.0.0.1:6060"`.
210
+ rest_ssl_verify: SSL verification mode for HTTPS requests:
211
+ - True: Use system CAs.
212
+ - False: Disable verification (insecure).
213
+ - str: Path to custom CA or directory. To include system CAs, combine them into one file (e.g., cat custom_ca.pem /etc/ssl/certs/ca-certificates.crt > combined_ca.pem).
214
+ rest_ssl_client_cert: SSL client certificate:
215
+ - str: Path to single PEM with cert+key
216
+ - tuple: (cert_path, key_path)
217
+ rest_timeout: Timeouts `(connect, read)` in seconds. Default `(60, 300)`.
218
+ rest_cookie_file: Cookie file path for session persistence (alternative to jwt_token).
219
+ jwt_token: JWT token for API authentication, overrides cookie file if both provided.
220
+ auto_refresh_token: Auto-refresh JWT before expiry (supports App Mesh and Keycloak tokens).
235
221
  """
236
222
  self._ensure_logging_configured()
237
223
  self.auth_server_url = rest_url
@@ -1202,13 +1188,13 @@ class AppMeshClient(metaclass=abc.ABCMeta):
1202
1188
  ########################################
1203
1189
  # File management
1204
1190
  ########################################
1205
- def download_file(self, remote_file: str, local_file: str, apply_file_attributes: bool = True) -> None:
1191
+ def download_file(self, remote_file: str, local_file: str, preserve_permissions: bool = True) -> None:
1206
1192
  """Download a remote file to the local system. Optionally, the local file will have the same permission as the remote file.
1207
1193
 
1208
1194
  Args:
1209
1195
  remote_file (str): the remote file path.
1210
1196
  local_file (str): the local file path to be downloaded.
1211
- apply_file_attributes (bool): whether to apply file attributes (permissions, owner, group) to the local file.
1197
+ preserve_permissions (bool): whether to apply file attributes (permissions, owner, group) to the local file.
1212
1198
  """
1213
1199
  resp = self._request_http(AppMeshClient.Method.GET, path="/appmesh/file/download", header={self.HTTP_HEADER_KEY_X_FILE_PATH: remote_file})
1214
1200
  resp.raise_for_status()
@@ -1220,7 +1206,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
1220
1206
  fp.write(chunk)
1221
1207
 
1222
1208
  # Apply file attributes (permissions, owner, group) if requested
1223
- if apply_file_attributes and sys.platform != "win32":
1209
+ if preserve_permissions and sys.platform != "win32":
1224
1210
  if "X-File-Mode" in resp.headers:
1225
1211
  os.chmod(path=local_file, mode=int(resp.headers["X-File-Mode"]))
1226
1212
  if "X-File-User" in resp.headers and "X-File-Group" in resp.headers:
@@ -1231,7 +1217,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
1231
1217
  except PermissionError:
1232
1218
  logging.warning(f"Warning: Unable to change owner/group of {local_file}. Operation requires elevated privileges.")
1233
1219
 
1234
- def upload_file(self, local_file: str, remote_file: str, apply_file_attributes: bool = True) -> None:
1220
+ def upload_file(self, local_file: str, remote_file: str, preserve_permissions: bool = True) -> None:
1235
1221
  """Upload a local file to the remote server. Optionally, the remote file will have the same permission as the local file.
1236
1222
 
1237
1223
  Dependency:
@@ -1241,7 +1227,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
1241
1227
  Args:
1242
1228
  local_file (str): the local file path.
1243
1229
  remote_file (str): the target remote file to be uploaded.
1244
- apply_file_attributes (bool): whether to upload file attributes (permissions, owner, group) along with the file.
1230
+ preserve_permissions (bool): whether to upload file attributes (permissions, owner, group) along with the file.
1245
1231
  """
1246
1232
  if not os.path.exists(local_file):
1247
1233
  raise FileNotFoundError(f"Local file not found: {local_file}")
@@ -1253,7 +1239,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
1253
1239
  header = {self.HTTP_HEADER_KEY_X_FILE_PATH: parse.quote(remote_file), "Content-Type": encoder.content_type}
1254
1240
 
1255
1241
  # Include file attributes (permissions, owner, group) if requested
1256
- if apply_file_attributes:
1242
+ if preserve_permissions:
1257
1243
  file_stat = os.stat(local_file)
1258
1244
  header["X-File-Mode"] = str(file_stat.st_mode & 0o777) # Mask to keep only permission bits
1259
1245
  header["X-File-User"] = str(file_stat.st_uid)
@@ -1486,24 +1472,24 @@ class AppMeshClient(metaclass=abc.ABCMeta):
1486
1472
  header.setdefault("Content-Type", "application/json")
1487
1473
 
1488
1474
  try:
1475
+ request_kwargs = {
1476
+ "url": rest_url,
1477
+ "headers": header,
1478
+ "cert": self.ssl_client_cert,
1479
+ "verify": self.ssl_verify,
1480
+ "timeout": self.rest_timeout,
1481
+ }
1482
+
1489
1483
  if method is AppMeshClient.Method.GET:
1490
- resp = self.session.get(url=rest_url, params=query, headers=header, cert=self.ssl_client_cert, verify=self.ssl_verify, timeout=self.rest_timeout)
1484
+ resp = self.session.get(params=query, **request_kwargs)
1491
1485
  elif method is AppMeshClient.Method.POST:
1492
- resp = self.session.post(
1493
- url=rest_url,
1494
- params=query,
1495
- headers=header,
1496
- data=body,
1497
- cert=self.ssl_client_cert,
1498
- verify=self.ssl_verify,
1499
- timeout=self.rest_timeout,
1500
- )
1486
+ resp = self.session.post(params=query, data=body, **request_kwargs)
1501
1487
  elif method is AppMeshClient.Method.POST_STREAM:
1502
- resp = self.session.post(url=rest_url, params=query, headers=header, data=body, cert=self.ssl_client_cert, verify=self.ssl_verify, stream=True, timeout=self.rest_timeout)
1488
+ resp = self.session.post(params=query, data=body, stream=True, **request_kwargs)
1503
1489
  elif method is AppMeshClient.Method.DELETE:
1504
- resp = self.session.delete(url=rest_url, headers=header, cert=self.ssl_client_cert, verify=self.ssl_verify, timeout=self.rest_timeout)
1490
+ resp = self.session.delete(**request_kwargs)
1505
1491
  elif method is AppMeshClient.Method.PUT:
1506
- resp = self.session.put(url=rest_url, params=query, headers=header, data=body, cert=self.ssl_client_cert, verify=self.ssl_verify, timeout=self.rest_timeout)
1492
+ resp = self.session.put(params=query, data=body, **request_kwargs)
1507
1493
  else:
1508
1494
  raise Exception("Invalid http method", method)
1509
1495
 
@@ -18,8 +18,8 @@ class AppMeshClientOAuth(AppMeshClient):
18
18
  self,
19
19
  oauth2: dict, # Required for Keycloak
20
20
  rest_url: str = "https://127.0.0.1:6060",
21
- rest_ssl_verify=AppMeshClient.DEFAULT_SSL_CA_CERT_PATH if os.path.exists(AppMeshClient.DEFAULT_SSL_CA_CERT_PATH) else False,
22
- rest_ssl_client_cert=(AppMeshClient.DEFAULT_SSL_CLIENT_CERT_PATH, AppMeshClient.DEFAULT_SSL_CLIENT_KEY_PATH) if os.path.exists(AppMeshClient.DEFAULT_SSL_CLIENT_CERT_PATH) else None,
21
+ rest_ssl_verify=AppMeshClient.DEFAULT_SSL_CA_CERT_PATH,
22
+ rest_ssl_client_cert=AppMeshClient.DEFAULT_SSL_CLIENT_CERT_PATH,
23
23
  rest_timeout=(60, 300),
24
24
  jwt_token=None, # Keycloak dict
25
25
  auto_refresh_token: bool = True, # Default to True for Keycloak
appmesh/client_tcp.py CHANGED
@@ -7,6 +7,7 @@ import os
7
7
  import socket
8
8
  import sys
9
9
  import uuid
10
+ from typing import Optional, Tuple, Union
10
11
 
11
12
  # Third-party imports
12
13
  import requests
@@ -18,8 +19,7 @@ from .tcp_transport import TCPTransport
18
19
 
19
20
 
20
21
  class AppMeshClientTCP(AppMeshClient):
21
- """
22
- Client SDK for interacting with the App Mesh service over TCP, with enhanced support for large file transfers.
22
+ """Client SDK for interacting with the App Mesh service over TCP.
23
23
 
24
24
  The `AppMeshClientTCP` class extends the functionality of `AppMeshClient` by offering a TCP-based communication layer
25
25
  for the App Mesh REST API. It overrides the file download and upload methods to support large file transfers with
@@ -28,30 +28,23 @@ class AppMeshClientTCP(AppMeshClient):
28
28
  This client is suitable for applications requiring efficient data transfers and high-throughput operations within the
29
29
  App Mesh ecosystem, while maintaining compatibility with all other attributes and methods from `AppMeshClient`.
30
30
 
31
- Dependency:
32
- - Install the required package for message serialization:
33
- pip3 install msgpack
34
-
35
- Usage:
36
- - Import the client module:
37
- from appmesh import AppMeshClientTCP
38
-
39
- Example:
40
- client = AppMeshClientTCP()
41
- client.login("your-name", "your-password")
42
- client.file_download("/tmp/os-release", "os-release")
43
-
44
31
  Attributes:
45
- - Inherits all attributes from `AppMeshClient`, including TLS secure connections and JWT-based authentication.
46
- - Optimized for TCP-based communication to provide better performance for large file transfers.
32
+ Inherits all attributes from `AppMeshClient`, including TLS secure connections and JWT-based authentication.
47
33
 
48
34
  Methods:
49
- - file_download()
50
- - file_upload()
35
+ - download_file()
36
+ - upload_file()
51
37
  - Inherits all other methods from `AppMeshClient`, providing a consistent interface for managing applications within App Mesh.
38
+
39
+ Example:
40
+ >>> from appmesh import AppMeshClientTCP
41
+ >>> client = AppMeshClientTCP()
42
+ >>> client.login("your-name", "your-password")
43
+ >>> client.download_file("/tmp/os-release", "os-release")
52
44
  """
53
45
 
54
- TCP_BLOCK_SIZE = 16 * 1024 - 128 # TLS-optimized chunk size, leaves some room for TLS overhead (like headers) within the 16 KB limit.
46
+ # TLS-optimized chunk size, leaves room for TLS overhead within the 16 KB limit
47
+ TCP_BLOCK_SIZE = 16 * 1024 - 128
55
48
  ENCODING_UTF8 = "utf-8"
56
49
  HTTP_USER_AGENT_TCP = "appmesh/python/tcp"
57
50
  HTTP_HEADER_KEY_X_SEND_FILE_SOCKET = "X-Send-File-Socket"
@@ -59,32 +52,27 @@ class AppMeshClientTCP(AppMeshClient):
59
52
 
60
53
  def __init__(
61
54
  self,
62
- rest_ssl_verify=AppMeshClient.DEFAULT_SSL_CA_CERT_PATH if os.path.exists(AppMeshClient.DEFAULT_SSL_CA_CERT_PATH) else False,
63
- rest_ssl_client_cert=None,
64
- jwt_token=None,
65
- tcp_address=("127.0.0.1", 6059),
55
+ rest_ssl_verify: Union[bool, str] = AppMeshClient.DEFAULT_SSL_CA_CERT_PATH,
56
+ rest_ssl_client_cert: Optional[Union[str, Tuple[str, str]]] = None,
57
+ jwt_token: Optional[str] = None,
58
+ tcp_address: Tuple[str, int] = ("127.0.0.1", 6059),
66
59
  ):
67
60
  """Construct an App Mesh client TCP object to communicate securely with an App Mesh server over TLS.
68
61
 
69
62
  Args:
70
- rest_ssl_verify (Union[bool, str], optional): Specifies SSL certificate verification behavior. Can be:
71
- - `True`: Uses the system’s default CA certificates to verify the server’s identity.
72
- - `False`: Disables SSL certificate verification (insecure, intended for development).
73
- - `str`: Specifies a custom CA bundle or directory for server certificate verification. If a string is provided,
74
- it should either be a file path to a custom CA certificate (CA bundle) or a directory path containing multiple
75
- certificates (CA directory).
76
-
77
- **Note**: Unlike HTTP requests, TCP connections cannot automatically retrieve intermediate or public CA certificates.
78
- When `rest_ssl_verify` is a path, it explicitly identifies a CA issuer to ensure certificate validation.
79
-
80
- rest_ssl_client_cert (Union[str, Tuple[str, str]], optional): Path to the SSL client certificate and key. If a `str`,
81
- it should be the path to a PEM file containing both the client certificate and private key. If a `tuple`, it should
82
- be a pair of paths: (`cert`, `key`), where `cert` is the client certificate file and `key` is the private key file.
83
-
84
- jwt_token (str, optional): JWT token for authentication. Used in methods requiring login and user authorization.
85
-
86
- tcp_address (Tuple[str, int], optional): Address and port for establishing a TCP connection to the server.
87
- Defaults to `("127.0.0.1", 6059)`.
63
+ rest_ssl_verify: SSL certificate verification behavior. Can be True, False, or a path to CA bundle.
64
+ - True: Use system CA certificates (e.g., /etc/ssl/certs/ on Linux)
65
+ - False: Disable verification (insecure)
66
+ - str: Path to custom CA bundle or directory
67
+ ssl_client_cert: SSL client certificate:
68
+ - str: Path to single PEM with cert+key
69
+ - tuple: (cert_path, key_path)
70
+ jwt_token: Pre-configured JWT Token for authenticating requests.
71
+ tcp_address: Server address as (host, port) tuple, defaults to ("127.0.0.1", 6059).
72
+
73
+ Note:
74
+ TCP connections require an explicit full-chain CA specification for certificate validation,
75
+ unlike HTTP, which can retrieve intermediate certificates automatically.
88
76
  """
89
77
  self.tcp_transport = TCPTransport(address=tcp_address, ssl_verify=rest_ssl_verify, ssl_client_cert=rest_ssl_client_cert)
90
78
  super().__init__(rest_ssl_verify=rest_ssl_verify, rest_ssl_client_cert=rest_ssl_client_cert, jwt_token=jwt_token)
@@ -102,93 +90,159 @@ class AppMeshClientTCP(AppMeshClient):
102
90
  self.close()
103
91
  except Exception:
104
92
  pass # Never raise in __del__
105
- super().__del__()
106
93
 
107
- def _request_http(self, method: AppMeshClient.Method, path: str, query: dict = None, header: dict = None, body=None) -> requests.Response:
94
+ def _covert_bytes(self, body) -> bytes:
95
+ """Prepare request body for transmission."""
96
+ if body is None:
97
+ return b""
98
+
99
+ if isinstance(body, (bytes, bytearray, memoryview)):
100
+ return body
101
+
102
+ if isinstance(body, str):
103
+ return body.encode(self.ENCODING_UTF8)
104
+
105
+ if isinstance(body, (dict, list)):
106
+ return json.dumps(body).encode(self.ENCODING_UTF8)
107
+
108
+ raise TypeError(f"Unsupported body type: {type(body)}")
109
+
110
+ def _request_http(self, method: AppMeshClient.Method, path: str, query: Optional[dict] = None, header: Optional[dict] = None, body=None) -> requests.Response:
108
111
  """Send HTTP request over TCP transport.
109
112
 
110
113
  Args:
111
- method (Method): HTTP method.
112
- path (str): URI path.
113
- query (dict, optional): Query parameters.
114
- header (dict, optional): HTTP headers.
114
+ method: HTTP method.
115
+ path: URI path.
116
+ query: Query parameters.
117
+ header: HTTP headers.
115
118
  body: Request body.
116
119
 
117
120
  Returns:
118
- requests.Response: Simulated HTTP response.
121
+ Simulated HTTP response.
119
122
  """
123
+
124
+ # Check for unsupported features
125
+ if super().forward_to:
126
+ raise RuntimeError("Forward request is not supported in TCP mode")
127
+
120
128
  if not self.tcp_transport.connected():
121
129
  self.tcp_transport.connect()
122
130
 
131
+ # Prepare request message (ensure no fields are assigned None!)
123
132
  appmesh_request = RequestMessage()
124
- token = self._get_access_token()
125
- if token:
126
- appmesh_request.headers[self.HTTP_HEADER_KEY_AUTH] = token
127
- if super().forward_to and len(super().forward_to) > 0:
128
- raise Exception("Not support forward request in TCP mode")
129
- appmesh_request.headers[self.HTTP_HEADER_KEY_USER_AGENT] = self.HTTP_USER_AGENT_TCP
130
133
  appmesh_request.uuid = str(uuid.uuid1())
131
134
  appmesh_request.http_method = method.value
132
135
  appmesh_request.request_uri = path
133
136
  appmesh_request.client_addr = socket.gethostname()
137
+ appmesh_request.headers[self.HTTP_HEADER_KEY_USER_AGENT] = self.HTTP_USER_AGENT_TCP
134
138
 
135
- if body:
136
- if isinstance(body, (dict, list)):
137
- appmesh_request.body = bytes(json.dumps(body, indent=2), self.ENCODING_UTF8)
138
- elif isinstance(body, str):
139
- appmesh_request.body = bytes(body, self.ENCODING_UTF8)
140
- elif isinstance(body, bytes):
141
- appmesh_request.body = body
142
- else:
143
- raise Exception(f"UnSupported body type: {type(body)}")
139
+ # Add authentication token
140
+ token = self._get_access_token()
141
+ if token:
142
+ appmesh_request.headers[self.HTTP_HEADER_KEY_AUTH] = token
144
143
 
144
+ # Add custom headers
145
145
  if header:
146
- for k, v in header.items():
147
- appmesh_request.headers[k] = v
146
+ appmesh_request.headers.update(header)
147
+
148
+ # Add query parameters
148
149
  if query:
149
- for k, v in query.items():
150
- appmesh_request.query[k] = v
150
+ appmesh_request.query.update(query)
151
+
152
+ # Prepare body
153
+ body_bytes = self._covert_bytes(body)
154
+ if body_bytes:
155
+ appmesh_request.body = body_bytes
151
156
 
157
+ # Send request
152
158
  data = appmesh_request.serialize()
153
159
  self.tcp_transport.send_message(data)
154
160
 
161
+ # Receive response
155
162
  resp_data = self.tcp_transport.receive_message()
156
163
  if not resp_data: # Covers None and empty bytes
157
164
  self.tcp_transport.close()
158
- raise Exception("socket connection broken")
165
+ raise ConnectionError("Socket connection broken")
159
166
 
167
+ # Parse response
160
168
  appmesh_resp = ResponseMessage().deserialize(resp_data)
161
169
  response = requests.Response()
162
170
  response.status_code = appmesh_resp.http_status
163
- # response.encoding = self.ENCODING_UTF8 # only need when charset not in appmesh_resp.body_msg_type
164
- response._content = appmesh_resp.body if isinstance(appmesh_resp.body, bytes) else str(appmesh_resp.body).encode(self.ENCODING_UTF8)
165
171
  response.headers = appmesh_resp.headers
172
+
173
+ # Set response content
174
+ # response.encoding = self.ENCODING_UTF8 # only need when charset not in appmesh_resp.body_msg_type
175
+ if isinstance(appmesh_resp.body, bytes):
176
+ response._content = appmesh_resp.body
177
+ else:
178
+ response._content = str(appmesh_resp.body).encode(self.ENCODING_UTF8)
179
+
180
+ # Set content type
166
181
  if appmesh_resp.body_msg_type:
167
182
  response.headers["Content-Type"] = appmesh_resp.body_msg_type
168
183
 
169
184
  return AppMeshClient.EncodingResponse(response)
170
185
 
171
- ########################################
172
- # File management
173
- ########################################
174
- def download_file(self, remote_file: str, local_file: str, apply_file_attributes: bool = True) -> None:
186
+ def _apply_file_attributes(self, local_file: str, headers: dict) -> None:
187
+ """Apply file attributes from headers to local file."""
188
+ if sys.platform == "win32":
189
+ return
190
+
191
+ # Apply file mode
192
+ if "X-File-Mode" in headers:
193
+ try:
194
+ os.chmod(local_file, int(headers["X-File-Mode"]))
195
+ except (ValueError, OSError) as e:
196
+ self._logger.warning("Failed to set file mode: %s", e)
197
+
198
+ # Apply file ownership
199
+ if "X-File-User" in headers and "X-File-Group" in headers:
200
+ try:
201
+ file_uid = int(headers["X-File-User"])
202
+ file_gid = int(headers["X-File-Group"])
203
+ os.chown(local_file, uid=file_uid, gid=file_gid)
204
+ except (ValueError, PermissionError, OSError) as e:
205
+ if isinstance(e, PermissionError):
206
+ print(f"Warning: Unable to change owner/group of {local_file}. " "Operation requires elevated privileges.")
207
+ else:
208
+ self._logger.warning("Failed to set file ownership: %s", e)
209
+
210
+ def _get_file_attributes(self, local_file: str) -> dict:
211
+ """Get file attributes as header dictionary."""
212
+ if sys.platform == "win32":
213
+ return {}
214
+
215
+ try:
216
+ file_stat = os.stat(local_file)
217
+ return {
218
+ "X-File-Mode": str(file_stat.st_mode & 0o777),
219
+ "X-File-User": str(file_stat.st_uid),
220
+ "X-File-Group": str(file_stat.st_gid),
221
+ }
222
+ except OSError as e:
223
+ self._logger.warning("Failed to get file attributes: %s", e)
224
+ return {}
225
+
226
+ def download_file(self, remote_file: str, local_file: str, preserve_permissions: bool = True) -> None:
175
227
  """Copy a remote file to local, preserving file attributes if requested.
176
228
 
177
229
  Args:
178
- remote_file (str): Remote file path.
179
- local_file (str): Local destination path.
180
- apply_file_attributes (bool): Apply remote file permissions/ownership locally.
230
+ remote_file: Remote file path.
231
+ local_file: Local destination path.
232
+ preserve_permissions: Apply remote file permissions/ownership locally.
181
233
  """
182
234
  header = {
183
235
  AppMeshClient.HTTP_HEADER_KEY_X_FILE_PATH: remote_file,
184
236
  self.HTTP_HEADER_KEY_X_RECV_FILE_SOCKET: "true",
185
237
  }
238
+
186
239
  resp = self._request_http(AppMeshClient.Method.GET, path="/appmesh/file/download", header=header)
187
240
  resp.raise_for_status()
188
241
 
189
242
  if self.HTTP_HEADER_KEY_X_RECV_FILE_SOCKET not in resp.headers:
190
- raise ValueError(f"Server did not respond with socket transfer option: {self.HTTP_HEADER_KEY_X_RECV_FILE_SOCKET}")
243
+ raise ValueError(f"Server did not respond with socket transfer option: " f"{self.HTTP_HEADER_KEY_X_RECV_FILE_SOCKET}")
191
244
 
245
+ # Download file chunks
192
246
  with open(local_file, "wb") as fp:
193
247
  while True:
194
248
  chunk_data = self.tcp_transport.receive_message()
@@ -196,50 +250,43 @@ class AppMeshClientTCP(AppMeshClient):
196
250
  break
197
251
  fp.write(chunk_data)
198
252
 
199
- if apply_file_attributes and sys.platform != "win32":
200
- if "X-File-Mode" in resp.headers:
201
- os.chmod(path=local_file, mode=int(resp.headers["X-File-Mode"]))
202
- if "X-File-User" in resp.headers and "X-File-Group" in resp.headers:
203
- file_uid = int(resp.headers["X-File-User"])
204
- file_gid = int(resp.headers["X-File-Group"])
205
- try:
206
- os.chown(path=local_file, uid=file_uid, gid=file_gid)
207
- except PermissionError:
208
- print(f"Warning: Unable to change owner/group of {local_file}. Operation requires elevated privileges.")
209
-
210
- def upload_file(self, local_file: str, remote_file: str, apply_file_attributes: bool = True) -> None:
253
+ # Apply file attributes if requested
254
+ if preserve_permissions:
255
+ self._apply_file_attributes(local_file, resp.headers)
256
+
257
+ def upload_file(self, local_file: str, remote_file: str, preserve_permissions: bool = True) -> None:
211
258
  """Upload a local file to remote server, preserving file attributes if requested.
212
259
 
213
260
  Args:
214
- local_file (str): Local file path.
215
- remote_file (str): Remote destination path.
216
- apply_file_attributes (bool): Upload file permissions/ownership metadata.
261
+ local_file: Local file path.
262
+ remote_file: Remote destination path.
263
+ preserve_permissions: Upload file permissions/ownership metadata.
217
264
  """
218
265
  if not os.path.exists(local_file):
219
266
  raise FileNotFoundError(f"Local file not found: {local_file}")
220
267
 
221
- with open(file=local_file, mode="rb") as fp:
222
- header = {
223
- AppMeshClient.HTTP_HEADER_KEY_X_FILE_PATH: remote_file,
224
- "Content-Type": "text/plain",
225
- self.HTTP_HEADER_KEY_X_SEND_FILE_SOCKET: "true",
226
- }
268
+ # Prepare headers
269
+ header = {
270
+ AppMeshClient.HTTP_HEADER_KEY_X_FILE_PATH: remote_file,
271
+ "Content-Type": "text/plain",
272
+ self.HTTP_HEADER_KEY_X_SEND_FILE_SOCKET: "true",
273
+ }
227
274
 
228
- if apply_file_attributes:
229
- file_stat = os.stat(local_file)
230
- header["X-File-Mode"] = str(file_stat.st_mode & 0o777) # Mask to keep only permission bits
231
- header["X-File-User"] = str(file_stat.st_uid)
232
- header["X-File-Group"] = str(file_stat.st_gid)
275
+ # Add file attributes if requested
276
+ if preserve_permissions:
277
+ header.update(self._get_file_attributes(local_file))
233
278
 
234
- resp = self._request_http(AppMeshClient.Method.POST, path="/appmesh/file/upload", header=header)
235
- resp.raise_for_status()
279
+ # Initiate upload
280
+ resp = self._request_http(AppMeshClient.Method.POST, path="/appmesh/file/upload", header=header)
281
+ resp.raise_for_status()
236
282
 
237
- if self.HTTP_HEADER_KEY_X_SEND_FILE_SOCKET not in resp.headers:
238
- raise ValueError(f"Server did not respond with socket transfer option: {self.HTTP_HEADER_KEY_X_SEND_FILE_SOCKET}")
283
+ if self.HTTP_HEADER_KEY_X_SEND_FILE_SOCKET not in resp.headers:
284
+ raise ValueError(f"Server did not respond with socket transfer option: " f"{self.HTTP_HEADER_KEY_X_SEND_FILE_SOCKET}")
239
285
 
240
- chunk_size = self.TCP_BLOCK_SIZE
286
+ # Upload file chunks
287
+ with open(local_file, "rb") as fp:
241
288
  while True:
242
- chunk_data = fp.read(chunk_size)
289
+ chunk_data = fp.read(self.TCP_BLOCK_SIZE)
243
290
  if not chunk_data:
244
291
  self.tcp_transport.send_message([]) # EOF signal
245
292
  break
appmesh/server_http.py CHANGED
@@ -1,6 +1,8 @@
1
1
  # server_http.py
2
2
  # pylint: disable=line-too-long,broad-exception-raised,broad-exception-caught,import-outside-toplevel,protected-access
3
3
 
4
+ """HTTP server SDK implementation for App Mesh."""
5
+
4
6
  # Standard library imports
5
7
  import abc
6
8
  import logging
@@ -16,8 +18,7 @@ logger = logging.getLogger(__name__)
16
18
 
17
19
 
18
20
  class AppMeshServer(metaclass=abc.ABCMeta):
19
- """
20
- Server SDK for App Mesh application interacting with the local App Mesh REST service over HTTPS.
21
+ """Server SDK for App Mesh application interacting with the local App Mesh REST service over HTTPS.
21
22
 
22
23
  Build-in runtime environment variables required:
23
24
  - APP_MESH_PROCESS_KEY
@@ -34,18 +35,13 @@ class AppMeshServer(metaclass=abc.ABCMeta):
34
35
  context.task_return(result)
35
36
  """
36
37
 
38
+ _RETRY_DELAY_SECONDS = 0.1
39
+
37
40
  def __init__(
38
41
  self,
39
42
  rest_url: str = "https://127.0.0.1:6060",
40
- rest_ssl_verify=AppMeshClient.DEFAULT_SSL_CA_CERT_PATH if os.path.exists(AppMeshClient.DEFAULT_SSL_CA_CERT_PATH) else False,
41
- rest_ssl_client_cert=(
42
- (
43
- AppMeshClient.DEFAULT_SSL_CLIENT_CERT_PATH,
44
- AppMeshClient.DEFAULT_SSL_CLIENT_KEY_PATH,
45
- )
46
- if os.path.exists(AppMeshClient.DEFAULT_SSL_CLIENT_CERT_PATH)
47
- else None
48
- ),
43
+ rest_ssl_verify: Union[bool, str] = AppMeshClient.DEFAULT_SSL_CA_CERT_PATH,
44
+ rest_ssl_client_cert: Optional[Union[str, Tuple[str, str]]] = None,
49
45
  rest_timeout: Tuple[float, float] = (60, 300),
50
46
  *,
51
47
  logger_: Optional[logging.Logger] = None,
@@ -63,6 +59,7 @@ class AppMeshServer(metaclass=abc.ABCMeta):
63
59
  """Read and validate required runtime environment variables."""
64
60
  process_key = os.getenv("APP_MESH_PROCESS_KEY")
65
61
  app_name = os.getenv("APP_MESH_APPLICATION_NAME")
62
+
66
63
  if not process_key:
67
64
  raise Exception("Missing environment variable: APP_MESH_PROCESS_KEY. This must be set by App Mesh service.")
68
65
  if not app_name:
@@ -75,43 +72,43 @@ class AppMeshServer(metaclass=abc.ABCMeta):
75
72
  Used by App Mesh application process to obtain the payload from App Mesh service
76
73
  that a client pushed to it.
77
74
 
78
-
79
75
  Returns:
80
- Union[str, bytes]: The payload provided by the client as returned by the service.
76
+ The payload provided by the client as returned by the service.
81
77
  """
82
78
  pkey, app_name = self._get_runtime_env()
83
79
  path = f"/appmesh/app/{app_name}/task"
80
+ query_params = {"process_key": pkey}
84
81
 
85
82
  while True:
86
83
  resp = self._client._request_http(
87
84
  AppMeshClient.Method.GET,
88
85
  path=path,
89
- query={"process_key": pkey},
86
+ query=query_params,
90
87
  )
91
88
 
92
- if resp.status_code != HTTPStatus.OK:
93
- self._logger.warning(f"task_fetch failed with status {resp.status_code}: {resp.text}, retrying...")
94
- time.sleep(0.1)
95
- continue
89
+ if resp.status_code == HTTPStatus.OK:
90
+ return resp.content
96
91
 
97
- return resp.content
92
+ self._logger.warning("task_fetch failed with status %d: %s, retrying...", resp.status_code, resp.text)
93
+ time.sleep(self._RETRY_DELAY_SECONDS)
98
94
 
99
95
  def task_return(self, result: Union[str, bytes]) -> None:
100
96
  """Return the result of a server-side invocation back to the original client.
101
97
 
102
- Used by App Mesh application process to posts the `result` to App Mesh service
103
- after processed payload data so the invoking client can retrieve it.
98
+ Used by App Mesh application process to post the `result` to App Mesh service
99
+ after processing payload data so the invoking client can retrieve it.
104
100
 
105
101
  Args:
106
- result (Union[str, bytes]): Result payload to be delivered back to the client.
102
+ result: Result payload to be delivered back to the client.
107
103
  """
108
104
  pkey, app_name = self._get_runtime_env()
109
105
  path = f"/appmesh/app/{app_name}/task"
106
+ query_params = {"process_key": pkey}
110
107
 
111
108
  resp = self._client._request_http(
112
109
  AppMeshClient.Method.PUT,
113
110
  path=path,
114
- query={"process_key": pkey},
111
+ query=query_params,
115
112
  body=result,
116
113
  )
117
114
 
appmesh/server_tcp.py CHANGED
@@ -3,8 +3,7 @@
3
3
 
4
4
  # Standard library imports
5
5
  import logging
6
- import os
7
- from typing import Optional, Tuple
6
+ from typing import Optional, Tuple, Union
8
7
 
9
8
  # Local imports
10
9
  from .client_http import AppMeshClient
@@ -15,14 +14,12 @@ logger = logging.getLogger(__name__)
15
14
 
16
15
 
17
16
  class AppMeshServerTCP(AppMeshServer):
18
- """
19
- Server SDK for interacting with the local App Mesh service over TCP (TLS).
20
- """
17
+ """Server SDK for interacting with the local App Mesh service over TCP (TLS)."""
21
18
 
22
19
  def __init__(
23
20
  self,
24
- rest_ssl_verify=AppMeshClient.DEFAULT_SSL_CA_CERT_PATH if os.path.exists(AppMeshClient.DEFAULT_SSL_CA_CERT_PATH) else False,
25
- rest_ssl_client_cert=None,
21
+ rest_ssl_verify: Union[bool, str] = AppMeshClient.DEFAULT_SSL_CA_CERT_PATH,
22
+ rest_ssl_client_cert: Optional[Union[str, Tuple[str, str]]] = None,
26
23
  tcp_address: Tuple[str, int] = ("127.0.0.1", 6059),
27
24
  *,
28
25
  logger_: Optional[logging.Logger] = None,
@@ -34,6 +31,5 @@ class AppMeshServerTCP(AppMeshServer):
34
31
  """
35
32
  # Deliberately avoid calling super().__init__ to inject a TCP client while keeping the same public API.
36
33
  object.__init__(self)
37
- # super().__init__(rest_ssl_verify=rest_ssl_verify, rest_ssl_client_cert=rest_ssl_client_cert)
38
34
  self._client = AppMeshClientTCP(rest_ssl_verify=rest_ssl_verify, rest_ssl_client_cert=rest_ssl_client_cert, tcp_address=tcp_address)
39
35
  self._logger = logger_ or logger
appmesh/tcp_transport.py CHANGED
@@ -29,12 +29,10 @@ class TCPTransport:
29
29
 
30
30
  Args:
31
31
  address: Server address as (host, port) tuple.
32
-
33
32
  ssl_verify: SSL server verification mode:
34
33
  - True: Use system CA certificates
35
34
  - False: Disable verification (insecure)
36
35
  - str: Path to custom CA bundle or directory
37
-
38
36
  ssl_client_cert: SSL client certificate:
39
37
  - str: Path to PEM file with cert and key
40
38
  - tuple: (cert_path, key_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: appmesh
3
- Version: 1.6.13
3
+ Version: 1.6.14
4
4
  Summary: Client SDK for App Mesh
5
5
  Home-page: https://github.com/laoshanxi/app-mesh
6
6
  Author: laoshanxi
@@ -0,0 +1,16 @@
1
+ appmesh/__init__.py,sha256=on6GmTTwo6mrRbw_XUuf8sZdl5zBYsGT4CSf8l__NnI,2488
2
+ appmesh/app.py,sha256=bJyv_uOSMj5esMgkFWuvpMPS5ayFTBJLVll2Tvw8xB4,11213
3
+ appmesh/app_output.py,sha256=s6eqevFxETTVXSPTJX6JyGNpHHILv4ZyM7YWvlkuqQs,741
4
+ appmesh/app_run.py,sha256=m3ihaacx84o1rl2Oc3EbnppW8D-PTxdehftbWRe8rPk,1732
5
+ appmesh/appmesh_client.py,sha256=ywB2222PtJUffdfdxZcBfdhZs1KYyc7JvzMxwuK2qyI,378
6
+ appmesh/client_http.py,sha256=BsFgafrk4iUJlmrz21hlovyN5XAPYTGKjLNMt6aH8nM,58986
7
+ appmesh/client_http_oauth.py,sha256=zES-f6AnG6dUo754zd-HM6Pvu4vp_64vTKT61npY6HE,5934
8
+ appmesh/client_tcp.py,sha256=K3IneOSl8xiOIC9z4jHK_4Mhq2n8eeUDASvXJQiRjfQ,11817
9
+ appmesh/server_http.py,sha256=g2NYREF-f-vnSFVntAJDc0H43v_kNEqS7M4y7Q_DpEY,4193
10
+ appmesh/server_tcp.py,sha256=GXEVDmYwM19ZUKkRXm8eEzBep9rE1vzV-PwaTlROpFU,1364
11
+ appmesh/tcp_messages.py,sha256=E0cKWUta7NjuLoTGk-Z9CvbdZyakwG7hO8st_07E5L4,1991
12
+ appmesh/tcp_transport.py,sha256=nkml2hqSkyeJrGDKGAYjWDgOnSyYMEImVT1PQ6jeudk,8163
13
+ appmesh-1.6.14.dist-info/METADATA,sha256=2LZhu1Rp50ERLUa9DxhS-zQNM5om3-aLwtlcB4zNrUw,11814
14
+ appmesh-1.6.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ appmesh-1.6.14.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
16
+ appmesh-1.6.14.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- appmesh/__init__.py,sha256=on6GmTTwo6mrRbw_XUuf8sZdl5zBYsGT4CSf8l__NnI,2488
2
- appmesh/app.py,sha256=5kzQCvGH3LRRUTHsI2_MKEPD3M2qs8kvTxLhbNUqit4,10601
3
- appmesh/app_output.py,sha256=s6eqevFxETTVXSPTJX6JyGNpHHILv4ZyM7YWvlkuqQs,741
4
- appmesh/app_run.py,sha256=m3ihaacx84o1rl2Oc3EbnppW8D-PTxdehftbWRe8rPk,1732
5
- appmesh/appmesh_client.py,sha256=ywB2222PtJUffdfdxZcBfdhZs1KYyc7JvzMxwuK2qyI,378
6
- appmesh/client_http.py,sha256=FXCldfTZVU_5RSuSknlH1K2UiC6gOeotj-4NDQzdEhY,60752
7
- appmesh/client_http_oauth.py,sha256=1d51o0JX_xtB8d2bEuM7_XJHcwMnhcjkbIq7GE1Zxm8,6120
8
- appmesh/client_tcp.py,sha256=RX3T3OG4iOAju8ZPOnTjI_y97Y23_kWoNDjmKjtrBeU,11524
9
- appmesh/server_http.py,sha256=wfyiIa2zC-uJR2ZNTGMjYWheqAfSRl0aAv5b_GYcwpE,4343
10
- appmesh/server_tcp.py,sha256=J65kmN7DJftyW1LlF--S3keQ6VGmqXb778E79I1R0_k,1488
11
- appmesh/tcp_messages.py,sha256=E0cKWUta7NjuLoTGk-Z9CvbdZyakwG7hO8st_07E5L4,1991
12
- appmesh/tcp_transport.py,sha256=oUpcBXOaJhG5Hs6yqt9UG8_eENu0cEdNZIyC87LqI7Q,8165
13
- appmesh-1.6.13.dist-info/METADATA,sha256=BJJB6M94Sz2yytdr9WRxCf56q1-LCzQ-2zQAfP5_1eU,11814
14
- appmesh-1.6.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
- appmesh-1.6.13.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
16
- appmesh-1.6.13.dist-info/RECORD,,