Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Prefer type(x) is int to isinstance(x, int) #14945

Merged
merged 2 commits into from
Jan 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/14945.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix various long-standing bugs in Synapse's config, event and request handling where booleans were unintentionally accepted where an integer was expected.
72 changes: 50 additions & 22 deletions synapse/config/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,29 @@ def __init__(self, root_config: "RootConfig" = None):

@staticmethod
def parse_size(value: Union[str, int]) -> int:
if isinstance(value, int):
"""Interpret `value` as a number of bytes.

If an integer is provided it is treated as bytes and is unchanged.

String byte sizes can have a suffix of 'K' or `M`, representing kibibytes and
mebibytes respectively. No suffix is understood as a plain byte count.

Raises:
TypeError, if given something other than an integer or a string
ValueError: if given a string not of the form described above.
"""
if type(value) is int:
return value
sizes = {"K": 1024, "M": 1024 * 1024}
size = 1
suffix = value[-1]
if suffix in sizes:
value = value[:-1]
size = sizes[suffix]
return int(value) * size
elif type(value) is str:
sizes = {"K": 1024, "M": 1024 * 1024}
size = 1
suffix = value[-1]
if suffix in sizes:
value = value[:-1]
size = sizes[suffix]
return int(value) * size
else:
raise TypeError(f"Bad byte size {value!r}")

@staticmethod
def parse_duration(value: Union[str, int]) -> int:
Expand All @@ -198,22 +212,36 @@ def parse_duration(value: Union[str, int]) -> int:

Returns:
The number of milliseconds in the duration.

Raises:
TypeError, if given something other than an integer or a string
ValueError: if given a string not of the form described above.
"""
if isinstance(value, int):
if type(value) is int:
return value
second = 1000
minute = 60 * second
hour = 60 * minute
day = 24 * hour
week = 7 * day
year = 365 * day
sizes = {"s": second, "m": minute, "h": hour, "d": day, "w": week, "y": year}
size = 1
suffix = value[-1]
if suffix in sizes:
value = value[:-1]
size = sizes[suffix]
return int(value) * size
elif type(value) is str:
second = 1000
minute = 60 * second
hour = 60 * minute
day = 24 * hour
week = 7 * day
year = 365 * day
sizes = {
"s": second,
"m": minute,
"h": hour,
"d": day,
"w": week,
"y": year,
}
size = 1
suffix = value[-1]
if suffix in sizes:
value = value[:-1]
size = sizes[suffix]
return int(value) * size
else:
raise TypeError(f"Bad duration {value!r}")

@staticmethod
def abspath(file_path: str) -> str:
Expand Down
4 changes: 2 additions & 2 deletions synapse/config/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None:

cache_config = config.get("caches") or {}
self.global_factor = cache_config.get("global_factor", _DEFAULT_FACTOR_SIZE)
if not isinstance(self.global_factor, (int, float)):
if type(self.global_factor) not in (int, float):
raise ConfigError("caches.global_factor must be a number.")

# Load cache factors from the config
Expand All @@ -151,7 +151,7 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None:
)

for cache, factor in individual_factors.items():
if not isinstance(factor, (int, float)):
if type(factor) not in (int, float):
raise ConfigError(
"caches.per_cache_factors.%s must be a number" % (cache,)
)
Expand Down
2 changes: 1 addition & 1 deletion synapse/config/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -904,7 +904,7 @@ def parse_listener_def(num: int, listener: Any) -> ListenerConfig:
raise ConfigError(DIRECT_TCP_ERROR, ("listeners", str(num), "type"))

port = listener.get("port")
if not isinstance(port, int):
if type(port) is not int:
raise ConfigError("Listener configuration is lacking a valid 'port' option")

tls = listener.get("tls", False)
Expand Down
4 changes: 2 additions & 2 deletions synapse/events/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,15 @@ def _validate_retention(self, event: EventBase) -> None:
max_lifetime = event.content.get("max_lifetime")

if min_lifetime is not None:
if not isinstance(min_lifetime, int):
if type(min_lifetime) is not int:
raise SynapseError(
code=400,
msg="'min_lifetime' must be an integer",
errcode=Codes.BAD_JSON,
)

if max_lifetime is not None:
if not isinstance(max_lifetime, int):
if type(max_lifetime) is not int:
raise SynapseError(
code=400,
msg="'max_lifetime' must be an integer",
Expand Down
2 changes: 1 addition & 1 deletion synapse/federation/federation_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1864,7 +1864,7 @@ def from_json_dict(cls, d: JsonDict) -> "TimestampToEventResponse":
)

origin_server_ts = d.get("origin_server_ts")
if not isinstance(origin_server_ts, int):
if type(origin_server_ts) is not int:
raise ValueError(
"Invalid response: 'origin_server_ts' must be a int but received %r"
% origin_server_ts
Expand Down
2 changes: 1 addition & 1 deletion synapse/handlers/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ def maybe_schedule_expiry(self, event: EventBase) -> None:
"""

expiry_ts = event.content.get(EventContentFields.SELF_DESTRUCT_AFTER)
if not isinstance(expiry_ts, int) or event.is_state():
if type(expiry_ts) is not int or event.is_state():
return

# _schedule_expiry_for_event won't actually schedule anything if there's already
Expand Down
2 changes: 1 addition & 1 deletion synapse/rest/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ async def on_POST(
logger.info("[purge] purging up to token %s (event_id %s)", token, event_id)
elif "purge_up_to_ts" in body:
ts = body["purge_up_to_ts"]
if not isinstance(ts, int):
if type(ts) is not int:
raise SynapseError(
HTTPStatus.BAD_REQUEST,
"purge_up_to_ts must be an int",
Expand Down
15 changes: 7 additions & 8 deletions synapse/rest/admin/registration_tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
else:
# Get length of token to generate (default is 16)
length = body.get("length", 16)
if not isinstance(length, int):
if type(length) is not int:
raise SynapseError(
HTTPStatus.BAD_REQUEST,
"length must be an integer",
Expand All @@ -163,8 +163,7 @@ async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:

uses_allowed = body.get("uses_allowed", None)
if not (
uses_allowed is None
or (isinstance(uses_allowed, int) and uses_allowed >= 0)
uses_allowed is None or (type(uses_allowed) is int and uses_allowed >= 0)
):
raise SynapseError(
HTTPStatus.BAD_REQUEST,
Expand All @@ -173,13 +172,13 @@ async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
)

expiry_time = body.get("expiry_time", None)
if not isinstance(expiry_time, (int, type(None))):
if type(expiry_time) not in (int, type(None)):
raise SynapseError(
HTTPStatus.BAD_REQUEST,
"expiry_time must be an integer or null",
Codes.INVALID_PARAM,
)
if isinstance(expiry_time, int) and expiry_time < self.clock.time_msec():
if type(expiry_time) is int and expiry_time < self.clock.time_msec():
raise SynapseError(
HTTPStatus.BAD_REQUEST,
"expiry_time must not be in the past",
Expand Down Expand Up @@ -284,7 +283,7 @@ async def on_PUT(self, request: SynapseRequest, token: str) -> Tuple[int, JsonDi
uses_allowed = body["uses_allowed"]
if not (
uses_allowed is None
or (isinstance(uses_allowed, int) and uses_allowed >= 0)
or (type(uses_allowed) is int and uses_allowed >= 0)
):
raise SynapseError(
HTTPStatus.BAD_REQUEST,
Expand All @@ -295,13 +294,13 @@ async def on_PUT(self, request: SynapseRequest, token: str) -> Tuple[int, JsonDi

if "expiry_time" in body:
expiry_time = body["expiry_time"]
if not isinstance(expiry_time, (int, type(None))):
if type(expiry_time) not in (int, type(None)):
raise SynapseError(
HTTPStatus.BAD_REQUEST,
"expiry_time must be an integer or null",
Codes.INVALID_PARAM,
)
if isinstance(expiry_time, int) and expiry_time < self.clock.time_msec():
if type(expiry_time) is int and expiry_time < self.clock.time_msec():
raise SynapseError(
HTTPStatus.BAD_REQUEST,
"expiry_time must not be in the past",
Expand Down
6 changes: 3 additions & 3 deletions synapse/rest/admin/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -973,7 +973,7 @@ async def on_POST(
body = parse_json_object_from_request(request, allow_empty_body=True)

valid_until_ms = body.get("valid_until_ms")
if valid_until_ms and not isinstance(valid_until_ms, int):
if type(valid_until_ms) not in (int, type(None)):
raise SynapseError(
HTTPStatus.BAD_REQUEST, "'valid_until_ms' parameter must be an int"
)
Expand Down Expand Up @@ -1125,14 +1125,14 @@ async def on_POST(
messages_per_second = body.get("messages_per_second", 0)
burst_count = body.get("burst_count", 0)

if not isinstance(messages_per_second, int) or messages_per_second < 0:
if type(messages_per_second) is not int or messages_per_second < 0:
raise SynapseError(
HTTPStatus.BAD_REQUEST,
"%r parameter must be a positive int" % (messages_per_second,),
errcode=Codes.INVALID_PARAM,
)

if not isinstance(burst_count, int) or burst_count < 0:
if type(burst_count) is not int or burst_count < 0:
raise SynapseError(
HTTPStatus.BAD_REQUEST,
"%r parameter must be a positive int" % (burst_count,),
Expand Down
2 changes: 1 addition & 1 deletion synapse/rest/client/report_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ async def on_POST(
"Param 'reason' must be a string",
Codes.BAD_JSON,
)
if not isinstance(body.get("score", 0), int):
if type(body.get("score", 0)) is not int:
raise SynapseError(
HTTPStatus.BAD_REQUEST,
"Param 'score' must be an integer",
Expand Down
2 changes: 1 addition & 1 deletion synapse/rest/media/v1/oembed.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def parse_oembed_response(self, url: str, raw_body: bytes) -> OEmbedResult:
calc_description_and_urls(open_graph_response, oembed["html"])
for size in ("width", "height"):
val = oembed.get(size)
if val is not None and isinstance(val, int):
if type(val) is int:
open_graph_response[f"og:video:{size}"] = val

elif oembed_type == "link":
Expand Down
2 changes: 1 addition & 1 deletion synapse/rest/media/v1/thumbnailer.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def __init__(self, input_path: str):
image_exif = self.image._getexif() # type: ignore
if image_exif is not None:
image_orientation = image_exif.get(EXIF_ORIENTATION_TAG)
assert isinstance(image_orientation, int)
assert type(image_orientation) is int
self.transpose_method = EXIF_TRANSPOSE_MAPPINGS.get(image_orientation)
except Exception as e:
# A lot of parsing errors can happen when parsing EXIF
Expand Down
6 changes: 3 additions & 3 deletions synapse/storage/databases/main/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -1651,7 +1651,7 @@ def _update_metadata_tables_txn(
if self._ephemeral_messages_enabled:
# If there's an expiry timestamp on the event, store it.
expiry_ts = event.content.get(EventContentFields.SELF_DESTRUCT_AFTER)
if isinstance(expiry_ts, int) and not event.is_state():
if type(expiry_ts) is int and not event.is_state():
self._insert_event_expiry_txn(txn, event.event_id, expiry_ts)

# Insert into the room_memberships table.
Expand Down Expand Up @@ -2133,10 +2133,10 @@ def _store_retention_policy_for_room_txn(
):
if (
"min_lifetime" in event.content
and not isinstance(event.content.get("min_lifetime"), int)
and type(event.content["min_lifetime"]) is not int
) or (
"max_lifetime" in event.content
and not isinstance(event.content.get("max_lifetime"), int)
and type(event.content["max_lifetime"]) is not int
):
# Ignore the event if one of the value isn't an integer.
return
Expand Down