Skip to content

Commit dfea27b

Browse files
committed
Version bump v2.0.0-beta.20
+ Using main thread for most things and sending thread for sending commands + Stream for error handler always using "sys.stderr" + ECMASCript: Fix extra paths name
1 parent 56ba0c0 commit dfea27b

File tree

5 files changed

+106
-88
lines changed

5 files changed

+106
-88
lines changed

codeintel/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '2.0.0-beta.19'
1+
__version__ = '2.0.0-beta.20'

codeintel/__main__.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,14 @@ def flush(self):
5555

5656

5757
class BinaryStream(object):
58-
def __init__(self, stream):
59-
self.stream = stream
58+
def __init__(self, stream=None):
59+
self._stream = stream
60+
61+
@property
62+
def stream(self):
63+
if self._stream is not None:
64+
return self._stream
65+
return sys.stderr
6066

6167
def encode(self, message):
6268
if isinstance(message, six.text_type):
@@ -94,7 +100,7 @@ class Shell(cmdln.Cmdln):
94100
def __init__(self, *args, **kwargs):
95101
cmdln.Cmdln.__init__(self, *args, **kwargs)
96102

97-
logging.basicConfig(stream=TextStream(sys.stderr))
103+
logging.basicConfig(stream=TextStream())
98104

99105
# Don't redirect output
100106
os.environ["KOMODO_VERBOSE"] = "1"
@@ -197,19 +203,14 @@ def set_verbosity(self, option, opt_str, value, parser):
197203
logging.getLogger('codeintel').setLevel(logging.DEBUG)
198204

199205
def set_log_file(self, option, opt_str, value, parser):
200-
if value in ('stdout', '/dev/stdout'):
201-
stream = sys.stdout
202-
elif value in ('stderr', '/dev/stderr'):
203-
stream = sys.stderr
204-
else:
206+
if value not in ('stdout', '/dev/stdout', 'stderr', '/dev/stderr'):
205207
log_dir = os.path.dirname(value)
206208
if not os.path.exists(log_dir):
207209
os.makedirs(log_dir)
208210
stream = open(value, 'wt')
209-
logging.getLogger().addHandler(logging.StreamHandler(stream=TextStream(stream)))
210-
# XXX marky horrible ugly hack
211-
sys.stderr = stream
212-
sys.stdout = stream
211+
# XXX marky horrible ugly hack
212+
sys.stderr = stream
213+
sys.stdout = stream
213214

214215
def get_optparser(self):
215216
optparser = cmdln.Cmdln.get_optparser(self)

codeintel/client.py

Lines changed: 85 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,19 @@
5151
PRIORITY_BACKGROUND = 4 # info may be needed sometime
5252

5353
logger_name = 'CodeIntel.codeintel'
54+
logger_level = logging.INFO # INFO
5455

55-
logging.getLogger(logger_name).setLevel(logging.INFO) # INFO
56+
logger = logging.getLogger(logger_name)
57+
logger.setLevel(logger_level)
5658

5759

5860
class CodeIntel(object):
59-
def __init__(self):
61+
def __init__(self, main_thread_runner):
62+
"""
63+
main_thread_runner - Must be a function receiving a single parameter,
64+
a function to be executed in the main thread.
65+
"""
66+
self._main_thread_runner = main_thread_runner
6067
self.log = logging.getLogger(logger_name + '.' + self.__class__.__name__)
6168
self.mgr = None
6269
self._mgr_lock = threading.Lock()
@@ -151,7 +158,7 @@ def activate(self, reset_db_as_necessary=False, codeintel_command=None, oop_mode
151158

152159
@property
153160
def enabled(self):
154-
return self._enabled
161+
return self._enabled and self.mgr and self.mgr.is_alive()
155162

156163
def deactivate(self):
157164
with self._mgr_lock:
@@ -449,6 +456,7 @@ def __init__(self, service, progress_callback=None, shutdown_callback=None, code
449456
if env is not None:
450457
self.env = env
451458
self._state_condvar = threading.Condition()
459+
self._discard_time = time.time()
452460
self.requests = {} # keyed by request id; value is tuple (callback, request data, time sent) requests will time out at some point...
453461
self.unsent_requests = queue.Queue()
454462
threading.Thread.__init__(self, name="CodeIntel Manager Thread")
@@ -469,26 +477,29 @@ def start(self, reset_db_as_necessary=False):
469477

470478
def shutdown(self):
471479
"""Abort any outstanding requests and shut down gracefully"""
472-
self.abort()
473480
if self.state is CodeIntelManager.STATE_DESTROYED:
474481
return # already dead
482+
self.abort()
483+
self.quit()
475484
if not self.pipe:
476485
# not quite dead, but already disconnected... ungraceful shutdown
477486
self.kill()
478487
return
479-
self._send(command='quit', callback=self.do_quit)
480-
self.state = CodeIntelManager.STATE_QUITTING
481488

482489
def abort(self):
483490
"""Abort all running requests"""
484491
for req in list(self.requests.keys()):
485492
self._abort.add(req)
486-
self._send(
493+
self.send(
487494
command='abort',
488495
id=req,
489496
callback=lambda request, response: None,
490497
)
491498

499+
def quit(self):
500+
self.send(command='quit', callback=self.do_quit)
501+
self.state = CodeIntelManager.STATE_QUITTING
502+
492503
def close(self):
493504
try:
494505
self.pipe.close()
@@ -739,10 +750,10 @@ def initialization_completed():
739750
self._send_request_thread.start()
740751
update("CodeIntel ready.", state=CodeIntelManager.STATE_READY)
741752

742-
self._send(callback=get_cpln_langs, command='get-languages', type='cpln')
743753
self._send(callback=get_citadel_langs, command='get-languages', type='citadel')
744754
self._send(callback=get_xml_langs, command='get-languages', type='xml')
745755
self._send(callback=get_stdlib_langs, command='get-languages', type='stdlib-supported')
756+
self._send(callback=get_cpln_langs, command='get-languages', type='cpln')
746757

747758
self.set_global_environment(self.env, self.prefs)
748759

@@ -756,7 +767,7 @@ def update_callback(response):
756767
def set_global_environment(self, env, prefs):
757768
self.env = env
758769
self.prefs = [prefs] if isinstance(prefs, dict) else prefs
759-
self._send(
770+
self.send(
760771
command='set-environment',
761772
env=self.env,
762773
prefs=self.prefs,
@@ -768,7 +779,7 @@ def get_available_catalogs(request, response):
768779
self.available_catalogs = response.get('catalogs', [])
769780
if update_callback:
770781
update_callback(response)
771-
self._send(callback=get_available_catalogs, command='get-available-catalogs')
782+
self.send(callback=get_available_catalogs, command='get-available-catalogs')
772783

773784
def send(self, callback=None, **kwargs):
774785
"""Public API for sending a request.
@@ -783,11 +794,12 @@ def send(self, callback=None, **kwargs):
783794

784795
def _send_queued_requests(self):
785796
"""Worker to send unsent requests"""
786-
while True:
787-
with self._state_condvar:
788-
if self.state is CodeIntelManager.STATE_DESTROYED:
789-
break # Manager already shut down
790-
if self.state is not CodeIntelManager.STATE_READY:
797+
798+
self.log.info("%s thread started..." % threading.current_thread().name)
799+
800+
while self.state not in (CodeIntelManager.STATE_QUITTING, CodeIntelManager.STATE_DESTROYED):
801+
if self.state is not CodeIntelManager.STATE_READY:
802+
with self._state_condvar:
791803
self._state_condvar.wait()
792804
continue # wait...
793805
callback, kwargs = self.unsent_requests.get()
@@ -796,6 +808,8 @@ def _send_queued_requests(self):
796808
break
797809
self._send(callback, **kwargs)
798810

811+
self.log.info("%s thread ended!" % threading.current_thread().name)
812+
799813
def _send(self, callback=None, **kwargs):
800814
"""
801815
Private API for sending; ignores the current state of the manager and
@@ -804,8 +818,6 @@ def _send(self, callback=None, **kwargs):
804818
calling thread until the data has been written (though possibly not yet
805819
received on the other end).
806820
"""
807-
if not self.pipe or self.state is CodeIntelManager.STATE_QUITTING:
808-
return # Nope, eating all commands during quit
809821
req_id = hex(self._next_id)
810822
kwargs['req_id'] = req_id
811823
text = json.dumps(kwargs, separators=(',', ':'))
@@ -833,17 +845,16 @@ def run(self):
833845
assert threading.current_thread().name != "MainThread", \
834846
"CodeIntelManager.run should run on background thread!"
835847

836-
self.log.debug("CodeIntelManager thread started...")
848+
self.log.info("%s thread started..." % threading.current_thread().name)
837849

838-
while True:
850+
while self.state not in (CodeIntelManager.STATE_QUITTING, CodeIntelManager.STATE_DESTROYED):
839851
ok = False
840852

841853
self.init_child()
842854
if not self.proc:
843855
break # init child failed
844856

845857
first_buf = True
846-
discard_time = 0.0
847858
try:
848859
buf = b''
849860
while self.proc and self.pipe:
@@ -875,20 +886,6 @@ def run(self):
875886
raise ValueError("Invalid frame length character: %r" % ch)
876887
buf += ch
877888

878-
now = time.time()
879-
if now - discard_time > 60: # discard some stale results
880-
for req_id, (callback, request, sent_time) in list(self.requests.items()):
881-
if sent_time < now - 5 * 60:
882-
# sent 5 minutes ago - it's irrelevant now
883-
try:
884-
if callback:
885-
callback(request, {})
886-
except Exception as e:
887-
self.log.error("Failed timing out request")
888-
else:
889-
self.log.debug("Discarding request %r", request)
890-
del self.requests[req_id]
891-
892889
except Exception as e:
893890
if self.state in (CodeIntelManager.STATE_QUITTING, CodeIntelManager.STATE_DESTROYED):
894891
self.log.debug("IOError in codeintel during shutdown; ignoring")
@@ -902,43 +899,63 @@ def run(self):
902899
if not ok:
903900
time.sleep(3)
904901

905-
self.log.debug("CodeIntelManager thread ended!")
902+
self.log.info("%s thread ended!" % threading.current_thread().name)
906903

907904
def handle(self, response):
908905
"""Handle a response from the codeintel process"""
909-
self.log.debug("handling: %r", response)
910-
req_id = response.get('req_id')
911-
callback, request, sent_time = self.requests.get(req_id, (None, None, None))
912-
request_command = request.get('command', '') if request else None
913-
response_command = response.get('command', request_command)
914-
if req_id is None or request_command != response_command:
915-
# unsolicited response, look for a handler
916-
try:
917-
if not response_command:
918-
self.log.error("No 'command' in response %r", response)
919-
raise ValueError("Invalid response frame %r" % response)
920-
meth = getattr(self, 'do_' + response_command.replace('-', '_'), None)
921-
if not meth:
922-
self.log.error("Unknown command %r, response %r", response_command, response)
923-
raise ValueError("Unknown unsolicited response \"%s\"" % response_command)
924-
meth(response)
925-
except Exception as e:
926-
self.log.error("Error handling unsolicited response")
927-
return
928-
if not request:
929-
self.log.error("Discard response for unknown request %s (command %s): have %s",
930-
req_id, response_command or '%r' % response, sorted(self.requests.keys()))
931-
return
932-
self.log.debug("Request %s (command %s) took %0.2f seconds", req_id, request_command or '<unknown>', time.time() - sent_time)
933-
if 'success' in response:
934-
# remove completed request
935-
self.log.debug("Removing completed request %s", req_id)
936-
del self.requests[req_id]
937-
else:
938-
# unfinished response; update the sent time so it doesn't time out
939-
self.requests[req_id] = (callback, request, time.time())
940-
if callback:
941-
callback(request, response)
906+
def _handle():
907+
assert threading.current_thread().name == "MainThread", \
908+
"CodeIntelManager.handle() should run on main thread!"
909+
910+
now = time.time()
911+
if now - self._discard_time > 60: # (not so often) discard some stale results
912+
self._discard_time = now
913+
for req_id, (callback, request, sent_time) in list(self.requests.items()):
914+
if sent_time < now - 5 * 60:
915+
# sent 5 minutes ago - it's irrelevant now
916+
try:
917+
if callback:
918+
callback(request, {})
919+
except Exception as e:
920+
self.log.error("Failed timing out request")
921+
else:
922+
self.log.debug("Discarding request %r", request)
923+
del self.requests[req_id]
924+
925+
self.log.debug("handling: %r", response)
926+
req_id = response.get('req_id')
927+
callback, request, sent_time = self.requests.get(req_id, (None, None, None))
928+
request_command = request.get('command', '') if request else None
929+
response_command = response.get('command', request_command)
930+
if req_id is None or request_command != response_command:
931+
# unsolicited response, look for a handler
932+
try:
933+
if not response_command:
934+
self.log.error("No 'command' in response %r", response)
935+
raise ValueError("Invalid response frame %r" % response)
936+
meth = getattr(self, 'do_' + response_command.replace('-', '_'), None)
937+
if not meth:
938+
self.log.error("Unknown command %r, response %r", response_command, response)
939+
raise ValueError("Unknown unsolicited response \"%s\"" % response_command)
940+
meth(response)
941+
except Exception as e:
942+
self.log.error("Error handling unsolicited response")
943+
return
944+
if not request:
945+
self.log.error("Discard response for unknown request %s (command %s): have %s",
946+
req_id, response_command or '%r' % response, sorted(self.requests.keys()))
947+
return
948+
self.log.debug("Request %s (command %s) took %0.2f seconds", req_id, request_command or '<unknown>', time.time() - sent_time)
949+
if 'success' in response:
950+
# remove completed request
951+
self.log.debug("Removing completed request %s", req_id)
952+
del self.requests[req_id]
953+
else:
954+
# unfinished response; update the sent time so it doesn't time out
955+
self.requests[req_id] = (callback, request, time.time())
956+
if callback:
957+
callback(request, response)
958+
self.service._main_thread_runner(_handle) # Do handling in main thread
942959

943960
def do_scan_complete(self, response):
944961
"""Scan complete unsolicited response"""

codeintel/codeintel2/lang_ecma.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@ class ECMAScriptLangIntel(CitadelLangIntel,
218218
PythonCITDLExtractorMixin):
219219
lang = lang
220220
interpreterPrefName = "node"
221-
extraPathsPrefName = "esExtraPaths"
222-
excludePathsPrefName = "esExcludePaths"
221+
extraPathsPrefName = "ecmascriptExtraPaths"
222+
excludePathsPrefName = "ecmascriptExcludePaths"
223223

224224
# Define the trigger chars we use, used by ProgLangTriggerIntelMixin
225225
trg_chars = tuple(".(,@'\" ")

codeintel/codeintel2/oop/driver.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -508,15 +508,15 @@ def start(self):
508508
self.quit = True
509509
break
510510
if ch == b'{':
511-
size = int(buf, 10)
511+
length = int(buf, 10)
512512
try:
513513
buf = ch
514-
while len(buf) < size:
515-
last_size = len(buf)
516-
buf += self.fd_in.read(size - len(buf))
517-
if len(buf) == last_size:
514+
while len(buf) < length:
515+
data = self.fd_in.read(length - len(buf))
516+
if not data:
518517
# nothing read, EOF
519518
raise IOError("Failed to read frame from socket")
519+
buf += data
520520
except IOError:
521521
log.debug("Failed to read frame data, assuming connection died")
522522
self.quit = True

0 commit comments

Comments
 (0)