deva 1.2.3__tar.gz → 1.2.4__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.
- {deva-1.2.3 → deva-1.2.4}/PKG-INFO +1 -1
- {deva-1.2.3 → deva-1.2.4}/deva/__init__.py +1 -1
- {deva-1.2.3 → deva-1.2.4}/deva/core.py +5 -2
- {deva-1.2.3 → deva-1.2.4}/deva/endpoints.py +4 -4
- {deva-1.2.3 → deva-1.2.4}/deva/namespace.py +13 -2
- {deva-1.2.3 → deva-1.2.4}/deva/sources.py +115 -124
- deva-1.2.4/deva/topic.py +165 -0
- {deva-1.2.3 → deva-1.2.4}/deva.egg-info/PKG-INFO +1 -1
- {deva-1.2.3 → deva-1.2.4}/setup.py +1 -1
- deva-1.2.3/deva/topic.py +0 -94
- {deva-1.2.3 → deva-1.2.4}/MANIFEST.in +0 -0
- {deva-1.2.3 → deva-1.2.4}/README.rst +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/bus.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/compute.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/dask.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/future.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/graph.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/lambdas.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/monitor.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/page.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/pipe.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/river.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/sample/.DS_Store +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/sample/bus/bus_in.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/sample/bus/bus_out.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/sample/crawler/.DS_Store +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/sample/crawler/asynchttp.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/sample/matmul.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/sample/my_future.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/sample/search/.DS_Store +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/sample/search/cpiplog.db +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/sample/search/search_sql.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/sample/search/search_stream.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/sample/webview/stream_page.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/sample/when/scheduler.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/sample/when/timer.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/sample/when/worker.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/search.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/state.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/store.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/templates/monitor.html +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/templates/stream.html +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/templates/streams.html +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/templates/xterm.css +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/templates/xterm.js +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/utils/__init__.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/utils/simhash.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/utils/sqlitedict.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/utils/whooshalchemy.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva/when.py +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva.egg-info/SOURCES.txt +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva.egg-info/dependency_links.txt +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva.egg-info/not-zip-safe +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva.egg-info/requires.txt +0 -0
- {deva-1.2.3 → deva-1.2.4}/deva.egg-info/top_level.txt +0 -0
- {deva-1.2.3 → deva-1.2.4}/setup.cfg +0 -0
|
@@ -993,14 +993,17 @@ def httpx(req, render=False, **kwargs):
|
|
|
993
993
|
if isinstance(req, str):
|
|
994
994
|
response = yield httpclient.get(req)
|
|
995
995
|
elif isinstance(req, dict):
|
|
996
|
-
|
|
996
|
+
if req.get('method') == 'post':
|
|
997
|
+
response = yield httpclient.post(**req)
|
|
998
|
+
else:
|
|
999
|
+
response = yield httpclient.get(**req)
|
|
997
1000
|
|
|
998
1001
|
if render:
|
|
999
1002
|
yield response.html.arender(**kwargs)
|
|
1000
1003
|
|
|
1001
1004
|
return response
|
|
1002
1005
|
except Exception as e:
|
|
1003
|
-
(req, e)
|
|
1006
|
+
print(req, e)
|
|
1004
1007
|
logger.exception(e)
|
|
1005
1008
|
|
|
1006
1009
|
|
|
@@ -156,10 +156,8 @@ class Dtalk(Stream):
|
|
|
156
156
|
msg = str(msg)
|
|
157
157
|
|
|
158
158
|
data = {"msgtype": "text", "text": {"content": msg},
|
|
159
|
-
"at": {"atMobiles": [], "isAtAll":
|
|
160
|
-
|
|
161
|
-
data = {"msgtype": "text", "text": {"content": msg},
|
|
162
|
-
"at": {"atMobiles": [], "isAtAll": True}}
|
|
159
|
+
"at": {"atMobiles": [], "isAtAll": '@all' in msg}}
|
|
160
|
+
|
|
163
161
|
if msg.startswith('@md@'):
|
|
164
162
|
# @md@财联社新闻汇总|text
|
|
165
163
|
content = msg[4:]
|
|
@@ -171,6 +169,8 @@ class Dtalk(Stream):
|
|
|
171
169
|
}
|
|
172
170
|
|
|
173
171
|
post_data = json.JSONEncoder().encode(data)
|
|
172
|
+
# import urllib
|
|
173
|
+
# post_data = urllib.parse.urlencode(data)
|
|
174
174
|
|
|
175
175
|
headers = {'Content-Type': 'application/json'}
|
|
176
176
|
|
|
@@ -17,13 +17,14 @@ Example usage::
|
|
|
17
17
|
|
|
18
18
|
from .core import Stream
|
|
19
19
|
from .store import DBStream, X
|
|
20
|
-
from .topic import Topic
|
|
20
|
+
from .topic import Topic, TCPTopic
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class Namespace(dict):
|
|
24
24
|
def __init__(self):
|
|
25
25
|
self['stream'] = {}
|
|
26
26
|
self['topic'] = {}
|
|
27
|
+
self['tcptopic'] = {}
|
|
27
28
|
self['table'] = {}
|
|
28
29
|
self['data'] = {}
|
|
29
30
|
self['webserver'] = {}
|
|
@@ -32,7 +33,8 @@ class Namespace(dict):
|
|
|
32
33
|
constructor = {'stream': Stream,
|
|
33
34
|
'topic': Topic,
|
|
34
35
|
'table': DBStream,
|
|
35
|
-
'data': X,
|
|
36
|
+
'data': X,
|
|
37
|
+
'tcptopic': TCPTopic}
|
|
36
38
|
|
|
37
39
|
if typ == 'webserver':
|
|
38
40
|
from .page import PageServer
|
|
@@ -75,6 +77,15 @@ def NT(name='', *args, **kwargs):
|
|
|
75
77
|
return None
|
|
76
78
|
|
|
77
79
|
|
|
80
|
+
def NWT(name='', *args, **kwargs):
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
return namespace.create(typ='tcptopic', name=name, *args, **kwargs)
|
|
84
|
+
except Exception as e:
|
|
85
|
+
print(e)
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
|
|
78
89
|
def NB(name, *args, **kwargs):
|
|
79
90
|
"""创建命名的DBStream.
|
|
80
91
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import pandas as pd
|
|
2
1
|
import subprocess
|
|
3
2
|
import json
|
|
4
3
|
from tornado.web import RequestHandler, Application
|
|
@@ -12,15 +11,14 @@ from glob import glob
|
|
|
12
11
|
import os
|
|
13
12
|
import tornado.ioloop
|
|
14
13
|
|
|
15
|
-
from .topic import RedisStream
|
|
16
14
|
from .core import Stream
|
|
17
|
-
from .namespace import NB, NS
|
|
18
15
|
|
|
19
16
|
import logging
|
|
20
17
|
import asyncio
|
|
21
18
|
|
|
22
|
-
from urllib.parse import unquote
|
|
23
19
|
|
|
20
|
+
import aioredis
|
|
21
|
+
import time
|
|
24
22
|
|
|
25
23
|
logger = logging.getLogger(__name__)
|
|
26
24
|
|
|
@@ -512,6 +510,84 @@ class from_kafka(Source):
|
|
|
512
510
|
self.stopped = True
|
|
513
511
|
|
|
514
512
|
|
|
513
|
+
@Stream.register_api(staticmethod)
|
|
514
|
+
class RedisStream(Stream):
|
|
515
|
+
"""redis stream,read and write.
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
上游进来的写入redis ,redis的读出来的压入下游,
|
|
519
|
+
exapmle::
|
|
520
|
+
|
|
521
|
+
news = Stream.RedisStream('news')
|
|
522
|
+
l = list()
|
|
523
|
+
news>>l
|
|
524
|
+
for i in range(1000):
|
|
525
|
+
i>>news
|
|
526
|
+
|
|
527
|
+
l|len
|
|
528
|
+
|
|
529
|
+
"""
|
|
530
|
+
|
|
531
|
+
def __init__(self, topic, start=True,
|
|
532
|
+
group=None, address='localhost', db=0, password=None, **kwargs):
|
|
533
|
+
self.topic = topic
|
|
534
|
+
self.redis_address = address
|
|
535
|
+
self.redis_password = password
|
|
536
|
+
self.group = group or hash(self)+hash(time.time())
|
|
537
|
+
self.consumer = hash(self)
|
|
538
|
+
|
|
539
|
+
super(RedisStream, self).__init__(ensure_io_loop=True, **kwargs)
|
|
540
|
+
self.redis = None
|
|
541
|
+
self.stopped = True
|
|
542
|
+
if start:
|
|
543
|
+
self.start()
|
|
544
|
+
|
|
545
|
+
@gen.coroutine
|
|
546
|
+
def process(self):
|
|
547
|
+
if not self.redis:
|
|
548
|
+
self.redis = yield aioredis.Redis(host=self.redis_address, password=self.redis_password)
|
|
549
|
+
|
|
550
|
+
topic_exists = yield self.redis.exists(self.topic)
|
|
551
|
+
if not topic_exists:
|
|
552
|
+
print('create topic:', self.topic)
|
|
553
|
+
yield self.redis.xadd(self.topic, {'data': dill.dumps('go')})
|
|
554
|
+
try:
|
|
555
|
+
yield self.redis.xgroup_create(self.topic, self.group)
|
|
556
|
+
except Exception as e:
|
|
557
|
+
print(e)
|
|
558
|
+
|
|
559
|
+
while True:
|
|
560
|
+
results = yield self.redis.xread(count=10, block=500, streams={self.topic: '$'})
|
|
561
|
+
if results:
|
|
562
|
+
for result in results:
|
|
563
|
+
print('rec:', result)
|
|
564
|
+
data = dill.loads(result[1][0][1][b'data'])
|
|
565
|
+
self._emit(data)
|
|
566
|
+
|
|
567
|
+
if self.stopped:
|
|
568
|
+
break
|
|
569
|
+
|
|
570
|
+
@gen.coroutine
|
|
571
|
+
def _send(self, data):
|
|
572
|
+
if not self.redis:
|
|
573
|
+
self.redis = yield aioredis.Redis(host=self.redis_address, password=self.redis_password)
|
|
574
|
+
x = yield self.redis.xadd(self.topic, {'data': dill.dumps(data)}, maxlen=20)
|
|
575
|
+
print('send:', x)
|
|
576
|
+
|
|
577
|
+
def emit(self, x, asynchronous=True):
|
|
578
|
+
self.loop.add_callback(self._send, x)
|
|
579
|
+
return x
|
|
580
|
+
|
|
581
|
+
def start(self):
|
|
582
|
+
if self.stopped:
|
|
583
|
+
self.stopped = False
|
|
584
|
+
self.loop.add_callback(self.process)
|
|
585
|
+
|
|
586
|
+
def stop(self,):
|
|
587
|
+
self.stopped = True
|
|
588
|
+
self.loop.add_callback(self.redis.close)
|
|
589
|
+
|
|
590
|
+
|
|
515
591
|
@Stream.register_api(staticmethod)
|
|
516
592
|
class from_redis(Stream):
|
|
517
593
|
def __init__(self, topic, group=None, max_len=100, **kwargs):
|
|
@@ -586,21 +662,21 @@ class from_http_request(Stream):
|
|
|
586
662
|
|
|
587
663
|
|
|
588
664
|
class StreamTCPServer(TCPServer):
|
|
589
|
-
"""
|
|
590
|
-
server.in_s
|
|
591
|
-
server.out_s
|
|
592
|
-
"""
|
|
593
|
-
|
|
594
665
|
def __init__(self, port=2345, **kwargs):
|
|
595
666
|
self.delimiter = 'zjw-split-0358'.encode('utf-8')
|
|
596
667
|
self.out_s = Stream()
|
|
597
668
|
self.in_s = Stream()
|
|
669
|
+
|
|
670
|
+
# 进来的数据,处理后,再发出去。
|
|
671
|
+
self.in_s >> self.out_s
|
|
672
|
+
|
|
598
673
|
super(StreamTCPServer, self).__init__(**kwargs)
|
|
599
674
|
self.handlers = dict()
|
|
600
675
|
self.listen(port)
|
|
601
676
|
|
|
602
677
|
def __rrshift__(self, x):
|
|
603
|
-
|
|
678
|
+
# 进入消息来源之一,直接塞入数据
|
|
679
|
+
x >> self.in_s
|
|
604
680
|
|
|
605
681
|
@gen.coroutine
|
|
606
682
|
def handle_stream(self, stream, address):
|
|
@@ -609,17 +685,22 @@ class StreamTCPServer(TCPServer):
|
|
|
609
685
|
stream.write(x)
|
|
610
686
|
stream.write(self.delimiter)
|
|
611
687
|
except StreamClosedError:
|
|
612
|
-
|
|
688
|
+
print('%s connect close' % str(address))
|
|
613
689
|
self.handlers.get(address).destroy()
|
|
614
690
|
del self.handlers[address]
|
|
615
691
|
|
|
692
|
+
# 将客户端io输出挂载到分发管道 ,出去的消息,dill后写出
|
|
616
693
|
self.handlers[address] = self.out_s.map(dill.dumps).sink(_write)
|
|
694
|
+
|
|
695
|
+
# 进入消息来源之二,不停的读取网络消息
|
|
617
696
|
while True:
|
|
618
697
|
try:
|
|
619
698
|
data = yield stream.read_until(self.delimiter)
|
|
620
|
-
|
|
699
|
+
# 进入消息来源之一、服务端读取客户端消息后放入接收管道
|
|
700
|
+
yield self.in_s.emit(dill.loads(data))
|
|
701
|
+
# yield self.out_s._emit(dill.loads(data))
|
|
621
702
|
except StreamClosedError:
|
|
622
|
-
|
|
703
|
+
print('%s connect close' % str(address))
|
|
623
704
|
break
|
|
624
705
|
|
|
625
706
|
def stop(self):
|
|
@@ -632,8 +713,10 @@ class StreamTCPServer(TCPServer):
|
|
|
632
713
|
|
|
633
714
|
class StreamTCPClient():
|
|
634
715
|
"""从tcp端口订阅数据
|
|
635
|
-
|
|
636
|
-
|
|
716
|
+
c1 = StreamTCPClient("127.0.0.1", 23)
|
|
717
|
+
c1.start()
|
|
718
|
+
client = StreamTCPClient(host='127.0.0.1',port=2345)
|
|
719
|
+
client2 = StreamTCPClient()
|
|
637
720
|
|
|
638
721
|
"""
|
|
639
722
|
|
|
@@ -642,37 +725,44 @@ class StreamTCPClient():
|
|
|
642
725
|
self.host = host
|
|
643
726
|
self.port = port
|
|
644
727
|
self.delimiter = 'zjw-split-0358'.encode('utf-8')
|
|
645
|
-
|
|
646
|
-
self.
|
|
728
|
+
|
|
729
|
+
self.out_s = Stream() # 发去服务端
|
|
730
|
+
self.in_s = Stream(ensure_io_loop=True) # 进入消息
|
|
647
731
|
self._stream = None
|
|
648
732
|
self.in_s.filter(lambda x: x == 'exit').sink(lambda x: self.stop())
|
|
733
|
+
self.stopped = True
|
|
649
734
|
self.start()
|
|
650
735
|
|
|
651
736
|
def __rrshift__(self, x):
|
|
737
|
+
# 直接发消息去服务端
|
|
652
738
|
x >> self.out_s
|
|
653
739
|
|
|
654
740
|
@gen.coroutine
|
|
655
741
|
def start(self):
|
|
656
742
|
try:
|
|
657
743
|
self._stream = yield TCPClient().connect(self.host, self.port)
|
|
744
|
+
self.stopped = False
|
|
658
745
|
except Exception as e:
|
|
659
|
-
|
|
746
|
+
print(e, 'connect', self.host, self.port, 'error')
|
|
660
747
|
|
|
661
748
|
def _write(x):
|
|
662
749
|
try:
|
|
663
750
|
self._stream.write(x)
|
|
664
751
|
self._stream.write(self.delimiter)
|
|
665
752
|
except StreamClosedError:
|
|
666
|
-
|
|
753
|
+
print(f'{self.host}:{self.port} connect close')
|
|
667
754
|
self.out_handler.destroy()
|
|
668
755
|
|
|
756
|
+
# 将客户端io输出挂载到分发管道
|
|
669
757
|
self.out_handler = self.out_s.map(dill.dumps).sink(_write)
|
|
670
|
-
#
|
|
758
|
+
#
|
|
671
759
|
while self._stream:
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
760
|
+
try:
|
|
761
|
+
data = yield self._stream.read_until(self.delimiter)
|
|
762
|
+
yield self.in_s.emit(dill.loads(data))
|
|
763
|
+
except StreamClosedError:
|
|
764
|
+
print('tornado.iostream.StreamClosedError')
|
|
765
|
+
break
|
|
676
766
|
|
|
677
767
|
def stop(self):
|
|
678
768
|
if not self._stream.closed() and self._stream.close():
|
|
@@ -689,6 +779,7 @@ class from_mail(Source):
|
|
|
689
779
|
starttls=False,
|
|
690
780
|
interval=60*15, start=False, **kwargs):
|
|
691
781
|
from imbox import Imbox
|
|
782
|
+
from .namespace import NB
|
|
692
783
|
if not host:
|
|
693
784
|
try:
|
|
694
785
|
hostname = NB('mail')['hostname']
|
|
@@ -753,106 +844,6 @@ class from_periodic(Source):
|
|
|
753
844
|
await asyncio.sleep(self._poll)
|
|
754
845
|
|
|
755
846
|
|
|
756
|
-
@Stream.register_api(staticmethod)
|
|
757
|
-
class http_topic(Stream):
|
|
758
|
-
"""Receive data from http request,emit httprequest data to stream."""
|
|
759
|
-
|
|
760
|
-
def __init__(self, port=7777, path='/.*', start=False, server_kwargs=None):
|
|
761
|
-
self.port = port
|
|
762
|
-
self.path = path
|
|
763
|
-
self.server_kwargs = server_kwargs or {}
|
|
764
|
-
super(http_topic, self).__init__(ensure_io_loop=True)
|
|
765
|
-
self.stopped = True
|
|
766
|
-
self.server = None
|
|
767
|
-
if start: # pragma: no cover
|
|
768
|
-
self.start()
|
|
769
|
-
|
|
770
|
-
def _start_server(self):
|
|
771
|
-
class Handler(RequestHandler):
|
|
772
|
-
source = self
|
|
773
|
-
|
|
774
|
-
def _loads(self, body):
|
|
775
|
-
"""解析从web端口提交过来的数据.
|
|
776
|
-
|
|
777
|
-
可能的数据有字符串和二进制,
|
|
778
|
-
字符串可能是直接字符串,也可能是json编码后的字符串
|
|
779
|
-
二进制可能是图像等直接可用的二进制,也可能是dill编码的二进制pyobject
|
|
780
|
-
"""
|
|
781
|
-
try:
|
|
782
|
-
body = dill.loads(body)
|
|
783
|
-
except TypeError:
|
|
784
|
-
body = json.loads(body)
|
|
785
|
-
try:
|
|
786
|
-
body = pd.DataFrame.from_dict(body)
|
|
787
|
-
except:
|
|
788
|
-
body = body
|
|
789
|
-
except ValueError:
|
|
790
|
-
body = body.decode('utf-8')
|
|
791
|
-
finally:
|
|
792
|
-
return body
|
|
793
|
-
|
|
794
|
-
def _encode(self, body):
|
|
795
|
-
if isinstance(body, pd.DataFrame):
|
|
796
|
-
return body.sample(20).to_html()
|
|
797
|
-
else:
|
|
798
|
-
return json.dumps(body, ensure_ascii=False)
|
|
799
|
-
|
|
800
|
-
@gen.coroutine
|
|
801
|
-
def post(self):
|
|
802
|
-
body = dill.loads(self.request.body)
|
|
803
|
-
body = [self._loads(i) for i in body]
|
|
804
|
-
if not isinstance(body, list):
|
|
805
|
-
body = [body]
|
|
806
|
-
|
|
807
|
-
tag = unquote(self.request.headers['tag'])
|
|
808
|
-
print(tag)
|
|
809
|
-
if tag:
|
|
810
|
-
source = NS(tag)
|
|
811
|
-
if not source.is_cache:
|
|
812
|
-
source.start_cache(5, 64*64*24*5)
|
|
813
|
-
else:
|
|
814
|
-
source = self.source
|
|
815
|
-
for i in body:
|
|
816
|
-
yield source._emit(i)
|
|
817
|
-
self.write('OK')
|
|
818
|
-
|
|
819
|
-
@gen.coroutine
|
|
820
|
-
def get(self):
|
|
821
|
-
topic = unquote(self.request.path)
|
|
822
|
-
if topic == '/':
|
|
823
|
-
data = self.source.recent()
|
|
824
|
-
else:
|
|
825
|
-
stream = NS(topic.split('/')[1])
|
|
826
|
-
if not stream.is_cache:
|
|
827
|
-
stream.start_cache(10, 64*64*24*7)
|
|
828
|
-
data = stream.recent()
|
|
829
|
-
if 'deva' in self.request.headers['User-Agent']:
|
|
830
|
-
self.write(dill.dumps(data))
|
|
831
|
-
else:
|
|
832
|
-
for i in data:
|
|
833
|
-
self.write(self._encode(i)+'</br>')
|
|
834
|
-
|
|
835
|
-
self.application = Application([
|
|
836
|
-
(self.path, Handler),
|
|
837
|
-
])
|
|
838
|
-
# self.server = HTTPServer(application, **self.server_kwargs)
|
|
839
|
-
self.server = self.application.listen(self.port)
|
|
840
|
-
|
|
841
|
-
def start(self):
|
|
842
|
-
if self.stopped:
|
|
843
|
-
self.stopped = False
|
|
844
|
-
self._start_server()
|
|
845
|
-
self.start_cache(5, 60*60*48)
|
|
846
|
-
# self.loop.add_callback(self._start_server)#这个会导致在端口占用情况下不报错
|
|
847
|
-
|
|
848
|
-
def stop(self):
|
|
849
|
-
"""Shutdown HTTP server."""
|
|
850
|
-
if not self.stopped:
|
|
851
|
-
self.server.stop()
|
|
852
|
-
self.server = None
|
|
853
|
-
self.stopped = True
|
|
854
|
-
|
|
855
|
-
|
|
856
847
|
def gen_block_test() -> int:
|
|
857
848
|
import time
|
|
858
849
|
import datetime
|
deva-1.2.4/deva/topic.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from .core import Stream
|
|
2
|
+
# from tornado import gen
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
from .sources import StreamTCPClient, RedisStream
|
|
6
|
+
from urllib.parse import unquote
|
|
7
|
+
from tornado.web import RequestHandler, Application
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import dill
|
|
10
|
+
from tornado import gen
|
|
11
|
+
import json
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@Stream.register_api()
|
|
17
|
+
class Topic(RedisStream):
|
|
18
|
+
|
|
19
|
+
def __init__(self, name='', group=str(os.getpid()), maxsize=None, **kwargs):
|
|
20
|
+
super().__init__(topic=name,
|
|
21
|
+
group=group,
|
|
22
|
+
start=True,
|
|
23
|
+
name=name,
|
|
24
|
+
**kwargs)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@Stream.register_api(staticmethod)
|
|
28
|
+
class TCPStream(Stream):
|
|
29
|
+
"""redis stream,read and write.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
上游进来的写入,读出来的压入下游,
|
|
33
|
+
exapmle::
|
|
34
|
+
|
|
35
|
+
bus = TCPStream()
|
|
36
|
+
bus>>log
|
|
37
|
+
bus2 = TCPStream()
|
|
38
|
+
bus2.map(lambda x:x*2)>>log
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self, host='127.0.0.1', port=2345, topic='', **kwargs):
|
|
43
|
+
self.topic = topic
|
|
44
|
+
super(TCPStream, self).__init__(ensure_io_loop=True, **kwargs)
|
|
45
|
+
try:
|
|
46
|
+
self.client = StreamTCPClient(host=host, port=port)
|
|
47
|
+
# 进来的消息发下游
|
|
48
|
+
self.client.in_s.sink(lambda x: self._emit(x))
|
|
49
|
+
except Exception as e:
|
|
50
|
+
print(e)
|
|
51
|
+
|
|
52
|
+
def emit(self, x, asynchronous=True):
|
|
53
|
+
x >> self.client
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@Stream.register_api()
|
|
57
|
+
class TCPTopic(TCPStream):
|
|
58
|
+
|
|
59
|
+
def __init__(self, name='', **kwargs):
|
|
60
|
+
super().__init__(topic=name,
|
|
61
|
+
name=name,
|
|
62
|
+
**kwargs)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@Stream.register_api(staticmethod)
|
|
66
|
+
class http_topic(Stream):
|
|
67
|
+
|
|
68
|
+
"""Receive data from http request,emit httprequest data to stream."""
|
|
69
|
+
|
|
70
|
+
def __init__(self, port=7777, path='/.*', start=False, server_kwargs=None):
|
|
71
|
+
self.port = port
|
|
72
|
+
self.path = path
|
|
73
|
+
self.server_kwargs = server_kwargs or {}
|
|
74
|
+
super(http_topic, self).__init__(ensure_io_loop=True)
|
|
75
|
+
self.stopped = True
|
|
76
|
+
self.server = None
|
|
77
|
+
if start: # pragma: no cover
|
|
78
|
+
self.start()
|
|
79
|
+
|
|
80
|
+
def _start_server(self):
|
|
81
|
+
from .namespace import NS
|
|
82
|
+
|
|
83
|
+
class Handler(RequestHandler):
|
|
84
|
+
source = self
|
|
85
|
+
|
|
86
|
+
def _loads(self, body):
|
|
87
|
+
"""解析从web端口提交过来的数据.
|
|
88
|
+
|
|
89
|
+
可能的数据有字符串和二进制,
|
|
90
|
+
字符串可能是直接字符串,也可能是json编码后的字符串
|
|
91
|
+
二进制可能是图像等直接可用的二进制,也可能是dill编码的二进制pyobject
|
|
92
|
+
"""
|
|
93
|
+
try:
|
|
94
|
+
body = dill.loads(body)
|
|
95
|
+
except TypeError:
|
|
96
|
+
body = json.loads(body)
|
|
97
|
+
try:
|
|
98
|
+
body = pd.DataFrame.from_dict(body)
|
|
99
|
+
except:
|
|
100
|
+
body = body
|
|
101
|
+
except ValueError:
|
|
102
|
+
body = body.decode('utf-8')
|
|
103
|
+
finally:
|
|
104
|
+
return body
|
|
105
|
+
|
|
106
|
+
def _encode(self, body):
|
|
107
|
+
if isinstance(body, pd.DataFrame):
|
|
108
|
+
return body.sample(20).to_html()
|
|
109
|
+
else:
|
|
110
|
+
return json.dumps(body, ensure_ascii=False)
|
|
111
|
+
|
|
112
|
+
@gen.coroutine
|
|
113
|
+
def post(self):
|
|
114
|
+
body = dill.loads(self.request.body)
|
|
115
|
+
body = [self._loads(i) for i in body]
|
|
116
|
+
if not isinstance(body, list):
|
|
117
|
+
body = [body]
|
|
118
|
+
|
|
119
|
+
tag = unquote(self.request.headers['tag'])
|
|
120
|
+
print(tag)
|
|
121
|
+
if tag:
|
|
122
|
+
source = NS(tag)
|
|
123
|
+
if not source.is_cache:
|
|
124
|
+
source.start_cache(5, 64*64*24*5)
|
|
125
|
+
else:
|
|
126
|
+
source = self.source
|
|
127
|
+
for i in body:
|
|
128
|
+
yield source._emit(i)
|
|
129
|
+
self.write('OK')
|
|
130
|
+
|
|
131
|
+
@gen.coroutine
|
|
132
|
+
def get(self):
|
|
133
|
+
topic = unquote(self.request.path)
|
|
134
|
+
if topic == '/':
|
|
135
|
+
data = self.source.recent()
|
|
136
|
+
else:
|
|
137
|
+
stream = NS(topic.split('/')[1])
|
|
138
|
+
if not stream.is_cache:
|
|
139
|
+
stream.start_cache(10, 64*64*24*7)
|
|
140
|
+
data = stream.recent()
|
|
141
|
+
if 'deva' in self.request.headers['User-Agent']:
|
|
142
|
+
self.write(dill.dumps(data))
|
|
143
|
+
else:
|
|
144
|
+
for i in data:
|
|
145
|
+
self.write(self._encode(i)+'</br>')
|
|
146
|
+
|
|
147
|
+
self.application = Application([
|
|
148
|
+
(self.path, Handler),
|
|
149
|
+
])
|
|
150
|
+
# self.server = HTTPServer(application, **self.server_kwargs)
|
|
151
|
+
self.server = self.application.listen(self.port)
|
|
152
|
+
|
|
153
|
+
def start(self):
|
|
154
|
+
if self.stopped:
|
|
155
|
+
self.stopped = False
|
|
156
|
+
self._start_server()
|
|
157
|
+
self.start_cache(5, 60*60*48)
|
|
158
|
+
# self.loop.add_callback(self._start_server)#这个会导致在端口占用情况下不报错
|
|
159
|
+
|
|
160
|
+
def stop(self):
|
|
161
|
+
"""Shutdown HTTP server."""
|
|
162
|
+
if not self.stopped:
|
|
163
|
+
self.server.stop()
|
|
164
|
+
self.server = None
|
|
165
|
+
self.stopped = True
|
deva-1.2.3/deva/topic.py
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
from .core import Stream, sync
|
|
2
|
-
from tornado import gen
|
|
3
|
-
import aioredis
|
|
4
|
-
import dill
|
|
5
|
-
import logging
|
|
6
|
-
import os
|
|
7
|
-
import time
|
|
8
|
-
|
|
9
|
-
logger = logging.getLogger(__name__)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@Stream.register_api(staticmethod)
|
|
13
|
-
class RedisStream(Stream):
|
|
14
|
-
"""redis stream,read and write.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
上游进来的写入redis ,redis的读出来的压入下游,
|
|
18
|
-
exapmle::
|
|
19
|
-
|
|
20
|
-
news = Stream.RedisStream('news')
|
|
21
|
-
l = list()
|
|
22
|
-
news>>l
|
|
23
|
-
for i in range(1000):
|
|
24
|
-
i>>news
|
|
25
|
-
|
|
26
|
-
l|len
|
|
27
|
-
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
def __init__(self, topic, start=True,
|
|
31
|
-
group=None, address='localhost', db=0, password=None, **kwargs):
|
|
32
|
-
self.topic = topic
|
|
33
|
-
self.redis_address = address
|
|
34
|
-
self.redis_password = password
|
|
35
|
-
self.group = group or hash(self)+hash(time.time())
|
|
36
|
-
self.consumer = hash(self)
|
|
37
|
-
|
|
38
|
-
super(RedisStream, self).__init__(ensure_io_loop=True, **kwargs)
|
|
39
|
-
self.redis = None
|
|
40
|
-
self.stopped = True
|
|
41
|
-
if start:
|
|
42
|
-
self.start()
|
|
43
|
-
|
|
44
|
-
@gen.coroutine
|
|
45
|
-
def process(self):
|
|
46
|
-
if not self.redis:
|
|
47
|
-
self.redis = yield aioredis.Redis(host=self.redis_address, password=self.redis_password)
|
|
48
|
-
|
|
49
|
-
topic_exists = yield self.redis.exists(self.topic)
|
|
50
|
-
if not topic_exists:
|
|
51
|
-
print('create topic:', self.topic)
|
|
52
|
-
yield self.redis.xadd(self.topic, {'data': dill.dumps('go')})
|
|
53
|
-
try:
|
|
54
|
-
yield self.redis.xgroup_create(self.topic, self.group)
|
|
55
|
-
except Exception as e:
|
|
56
|
-
print(e)
|
|
57
|
-
|
|
58
|
-
while True:
|
|
59
|
-
result = yield self.redis.xread(count=1, block=500, streams={self.topic: '$'})
|
|
60
|
-
if result:
|
|
61
|
-
data = dill.loads(result[0][1][0][1][b'data'])
|
|
62
|
-
self._emit(data)
|
|
63
|
-
if self.stopped:
|
|
64
|
-
break
|
|
65
|
-
|
|
66
|
-
@gen.coroutine
|
|
67
|
-
def _send(self, data):
|
|
68
|
-
if not self.redis:
|
|
69
|
-
self.redis = yield aioredis.Redis(host=self.redis_address, password=self.redis_password)
|
|
70
|
-
yield self.redis.xadd(self.topic, {'data': dill.dumps(data)})
|
|
71
|
-
|
|
72
|
-
def emit(self, x, asynchronous=True):
|
|
73
|
-
self.loop.add_callback(self._send, x)
|
|
74
|
-
return x
|
|
75
|
-
|
|
76
|
-
def start(self):
|
|
77
|
-
if self.stopped:
|
|
78
|
-
self.stopped = False
|
|
79
|
-
self.loop.add_callback(self.process)
|
|
80
|
-
|
|
81
|
-
def stop(self,):
|
|
82
|
-
self.stopped = True
|
|
83
|
-
self.loop.add_callback(self.redis.close)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
@Stream.register_api()
|
|
87
|
-
class Topic(RedisStream):
|
|
88
|
-
|
|
89
|
-
def __init__(self, name='', group=str(os.getpid()), maxsize=None, **kwargs):
|
|
90
|
-
super().__init__(topic=name,
|
|
91
|
-
group=group,
|
|
92
|
-
start=True,
|
|
93
|
-
name=name,
|
|
94
|
-
**kwargs)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|