Remove various bits of compatibility code for Python <3.6 (#9879)
I went through and removed a bunch of cruft that was lying around for compatibility with old Python versions. This PR also will now prevent Synapse from starting unless you're running Python 3.6+.
This commit is contained in:
parent
1350b053da
commit
fe604a022a
|
@ -0,0 +1 @@
|
||||||
|
Remove backwards-compatibility code for Python versions < 3.6.
|
1
mypy.ini
1
mypy.ini
|
@ -41,7 +41,6 @@ files =
|
||||||
synapse/push,
|
synapse/push,
|
||||||
synapse/replication,
|
synapse/replication,
|
||||||
synapse/rest,
|
synapse/rest,
|
||||||
synapse/secrets.py,
|
|
||||||
synapse/server.py,
|
synapse/server.py,
|
||||||
synapse/server_notices,
|
synapse/server_notices,
|
||||||
synapse/spam_checker_api,
|
synapse/spam_checker_api,
|
||||||
|
|
|
@ -21,8 +21,8 @@ import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
# Check that we're not running on an unsupported Python version.
|
# Check that we're not running on an unsupported Python version.
|
||||||
if sys.version_info < (3, 5):
|
if sys.version_info < (3, 6):
|
||||||
print("Synapse requires Python 3.5 or above.")
|
print("Synapse requires Python 3.6 or above.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Twisted and canonicaljson will fail to import when this file is executed to
|
# Twisted and canonicaljson will fail to import when this file is executed to
|
||||||
|
|
|
@ -85,7 +85,7 @@ REQUIREMENTS = [
|
||||||
"typing-extensions>=3.7.4",
|
"typing-extensions>=3.7.4",
|
||||||
# We enforce that we have a `cryptography` version that bundles an `openssl`
|
# We enforce that we have a `cryptography` version that bundles an `openssl`
|
||||||
# with the latest security patches.
|
# with the latest security patches.
|
||||||
"cryptography>=3.4.7;python_version>='3.6'",
|
"cryptography>=3.4.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
CONDITIONAL_REQUIREMENTS = {
|
CONDITIONAL_REQUIREMENTS = {
|
||||||
|
@ -100,14 +100,9 @@ CONDITIONAL_REQUIREMENTS = {
|
||||||
# that use the protocol, such as Let's Encrypt.
|
# that use the protocol, such as Let's Encrypt.
|
||||||
"acme": [
|
"acme": [
|
||||||
"txacme>=0.9.2",
|
"txacme>=0.9.2",
|
||||||
# txacme depends on eliot. Eliot 1.8.0 is incompatible with
|
|
||||||
# python 3.5.2, as per https://github.com/itamarst/eliot/issues/418
|
|
||||||
"eliot<1.8.0;python_version<'3.5.3'",
|
|
||||||
],
|
],
|
||||||
"saml2": [
|
"saml2": [
|
||||||
# pysaml2 6.4.0 is incompatible with Python 3.5 (see https://github.com/IdentityPython/pysaml2/issues/749)
|
"pysaml2>=4.5.0",
|
||||||
"pysaml2>=4.5.0,<6.4.0;python_version<'3.6'",
|
|
||||||
"pysaml2>=4.5.0;python_version>='3.6'",
|
|
||||||
],
|
],
|
||||||
"oidc": ["authlib>=0.14.0"],
|
"oidc": ["authlib>=0.14.0"],
|
||||||
# systemd-python is necessary for logging to the systemd journal via
|
# systemd-python is necessary for logging to the systemd journal via
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
import hashlib
|
import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
import logging
|
import logging
|
||||||
|
import secrets
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
|
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
|
@ -375,7 +376,7 @@ class UserRegisterServlet(RestServlet):
|
||||||
"""
|
"""
|
||||||
self._clear_old_nonces()
|
self._clear_old_nonces()
|
||||||
|
|
||||||
nonce = self.hs.get_secrets().token_hex(64)
|
nonce = secrets.token_hex(64)
|
||||||
self.nonces[nonce] = int(self.reactor.seconds())
|
self.nonces[nonce] = int(self.reactor.seconds())
|
||||||
return 200, {"nonce": nonce}
|
return 200, {"nonce": nonce}
|
||||||
|
|
||||||
|
|
|
@ -32,14 +32,6 @@ TEMPLATE_LANGUAGE = "en"
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# use hmac.compare_digest if we have it (python 2.7.7), else just use equality
|
|
||||||
if hasattr(hmac, "compare_digest"):
|
|
||||||
compare_digest = hmac.compare_digest
|
|
||||||
else:
|
|
||||||
|
|
||||||
def compare_digest(a, b):
|
|
||||||
return a == b
|
|
||||||
|
|
||||||
|
|
||||||
class ConsentResource(DirectServeHtmlResource):
|
class ConsentResource(DirectServeHtmlResource):
|
||||||
"""A twisted Resource to display a privacy policy and gather consent to it
|
"""A twisted Resource to display a privacy policy and gather consent to it
|
||||||
|
@ -209,5 +201,5 @@ class ConsentResource(DirectServeHtmlResource):
|
||||||
.encode("ascii")
|
.encode("ascii")
|
||||||
)
|
)
|
||||||
|
|
||||||
if not compare_digest(want_mac, userhmac):
|
if not hmac.compare_digest(want_mac, userhmac):
|
||||||
raise SynapseError(HTTPStatus.FORBIDDEN, "HMAC incorrect")
|
raise SynapseError(HTTPStatus.FORBIDDEN, "HMAC incorrect")
|
||||||
|
|
|
@ -21,7 +21,7 @@ from typing import Callable, List
|
||||||
NEW_FORMAT_ID_RE = re.compile(r"^\d\d\d\d-\d\d-\d\d")
|
NEW_FORMAT_ID_RE = re.compile(r"^\d\d\d\d-\d\d-\d\d")
|
||||||
|
|
||||||
|
|
||||||
def _wrap_in_base_path(func: "Callable[..., str]") -> "Callable[..., str]":
|
def _wrap_in_base_path(func: Callable[..., str]) -> Callable[..., str]:
|
||||||
"""Takes a function that returns a relative path and turns it into an
|
"""Takes a function that returns a relative path and turns it into an
|
||||||
absolute path based on the location of the primary media store
|
absolute path based on the location of the primary media store
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
# Copyright 2018 New Vector Ltd
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Injectable secrets module for Synapse.
|
|
||||||
|
|
||||||
See https://docs.python.org/3/library/secrets.html#module-secrets for the API
|
|
||||||
used in Python 3.6, and the API emulated in Python 2.7.
|
|
||||||
"""
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# secrets is available since python 3.6
|
|
||||||
if sys.version_info[0:2] >= (3, 6):
|
|
||||||
import secrets
|
|
||||||
|
|
||||||
class Secrets:
|
|
||||||
def token_bytes(self, nbytes: int = 32) -> bytes:
|
|
||||||
return secrets.token_bytes(nbytes)
|
|
||||||
|
|
||||||
def token_hex(self, nbytes: int = 32) -> str:
|
|
||||||
return secrets.token_hex(nbytes)
|
|
||||||
|
|
||||||
|
|
||||||
else:
|
|
||||||
import binascii
|
|
||||||
import os
|
|
||||||
|
|
||||||
class Secrets:
|
|
||||||
def token_bytes(self, nbytes: int = 32) -> bytes:
|
|
||||||
return os.urandom(nbytes)
|
|
||||||
|
|
||||||
def token_hex(self, nbytes: int = 32) -> str:
|
|
||||||
return binascii.hexlify(self.token_bytes(nbytes)).decode("ascii")
|
|
|
@ -126,7 +126,6 @@ from synapse.rest.media.v1.media_repository import (
|
||||||
MediaRepository,
|
MediaRepository,
|
||||||
MediaRepositoryResource,
|
MediaRepositoryResource,
|
||||||
)
|
)
|
||||||
from synapse.secrets import Secrets
|
|
||||||
from synapse.server_notices.server_notices_manager import ServerNoticesManager
|
from synapse.server_notices.server_notices_manager import ServerNoticesManager
|
||||||
from synapse.server_notices.server_notices_sender import ServerNoticesSender
|
from synapse.server_notices.server_notices_sender import ServerNoticesSender
|
||||||
from synapse.server_notices.worker_server_notices_sender import (
|
from synapse.server_notices.worker_server_notices_sender import (
|
||||||
|
@ -641,10 +640,6 @@ class HomeServer(metaclass=abc.ABCMeta):
|
||||||
def get_groups_attestation_renewer(self) -> GroupAttestionRenewer:
|
def get_groups_attestation_renewer(self) -> GroupAttestionRenewer:
|
||||||
return GroupAttestionRenewer(self)
|
return GroupAttestionRenewer(self)
|
||||||
|
|
||||||
@cache_in_self
|
|
||||||
def get_secrets(self) -> Secrets:
|
|
||||||
return Secrets()
|
|
||||||
|
|
||||||
@cache_in_self
|
@cache_in_self
|
||||||
def get_stats_handler(self) -> StatsHandler:
|
def get_stats_handler(self) -> StatsHandler:
|
||||||
return StatsHandler(self)
|
return StatsHandler(self)
|
||||||
|
|
|
@ -114,7 +114,7 @@ def db_to_json(db_content: Union[memoryview, bytes, bytearray, str]) -> Any:
|
||||||
db_content = db_content.tobytes()
|
db_content = db_content.tobytes()
|
||||||
|
|
||||||
# Decode it to a Unicode string before feeding it to the JSON decoder, since
|
# Decode it to a Unicode string before feeding it to the JSON decoder, since
|
||||||
# Python 3.5 does not support deserializing bytes.
|
# it only supports handling strings
|
||||||
if isinstance(db_content, (bytes, bytearray)):
|
if isinstance(db_content, (bytes, bytearray)):
|
||||||
db_content = db_content.decode("utf8")
|
db_content = db_content.decode("utf8")
|
||||||
|
|
||||||
|
|
|
@ -171,10 +171,7 @@ class LoggingDatabaseConnection:
|
||||||
|
|
||||||
|
|
||||||
# The type of entry which goes on our after_callbacks and exception_callbacks lists.
|
# The type of entry which goes on our after_callbacks and exception_callbacks lists.
|
||||||
#
|
_CallbackListEntry = Tuple[Callable[..., None], Iterable[Any], Dict[str, Any]]
|
||||||
# Python 3.5.2 doesn't support Callable with an ellipsis, so we wrap it in quotes so
|
|
||||||
# that mypy sees the type but the runtime python doesn't.
|
|
||||||
_CallbackListEntry = Tuple["Callable[..., None]", Iterable[Any], Dict[str, Any]]
|
|
||||||
|
|
||||||
|
|
||||||
R = TypeVar("R")
|
R = TypeVar("R")
|
||||||
|
@ -221,7 +218,7 @@ class LoggingTransaction:
|
||||||
self.after_callbacks = after_callbacks
|
self.after_callbacks = after_callbacks
|
||||||
self.exception_callbacks = exception_callbacks
|
self.exception_callbacks = exception_callbacks
|
||||||
|
|
||||||
def call_after(self, callback: "Callable[..., None]", *args: Any, **kwargs: Any):
|
def call_after(self, callback: Callable[..., None], *args: Any, **kwargs: Any):
|
||||||
"""Call the given callback on the main twisted thread after the
|
"""Call the given callback on the main twisted thread after the
|
||||||
transaction has finished. Used to invalidate the caches on the
|
transaction has finished. Used to invalidate the caches on the
|
||||||
correct thread.
|
correct thread.
|
||||||
|
@ -233,7 +230,7 @@ class LoggingTransaction:
|
||||||
self.after_callbacks.append((callback, args, kwargs))
|
self.after_callbacks.append((callback, args, kwargs))
|
||||||
|
|
||||||
def call_on_exception(
|
def call_on_exception(
|
||||||
self, callback: "Callable[..., None]", *args: Any, **kwargs: Any
|
self, callback: Callable[..., None], *args: Any, **kwargs: Any
|
||||||
):
|
):
|
||||||
# if self.exception_callbacks is None, that means that whatever constructed the
|
# if self.exception_callbacks is None, that means that whatever constructed the
|
||||||
# LoggingTransaction isn't expecting there to be any callbacks; assert that
|
# LoggingTransaction isn't expecting there to be any callbacks; assert that
|
||||||
|
@ -485,7 +482,7 @@ class DatabasePool:
|
||||||
desc: str,
|
desc: str,
|
||||||
after_callbacks: List[_CallbackListEntry],
|
after_callbacks: List[_CallbackListEntry],
|
||||||
exception_callbacks: List[_CallbackListEntry],
|
exception_callbacks: List[_CallbackListEntry],
|
||||||
func: "Callable[..., R]",
|
func: Callable[..., R],
|
||||||
*args: Any,
|
*args: Any,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> R:
|
) -> R:
|
||||||
|
@ -618,7 +615,7 @@ class DatabasePool:
|
||||||
async def runInteraction(
|
async def runInteraction(
|
||||||
self,
|
self,
|
||||||
desc: str,
|
desc: str,
|
||||||
func: "Callable[..., R]",
|
func: Callable[..., R],
|
||||||
*args: Any,
|
*args: Any,
|
||||||
db_autocommit: bool = False,
|
db_autocommit: bool = False,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
|
@ -678,7 +675,7 @@ class DatabasePool:
|
||||||
|
|
||||||
async def runWithConnection(
|
async def runWithConnection(
|
||||||
self,
|
self,
|
||||||
func: "Callable[..., R]",
|
func: Callable[..., R],
|
||||||
*args: Any,
|
*args: Any,
|
||||||
db_autocommit: bool = False,
|
db_autocommit: bool = False,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
|
|
|
@ -110,7 +110,7 @@ class ResponseCache(Generic[T]):
|
||||||
return result.observe()
|
return result.observe()
|
||||||
|
|
||||||
def wrap(
|
def wrap(
|
||||||
self, key: T, callback: "Callable[..., Any]", *args: Any, **kwargs: Any
|
self, key: T, callback: Callable[..., Any], *args: Any, **kwargs: Any
|
||||||
) -> defer.Deferred:
|
) -> defer.Deferred:
|
||||||
"""Wrap together a *get* and *set* call, taking care of logcontexts
|
"""Wrap together a *get* and *set* call, taking care of logcontexts
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ import json
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from binascii import unhexlify
|
from binascii import unhexlify
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
import synapse.rest.admin
|
import synapse.rest.admin
|
||||||
from synapse.api.constants import UserTypes
|
from synapse.api.constants import UserTypes
|
||||||
|
@ -54,8 +54,6 @@ class UserRegisterTestCase(unittest.HomeserverTestCase):
|
||||||
self.datastore = Mock(return_value=Mock())
|
self.datastore = Mock(return_value=Mock())
|
||||||
self.datastore.get_current_state_deltas = Mock(return_value=(0, []))
|
self.datastore.get_current_state_deltas = Mock(return_value=(0, []))
|
||||||
|
|
||||||
self.secrets = Mock()
|
|
||||||
|
|
||||||
self.hs = self.setup_test_homeserver()
|
self.hs = self.setup_test_homeserver()
|
||||||
|
|
||||||
self.hs.config.registration_shared_secret = "shared"
|
self.hs.config.registration_shared_secret = "shared"
|
||||||
|
@ -84,10 +82,9 @@ class UserRegisterTestCase(unittest.HomeserverTestCase):
|
||||||
Calling GET on the endpoint will return a randomised nonce, using the
|
Calling GET on the endpoint will return a randomised nonce, using the
|
||||||
homeserver's secrets provider.
|
homeserver's secrets provider.
|
||||||
"""
|
"""
|
||||||
secrets = Mock()
|
with patch("secrets.token_hex") as token_hex:
|
||||||
secrets.token_hex = Mock(return_value="abcd")
|
# Patch secrets.token_hex for the duration of this context
|
||||||
|
token_hex.return_value = "abcd"
|
||||||
self.hs.get_secrets = Mock(return_value=secrets)
|
|
||||||
|
|
||||||
channel = self.make_request("GET", self.url)
|
channel = self.make_request("GET", self.url)
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import secrets
|
||||||
|
|
||||||
from tests import unittest
|
from tests import unittest
|
||||||
|
|
||||||
|
@ -21,7 +22,7 @@ class UpsertManyTests(unittest.HomeserverTestCase):
|
||||||
def prepare(self, reactor, clock, hs):
|
def prepare(self, reactor, clock, hs):
|
||||||
self.storage = hs.get_datastore()
|
self.storage = hs.get_datastore()
|
||||||
|
|
||||||
self.table_name = "table_" + hs.get_secrets().token_hex(6)
|
self.table_name = "table_" + secrets.token_hex(6)
|
||||||
self.get_success(
|
self.get_success(
|
||||||
self.storage.db_pool.runInteraction(
|
self.storage.db_pool.runInteraction(
|
||||||
"create",
|
"create",
|
||||||
|
|
|
@ -18,6 +18,7 @@ import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
|
import secrets
|
||||||
import time
|
import time
|
||||||
from typing import Callable, Dict, Iterable, Optional, Tuple, Type, TypeVar, Union
|
from typing import Callable, Dict, Iterable, Optional, Tuple, Type, TypeVar, Union
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
@ -626,7 +627,6 @@ class HomeserverTestCase(TestCase):
|
||||||
str: The new event's ID.
|
str: The new event's ID.
|
||||||
"""
|
"""
|
||||||
event_creator = self.hs.get_event_creation_handler()
|
event_creator = self.hs.get_event_creation_handler()
|
||||||
secrets = self.hs.get_secrets()
|
|
||||||
requester = create_requester(user)
|
requester = create_requester(user)
|
||||||
|
|
||||||
event, context = self.get_success(
|
event, context = self.get_success(
|
||||||
|
|
9
tox.ini
9
tox.ini
|
@ -21,13 +21,11 @@ deps =
|
||||||
# installed on that).
|
# installed on that).
|
||||||
#
|
#
|
||||||
# anyway, make sure that we have a recent enough setuptools.
|
# anyway, make sure that we have a recent enough setuptools.
|
||||||
setuptools>=18.5 ; python_version >= '3.6'
|
setuptools>=18.5
|
||||||
setuptools>=18.5,<51.0.0 ; python_version < '3.6'
|
|
||||||
|
|
||||||
# we also need a semi-recent version of pip, because old ones fail to
|
# we also need a semi-recent version of pip, because old ones fail to
|
||||||
# install the "enum34" dependency of cryptography.
|
# install the "enum34" dependency of cryptography.
|
||||||
pip>=10 ; python_version >= '3.6'
|
pip>=10
|
||||||
pip>=10,<21.0 ; python_version < '3.6'
|
|
||||||
|
|
||||||
# directories/files we run the linters on.
|
# directories/files we run the linters on.
|
||||||
# if you update this list, make sure to do the same in scripts-dev/lint.sh
|
# if you update this list, make sure to do the same in scripts-dev/lint.sh
|
||||||
|
@ -168,8 +166,7 @@ skip_install = true
|
||||||
usedevelop = false
|
usedevelop = false
|
||||||
deps =
|
deps =
|
||||||
coverage
|
coverage
|
||||||
pip>=10 ; python_version >= '3.6'
|
pip>=10
|
||||||
pip>=10,<21.0 ; python_version < '3.6'
|
|
||||||
commands=
|
commands=
|
||||||
coverage combine
|
coverage combine
|
||||||
coverage report
|
coverage report
|
||||||
|
|
Loading…
Reference in New Issue