|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2013 - 2014 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a Qt free version of a background client for the various |
|
8 checkers and other python interpreter dependent functions. |
|
9 """ |
|
10 |
|
11 from __future__ import unicode_literals |
|
12 try: |
|
13 bytes = unicode #__IGNORE_WARNING__ |
|
14 except NameError: |
|
15 pass |
|
16 |
|
17 import json |
|
18 import os |
|
19 import socket |
|
20 import struct |
|
21 import sys |
|
22 from zlib import adler32 |
|
23 |
|
24 if __name__ == '__main__': |
|
25 # Add Eric basepath to sys.path to be able to import modules which are |
|
26 # laying not only below Utilities |
|
27 path = os.path.dirname(sys.argv[0]) |
|
28 path = os.path.dirname(path) |
|
29 sys.path.append(path) |
|
30 |
|
31 from Plugins.CheckerPlugins.SyntaxChecker import SyntaxCheck |
|
32 |
|
33 |
|
34 class BackgroundClient(object): |
|
35 """ |
|
36 Class implementing the main part of the background client. |
|
37 """ |
|
38 def __init__(self, host, port): |
|
39 """ |
|
40 Constructor of the BackgroundClient class. |
|
41 |
|
42 @param host ip address the background service is listening |
|
43 @param port port of the background service |
|
44 """ |
|
45 self.connection = socket.create_connection((host, port)) |
|
46 ver = b'2' if sys.version_info[0] == 2 else b'3' |
|
47 self.connection.sendall(ver) |
|
48 self.connection.settimeout(0.25) |
|
49 |
|
50 def __send(self, fx, fn, data): |
|
51 """ |
|
52 Private method to send a job response back to the BackgroundService. |
|
53 |
|
54 @param fx remote function name to execute (str) |
|
55 @param fn filename for identification (str) |
|
56 @param data return value(s) (any basic datatype) |
|
57 """ |
|
58 packedData = json.dumps([fx, fn, data]) |
|
59 if sys.version_info[0] == 3: |
|
60 packedData = bytes(packedData, 'utf-8') |
|
61 header = struct.pack( |
|
62 b'!II', len(packedData), adler32(packedData) & 0xffffffff) |
|
63 self.connection.sendall(header) |
|
64 self.connection.sendall(packedData) |
|
65 |
|
66 def run(self): |
|
67 """ |
|
68 Implement the main loop of the client. |
|
69 """ |
|
70 while True: |
|
71 try: |
|
72 header = self.connection.recv(8) # __IGNORE_EXCEPTION__ |
|
73 except socket.timeout: |
|
74 continue |
|
75 except socket.error: |
|
76 # Leave main loop if connection was closed. |
|
77 break |
|
78 # Leave main loop if connection was closed. |
|
79 if not header: |
|
80 break |
|
81 |
|
82 length, datahash = struct.unpack(b'!II', header) |
|
83 |
|
84 packedData = b'' |
|
85 while len(packedData) < length: |
|
86 packedData += self.connection.recv(length - len(packedData)) |
|
87 |
|
88 assert adler32(packedData) & 0xffffffff == datahash, \ |
|
89 'Hashes not equal' |
|
90 if sys.version_info[0] == 3: |
|
91 packedData = packedData.decode('utf-8') |
|
92 fx, fn, data = json.loads(packedData) |
|
93 if fx == 'syntax': |
|
94 ret = SyntaxCheck.syntaxAndPyflakesCheck(fn, *data) |
|
95 elif fx == 'style': |
|
96 print(data) |
|
97 elif fx == 'indent': |
|
98 pass |
|
99 else: |
|
100 continue |
|
101 |
|
102 self.__send(fx, fn, ret) |
|
103 |
|
104 self.connection.close() |
|
105 sys.exit() |
|
106 |
|
107 def __unhandled_exception(self, exctype, excval, exctb): |
|
108 """ |
|
109 Private method called to report an uncaught exception. |
|
110 |
|
111 @param exctype the type of the exception |
|
112 @param excval data about the exception |
|
113 @param exctb traceback for the exception |
|
114 """ |
|
115 # TODO: Wrap arguments so they can be serialized by JSON |
|
116 self.__send( |
|
117 'exception', '?', [str(exctype), str(excval), str(exctb)]) |
|
118 |
|
119 if __name__ == '__main__': |
|
120 if len(sys.argv) != 3: |
|
121 print('Host and port parameters are missing. Abort.') |
|
122 sys.exit(1) |
|
123 |
|
124 host, port = sys.argv[1:] |
|
125 backgroundClient = BackgroundClient(host, int(port)) |
|
126 # set the system exception handling function to ensure, that |
|
127 # we report on all unhandled exceptions |
|
128 sys.excepthook = backgroundClient._BackgroundClient__unhandled_exception |
|
129 # Start the main loop |
|
130 backgroundClient.run() |