diff --git a/.versioneer-lookup b/.versioneer-lookup index 5e4abd00..dd99c543 100644 --- a/.versioneer-lookup +++ b/.versioneer-lookup @@ -22,10 +22,10 @@ maintenance 1.3.6 1a6dbb3f4a5bef857cdeb13c031b9deca2cf30a2 pep440-dev fix/.* 1.3.6 1a6dbb3f4a5bef857cdeb13c031b9deca2cf30a2 pep440-dev improve/.* 1.3.6 1a6dbb3f4a5bef857cdeb13c031b9deca2cf30a2 pep440-dev -# staging/maintenance is currently the branch for preparation of 1.3.5rc3 +# staging/maintenance is currently the branch for preparation of 1.3.5rc4 # so is regressionfix/... -staging/maintenance 1.3.5rc3 1f9fe8c8689f727566ae92707fe1e3f40064dcdf pep440-dev -regressionfix/.* 1.3.5rc3 1f9fe8c8689f727566ae92707fe1e3f40064dcdf pep440-dev +staging/maintenance 1.3.5rc4 679674df2282af0c4500367fa93864c6defa3802 pep440-dev +regressionfix/.* 1.3.5rc4 679674df2282af0c4500367fa93864c6defa3802 pep440-dev # every other branch is a development branch and thus gets resolved to 1.4.0-dev for now .* 1.4.0 7f5d03d0549bcbd26f40e7e4a3297ea5204fb1cc pep440-dev diff --git a/CHANGELOG.md b/CHANGELOG.md index 931647a7..71a43c2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # OctoPrint Changelog +## 1.3.5rc3 (2017-08-25) + +### Bug fixes + + * [#2059](https://github.com/foosel/OctoPrint/issues/2059) - Fix an issue causing the new temperature controls to wrap on touch enabled devices when the temperature dropdown is opened. + * [#2090](https://github.com/foosel/OctoPrint/issues/2090) - Fix an issue causing an aborted server startup under Windows if the timing is just right. + * Fix an issue causing rollover of `serial.log` to fail under Windows. + +([Commits](https://github.com/foosel/OctoPrint/compare/1.3.5rc2...1.3.5rc3)) + ## 1.3.5rc2 (2017-07-27) ### Bug fixes diff --git a/SUPPORTERS.md b/SUPPORTERS.md index 6cb81dcb..eb770119 100644 --- a/SUPPORTERS.md +++ b/SUPPORTERS.md @@ -46,6 +46,7 @@ thanks to everyone who contributed! * Masayoshi Mitsui * Michael Aumock * Miles Flavel + * mitchell hirsch * Mohammed khorakiwala * Noe Ruiz * Patrick McGinnis @@ -66,4 +67,4 @@ thanks to everyone who contributed! * Timeshell.ca * Trent Shumay -and 1117 more wonderful people pledging on the [Patreon campaign](https://patreon.com/foosel)! \ No newline at end of file +and 1130 more wonderful people pledging on the [Patreon campaign](https://patreon.com/foosel)! \ No newline at end of file diff --git a/src/octoprint/__init__.py b/src/octoprint/__init__.py index 7d6d07ce..785a5e5c 100644 --- a/src/octoprint/__init__.py +++ b/src/octoprint/__init__.py @@ -194,7 +194,8 @@ def init_logging(settings, use_logging_file=True, logging_file=None, default_con "level": "DEBUG", "formatter": "serial", "backupCount": 3, - "filename": os.path.join(settings.getBaseFolder("logs"), "serial.log") + "filename": os.path.join(settings.getBaseFolder("logs"), "serial.log"), + "delay": True } }, "loggers": { diff --git a/src/octoprint/events.py b/src/octoprint/events.py index f8c35558..b789f052 100644 --- a/src/octoprint/events.py +++ b/src/octoprint/events.py @@ -134,9 +134,11 @@ class EventManager(object): self._registeredListeners = collections.defaultdict(list) self._logger = logging.getLogger(__name__) + self._startup_signaled = False self._shutdown_signaled = False self._queue = queue.Queue() + self._held_back = queue.Queue() self._worker = threading.Thread(target=self._work) self._worker.daemon = True @@ -163,7 +165,8 @@ class EventManager(object): octoprint.plugin.call_plugin(octoprint.plugin.types.EventHandlerPlugin, "on_event", - args=(event, payload)) + args=(event, payload), + initialized=True) self._logger.info("Event loop shut down") except: self._logger.exception("Ooops, the event bus worker loop crashed") @@ -179,7 +182,30 @@ class EventManager(object): payload being a payload object specific to the event. """ - self._queue.put((event, payload)) + send_held_back = False + if event == Events.STARTUP: + self._logger.info("Processing startup event, this is our first event") + self._startup_signaled = True + send_held_back = True + + self._enqueue(event, payload) + + if send_held_back: + self._logger.info("Adding {} events to queue that " + "were held back before startup event".format(self._held_back.qsize())) + while True: + try: + self._queue.put(self._held_back.get(block=False)) + except queue.Empty: + break + + def _enqueue(self, event, payload): + if self._startup_signaled: + q = self._queue + else: + q = self._held_back + + q.put((event, payload)) if event == Events.UPDATED_FILES and "type" in payload and payload["type"] == "printables": # when sending UpdatedFiles with type "printables", also send another event with deprecated type "gcode" @@ -187,7 +213,7 @@ class EventManager(object): import copy legacy_payload = copy.deepcopy(payload) legacy_payload["type"] = "gcode" - self._queue.put((event, legacy_payload)) + q.put((event, legacy_payload)) def subscribe(self, event, callback): """ diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index 61c3bc93..dbc87c19 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -221,14 +221,13 @@ class Server(object): ### IMPORTANT! ### - ### Do not start any subprocesses until the intermediary server shuts down again or they WILL inherit the - ### open port and prevent us from firing up Tornado later. Thanks to close_fds not being available on Popen - ### on Windows if you also want to be able to redirect stdout/stderr/stdin and fnctl also not being available - ### we don't have a good way around this issue besides being careful not to spawn processes here. + ### Best do not start any subprocesses until the intermediary server shuts down again or they MIGHT inherit the + ### open port and prevent us from firing up Tornado later. ### - ### Which kinda sucks tbh. + ### The intermediary server's socket should have the CLOSE_EXEC flag (or its equivalent) set where possible, but + ### we can only do that if fcntl is availabel or we are on Windows, so better safe than sorry. ### - ### See also issue #2035 + ### See also issues #2035 and #2090 # then initialize the plugin manager pluginManager.reload_plugins(startup=True, initialize_implementations=False) @@ -1382,10 +1381,11 @@ class Server(object): bind_and_activate=False) # if possible, make sure our socket's port descriptor isn't handed over to subprocesses - if fcntl is not None and hasattr(fcntl, "FD_CLOEXEC"): - flags = fcntl.fcntl(self._intermediary_server.socket, fcntl.F_GETFD) - flags |= fcntl.FD_CLOEXEC - fcntl.fcntl(self._intermediary_server.socket, fcntl.F_SETFD, flags) + from octoprint.util.platform import set_close_exec + try: + set_close_exec(self._intermediary_server.fileno()) + except: + self._logger.exception("Error while attempting to set_close_exec on intermediary server socket") # then bind the server and have it serve our handler until stopped try: @@ -1395,7 +1395,13 @@ class Server(object): self._intermediary_server.server_close() raise - thread = threading.Thread(target=self._intermediary_server.serve_forever) + def serve(): + try: + self._intermediary_server.serve_forever() + except: + self._logger.exception("Error in intermediary server") + + thread = threading.Thread(target=serve) thread.daemon = True thread.start() diff --git a/src/octoprint/static/js/lib/bootstrap/bootstrap.js b/src/octoprint/static/js/lib/bootstrap/bootstrap.js index 5ca3cc94..94c4b9f3 100644 --- a/src/octoprint/static/js/lib/bootstrap/bootstrap.js +++ b/src/octoprint/static/js/lib/bootstrap/bootstrap.js @@ -687,7 +687,11 @@ if (!isActive) { if ('ontouchstart' in document.documentElement) { // if mobile we we use a backdrop because click events don't delegate - $('
').insertBefore($(this)).on('click', clearMenus) + + // PATCH by foosel based on CapnBry's suggestion - see foosel/OctoPrint#2059 for details + //$('').insertBefore($(this)).on('click', clearMenus) + $('').insertAfter($(this)).on('click', clearMenus) + // /PATCH } $parent.toggleClass('open') } diff --git a/src/octoprint/util/platform/__init__.py b/src/octoprint/util/platform/__init__.py new file mode 100644 index 00000000..529c2676 --- /dev/null +++ b/src/octoprint/util/platform/__init__.py @@ -0,0 +1,41 @@ +# coding=utf-8 +from __future__ import absolute_import, division, print_function + +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' +__copyright__ = "Copyright (C) 2017 The OctoPrint Project - Released under terms of the AGPLv3 License" + +import sys + +try: + import fcntl +except ImportError: + fcntl = None + +# set_close_exec + +if fcntl is not None and hasattr(fcntl, "FD_CLOEXEC"): + def set_close_exec(handle): + flags = fcntl.fcntl(handle, fcntl.F_GETFD) + flags |= fcntl.FD_CLOEXEC + fcntl.fcntl(handle, fcntl.F_SETFD, flags) + +elif sys.platform == "win32": + def set_close_exec(handle): + import ctypes + import ctypes.wintypes + + # see https://msdn.microsoft.com/en-us/library/ms724935(v=vs.85).aspx + SetHandleInformation = ctypes.windll.kernel32.SetHandleInformation + SetHandleInformation.argtypes = (ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD) + SetHandleInformation.restype = ctypes.c_bool + + HANDLE_FLAG_INHERIT = 0x00000001 + + result = SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0) + if not result: + raise ctypes.GetLastError() + +else: + def set_close_exec(handle): + # no-op + pass