diff --git a/setup.py b/setup.py index 71b81f0d..d2c98c6a 100644 --- a/setup.py +++ b/setup.py @@ -14,34 +14,31 @@ import octoprint_setuptools # Requirements for our application INSTALL_REQUIRES = [ - "flask>=0.9,<0.11", + "flask>=0.11,<0.12", + "werkzeug>=0.11.1,<0.12", + "tornado>=4.4.2,<4.5", "Jinja2>=2.8,<2.9", # Jinja 2.9 has breaking changes WRT template scope - we can't # guarantee backwards compatibility for plugins and such with that # version, hence we need to pin to a lower version for now. See #1697 - "werkzeug>=0.8.3,<0.9", - "tornado==4.0.2", # pinned for now, we need to migrate to a newer tornado, but due - # to some voodoo needed to get large streamed uploads and downloads - # to work that is probably not completely straightforward and therefore - # something for post-1.3.0-stable release "sockjs-tornado>=1.0.2,<1.1", - "PyYAML>=3.10,<3.11", - "Flask-Login>=0.2.2,<0.3", - "Flask-Principal>=0.3.5,<0.4", - "Flask-Babel>=0.9,<0.10", - "Flask-Assets>=0.10,<0.11", + "PyYAML>=3.12,<3.13", + "Flask-Login>=0.3,<0.4", + "Flask-Principal>=0.4,<0.5", + "Flask-Babel>=0.11,<0.12", + "Flask-Assets>=0.12,<0.13", "markdown>=2.6.4,<2.7", - "pyserial>=2.7,<2.8", + "pyserial>=3.1.1,<3.2", "netaddr>=0.7.17,<0.8", "watchdog>=0.8.3,<0.9", "sarge>=0.1.4,<0.2", "netifaces>=0.10,<0.11", "pylru>=1.0.9,<1.1", - "rsa>=3.2,<3.3", - "pkginfo>=1.2.1,<1.3", - "requests>=2.7,<2.8", - "semantic_version>=2.4.2,<2.5", - "psutil>=3.2.1,<3.3", - "Click>=6.2,<6.3", + "rsa>=3.4,<3.5", + "pkginfo>=1.3.2,<1.4", + "requests>=2.11.1,<2.12", + "semantic_version>=2.6.0,<2.7", + "psutil>=4.3.1,<4.4", + "Click>=6.6,<6.7", "awesome-slugify>=1.6.5,<1.7", "feedparser>=5.2.1,<5.3", "chainmap>=1.0.2,<1.1", diff --git a/src/octoprint/plugin/types.py b/src/octoprint/plugin/types.py index 6f72856a..6b05c790 100644 --- a/src/octoprint/plugin/types.py +++ b/src/octoprint/plugin/types.py @@ -1375,7 +1375,7 @@ class SettingsPlugin(OctoPrintPlugin): :return: the current settings of the plugin, as a dictionary """ - from flask.ext.login import current_user + from flask_login import current_user import copy data = copy.deepcopy(self._settings.get_all_data(merged=True)) @@ -1420,8 +1420,8 @@ class SettingsPlugin(OctoPrintPlugin): else: node[key] = None - conditions = dict(user=lambda: current_user is not None and not current_user.is_anonymous(), - admin=lambda: current_user is not None and not current_user.is_anonymous() and current_user.is_admin(), + conditions = dict(user=lambda: current_user is not None and not current_user.is_anonymous, + admin=lambda: current_user is not None and not current_user.is_anonymous and current_user.is_admin, never=lambda: False) for level, condition in conditions.items(): diff --git a/src/octoprint/plugins/announcements/__init__.py b/src/octoprint/plugins/announcements/__init__.py index 0ae7d9a5..b4ef690c 100644 --- a/src/octoprint/plugins/announcements/__init__.py +++ b/src/octoprint/plugins/announcements/__init__.py @@ -20,7 +20,7 @@ import flask from octoprint.server import admin_permission from octoprint.server.util.flask import restricted_access, with_revalidation_checking, check_etag -from flask.ext.babel import gettext +from flask_babel import gettext class AnnouncementPlugin(octoprint.plugin.AssetPlugin, octoprint.plugin.SettingsPlugin, diff --git a/src/octoprint/plugins/corewizard/__init__.py b/src/octoprint/plugins/corewizard/__init__.py index 7ce014be..f5475239 100644 --- a/src/octoprint/plugins/corewizard/__init__.py +++ b/src/octoprint/plugins/corewizard/__init__.py @@ -9,7 +9,7 @@ __copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms import octoprint.plugin -from flask.ext.babel import gettext +from flask_babel import gettext class CoreWizardPlugin(octoprint.plugin.AssetPlugin, diff --git a/src/octoprint/plugins/pluginmanager/__init__.py b/src/octoprint/plugins/pluginmanager/__init__.py index 90605517..523187b4 100644 --- a/src/octoprint/plugins/pluginmanager/__init__.py +++ b/src/octoprint/plugins/pluginmanager/__init__.py @@ -15,7 +15,7 @@ from octoprint.server import admin_permission, VERSION from octoprint.util.pip import LocalPipCaller, UnknownPip from flask import jsonify, make_response -from flask.ext.babel import gettext +from flask_babel import gettext import logging import sarge diff --git a/src/octoprint/plugins/softwareupdate/__init__.py b/src/octoprint/plugins/softwareupdate/__init__.py index 5fa644fc..61558718 100644 --- a/src/octoprint/plugins/softwareupdate/__init__.py +++ b/src/octoprint/plugins/softwareupdate/__init__.py @@ -522,7 +522,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, ##~~ TemplatePlugin API def get_template_configs(self): - from flask.ext.babel import gettext + from flask_babel import gettext return [ dict(type="settings", name=gettext("Software Update")) ] @@ -842,7 +842,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, result = dict(check) if target == "octoprint": - from flask.ext.babel import gettext + from flask_babel import gettext result["displayName"] = check.get("displayName") if result["displayName"] is None: diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index f1d48477..abc717ff 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -8,10 +8,10 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms import uuid from sockjs.tornado import SockJSRouter from flask import Flask, g, request, session, Blueprint, Request, Response -from flask.ext.login import LoginManager, current_user -from flask.ext.principal import Principal, Permission, RoleNeed, identity_loaded, UserNeed -from flask.ext.babel import Babel, gettext, ngettext -from flask.ext.assets import Environment, Bundle +from flask_login import LoginManager, current_user +from flask_principal import Principal, Permission, RoleNeed, identity_loaded, UserNeed +from flask_babel import Babel, gettext, ngettext +from flask_assets import Environment, Bundle from babel import Locale from watchdog.observers import Observer from watchdog.observers.polling import PollingObserver @@ -89,9 +89,9 @@ def on_identity_loaded(sender, identity): return identity.provides.add(UserNeed(user.get_id())) - if user.is_user(): + if user.is_user: identity.provides.add(RoleNeed("user")) - if user.is_admin(): + if user.is_admin: identity.provides.add(RoleNeed("admin")) def load_user(id): @@ -170,8 +170,7 @@ class Server(object): self._logger = logging.getLogger(__name__) pluginManager = self._plugin_manager - # monkey patch a bunch of stuff - util.tornado.fix_ioloop_scheduling() + # monkey patch some stuff util.flask.enable_additional_translations(additional_folders=[self._settings.getBaseFolder("translations")]) # setup app diff --git a/src/octoprint/server/api/__init__.py b/src/octoprint/server/api/__init__.py index 35401e45..6d056fe0 100644 --- a/src/octoprint/server/api/__init__.py +++ b/src/octoprint/server/api/__init__.py @@ -10,8 +10,8 @@ import netaddr import sarge from flask import Blueprint, request, jsonify, abort, current_app, session, make_response, g -from flask.ext.login import login_user, logout_user, current_user -from flask.ext.principal import Identity, identity_changed, AnonymousIdentity +from flask_login import login_user, logout_user, current_user +from flask_principal import Identity, identity_changed, AnonymousIdentity import octoprint.util as util import octoprint.users @@ -62,7 +62,7 @@ def pluginData(name): return make_response("More than one api provider registered for {name}, can't proceed".format(name=name), 500) api_plugin = api_plugins[0] - if api_plugin.is_api_adminonly() and not current_user.is_admin(): + if api_plugin.is_api_adminonly() and not current_user.is_admin: return make_response("Forbidden", 403) response = api_plugin.on_api_get(request) @@ -89,7 +89,7 @@ def pluginCommand(name): if valid_commands is None: return make_response("Method not allowed", 405) - if api_plugin.is_api_adminonly() and not current_user.is_admin(): + if api_plugin.is_api_adminonly() and not current_user.is_admin: return make_response("Forbidden", 403) command, data, response = get_json_command_from_request(request, valid_commands) diff --git a/src/octoprint/server/api/languages.py b/src/octoprint/server/api/languages.py index e0749658..3d5f1f2c 100644 --- a/src/octoprint/server/api/languages.py +++ b/src/octoprint/server/api/languages.py @@ -26,7 +26,7 @@ from octoprint.server.util.flask import restricted_access from octoprint.plugin import plugin_manager -from flask.ext.babel import Locale +from flask_babel import Locale @api.route("/languages", methods=["GET"]) @restricted_access diff --git a/src/octoprint/server/api/settings.py b/src/octoprint/server/api/settings.py index 0276eb01..7ef37a35 100644 --- a/src/octoprint/server/api/settings.py +++ b/src/octoprint/server/api/settings.py @@ -8,7 +8,7 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms import logging from flask import request, jsonify, make_response -from flask.ext.login import current_user +from flask_login import current_user from werkzeug.exceptions import BadRequest from octoprint.events import eventManager, Events @@ -39,7 +39,7 @@ def _etag(lm=None): for key in sorted(plugin_settings.keys()): sorted_plugin_settings[key] = plugin_settings.get(key, dict()) - if current_user is not None and not current_user.is_anonymous(): + if current_user is not None and not current_user.is_anonymous: roles = sorted(current_user.roles) else: roles = [] diff --git a/src/octoprint/server/api/system.py b/src/octoprint/server/api/system.py index 058476d3..14a0e084 100644 --- a/src/octoprint/server/api/system.py +++ b/src/octoprint/server/api/system.py @@ -9,7 +9,7 @@ import logging import sarge from flask import request, make_response, jsonify, url_for -from flask.ext.babel import gettext +from flask_babel import gettext from octoprint.settings import settings as s diff --git a/src/octoprint/server/api/users.py b/src/octoprint/server/api/users.py index 73233f5e..fbee5c16 100644 --- a/src/octoprint/server/api/users.py +++ b/src/octoprint/server/api/users.py @@ -7,7 +7,7 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms from flask import request, jsonify, abort, make_response from werkzeug.exceptions import BadRequest -from flask.ext.login import current_user +from flask_login import current_user import octoprint.users as users @@ -72,7 +72,7 @@ def getUser(username): if not userManager.enabled: return jsonify(SUCCESS) - if current_user is not None and not current_user.is_anonymous() and (current_user.get_name() == username or current_user.is_admin()): + if current_user is not None and not current_user.is_anonymous and (current_user.get_name() == username or current_user.is_admin): user = userManager.findUser(username) if user is not None: return jsonify(user.asDict()) @@ -133,7 +133,7 @@ def changePasswordForUser(username): if not userManager.enabled: return jsonify(SUCCESS) - if current_user is not None and not current_user.is_anonymous() and (current_user.get_name() == username or current_user.is_admin()): + if current_user is not None and not current_user.is_anonymous and (current_user.get_name() == username or current_user.is_admin): if not "application/json" in request.headers["Content-Type"]: return make_response("Expected content-type JSON", 400) @@ -161,7 +161,7 @@ def getSettingsForUser(username): if not userManager.enabled: return jsonify(SUCCESS) - if current_user is None or current_user.is_anonymous() or (current_user.get_name() != username and not current_user.is_admin()): + if current_user is None or current_user.is_anonymous or (current_user.get_name() != username and not current_user.is_admin): return make_response("Forbidden", 403) try: @@ -175,7 +175,7 @@ def changeSettingsForUser(username): if not userManager.enabled: return jsonify(SUCCESS) - if current_user is None or current_user.is_anonymous() or (current_user.get_name() != username and not current_user.is_admin()): + if current_user is None or current_user.is_anonymous or (current_user.get_name() != username and not current_user.is_admin): return make_response("Forbidden", 403) try: @@ -195,7 +195,7 @@ def deleteApikeyForUser(username): if not userManager.enabled: return jsonify(SUCCESS) - if current_user is not None and not current_user.is_anonymous() and (current_user.get_name() == username or current_user.is_admin()): + if current_user is not None and not current_user.is_anonymous and (current_user.get_name() == username or current_user.is_admin): try: userManager.deleteApikey(username) except users.UnknownUser: @@ -211,7 +211,7 @@ def generateApikeyForUser(username): if not userManager.enabled: return jsonify(SUCCESS) - if current_user is not None and not current_user.is_anonymous() and (current_user.get_name() == username or current_user.is_admin()): + if current_user is not None and not current_user.is_anonymous and (current_user.get_name() == username or current_user.is_admin): try: apikey = userManager.generateApiKey(username) except users.UnknownUser: diff --git a/src/octoprint/server/util/flask.py b/src/octoprint/server/util/flask.py index 5f41d661..9fc0defd 100644 --- a/src/octoprint/server/util/flask.py +++ b/src/octoprint/server/util/flask.py @@ -8,9 +8,9 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms import tornado.web import flask -import flask.ext.login -import flask.ext.principal -import flask.ext.assets +import flask_login +import flask_principal +import flask_assets import webassets.updater import webassets.utils import functools @@ -40,7 +40,7 @@ def enable_additional_translations(default_locale="en", additional_folders=None) import os from flask import _request_ctx_stack from babel import support, Locale - import flask.ext.babel + import flask_babel if additional_folders is None: additional_folders = [] @@ -91,7 +91,7 @@ def enable_additional_translations(default_locale="en", additional_folders=None) return None translations = getattr(ctx, 'babel_translations', None) if translations is None: - locale = flask.ext.babel.get_locale() + locale = flask_babel.get_locale() translations = support.Translations() if str(locale) != default_locale: @@ -129,8 +129,8 @@ def enable_additional_translations(default_locale="en", additional_folders=None) ctx.babel_translations = translations return translations - flask.ext.babel.Babel.list_translations = fixed_list_translations - flask.ext.babel.get_translations = fixed_get_translations + flask_babel.Babel.list_translations = fixed_list_translations + flask_babel.get_translations = fixed_get_translations def fix_webassets_cache(): from webassets import cache @@ -478,12 +478,12 @@ class OctoPrintFlaskResponse(flask.Response): def passive_login(): if octoprint.server.userManager.enabled: - user = octoprint.server.userManager.login_user(flask.ext.login.current_user) + user = octoprint.server.userManager.login_user(flask_login.current_user) else: - user = flask.ext.login.current_user + user = flask_login.current_user - if user is not None and not user.is_anonymous(): - flask.ext.principal.identity_changed.send(flask.current_app._get_current_object(), identity=flask.ext.principal.Identity(user.get_id())) + if user is not None and not user.is_anonymous: + flask_principal.identity_changed.send(flask.current_app._get_current_object(), identity=flask_principal.Identity(user.get_id())) if hasattr(user, "get_session"): flask.session["usersession.id"] = user.get_session() flask.g.user = user @@ -505,8 +505,8 @@ def passive_login(): user = octoprint.server.userManager.login_user(user) flask.session["usersession.id"] = user.get_session() flask.g.user = user - flask.ext.login.login_user(user) - flask.ext.principal.identity_changed.send(flask.current_app._get_current_object(), identity=flask.ext.principal.Identity(user.get_id())) + flask_login.login_user(user) + flask_principal.identity_changed.send(flask.current_app._get_current_object(), identity=flask_principal.Identity(user.get_id())) return flask.jsonify(user.asDict()) except: logger = logging.getLogger(__name__) @@ -1023,7 +1023,7 @@ def admin_validator(request): """ user = _get_flask_user_from_request(request) - if user is None or not user.is_authenticated() or not user.is_admin(): + if user is None or not user.is_authenticated or not user.is_admin: raise tornado.web.HTTPError(403) @@ -1038,7 +1038,7 @@ def user_validator(request): """ user = _get_flask_user_from_request(request) - if user is None or not user.is_authenticated(): + if user is None or not user.is_authenticated: raise tornado.web.HTTPError(403) @@ -1051,14 +1051,14 @@ def _get_flask_user_from_request(request): :return: the user or None if no user could be determined """ import octoprint.server.util - import flask.ext.login + import flask_login from octoprint.settings import settings apikey = octoprint.server.util.get_api_key(request) if settings().getBoolean(["api", "enabled"]) and apikey is not None: user = octoprint.server.util.get_user_for_apikey(apikey) else: - user = flask.ext.login.current_user + user = flask_login.current_user return user @@ -1103,7 +1103,7 @@ def restricted_access(func): if settings().getBoolean(["server", "firstRun"]) and settings().getBoolean(["accessControl", "enabled"]) and (octoprint.server.userManager is None or not octoprint.server.userManager.hasBeenCustomized()): return flask.make_response("OctoPrint isn't setup yet", 403) - return flask.ext.login.login_required(func)(*args, **kwargs) + return flask_login.login_required(func)(*args, **kwargs) return decorated_view @@ -1201,7 +1201,7 @@ def get_json_command_from_request(request, valid_commands): ##~~ Flask-Assets resolver with plugin asset support -class PluginAssetResolver(flask.ext.assets.FlaskResolver): +class PluginAssetResolver(flask_assets.FlaskResolver): def split_prefix(self, ctx, item): app = ctx.environment._app @@ -1210,14 +1210,14 @@ class PluginAssetResolver(flask.ext.assets.FlaskResolver): prefix, plugin, name = item.split("/", 2) blueprint = prefix + "." + plugin - directory = flask.ext.assets.get_static_folder(app.blueprints[blueprint]) + directory = flask_assets.get_static_folder(app.blueprints[blueprint]) item = name endpoint = blueprint + ".static" return directory, item, endpoint except (ValueError, KeyError): pass - return flask.ext.assets.FlaskResolver.split_prefix(self, ctx, item) + return flask_assets.FlaskResolver.split_prefix(self, ctx, item) def resolve_output_to_path(self, ctx, target, bundle): import os diff --git a/src/octoprint/server/util/tornado.py b/src/octoprint/server/util/tornado.py index 12ba3b9b..8325bcaf 100644 --- a/src/octoprint/server/util/tornado.py +++ b/src/octoprint/server/util/tornado.py @@ -29,36 +29,6 @@ import tornado.util import octoprint.util -#~~ Monkey patching - - -def fix_ioloop_scheduling(): - """ - This monkey patches tornado's :meth:``tornado.ioloop.PeriodicCallback._schedule_next`` method so it no longer - blocks for long times on slow machines (RPi) when the system time happens to change by a large amount (e.g. due to - the first ever contact to an NTP server). - - Patch by @nosyjoe on Github. See this PR against tornado: https://github.com/tornadoweb/tornado/pull/1290 - """ - - import math - - # patched implementation taken from PR - def _schedule_next(self): - if self._running: - current_time = self.io_loop.time() - - if self._next_timeout <= current_time: - callback_time_sec = self.callback_time / 1000.0 - self._next_timeout += (math.floor((current_time - self._next_timeout) / callback_time_sec) + 1) * callback_time_sec - - self._timeout = self.io_loop.add_timeout(self._next_timeout, self._run) - - # replace original implementation with patched version - import tornado.ioloop - tornado.ioloop.PeriodicCallback._schedule_next = _schedule_next - - #~~ WSGI middleware @@ -672,8 +642,10 @@ class CustomHTTPServer(tornado.httpserver.HTTPServer): ``default_max_body_size`` is the default maximum body size to apply if no specific one from ``max_body_sizes`` matches. """ + def __init__(self, *args, **kwargs): + pass - def __init__(self, request_callback, no_keep_alive=False, io_loop=None, + def initialize(self, request_callback, no_keep_alive=False, io_loop=None, xheaders=False, ssl_options=None, protocol=None, decompress_request=False, chunk_size=None, max_header_size=None, diff --git a/src/octoprint/users.py b/src/octoprint/users.py index c5bb27d6..1803104b 100644 --- a/src/octoprint/users.py +++ b/src/octoprint/users.py @@ -5,8 +5,8 @@ __author__ = "Gina Häußge " __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License" -from flask.ext.login import UserMixin -from flask.ext.principal import Identity +from flask_login import UserMixin +from flask_principal import Identity from werkzeug.local import LocalProxy import hashlib import os @@ -428,9 +428,9 @@ class User(UserMixin): def asDict(self): return { "name": self._username, - "active": self.is_active(), - "admin": self.is_admin(), - "user": self.is_user(), + "active": self.is_active, + "admin": self.is_admin, + "user": self.is_user, "apikey": self._apikey, "settings": self._settings } @@ -444,12 +444,15 @@ class User(UserMixin): def get_name(self): return self._username + @property def is_active(self): return self._active + @property def is_user(self): return "user" in self._roles + @property def is_admin(self): return "admin" in self._roles @@ -500,7 +503,7 @@ class User(UserMixin): return True def __repr__(self): - return "User(id=%s,name=%s,active=%r,user=%r,admin=%r)" % (self.get_id(), self.get_name(), self.is_active(), self.is_user(), self.is_admin()) + return "User(id=%s,name=%s,active=%r,user=%r,admin=%r)" % (self.get_id(), self.get_name(), self.is_active, self.is_user, self.is_admin) class SessionUser(User): def __init__(self, user): @@ -530,7 +533,7 @@ class SessionUser(User): return self._session def __repr__(self): - return "SessionUser(id=%s,name=%s,active=%r,user=%r,admin=%r,session=%s,created=%s)" % (self.get_id(), self.get_name(), self.is_active(), self.is_user(), self.is_admin(), self._session, self._created) + return "SessionUser(id=%s,name=%s,active=%r,user=%r,admin=%r,session=%s,created=%s)" % (self.get_id(), self.get_name(), self.is_active, self.is_user, self.is_admin, self._session, self._created) ##~~ DummyUser object to use when accessControl is disabled