prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Software update fails on Debian testing/unstable
  Tested with Octoprint 1.3.10
  
  Right now, the software update will not work in Debian testing and unstable. The problem here is that Debian decided to name its python version `2.7.15+` (yes the '+' is part of the version string returned by `python --version`. Octoprint's version compare cannot cope with this and sees this as < 2.7.9 (which leads to a very confusing output of the software update component telling you why it doesn't want to update: `Python: 2.7.9 (you have: 2.7.15+)` ... took me some time to figure out what is actually going on)
  
  There is a bug report for Debian's python2.7 package already here https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=914072
  Sadly there is no feedback from the Debian maintainers on why they named it this way and if this might be changed again in the future.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `src/octoprint/util/version.py`
  Content:
  ```
  1 # coding=utf-8
  2 """
  3 This module provides a bunch of utility methods and helpers for version handling.
  4 """
  5 from __future__ import absolute_import, division, print_function
  6 
  7 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
  8 
  9 import pkg_resources
  10 import logging
  11 
  12 from octoprint import __version__
  13 
  14 
  15 def get_octoprint_version_string():
  16 	return __version__
  17 
  18 
  19 def get_octoprint_version(base=False):
  20 	octoprint_version_string = get_octoprint_version_string()
  21 	return get_comparable_version(octoprint_version_string, base=base)
  22 
  23 
  24 def is_released_octoprint_version(version=None):
  25 	"""
  26 	>>> import pkg_resources
  27 	>>> is_released_octoprint_version(version=pkg_resources.parse_version("1.3.6rc3"))
  28 	True
  29 	>>> is_released_octoprint_version(version=pkg_resources.parse_version("1.3.6rc3.dev2+g1234"))
  30 	False
  31 	>>> is_released_octoprint_version(version=pkg_resources.parse_version("1.3.6"))
  32 	True
  33 	>>> is_released_octoprint_version(version=pkg_resources.parse_version("1.3.6.post1+g1234"))
  34 	True
  35 	>>> is_released_octoprint_version(version=pkg_resources.parse_version("1.3.6.post1.dev0+g1234"))
  36 	False
  37 	>>> is_released_octoprint_version(version=pkg_resources.parse_version("1.3.7.dev123+g23545"))
  38 	False
  39 	"""
  40 
  41 	if version is None:
  42 		version = get_octoprint_version()
  43 
  44 	if isinstance(version, tuple):
  45 		# old setuptools
  46 		return "*@" not in version
  47 	else:
  48 		# new setuptools
  49 		return "dev" not in version.public
  50 
  51 
  52 def is_stable_octoprint_version(version=None):
  53 	"""
  54 	>>> import pkg_resources
  55 	>>> is_stable_octoprint_version(version=pkg_resources.parse_version("1.3.6rc3"))
  56 	False
  57 	>>> is_stable_octoprint_version(version=pkg_resources.parse_version("1.3.6rc3.dev2+g1234"))
  58 	False
  59 	>>> is_stable_octoprint_version(version=pkg_resources.parse_version("1.3.6"))
  60 	True
  61 	>>> is_stable_octoprint_version(version=pkg_resources.parse_version("1.3.6.post1+g1234"))
  62 	True
  63 	>>> is_stable_octoprint_version(version=pkg_resources.parse_version("1.3.6.post1.dev0+g1234"))
  64 	False
  65 	>>> is_stable_octoprint_version(version=pkg_resources.parse_version("1.3.7.dev123+g23545"))
  66 	False
  67 	"""
  68 
  69 	if version is None:
  70 		version = get_octoprint_version()
  71 
  72 	if not is_released_octoprint_version(version=version):
  73 		return False
  74 
  75 	if isinstance(version, tuple):
  76 		return "*a" not in version and "*b" not in version and "*c" not in version
  77 	else:
  78 		return not version.is_prerelease
  79 
  80 
  81 def is_octoprint_compatible(*compatibility_entries, **kwargs):
  82 	"""
  83 	Tests if the current ``octoprint_version`` is compatible to any of the provided ``compatibility_entries``.
  84 
  85 	Arguments:
  86 		compatibility_entries (str): compatibility string(s) to test against, result will be `True` if any match
  87 			is found
  88 		octoprint_version (tuple or SetuptoolsVersion): optional OctoPrint version to match against, if not current
  89 			base version will be determined via :func:`get_octoprint_version`.
  90 
  91 	Returns:
  92 		(bool) ``True`` if any of the provided compatibility entries matches or there are no entries, else ``False``
  93 	"""
  94 
  95 	logger = logging.getLogger(__name__)
  96 
  97 	if not compatibility_entries:
  98 		return True
  99 
  100 	octoprint_version = kwargs.get("octoprint_version")
  101 	if octoprint_version is None:
  102 		octoprint_version = get_octoprint_version(base=True)
  103 
  104 	for octo_compat in compatibility_entries:
  105 		try:
  106 			if not any(octo_compat.startswith(c) for c in ("<", "<=", "!=", "==", ">=", ">", "~=", "===")):
  107 				octo_compat = ">={}".format(octo_compat)
  108 
  109 			s = pkg_resources.Requirement.parse("OctoPrint" + octo_compat)
  110 			if octoprint_version in s:
  111 				break
  112 		except:
  113 			logger.exception("Something is wrong with this compatibility string for OctoPrint: {}".format(octo_compat))
  114 	else:
  115 		return False
  116 
  117 	return True
  118 
  119 
  120 def get_comparable_version(version_string, base=False):
  121 	if "-" in version_string:
  122 		version_string = version_string[:version_string.find("-")]
  123 
  124 	version = pkg_resources.parse_version(version_string)
  125 
  126 	# A leading v is common in github release tags and old setuptools doesn't remove it.
  127 	if version and isinstance(version, tuple) and version[0].lower() == "*v":
  128 		version = version[1:]
  129 
  130 	if base:
  131 		if isinstance(version, tuple):
  132 			# old setuptools
  133 			base_version = []
  134 			for part in version:
  135 				if part.startswith("*"):
  136 					break
  137 				base_version.append(part)
  138 			base_version.append("*final")
  139 			version = tuple(base_version)
  140 		else:
  141 			# new setuptools
  142 			version = pkg_resources.parse_version(version.base_version)
  143 	return version
  144
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/src/octoprint/util/version.py b/src/octoprint/util/version.py
  --- a/src/octoprint/util/version.py
  +++ b/src/octoprint/util/version.py
  @@ -121,6 +121,10 @@
   	if "-" in version_string:
   		version_string = version_string[:version_string.find("-")]
   
  +	# Debian has the python version set to 2.7.15+ which is not PEP440 compliant (bug 914072)
  +	if version_string.endswith("+"):
  +		version_string = version_string[:-1]
  +
   	version = pkg_resources.parse_version(version_string)
   
   	# A leading v is common in github release tags and old setuptools doesn't remove it.
  
verification_info: {"golden_diff": "diff --git a/src/octoprint/util/version.py b/src/octoprint/util/version.py\n--- a/src/octoprint/util/version.py\n+++ b/src/octoprint/util/version.py\n@@ -121,6 +121,10 @@\n \tif \"-\" in version_string:\n \t\tversion_string = version_string[:version_string.find(\"-\")]\n \n+\t# Debian has the python version set to 2.7.15+ which is not PEP440 compliant (bug 914072)\n+\tif version_string.endswith(\"+\"):\n+\t\tversion_string = version_string[:-1]\n+\n \tversion = pkg_resources.parse_version(version_string)\n \n \t# A leading v is common in github release tags and old setuptools doesn't remove it.\n", "issue": "Software update fails on Debian testing/unstable\nTested with Octoprint 1.3.10\r\n\r\nRight now, the software update will not work in Debian testing and unstable. The problem here is that Debian decided to name its python version `2.7.15+` (yes the '+' is part of the version string returned by `python --version`. Octoprint's version compare cannot cope with this and sees this as < 2.7.9 (which leads to a very confusing output of the software update component telling you why it doesn't want to update: `Python: 2.7.9 (you have: 2.7.15+)` ... took me some time to figure out what is actually going on)\r\n\r\nThere is a bug report for Debian's python2.7 package already here https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=914072\r\nSadly there is no feedback from the Debian maintainers on why they named it this way and if this might be changed again in the future.\n", "before_files": [{"content": "# coding=utf-8\n\"\"\"\nThis module provides a bunch of utility methods and helpers for version handling.\n\"\"\"\nfrom __future__ import absolute_import, division, print_function\n\n__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'\n\nimport pkg_resources\nimport logging\n\nfrom octoprint import __version__\n\n\ndef get_octoprint_version_string():\n\treturn __version__\n\n\ndef get_octoprint_version(base=False):\n\toctoprint_version_string = get_octoprint_version_string()\n\treturn get_comparable_version(octoprint_version_string, base=base)\n\n\ndef is_released_octoprint_version(version=None):\n\t\"\"\"\n\t>>> import pkg_resources\n\t>>> is_released_octoprint_version(version=pkg_resources.parse_version(\"1.3.6rc3\"))\n\tTrue\n\t>>> is_released_octoprint_version(version=pkg_resources.parse_version(\"1.3.6rc3.dev2+g1234\"))\n\tFalse\n\t>>> is_released_octoprint_version(version=pkg_resources.parse_version(\"1.3.6\"))\n\tTrue\n\t>>> is_released_octoprint_version(version=pkg_resources.parse_version(\"1.3.6.post1+g1234\"))\n\tTrue\n\t>>> is_released_octoprint_version(version=pkg_resources.parse_version(\"1.3.6.post1.dev0+g1234\"))\n\tFalse\n\t>>> is_released_octoprint_version(version=pkg_resources.parse_version(\"1.3.7.dev123+g23545\"))\n\tFalse\n\t\"\"\"\n\n\tif version is None:\n\t\tversion = get_octoprint_version()\n\n\tif isinstance(version, tuple):\n\t\t# old setuptools\n\t\treturn \"*@\" not in version\n\telse:\n\t\t# new setuptools\n\t\treturn \"dev\" not in version.public\n\n\ndef is_stable_octoprint_version(version=None):\n\t\"\"\"\n\t>>> import pkg_resources\n\t>>> is_stable_octoprint_version(version=pkg_resources.parse_version(\"1.3.6rc3\"))\n\tFalse\n\t>>> is_stable_octoprint_version(version=pkg_resources.parse_version(\"1.3.6rc3.dev2+g1234\"))\n\tFalse\n\t>>> is_stable_octoprint_version(version=pkg_resources.parse_version(\"1.3.6\"))\n\tTrue\n\t>>> is_stable_octoprint_version(version=pkg_resources.parse_version(\"1.3.6.post1+g1234\"))\n\tTrue\n\t>>> is_stable_octoprint_version(version=pkg_resources.parse_version(\"1.3.6.post1.dev0+g1234\"))\n\tFalse\n\t>>> is_stable_octoprint_version(version=pkg_resources.parse_version(\"1.3.7.dev123+g23545\"))\n\tFalse\n\t\"\"\"\n\n\tif version is None:\n\t\tversion = get_octoprint_version()\n\n\tif not is_released_octoprint_version(version=version):\n\t\treturn False\n\n\tif isinstance(version, tuple):\n\t\treturn \"*a\" not in version and \"*b\" not in version and \"*c\" not in version\n\telse:\n\t\treturn not version.is_prerelease\n\n\ndef is_octoprint_compatible(*compatibility_entries, **kwargs):\n\t\"\"\"\n\tTests if the current ``octoprint_version`` is compatible to any of the provided ``compatibility_entries``.\n\n\tArguments:\n\t\tcompatibility_entries (str): compatibility string(s) to test against, result will be `True` if any match\n\t\t\tis found\n\t\toctoprint_version (tuple or SetuptoolsVersion): optional OctoPrint version to match against, if not current\n\t\t\tbase version will be determined via :func:`get_octoprint_version`.\n\n\tReturns:\n\t\t(bool) ``True`` if any of the provided compatibility entries matches or there are no entries, else ``False``\n\t\"\"\"\n\n\tlogger = logging.getLogger(__name__)\n\n\tif not compatibility_entries:\n\t\treturn True\n\n\toctoprint_version = kwargs.get(\"octoprint_version\")\n\tif octoprint_version is None:\n\t\toctoprint_version = get_octoprint_version(base=True)\n\n\tfor octo_compat in compatibility_entries:\n\t\ttry:\n\t\t\tif not any(octo_compat.startswith(c) for c in (\"<\", \"<=\", \"!=\", \"==\", \">=\", \">\", \"~=\", \"===\")):\n\t\t\t\tocto_compat = \">={}\".format(octo_compat)\n\n\t\t\ts = pkg_resources.Requirement.parse(\"OctoPrint\" + octo_compat)\n\t\t\tif octoprint_version in s:\n\t\t\t\tbreak\n\t\texcept:\n\t\t\tlogger.exception(\"Something is wrong with this compatibility string for OctoPrint: {}\".format(octo_compat))\n\telse:\n\t\treturn False\n\n\treturn True\n\n\ndef get_comparable_version(version_string, base=False):\n\tif \"-\" in version_string:\n\t\tversion_string = version_string[:version_string.find(\"-\")]\n\n\tversion = pkg_resources.parse_version(version_string)\n\n\t# A leading v is common in github release tags and old setuptools doesn't remove it.\n\tif version and isinstance(version, tuple) and version[0].lower() == \"*v\":\n\t\tversion = version[1:]\n\n\tif base:\n\t\tif isinstance(version, tuple):\n\t\t\t# old setuptools\n\t\t\tbase_version = []\n\t\t\tfor part in version:\n\t\t\t\tif part.startswith(\"*\"):\n\t\t\t\t\tbreak\n\t\t\t\tbase_version.append(part)\n\t\t\tbase_version.append(\"*final\")\n\t\t\tversion = tuple(base_version)\n\t\telse:\n\t\t\t# new setuptools\n\t\t\tversion = pkg_resources.parse_version(version.base_version)\n\treturn version\n", "path": "src/octoprint/util/version.py"}], "after_files": [{"content": "# coding=utf-8\n\"\"\"\nThis module provides a bunch of utility methods and helpers for version handling.\n\"\"\"\nfrom __future__ import absolute_import, division, print_function\n\n__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'\n\nimport pkg_resources\nimport logging\n\nfrom octoprint import __version__\n\n\ndef get_octoprint_version_string():\n\treturn __version__\n\n\ndef get_octoprint_version(base=False):\n\toctoprint_version_string = get_octoprint_version_string()\n\treturn get_comparable_version(octoprint_version_string, base=base)\n\n\ndef is_released_octoprint_version(version=None):\n\t\"\"\"\n\t>>> import pkg_resources\n\t>>> is_released_octoprint_version(version=pkg_resources.parse_version(\"1.3.6rc3\"))\n\tTrue\n\t>>> is_released_octoprint_version(version=pkg_resources.parse_version(\"1.3.6rc3.dev2+g1234\"))\n\tFalse\n\t>>> is_released_octoprint_version(version=pkg_resources.parse_version(\"1.3.6\"))\n\tTrue\n\t>>> is_released_octoprint_version(version=pkg_resources.parse_version(\"1.3.6.post1+g1234\"))\n\tTrue\n\t>>> is_released_octoprint_version(version=pkg_resources.parse_version(\"1.3.6.post1.dev0+g1234\"))\n\tFalse\n\t>>> is_released_octoprint_version(version=pkg_resources.parse_version(\"1.3.7.dev123+g23545\"))\n\tFalse\n\t\"\"\"\n\n\tif version is None:\n\t\tversion = get_octoprint_version()\n\n\tif isinstance(version, tuple):\n\t\t# old setuptools\n\t\treturn \"*@\" not in version\n\telse:\n\t\t# new setuptools\n\t\treturn \"dev\" not in version.public\n\n\ndef is_stable_octoprint_version(version=None):\n\t\"\"\"\n\t>>> import pkg_resources\n\t>>> is_stable_octoprint_version(version=pkg_resources.parse_version(\"1.3.6rc3\"))\n\tFalse\n\t>>> is_stable_octoprint_version(version=pkg_resources.parse_version(\"1.3.6rc3.dev2+g1234\"))\n\tFalse\n\t>>> is_stable_octoprint_version(version=pkg_resources.parse_version(\"1.3.6\"))\n\tTrue\n\t>>> is_stable_octoprint_version(version=pkg_resources.parse_version(\"1.3.6.post1+g1234\"))\n\tTrue\n\t>>> is_stable_octoprint_version(version=pkg_resources.parse_version(\"1.3.6.post1.dev0+g1234\"))\n\tFalse\n\t>>> is_stable_octoprint_version(version=pkg_resources.parse_version(\"1.3.7.dev123+g23545\"))\n\tFalse\n\t\"\"\"\n\n\tif version is None:\n\t\tversion = get_octoprint_version()\n\n\tif not is_released_octoprint_version(version=version):\n\t\treturn False\n\n\tif isinstance(version, tuple):\n\t\treturn \"*a\" not in version and \"*b\" not in version and \"*c\" not in version\n\telse:\n\t\treturn not version.is_prerelease\n\n\ndef is_octoprint_compatible(*compatibility_entries, **kwargs):\n\t\"\"\"\n\tTests if the current ``octoprint_version`` is compatible to any of the provided ``compatibility_entries``.\n\n\tArguments:\n\t\tcompatibility_entries (str): compatibility string(s) to test against, result will be `True` if any match\n\t\t\tis found\n\t\toctoprint_version (tuple or SetuptoolsVersion): optional OctoPrint version to match against, if not current\n\t\t\tbase version will be determined via :func:`get_octoprint_version`.\n\n\tReturns:\n\t\t(bool) ``True`` if any of the provided compatibility entries matches or there are no entries, else ``False``\n\t\"\"\"\n\n\tlogger = logging.getLogger(__name__)\n\n\tif not compatibility_entries:\n\t\treturn True\n\n\toctoprint_version = kwargs.get(\"octoprint_version\")\n\tif octoprint_version is None:\n\t\toctoprint_version = get_octoprint_version(base=True)\n\n\tfor octo_compat in compatibility_entries:\n\t\ttry:\n\t\t\tif not any(octo_compat.startswith(c) for c in (\"<\", \"<=\", \"!=\", \"==\", \">=\", \">\", \"~=\", \"===\")):\n\t\t\t\tocto_compat = \">={}\".format(octo_compat)\n\n\t\t\ts = pkg_resources.Requirement.parse(\"OctoPrint\" + octo_compat)\n\t\t\tif octoprint_version in s:\n\t\t\t\tbreak\n\t\texcept:\n\t\t\tlogger.exception(\"Something is wrong with this compatibility string for OctoPrint: {}\".format(octo_compat))\n\telse:\n\t\treturn False\n\n\treturn True\n\n\ndef get_comparable_version(version_string, base=False):\n\tif \"-\" in version_string:\n\t\tversion_string = version_string[:version_string.find(\"-\")]\n\n\t# Debian has the python version set to 2.7.15+ which is not PEP440 compliant (bug 914072)\n\tif version_string.endswith(\"+\"):\n\t\tversion_string = version_string[:-1]\n\n\tversion = pkg_resources.parse_version(version_string)\n\n\t# A leading v is common in github release tags and old setuptools doesn't remove it.\n\tif version and isinstance(version, tuple) and version[0].lower() == \"*v\":\n\t\tversion = version[1:]\n\n\tif base:\n\t\tif isinstance(version, tuple):\n\t\t\t# old setuptools\n\t\t\tbase_version = []\n\t\t\tfor part in version:\n\t\t\t\tif part.startswith(\"*\"):\n\t\t\t\t\tbreak\n\t\t\t\tbase_version.append(part)\n\t\t\tbase_version.append(\"*final\")\n\t\t\tversion = tuple(base_version)\n\t\telse:\n\t\t\t# new setuptools\n\t\t\tversion = pkg_resources.parse_version(version.base_version)\n\treturn version\n", "path": "src/octoprint/util/version.py"}]}