kivyblocks/kivyblocks/threadcall.py
2020-04-24 18:35:30 +08:00

223 lines
5.9 KiB
Python

import time
from threading import Thread, Lock, BoundedSemaphore
import requests
from kivy.event import EventDispatcher
from kivy.clock import Clock
from kivy.app import App
from .login import LoginForm
from .utils import NeedLogin, InsufficientPrivilege, HTTPError
class ThreadCall(Thread,EventDispatcher):
def __init__(self,target, args=(), kwargs={}):
Thread.__init__(self)
EventDispatcher.__init__(self)
self.register_event_type('on_result')
self.register_event_type('on_error')
self.rez = None
self.daemon = False
self.target = target
self.args = args
self.timing = None
self.kwargs = kwargs
def start(self):
Thread.start(self)
self.timing = Clock.schedule_once(self.checkStop,0)
def run(self):
try:
self.rez = self.target(*self.args,**self.kwargs)
self.dispatch('on_result',self.rez)
except Exception as e:
self.dispatch('on_error',e)
def on_result(self, v):
pass
def on_error(self,e):
pass
def checkStop(self,timestamp):
x = self.join(timeout=0.001)
if self.is_alive():
self.timing = Clock.schedule_once(self.checkStop,0)
return
class Workers(Thread):
def __init__(self,maxworkers):
super().__init__()
self.max_workers = maxworkers
self.tasks = []
# task = [callee,callback,kwargs]
self.lock = Lock()
self.work_sema = BoundedSemaphore(value=self.max_workers)
self.running = False
def run(self):
self.running = True
while self.running:
if len(self.tasks) == 0:
time.sleep(0.001)
continue
task = None
with self.lock:
task = self.tasks.pop()
if task is None:
continue
with self.work_sema:
callee,callback,errback,kwargs = task
x = ThreadCall(callee,kwargs=kwargs)
x.bind(on_result=callback)
if errback:
x.bind(on_error=errback)
x.start()
def add(self,callee,callback,errback=None,kwargs={}):
with self.lock:
self.tasks.insert(0,[callee,callback,errback,kwargs])
hostsessions = {}
class HttpClient:
def __init__(self):
self.s = requests.Session()
self.workers = App.get_running_app().workers
def url2domain(self,url):
parts = url.split('/')[:3]
pre = '/'.join(parts)
return pre
def webcall(self,url,method="GET",params={},files={},headers={}):
app = App.get_running_app()
domain = self.url2domain(url)
sessionid = hostsessions.get(domain,None)
print('hostsessions=', hostsessions,
'sessionid=',sessionid,
'domain=',domain,
'url=',url
)
if sessionid:
headers.update({'session':sessionid})
elif app.getAuthHeader():
headers.update(app.getAuthHeader())
print('headers=',headers)
if method in ['GET']:
req = requests.Request(method,url,
params=params,headers=headers)
else:
req = requests.Request(method,url,
data=params,files=files,headers=headers)
prepped = self.s.prepare_request(req)
resp = self.s.send(prepped)
if resp.status_code == 200:
h = resp.headers.get('Set-Cookie',None)
if h:
print('*************set-Cookie=',h,'domain=',domain)
sessionid = h.split(';')[0]
hostsessions[domain] = sessionid
try:
data = resp.json()
if type(data) != type({}):
return data
status = data.get('status',None)
if status is None:
return data
if status == 'OK':
return data['data']
return data
except:
return resp.text
if resp.status_code == 401:
print('NeedLogin:',url)
raise NeedLogin
if resp.status_code == 403:
raise InsufficientPrivilege
print('Error', url, method,
params, resp.status_code,
type(resp.status_code))
raise HTTPError(resp.status_code)
def __call__(self,url,method="GET",params={},headers={},files={},callback=None,errback=None):
def cb(t,resp):
return resp
if callback is None:
try:
resp = self.webcall(url, method=method,
params=params, files=files, headers=headers)
return cb(None,resp)
except Exception as e:
raise e
except Exception as e:
if errback is not None:
errback(e)
return None
kwargs = {
"url":url,
"method":method,
"params":params,
"files":files,
"headers":headers
}
self.workers.add(self.webcall,callback,errback,kwargs=kwargs)
def get(self, url, params={}, headers={}, callback=None, errback=None):
return self.__call__(url,method='GET',params=params,
headers=headers, callback=callback,
errback=errback)
def post(self, url, params={}, headers={}, files={}, callback=None, errback=None):
return self.__call__(url,method='POST',params=params, files=files,
headers=headers, callback=callback,
errback=errback)
def put(self, url, params={}, headers={}, callback=None, errback=None):
return self.__call__(url,method='PUT',params=params,
headers=headers, callback=callback,
errback=errback)
def delete(self, url, params={}, headers={}, callback=None, errback=None):
return self.__call__(url,method='DELETE',params=params,
headers=headers, callback=callback,
errback=errback)
def option(self, url, params={}, headers={}, callback=None, errback=None):
return self.__call__(url,method='OPTION',params=params,
headers=headers, callback=callback, errback=errback)
if __name__ == '__main__':
from kivy.uix.textinput import TextInput
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class MyApp(App):
def build(self):
self.hc = HttpClient()
x = BoxLayout(orientation='vertical')
y = BoxLayout(orientation='horizontal',size_hint_y=0.07)
self.ti = TextInput(size_hint_x=0.95,multiline=False)
btn = Button(size_hint_x=0.05,text='go')
y.add_widget(self.ti)
y.add_widget(btn)
btn.bind(on_press=self.getHtml)
self.texti = TextInput(multiline=True,readonly=True)
x.add_widget(y)
x.add_widget(self.texti)
return x
def getHtml(self,v=None):
url = self.ti.text
self.hc.get(url,callback=self.showResult)
self.texti.text = 'loading...'
def showResult(self,target,resp):
if resp.status_code==200:
self.texti.text = resp.text
else:
print(reps.status_code,'...............')
MyApp().run()