hobbyrpc 0.1.0__tar.gz

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.
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.3
2
+ Name: hobbyrpc
3
+ Version: 0.1.0
4
+ Summary: To make RPC clients and servers with Python
5
+ Author: Anatoly Chernov
6
+ Author-email: Anatoly Chernov <chertoly@gmail.com>
7
+ Requires-Python: >=3.13
8
+ Description-Content-Type: text/markdown
9
+
10
+ ## Introduction
11
+
12
+ It is a Python package to make [Hobby-RPC][hobby_rpc] clients and servers.
13
+
14
+ To install:
15
+
16
+ ```
17
+ pip install hobbyrpc
18
+ ```
19
+
20
+ ## Clients
21
+
22
+ A usage example:
23
+
24
+ ```python
25
+ from hobbyrpc import Client
26
+
27
+ # via tcp
28
+ call = Client(
29
+ address='http://127.0.0.1:8080',
30
+ token="authorization token",
31
+ )
32
+
33
+ # or via unix socket
34
+ call = Client(
35
+ address='/tmp/path/to/rpc.socket',
36
+ token="authorization token",
37
+ )
38
+
39
+ output = call('SomeNullaryFunction')
40
+ output = call('SomeUnaryFunction', **input)
41
+
42
+ # a call to Compile that compiles CoffeeScript code to JavaScript
43
+ output = call(
44
+ 'Compile',
45
+ code='answer = 42',
46
+ bare=True,
47
+ )
48
+ ```
49
+
50
+ `input` and `output` are Python objects serializable to and deserializable from JSON.
51
+
52
+ ## Servers
53
+
54
+ Not implemented yet.
55
+
56
+ [hobby_rpc]: https://github.com/ch1c0t/hobby-rpc.protocol
@@ -0,0 +1,47 @@
1
+ ## Introduction
2
+
3
+ It is a Python package to make [Hobby-RPC][hobby_rpc] clients and servers.
4
+
5
+ To install:
6
+
7
+ ```
8
+ pip install hobbyrpc
9
+ ```
10
+
11
+ ## Clients
12
+
13
+ A usage example:
14
+
15
+ ```python
16
+ from hobbyrpc import Client
17
+
18
+ # via tcp
19
+ call = Client(
20
+ address='http://127.0.0.1:8080',
21
+ token="authorization token",
22
+ )
23
+
24
+ # or via unix socket
25
+ call = Client(
26
+ address='/tmp/path/to/rpc.socket',
27
+ token="authorization token",
28
+ )
29
+
30
+ output = call('SomeNullaryFunction')
31
+ output = call('SomeUnaryFunction', **input)
32
+
33
+ # a call to Compile that compiles CoffeeScript code to JavaScript
34
+ output = call(
35
+ 'Compile',
36
+ code='answer = 42',
37
+ bare=True,
38
+ )
39
+ ```
40
+
41
+ `input` and `output` are Python objects serializable to and deserializable from JSON.
42
+
43
+ ## Servers
44
+
45
+ Not implemented yet.
46
+
47
+ [hobby_rpc]: https://github.com/ch1c0t/hobby-rpc.protocol
@@ -0,0 +1,23 @@
1
+ [project]
2
+ name = "hobbyrpc"
3
+ version = "0.1.0"
4
+ description = "To make RPC clients and servers with Python"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Anatoly Chernov", email = "chertoly@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.13"
10
+ dependencies = []
11
+
12
+ [project.scripts]
13
+ hobbyrpc = "hobbyrpc:main"
14
+
15
+ [build-system]
16
+ requires = ["uv_build>=0.9.26,<0.10.0"]
17
+ build-backend = "uv_build"
18
+
19
+ [dependency-groups]
20
+ dev = [
21
+ "pytest>=9.0.3",
22
+ "pytest-xprocess>=1.0.2",
23
+ ]
@@ -0,0 +1 @@
1
+ from .client import Client
@@ -0,0 +1,32 @@
1
+ from urllib.parse import urlparse
2
+ from http.client import HTTPConnection, HTTPSConnection
3
+
4
+ from .unix import UnixSocketConnection
5
+ from .exceptions import NotImplementedScheme
6
+
7
+ class Client:
8
+ def __init__(self, address, token=None):
9
+ if address.startswith('/'):
10
+ self.http = UnixSocketConnection(address)
11
+ self.http_path = '/'
12
+ else:
13
+ url = urlparse(address)
14
+ match url.scheme:
15
+ case 'http':
16
+ Connection = HTTPConnection
17
+ case 'https':
18
+ Connection = HTTPSConnection
19
+ case _:
20
+ raise NotImplementedScheme(f'#{url.scheme} of #{url}')
21
+ self.http = Connection(url.hostname, url.port)
22
+ self.http_path = url.path
23
+
24
+ self.headers = {
25
+ 'Content-type': 'application/json',
26
+ }
27
+
28
+ if token:
29
+ self.headers['Authorization'] = token
30
+
31
+ from .call import call
32
+ Client.__call__ = call
@@ -0,0 +1,30 @@
1
+ import json
2
+ from .exceptions import BadRequest, Forbidden, UnexpectedStatus
3
+
4
+ def call(self, name, **input):
5
+ body = {
6
+ 'fn': name,
7
+ }
8
+
9
+ if input:
10
+ body['in'] = input
11
+
12
+ self.http.request(
13
+ 'POST', self.http_path,
14
+ body=json.dumps(body),
15
+ headers=self.headers,
16
+ )
17
+
18
+ response = self.http.getresponse()
19
+ raw_data = response.read()
20
+
21
+ match response.status:
22
+ case 200:
23
+ data = json.loads(raw_data.decode('utf-8'))
24
+ return data
25
+ case 400:
26
+ raise BadRequest
27
+ case 403:
28
+ raise Forbidden
29
+ case _:
30
+ raise UnexpectedStatus(f'{response.status} in response {response}')
@@ -0,0 +1,11 @@
1
+ class BadRequest(Exception):
2
+ pass
3
+
4
+ class Forbidden(Exception):
5
+ pass
6
+
7
+ class UnexpectedStatus(Exception):
8
+ pass
9
+
10
+ class NotImplementedScheme(Exception):
11
+ pass
@@ -0,0 +1,12 @@
1
+ from http.client import HTTPConnection
2
+ from pathlib import Path
3
+ import socket
4
+
5
+ class UnixSocketConnection(HTTPConnection):
6
+ def __init__(self, path):
7
+ self.socket_path = Path(path)
8
+ super().__init__(host='0.0.0.0')
9
+
10
+ def connect(self):
11
+ self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
12
+ self.sock.connect(self.socket_path.as_posix())