diff --git a/bin/collector_loadtest.py b/bin/collector_loadtest.py
--- a/bin/collector_loadtest.py
+++ b/bin/collector_loadtest.py
@@ -1,65 +1,51 @@
#!bin/python
+import asyncio
import logging
import time
from typing import cast
-import twisted.internet.reactor
-from light9.collector.collector_client import sendToCollector
+from light9.collector.collector_client_asyncio import sendToCollector
from light9.effect.settings import DeviceSettings
from light9.namespaces import DEV, L9
from light9.run_local import log
-from twisted.internet.interfaces import IReactorCore
-reactor = cast(IReactorCore, twisted.internet.reactor)
log.setLevel(logging.DEBUG)
-def loadTest():
+async def loadTest():
print("scheduling loadtest")
- n = 2500
- times = [None] * n
+ n = 200000
+ period=1
+ times = []
session = "loadtest%s" % time.time()
- offset = 0
for i in range(n):
+ if i % 100 == 0:
+ log.info('sendToCollector %s', i)
+ start = time.time()
+ await sendToCollector(
+ "http://localhost:8202/",
+ session,
+ DeviceSettings(
+ graph=None,
+ settingsList=[
+ [DEV["backlight1"], L9["color"], "#ffffff"], #
+ [DEV["backlight2"], L9["color"], "#ffffff"],
+ [DEV["backlight3"], L9["color"], "#ffffff"],
+ [DEV["backlight4"], L9["color"], "#ffffff"],
+ [DEV["backlight5"], L9["color"], "#ffffff"],
+ [DEV["down2"], L9["color"], "#ffffff"],
+ [DEV["down3"], L9["color"], "#ffffff"],
+ [DEV["down4"], L9["color"], "#ffffff"],
+ [DEV["houseSide"], L9["level"], .8],
+ [DEV["backlight5"], L9["uv"], 0.011]
+ ]))
+ times.append(time.time() - start)
+ await asyncio.sleep(period)
- def send(i):
- if i % 100 == 0:
- log.info('sendToCollector %s', i)
- d = sendToCollector(
- "http://localhost:8202/",
- session,
- DeviceSettings(
- graph=None,
- settingsList=[
- [DEV["backlight1"], L9["color"], "#ffffff"], #
- [DEV["backlight2"], L9["color"], "#ffffff"],
- [DEV["backlight3"], L9["color"], "#ffffff"],
- [DEV["backlight4"], L9["color"], "#ffffff"],
- [DEV["backlight5"], L9["color"], "#ffffff"],
- [DEV["down2"], L9["color"], "#ffffff"],
- [DEV["down3"], L9["color"], "#ffffff"],
- [DEV["down4"], L9["color"], "#ffffff"],
- [DEV["houseSide"], L9["level"], .8],
- [DEV["backlight5"], L9["uv"], 0.011]
- ]))
-
- def ontime(dt, i=i):
- times[i] = dt
-
- d.addCallback(ontime)
-
- reactor.callLater(offset, send, i)
- offset += .002
-
- def done():
- print("loadtest done")
- with open('/tmp/times', 'w') as f:
- f.write(''.join('%s\n' % t for t in times))
- reactor.stop()
-
- reactor.callLater(offset + .5, done)
- reactor.run()
+ print("loadtest done")
+ with open('/tmp/times', 'w') as f:
+ f.write(''.join('%s\n' % t for t in times))
if __name__ == '__main__':
- loadTest()
+ asyncio.run(loadTest())
diff --git a/light9/collector/collector.py b/light9/collector/collector.py
--- a/light9/collector/collector.py
+++ b/light9/collector/collector.py
@@ -8,14 +8,13 @@ from light9.collector.device import toOu
from light9.collector.output import Output as OutputInstance
from light9.collector.weblisteners import WebListeners
from light9.namespaces import L9, RDF
-from rdfdb.syncedgraph import SyncedGraph
+from rdfdb.syncedgraph.syncedgraph import SyncedGraph
from light9.newtypes import ClientType, ClientSessionType, OutputUri, DeviceUri, DeviceClass, DmxIndex, DmxMessageIndex, DeviceAttr, OutputAttr, OutputValue, UnixTime, OutputRange
+
log = logging.getLogger('collector')
-def outputMap(
- graph: Graph, outputs: List[OutputInstance]
-) -> Dict[Tuple[DeviceUri, OutputAttr], Tuple[OutputInstance, DmxMessageIndex]]:
+def outputMap(graph: SyncedGraph, outputs: List[OutputInstance]) -> Dict[Tuple[DeviceUri, OutputAttr], Tuple[OutputInstance, DmxMessageIndex]]:
"""From rdf config graph, compute a map of
(device, outputattr) : (output, index)
that explains which output index to set for any device update.
@@ -42,10 +41,8 @@ def outputMap(
raise ValueError('no :dmxBase for %s' % dev)
dmxBase = DmxIndex(cast(Literal, base).toPython())
for row in graph.objects(dc, L9['attr']):
- outputAttr = cast(OutputAttr,
- graph.value(row, L9['outputAttr']))
- offset = DmxIndex(
- cast(Literal, graph.value(row, L9['dmxOffset'])).toPython())
+ outputAttr = cast(OutputAttr, graph.value(row, L9['outputAttr']))
+ offset = DmxIndex(cast(Literal, graph.value(row, L9['dmxOffset'])).toPython())
index = DmxMessageIndex(dmxBase + offset - 1)
ret[(dev, outputAttr)] = (output, index)
log.debug(' map %s to %s,%s', outputAttr, output, index)
@@ -53,12 +50,9 @@ def outputMap(
class Collector:
+ """receives setAttrs calls; combines settings; renders them into what outputs like; call Output.update"""
- def __init__(self,
- graph: SyncedGraph,
- outputs: List[OutputInstance],
- listeners: Optional[WebListeners] = None,
- clientTimeoutSec: float = 10):
+ def __init__(self, graph: SyncedGraph, outputs: List[OutputInstance], listeners: WebListeners, clientTimeoutSec: float = 10):
self.graph = graph
self.outputs = outputs
self.listeners = listeners
@@ -69,8 +63,7 @@ class Collector:
self.graph.addHandler(self.rebuildOutputMap)
# client : (session, time, {(dev,devattr): latestValue})
- self.lastRequest: Dict[Tuple[ClientType, ClientSessionType], Tuple[
- UnixTime, Dict[Tuple[DeviceUri, DeviceAttr], float]]] = {}
+ self.lastRequest: Dict[Tuple[ClientType, ClientSessionType], Tuple[UnixTime, Dict[Tuple[DeviceUri, DeviceAttr], float]]] = {}
# (dev, devAttr): value to use instead of 0
self.stickyAttrs: Dict[Tuple[DeviceUri, DeviceAttr], float] = {}
@@ -78,19 +71,18 @@ class Collector:
def rebuildOutputMap(self):
self.outputMap = outputMap(self.graph, self.outputs)
self.deviceType: Dict[DeviceUri, DeviceClass] = {}
- self.remapOut: Dict[Tuple[DeviceUri, DeviceAttr], OutputRange] = {}
+ self.remapOut: Dict[Tuple[DeviceUri, OutputAttr], OutputRange] = {}
for dc in self.graph.subjects(RDF.type, L9['DeviceClass']):
- for dev in map(DeviceUri, self.graph.subjects(RDF.type, dc)):
+ dc = cast(DeviceClass, dc)
+ for dev in self.graph.subjects(RDF.type, dc):
+ dev = cast(DeviceUri, dev)
self.allDevices.add(dev)
self.deviceType[dev] = dc
for remap in self.graph.objects(dev, L9['outputAttrRange']):
attr = OutputAttr(self.graph.value(remap, L9['outputAttr']))
- start = cast(Literal,
- self.graph.value(remap,
- L9['start'])).toPython()
- end = cast(Literal, self.graph.value(remap,
- L9['end'])).toPython()
+ start = cast(Literal, self.graph.value(remap, L9['start'])).toPython()
+ end = cast(Literal, self.graph.value(remap, L9['end'])).toPython()
self.remapOut[(dev, attr)] = OutputRange((start, end))
def _forgetStaleClients(self, now):
@@ -104,9 +96,7 @@ class Collector:
del self.lastRequest[c]
# todo: move to settings.py
- def resolvedSettingsDict(
- self, settingsList: List[Tuple[DeviceUri, DeviceAttr, float]]
- ) -> Dict[Tuple[DeviceUri, DeviceAttr], float]:
+ def resolvedSettingsDict(self, settingsList: List[Tuple[DeviceUri, DeviceAttr, float]]) -> Dict[Tuple[DeviceUri, DeviceAttr], float]:
out: Dict[Tuple[DeviceUri, DeviceAttr], float] = {}
for d, da, v in settingsList:
if (d, da) in out:
@@ -117,16 +107,12 @@ class Collector:
def _warnOnLateRequests(self, client, now, sendTime):
requestLag = now - sendTime
- if requestLag > .1 and now > self.initTime + 10 and getattr(
- self, '_lastWarnTime', 0) < now - 3:
+ if requestLag > .1 and now > self.initTime + 10 and getattr(self, '_lastWarnTime', 0) < now - 3:
self._lastWarnTime = now
- log.warn(
- 'collector.setAttrs from %s is running %.1fms after the request was made',
- client, requestLag * 1000)
+ log.warn('collector.setAttrs from %s is running %.1fms after the request was made', client, requestLag * 1000)
def _merge(self, lastRequests):
- deviceAttrs: Dict[DeviceUri, Dict[DeviceAttr, float]] = {
- } # device: {deviceAttr: value}
+ deviceAttrs: Dict[DeviceUri, Dict[DeviceAttr, float]] = {} # device: {deviceAttr: value}
for _, lastSettings in lastRequests:
for (device, deviceAttr), value in lastSettings.items():
if (device, deviceAttr) in self.remapOut:
@@ -135,14 +121,13 @@ class Collector:
attrs = deviceAttrs.setdefault(device, {})
if deviceAttr in attrs:
- value = resolve(device, deviceAttr,
- [attrs[deviceAttr], value])
+ value = resolve(device, deviceAttr, [attrs[deviceAttr], value])
attrs[deviceAttr] = value
# list should come from the graph. these are attrs
# that should default to holding the last position,
# not going to 0.
if deviceAttr in [L9['rx'], L9['ry'], L9['zoom'], L9['focus']]:
- self.stickyAttrs[(device, deviceAttr)] = value
+ self.stickyAttrs[(device, deviceAttr)] = cast(float, value)
# e.g. don't let an unspecified rotation go to 0
for (d, da), v in self.stickyAttrs.items():
@@ -152,9 +137,7 @@ class Collector:
return deviceAttrs
- def setAttrs(self, client: ClientType, clientSession: ClientSessionType,
- settings: List[Tuple[DeviceUri, DeviceAttr, float]],
- sendTime: UnixTime):
+ def setAttrs(self, client: ClientType, clientSession: ClientSessionType, settings: List[Tuple[DeviceUri, DeviceAttr, float]], sendTime: UnixTime):
"""
settings is a list of (device, attr, value). These attrs are
device attrs. We resolve conflicting values, process them into
@@ -185,9 +168,7 @@ class Collector:
continue
try:
outputAttrs[d] = toOutputAttrs(devType, deviceAttrs.get(d, {}))
- if self.listeners:
- self.listeners.outputAttrsSet(d, outputAttrs[d],
- self.outputMap)
+ self.listeners.outputAttrsSet(d, outputAttrs[d], self.outputMap)
except Exception as e:
log.error('failing toOutputAttrs on %s: %r', d, e)
@@ -202,9 +183,7 @@ class Collector:
index = DmxMessageIndex(_index)
_, outArray = pendingOut[outputUri]
if outArray[index] != 0:
- log.warn(
- f'conflict: {output} output array was already nonzero at 0-based index {index}'
- )
+ log.warn(f'conflict: {output} output array was already nonzero at 0-based index {index}')
raise ValueError(f"someone already wrote to index {index}")
outArray[index] = value
@@ -213,7 +192,4 @@ class Collector:
out.update(bytes(buf))
dt2 = 1000 * (time.time() - now)
if dt1 > 30:
- log.warn(
- "slow setAttrs: %.1fms -> flush -> %.1fms. lr %s da %s oa %s" %
- (dt1, dt2, len(
- self.lastRequest), len(deviceAttrs), len(outputAttrs)))
+ log.warn("slow setAttrs: %.1fms -> flush -> %.1fms. lr %s da %s oa %s" % (dt1, dt2, len(self.lastRequest), len(deviceAttrs), len(outputAttrs)))
diff --git a/light9/collector/collector_client_asyncio.py b/light9/collector/collector_client_asyncio.py
new file mode 100644
--- /dev/null
+++ b/light9/collector/collector_client_asyncio.py
@@ -0,0 +1,23 @@
+from light9 import networking
+from light9.collector.collector_client import toCollectorJson
+from light9.effect.settings import DeviceSettings
+import aiohttp
+
+class Sender:
+ def __init__(self):
+ pass#self.http_session = aiohttp.ClientSession()
+
+ async def send(self, client: str, session: str, settings: DeviceSettings):
+ msg = toCollectorJson(client, session, settings).encode('utf8')
+
+ async with aiohttp.ClientSession() as ses: # i want to use the one from __init__!
+ async with ses.put(
+ networking.collector.path('attrs'), data=msg, timeout=1) as resp:
+ if resp.status != 202:
+ body = await resp.text()
+ raise ValueError(f'collector returned {resp.status}: {body}')
+
+_sender = Sender()
+
+async def sendToCollector(client: str, session: str, settings: DeviceSettings, useZmq=False):
+ await _sender.send(client, session, settings)
diff --git a/light9/collector/device.py b/light9/collector/device.py
--- a/light9/collector/device.py
+++ b/light9/collector/device.py
@@ -1,11 +1,12 @@
import logging
-from typing import Dict, List, Any
+from typing import Dict, List, Any, TypeVar, cast
from light9.namespaces import L9
from rdflib import Literal, URIRef
from webcolors import hex_to_rgb, rgb_to_hex
from colormath.color_objects import sRGBColor, CMYColor
import colormath.color_conversions
from light9.newtypes import OutputAttr, OutputValue, DeviceUri, DeviceAttr
+
log = logging.getLogger('device')
@@ -43,10 +44,19 @@ def _8bit(f):
return clamp255(int(f * 255))
+def _maxColor(values: List[str]) -> str:
+ rgbs = [hex_to_rgb(v) for v in values]
+ maxes = [max(component) for component in zip(*rgbs)]
+ return rgb_to_hex(tuple(maxes))
+
+
+VT = TypeVar('VT', float, int, str)
+
+
def resolve(
deviceType: DeviceUri, # should be DeviceClass?
deviceAttr: DeviceAttr,
- values: List[Any]):
+ values: List[VT]) -> Any: # todo: return should be VT
"""
return one value to use for this attr, given a set of them that
have come in simultaneously. len(values) >= 1.
@@ -56,12 +66,9 @@ def resolve(
if len(values) == 1:
return values[0]
if deviceAttr == DeviceAttr(L9['color']):
- rgbs = [hex_to_rgb(v) for v in values]
- return rgb_to_hex([max(*component) for component in zip(*rgbs)])
+ return _maxColor(cast(List[str], values))
# incomplete. how-to-resolve should be on the DeviceAttr defs in the graph.
- if deviceAttr in map(
- DeviceAttr,
- [L9['rx'], L9['ry'], L9['zoom'], L9['focus'], L9['iris']]):
+ if deviceAttr in map(DeviceAttr, [L9['rx'], L9['ry'], L9['zoom'], L9['focus'], L9['iris']]):
floatVals = []
for v in values:
if isinstance(v, Literal):
@@ -76,11 +83,8 @@ def resolve(
return max(values)
-def toOutputAttrs(deviceType,
- deviceAttrSettings) -> Dict[OutputAttr, OutputValue]:
- return dict(
- (OutputAttr(u), OutputValue(v)) for u, v in untype_toOutputAttrs(
- deviceType, deviceAttrSettings).items())
+def toOutputAttrs(deviceType, deviceAttrSettings) -> Dict[OutputAttr, OutputValue]:
+ return dict((OutputAttr(u), OutputValue(v)) for u, v in untype_toOutputAttrs(deviceType, deviceAttrSettings).items())
def untype_toOutputAttrs(deviceType, deviceAttrSettings) -> Dict[URIRef, int]:
@@ -104,8 +108,7 @@ def untype_toOutputAttrs(deviceType, dev
return r, g, b
def cmyAttr(attr):
- rgb = sRGBColor.new_from_rgb_hex(deviceAttrSettings.get(
- attr, '#000000'))
+ rgb = sRGBColor.new_from_rgb_hex(deviceAttrSettings.get(attr, '#000000'))
out = colormath.color_conversions.convert_color(rgb, CMYColor)
return (_8bit(out.cmy_c), _8bit(out.cmy_m), _8bit(out.cmy_y))
@@ -131,23 +134,10 @@ def untype_toOutputAttrs(deviceType, dev
return {L9['red']: r, L9['green']: g, L9['blue']: b}
elif deviceType == L9['LedPar90']:
r, g, b = rgbAttr(L9['color'])
- return {
- L9['master']: 255,
- L9['red']: r,
- L9['green']: g,
- L9['blue']: b,
- L9['white']: 0
- }
+ return {L9['master']: 255, L9['red']: r, L9['green']: g, L9['blue']: b, L9['white']: 0}
elif deviceType == L9['LedPar54']:
r, g, b = rgbAttr(L9['color'])
- return {
- L9['master']: 255,
- L9['red']: r,
- L9['green']: g,
- L9['blue']: b,
- L9['white']: 0,
- L9['strobe']: 0
- }
+ return {L9['master']: 255, L9['red']: r, L9['green']: g, L9['blue']: b, L9['white']: 0, L9['strobe']: 0}
elif deviceType == L9['SimpleDimmer']:
return {L9['level']: _8bit(floatAttr(L9['brightness']))}
elif deviceType == L9['MegaFlash']:
@@ -179,8 +169,7 @@ def untype_toOutputAttrs(deviceType, dev
return out
elif deviceType == L9['ChauvetHex12']:
out = {}
- out[L9['red']], out[L9['green']], out[L9['blue']] = r, g, b = rgbAttr(
- L9['color'])
+ out[L9['red']], out[L9['green']], out[L9['blue']] = r, g, b = rgbAttr(L9['color'])
out[L9['amber']] = 0
out[L9['white']] = min(r, g, b)
out[L9['uv']] = _8bit(floatAttr(L9['uv']))
@@ -231,15 +220,13 @@ def untype_toOutputAttrs(deviceType, dev
}
# note these values are set to 'fade', so they update slowly. Haven't found where to turn that off.
- out[L9['cyan']], out[L9['magenta']], out[L9['yellow']] = cmyAttr(
- L9['color'])
+ out[L9['cyan']], out[L9['magenta']], out[L9['yellow']] = cmyAttr(L9['color'])
out[L9['focusHi']], out[L9['focusLo']] = fine16Attr(L9['focus'])
out[L9['panHi']], out[L9['panLo']] = fine16Attr(L9['rx'])
out[L9['tiltHi']], out[L9['tiltLo']] = fine16Attr(L9['ry'])
out[L9['zoomHi']], out[L9['zoomLo']] = fine16Attr(L9['zoom'])
- out[L9['dimmerFadeHi']] = 0 if deviceAttrSettings.get(
- L9['color'], '#000000') == '#000000' else 255
+ out[L9['dimmerFadeHi']] = 0 if deviceAttrSettings.get(L9['color'], '#000000') == '#000000' else 255
out[L9['goboChoice']] = {
L9['open']: 0,
diff --git a/light9/collector/service.py b/light9/collector/service.py
--- a/light9/collector/service.py
+++ b/light9/collector/service.py
@@ -6,6 +6,7 @@ custom code for some attributes.
Input can be over http or zmq.
"""
+import asyncio
import functools
import logging
import os
@@ -15,12 +16,12 @@ from typing import List
from light9 import networking
from light9.collector.collector import Collector
from light9.collector.output import ArtnetDmx, DummyOutput, Output # noqa
-from light9.collector.weblisteners import WebListeners
+from light9.collector.weblisteners import WebListeners, UiListener
from light9.namespaces import L9
from light9.run_local import log
from light9.zmqtransport import parseJsonMessage
from prometheus_client import Summary
-from rdfdb.syncedgraph import SyncedGraph
+from rdfdb.syncedgraph.syncedgraph import SyncedGraph
from starlette.applications import Starlette
from starlette.endpoints import WebSocketEndpoint
from starlette.responses import Response
@@ -28,10 +29,9 @@ from starlette.routing import Route, Web
from starlette.types import Receive, Scope, Send
from starlette.websockets import WebSocket
from starlette_exporter import PrometheusMiddleware, handle_metrics
-from twisted.internet import reactor, utils
-class Updates(WebSocketEndpoint):
+class Updates(WebSocketEndpoint, UiListener):
def __init__(self, listeners, scope: Scope, receive: Receive, send: Send) -> None:
super().__init__(scope, receive, send)
@@ -40,39 +40,42 @@ class Updates(WebSocketEndpoint):
async def on_connect(self, websocket: WebSocket):
await websocket.accept()
log.info('socket connect %s', self.scope['client'])
- self.listeners.addClient(websocket)
+ self.websocket = websocket
+ self.listeners.addClient(self)
+
+ async def sendMessage(self, msgText):
+ await self.websocket.send_text(msgText)
# async def on_receive(self, websocket, data):
# json.loads(data)
async def on_disconnect(self, websocket: WebSocket, close_code: int):
- self.listeners.delClient(websocket)
+ self.listeners.delClient(self)
pass
-class stats:
- setAttr = Summary('set_attr', 'setAttr calls')
+STAT_SETATTR = Summary('set_attr', 'setAttr calls')
-async def PutAttrs(request):
- with stats.setAttr.time():
- client, clientSession, settings, sendTime = parseJsonMessage(await request.body())
- print(f'collector.setAttrs({client=}, {clientSession=}, {settings=}, {sendTime=}')
- return Response('', status_code=202)
+async def PutAttrs(collector: Collector, request):
+ client, clientSession, settings, sendTime = parseJsonMessage(await request.body())
+ collector.setAttrs(client, clientSession, settings, sendTime)
+ return Response('', status_code=202)
def main():
- verbose = os.environ.get('VERBOSE', False)
- logdmx = os.environ.get('LOGDMX', False) # log all dmx sends
+ # verbose = os.environ.get('VERBOSE', False)
+ # logdmx = os.environ.get('LOGDMX', False) # log all dmx sends
- log.setLevel(logging.DEBUG if verbose else logging.INFO)
- logging.getLogger('output').setLevel(logging.DEBUG)
+ log.setLevel(logging.DEBUG)
+ # logging.getLogger('output').setLevel(logging.DEBUG)
- logging.getLogger('output.allDmx').setLevel(logging.DEBUG if logdmx else logging.INFO)
- logging.getLogger('colormath').setLevel(logging.INFO)
+ # logging.getLogger('output.allDmx').setLevel(logging.DEBUG if logdmx else logging.INFO)
+ # logging.getLogger('colormath').setLevel(logging.INFO)
graph = SyncedGraph(networking.rdfdb.url, "collector")
+
try:
# todo: drive outputs with config files
rate = 30
@@ -90,36 +93,34 @@ def main():
listeners = WebListeners()
c = Collector(graph, outputs, listeners)
- graph.initiallySynced.addCallback(lambda _: launch(graph, loadtest)).addErrback(lambda e: reactor.crash())
-
app = Starlette(
debug=True,
routes=[
# Route('/recentRequests', lambda req: get_recentRequests(req, db)),
WebSocketRoute('/updates', endpoint=functools.partial(Updates, listeners)),
- Route('/attrs', PutAttrs, methods=['PUT']),
+ Route('/attrs', functools.partial(PutAttrs, c), methods=['PUT']),
],
)
app.add_middleware(PrometheusMiddleware)
app.add_route("/metrics", handle_metrics)
- loadtest = os.environ.get('LOADTEST', False) # call myself with some synthetic load then exit
- if loadtest:
- # in a subprocess since we don't want this client to be
- # cooperating with the main event loop and only sending
- # requests when there's free time
- def afterWarmup():
- log.info('running collector_loadtest')
- d = utils.getProcessValue('bin/python', ['bin/collector_loadtest.py'])
+ # loadtest = os.environ.get('LOADTEST', False) # call myself with some synthetic load then exit
+ # if loadtest:
+ # # in a subprocess since we don't want this client to be
+ # # cooperating with the main event loop and only sending
+ # # requests when there's free time
+ # def afterWarmup():
+ # log.info('running collector_loadtest')
+ # d = utils.getProcessValue('bin/python', ['bin/collector_loadtest.py'])
- def done(*a):
- log.info('loadtest done')
- reactor.stop()
+ # def done(*a):
+ # log.info('loadtest done')
+ # reactor.stop()
- d.addCallback(done)
+ # d.addCallback(done)
- reactor.callLater(2, afterWarmup)
+ # reactor.callLater(2, afterWarmup)
return app
diff --git a/light9/collector/web/Light9CollectorUi.ts b/light9/collector/web/Light9CollectorUi.ts
--- a/light9/collector/web/Light9CollectorUi.ts
+++ b/light9/collector/web/Light9CollectorUi.ts
@@ -1,7 +1,8 @@
-import { sortBy, uniq } from "underscore";
import debug from "debug";
import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators.js";
+import ReconnectingWebSocket from "reconnectingwebsocket";
+import { sortBy, uniq } from "underscore";
debug.enable('*');
const log = debug("collector");
@@ -27,32 +28,32 @@ export class Light9CollectorUi extends L
return html`
-
+
Devices
-
+ ${this.devices.map((d)=>html``)}
`;
}
@property() graph: Object = {};
- @property() updates: Object = {};
+ @property() updates: Updates;
@property() devices: Array = [];
// observers: [
// 'onGraph(graph)',
// ],
- ready() {
+ constructor() {
+ super();
this.updates = new Updates();
- reconnectingWebSocket(
- "updates",
- function (msg) {
- this.updates.onMessage(msg);
- }.bind(this)
- );
+ const ws = new ReconnectingWebSocket(location.href.replace("http", "ws") + "api/updates");
+ ws.addEventListener("message", (ev: any) => {
+ log("ws msg", ev);
+ this.updates.onMessage(ev.data);
+ });
}
onGraph(graph) {
diff --git a/light9/collector/weblisteners.py b/light9/collector/weblisteners.py
--- a/light9/collector/weblisteners.py
+++ b/light9/collector/weblisteners.py
@@ -1,46 +1,53 @@
-import logging, traceback, time, json
-from typing import List, Tuple, Any, Dict
+import asyncio
+import json
+import logging
+import time
+from typing import Any, Awaitable, Dict, List, Protocol, Tuple
-import cyclone.websocket
-from rdflib import URIRef
-
-from light9.newtypes import DeviceUri, DmxIndex, DmxMessageIndex, OutputAttr, OutputValue
from light9.collector.output import Output as OutputInstance
+from light9.newtypes import (DeviceUri, DmxIndex, DmxMessageIndex, OutputAttr, OutputValue)
log = logging.getLogger('weblisteners')
+class UiListener(Protocol):
+
+ async def sendMessage(self, msg):
+ ...
+
+
class WebListeners(object):
def __init__(self) -> None:
- self.clients: List[Tuple[Any, Dict[URIRef, Dict[URIRef, Any]]]] = []
- self.pendingMessageForDev: Dict[DeviceUri, Tuple[
- Dict[OutputAttr, OutputValue],
- Dict[Tuple[DeviceUri, OutputAttr],
- Tuple[OutputInstance, DmxMessageIndex]]]] = {}
+ self.clients: List[Tuple[UiListener, Dict[DeviceUri, Dict[OutputAttr, OutputValue]]]] = []
+ self.pendingMessageForDev: Dict[DeviceUri, Tuple[Dict[OutputAttr, OutputValue], Dict[Tuple[DeviceUri, OutputAttr], Tuple[OutputInstance,
+ DmxMessageIndex]]]] = {}
self.lastFlush = 0
+ asyncio.create_task(self.flusher())
- def addClient(self, client: cyclone.websocket.WebSocketHandler):
+ def addClient(self, client: UiListener):
self.clients.append((client, {})) # seen = {dev: attrs}
log.info('added client %s %s', len(self.clients), client)
+ # todo: it would be nice to immediately fill in the client on the
+ # latest settings, but I lost them so I can't.
- def delClient(self, client: cyclone.websocket.WebSocketHandler):
+ def delClient(self, client: UiListener):
self.clients = [(c, t) for c, t in self.clients if c != client]
log.info('delClient %s, %s left', client, len(self.clients))
- def outputAttrsSet(self, dev: DeviceUri, attrs: Dict[OutputAttr, Any],
- outputMap: Dict[Tuple[DeviceUri, OutputAttr],
- Tuple[OutputInstance, DmxMessageIndex]]):
+ def outputAttrsSet(self, dev: DeviceUri, attrs: Dict[OutputAttr, Any], outputMap: Dict[Tuple[DeviceUri, OutputAttr], Tuple[OutputInstance,
+ DmxMessageIndex]]):
"""called often- don't be slow"""
-
self.pendingMessageForDev[dev] = (attrs, outputMap)
- try:
- self._flush()
- except Exception:
- traceback.print_exc()
- raise
+ # maybe put on a stack for flusher or something
- def _flush(self):
+ async def flusher(self):
+ await asyncio.sleep(3) # help startup faster?
+ while True:
+ await self._flush()
+ await asyncio.sleep(.05)
+
+ async def _flush(self):
now = time.time()
if now < self.lastFlush + .05 or not self.clients:
return
@@ -51,6 +58,8 @@ class WebListeners(object):
msg = None # lazy, since makeMsg is slow
+ sendAwaits: List[Awaitable[None]] = []
+
# this omits repeats, but can still send many
# messages/sec. Not sure if piling up messages for the browser
# could lead to slowdowns in the real dmx output.
@@ -61,27 +70,18 @@ class WebListeners(object):
msg = self.makeMsg(dev, attrs, outputMap)
seen[dev] = attrs
- client.sendMessage(msg)
+ sendAwaits.append(client.sendMessage(msg))
+ await asyncio.gather(*sendAwaits)
- def makeMsg(self, dev: DeviceUri, attrs: Dict[OutputAttr, Any],
- outputMap: Dict[Tuple[DeviceUri, OutputAttr],
- Tuple[OutputInstance, DmxMessageIndex]]):
+ def makeMsg(self, dev: DeviceUri, attrs: Dict[OutputAttr, Any], outputMap: Dict[Tuple[DeviceUri, OutputAttr], Tuple[OutputInstance, DmxMessageIndex]]):
attrRows = []
for attr, val in attrs.items():
output, bufIndex = outputMap[(dev, attr)]
dmxIndex = DmxIndex(bufIndex + 1)
- attrRows.append({
- 'attr': attr.rsplit('/')[-1],
- 'val': val,
- 'chan': (output.shortId(), dmxIndex)
- })
+ attrRows.append({'attr': attr.rsplit('/')[-1], 'val': val, 'chan': (output.shortId(), dmxIndex)})
attrRows.sort(key=lambda r: r['chan'])
for row in attrRows:
row['chan'] = '%s %s' % (row['chan'][0], row['chan'][1])
- msg = json.dumps({'outputAttrsSet': {
- 'dev': dev,
- 'attrs': attrRows
- }},
- sort_keys=True)
+ msg = json.dumps({'outputAttrsSet': {'dev': dev, 'attrs': attrRows}}, sort_keys=True)
return msg
diff --git a/light9/namespaces.py b/light9/namespaces.py
--- a/light9/namespaces.py
+++ b/light9/namespaces.py
@@ -1,4 +1,4 @@
-from rdflib import Namespace, RDF, RDFS # noqa
+from rdflib import URIRef, Namespace, RDF, RDFS # noqa
from typing import Dict
@@ -7,9 +7,9 @@ class FastNs(object):
def __init__(self, base):
self.ns = Namespace(base)
- self.cache: Dict[str, Namespace] = {}
+ self.cache: Dict[str, URIRef] = {}
- def __getitem__(self, term):
+ def __getitem__(self, term) -> URIRef:
if term not in self.cache:
self.cache[term] = self.ns[term]
return self.cache[term]
diff --git a/pdm.lock b/pdm.lock
--- a/pdm.lock
+++ b/pdm.lock
@@ -1,3 +1,27 @@
+[[package]]
+name = "aiohttp"
+version = "3.8.1"
+requires_python = ">=3.6"
+summary = "Async http client/server framework (asyncio)"
+dependencies = [
+ "aiosignal>=1.1.2",
+ "async-timeout<5.0,>=4.0.0a3",
+ "attrs>=17.3.0",
+ "charset-normalizer<3.0,>=2.0",
+ "frozenlist>=1.1.1",
+ "multidict<7.0,>=4.5",
+ "yarl<2.0,>=1.0",
+]
+
+[[package]]
+name = "aiosignal"
+version = "1.2.0"
+requires_python = ">=3.6"
+summary = "aiosignal: a list of registered asynchronous callbacks"
+dependencies = [
+ "frozenlist>=1.1.0",
+]
+
[[package]]
name = "anyio"
version = "3.5.0"
@@ -28,6 +52,12 @@ dependencies = [
]
[[package]]
+name = "async-timeout"
+version = "4.0.2"
+requires_python = ">=3.6"
+summary = "Timeout context manager for asyncio programs"
+
+[[package]]
name = "asyncinotify"
version = "2.0.3"
summary = "A simple optionally-async python inotify library, focused on simplicity of use and operation, and leveraging modern Python features"
@@ -208,6 +238,12 @@ dependencies = [
]
[[package]]
+name = "frozenlist"
+version = "1.3.0"
+requires_python = ">=3.7"
+summary = "A list-like structure which implements collections.abc.MutableSequence"
+
+[[package]]
name = "genshi"
version = "0.7.6"
summary = "A toolkit for generation of output for the web"
@@ -413,6 +449,12 @@ dependencies = [
]
[[package]]
+name = "multidict"
+version = "6.0.2"
+requires_python = ">=3.7"
+summary = "multidict implementation"
+
+[[package]]
name = "mypy"
version = "0.942"
requires_python = ">=3.6"
@@ -1060,6 +1102,16 @@ version = "0.32.0"
summary = "A formatter for Python code."
[[package]]
+name = "yarl"
+version = "1.7.2"
+requires_python = ">=3.6"
+summary = "Yet another URL library"
+dependencies = [
+ "idna>=2.0",
+ "multidict>=4.0",
+]
+
+[[package]]
name = "zope.interface"
version = "5.4.0"
requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
@@ -1070,9 +1122,87 @@ dependencies = [
[metadata]
lock_version = "3.1"
-content_hash = "sha256:c728b42b81783d65e91a0baaf2316d34815161d5754370bd81280aaee2660140"
+content_hash = "sha256:65302d7bb6965b25e3d4e0438342f08df203ee8fc294b22444bb82f617a043e8"
[metadata.files]
+"aiohttp 3.8.1" = [
+ {file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1ed0b6477896559f17b9eaeb6d38e07f7f9ffe40b9f0f9627ae8b9926ae260a8"},
+ {file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7dadf3c307b31e0e61689cbf9e06be7a867c563d5a63ce9dca578f956609abf8"},
+ {file = "aiohttp-3.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a79004bb58748f31ae1cbe9fa891054baaa46fb106c2dc7af9f8e3304dc30316"},
+ {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12de6add4038df8f72fac606dff775791a60f113a725c960f2bab01d8b8e6b15"},
+ {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f0d5f33feb5f69ddd57a4a4bd3d56c719a141080b445cbf18f238973c5c9923"},
+ {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eaba923151d9deea315be1f3e2b31cc39a6d1d2f682f942905951f4e40200922"},
+ {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:099ebd2c37ac74cce10a3527d2b49af80243e2a4fa39e7bce41617fbc35fa3c1"},
+ {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2e5d962cf7e1d426aa0e528a7e198658cdc8aa4fe87f781d039ad75dcd52c516"},
+ {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fa0ffcace9b3aa34d205d8130f7873fcfefcb6a4dd3dd705b0dab69af6712642"},
+ {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61bfc23df345d8c9716d03717c2ed5e27374e0fe6f659ea64edcd27b4b044cf7"},
+ {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:31560d268ff62143e92423ef183680b9829b1b482c011713ae941997921eebc8"},
+ {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:01d7bdb774a9acc838e6b8f1d114f45303841b89b95984cbb7d80ea41172a9e3"},
+ {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97ef77eb6b044134c0b3a96e16abcb05ecce892965a2124c566af0fd60f717e2"},
+ {file = "aiohttp-3.8.1-cp310-cp310-win32.whl", hash = "sha256:c2aef4703f1f2ddc6df17519885dbfa3514929149d3ff900b73f45998f2532fa"},
+ {file = "aiohttp-3.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:713ac174a629d39b7c6a3aa757b337599798da4c1157114a314e4e391cd28e32"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:473d93d4450880fe278696549f2e7aed8cd23708c3c1997981464475f32137db"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b5eeae8e019e7aad8af8bb314fb908dd2e028b3cdaad87ec05095394cce632"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af642b43ce56c24d063325dd2cf20ee012d2b9ba4c3c008755a301aaea720ad"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3630c3ef435c0a7c549ba170a0633a56e92629aeed0e707fec832dee313fb7a"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4a4a4e30bf1edcad13fb0804300557aedd07a92cabc74382fdd0ba6ca2661091"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6f8b01295e26c68b3a1b90efb7a89029110d3a4139270b24fda961893216c440"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a25fa703a527158aaf10dafd956f7d42ac6d30ec80e9a70846253dd13e2f067b"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5bfde62d1d2641a1f5173b8c8c2d96ceb4854f54a44c23102e2ccc7e02f003ec"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:51467000f3647d519272392f484126aa716f747859794ac9924a7aafa86cd411"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:03a6d5349c9ee8f79ab3ff3694d6ce1cfc3ced1c9d36200cb8f08ba06bd3b782"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:102e487eeb82afac440581e5d7f8f44560b36cf0bdd11abc51a46c1cd88914d4"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-win32.whl", hash = "sha256:4aed991a28ea3ce320dc8ce655875e1e00a11bdd29fe9444dd4f88c30d558602"},
+ {file = "aiohttp-3.8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b0e20cddbd676ab8a64c774fefa0ad787cc506afd844de95da56060348021e96"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:37951ad2f4a6df6506750a23f7cbabad24c73c65f23f72e95897bb2cecbae676"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c23b1ad869653bc818e972b7a3a79852d0e494e9ab7e1a701a3decc49c20d51"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15b09b06dae900777833fe7fc4b4aa426556ce95847a3e8d7548e2d19e34edb8"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:477c3ea0ba410b2b56b7efb072c36fa91b1e6fc331761798fa3f28bb224830dd"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2f2f69dca064926e79997f45b2f34e202b320fd3782f17a91941f7eb85502ee2"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ef9612483cb35171d51d9173647eed5d0069eaa2ee812793a75373447d487aa4"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6d69f36d445c45cda7b3b26afef2fc34ef5ac0cdc75584a87ef307ee3c8c6d00"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:55c3d1072704d27401c92339144d199d9de7b52627f724a949fc7d5fc56d8b93"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b9d00268fcb9f66fbcc7cd9fe423741d90c75ee029a1d15c09b22d23253c0a44"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:07b05cd3305e8a73112103c834e91cd27ce5b4bd07850c4b4dbd1877d3f45be7"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c34dc4958b232ef6188c4318cb7b2c2d80521c9a56c52449f8f93ab7bc2a8a1c"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-win32.whl", hash = "sha256:d2f9b69293c33aaa53d923032fe227feac867f81682f002ce33ffae978f0a9a9"},
+ {file = "aiohttp-3.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6ae828d3a003f03ae31915c31fa684b9890ea44c9c989056fea96e3d12a9fa17"},
+ {file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0c7ebbbde809ff4e970824b2b6cb7e4222be6b95a296e46c03cf050878fc1785"},
+ {file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b7ef7cbd4fec9a1e811a5de813311ed4f7ac7d93e0fda233c9b3e1428f7dd7b"},
+ {file = "aiohttp-3.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c3d6a4d0619e09dcd61021debf7059955c2004fa29f48788a3dfaf9c9901a7cd"},
+ {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:718626a174e7e467f0558954f94af117b7d4695d48eb980146016afa4b580b2e"},
+ {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:589c72667a5febd36f1315aa6e5f56dd4aa4862df295cb51c769d16142ddd7cd"},
+ {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ed076098b171573161eb146afcb9129b5ff63308960aeca4b676d9d3c35e700"},
+ {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:086f92daf51a032d062ec5f58af5ca6a44d082c35299c96376a41cbb33034675"},
+ {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:11691cf4dc5b94236ccc609b70fec991234e7ef8d4c02dd0c9668d1e486f5abf"},
+ {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:31d1e1c0dbf19ebccbfd62eff461518dcb1e307b195e93bba60c965a4dcf1ba0"},
+ {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:11a67c0d562e07067c4e86bffc1553f2cf5b664d6111c894671b2b8712f3aba5"},
+ {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:bb01ba6b0d3f6c68b89fce7305080145d4877ad3acaed424bae4d4ee75faa950"},
+ {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:44db35a9e15d6fe5c40d74952e803b1d96e964f683b5a78c3cc64eb177878155"},
+ {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:844a9b460871ee0a0b0b68a64890dae9c415e513db0f4a7e3cab41a0f2fedf33"},
+ {file = "aiohttp-3.8.1-cp38-cp38-win32.whl", hash = "sha256:7d08744e9bae2ca9c382581f7dce1273fe3c9bae94ff572c3626e8da5b193c6a"},
+ {file = "aiohttp-3.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:04d48b8ce6ab3cf2097b1855e1505181bdd05586ca275f2505514a6e274e8e75"},
+ {file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f5315a2eb0239185af1bddb1abf472d877fede3cc8d143c6cddad37678293237"},
+ {file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a996d01ca39b8dfe77440f3cd600825d05841088fd6bc0144cc6c2ec14cc5f74"},
+ {file = "aiohttp-3.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:13487abd2f761d4be7c8ff9080de2671e53fff69711d46de703c310c4c9317ca"},
+ {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea302f34477fda3f85560a06d9ebdc7fa41e82420e892fc50b577e35fc6a50b2"},
+ {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2f635ce61a89c5732537a7896b6319a8fcfa23ba09bec36e1b1ac0ab31270d2"},
+ {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e999f2d0e12eea01caeecb17b653f3713d758f6dcc770417cf29ef08d3931421"},
+ {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0770e2806a30e744b4e21c9d73b7bee18a1cfa3c47991ee2e5a65b887c49d5cf"},
+ {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d15367ce87c8e9e09b0f989bfd72dc641bcd04ba091c68cd305312d00962addd"},
+ {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6c7cefb4b0640703eb1069835c02486669312bf2f12b48a748e0a7756d0de33d"},
+ {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:71927042ed6365a09a98a6377501af5c9f0a4d38083652bcd2281a06a5976724"},
+ {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:28d490af82bc6b7ce53ff31337a18a10498303fe66f701ab65ef27e143c3b0ef"},
+ {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:b6613280ccedf24354406caf785db748bebbddcf31408b20c0b48cb86af76866"},
+ {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81e3d8c34c623ca4e36c46524a3530e99c0bc95ed068fd6e9b55cb721d408fb2"},
+ {file = "aiohttp-3.8.1-cp39-cp39-win32.whl", hash = "sha256:7187a76598bdb895af0adbd2fb7474d7f6025d170bc0a1130242da817ce9e7d1"},
+ {file = "aiohttp-3.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:1c182cb873bc91b411e184dab7a2b664d4fea2743df0e4d57402f7f3fa644bac"},
+ {file = "aiohttp-3.8.1.tar.gz", hash = "sha256:fc5471e1a54de15ef71c1bc6ebe80d4dc681ea600e68bfd1cbce40427f0b7578"},
+]
+"aiosignal 1.2.0" = [
+ {file = "aiosignal-1.2.0-py3-none-any.whl", hash = "sha256:26e62109036cd181df6e6ad646f91f0dcfd05fe16d0cb924138ff2ab75d64e3a"},
+ {file = "aiosignal-1.2.0.tar.gz", hash = "sha256:78ed67db6c7b7ced4f98e495e572106d5c432a93e1ddd1bf475e1dc05f5b7df2"},
+]
"anyio 3.5.0" = [
{file = "anyio-3.5.0-py3-none-any.whl", hash = "sha256:b5fa16c5ff93fa1046f2eeb5bbff2dad4d3514d6cda61d02816dba34fa8c3c2e"},
{file = "anyio-3.5.0.tar.gz", hash = "sha256:a0aeffe2fb1fdf374a8e4b471444f0f3ac4fb9f5a5b542b48824475e0042a5a6"},
@@ -1089,6 +1219,10 @@ content_hash = "sha256:c728b42b81783d65e
{file = "asttokens-2.0.5-py2.py3-none-any.whl", hash = "sha256:0844691e88552595a6f4a4281a9f7f79b8dd45ca4ccea82e5e05b4bbdb76705c"},
{file = "asttokens-2.0.5.tar.gz", hash = "sha256:9a54c114f02c7a9480d56550932546a3f1fe71d8a02f1bc7ccd0ee3ee35cf4d5"},
]
+"async-timeout 4.0.2" = [
+ {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
+ {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
+]
"asyncinotify 2.0.3" = [
{file = "asyncinotify-2.0.3-py3-none-any.whl", hash = "sha256:51532a1709327c35f5eeeb6d00e345e641a48364984af4aea3784adb61ea2ed7"},
{file = "asyncinotify-2.0.3.tar.gz", hash = "sha256:a14baf680a3d3e1cf54e082ab56f56c475d59d3644cfc25c00c460e56d9bbdf7"},
@@ -1284,6 +1418,67 @@ content_hash = "sha256:c728b42b81783d65e
{file = "freezegun-1.2.1-py3-none-any.whl", hash = "sha256:15103a67dfa868ad809a8f508146e396be2995172d25f927e48ce51c0bf5cb09"},
{file = "freezegun-1.2.1.tar.gz", hash = "sha256:b4c64efb275e6bc68dc6e771b17ffe0ff0f90b81a2a5189043550b6519926ba4"},
]
+"frozenlist 1.3.0" = [
+ {file = "frozenlist-1.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2257aaba9660f78c7b1d8fea963b68f3feffb1a9d5d05a18401ca9eb3e8d0a3"},
+ {file = "frozenlist-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4a44ebbf601d7bac77976d429e9bdb5a4614f9f4027777f9e54fd765196e9d3b"},
+ {file = "frozenlist-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:45334234ec30fc4ea677f43171b18a27505bfb2dba9aca4398a62692c0ea8868"},
+ {file = "frozenlist-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47be22dc27ed933d55ee55845d34a3e4e9f6fee93039e7f8ebadb0c2f60d403f"},
+ {file = "frozenlist-1.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03a7dd1bfce30216a3f51a84e6dd0e4a573d23ca50f0346634916ff105ba6e6b"},
+ {file = "frozenlist-1.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:691ddf6dc50480ce49f68441f1d16a4c3325887453837036e0fb94736eae1e58"},
+ {file = "frozenlist-1.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bde99812f237f79eaf3f04ebffd74f6718bbd216101b35ac7955c2d47c17da02"},
+ {file = "frozenlist-1.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a202458d1298ced3768f5a7d44301e7c86defac162ace0ab7434c2e961166e8"},
+ {file = "frozenlist-1.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9e3e9e365991f8cc5f5edc1fd65b58b41d0514a6a7ad95ef5c7f34eb49b3d3e"},
+ {file = "frozenlist-1.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:04cb491c4b1c051734d41ea2552fde292f5f3a9c911363f74f39c23659c4af78"},
+ {file = "frozenlist-1.3.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:436496321dad302b8b27ca955364a439ed1f0999311c393dccb243e451ff66aa"},
+ {file = "frozenlist-1.3.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:754728d65f1acc61e0f4df784456106e35afb7bf39cfe37227ab00436fb38676"},
+ {file = "frozenlist-1.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6eb275c6385dd72594758cbe96c07cdb9bd6becf84235f4a594bdf21e3596c9d"},
+ {file = "frozenlist-1.3.0-cp310-cp310-win32.whl", hash = "sha256:e30b2f9683812eb30cf3f0a8e9f79f8d590a7999f731cf39f9105a7c4a39489d"},
+ {file = "frozenlist-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f7353ba3367473d1d616ee727945f439e027f0bb16ac1a750219a8344d1d5d3c"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88aafd445a233dbbf8a65a62bc3249a0acd0d81ab18f6feb461cc5a938610d24"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4406cfabef8f07b3b3af0f50f70938ec06d9f0fc26cbdeaab431cbc3ca3caeaa"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8cf829bd2e2956066dd4de43fd8ec881d87842a06708c035b37ef632930505a2"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:603b9091bd70fae7be28bdb8aa5c9990f4241aa33abb673390a7f7329296695f"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25af28b560e0c76fa41f550eacb389905633e7ac02d6eb3c09017fa1c8cdfde1"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94c7a8a9fc9383b52c410a2ec952521906d355d18fccc927fca52ab575ee8b93"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:65bc6e2fece04e2145ab6e3c47428d1bbc05aede61ae365b2c1bddd94906e478"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3f7c935c7b58b0d78c0beea0c7358e165f95f1fd8a7e98baa40d22a05b4a8141"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd89acd1b8bb4f31b47072615d72e7f53a948d302b7c1d1455e42622de180eae"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:6983a31698490825171be44ffbafeaa930ddf590d3f051e397143a5045513b01"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:adac9700675cf99e3615eb6a0eb5e9f5a4143c7d42c05cea2e7f71c27a3d0846"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-win32.whl", hash = "sha256:0c36e78b9509e97042ef869c0e1e6ef6429e55817c12d78245eb915e1cca7468"},
+ {file = "frozenlist-1.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:57f4d3f03a18facacb2a6bcd21bccd011e3b75d463dc49f838fd699d074fabd1"},
+ {file = "frozenlist-1.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8c905a5186d77111f02144fab5b849ab524f1e876a1e75205cd1386a9be4b00a"},
+ {file = "frozenlist-1.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b5009062d78a8c6890d50b4e53b0ddda31841b3935c1937e2ed8c1bda1c7fb9d"},
+ {file = "frozenlist-1.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2fdc3cd845e5a1f71a0c3518528bfdbfe2efaf9886d6f49eacc5ee4fd9a10953"},
+ {file = "frozenlist-1.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92e650bd09b5dda929523b9f8e7f99b24deac61240ecc1a32aeba487afcd970f"},
+ {file = "frozenlist-1.3.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:40dff8962b8eba91fd3848d857203f0bd704b5f1fa2b3fc9af64901a190bba08"},
+ {file = "frozenlist-1.3.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:768efd082074bb203c934e83a61654ed4931ef02412c2fbdecea0cff7ecd0274"},
+ {file = "frozenlist-1.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:006d3595e7d4108a12025ddf415ae0f6c9e736e726a5db0183326fd191b14c5e"},
+ {file = "frozenlist-1.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:871d42623ae15eb0b0e9df65baeee6976b2e161d0ba93155411d58ff27483ad8"},
+ {file = "frozenlist-1.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aff388be97ef2677ae185e72dc500d19ecaf31b698986800d3fc4f399a5e30a5"},
+ {file = "frozenlist-1.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9f892d6a94ec5c7b785e548e42722e6f3a52f5f32a8461e82ac3e67a3bd073f1"},
+ {file = "frozenlist-1.3.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:e982878792c971cbd60ee510c4ee5bf089a8246226dea1f2138aa0bb67aff148"},
+ {file = "frozenlist-1.3.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c6c321dd013e8fc20735b92cb4892c115f5cdb82c817b1e5b07f6b95d952b2f0"},
+ {file = "frozenlist-1.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:30530930410855c451bea83f7b272fb1c495ed9d5cc72895ac29e91279401db3"},
+ {file = "frozenlist-1.3.0-cp38-cp38-win32.whl", hash = "sha256:40ec383bc194accba825fbb7d0ef3dda5736ceab2375462f1d8672d9f6b68d07"},
+ {file = "frozenlist-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:f20baa05eaa2bcd5404c445ec51aed1c268d62600362dc6cfe04fae34a424bd9"},
+ {file = "frozenlist-1.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0437fe763fb5d4adad1756050cbf855bbb2bf0d9385c7bb13d7a10b0dd550486"},
+ {file = "frozenlist-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b684c68077b84522b5c7eafc1dc735bfa5b341fb011d5552ebe0968e22ed641c"},
+ {file = "frozenlist-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:93641a51f89473837333b2f8100f3f89795295b858cd4c7d4a1f18e299dc0a4f"},
+ {file = "frozenlist-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6d32ff213aef0fd0bcf803bffe15cfa2d4fde237d1d4838e62aec242a8362fa"},
+ {file = "frozenlist-1.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31977f84828b5bb856ca1eb07bf7e3a34f33a5cddce981d880240ba06639b94d"},
+ {file = "frozenlist-1.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3c62964192a1c0c30b49f403495911298810bada64e4f03249ca35a33ca0417a"},
+ {file = "frozenlist-1.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4eda49bea3602812518765810af732229b4291d2695ed24a0a20e098c45a707b"},
+ {file = "frozenlist-1.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acb267b09a509c1df5a4ca04140da96016f40d2ed183cdc356d237286c971b51"},
+ {file = "frozenlist-1.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e1e26ac0a253a2907d654a37e390904426d5ae5483150ce3adedb35c8c06614a"},
+ {file = "frozenlist-1.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f96293d6f982c58ebebb428c50163d010c2f05de0cde99fd681bfdc18d4b2dc2"},
+ {file = "frozenlist-1.3.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e84cb61b0ac40a0c3e0e8b79c575161c5300d1d89e13c0e02f76193982f066ed"},
+ {file = "frozenlist-1.3.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:ff9310f05b9d9c5c4dd472983dc956901ee6cb2c3ec1ab116ecdde25f3ce4951"},
+ {file = "frozenlist-1.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d26b650b71fdc88065b7a21f8ace70175bcf3b5bdba5ea22df4bfd893e795a3b"},
+ {file = "frozenlist-1.3.0-cp39-cp39-win32.whl", hash = "sha256:01a73627448b1f2145bddb6e6c2259988bb8aee0fb361776ff8604b99616cd08"},
+ {file = "frozenlist-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:772965f773757a6026dea111a15e6e2678fbd6216180f82a48a40b27de1ee2ab"},
+ {file = "frozenlist-1.3.0.tar.gz", hash = "sha256:ce6f2ba0edb7b0c1d8976565298ad2deba6f8064d2bebb6ffce2ca896eb35b0b"},
+]
"genshi 0.7.6" = [
{file = "Genshi-0.7.6-py3-none-any.whl", hash = "sha256:f2374cf48b298f5c5d154adc5940023c1bc3f07934339b81330e0ee22db92956"},
{file = "Genshi-0.7.6.tar.gz", hash = "sha256:34a2ce8b80e843f620c5b7b7e59aaa362a76ce9764a6f11032283ed9458c3a59"},
@@ -1448,6 +1643,67 @@ content_hash = "sha256:c728b42b81783d65e
"moviepy 1.0.3" = [
{file = "moviepy-1.0.3.tar.gz", hash = "sha256:2884e35d1788077db3ff89e763c5ba7bfddbd7ae9108c9bc809e7ba58fa433f5"},
]
+"multidict 6.0.2" = [
+ {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"},
+ {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"},
+ {file = "multidict-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:041b81a5f6b38244b34dc18c7b6aba91f9cdaf854d9a39e5ff0b58e2b5773b9c"},
+ {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fdda29a3c7e76a064f2477c9aab1ba96fd94e02e386f1e665bca1807fc5386f"},
+ {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3368bf2398b0e0fcbf46d85795adc4c259299fec50c1416d0f77c0a843a3eed9"},
+ {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4f052ee022928d34fe1f4d2bc743f32609fb79ed9c49a1710a5ad6b2198db20"},
+ {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:225383a6603c086e6cef0f2f05564acb4f4d5f019a4e3e983f572b8530f70c88"},
+ {file = "multidict-6.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50bd442726e288e884f7be9071016c15a8742eb689a593a0cac49ea093eef0a7"},
+ {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:47e6a7e923e9cada7c139531feac59448f1f47727a79076c0b1ee80274cd8eee"},
+ {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0556a1d4ea2d949efe5fd76a09b4a82e3a4a30700553a6725535098d8d9fb672"},
+ {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:626fe10ac87851f4cffecee161fc6f8f9853f0f6f1035b59337a51d29ff3b4f9"},
+ {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8064b7c6f0af936a741ea1efd18690bacfbae4078c0c385d7c3f611d11f0cf87"},
+ {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2d36e929d7f6a16d4eb11b250719c39560dd70545356365b494249e2186bc389"},
+ {file = "multidict-6.0.2-cp310-cp310-win32.whl", hash = "sha256:fcb91630817aa8b9bc4a74023e4198480587269c272c58b3279875ed7235c293"},
+ {file = "multidict-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:8cbf0132f3de7cc6c6ce00147cc78e6439ea736cee6bca4f068bcf892b0fd658"},
+ {file = "multidict-6.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:05f6949d6169878a03e607a21e3b862eaf8e356590e8bdae4227eedadacf6e51"},
+ {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2c2e459f7050aeb7c1b1276763364884595d47000c1cddb51764c0d8976e608"},
+ {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0509e469d48940147e1235d994cd849a8f8195e0bca65f8f5439c56e17872a3"},
+ {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:514fe2b8d750d6cdb4712346a2c5084a80220821a3e91f3f71eec11cf8d28fd4"},
+ {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19adcfc2a7197cdc3987044e3f415168fc5dc1f720c932eb1ef4f71a2067e08b"},
+ {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9d153e7f1f9ba0b23ad1568b3b9e17301e23b042c23870f9ee0522dc5cc79e8"},
+ {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aef9cc3d9c7d63d924adac329c33835e0243b5052a6dfcbf7732a921c6e918ba"},
+ {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4571f1beddff25f3e925eea34268422622963cd8dc395bb8778eb28418248e43"},
+ {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d48b8ee1d4068561ce8033d2c344cf5232cb29ee1a0206a7b828c79cbc5982b8"},
+ {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:45183c96ddf61bf96d2684d9fbaf6f3564d86b34cb125761f9a0ef9e36c1d55b"},
+ {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:75bdf08716edde767b09e76829db8c1e5ca9d8bb0a8d4bd94ae1eafe3dac5e15"},
+ {file = "multidict-6.0.2-cp37-cp37m-win32.whl", hash = "sha256:a45e1135cb07086833ce969555df39149680e5471c04dfd6a915abd2fc3f6dbc"},
+ {file = "multidict-6.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6f3cdef8a247d1eafa649085812f8a310e728bdf3900ff6c434eafb2d443b23a"},
+ {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0327292e745a880459ef71be14e709aaea2f783f3537588fb4ed09b6c01bca60"},
+ {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e875b6086e325bab7e680e4316d667fc0e5e174bb5611eb16b3ea121c8951b86"},
+ {file = "multidict-6.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feea820722e69451743a3d56ad74948b68bf456984d63c1a92e8347b7b88452d"},
+ {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc57c68cb9139c7cd6fc39f211b02198e69fb90ce4bc4a094cf5fe0d20fd8b0"},
+ {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:497988d6b6ec6ed6f87030ec03280b696ca47dbf0648045e4e1d28b80346560d"},
+ {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89171b2c769e03a953d5969b2f272efa931426355b6c0cb508022976a17fd376"},
+ {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684133b1e1fe91eda8fa7447f137c9490a064c6b7f392aa857bba83a28cfb693"},
+ {file = "multidict-6.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd9fc9c4849a07f3635ccffa895d57abce554b467d611a5009ba4f39b78a8849"},
+ {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e07c8e79d6e6fd37b42f3250dba122053fddb319e84b55dd3a8d6446e1a7ee49"},
+ {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4070613ea2227da2bfb2c35a6041e4371b0af6b0be57f424fe2318b42a748516"},
+ {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:47fbeedbf94bed6547d3aa632075d804867a352d86688c04e606971595460227"},
+ {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5774d9218d77befa7b70d836004a768fb9aa4fdb53c97498f4d8d3f67bb9cfa9"},
+ {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2957489cba47c2539a8eb7ab32ff49101439ccf78eab724c828c1a54ff3ff98d"},
+ {file = "multidict-6.0.2-cp38-cp38-win32.whl", hash = "sha256:e5b20e9599ba74391ca0cfbd7b328fcc20976823ba19bc573983a25b32e92b57"},
+ {file = "multidict-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:8004dca28e15b86d1b1372515f32eb6f814bdf6f00952699bdeb541691091f96"},
+ {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2e4a0785b84fb59e43c18a015ffc575ba93f7d1dbd272b4cdad9f5134b8a006c"},
+ {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6701bf8a5d03a43375909ac91b6980aea74b0f5402fbe9428fc3f6edf5d9677e"},
+ {file = "multidict-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a007b1638e148c3cfb6bf0bdc4f82776cef0ac487191d093cdc316905e504071"},
+ {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07a017cfa00c9890011628eab2503bee5872f27144936a52eaab449be5eaf032"},
+ {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c207fff63adcdf5a485969131dc70e4b194327666b7e8a87a97fbc4fd80a53b2"},
+ {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:373ba9d1d061c76462d74e7de1c0c8e267e9791ee8cfefcf6b0b2495762c370c"},
+ {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfba7c6d5d7c9099ba21f84662b037a0ffd4a5e6b26ac07d19e423e6fdf965a9"},
+ {file = "multidict-6.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19d9bad105dfb34eb539c97b132057a4e709919ec4dd883ece5838bcbf262b80"},
+ {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:de989b195c3d636ba000ee4281cd03bb1234635b124bf4cd89eeee9ca8fcb09d"},
+ {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7c40b7bbece294ae3a87c1bc2abff0ff9beef41d14188cda94ada7bcea99b0fb"},
+ {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:d16cce709ebfadc91278a1c005e3c17dd5f71f5098bfae1035149785ea6e9c68"},
+ {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:a2c34a93e1d2aa35fbf1485e5010337c72c6791407d03aa5f4eed920343dd360"},
+ {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:feba80698173761cddd814fa22e88b0661e98cb810f9f986c54aa34d281e4937"},
+ {file = "multidict-6.0.2-cp39-cp39-win32.whl", hash = "sha256:23b616fdc3c74c9fe01d76ce0d1ce872d2d396d8fa8e4899398ad64fb5aa214a"},
+ {file = "multidict-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:4bae31803d708f6f15fd98be6a6ac0b6958fcf68fda3c77a048a4f9073704aae"},
+ {file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"},
+]
"mypy 0.942" = [
{file = "mypy-0.942-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5bf44840fb43ac4074636fd47ee476d73f0039f4f54e86d7265077dc199be24d"},
{file = "mypy-0.942-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dcd955f36e0180258a96f880348fbca54ce092b40fbb4b37372ae3b25a0b0a46"},
@@ -2054,6 +2310,80 @@ content_hash = "sha256:c728b42b81783d65e
{file = "yapf-0.32.0-py2.py3-none-any.whl", hash = "sha256:8fea849025584e486fd06d6ba2bed717f396080fd3cc236ba10cb97c4c51cf32"},
{file = "yapf-0.32.0.tar.gz", hash = "sha256:a3f5085d37ef7e3e004c4ba9f9b3e40c54ff1901cd111f05145ae313a7c67d1b"},
]
+"yarl 1.7.2" = [
+ {file = "yarl-1.7.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2a8508f7350512434e41065684076f640ecce176d262a7d54f0da41d99c5a95"},
+ {file = "yarl-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da6df107b9ccfe52d3a48165e48d72db0eca3e3029b5b8cb4fe6ee3cb870ba8b"},
+ {file = "yarl-1.7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1d0894f238763717bdcfea74558c94e3bc34aeacd3351d769460c1a586a8b05"},
+ {file = "yarl-1.7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe4b95b7e00c6635a72e2d00b478e8a28bfb122dc76349a06e20792eb53a523"},
+ {file = "yarl-1.7.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c145ab54702334c42237a6c6c4cc08703b6aa9b94e2f227ceb3d477d20c36c63"},
+ {file = "yarl-1.7.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ca56f002eaf7998b5fcf73b2421790da9d2586331805f38acd9997743114e98"},
+ {file = "yarl-1.7.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1d3d5ad8ea96bd6d643d80c7b8d5977b4e2fb1bab6c9da7322616fd26203d125"},
+ {file = "yarl-1.7.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:167ab7f64e409e9bdd99333fe8c67b5574a1f0495dcfd905bc7454e766729b9e"},
+ {file = "yarl-1.7.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:95a1873b6c0dd1c437fb3bb4a4aaa699a48c218ac7ca1e74b0bee0ab16c7d60d"},
+ {file = "yarl-1.7.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6152224d0a1eb254f97df3997d79dadd8bb2c1a02ef283dbb34b97d4f8492d23"},
+ {file = "yarl-1.7.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:5bb7d54b8f61ba6eee541fba4b83d22b8a046b4ef4d8eb7f15a7e35db2e1e245"},
+ {file = "yarl-1.7.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:9c1f083e7e71b2dd01f7cd7434a5f88c15213194df38bc29b388ccdf1492b739"},
+ {file = "yarl-1.7.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f44477ae29025d8ea87ec308539f95963ffdc31a82f42ca9deecf2d505242e72"},
+ {file = "yarl-1.7.2-cp310-cp310-win32.whl", hash = "sha256:cff3ba513db55cc6a35076f32c4cdc27032bd075c9faef31fec749e64b45d26c"},
+ {file = "yarl-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:c9c6d927e098c2d360695f2e9d38870b2e92e0919be07dbe339aefa32a090265"},
+ {file = "yarl-1.7.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9b4c77d92d56a4c5027572752aa35082e40c561eec776048330d2907aead891d"},
+ {file = "yarl-1.7.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c01a89a44bb672c38f42b49cdb0ad667b116d731b3f4c896f72302ff77d71656"},
+ {file = "yarl-1.7.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c19324a1c5399b602f3b6e7db9478e5b1adf5cf58901996fc973fe4fccd73eed"},
+ {file = "yarl-1.7.2-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3abddf0b8e41445426d29f955b24aeecc83fa1072be1be4e0d194134a7d9baee"},
+ {file = "yarl-1.7.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6a1a9fe17621af43e9b9fcea8bd088ba682c8192d744b386ee3c47b56eaabb2c"},
+ {file = "yarl-1.7.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8b0915ee85150963a9504c10de4e4729ae700af11df0dc5550e6587ed7891e92"},
+ {file = "yarl-1.7.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:29e0656d5497733dcddc21797da5a2ab990c0cb9719f1f969e58a4abac66234d"},
+ {file = "yarl-1.7.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:bf19725fec28452474d9887a128e98dd67eee7b7d52e932e6949c532d820dc3b"},
+ {file = "yarl-1.7.2-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:d6f3d62e16c10e88d2168ba2d065aa374e3c538998ed04996cd373ff2036d64c"},
+ {file = "yarl-1.7.2-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ac10bbac36cd89eac19f4e51c032ba6b412b3892b685076f4acd2de18ca990aa"},
+ {file = "yarl-1.7.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:aa32aaa97d8b2ed4e54dc65d241a0da1c627454950f7d7b1f95b13985afd6c5d"},
+ {file = "yarl-1.7.2-cp36-cp36m-win32.whl", hash = "sha256:87f6e082bce21464857ba58b569370e7b547d239ca22248be68ea5d6b51464a1"},
+ {file = "yarl-1.7.2-cp36-cp36m-win_amd64.whl", hash = "sha256:ac35ccde589ab6a1870a484ed136d49a26bcd06b6a1c6397b1967ca13ceb3913"},
+ {file = "yarl-1.7.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a467a431a0817a292121c13cbe637348b546e6ef47ca14a790aa2fa8cc93df63"},
+ {file = "yarl-1.7.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ab0c3274d0a846840bf6c27d2c60ba771a12e4d7586bf550eefc2df0b56b3b4"},
+ {file = "yarl-1.7.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d260d4dc495c05d6600264a197d9d6f7fc9347f21d2594926202fd08cf89a8ba"},
+ {file = "yarl-1.7.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fc4dd8b01a8112809e6b636b00f487846956402834a7fd59d46d4f4267181c41"},
+ {file = "yarl-1.7.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c1164a2eac148d85bbdd23e07dfcc930f2e633220f3eb3c3e2a25f6148c2819e"},
+ {file = "yarl-1.7.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:67e94028817defe5e705079b10a8438b8cb56e7115fa01640e9c0bb3edf67332"},
+ {file = "yarl-1.7.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:89ccbf58e6a0ab89d487c92a490cb5660d06c3a47ca08872859672f9c511fc52"},
+ {file = "yarl-1.7.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8cce6f9fa3df25f55521fbb5c7e4a736683148bcc0c75b21863789e5185f9185"},
+ {file = "yarl-1.7.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:211fcd65c58bf250fb994b53bc45a442ddc9f441f6fec53e65de8cba48ded986"},
+ {file = "yarl-1.7.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c10ea1e80a697cf7d80d1ed414b5cb8f1eec07d618f54637067ae3c0334133c4"},
+ {file = "yarl-1.7.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:52690eb521d690ab041c3919666bea13ab9fbff80d615ec16fa81a297131276b"},
+ {file = "yarl-1.7.2-cp37-cp37m-win32.whl", hash = "sha256:695ba021a9e04418507fa930d5f0704edbce47076bdcfeeaba1c83683e5649d1"},
+ {file = "yarl-1.7.2-cp37-cp37m-win_amd64.whl", hash = "sha256:c17965ff3706beedafd458c452bf15bac693ecd146a60a06a214614dc097a271"},
+ {file = "yarl-1.7.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fce78593346c014d0d986b7ebc80d782b7f5e19843ca798ed62f8e3ba8728576"},
+ {file = "yarl-1.7.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c2a1ac41a6aa980db03d098a5531f13985edcb451bcd9d00670b03129922cd0d"},
+ {file = "yarl-1.7.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:39d5493c5ecd75c8093fa7700a2fb5c94fe28c839c8e40144b7ab7ccba6938c8"},
+ {file = "yarl-1.7.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1eb6480ef366d75b54c68164094a6a560c247370a68c02dddb11f20c4c6d3c9d"},
+ {file = "yarl-1.7.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ba63585a89c9885f18331a55d25fe81dc2d82b71311ff8bd378fc8004202ff6"},
+ {file = "yarl-1.7.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e39378894ee6ae9f555ae2de332d513a5763276a9265f8e7cbaeb1b1ee74623a"},
+ {file = "yarl-1.7.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c0910c6b6c31359d2f6184828888c983d54d09d581a4a23547a35f1d0b9484b1"},
+ {file = "yarl-1.7.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6feca8b6bfb9eef6ee057628e71e1734caf520a907b6ec0d62839e8293e945c0"},
+ {file = "yarl-1.7.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8300401dc88cad23f5b4e4c1226f44a5aa696436a4026e456fe0e5d2f7f486e6"},
+ {file = "yarl-1.7.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:788713c2896f426a4e166b11f4ec538b5736294ebf7d5f654ae445fd44270832"},
+ {file = "yarl-1.7.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:fd547ec596d90c8676e369dd8a581a21227fe9b4ad37d0dc7feb4ccf544c2d59"},
+ {file = "yarl-1.7.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:737e401cd0c493f7e3dd4db72aca11cfe069531c9761b8ea474926936b3c57c8"},
+ {file = "yarl-1.7.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baf81561f2972fb895e7844882898bda1eef4b07b5b385bcd308d2098f1a767b"},
+ {file = "yarl-1.7.2-cp38-cp38-win32.whl", hash = "sha256:ede3b46cdb719c794427dcce9d8beb4abe8b9aa1e97526cc20de9bd6583ad1ef"},
+ {file = "yarl-1.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:cc8b7a7254c0fc3187d43d6cb54b5032d2365efd1df0cd1749c0c4df5f0ad45f"},
+ {file = "yarl-1.7.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:580c1f15500e137a8c37053e4cbf6058944d4c114701fa59944607505c2fe3a0"},
+ {file = "yarl-1.7.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ec1d9a0d7780416e657f1e405ba35ec1ba453a4f1511eb8b9fbab81cb8b3ce1"},
+ {file = "yarl-1.7.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3bf8cfe8856708ede6a73907bf0501f2dc4e104085e070a41f5d88e7faf237f3"},
+ {file = "yarl-1.7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1be4bbb3d27a4e9aa5f3df2ab61e3701ce8fcbd3e9846dbce7c033a7e8136746"},
+ {file = "yarl-1.7.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:534b047277a9a19d858cde163aba93f3e1677d5acd92f7d10ace419d478540de"},
+ {file = "yarl-1.7.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6ddcd80d79c96eb19c354d9dca95291589c5954099836b7c8d29278a7ec0bda"},
+ {file = "yarl-1.7.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9bfcd43c65fbb339dc7086b5315750efa42a34eefad0256ba114cd8ad3896f4b"},
+ {file = "yarl-1.7.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f64394bd7ceef1237cc604b5a89bf748c95982a84bcd3c4bbeb40f685c810794"},
+ {file = "yarl-1.7.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044daf3012e43d4b3538562da94a88fb12a6490652dbc29fb19adfa02cf72eac"},
+ {file = "yarl-1.7.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:368bcf400247318382cc150aaa632582d0780b28ee6053cd80268c7e72796dec"},
+ {file = "yarl-1.7.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:bab827163113177aee910adb1f48ff7af31ee0289f434f7e22d10baf624a6dfe"},
+ {file = "yarl-1.7.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0cba38120db72123db7c58322fa69e3c0efa933040ffb586c3a87c063ec7cae8"},
+ {file = "yarl-1.7.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:59218fef177296451b23214c91ea3aba7858b4ae3306dde120224cfe0f7a6ee8"},
+ {file = "yarl-1.7.2-cp39-cp39-win32.whl", hash = "sha256:1edc172dcca3f11b38a9d5c7505c83c1913c0addc99cd28e993efeaafdfaa18d"},
+ {file = "yarl-1.7.2-cp39-cp39-win_amd64.whl", hash = "sha256:797c2c412b04403d2da075fb93c123df35239cd7b4cc4e0cd9e5839b73f52c58"},
+ {file = "yarl-1.7.2.tar.gz", hash = "sha256:45399b46d60c253327a460e99856752009fcee5f5d3c80b2f7c0cae1c38d56dd"},
+]
"zope.interface 5.4.0" = [
{file = "zope.interface-5.4.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:7df1e1c05304f26faa49fa752a8c690126cf98b40b91d54e6e9cc3b7d6ffe8b7"},
{file = "zope.interface-5.4.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2c98384b254b37ce50eddd55db8d381a5c53b4c10ee66e1e7fe749824f894021"},
diff --git a/pyproject.toml b/pyproject.toml
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -42,6 +42,7 @@ dependencies = [
"prometheus-client>=0.14.1",
"starlette-exporter>=0.12.0",
"PyGObject>=3.42.1",
+ "aiohttp>=3.8.1",
"rdfdb @ file:///my/proj/rdfdb",
]
requires-python = ">=3.9"