Skip to content

Commit

Permalink
Tighten up ruff ignore list (#18837)
Browse files Browse the repository at this point in the history
* Tighten up ruff ignore list

* Okay that fix was indeed unsafe

* enable type-name-incorrect-variance

* enable literal-membership

* enable non-augmented-assignment

* enable useless-return

* enable global-variable-not-assigned

* -

* fixup

* Clean up roff.toml from annotations

* use ignore instead of explicit re-export

* use more descriptive names

---------

Co-authored-by: Kyle Altendorf <[email protected]>
  • Loading branch information
Quexington and altendky authored Nov 12, 2024
1 parent 06b2447 commit 6c90a76
Show file tree
Hide file tree
Showing 100 changed files with 195 additions and 238 deletions.
2 changes: 1 addition & 1 deletion activated.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def main(*args: str) -> int:
script = "activated.sh"
command = ["sh", os.fspath(here.joinpath(script)), env.value, *args]

completed_process = subprocess.run(command)
completed_process = subprocess.run(command, check=False)

return completed_process.returncode

Expand Down
4 changes: 2 additions & 2 deletions benchmarks/streamable.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ def run(data: Data, mode: Mode, runs: int, ms: int, live: bool, output: TextIO,
results: dict[Data, dict[Mode, list[list[int]]]] = {}
bench_results: dict[str, Any] = {"version": _version, "commit_hash": get_commit_hash()}
for current_data, parameter in benchmark_parameter.items():
if data == Data.all or current_data == data:
if data in {Data.all, current_data}:
results[current_data] = {}
bench_results[current_data] = {}
print(
Expand All @@ -252,7 +252,7 @@ def run(data: Data, mode: Mode, runs: int, ms: int, live: bool, output: TextIO,
)
for current_mode, current_mode_parameter in parameter.mode_parameter.items():
results[current_data][current_mode] = []
if mode == Mode.all or current_mode == mode:
if mode in {Mode.all, current_mode}:
us_iteration_results: list[int]
all_results: list[list[int]] = results[current_data][current_mode]
obj = parameter.object_creation_cb()
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/blockchain/test_blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -2623,7 +2623,7 @@ async def test_cost_exceeds_max(
fork_info=fork_info,
)
)[1]
assert err in [Err.BLOCK_COST_EXCEEDS_MAX]
assert err == Err.BLOCK_COST_EXCEEDS_MAX
future = await pre_validate_block(
b.constants,
AugmentedBlockchain(b),
Expand Down
1 change: 0 additions & 1 deletion chia/_tests/cmds/wallet/test_vcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,6 @@ def test_vcs_add_proof_reveal(capsys: object, get_test_cli_clients: tuple[TestRp
class VcsAddProofRevealRpcClient(TestWalletRpcClient):
async def vc_add_proofs(self, proofs: dict[str, Any]) -> None:
self.add_to_log("vc_add_proofs", (proofs,))
return None

inst_rpc_client = VcsAddProofRevealRpcClient()
test_rpc_clients.wallet_rpc_client = inst_rpc_client
Expand Down
2 changes: 0 additions & 2 deletions chia/_tests/cmds/wallet/test_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,6 @@ def test_del_unconfirmed_tx(capsys: object, get_test_cli_clients: tuple[TestRpcC
class UnconfirmedTxRpcClient(TestWalletRpcClient):
async def delete_unconfirmed_transactions(self, wallet_id: int) -> None:
self.add_to_log("delete_unconfirmed_transactions", (wallet_id,))
return None

inst_rpc_client = UnconfirmedTxRpcClient()
test_rpc_clients.wallet_rpc_client = inst_rpc_client
Expand Down Expand Up @@ -655,7 +654,6 @@ async def create_wallet_for_existing_cat(self, asset_id: bytes) -> dict[str, int

async def set_cat_name(self, wallet_id: int, name: str) -> None:
self.add_to_log("set_cat_name", (wallet_id, name))
return None # we don't need to do anything here

inst_rpc_client = AddTokenRpcClient()
test_rpc_clients.wallet_rpc_client = inst_rpc_client
Expand Down
4 changes: 2 additions & 2 deletions chia/_tests/core/data_layer/test_data_layer_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,14 @@ def definition(key: bytes, value: bytes) -> bytes32:

data: list[tuple[bytes, bytes, bytes32]] = []
for cycle in range(20000):
if cycle in (0, 1):
if cycle in {0, 1}:
length = 0
else:
length = seeded_random.randrange(100)

key = get_random_bytes(length=length, r=seeded_random)

if cycle in (1, 2):
if cycle in {1, 2}:
length = 0
else:
length = seeded_random.randrange(100)
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/core/data_layer/test_data_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -3087,7 +3087,7 @@ async def test_pagination_cmds(
)
elif layer == InterfaceLayer.cli:
for command in ("get_keys", "get_keys_values", "get_kv_diff"):
if command == "get_keys" or command == "get_keys_values":
if command in {"get_keys", "get_keys_values"}:
args: list[str] = [
sys.executable,
"-m",
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/core/data_layer/test_data_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ async def test_batch_update(
[0.4, 0.2, 0.2, 0.2],
k=1,
)
if op_type == "insert" or op_type == "upsert-insert" or len(keys_values) == 0:
if op_type in {"insert", "upsert-insert"} or len(keys_values) == 0:
if len(keys_values) == 0:
op_type = "insert"
key = operation.to_bytes(4, byteorder="big")
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/core/data_layer/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def run(
kwargs["stderr"] = stderr

try:
return subprocess.run(*final_args, **kwargs)
return subprocess.run(*final_args, **kwargs) # noqa: PLW1510
except OSError as e:
raise Exception(f"failed to run:\n {final_args}\n {kwargs}") from e

Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/core/full_node/test_full_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ async def have_msgs():
if msg is not None and not (len(msg.peer_list) == 1):
return False
peer = msg.peer_list[0]
return (peer.host == self_hostname or peer.host == "127.0.0.1") and peer.port == 1000
return (peer.host in {self_hostname, "127.0.0.1"}) and peer.port == 1000

await time_out_assert_custom_interval(10, 1, have_msgs, True)
full_node_1.full_node.full_node_peers.address_manager = AddressManager()
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/core/make_block_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@


def int_to_public_key(index: int) -> G1Element:
index = index % GROUP_ORDER
index %= GROUP_ORDER
private_key_from_int = PrivateKey.from_bytes(index.to_bytes(32, "big"))
return private_key_from_int.get_g1()

Expand Down
8 changes: 4 additions & 4 deletions chia/_tests/core/mempool/test_mempool.py
Original file line number Diff line number Diff line change
Expand Up @@ -2232,7 +2232,7 @@ def test_invalid_condition_list_terminator(self, softfork_height: uint32) -> Non
# note how the list of conditions isn't correctly terminated with a
# NIL atom. This is a failure
npc_result = generator_condition_tester("(80 50) . 3", height=softfork_height)
assert npc_result.error in [Err.INVALID_CONDITION.value, Err.GENERATOR_RUNTIME_ERROR.value]
assert npc_result.error in {Err.INVALID_CONDITION.value, Err.GENERATOR_RUNTIME_ERROR.value}

@pytest.mark.parametrize(
"opcode",
Expand Down Expand Up @@ -2346,7 +2346,7 @@ def test_create_coin_cost(self, softfork_height: uint32) -> None:
max_cost=generator_base_cost + 95 * COST_PER_BYTE + ConditionCost.CREATE_COIN.value - 1,
height=softfork_height,
)
assert npc_result.error in [Err.BLOCK_COST_EXCEEDS_MAX.value, Err.INVALID_BLOCK_COST.value]
assert npc_result.error in {Err.BLOCK_COST_EXCEEDS_MAX.value, Err.INVALID_BLOCK_COST.value}

@pytest.mark.parametrize(
"condition",
Expand Down Expand Up @@ -2388,11 +2388,11 @@ def test_agg_sig_cost(self, condition: ConditionOpcode, softfork_height: uint32)
max_cost=generator_base_cost + 117 * COST_PER_BYTE + expected_cost - 1,
height=softfork_height,
)
assert npc_result.error in [
assert npc_result.error in {
Err.GENERATOR_RUNTIME_ERROR.value,
Err.BLOCK_COST_EXCEEDS_MAX.value,
Err.INVALID_BLOCK_COST.value,
]
}

@pytest.mark.parametrize(
"condition",
Expand Down
4 changes: 2 additions & 2 deletions chia/_tests/core/mempool/test_mempool_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1217,9 +1217,9 @@ async def get_coin_records(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
assert expect_limit is not None
item = mempool_manager.get_mempool_item(bundle_name)
assert item is not None
if opcode in [co.ASSERT_BEFORE_SECONDS_ABSOLUTE, co.ASSERT_BEFORE_SECONDS_RELATIVE]:
if opcode in {co.ASSERT_BEFORE_SECONDS_ABSOLUTE, co.ASSERT_BEFORE_SECONDS_RELATIVE}:
assert item.assert_before_seconds == expect_limit
elif opcode in [co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, co.ASSERT_BEFORE_HEIGHT_RELATIVE]:
elif opcode in {co.ASSERT_BEFORE_HEIGHT_ABSOLUTE, co.ASSERT_BEFORE_HEIGHT_RELATIVE}:
assert item.assert_before_height == expect_limit
else:
assert False
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/core/server/test_capabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def test_known_active_capabilities_filter(
disabled: bool,
) -> None:
if duplicated:
values = values * 2
values *= 2

if disabled:
values = [(value, "0") for value, state in values]
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/core/services/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ async def test_daemon_terminates(signal_number: signal.Signals, chia_root: ChiaR
"chia.timelord.timelord_launcher",
"timelord_launcher",
marks=pytest.mark.skipif(
sys.platform in ("win32", "cygwin"),
sys.platform in {"win32", "cygwin"},
reason="windows is not supported by the timelord launcher",
),
),
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/core/test_farmer_harvester_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
async def wait_for_plot_sync(receiver: Receiver, previous_last_sync_id: uint64) -> None:
def wait() -> bool:
current_last_sync_id = receiver.last_sync().sync_id
return current_last_sync_id != 0 and current_last_sync_id != previous_last_sync_id
return current_last_sync_id not in {0, previous_last_sync_id}

await time_out_assert(30, wait)

Expand Down
4 changes: 2 additions & 2 deletions chia/_tests/core/test_full_node_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ async def test1(two_nodes_sim_and_wallets_services, self_hostname, consensus_mod
coin_records[i].coin.amount, ph_receiver, coin_records[i].coin
)
await client.push_tx(spend_bundle)
coin_spends = coin_spends + spend_bundle.coin_spends
coin_spends += spend_bundle.coin_spends
await time_out_assert(
5, full_node_api_1.full_node.mempool_manager.get_spendbundle, spend_bundle, spend_bundle.name()
)
Expand All @@ -228,7 +228,7 @@ async def test1(two_nodes_sim_and_wallets_services, self_hostname, consensus_mod
block_spends = await client.get_block_spends(block.header_hash)

assert len(block_spends) == 3
assert sorted(block_spends, key=lambda x: str(x)) == sorted(coin_spends, key=lambda x: str(x))
assert sorted(block_spends, key=str) == sorted(coin_spends, key=str)

block_spends_with_conditions = await client.get_block_spends_with_conditions(block.header_hash)

Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/core/test_seeder.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ async def test_error_conditions(
no_peers_response = await make_dns_query(use_tcp, target_address, port, no_peers)
assert no_peers_response.rcode() == dns.rcode.NOERROR

if request_type == dns.rdatatype.A or request_type == dns.rdatatype.AAAA:
if request_type in {dns.rdatatype.A, dns.rdatatype.AAAA}:
assert len(no_peers_response.answer) == 0 # no response, as expected
elif request_type == dns.rdatatype.ANY: # ns + soa
assert len(no_peers_response.answer) == 2
Expand Down
8 changes: 4 additions & 4 deletions chia/_tests/core/util/test_block_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ async def test_block_cache(seeded_random: random.Random) -> None:
if i == 0:
continue
assert await a.prev_block_hash([hh]) == [hashes[i - 1]]
assert a.try_block_record(hh) == BR(i + 1, hashes[i], hashes[i - 1])
assert a.block_record(hh) == BR(i + 1, hashes[i], hashes[i - 1])
assert a.height_to_hash(uint32(i + 1)) == hashes[i]
assert a.height_to_block_record(uint32(i + 1)) == BR(i + 1, hashes[i], hashes[i - 1])
assert a.try_block_record(hh) == BR(i + 1, hh, hashes[i - 1])
assert a.block_record(hh) == BR(i + 1, hh, hashes[i - 1])
assert a.height_to_hash(uint32(i + 1)) == hh
assert a.height_to_block_record(uint32(i + 1)) == BR(i + 1, hh, hashes[i - 1])
assert a.contains_block(hh)
assert a.contains_height(uint32(i + 1))
4 changes: 2 additions & 2 deletions chia/_tests/core/util/test_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ async def test_write_file_default_permissions(self, tmp_path: Path):
Write a file to a location and use the default permissions.
"""

if sys.platform in ["win32", "cygwin"]:
if sys.platform in {"win32", "cygwin"}:
pytest.skip("Setting UNIX file permissions doesn't apply to Windows")

dest_path: Path = tmp_path / "test_write_file/test_write_file.txt"
Expand All @@ -355,7 +355,7 @@ async def test_write_file_custom_permissions(self, tmp_path: Path):
Write a file to a location and use custom permissions.
"""

if sys.platform in ["win32", "cygwin"]:
if sys.platform in {"win32", "cygwin"}:
pytest.skip("Setting UNIX file permissions doesn't apply to Windows")

dest_path: Path = tmp_path / "test_write_file/test_write_file.txt"
Expand Down
5 changes: 3 additions & 2 deletions chia/_tests/core/util/test_keychain.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ def test_add_private_key_label(self, empty_temp_file_keyring: TempKeyring):

# All added keys should still be valid with their label
assert all(
key_data in [key_data_0, key_data_1, key_data_2] for key_data in keychain.get_keys(include_secrets=True)
key_data in (key_data_0, key_data_1, key_data_2) # noqa: PLR6201
for key_data in keychain.get_keys(include_secrets=True)
)

def test_bip39_eip2333_test_vector(self, empty_temp_file_keyring: TempKeyring):
Expand Down Expand Up @@ -427,7 +428,7 @@ async def test_set_label(get_temp_keyring: Keychain) -> None:
keychain.set_label(fingerprint=key_data_1.fingerprint, label=key_data_1.label)
assert key_data_0 == keychain.get_key(fingerprint=key_data_0.fingerprint, include_secrets=True)
# All added keys should still be valid with their label
assert all(key_data in [key_data_0, key_data_1] for key_data in keychain.get_keys(include_secrets=True))
assert all(key_data in (key_data_0, key_data_1) for key_data in keychain.get_keys(include_secrets=True)) # noqa: PLR6201


@pytest.mark.parametrize(
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/environments/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ async def change_balances(self, update_dictionary: dict[Union[int, str], dict[st
new_values: dict[str, int] = {}
existing_values: Balance = await self.node.get_balance(wallet_id)
if "init" in kwargs and kwargs["init"]:
new_values = {k: v for k, v in kwargs.items() if k not in ("set_remainder", "init")}
new_values = {k: v for k, v in kwargs.items() if k not in {"set_remainder", "init"}}
elif wallet_id not in self.wallet_states:
raise ValueError(
f"Wallet id {wallet_id} (alias: {self.alias_wallet_id(wallet_id)}) does not have a current state. "
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/farmer_harvester/test_farmer_harvester.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def check_config_match(config1: dict[str, Any], config2: dict[str, Any]) -> None
harvester_config["parallel_decompressor_count"] += 1
harvester_config["decompressor_thread_count"] += 1
harvester_config["recursive_plot_scan"] = not harvester_config["recursive_plot_scan"]
harvester_config["refresh_parameter_interval_seconds"] = harvester_config["refresh_parameter_interval_seconds"] + 1
harvester_config["refresh_parameter_interval_seconds"] += 1

res = await update_harvester_config(harvester_rpc_port, bt.root_path, harvester_config)
assert res is True
Expand Down
4 changes: 2 additions & 2 deletions chia/_tests/plot_sync/test_receiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ async def run_sync_step(receiver: Receiver, sync_step: SyncStepData) -> None:
assert receiver.current_sync().state == sync_step.state
last_sync_time_before = receiver._last_sync.time_done
# For the list types invoke the trigger function in batches
if sync_step.payload_type == PlotSyncPlotList or sync_step.payload_type == PlotSyncPathList:
if sync_step.payload_type in {PlotSyncPlotList, PlotSyncPathList}:
step_data, _ = sync_step.args
assert len(step_data) == 10
# Invoke batches of: 1, 2, 3, 4 items and validate the data against plot store before and after
Expand Down Expand Up @@ -289,7 +289,7 @@ async def test_to_dict(counts_only: bool, seeded_random: random.Random) -> None:
for state in State:
await run_sync_step(receiver, sync_steps[state])

if state != State.idle and state != State.removed and state != State.done:
if state not in {State.idle, State.removed, State.done}:
expected_plot_files_processed += len(sync_steps[state].args[0])

sync_data = receiver.to_dict()["syncing"]
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/plotting/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
def get_test_plots(sub_dir: str = "") -> list[Path]:
path = get_plot_dir()
if sub_dir != "":
path = path / sub_dir
path /= sub_dir
return list(sorted(path.glob("*.plot")))
4 changes: 0 additions & 4 deletions chia/_tests/wallet/dao_wallet/test_dao_clvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,8 +596,6 @@ def test_validator() -> None:
conds = proposal_validator.run(solution)
assert len(conds.as_python()) == 3

return


def test_spend_p2_singleton() -> None:
# Curried values
Expand Down Expand Up @@ -751,8 +749,6 @@ def test_merge_p2_singleton() -> None:
assert cca in agg_acas
assert merge_conds[ConditionOpcode.ASSERT_MY_COIN_ID][0].vars[0] == coin_id

return


def test_treasury() -> None:
"""
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/wallet/test_conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ def test_invalid_condition(
],
prg: bytes,
) -> None:
if (cond == Remark or cond == UnknownCondition) and prg != b"\x80":
if (cond in {Remark, UnknownCondition}) and prg != b"\x80":
pytest.skip("condition takes arbitrary arguments")

with pytest.raises((ValueError, EvalError, KeyError)):
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/wallet/test_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ async def track_coin_state(*args: Any) -> bool:
wallet_node_2.config["enable_notifications"] = True
AMOUNT = uint64(1)
FEE = uint64(0)
elif case in ("allow", "allow_larger"):
elif case in {"allow", "allow_larger"}:
wallet_node_2.config["required_notification_amount"] = 750000000000
if case == "allow_larger":
AMOUNT = uint64(1000000000000)
Expand Down
4 changes: 2 additions & 2 deletions chia/_tests/wallet/test_wallet_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ def test_get_last_used_fingerprint_file_doesnt_exist(root_path_populated_with_co


def test_get_last_used_fingerprint_file_cant_read_unix(root_path_populated_with_config: Path) -> None:
if sys.platform in ["win32", "cygwin"]:
if sys.platform in {"win32", "cygwin"}:
pytest.skip("Setting UNIX file permissions doesn't apply to Windows")

root_path = root_path_populated_with_config
Expand Down Expand Up @@ -332,7 +332,7 @@ def test_get_last_used_fingerprint_file_cant_read_unix(root_path_populated_with_
def test_get_last_used_fingerprint_file_cant_read_win32(
root_path_populated_with_config: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
if sys.platform not in ["win32", "cygwin"]:
if sys.platform not in {"win32", "cygwin"}:
pytest.skip("Windows-specific test")

called_read_text = False
Expand Down
6 changes: 3 additions & 3 deletions chia/_tests/wallet/vc_wallet/test_vc_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -716,15 +716,15 @@ async def test_vc_lifecycle(test_syncing: bool, cost_logger: CostLogger) -> None
62,
(
cr_1.expected_announcement()
if error not in ["use_malicious_cats", "attempt_honest_cat_piggyback"]
if error not in {"use_malicious_cats", "attempt_honest_cat_piggyback"}
else malicious_cr_1.expected_announcement()
),
],
[
62,
(
cr_2.expected_announcement()
if error not in ["use_malicious_cats", "attempt_honest_cat_piggyback"]
if error not in {"use_malicious_cats", "attempt_honest_cat_piggyback"}
else malicious_cr_2.expected_announcement()
),
],
Expand Down Expand Up @@ -755,7 +755,7 @@ async def test_vc_lifecycle(test_syncing: bool, cost_logger: CostLogger) -> None
else:
vc = new_vc
await sim.farm_block()
elif error in ["forget_vc", "use_malicious_cats", "attempt_honest_cat_piggyback"]:
elif error in {"forget_vc", "use_malicious_cats", "attempt_honest_cat_piggyback"}:
assert result == (MempoolInclusionStatus.FAILED, Err.ASSERT_ANNOUNCE_CONSUMED_FAILED)
elif error == "make_banned_announcement":
assert result == (MempoolInclusionStatus.FAILED, Err.GENERATOR_RUNTIME_ERROR)
Expand Down
Loading

0 comments on commit 6c90a76

Please sign in to comment.