--------
 
 - Fix bug in v0.33.3rc1 which caused infinite loops and OOMs
 ([\#3723](https://github.com/matrix-org/synapse/issues/3723))
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEIQBQJ4l+yK4dlKkFIwi0edOSShEFAlt6/yMACgkQIwi0edOS
 ShFrzAgAhLA74fClydGLfhPNCXIw12Amv2ASA6+r0ukqjYQ/9MZ0CRsL+PMZJTk6
 Z75PK3TkX38zSpPaUx9HdMQO/yE+rpI99ZxlBM+8jzsf1jyF8ljqxSfixXUlaXX9
 698PQQ3f+IDoSs59NKrHvoWhUThaBpf/1GwRxG5mUcm2oKJItNMHk/uLurNCaIIZ
 YSwWcxvZdluOMX0WrlF/S524DqtgULcRf3E9x3mumobkVGxI3UH73hXgGUcPi7XV
 ttLuy7fVbV2Cun/wvjBqD7kc8LNbKwSaT9y+/xS0D5QYjph4rpQiTG7S5rtPLPMU
 Z2lL1C5ggBCkD43g3orDvehbNWCL5g==
 =0vEe
 -----END PGP SIGNATURE-----

Merge tag 'v0.33.3rc2' into matrix-org-hotfixes

Bugfixes
--------

- Fix bug in v0.33.3rc1 which caused infinite loops and OOMs
([\#3723](https://github.com/matrix-org/synapse/issues/3723))
This commit is contained in:
hera 2018-08-21 19:12:14 +00:00
commit d1065e6f51
55 changed files with 181 additions and 85 deletions

View file

@ -1,3 +1,76 @@
Synapse 0.33.3rc2 (2018-08-21)
==============================
Bugfixes
--------
- Fix bug in v0.33.3rc1 which caused infinite loops and OOMs ([\#3723](https://github.com/matrix-org/synapse/issues/3723))
Synapse 0.33.3rc1 (2018-08-21)
==============================
Features
--------
- Add support for the SNI extension to federation TLS connections ([\#1491](https://github.com/matrix-org/synapse/issues/1491))
- Add /_media/r0/config ([\#3184](https://github.com/matrix-org/synapse/issues/3184))
- speed up /members API and add `at` and `membership` params as per MSC1227 ([\#3568](https://github.com/matrix-org/synapse/issues/3568))
- implement `summary` block in /sync response as per MSC688 ([\#3574](https://github.com/matrix-org/synapse/issues/3574))
- Add lazy-loading support to /messages as per MSC1227 ([\#3589](https://github.com/matrix-org/synapse/issues/3589))
- Add ability to limit number of monthly active users on the server ([\#3633](https://github.com/matrix-org/synapse/issues/3633))
- Support more federation endpoints on workers ([\#3653](https://github.com/matrix-org/synapse/issues/3653))
- Basic support for room versioning ([\#3654](https://github.com/matrix-org/synapse/issues/3654))
- Ability to disable client/server Synapse via conf toggle ([\#3655](https://github.com/matrix-org/synapse/issues/3655))
- Ability to whitelist specific threepids against monthly active user limiting ([\#3662](https://github.com/matrix-org/synapse/issues/3662))
- Add some metrics for the appservice and federation event sending loops ([\#3664](https://github.com/matrix-org/synapse/issues/3664))
- Where server is disabled, block ability for locked out users to read new messages ([\#3670](https://github.com/matrix-org/synapse/issues/3670))
- set admin uri via config, to be used in error messages where the user should contact the administrator ([\#3687](https://github.com/matrix-org/synapse/issues/3687))
- Synapse's presence functionality can now be disabled with the "use_presence" configuration option. ([\#3694](https://github.com/matrix-org/synapse/issues/3694))
- For resource limit blocked users, prevent writing into rooms ([\#3708](https://github.com/matrix-org/synapse/issues/3708))
Bugfixes
--------
- Fix occasional glitches in the synapse_event_persisted_position metric ([\#3658](https://github.com/matrix-org/synapse/issues/3658))
- Fix bug on deleting 3pid when using identity servers that don't support unbind API ([\#3661](https://github.com/matrix-org/synapse/issues/3661))
- Make the tests pass on Twisted < 18.7.0 ([\#3676](https://github.com/matrix-org/synapse/issues/3676))
- Dont ship recaptcha_ajax.js, use it directly from Google ([\#3677](https://github.com/matrix-org/synapse/issues/3677))
- Fixes test_reap_monthly_active_users so it passes under postgres ([\#3681](https://github.com/matrix-org/synapse/issues/3681))
- Fix mau blocking calulation bug on login ([\#3689](https://github.com/matrix-org/synapse/issues/3689))
- Fix missing yield in synapse.storage.monthly_active_users.initialise_reserved_users ([\#3692](https://github.com/matrix-org/synapse/issues/3692))
- Improve HTTP request logging to include all requests ([\#3700](https://github.com/matrix-org/synapse/issues/3700))
- Avoid timing out requests while we are streaming back the response ([\#3701](https://github.com/matrix-org/synapse/issues/3701))
- Support more federation endpoints on workers ([\#3705](https://github.com/matrix-org/synapse/issues/3705), [\#3713](https://github.com/matrix-org/synapse/issues/3713))
- Fix "Starting db txn 'get_all_updated_receipts' from sentinel context" warning ([\#3710](https://github.com/matrix-org/synapse/issues/3710))
- Fix bug where `state_cache` cache factor ignored environment variables ([\#3719](https://github.com/matrix-org/synapse/issues/3719))
Deprecations and Removals
-------------------------
- The Shared-Secret registration method of the legacy v1/register REST endpoint has been removed. For a replacement, please see [the admin/register API documentation](https://github.com/matrix-org/synapse/blob/master/docs/admin_api/register_api.rst). ([\#3703](https://github.com/matrix-org/synapse/issues/3703))
Internal Changes
----------------
- The test suite now can run under PostgreSQL. ([\#3423](https://github.com/matrix-org/synapse/issues/3423))
- Refactor HTTP replication endpoints to reduce code duplication ([\#3632](https://github.com/matrix-org/synapse/issues/3632))
- Tests now correctly execute on Python 3. ([\#3647](https://github.com/matrix-org/synapse/issues/3647))
- Sytests can now be run inside a Docker container. ([\#3660](https://github.com/matrix-org/synapse/issues/3660))
- Port over enough to Python 3 to allow the sytests to start. ([\#3668](https://github.com/matrix-org/synapse/issues/3668))
- Update docker base image from alpine 3.7 to 3.8. ([\#3669](https://github.com/matrix-org/synapse/issues/3669))
- Rename synapse.util.async to synapse.util.async_helpers to mitigate async becoming a keyword on Python 3.7. ([\#3678](https://github.com/matrix-org/synapse/issues/3678))
- Synapse's tests are now formatted with the black autoformatter. ([\#3679](https://github.com/matrix-org/synapse/issues/3679))
- Implemented a new testing base class to reduce test boilerplate. ([\#3684](https://github.com/matrix-org/synapse/issues/3684))
- Rename MAU prometheus metrics ([\#3690](https://github.com/matrix-org/synapse/issues/3690))
- add new error type ResourceLimit ([\#3707](https://github.com/matrix-org/synapse/issues/3707))
- Logcontexts for replication command handlers ([\#3709](https://github.com/matrix-org/synapse/issues/3709))
- Update admin register API documentation to reference a real user ID. ([\#3712](https://github.com/matrix-org/synapse/issues/3712))
Synapse 0.33.2 (2018-08-09) Synapse 0.33.2 (2018-08-09)
=========================== ===========================

View file

@ -1 +0,0 @@
Add support for the SNI extension to federation TLS connections

View file

@ -1 +0,0 @@
Add /_media/r0/config

View file

@ -1 +0,0 @@
The test suite now can run under PostgreSQL.

View file

@ -1 +0,0 @@
speed up /members API and add `at` and `membership` params as per MSC1227

View file

@ -1 +0,0 @@
implement `summary` block in /sync response as per MSC688

View file

@ -1 +0,0 @@
Add lazy-loading support to /messages as per MSC1227

View file

@ -1 +0,0 @@
Refactor HTTP replication endpoints to reduce code duplication

View file

@ -1 +0,0 @@
Add ability to limit number of monthly active users on the server

View file

@ -1 +0,0 @@
Tests now correctly execute on Python 3.

View file

@ -1 +0,0 @@
Support more federation endpoints on workers

View file

@ -1 +0,0 @@
Basic support for room versioning

View file

@ -1 +0,0 @@
Ability to disable client/server Synapse via conf toggle

View file

@ -1 +0,0 @@
Fix occasional glitches in the synapse_event_persisted_position metric

View file

@ -1 +0,0 @@
Sytests can now be run inside a Docker container.

View file

@ -1 +0,0 @@
Fix bug on deleting 3pid when using identity servers that don't support unbind API

View file

@ -1 +0,0 @@
Ability to whitelist specific threepids against monthly active user limiting

View file

@ -1 +0,0 @@
Add some metrics for the appservice and federation event sending loops

View file

@ -1 +0,0 @@
Update docker base image from alpine 3.7 to 3.8.

View file

@ -1 +0,0 @@
Where server is disabled, block ability for locked out users to read new messages

View file

@ -1 +0,0 @@
Make the tests pass on Twisted < 18.7.0

View file

@ -1 +0,0 @@
Dont ship recaptcha_ajax.js, use it directly from Google

View file

@ -1 +0,0 @@
Rename synapse.util.async to synapse.util.async_helpers to mitigate async becoming a keyword on Python 3.7.

View file

@ -1 +0,0 @@
Synapse's tests are now formatted with the black autoformatter.

View file

@ -1 +0,0 @@
Fixes test_reap_monthly_active_users so it passes under postgres

View file

@ -1 +0,0 @@
Implemented a new testing base class to reduce test boilerplate.

View file

@ -1 +0,0 @@
set admin uri via config, to be used in error messages where the user should contact the administrator

View file

@ -1 +0,0 @@
Fix mau blocking calulation bug on login

View file

@ -1 +0,0 @@
Rename MAU prometheus metrics

View file

@ -1 +0,0 @@
Fix missing yield in synapse.storage.monthly_active_users.initialise_reserved_users

View file

@ -1 +0,0 @@
Synapse's presence functionality can now be disabled with the "use_presence" configuration option.

View file

@ -1 +0,0 @@
Improve HTTP request logging to include all requests

View file

@ -1 +0,0 @@
Avoid timing out requests while we are streaming back the response

View file

@ -1 +0,0 @@
The Shared-Secret registration method of the legacy v1/register REST endpoint has been removed. For a replacement, please see [the admin/register API documentation](https://github.com/matrix-org/synapse/blob/master/docs/admin_api/register_api.rst).

View file

@ -1 +0,0 @@
Support more federation endpoints on workers

View file

@ -1 +0,0 @@
add new error type ResourceLimit

View file

@ -1 +0,0 @@
For resource limit blocked users, prevent writing into rooms

View file

@ -1 +0,0 @@
Logcontexts for replication command handlers

View file

@ -1 +0,0 @@
Fix "Starting db txn 'get_all_updated_receipts' from sentinel context" warning

View file

@ -1 +0,0 @@
Update admin register API documentation to reference a real user ID.

View file

@ -1 +0,0 @@
Support more federation endpoints on workers

View file

@ -1 +0,0 @@
Fix bug where `state_cache` cache factor ignored environment variables

View file

@ -17,4 +17,4 @@
""" This is a reference implementation of a Matrix home server. """ This is a reference implementation of a Matrix home server.
""" """
__version__ = "0.33.2" __version__ = "0.33.3rc2"

View file

@ -211,7 +211,7 @@ class Auth(object):
user_agent = request.requestHeaders.getRawHeaders( user_agent = request.requestHeaders.getRawHeaders(
b"User-Agent", b"User-Agent",
default=[b""] default=[b""]
)[0] )[0].decode('ascii', 'surrogateescape')
if user and access_token and ip_addr: if user and access_token and ip_addr:
yield self.store.insert_client_ip( yield self.store.insert_client_ip(
user_id=user.to_string(), user_id=user.to_string(),
@ -682,7 +682,7 @@ class Auth(object):
Returns: Returns:
bool: False if no access_token was given, True otherwise. bool: False if no access_token was given, True otherwise.
""" """
query_params = request.args.get("access_token") query_params = request.args.get(b"access_token")
auth_headers = request.requestHeaders.getRawHeaders(b"Authorization") auth_headers = request.requestHeaders.getRawHeaders(b"Authorization")
return bool(query_params) or bool(auth_headers) return bool(query_params) or bool(auth_headers)
@ -698,7 +698,7 @@ class Auth(object):
401 since some of the old clients depended on auth errors returning 401 since some of the old clients depended on auth errors returning
403. 403.
Returns: Returns:
str: The access_token unicode: The access_token
Raises: Raises:
AuthError: If there isn't an access_token in the request. AuthError: If there isn't an access_token in the request.
""" """
@ -720,9 +720,9 @@ class Auth(object):
"Too many Authorization headers.", "Too many Authorization headers.",
errcode=Codes.MISSING_TOKEN, errcode=Codes.MISSING_TOKEN,
) )
parts = auth_headers[0].split(" ") parts = auth_headers[0].split(b" ")
if parts[0] == "Bearer" and len(parts) == 2: if parts[0] == b"Bearer" and len(parts) == 2:
return parts[1] return parts[1].decode('ascii')
else: else:
raise AuthError( raise AuthError(
token_not_found_http_status, token_not_found_http_status,
@ -738,7 +738,7 @@ class Auth(object):
errcode=Codes.MISSING_TOKEN errcode=Codes.MISSING_TOKEN
) )
return query_params[0] return query_params[0].decode('ascii')
@defer.inlineCallbacks @defer.inlineCallbacks
def check_in_room_or_world_readable(self, room_id, user_id): def check_in_room_or_world_readable(self, room_id, user_id):

View file

@ -72,7 +72,7 @@ class Ratelimiter(object):
return allowed, time_allowed return allowed, time_allowed
def prune_message_counts(self, time_now_s): def prune_message_counts(self, time_now_s):
for user_id in self.message_counts.keys(): for user_id in list(self.message_counts.keys()):
message_count, time_start, msg_rate_hz = ( message_count, time_start, msg_rate_hz = (
self.message_counts[user_id] self.message_counts[user_id]
) )

View file

@ -168,7 +168,8 @@ def setup_logging(config, use_worker_options=False):
if log_file: if log_file:
# TODO: Customisable file size / backup count # TODO: Customisable file size / backup count
handler = logging.handlers.RotatingFileHandler( handler = logging.handlers.RotatingFileHandler(
log_file, maxBytes=(1000 * 1000 * 100), backupCount=3 log_file, maxBytes=(1000 * 1000 * 100), backupCount=3,
encoding='utf8'
) )
def sighup(signum, stack): def sighup(signum, stack):

View file

@ -29,7 +29,7 @@ def parse_integer(request, name, default=None, required=False):
Args: Args:
request: the twisted HTTP request. request: the twisted HTTP request.
name (str): the name of the query parameter. name (bytes/unicode): the name of the query parameter.
default (int|None): value to use if the parameter is absent, defaults default (int|None): value to use if the parameter is absent, defaults
to None. to None.
required (bool): whether to raise a 400 SynapseError if the required (bool): whether to raise a 400 SynapseError if the
@ -46,6 +46,10 @@ def parse_integer(request, name, default=None, required=False):
def parse_integer_from_args(args, name, default=None, required=False): def parse_integer_from_args(args, name, default=None, required=False):
if not isinstance(name, bytes):
name = name.encode('ascii')
if name in args: if name in args:
try: try:
return int(args[name][0]) return int(args[name][0])
@ -65,7 +69,7 @@ def parse_boolean(request, name, default=None, required=False):
Args: Args:
request: the twisted HTTP request. request: the twisted HTTP request.
name (str): the name of the query parameter. name (bytes/unicode): the name of the query parameter.
default (bool|None): value to use if the parameter is absent, defaults default (bool|None): value to use if the parameter is absent, defaults
to None. to None.
required (bool): whether to raise a 400 SynapseError if the required (bool): whether to raise a 400 SynapseError if the
@ -83,11 +87,15 @@ def parse_boolean(request, name, default=None, required=False):
def parse_boolean_from_args(args, name, default=None, required=False): def parse_boolean_from_args(args, name, default=None, required=False):
if not isinstance(name, bytes):
name = name.encode('ascii')
if name in args: if name in args:
try: try:
return { return {
"true": True, b"true": True,
"false": False, b"false": False,
}[args[name][0]] }[args[name][0]]
except Exception: except Exception:
message = ( message = (
@ -104,21 +112,29 @@ def parse_boolean_from_args(args, name, default=None, required=False):
def parse_string(request, name, default=None, required=False, def parse_string(request, name, default=None, required=False,
allowed_values=None, param_type="string"): allowed_values=None, param_type="string", encoding='ascii'):
"""Parse a string parameter from the request query string. """
Parse a string parameter from the request query string.
If encoding is not None, the content of the query param will be
decoded to Unicode using the encoding, otherwise it will be encoded
Args: Args:
request: the twisted HTTP request. request: the twisted HTTP request.
name (str): the name of the query parameter. name (bytes/unicode): the name of the query parameter.
default (str|None): value to use if the parameter is absent, defaults default (bytes/unicode|None): value to use if the parameter is absent,
to None. defaults to None. Must be bytes if encoding is None.
required (bool): whether to raise a 400 SynapseError if the required (bool): whether to raise a 400 SynapseError if the
parameter is absent, defaults to False. parameter is absent, defaults to False.
allowed_values (list[str]): List of allowed values for the string, allowed_values (list[bytes/unicode]): List of allowed values for the
or None if any value is allowed, defaults to None string, or None if any value is allowed, defaults to None. Must be
the same type as name, if given.
encoding: The encoding to decode the name to, and decode the string
content with.
Returns: Returns:
str|None: A string value or the default. bytes/unicode|None: A string value or the default. Unicode if encoding
was given, bytes otherwise.
Raises: Raises:
SynapseError if the parameter is absent and required, or if the SynapseError if the parameter is absent and required, or if the
@ -126,14 +142,22 @@ def parse_string(request, name, default=None, required=False,
is not one of those allowed values. is not one of those allowed values.
""" """
return parse_string_from_args( return parse_string_from_args(
request.args, name, default, required, allowed_values, param_type, request.args, name, default, required, allowed_values, param_type, encoding
) )
def parse_string_from_args(args, name, default=None, required=False, def parse_string_from_args(args, name, default=None, required=False,
allowed_values=None, param_type="string"): allowed_values=None, param_type="string", encoding='ascii'):
if not isinstance(name, bytes):
name = name.encode('ascii')
if name in args: if name in args:
value = args[name][0] value = args[name][0]
if encoding:
value = value.decode(encoding)
if allowed_values is not None and value not in allowed_values: if allowed_values is not None and value not in allowed_values:
message = "Query parameter %r must be one of [%s]" % ( message = "Query parameter %r must be one of [%s]" % (
name, ", ".join(repr(v) for v in allowed_values) name, ", ".join(repr(v) for v in allowed_values)
@ -146,6 +170,10 @@ def parse_string_from_args(args, name, default=None, required=False,
message = "Missing %s query parameter %r" % (param_type, name) message = "Missing %s query parameter %r" % (param_type, name)
raise SynapseError(400, message, errcode=Codes.MISSING_PARAM) raise SynapseError(400, message, errcode=Codes.MISSING_PARAM)
else: else:
if encoding and isinstance(default, bytes):
return default.decode(encoding)
return default return default

View file

@ -182,7 +182,7 @@ class SynapseRequest(Request):
# the client disconnects. # the client disconnects.
with PreserveLoggingContext(self.logcontext): with PreserveLoggingContext(self.logcontext):
logger.warn( logger.warn(
"Error processing request: %s %s", reason.type, reason.value, "Error processing request %r: %s %s", self, reason.type, reason.value,
) )
if not self._is_processing: if not self._is_processing:
@ -219,6 +219,12 @@ class SynapseRequest(Request):
"""Log the completion of this request and update the metrics """Log the completion of this request and update the metrics
""" """
if self.logcontext is None:
# this can happen if the connection closed before we read the
# headers (so render was never called). In that case we'll already
# have logged a warning, so just bail out.
return
usage = self.logcontext.get_resource_usage() usage = self.logcontext.get_resource_usage()
if self._processing_finished_time is None: if self._processing_finished_time is None:
@ -235,7 +241,7 @@ class SynapseRequest(Request):
# need to decode as it could be raw utf-8 bytes # need to decode as it could be raw utf-8 bytes
# from a IDN servname in an auth header # from a IDN servname in an auth header
authenticated_entity = self.authenticated_entity authenticated_entity = self.authenticated_entity
if authenticated_entity is not None: if authenticated_entity is not None and isinstance(authenticated_entity, bytes):
authenticated_entity = authenticated_entity.decode("utf-8", "replace") authenticated_entity = authenticated_entity.decode("utf-8", "replace")
# ...or could be raw utf-8 bytes in the User-Agent header. # ...or could be raw utf-8 bytes in the User-Agent header.
@ -328,7 +334,7 @@ class SynapseSite(Site):
proxied = config.get("x_forwarded", False) proxied = config.get("x_forwarded", False)
self.requestFactory = SynapseRequestFactory(self, proxied) self.requestFactory = SynapseRequestFactory(self, proxied)
self.access_logger = logging.getLogger(logger_name) self.access_logger = logging.getLogger(logger_name)
self.server_version_string = server_version_string self.server_version_string = server_version_string.encode('ascii')
def log(self, request): def log(self, request):
pass pass

View file

@ -53,7 +53,7 @@ class HttpTransactionCache(object):
str: A transaction key str: A transaction key
""" """
token = self.auth.get_access_token_from_request(request) token = self.auth.get_access_token_from_request(request)
return request.path + "/" + token return request.path.decode('utf8') + "/" + token
def fetch_or_execute_request(self, request, fn, *args, **kwargs): def fetch_or_execute_request(self, request, fn, *args, **kwargs):
"""A helper function for fetch_or_execute which extracts """A helper function for fetch_or_execute which extracts

View file

@ -55,7 +55,7 @@ class UploadResource(Resource):
requester = yield self.auth.get_user_by_req(request) requester = yield self.auth.get_user_by_req(request)
# TODO: The checks here are a bit late. The content will have # TODO: The checks here are a bit late. The content will have
# already been uploaded to a tmp file at this point # already been uploaded to a tmp file at this point
content_length = request.getHeader("Content-Length") content_length = request.getHeader(b"Content-Length").decode('ascii')
if content_length is None: if content_length is None:
raise SynapseError( raise SynapseError(
msg="Request must specify a Content-Length", code=400 msg="Request must specify a Content-Length", code=400
@ -66,10 +66,10 @@ class UploadResource(Resource):
code=413, code=413,
) )
upload_name = parse_string(request, "filename") upload_name = parse_string(request, b"filename", encoding=None)
if upload_name: if upload_name:
try: try:
upload_name = upload_name.decode('UTF-8') upload_name = upload_name.decode('utf8')
except UnicodeDecodeError: except UnicodeDecodeError:
raise SynapseError( raise SynapseError(
msg="Invalid UTF-8 filename parameter: %r" % (upload_name), msg="Invalid UTF-8 filename parameter: %r" % (upload_name),
@ -78,8 +78,8 @@ class UploadResource(Resource):
headers = request.requestHeaders headers = request.requestHeaders
if headers.hasHeader("Content-Type"): if headers.hasHeader(b"Content-Type"):
media_type = headers.getRawHeaders(b"Content-Type")[0] media_type = headers.getRawHeaders(b"Content-Type")[0].decode('ascii')
else: else:
raise SynapseError( raise SynapseError(
msg="Upload request missing 'Content-Type'", msg="Upload request missing 'Content-Type'",

View file

@ -38,4 +38,4 @@ else:
return os.urandom(nbytes) return os.urandom(nbytes)
def token_hex(self, nbytes=32): def token_hex(self, nbytes=32):
return binascii.hexlify(self.token_bytes(nbytes)) return binascii.hexlify(self.token_bytes(nbytes)).decode('ascii')

View file

@ -385,7 +385,13 @@ class LoggingContextFilter(logging.Filter):
context = LoggingContext.current_context() context = LoggingContext.current_context()
for key, value in self.defaults.items(): for key, value in self.defaults.items():
setattr(record, key, value) setattr(record, key, value)
# context should never be None, but if it somehow ends up being, then
# we end up in a death spiral of infinite loops, so let's check, for
# robustness' sake.
if context is not None:
context.copy_to(record) context.copy_to(record)
return True return True
@ -396,7 +402,9 @@ class PreserveLoggingContext(object):
__slots__ = ["current_context", "new_context", "has_parent"] __slots__ = ["current_context", "new_context", "has_parent"]
def __init__(self, new_context=LoggingContext.sentinel): def __init__(self, new_context=None):
if new_context is None:
new_context = LoggingContext.sentinel
self.new_context = new_context self.new_context = new_context
def __enter__(self): def __enter__(self):

View file

@ -20,6 +20,8 @@ import time
from functools import wraps from functools import wraps
from inspect import getcallargs from inspect import getcallargs
from six import PY3
_TIME_FUNC_ID = 0 _TIME_FUNC_ID = 0
@ -28,6 +30,10 @@ def _log_debug_as_f(f, msg, msg_args):
logger = logging.getLogger(name) logger = logging.getLogger(name)
if logger.isEnabledFor(logging.DEBUG): if logger.isEnabledFor(logging.DEBUG):
if PY3:
lineno = f.__code__.co_firstlineno
pathname = f.__code__.co_filename
else:
lineno = f.func_code.co_firstlineno lineno = f.func_code.co_firstlineno
pathname = f.func_code.co_filename pathname = f.func_code.co_filename

View file

@ -16,6 +16,7 @@
import random import random
import string import string
from six import PY3
from six.moves import range from six.moves import range
_string_with_symbols = ( _string_with_symbols = (
@ -34,6 +35,17 @@ def random_string_with_symbols(length):
def is_ascii(s): def is_ascii(s):
if PY3:
if isinstance(s, bytes):
try:
s.decode('ascii').encode('ascii')
except UnicodeDecodeError:
return False
except UnicodeEncodeError:
return False
return True
try: try:
s.encode("ascii") s.encode("ascii")
except UnicodeEncodeError: except UnicodeEncodeError:
@ -49,6 +61,9 @@ def to_ascii(s):
If given None then will return None. If given None then will return None.
""" """
if PY3:
return s
if s is None: if s is None:
return None return None

View file

@ -30,7 +30,7 @@ def get_version_string(module):
['git', 'rev-parse', '--abbrev-ref', 'HEAD'], ['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
stderr=null, stderr=null,
cwd=cwd, cwd=cwd,
).strip() ).strip().decode('ascii')
git_branch = "b=" + git_branch git_branch = "b=" + git_branch
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
git_branch = "" git_branch = ""
@ -40,7 +40,7 @@ def get_version_string(module):
['git', 'describe', '--exact-match'], ['git', 'describe', '--exact-match'],
stderr=null, stderr=null,
cwd=cwd, cwd=cwd,
).strip() ).strip().decode('ascii')
git_tag = "t=" + git_tag git_tag = "t=" + git_tag
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
git_tag = "" git_tag = ""
@ -50,7 +50,7 @@ def get_version_string(module):
['git', 'rev-parse', '--short', 'HEAD'], ['git', 'rev-parse', '--short', 'HEAD'],
stderr=null, stderr=null,
cwd=cwd, cwd=cwd,
).strip() ).strip().decode('ascii')
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
git_commit = "" git_commit = ""
@ -60,7 +60,7 @@ def get_version_string(module):
['git', 'describe', '--dirty=' + dirty_string], ['git', 'describe', '--dirty=' + dirty_string],
stderr=null, stderr=null,
cwd=cwd, cwd=cwd,
).strip().endswith(dirty_string) ).strip().decode('ascii').endswith(dirty_string)
git_dirty = "dirty" if is_dirty else "" git_dirty = "dirty" if is_dirty else ""
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
@ -77,8 +77,8 @@ def get_version_string(module):
"%s (%s)" % ( "%s (%s)" % (
module.__version__, git_version, module.__version__, git_version,
) )
).encode("ascii") )
except Exception as e: except Exception as e:
logger.info("Failed to check for git repository: %s", e) logger.info("Failed to check for git repository: %s", e)
return module.__version__.encode("ascii") return module.__version__