remoterf 0.0.7.41__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.

Potentially problematic release.


This version of remoterf might be problematic. Click here for more details.

@@ -0,0 +1,52 @@
1
+ -----BEGIN PRIVATE KEY-----
2
+ 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
51
+ dnNqQyFgF4k8C3gXyqzTr+Zlc4xaFQ==
52
+ -----END PRIVATE KEY-----
@@ -0,0 +1,19 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDFDCCAfygAwIBAgIUaORvuL3d7VVDuewDxgBibhqrWqkwDQYJKoZIhvcNAQEL
3
+ BQAwETEPMA0GA1UEAwwGdW51c2VkMB4XDTI0MDYyNzIxMDU1OVoXDTI1MDYyNzIx
4
+ MDU1OVowETEPMA0GA1UEAwwGdW51c2VkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
5
+ MIIBCgKCAQEAlT1YZUyg3YZXzHxP6/dS7vmH/unpODa5vSFTnKnOF+lR1GSCok4j
6
+ ItBS9LeUjFcCxAlDBXU+ICQZVGi65udELi00wdewMF/uQb/6dyAb6TVVZUf/LGrM
7
+ rgg4kGfZhSP8IoB+n5qMGIwsypi3Ta1SAKL8xDypSxqqfbC2jeYTJy/3OwNMMs2C
8
+ vqWshdgIHn+hgY0Ge2zkx4GLi6P4ubmSfog6Nxk/2OrCUKmfXn359WawKEzfrXaY
9
+ UOzW9HmfGfF40u66i1TpACNc/BDR4eYaRZEizPLDWdrpz7JWyHBxEojclwvbLfT5
10
+ fSza69x2XGSP39sb3L10dZA0TKe2+j8qJQIDAQABo2QwYjAdBgNVHQ4EFgQUUlVE
11
+ SRlVWR+2IYH8JwBmyy5ntP4wHwYDVR0jBBgwFoAUUlVESRlVWR+2IYH8JwBmyy5n
12
+ tP4wDwYDVR0TAQH/BAUwAwEB/zAPBgNVHREECDAGhwTAqAFrMA0GCSqGSIb3DQEB
13
+ CwUAA4IBAQAvTM1MsqhQo47SNWgn2coGyHHrKFmK7XnFgw9raN9EKJJA67dfv+rj
14
+ ttl+SXuxRFlkQjP8VbHkopyiiL8qNUjW4N4Un4RSEvHZG5oJtQ83a6pwjW3cud1J
15
+ O8+whCU0QCxsXIapNDvsZyaE9rnH+Quq5lyOxrxqGgpEtJG1dTU81Cgpk25ODH1n
16
+ JNxC9vm4bbOBjJdB3YcAJ/K6iIepyP//owN9U2IiIsmpxpdvJi1P/Lp4zD3EHhzF
17
+ MmTwJBCxjOkN66VFjecLXeLlYvs8Xx45BZav+3D3H45XUvafn2cqR2pcmdMOkZAC
18
+ JHTjecrfKzWmnSTxxiY7qWWvxJPUp5KC
19
+ -----END CERTIFICATE-----
@@ -0,0 +1,28 @@
1
+ -----BEGIN PRIVATE KEY-----
2
+ MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCVPVhlTKDdhlfM
3
+ fE/r91Lu+Yf+6ek4Nrm9IVOcqc4X6VHUZIKiTiMi0FL0t5SMVwLECUMFdT4gJBlU
4
+ aLrm50QuLTTB17AwX+5Bv/p3IBvpNVVlR/8sasyuCDiQZ9mFI/wigH6fmowYjCzK
5
+ mLdNrVIAovzEPKlLGqp9sLaN5hMnL/c7A0wyzYK+payF2Agef6GBjQZ7bOTHgYuL
6
+ o/i5uZJ+iDo3GT/Y6sJQqZ9effn1ZrAoTN+tdphQ7Nb0eZ8Z8XjS7rqLVOkAI1z8
7
+ ENHh5hpFkSLM8sNZ2unPslbIcHESiNyXC9st9Pl9LNrr3HZcZI/f2xvcvXR1kDRM
8
+ p7b6PyolAgMBAAECggEAIkNWhVFJuHpEs42R0siySWvQIYIF+ZI04/tgBR6GEr7O
9
+ Ou9Ff6plTjOabBUuvWanm6bNtJXViM8I4rR1vpf3cwuNaniQupZ3rrA6+nkMd2b3
10
+ f1x/dOzjJ4x7Igo5qiPYGfq6t31lrygVi4DyypphcoR4+Du+GWBapqc4zky1doAm
11
+ iXYU08/Q4hToP1VJGNo9chMuOcSrJeiA0RgYlnkCfbL591PGf7VuQzS+PcbcnbiA
12
+ ODOFdBez39+Dlo4nZosIlgLgFPSX/M57zP6WgRcVkLqL75rECqtcs/52dJ73uxMu
13
+ a5GukSY5z5A4r0hCGOZDDqpW2B+IRfAqzSL/ViS7gQKBgQDN0o7GgOsLBqeoj27W
14
+ xOzT1dX39AkFsvdI8f47B1blYFd3egN5y0nLWLEbK/LcpanrCX+nkRz/M51DNGKa
15
+ 1lvMDDwt/99wSKUJFhO1SvuA6SB1DlPgLNRqXZu2cwzpZs1xJU6oVyK70ih3uWmW
16
+ lmi109pDlOkBPSHkIUx5GNXQpQKBgQC5n28/R3VV0L4zzWokewWbHtXVp87owXlh
17
+ 6typ/Stz98kkMNmQNkkQyQ3SxeBNXMkItLA9yYw2MDhDm6ZdyP+ZUdTv+FwMjg5M
18
+ 1FEty94LUAscDbu4QSADR0cfNQH/oPavPMWoFj/eTGnRVY76uKAzgkIPZP112Gqg
19
+ IYaPcs87gQKBgQCtzFrbrAjNqrTwVQ3XNTp2Nl8TnXHw6gp+AjoXRj9OOWgib1l4
20
+ Av0xRJdoxlexkgJFgiPVL2g45zCoixeof9HMH5jBjwryGaWiYo4JzGNLm4/YyFxz
21
+ M+e6WYkfK09/11sra8FbzRh7WR7q2hobGq8YuufXoWqPrTttE+ErcJk8MQKBgCTq
22
+ qothQNrCWzGeRt/ef6zsIwcNCr+LywJIXX6JVV0JCL3j4UEnKrzl35PtusBoFatf
23
+ lDWVogcc6/O2Pw4j7YGmnrzjHht5HSr2hvaetxh04ODBcnYgrac5Q9HXDwCyGJ2d
24
+ booRcr5ZYCo74+XFVkcoPs8k/Ku1UO9r+4EICmMBAoGAdFXHggoQu7fz0maZoH1o
25
+ TgFVhD8p4McuN+L0ddOWvS7l+0Lz/9J2KKmHRElfpMvJsSoXGp7Anyymwqukw8zq
26
+ iD/ZqGavuzcRWPWO6W514asHU1AxLSYGFqNuCYag/jElcWhgQyHojPMypaDM2Xfv
27
+ rgkCz2uZ2Zg2/GJ65CtboCA=
28
+ -----END PRIVATE KEY-----
@@ -0,0 +1,19 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDJjCCAg6gAwIBAgIUEutLmDonaXbPUYklSjQw9IoSI/wwDQYJKoZIhvcNAQEL
3
+ BQAwGTEXMBUGA1UEAwwOMTY0LjY3LjE5NS4yMDcwIBcNMjUwOTI5MjAyNjI2WhgP
4
+ MjEyNTA5MDUyMDI2MjZaMBkxFzAVBgNVBAMMDjE2NC42Ny4xOTUuMjA3MIIBIjAN
5
+ BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzTdU+Of3lKJlNVzPvB5FVF/o3TBT
6
+ DC+xi9O1cjAfm6IDBfBTyP8oD1kTyevNP+N14p6zn7Xxd1Dd6M0m4h+381+fwHQx
7
+ hKzPXaH1Xefy7OuKmMuyZH7i5GZtnOXMa+2sMuekEQhwqn9U2NIwL1jr2/z3sd3b
8
+ W52bdDfstU29htpTEgLZ3cS0zknElg7R5Qhd6ZX9L1/O8DP2XaOcM3UG4YcXXwuh
9
+ EV7fGy28JIwKHyUXHWNLOSINOKfsXdBTUJa1WEzid2pTDKjd1oKAMta3LP38MTK4
10
+ xkGQ7mQnBGhh4cbHSWNKqglGc/ZWBVlLC6jZJw6Q+RZpnHBZbpVN0kbXGQIDAQAB
11
+ o2QwYjAdBgNVHQ4EFgQUe1g9n2C1VBHUdIutCWzU/WNqUnswHwYDVR0jBBgwFoAU
12
+ e1g9n2C1VBHUdIutCWzU/WNqUnswDwYDVR0TAQH/BAUwAwEB/zAPBgNVHREECDAG
13
+ hwSkQ8PPMA0GCSqGSIb3DQEBCwUAA4IBAQBetHfCe999KPuT5rnL8+P+DKhCHYm1
14
+ vX6rmIhtYUj/m50xvf+TCJv/799fXfpX/G2fe1Gm40uPGjwNHYaUuJHaTDtqR7vs
15
+ R9F7+tOlJ+03u2Ue8e8jIzPbXt/ThpCbUPDsXbAImqgYEF6b2xR6abf5zn9jiBiM
16
+ jCawdjsB2mcgKp/5BlTeajQj6rM7Gyvl6YpQ5v03f2xDTIyqV9sAVChyv63jPU6j
17
+ iH9FFEFoBx7i3Vszx95lExid/bL/OTZoaQkpXHjSsqSwfL0BG3ZJEfjVsbfKolZN
18
+ HNIbcnf/t/B+XmLFFBy/wpVPuALnAOxMmYatrB/qMzq/UDIvA4DHgaSl
19
+ -----END CERTIFICATE-----
@@ -0,0 +1,28 @@
1
+ -----BEGIN PRIVATE KEY-----
2
+ MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDNN1T45/eUomU1
3
+ XM+8HkVUX+jdMFMML7GL07VyMB+bogMF8FPI/ygPWRPJ680/43XinrOftfF3UN3o
4
+ zSbiH7fzX5/AdDGErM9dofVd5/Ls64qYy7JkfuLkZm2c5cxr7awy56QRCHCqf1TY
5
+ 0jAvWOvb/Pex3dtbnZt0N+y1Tb2G2lMSAtndxLTOScSWDtHlCF3plf0vX87wM/Zd
6
+ o5wzdQbhhxdfC6ERXt8bLbwkjAofJRcdY0s5Ig04p+xd0FNQlrVYTOJ3alMMqN3W
7
+ goAy1rcs/fwxMrjGQZDuZCcEaGHhxsdJY0qqCUZz9lYFWUsLqNknDpD5FmmccFlu
8
+ lU3SRtcZAgMBAAECggEADBKewZvn8UsSeC6zG+aN/pY73VNb92IQ6GB2krapDV9d
9
+ HSrTywXTxf9gDid8pi8L/t6GQ7WQ9IuB2pHDIVdrW3p7K/fJ9/uwoVoHVNRx8qUy
10
+ vKVzI71hPL2X4jyUp57OXBpLiSL9edJO3AiVhBuPmJwLuF+MaZnejNpDZzEhHr87
11
+ RhD50umMNSXSL16pPuWihV73700UkoGbi4UChmbVPza0EJnS9trIqTeoKsQmAhC6
12
+ idmzfJGaTLl6BoQYJSxi8OR+zeGYN91OMtGOM10W0kZSV0p92/SOoaKN9LH7wgK1
13
+ HEabo5eyGpsDMAhBCDQ7XrcqZywpF88Ttp09GbzrjwKBgQD5f3TMEp3k0ADvf1DX
14
+ ln+f+MhVeDu3yfN4F/lPJciWHp24uu4feF++z78FKvlplDhXko6e85tSJKk86N8/
15
+ 6/kgkc9Ndljw6YxelhItPStHjQZXVMXOEQJr5x6patV+oeF5O+nnmYUcydw96PeA
16
+ rGiuJj1+y2QEppTi3aU0uK0TowKBgQDSkHJfeKjppLX4O/gApNw+Y3D5u6ezcjM1
17
+ yiB81UcBtN8gNMWoEFTphAQnD5ieNFvChmY6x0NpSVPstcWj7NSNOHfTU9ctptg8
18
+ LkuJtfDgcfoPUuRKqFZx30EAr/zDce3tvhR/98KyffjomYOdycUPWgVaIdcpXp+A
19
+ 8eIS3C42EwKBgAlkTd48uSra1Z4uWbmz1ZwM/mWRQBY8COk1ZdIf5vwXrPI+PyZ6
20
+ HoiNQ3CLXJYn2TMatjmvi+I8tQ4w6e57EIa0VmxhDxoKgJky5v5C9jUcTufMriGC
21
+ rGepbJMODXlV2Kn6omRwd/1Mb/j4oxB57PrZP6ldOKegKayckVJ5vTInAoGAUeP7
22
+ l0XF65gzbSdlfFqhjWH/PiDNR+zSHclOeT45L2C4pj3f4Uz4uRiuxMI8CMs0JKNA
23
+ fvugLAJosVbAVtB0mbesYnPo68hHsKAQoK4x0BiDuBH011RAymiZTzsigqB8eEnE
24
+ j0A8aE61z0Bk/6jJCv9ntXYTx/MU9YEc+1jGYCECgYB9Y32sH3xV2LdMKlhFtjux
25
+ WAyyAN+C7Ly8jzwpHQMxpSxHZ16tO/V+0mMIqZHRz1X+DKqO4h6t1j3Bn0u9Tm/i
26
+ vXNDEfy9RF/9U16s0ByK6QzwTodevNg7SEP6YzagR6DlCzIwj0Xbs9m9BHT7Ypia
27
+ J2qGfS7rsnmSP31AtQhikA==
28
+ -----END PRIVATE KEY-----
@@ -0,0 +1,143 @@
1
+ # src/remoteRF/core/remoterf_config.py
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import os
7
+ import sys
8
+ from pathlib import Path
9
+ from typing import Tuple
10
+
11
+ from .cert_fetcher import fetch_and_save_ca_cert # your earlier module
12
+
13
+ # -----------------------------
14
+ # Local config locations
15
+ # -----------------------------
16
+ def _config_root() -> Path:
17
+ # Keep it simple and consistent with earlier suggestions.
18
+ return Path(os.path.expanduser("~")) / ".config" / "remoterf"
19
+
20
+ def _env_path() -> Path:
21
+ return _config_root() / ".env"
22
+
23
+ def _certs_dir() -> Path:
24
+ return _config_root() / "certs"
25
+
26
+ def _parse_hostport(s: str) -> Tuple[str, int]:
27
+ s = s.strip()
28
+ if "://" in s:
29
+ s = s.split("://", 1)[1]
30
+
31
+ if ":" not in s:
32
+ raise ValueError("Expected format host:port")
33
+
34
+ host, port_str = s.rsplit(":", 1)
35
+ host = host.strip()
36
+ port = int(port_str.strip())
37
+ if not host:
38
+ raise ValueError("Host is empty")
39
+ if port <= 0 or port > 65535:
40
+ raise ValueError("Port out of range")
41
+ return host, port
42
+
43
+
44
+ def _write_env_kv(path: Path, kv: dict[str, str]) -> None:
45
+ """
46
+ Writes/overwrites a simple dotenv-style file.
47
+ (We overwrite the whole file for simplicity and correctness.)
48
+ """
49
+ path.parent.mkdir(parents=True, exist_ok=True)
50
+ lines = []
51
+ for k, v in kv.items():
52
+ # Quote only if needed; keep simple and safe.
53
+ if any(c.isspace() for c in v) or any(c in v for c in ['"', "'"]):
54
+ v = v.replace('"', '\\"')
55
+ lines.append(f'{k}="{v}"')
56
+ else:
57
+ lines.append(f"{k}={v}")
58
+ path.write_text("\n".join(lines) + "\n", encoding="utf-8")
59
+
60
+
61
+ def main() -> None:
62
+ parser = argparse.ArgumentParser(
63
+ prog="remoterf-config",
64
+ description="Configure RemoteRF client to talk to a specific server (fetch CA cert + save target)."
65
+ )
66
+ parser.add_argument(
67
+ "addr",
68
+ help="Server gRPC address in host:port form (this is the TLS gRPC port)."
69
+ )
70
+ parser.add_argument(
71
+ "--cert-port",
72
+ type=int,
73
+ default=None,
74
+ help="Bootstrap cert-provider port. If omitted, defaults to <grpc_port + 1>."
75
+ )
76
+ parser.add_argument(
77
+ "--profile",
78
+ default="default",
79
+ help="Profile name (controls CA cert filename). Default: default"
80
+ )
81
+ parser.add_argument(
82
+ "--timeout",
83
+ type=float,
84
+ default=3.0,
85
+ help="Network timeout seconds. Default: 3.0"
86
+ )
87
+ parser.add_argument(
88
+ "--overwrite",
89
+ action="store_true",
90
+ help="Overwrite existing cert/config if present."
91
+ )
92
+ args = parser.parse_args()
93
+
94
+ try:
95
+ host, grpc_port = _parse_hostport(args.addr)
96
+ except Exception as e:
97
+ print(f"Error: invalid addr '{args.addr}': {e}", file=sys.stderr)
98
+ sys.exit(2)
99
+
100
+ cert_port = args.cert_port if args.cert_port is not None else (grpc_port + 1)
101
+
102
+ # Destination CA cert file
103
+ certs_dir = _certs_dir()
104
+ certs_dir.mkdir(parents=True, exist_ok=True)
105
+ ca_out = certs_dir / f"{args.profile}.crt"
106
+
107
+ if ca_out.exists() and not (args.overwrite):
108
+ # Treat as OK; still (re)write env target.
109
+ fetched_ok = True
110
+ else:
111
+ fetched_ok = fetch_and_save_ca_cert(
112
+ host,
113
+ cert_port,
114
+ out_path=ca_out,
115
+ timeout_sec=args.timeout,
116
+ overwrite=True,
117
+ )
118
+
119
+ if not fetched_ok:
120
+ print(
121
+ f"Failed to fetch CA cert from {host}:{cert_port}.\n"
122
+ f"Expected cert provider at: http://{host}:{cert_port}/ca.crt (or raw TCP).\n"
123
+ f"Server must be running cert_provider on that port.",
124
+ file=sys.stderr
125
+ )
126
+ sys.exit(1)
127
+
128
+ # Write local .env (or config) with chosen server and CA path
129
+ env_file = _env_path()
130
+ _write_env_kv(env_file, {
131
+ "REMOTERF_ADDR": f"{host}:{grpc_port}",
132
+ "REMOTERF_CA_CERT": str(ca_out),
133
+ "REMOTERF_PROFILE": args.profile,
134
+ })
135
+
136
+ print("RemoteRF configured successfully.")
137
+ print(f" gRPC target : {host}:{grpc_port}")
138
+ print(f" cert port : {host}:{cert_port}")
139
+ print(f" CA cert : {ca_out}")
140
+ print(f" env file : {env_file}")
141
+
142
+ if __name__ == "__main__":
143
+ main()
@@ -0,0 +1,52 @@
1
+ from .grpc_client import rpc_client
2
+ from ..common.utils import *
3
+
4
+ import datetime
5
+
6
+ class RemoteRFAccount:
7
+ def __init__(self, username:str=None, password:str=None, email:str=None):
8
+ self.username = username
9
+ self.password = password
10
+ self.email = email
11
+
12
+ def create_user(self):
13
+ response = rpc_client(function_name="ACC:create_user", args={"un":map_arg(self.username), "pw":map_arg(self.password), "em":map_arg(self.email)})
14
+ if 'UC' in response.results:
15
+ print(f'User {unmap_arg(response.results["UC"])} successfully created.')
16
+ return True
17
+ elif 'UE' in response.results:
18
+ print(f'Error: {unmap_arg(response.results["UE"])}')
19
+ return False
20
+
21
+ def login_user(self):
22
+ username = self.username
23
+ password = self.password
24
+ response = rpc_client(function_name="ACC:login", args={"un":map_arg(username), "pw":map_arg(password)})
25
+ if 'UC' in response.results:
26
+ print(f'User {unmap_arg(response.results["UC"])} successful login.')
27
+ return True
28
+ elif 'UE' in response.results:
29
+ print(f'Error: {unmap_arg(response.results["UE"])}')
30
+ return False
31
+
32
+ def reserve_device(self, device_id:int, start_time:datetime, end_time:datetime):
33
+ response = rpc_client(function_name="ACC:reserve_device", args={"un":map_arg(self.username), "pw":map_arg(self.password), "dd":map_arg(device_id), "st":map_arg(int(start_time.timestamp())), "et":map_arg(int(end_time.timestamp()))})
34
+
35
+ if 'ace' in response.results:
36
+ raise Exception(f'{unmap_arg(response.results["ace"])}')
37
+ elif 'Token' in response.results:
38
+ return unmap_arg(response.results["Token"])
39
+
40
+ def get_reservations(self):
41
+ return rpc_client(function_name='ACC:get_res', args={"un":map_arg(self.username), "pw":map_arg(self.password)})
42
+
43
+ def get_devices(self):
44
+ return rpc_client(function_name='ACC:get_dev', args={"un":map_arg(self.username), "pw":map_arg(self.password)})
45
+
46
+ def cancel_reservation(self, res_id:int):
47
+ return rpc_client(function_name='ACC:cancel_res', args={"un":map_arg(self.username), "pw":map_arg(self.password), "res_id":map_arg(res_id)})
48
+
49
+ def get_perms(self):
50
+ return rpc_client(function_name='ACC:get_perms', args={"un":map_arg(self.username), "pw":map_arg(self.password)})
51
+
52
+
@@ -0,0 +1,100 @@
1
+ import socket
2
+ import getpass
3
+ from pathlib import Path
4
+
5
+ import grpc
6
+ from ..common.grpc import grpc_pb2
7
+ from ..common.grpc import grpc_pb2_grpc
8
+ from ..common.utils import *
9
+
10
+ # wlab credentials
11
+ server_ip = '164.67.195.207'
12
+ server_port = '61005'
13
+
14
+ options = [
15
+ ('grpc.max_send_message_length', 100 * 1024 * 1024),
16
+ ('grpc.max_receive_message_length', 100 * 1024 * 1024),
17
+ ]
18
+
19
+ # Server.crt
20
+ certs_path = Path(__file__).resolve().parent.parent/'core'/'certs'/'server.crt'
21
+ with certs_path.open('rb') as f:
22
+ trusted_certs = f.read()
23
+
24
+ credentials = grpc.ssl_channel_credentials(root_certificates=trusted_certs)
25
+ channel = grpc.secure_channel(f'{server_ip}:{server_port}', credentials, options=options)
26
+ stub = grpc_pb2_grpc.GenericRPCStub(channel)
27
+
28
+ tcp_calls = 0
29
+
30
+ def get_tcp_calls():
31
+ return tcp_calls
32
+
33
+ def rpc_client(*, function_name, args):
34
+ global tcp_calls
35
+ tcp_calls += 1
36
+ # print(tcp_calls)
37
+ # if not is_connected:
38
+ # response = rpc_client(function_name="UserLogin", args={"username": grpc_pb2.Argument(string_value=input("Username: ")), "password": grpc_pb2.Argument(string_value=getpass.getpass("Password: ")), "client_ip": grpc_pb2.Argument(string_value=local_ip)})
39
+ # if (response.results['status'].string_value == 'Success'):
40
+ # print("Login successful.")
41
+ # is_connected = True
42
+
43
+ # TODO: Handle user login
44
+
45
+ # print(f"Opening connection to {server_ip}:{server_port}")
46
+
47
+ # TODO: Handle Errors
48
+
49
+ # print(f"Calling function: {function_name}")
50
+ response = stub.Call(grpc_pb2.GenericRPCRequest(function_name=function_name, args=args))
51
+
52
+ if 'a' in response.results:
53
+ print(f"Error: {unmap_arg(response.results['a'])}")
54
+ exit()
55
+
56
+ if 'UE' in response.results:
57
+ print(f"UserError: {unmap_arg(response.results['UE'])}")
58
+ input("Hit enter to continue...")
59
+
60
+ if 'Message' in response.results:
61
+ print(f"{unmap_arg(response.results['Message'])}")
62
+
63
+
64
+ return response
65
+
66
+ #region Example Usage
67
+
68
+ # if __name__ == '__main__':
69
+ # args={
70
+ # "key1": grpc_pb2.Argument(string_value="Hello"),
71
+ # "key2": grpc_pb2.Argument(int32_value=123),
72
+ # "key3": grpc_pb2.Argument(float_value=4.56),
73
+ # "key4": grpc_pb2.Argument(bool_value=True),
74
+ # "client_ip": grpc_pb2.Argument(string_value=local_ip)
75
+ # }
76
+
77
+ # response = rpc_client(function_name="echo", args=args)
78
+
79
+ # if 'client_ip' in response.results:
80
+ # del response.results['client_ip']
81
+
82
+ # # Print results
83
+ # print("Received response:")
84
+ # for key, arg in response.results.items():
85
+ # # Decode the oneof fields
86
+ # if arg.HasField('string_value'):
87
+ # value = arg.string_value
88
+ # elif arg.HasField('int32_value'):
89
+ # value = arg.int32_value
90
+ # elif arg.HasField('float_value'):
91
+ # value = arg.float_value
92
+ # elif arg.HasField('bool_value'):
93
+ # value = arg.bool_value
94
+ # else:
95
+ # value = "Undefined"
96
+
97
+ # print(f"{key}: {value}")
98
+
99
+ #endregion
100
+
@@ -0,0 +1,8 @@
1
+ from importlib.metadata import version, PackageNotFoundError
2
+
3
+ def main():
4
+ try:
5
+ v = version("remoterf")
6
+ except PackageNotFoundError:
7
+ v = "unknown"
8
+ print(v)
File without changes
@@ -0,0 +1 @@
1
+ from . import pluto_remote as adi