Skip to content

Commit

Permalink
馃悰 Solve sorted issues under Python 3.x
Browse files Browse the repository at this point in the history
sorted does no longer support sorting None values under Python 3. This
patch introduces a utility function that ensures that None values are
replaced by a configurable default value ("" by default) and utilizes
it in places where sorted gets called with a custom sorting key that
can potentially consist of None values.

Closes #3300
  • Loading branch information
foosel committed Oct 10, 2019
1 parent 09a4fb2 commit a889bec
Show file tree
Hide file tree
Showing 8 changed files with 26 additions and 12 deletions.
4 changes: 3 additions & 1 deletion src/octoprint/__init__.py
Expand Up @@ -410,7 +410,9 @@ def handle_plugins_loaded(startup=False, initialize_implementations=True, force_
if not startup:
return

sorted_disabled_from_overlays = sorted([(key, value[0], value[1]) for key, value in disabled_from_overlays.items()], key=lambda x: (x[2] is None, x[2], x[0]))
from octoprint.util import sv

sorted_disabled_from_overlays = sorted([(key, value[0], value[1]) for key, value in disabled_from_overlays.items()], key=lambda x: (x[2] is None, sv(x[2]), sv(x[0])))

disabled_list = pm.plugin_disabled_list
already_processed = []
Expand Down
4 changes: 2 additions & 2 deletions src/octoprint/cli/user.py
Expand Up @@ -12,7 +12,7 @@
from octoprint.cli import get_ctx_obj_option
from octoprint.access.groups import FilebasedGroupManager
from octoprint.access.users import FilebasedUserManager, UnknownUser, UserAlreadyExists
from octoprint.util import get_class
from octoprint.util import get_class, sv

click.disable_unicode_literals_warning = True

Expand Down Expand Up @@ -170,7 +170,7 @@ def deactivate_command(ctx, username):

def _print_list(users):
click.echo("{} users registered in the system:".format(len(users)))
for user in sorted(map(lambda x: x.as_dict(), users), key=lambda x: x.get("name")):
for user in sorted(map(lambda x: x.as_dict(), users), key=lambda x: sv(x.get("name"))):
click.echo("\t{}".format(_user_to_line(user)))


Expand Down
6 changes: 3 additions & 3 deletions src/octoprint/plugin/core.py
Expand Up @@ -39,7 +39,7 @@

from past.builtins import unicode

from octoprint.util import to_unicode
from octoprint.util import to_unicode, sv
from octoprint.util.version import is_python_compatible, get_python_version_string

try:
Expand Down Expand Up @@ -1585,7 +1585,7 @@ def sort_func(impl):
sorting_value = None

plugin_info = self.get_plugin_info(impl[0], require_enabled=False)
return sorting_value is None, sorting_value, not plugin_info.bundled if plugin_info else True, impl[0]
return sorting_value is None, sv(sorting_value), not plugin_info.bundled if plugin_info else True, sv(impl[0])

return [impl[1] for impl in sorted(result, key=sort_func)]

Expand Down Expand Up @@ -1672,7 +1672,7 @@ def send_plugin_message(self, plugin, data, permissions=None):

def _sort_hooks(self, hook):
self._plugin_hooks[hook] = sorted(self._plugin_hooks[hook],
key=lambda x: (x[0] is None, x[0], x[1], x[2]))
key=lambda x: (x[0] is None, sv(x[0]), sv(x[1]), sv(x[2])))

def _get_callback_and_order(self, hook):
if callable(hook):
Expand Down
Expand Up @@ -9,6 +9,8 @@
import requests
import logging

from octoprint.util import sv

RELEASE_URL = "https://api.github.com/repos/{user}/{repo}/releases"

logger = logging.getLogger("octoprint.plugins.softwareupdate.version_checks.github_release")
Expand Down Expand Up @@ -69,7 +71,7 @@ def _filter_out_latest(releases,
nothing = None, None, None

if sort_key is None:
sort_key = lambda release: release.get("published_at", None)
sort_key = lambda release: sv(release.get("published_at", None))

# filter out prereleases and drafts
filter_function = lambda rel: not rel["prerelease"] and not rel["draft"]
Expand Down
4 changes: 3 additions & 1 deletion src/octoprint/server/api/files.py
Expand Up @@ -20,6 +20,8 @@
import octoprint.filemanager.storage
import octoprint.slicing

from octoprint.util import sv

import os
import psutil
import hashlib
Expand Down Expand Up @@ -85,7 +87,7 @@ def hash_update(value):

if path.endswith("/files") or path.endswith("/files/sdcard"):
# include sd data in etag
hash_update(repr(sorted(printer.get_sd_files(), key=lambda x: x[0])))
hash_update(repr(sorted(printer.get_sd_files(), key=lambda x: sv(x[0]))))

hash_update(_DATA_FORMAT_VERSION) # increment version if we change the API format

Expand Down
6 changes: 3 additions & 3 deletions src/octoprint/server/views.py
Expand Up @@ -22,7 +22,7 @@
from octoprint.access.permissions import Permissions
from octoprint.settings import settings
from octoprint.filemanager import full_extension_tree, get_all_extensions
from octoprint.util import to_unicode, to_bytes
from octoprint.util import to_unicode, to_bytes, sv

import re
import base64
Expand Down Expand Up @@ -750,14 +750,14 @@ def f(x, k):
def key_func(x):
config = templates[t]["entries"][x]
entry_order = config_extractor(config, "order", default_value=None)
return entry_order is None, entry_order, extractor(config, sort_key)
return entry_order is None, sv(entry_order), sv(extractor(config, sort_key))

sorted_missing = sorted(missing_in_order, key=key_func)
else:
def key_func(x):
config = templates[t]["entries"][x]
entry_order = config_extractor(config, "order", default_value=None)
return entry_order is None, entry_order
return entry_order is None, sv(entry_order)

sorted_missing = sorted(missing_in_order, key=key_func)

Expand Down
3 changes: 2 additions & 1 deletion src/octoprint/timelapse.py
Expand Up @@ -27,6 +27,7 @@
from octoprint.plugin import plugin_manager
from octoprint.util import monotonic_time
from octoprint.util import get_fully_qualified_classname as fqcn
from octoprint.util import sv
from octoprint.util.commandline import CommandlineCaller

import sarge
Expand Down Expand Up @@ -172,7 +173,7 @@ def finalize_fields(prefix, job):

return job

return sorted([util.dict_merge(dict(name=key), finalize_fields(key, value)) for key, value in jobs.items()], key=lambda x: x["name"])
return sorted([util.dict_merge(dict(name=key), finalize_fields(key, value)) for key, value in jobs.items()], key=lambda x: sv(x["name"]))


def delete_unrendered_timelapse(name):
Expand Down
7 changes: 7 additions & 0 deletions src/octoprint/util/__init__.py
Expand Up @@ -71,6 +71,13 @@ def to_native_str(s_or_u):
return to_unicode(s_or_u)


def sortable_value(value, default_value=""):
if value is None:
return default_value
return default_value
sv = sortable_value


def pp(value):
"""
>>> pp(dict()) # doctest: +ALLOW_UNICODE
Expand Down

0 comments on commit a889bec

Please sign in to comment.