Skip to content
Permalink
Browse files

πŸ› Solve sorted issues under Python 3.x

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 a889bec4d8bca8fab79b8eb48d2c838c072eee2c
@@ -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 = []
@@ -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

@@ -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)))


@@ -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:
@@ -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)]

@@ -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):
@@ -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")
@@ -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"]
@@ -20,6 +20,8 @@
import octoprint.filemanager.storage
import octoprint.slicing

from octoprint.util import sv

import os
import psutil
import hashlib
@@ -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

@@ -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
@@ -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)

@@ -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
@@ -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):
@@ -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

0 comments on commit a889bec

Please sign in to comment.
You can’t perform that action at this time.