Test Failed
Push — master ( fcf1be...4a2af5 )
by Heiko 'riot'
04:20
created

isomer.client.IsomerClient.stdin_read()   C

Complexity

Conditions 9

Size

Total Lines 39
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 31
nop 2
dl 0
loc 39
rs 6.6666
c 0
b 0
f 0
1
import sys
2
from json import dumps, loads
3
4
from circuits import handler, Timer, Event
5
from circuits.io import stdin
6
from circuits.net.events import write
7
from circuits.web.websockets import WebSocketClient
8
9
from isomer.component import LoggingComponent
10
from isomer.logger import error, verbose, debug, set_verbosity
11
from isomer.events.system import isomer_event
12
13
14
class get_data(isomer_event):
15
    def __init__(self, schema, search_filter):
16
        super(get_data, self).__init__()
17
        self.schema = schema
18
        self.search_filter = search_filter
19
20
21
class call_loop(isomer_event):
22
    pass
23
24
25
class IsomerClient(LoggingComponent):
26
    def __init__(self, no_stdin=False, no_stdout=False, loop_function=None, loop_frequency=60, *args, **kwargs):
27
        super(IsomerClient, self).__init__(*args, **kwargs)
28
        self.url = '{protocol}://{host}:{port}/{url}'.format(**kwargs)
29
        if no_stdout:
30
            set_verbosity(100)
31
32
        if not no_stdin:
33
            self.log("Connecting stdin")
34
            stdin.register(self)
35
36
        self.log("Connecting to isomer instance at", self.url)
37
        self.username = kwargs.get('username')
38
        self.password = kwargs.get('password')
39
40
        self.max_length = 100
41
        self._request_id = 0
42
43
        self.messages = []
44
        self.hooks = {}
45
46
        self.client = WebSocketClient(self.url).register(self)
47
48
        if loop_function is not None:
49
            self.log('Registering external loop function')
50
            self.loop_function = loop_function
51
            self.loop_timer = Timer(1/loop_frequency, call_loop(), persist=True).register(self)
52
53
        self.log("Ready")
54
55
    @handler("call_loop")
56
    def call_loop(self, event):
57
        """Runs a given client loop for interactive processes"""
58
        self.log('Running external loop')
59
        try:
60
            self.loop_function()
61
        except Exception:
62
            self.loop_timer.unregister()
63
            Timer(2, Event.create("quit")).register(self)
64
65
    @handler("quit")
66
    def quit(self):
67
        self.log("Quitting")
68
        sys.exit()
69
70
    @handler("registered")
71
    def registered(self, event, *args):
72
        if 'ws' not in event.channels:
73
            self.log("Hello", event)
74
            return
75
        self.log('Transmitting login')
76
77
        self.fireEvent(write(""), "ws")
78
79
        packet = {
80
            'component': 'auth',
81
            'action': 'login',
82
            'data': {
83
                'username': self.username,
84
                'password': self.password
85
            }
86
        }
87
88
        self._transmit(packet)
89
90
    def _transmit(self, packet):
91
92
        self.log(packet)
93
        unicode = dumps(packet).encode('utf-8')
94
        self.log(unicode, type(unicode))
95
96
        self.fireEvent(write(bytes(unicode)), 'ws')
97
98
    @handler("read")
99
    def read(self, *args):
100
        self.log("Reading")
101
        msg = args[0]
102
        self._handle_message(msg)
103
104
        self.messages.append(msg)
105
        if len(msg) > self.max_length:
106
            msg = str(msg)[:self.max_length] + " ..."
107
        self.log("Response [%i]: %s" % (len(self.messages), msg))
108
109
    def _handle_message(self, msg):
110
        try:
111
            decoded = loads(msg)
112
        except Exception:
113
            self.log("Couldn't decode message!")
114
            return
115
116
        if decoded['component'] == 'isomer.auth' and decoded['action'] == 'fail':
117
            self.log("Login failed. Check credentials and url!", lvl=error)
118
            sys.exit()
119
120
    @handler("read", channel="stdin")
121
    def stdin_read(self, data):
122
        """read Event (on channel ``stdin``)
123
        This is the event handler for ``read`` events specifically from the
124
        ``stdin`` channel. This is triggered each time stdin has data that
125
        it has read.
126
        """
127
128
        data = data.strip().decode("utf-8")
129
        self.log("Incoming:", data, lvl=verbose)
130
131
        if len(data) == 0:
132
            self.log('Use /help to get a list of client commands')
133
            return
134
135
        if data[0] == "/":
136
            cmd = data[1:]
137
            args = []
138
            if ' ' in cmd:
139
                cmd, args = cmd.split(' ', maxsplit=1)
140
                args = args.split(' ')
141
            if cmd in self.hooks:
142
                self.log('Firing hooked event:', cmd, args, lvl=debug)
143
                self.fireEvent(self.hooks[cmd](*args))
144
            if cmd.lower() in ('send', 's'):
145
                data = " ".join(args)
146
                json = loads(" ".join(args))
147
                self.log("Transmitting:", data, pretty=True, lvl=debug)
148
                self.fireEvent(write(data), 'ws')
149
            if cmd.lower() in ('history', 'h'):
150
                position = int(args[0])
151
                pretty = '-p' in args
152
                self.log('Response [%i]: %s' % (position, self.messages[position]),
153
                         pretty=pretty)
154
            if cmd.lower() == "test":
155
                self.fireEvent(get_data('user', {'name': 'riot'}))
156
            if cmd.lower() in ('quit', 'q'):
157
                self.log('Initiating system exit in 3 seconds on request.', lvl=verbose)
158
                Timer(3, Event.create("quit")).register(self)
159
160
    def get_data(self, event: get_data):
161
        """Request data from Isomer server"""
162
163
        request = {
164
            'component': 'isomer.events.objectmanager',
165
            'action': 'search',
166
            'data': {
167
                'schema': event.schema,
168
                'search': event.search_filter,
169
                'req': self._request_id
170
            }
171
        }
172
173
        self._transmit(request)
174
        self._request_id += 1
175