mirror of
https://github.com/avatao-content/baseimage-tutorial-framework
synced 2025-06-29 01:05:12 +00:00
Project-wide refactor of things named Component* to EventHandler*
This commit is contained in:
0
src/event_handlers/__init__.py
Normal file
0
src/event_handlers/__init__.py
Normal file
18
src/event_handlers/event_handler.py
Normal file
18
src/event_handlers/event_handler.py
Normal file
@ -0,0 +1,18 @@
|
||||
import json
|
||||
from functools import partial
|
||||
|
||||
from event_handler_base import EventHandlerBase
|
||||
|
||||
|
||||
class EventHandler(EventHandlerBase):
|
||||
def __init__(self, anchor, event_handler_function, zmq_context=None):
|
||||
super().__init__(anchor, event_handler_function, zmq_context)
|
||||
|
||||
def wrapper(msg_parts, handler):
|
||||
anchor, message = msg_parts
|
||||
data_json = json.loads(message)
|
||||
response = handler(data_json, self)
|
||||
encoded_response = json.dumps(response).encode('utf-8')
|
||||
self.zmq_push_socket.send_multipart([anchor, encoded_response])
|
||||
|
||||
self.zmq_sub_stream.on_recv(partial(wrapper, handler=event_handler_function))
|
48
src/event_handlers/event_handler_base.py
Normal file
48
src/event_handlers/event_handler_base.py
Normal file
@ -0,0 +1,48 @@
|
||||
import json
|
||||
import zmq
|
||||
from zmq.eventloop import ioloop
|
||||
from zmq.eventloop.zmqstream import ZMQStream
|
||||
|
||||
from config import PUBLISHER_PORT, RECEIVER_PORT
|
||||
|
||||
ioloop.install()
|
||||
|
||||
|
||||
class EventHandlerBase:
|
||||
def __init__(self, anchor, event_handler_function, zmq_context=None):
|
||||
self.anchor = anchor
|
||||
self.event_handler_function = event_handler_function
|
||||
self.zmq_context = zmq_context or zmq.Context.instance()
|
||||
self.zmq_sub_socket = self.zmq_context.socket(zmq.SUB)
|
||||
self.subscriptions = {self.anchor}
|
||||
self.zmq_sub_socket.setsockopt_string(zmq.SUBSCRIBE, self.anchor)
|
||||
self.zmq_sub_socket.connect('tcp://localhost:{}'.format(PUBLISHER_PORT))
|
||||
self.zmq_sub_stream = ZMQStream(self.zmq_sub_socket)
|
||||
self.zmq_push_socket = self.zmq_context.socket(zmq.PUSH)
|
||||
self.zmq_push_socket.connect('tcp://localhost:{}'.format(RECEIVER_PORT))
|
||||
|
||||
def message_other(self, anchor, data):
|
||||
encoded_anchor = anchor.encode('utf-8')
|
||||
message = {
|
||||
'anchor': anchor,
|
||||
'data': data
|
||||
}
|
||||
encoded_message = json.dumps(message).encode('utf-8')
|
||||
self.zmq_push_socket.send_multipart([encoded_anchor, encoded_message])
|
||||
|
||||
def subscribe(self, anchor):
|
||||
if anchor not in self.subscriptions:
|
||||
self.subscriptions.add(anchor)
|
||||
self.zmq_sub_socket.setsockopt_string(zmq.SUBSCRIBE, anchor)
|
||||
|
||||
def unsubscribe(self, anchor):
|
||||
try:
|
||||
self.subscriptions.remove(anchor)
|
||||
self.zmq_sub_socket.setsockopt_string(zmq.UNSUBSCRIBE, anchor)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def unsubscribe_all(self):
|
||||
for sub in self.subscriptions:
|
||||
self.zmq_sub_socket.setsockopt_string(zmq.UNSUBSCRIBE, sub)
|
||||
self.subscriptions.clear()
|
86
src/event_handlers/event_handler_example.py
Normal file
86
src/event_handlers/event_handler_example.py
Normal file
@ -0,0 +1,86 @@
|
||||
import codecs
|
||||
import sqlite3
|
||||
|
||||
import source_code
|
||||
from event_handler import EventHandler
|
||||
from stateful_event_handler import StatefulEventHandler
|
||||
from tornado.ioloop import IOLoop
|
||||
|
||||
from login_component import authorize_login
|
||||
|
||||
|
||||
def echo_handler(data):
|
||||
return data
|
||||
|
||||
|
||||
def rot13_handler(data, *args):
|
||||
data['data'] = codecs.encode(data['data'], 'rot13')
|
||||
return data
|
||||
|
||||
|
||||
def change_case_handler(data, *args):
|
||||
data['data'] = data['data'].upper() if data['data'].islower() else data['data'].lower()
|
||||
return data
|
||||
|
||||
|
||||
def reverse_handler(data, *args):
|
||||
data['data'] = data['data'][::-1]
|
||||
return data
|
||||
|
||||
|
||||
def login_handler(data, component):
|
||||
email, password = data['data']['email'], data['data']['password']
|
||||
try:
|
||||
sql_statement = source_code.find_local_variable_value(authorize_login, 'sql_statement')
|
||||
yield (
|
||||
'anchor_logger',
|
||||
'The SQL statement executed by the server will look like this:\n `{}`'.format(sql_statement)
|
||||
)
|
||||
|
||||
yield ('anchor_webide',
|
||||
source_code.get_source_code(authorize_login, strip_comments=False))
|
||||
|
||||
sql_statement_with_values = sql_statement.format(email, password)
|
||||
yield (
|
||||
'anchor_logger',
|
||||
'After the submitted parameters are substituted it looks like this:\n `{}`'.format(
|
||||
sql_statement_with_values
|
||||
)
|
||||
)
|
||||
|
||||
logged_in_email, is_admin = authorize_login(email, password)
|
||||
|
||||
yield (
|
||||
'anchor_logger',
|
||||
'After the query is executed, it returns _{}_ as email address, and _{}_ for is_admin'.format(
|
||||
logged_in_email, is_admin
|
||||
)
|
||||
)
|
||||
|
||||
if logged_in_email is not None:
|
||||
response = 'Logged in as _{}_. You __{}have__ admin privileges.'.format(
|
||||
logged_in_email,
|
||||
'' if is_admin else 'don\'t '
|
||||
)
|
||||
else:
|
||||
response = 'Bad username/password!'
|
||||
except sqlite3.Warning:
|
||||
response = 'Invalid request!'
|
||||
|
||||
yield ('anchor_login', '# Login page\n' + response)
|
||||
|
||||
|
||||
def source_code_handler(data, event_handler):
|
||||
event_handler.unsubscribe(data['anchor'])
|
||||
yield (data['anchor'],
|
||||
source_code.get_source_code(authorize_login, strip_comments=True))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
anchor_a = EventHandler('anchor_a', change_case_handler)
|
||||
anchor_b = EventHandler('anchor_b', rot13_handler)
|
||||
anchor_c = EventHandler('anchor_c', reverse_handler)
|
||||
anchor_login = StatefulEventHandler('anchor_login', login_handler)
|
||||
anchor_webide = StatefulEventHandler('anchor_webide', source_code_handler)
|
||||
IOLoop.instance().start()
|
||||
|
27
src/event_handlers/login_component.py
Normal file
27
src/event_handlers/login_component.py
Normal file
@ -0,0 +1,27 @@
|
||||
import sqlite3
|
||||
|
||||
|
||||
def get_db():
|
||||
return sqlite3.connect('users.db')
|
||||
|
||||
|
||||
def authorize_login(email, password):
|
||||
"""
|
||||
This method checks if a user is authorized and has admin privileges.
|
||||
:param email: The email address of the user.
|
||||
:param password: The password of the user.
|
||||
:return: A tuple, the first element is the email address if the user exists,
|
||||
and None if they don't; the second element is a boolean, which is True if
|
||||
the user has admin privileges.
|
||||
"""
|
||||
conn = get_db()
|
||||
sql_statement = '''SELECT email, is_admin FROM users
|
||||
WHERE email="{}" AND password="{}"'''
|
||||
# The problem with this approach is that it substitutes any value received
|
||||
# from the user, even if it is a valid SQL statement!
|
||||
result = conn.execute(sql_statement.format(email, password)).fetchone()
|
||||
if result is None:
|
||||
return None, False
|
||||
else:
|
||||
email, is_admin = result
|
||||
return email, is_admin == 1
|
39
src/event_handlers/stateful_event_handler.py
Normal file
39
src/event_handlers/stateful_event_handler.py
Normal file
@ -0,0 +1,39 @@
|
||||
import json
|
||||
from functools import partial
|
||||
|
||||
import zmq
|
||||
|
||||
from event_handler_base import EventHandlerBase
|
||||
|
||||
|
||||
class StatefulEventHandler(EventHandlerBase):
|
||||
def __init__(self, anchor, event_handler, zmq_context=None):
|
||||
super().__init__(anchor, event_handler, zmq_context)
|
||||
self.generator = None
|
||||
self.subscribe('reset')
|
||||
|
||||
def wrapper(msg_parts, handler):
|
||||
anchor, message = msg_parts
|
||||
if anchor == b'reset':
|
||||
self.generator = None
|
||||
self.unsubscribe_all()
|
||||
self.subscribe(self.anchor)
|
||||
self.subscribe('reset')
|
||||
return
|
||||
data_json = json.loads(message)
|
||||
if self.generator is None:
|
||||
self.generator = handler(data_json, self)
|
||||
response_anchor, response_data = next(self.generator)
|
||||
if response_anchor is None:
|
||||
return
|
||||
if response_anchor not in self.subscriptions:
|
||||
self.subscriptions.add(response_anchor)
|
||||
self.zmq_sub_socket.setsockopt_string(zmq.SUBSCRIBE, response_anchor)
|
||||
response_data = json.dumps({
|
||||
'anchor': response_anchor,
|
||||
'data': response_data,
|
||||
})
|
||||
response = [r.encode('utf-8') for r in (response_anchor, response_data)]
|
||||
self.zmq_push_socket.send_multipart(response)
|
||||
|
||||
self.zmq_sub_stream.on_recv(partial(wrapper, handler=event_handler))
|
BIN
src/event_handlers/users.db
Normal file
BIN
src/event_handlers/users.db
Normal file
Binary file not shown.
Reference in New Issue
Block a user