httpstate 0.0.12__tar.gz → 0.0.14__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.
- {httpstate-0.0.12 → httpstate-0.0.14}/PKG-INFO +29 -16
- {httpstate-0.0.12 → httpstate-0.0.14}/README.md +28 -15
- {httpstate-0.0.12 → httpstate-0.0.14}/pyproject.toml +1 -1
- {httpstate-0.0.12 → httpstate-0.0.14}/src/httpstate/__init__.py +6 -0
- {httpstate-0.0.12 → httpstate-0.0.14}/src/httpstate/httpstate.py +93 -23
- {httpstate-0.0.12 → httpstate-0.0.14}/.gitignore +0 -0
- {httpstate-0.0.12 → httpstate-0.0.14}/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: httpstate
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.14
|
|
4
4
|
Summary: HTTP State, httpstate.com
|
|
5
5
|
Author-email: "Alex Morales, HTTP State" <alex@httpstate.com>
|
|
6
6
|
License-Expression: AGPL-3.0
|
|
@@ -28,7 +28,7 @@ Import
|
|
|
28
28
|
import httpstate
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
Pick any valid UUID v4. You can [generate one here](https://
|
|
31
|
+
Pick any valid UUID v4. You can [generate one here](https://uuid.httpstate.com).
|
|
32
32
|
|
|
33
33
|
We'll use `45fb3654-0e92-44da-aa21-ca409c6bdab3` or `45fb36540e9244daaa21ca409c6bdab3` (without dashes).
|
|
34
34
|
|
|
@@ -44,7 +44,7 @@ and retrieve it with
|
|
|
44
44
|
data = httpstate.get('45fb36540e9244daaa21ca409c6bdab3')
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
You can also get
|
|
47
|
+
You can also get realtime updates
|
|
48
48
|
|
|
49
49
|
```python
|
|
50
50
|
hs = httpstate.HttpState('45fb36540e9244daaa21ca409c6bdab3')
|
|
@@ -58,40 +58,53 @@ That's it! 🐙
|
|
|
58
58
|
|
|
59
59
|
### Functions
|
|
60
60
|
|
|
61
|
-
- `get(uuid)`
|
|
61
|
+
- `get(uuid)`
|
|
62
62
|
Get state of UUIDv4.
|
|
63
63
|
|
|
64
|
-
- `
|
|
64
|
+
- `post(uuid, data)`
|
|
65
|
+
Alias for `set`.
|
|
66
|
+
|
|
67
|
+
- `read(uuid)`
|
|
65
68
|
Alias for `get`.
|
|
66
69
|
|
|
67
|
-
- `set(uuid, data)`
|
|
70
|
+
- `set(uuid, data)`
|
|
68
71
|
Set state of UUIDv4.
|
|
69
72
|
|
|
70
|
-
- `write(uuid, data)`
|
|
73
|
+
- `write(uuid, data)`
|
|
71
74
|
Alias for `set`.
|
|
72
75
|
|
|
73
76
|
### HttpState Class
|
|
74
77
|
|
|
75
|
-
- `HttpState(uuid)`
|
|
78
|
+
- `HttpState(uuid)`
|
|
76
79
|
Create a reactive state instance of UUIDv4.
|
|
80
|
+
- `<HttpState>.data`
|
|
81
|
+
Property with the most up-to-date state value.
|
|
77
82
|
|
|
78
83
|
<br>
|
|
79
84
|
|
|
80
|
-
- `<HttpState>.get()`
|
|
85
|
+
- `<HttpState>.get()`
|
|
81
86
|
Get state.
|
|
82
|
-
- `<HttpState>.
|
|
87
|
+
- `<HttpState>.post(data)`
|
|
88
|
+
Alias for `set`.
|
|
89
|
+
- `<HttpState>.read()`
|
|
83
90
|
Alias for `get`.
|
|
84
|
-
- `<HttpState>.set(data)`
|
|
91
|
+
- `<HttpState>.set(data)`
|
|
85
92
|
Set state.
|
|
86
|
-
- `<HttpState>.write(data)`
|
|
93
|
+
- `<HttpState>.write(data)`
|
|
87
94
|
Alias for `set`.
|
|
88
95
|
|
|
89
96
|
<br>
|
|
90
97
|
|
|
91
|
-
- `<HttpState>.off(type, callback)`
|
|
92
|
-
Unsubscribe from
|
|
93
|
-
- `<HttpState>.on(type, callback)`
|
|
94
|
-
Subscribe to
|
|
98
|
+
- `<HttpState>.off(type, callback)`
|
|
99
|
+
Unsubscribe from realtime updates.
|
|
100
|
+
- `<HttpState>.on(type, callback)`
|
|
101
|
+
Subscribe to realtime updates.
|
|
102
|
+
- `change`: fired when state data changes. Callback receives current data as argument.
|
|
103
|
+
|
|
104
|
+
<br>
|
|
105
|
+
|
|
106
|
+
- `<HttpState>.delete()`
|
|
107
|
+
Cleanup and delete the instance.
|
|
95
108
|
|
|
96
109
|
---
|
|
97
110
|
|
|
@@ -14,7 +14,7 @@ Import
|
|
|
14
14
|
import httpstate
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
Pick any valid UUID v4. You can [generate one here](https://
|
|
17
|
+
Pick any valid UUID v4. You can [generate one here](https://uuid.httpstate.com).
|
|
18
18
|
|
|
19
19
|
We'll use `45fb3654-0e92-44da-aa21-ca409c6bdab3` or `45fb36540e9244daaa21ca409c6bdab3` (without dashes).
|
|
20
20
|
|
|
@@ -30,7 +30,7 @@ and retrieve it with
|
|
|
30
30
|
data = httpstate.get('45fb36540e9244daaa21ca409c6bdab3')
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
You can also get
|
|
33
|
+
You can also get realtime updates
|
|
34
34
|
|
|
35
35
|
```python
|
|
36
36
|
hs = httpstate.HttpState('45fb36540e9244daaa21ca409c6bdab3')
|
|
@@ -44,40 +44,53 @@ That's it! 🐙
|
|
|
44
44
|
|
|
45
45
|
### Functions
|
|
46
46
|
|
|
47
|
-
- `get(uuid)`
|
|
47
|
+
- `get(uuid)`
|
|
48
48
|
Get state of UUIDv4.
|
|
49
49
|
|
|
50
|
-
- `
|
|
50
|
+
- `post(uuid, data)`
|
|
51
|
+
Alias for `set`.
|
|
52
|
+
|
|
53
|
+
- `read(uuid)`
|
|
51
54
|
Alias for `get`.
|
|
52
55
|
|
|
53
|
-
- `set(uuid, data)`
|
|
56
|
+
- `set(uuid, data)`
|
|
54
57
|
Set state of UUIDv4.
|
|
55
58
|
|
|
56
|
-
- `write(uuid, data)`
|
|
59
|
+
- `write(uuid, data)`
|
|
57
60
|
Alias for `set`.
|
|
58
61
|
|
|
59
62
|
### HttpState Class
|
|
60
63
|
|
|
61
|
-
- `HttpState(uuid)`
|
|
64
|
+
- `HttpState(uuid)`
|
|
62
65
|
Create a reactive state instance of UUIDv4.
|
|
66
|
+
- `<HttpState>.data`
|
|
67
|
+
Property with the most up-to-date state value.
|
|
63
68
|
|
|
64
69
|
<br>
|
|
65
70
|
|
|
66
|
-
- `<HttpState>.get()`
|
|
71
|
+
- `<HttpState>.get()`
|
|
67
72
|
Get state.
|
|
68
|
-
- `<HttpState>.
|
|
73
|
+
- `<HttpState>.post(data)`
|
|
74
|
+
Alias for `set`.
|
|
75
|
+
- `<HttpState>.read()`
|
|
69
76
|
Alias for `get`.
|
|
70
|
-
- `<HttpState>.set(data)`
|
|
77
|
+
- `<HttpState>.set(data)`
|
|
71
78
|
Set state.
|
|
72
|
-
- `<HttpState>.write(data)`
|
|
79
|
+
- `<HttpState>.write(data)`
|
|
73
80
|
Alias for `set`.
|
|
74
81
|
|
|
75
82
|
<br>
|
|
76
83
|
|
|
77
|
-
- `<HttpState>.off(type, callback)`
|
|
78
|
-
Unsubscribe from
|
|
79
|
-
- `<HttpState>.on(type, callback)`
|
|
80
|
-
Subscribe to
|
|
84
|
+
- `<HttpState>.off(type, callback)`
|
|
85
|
+
Unsubscribe from realtime updates.
|
|
86
|
+
- `<HttpState>.on(type, callback)`
|
|
87
|
+
Subscribe to realtime updates.
|
|
88
|
+
- `change`: fired when state data changes. Callback receives current data as argument.
|
|
89
|
+
|
|
90
|
+
<br>
|
|
91
|
+
|
|
92
|
+
- `<HttpState>.delete()`
|
|
93
|
+
Cleanup and delete the instance.
|
|
81
94
|
|
|
82
95
|
---
|
|
83
96
|
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
# version 3 of the License, or (at your option) any later version.
|
|
8
8
|
|
|
9
9
|
import asyncio
|
|
10
|
+
import struct
|
|
10
11
|
import threading
|
|
12
|
+
import types
|
|
11
13
|
import urllib.error
|
|
12
14
|
import urllib.request
|
|
13
15
|
import websockets
|
|
@@ -26,11 +28,38 @@ def get(uuid:str) -> None|str:
|
|
|
26
28
|
except Exception:
|
|
27
29
|
return None
|
|
28
30
|
|
|
31
|
+
class MessageType:
|
|
32
|
+
def __init__(self, uuid:str, timestamp:int, type:int, value:bytes) -> None:
|
|
33
|
+
self.uuid:str = uuid
|
|
34
|
+
self.timestamp:int = timestamp
|
|
35
|
+
self.type:int = type
|
|
36
|
+
self.value:bytes = value
|
|
37
|
+
|
|
38
|
+
class Message:
|
|
39
|
+
@staticmethod
|
|
40
|
+
def unpack(b:bytes) -> MessageType:
|
|
41
|
+
length:int = b[0]
|
|
42
|
+
|
|
43
|
+
return MessageType(
|
|
44
|
+
uuid=b[1:1+length].decode('utf-8'),
|
|
45
|
+
timestamp=struct.unpack_from('>Q', b, 1+length)[0],
|
|
46
|
+
type=b[1+length+8],
|
|
47
|
+
value=b[1+length+9:],
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
message:type = Message
|
|
51
|
+
|
|
52
|
+
def post(uuid:str, data:str) -> None|int:
|
|
53
|
+
return set(uuid, data)
|
|
54
|
+
|
|
55
|
+
def put(uuid:str, data:str) -> None|int:
|
|
56
|
+
return set(uuid, data)
|
|
57
|
+
|
|
29
58
|
def read(uuid:str) -> None|str:
|
|
30
59
|
return get(uuid)
|
|
31
60
|
|
|
32
61
|
def set(uuid:str, data:str) -> None|int:
|
|
33
|
-
req = urllib.request.Request(
|
|
62
|
+
req:urllib.request.Request = urllib.request.Request(
|
|
34
63
|
f'https://httpstate.com/{uuid}',
|
|
35
64
|
data=data.encode('utf-8'),
|
|
36
65
|
headers={ 'Content-Type':'text/plain;charset=UTF-8' },
|
|
@@ -50,23 +79,54 @@ def write(uuid:str, data:str) -> None|int:
|
|
|
50
79
|
|
|
51
80
|
# HTTP State
|
|
52
81
|
class HttpState:
|
|
53
|
-
def __init__(self, uuid:str):
|
|
82
|
+
def __init__(self, uuid:str) -> None:
|
|
54
83
|
self.data:None|str = None
|
|
84
|
+
self.el:None|asyncio.AbstractEventLoop = None
|
|
55
85
|
self.et:Dict[str, List[Callable[[None|str], None]]] = {}
|
|
56
86
|
self.uuid:str = uuid
|
|
57
87
|
self.ws:None|websockets.WebSocketClientProtocol = None
|
|
58
88
|
|
|
89
|
+
threading.Thread(
|
|
90
|
+
daemon=True,
|
|
91
|
+
target=self._el
|
|
92
|
+
).start()
|
|
93
|
+
|
|
59
94
|
threading.Thread(
|
|
60
95
|
daemon=True,
|
|
61
96
|
target=lambda : asyncio.run(self._ws())
|
|
62
97
|
).start()
|
|
98
|
+
|
|
99
|
+
def _el(self) -> None:
|
|
100
|
+
self.el:asyncio.AbstractEventLoop = asyncio.new_event_loop()
|
|
101
|
+
|
|
102
|
+
asyncio.set_event_loop(self.el)
|
|
103
|
+
|
|
104
|
+
self.el.call_soon_threadsafe(lambda : self.get())
|
|
105
|
+
|
|
106
|
+
self.el.run_forever()
|
|
63
107
|
|
|
64
|
-
async def _ws(self):
|
|
65
|
-
self.ws = await websockets.connect(f"wss://httpstate.com/{self.uuid}")
|
|
108
|
+
async def _ws(self) -> None:
|
|
109
|
+
self.ws:websockets.WebSocketClientProtocol = await websockets.connect(f"wss://httpstate.com/{self.uuid}")
|
|
66
110
|
|
|
67
111
|
await self.ws.send(f'{{"open":"{self.uuid}"}}')
|
|
112
|
+
self.emit('open')
|
|
113
|
+
|
|
114
|
+
async def data() -> None:
|
|
115
|
+
async for data in self.ws:
|
|
116
|
+
data:MessageType = message.unpack(data)
|
|
117
|
+
|
|
118
|
+
if(
|
|
119
|
+
data
|
|
120
|
+
and data.uuid == self.uuid
|
|
121
|
+
and data.type == 1
|
|
122
|
+
):
|
|
123
|
+
self.data:None|str = data.value.decode()
|
|
124
|
+
|
|
125
|
+
self.emit('change', self.data)
|
|
126
|
+
|
|
127
|
+
asyncio.create_task(data())
|
|
68
128
|
|
|
69
|
-
async def interval():
|
|
129
|
+
async def interval() -> None:
|
|
70
130
|
while True:
|
|
71
131
|
try:
|
|
72
132
|
await self.ws.ping()
|
|
@@ -76,28 +136,32 @@ class HttpState:
|
|
|
76
136
|
break
|
|
77
137
|
|
|
78
138
|
asyncio.create_task(interval())
|
|
139
|
+
|
|
140
|
+
await asyncio.Event().wait()
|
|
141
|
+
|
|
142
|
+
def delete(self) -> None:
|
|
143
|
+
pass
|
|
79
144
|
|
|
80
|
-
|
|
81
|
-
self.data = data.decode()
|
|
82
|
-
|
|
83
|
-
if(
|
|
84
|
-
self.data
|
|
85
|
-
and len(self.data) > 32
|
|
86
|
-
and self.data[:32] == self.uuid
|
|
87
|
-
and self.data[45] == '1'
|
|
88
|
-
):
|
|
89
|
-
self.emit('change', self.data[46:])
|
|
90
|
-
|
|
91
|
-
def emit(self, type:str, data:None|str) -> None:
|
|
145
|
+
def emit(self, type:str, data:None|str = None) -> "HttpState":
|
|
92
146
|
for callback in self.et.get(type, []):
|
|
93
|
-
|
|
147
|
+
if(data is None):
|
|
148
|
+
callback()
|
|
149
|
+
else:
|
|
150
|
+
callback(data)
|
|
94
151
|
|
|
95
152
|
return self
|
|
96
153
|
|
|
97
154
|
def get(self) -> None|str:
|
|
98
|
-
|
|
155
|
+
data:None|str = get(self.uuid)
|
|
156
|
+
|
|
157
|
+
if(data != self.data):
|
|
158
|
+
self.el.call_soon_threadsafe(lambda : self.emit('change', self.data))
|
|
159
|
+
|
|
160
|
+
self.data = data
|
|
161
|
+
|
|
162
|
+
return self.data
|
|
99
163
|
|
|
100
|
-
def off(self, type:str, callback:Callable[[None|str], None]):
|
|
164
|
+
def off(self, type:str, callback:Callable[[None|str], None]) -> "HttpState":
|
|
101
165
|
if type in self.et:
|
|
102
166
|
try:
|
|
103
167
|
self.et[type].remove(callback)
|
|
@@ -109,19 +173,25 @@ class HttpState:
|
|
|
109
173
|
|
|
110
174
|
return self
|
|
111
175
|
|
|
112
|
-
def on(self, type:str, callback:Callable[[None|str], None]) ->
|
|
176
|
+
def on(self, type:str, callback:Callable[[None|str], None]) -> "HttpState":
|
|
113
177
|
if type not in self.et:
|
|
114
178
|
self.et[type] = []
|
|
115
179
|
|
|
116
180
|
self.et[type].append(callback)
|
|
117
181
|
|
|
118
182
|
return self
|
|
183
|
+
|
|
184
|
+
def post(self, data:str) -> None|int:
|
|
185
|
+
return self.set(data)
|
|
186
|
+
|
|
187
|
+
def put(self, data:str) -> None|int:
|
|
188
|
+
return self.set(data)
|
|
119
189
|
|
|
120
190
|
def read(self) -> None|str:
|
|
121
|
-
return
|
|
191
|
+
return self.get()
|
|
122
192
|
|
|
123
193
|
def set(self, data:str) -> None|int:
|
|
124
194
|
return set(self.uuid, data)
|
|
125
195
|
|
|
126
196
|
def write(self, data:str) -> None|int:
|
|
127
|
-
return
|
|
197
|
+
return self.set(data)
|
|
File without changes
|
|
File without changes
|