diff --git a/README.md b/README.md
index e93d880a99d8aa0e9d0323172a236494a900f3c9..d194c52fb860c7cf8d55063ac94559d3b28e317d 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
-# cutelog - GUI for Python's logging module
+# cutelog – GUI for Python's logging module
+[](https://pypi.python.org/pypi/cutelog)
This is a graphical log viewer for Python's standard logging module.
It can be targeted as a SocketHandler with no additional setup (see [Usage](#usage)).
diff --git a/cutelog/config.py b/cutelog/config.py
index fa27727c4bb04d88bf4fafa00a7dd404799f4a00..de3a83da437dc8296887911087e474c57918b426 100644
--- a/cutelog/config.py
+++ b/cutelog/config.py
@@ -2,14 +2,11 @@ import logging
import os
import sys
from collections import namedtuple
-from platform import python_version
from pkg_resources import get_distribution, resource_filename
from PyQt5.QtCore import (QT_VERSION_STR, QCoreApplication, QFile, QObject,
QSettings, Qt, pyqtSignal)
-# from PyQt5.QtGui import QFont
-
if sys.platform == 'win':
DEFAULT_FONT = 'MS Shell Dlg 2'
@@ -19,9 +16,6 @@ else:
DEFAULT_FONT = 'Sans'
-# @Future: when the time is right, remove everything related to this mess:
-PY35_COMPAT = python_version().startswith('3.5')
-
# @Future: when Qt 5.6 becomes standard, remove this:
QT_VER = QT_VERSION_STR.split('.')
if QT_VER[0] == '5' and int(QT_VER[1]) < 6:
@@ -93,7 +87,6 @@ class Config(QObject):
self.benchmark_interval = None
self.update_attributes()
- # self.save_options()
def __getitem__(self, name):
# self.log.debug('Getting "{}"'.format(name))
@@ -258,16 +251,11 @@ def init_logging():
log = logging.getLogger('CL')
term_handler = logging.StreamHandler()
- if PY35_COMPAT:
- exc = ImportError
- else:
- exc = ModuleNotFoundError
-
try:
import colorlog
fmt = colorlog.ColoredFormatter('%(asctime)s %(log_color)s[%(name)12s:%(lineno)3s'
' %(funcName)18s ]\t%(levelname)-.6s %(message)s')
- except exc:
+ except ImportError:
fmt = logging.Formatter('%(asctime)s [%(name)12s:%(lineno)3s '
'%(funcName)18s ]\t%(levelname)-.6s %(message)s')
diff --git a/cutelog/listener.py b/cutelog/listener.py
index a032b36eb0da500edb4f5d7f441abbb752986779..151588999ea191a60fc87d61c1fb8dd787f64caa 100644
--- a/cutelog/listener.py
+++ b/cutelog/listener.py
@@ -26,7 +26,7 @@ class LogServer(QTcpServer):
self.host = QHostAddress(self.host)
self.benchmark = CONFIG['benchmark']
- self.threads = {} # socketDescriptor -> LogConnection
+ self.threads = {} # int(socketDescriptor) -> LogConnection
self.connections = 0
def start(self):
@@ -61,20 +61,25 @@ class LogServer(QTcpServer):
new_conn.start()
self.threads[int(socketDescriptor)] = new_conn
- def close_server(self):
+ def close_server(self, wait=True):
self.log.debug('Closing the server')
+ self.main_window.set_status('Stopping the server...')
self.close()
- self.stop_all_connections()
-
- def stop_all_connections(self):
- self.log.debug('Waiting for connection threads to stop')
- for _, thread in self.threads.items():
- thread.exit()
- for _, thread in self.threads.items():
- if not thread.wait(1000):
- self.log.error('Thread "{}" didn\'t stop in time, terminating...'.format(thread))
- thread.terminate()
- self.log.error('Thread "{}" terminated'.format(thread))
+ if wait:
+ self.wait_connections_stopped()
+ self.main_window.set_status('Server has stopped')
+
+ def wait_connections_stopped(self):
+ self.log.debug('Waiting for {} connections threads to stop'.format(len(self.threads)))
+ to_wait = self.threads.copy() # to protect against changes during iteration
+ for thread in to_wait.values():
+ try:
+ if not thread.wait(1000):
+ self.log.error('Thread "{}" didn\'t stop in time, terminating'.format(thread))
+ thread.terminate()
+ self.log.error('Thread "{}" terminated'.format(thread))
+ except RuntimeError: # happens when thread has been deleted before we got to it
+ self.log.debug('Thread {} has been deleted already'.format(thread))
self.log.debug('All connections stopped')
def cleanup_connection(self, socketDescriptor):
@@ -99,6 +104,10 @@ class LogConnection(QThread):
self.stop_signal = stop_signal
self.tab_closed = tab_closed
+ def __repr__(self):
+ return "{}(name={}, socketDescriptor={})".format(self.__class__.__name__, self.name,
+ self.socketDescriptor)
+
def run(self):
self.log.debug('Connection "{}" is starting'.format(self.name))
@@ -133,6 +142,7 @@ class LogConnection(QThread):
data = pickle.loads(data)
record = logging.makeLogRecord(data)
self.new_record.emit(record)
+
sock.disconnectFromHost()
sock.close()
self.connection_finished.emit(int(self.socketDescriptor))
diff --git a/cutelog/main_window.py b/cutelog/main_window.py
index 071b759d12b44b4b87544bcdd1e23f05360140f8..dd9f16f7806231b5a2183c33754b7fe673f2e354 100644
--- a/cutelog/main_window.py
+++ b/cutelog/main_window.py
@@ -183,11 +183,14 @@ class MainWindow(*MainWindowBase):
self.server.start()
self.server_running = True
await self.stop_signal.wait()
- self.server.close_server()
+ self.server.close_server(wait=False)
+
+ # executor is used here because stopping threads can take some time and stall the loop
+ await self.loop.run_in_executor(None, self.server.wait_connections_stopped)
+
self.server_running = False
self.log.debug('Run got the stop_signal with reason {}'.format(self.stop_reason))
if self.stop_reason == 'restart':
- # await self.wait_server_closed()
continue
elif self.stop_reason == 'pause':
await self.start_server_again.wait()
diff --git a/setup.py b/setup.py
index 953500b970584428653c922e827c5c9e101e0986..fa521a3aaf5dba854d0c5a6032b10c2a3d8605f7 100644
--- a/setup.py
+++ b/setup.py
@@ -4,7 +4,7 @@ from os.path import dirname, join
from setuptools import setup
-VERSION = '1.1.0'
+VERSION = '1.1.1'
# def build_qt_resources():