chore: switch to ruff (#339)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
42a786b23f
commit
eda37061c4
@ -37,14 +37,12 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args: [--py39-plus]
|
args: [--py39-plus]
|
||||||
- repo: https://github.com/PyCQA/isort
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: 5.13.2
|
rev: v0.8.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: isort
|
- id: ruff
|
||||||
- repo: https://github.com/psf/black
|
args: [--fix, --exit-non-zero-on-fix]
|
||||||
rev: 24.10.0
|
- id: ruff-format
|
||||||
hooks:
|
|
||||||
- id: black
|
|
||||||
# - repo: https://github.com/codespell-project/codespell
|
# - repo: https://github.com/codespell-project/codespell
|
||||||
# rev: v2.2.1
|
# rev: v2.2.1
|
||||||
# hooks:
|
# hooks:
|
||||||
|
|||||||
@ -15,8 +15,8 @@
|
|||||||
<a href="https://python-poetry.org/">
|
<a href="https://python-poetry.org/">
|
||||||
<img src="https://img.shields.io/badge/packaging-poetry-299bd7?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAASCAYAAABrXO8xAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJJSURBVHgBfZLPa1NBEMe/s7tNXoxW1KJQKaUHkXhQvHgW6UHQQ09CBS/6V3hKc/AP8CqCrUcpmop3Cx48eDB4yEECjVQrlZb80CRN8t6OM/teagVxYZi38+Yz853dJbzoMV3MM8cJUcLMSUKIE8AzQ2PieZzFxEJOHMOgMQQ+dUgSAckNXhapU/NMhDSWLs1B24A8sO1xrN4NECkcAC9ASkiIJc6k5TRiUDPhnyMMdhKc+Zx19l6SgyeW76BEONY9exVQMzKExGKwwPsCzza7KGSSWRWEQhyEaDXp6ZHEr416ygbiKYOd7TEWvvcQIeusHYMJGhTwF9y7sGnSwaWyFAiyoxzqW0PM/RjghPxF2pWReAowTEXnDh0xgcLs8l2YQmOrj3N7ByiqEoH0cARs4u78WgAVkoEDIDoOi3AkcLOHU60RIg5wC4ZuTC7FaHKQm8Hq1fQuSOBvX/sodmNJSB5geaF5CPIkUeecdMxieoRO5jz9bheL6/tXjrwCyX/UYBUcjCaWHljx1xiX6z9xEjkYAzbGVnB8pvLmyXm9ep+W8CmsSHQQY77Zx1zboxAV0w7ybMhQmfqdmmw3nEp1I0Z+FGO6M8LZdoyZnuzzBdjISicKRnpxzI9fPb+0oYXsNdyi+d3h9bm9MWYHFtPeIZfLwzmFDKy1ai3p+PDls1Llz4yyFpferxjnyjJDSEy9CaCx5m2cJPerq6Xm34eTrZt3PqxYO1XOwDYZrFlH1fWnpU38Y9HRze3lj0vOujZcXKuuXm3jP+s3KbZVra7y2EAAAAAASUVORK5CYII=" alt="Poetry">
|
<img src="https://img.shields.io/badge/packaging-poetry-299bd7?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAASCAYAAABrXO8xAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJJSURBVHgBfZLPa1NBEMe/s7tNXoxW1KJQKaUHkXhQvHgW6UHQQ09CBS/6V3hKc/AP8CqCrUcpmop3Cx48eDB4yEECjVQrlZb80CRN8t6OM/teagVxYZi38+Yz853dJbzoMV3MM8cJUcLMSUKIE8AzQ2PieZzFxEJOHMOgMQQ+dUgSAckNXhapU/NMhDSWLs1B24A8sO1xrN4NECkcAC9ASkiIJc6k5TRiUDPhnyMMdhKc+Zx19l6SgyeW76BEONY9exVQMzKExGKwwPsCzza7KGSSWRWEQhyEaDXp6ZHEr416ygbiKYOd7TEWvvcQIeusHYMJGhTwF9y7sGnSwaWyFAiyoxzqW0PM/RjghPxF2pWReAowTEXnDh0xgcLs8l2YQmOrj3N7ByiqEoH0cARs4u78WgAVkoEDIDoOi3AkcLOHU60RIg5wC4ZuTC7FaHKQm8Hq1fQuSOBvX/sodmNJSB5geaF5CPIkUeecdMxieoRO5jz9bheL6/tXjrwCyX/UYBUcjCaWHljx1xiX6z9xEjkYAzbGVnB8pvLmyXm9ep+W8CmsSHQQY77Zx1zboxAV0w7ybMhQmfqdmmw3nEp1I0Z+FGO6M8LZdoyZnuzzBdjISicKRnpxzI9fPb+0oYXsNdyi+d3h9bm9MWYHFtPeIZfLwzmFDKy1ai3p+PDls1Llz4yyFpferxjnyjJDSEy9CaCx5m2cJPerq6Xm34eTrZt3PqxYO1XOwDYZrFlH1fWnpU38Y9HRze3lj0vOujZcXKuuXm3jP+s3KbZVra7y2EAAAAAASUVORK5CYII=" alt="Poetry">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/ambv/black">
|
<a href="https://github.com/astral-sh/ruff">
|
||||||
<img src="https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square" alt="black">
|
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/pre-commit/pre-commit">
|
<a href="https://github.com/pre-commit/pre-commit">
|
||||||
<img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square" alt="pre-commit">
|
<img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square" alt="pre-commit">
|
||||||
|
|||||||
@ -25,7 +25,7 @@ class ExampleInterface(ServiceInterface):
|
|||||||
return [what1, what2]
|
return [what1, what2]
|
||||||
|
|
||||||
@method()
|
@method()
|
||||||
def GetVariantDict(self) -> "a{sv}":
|
def GetVariantDict(self) -> "a{sv}": # noqa: F722
|
||||||
return {
|
return {
|
||||||
"foo": Variant("s", "bar"),
|
"foo": Variant("s", "bar"),
|
||||||
"bat": Variant("x", -55),
|
"bat": Variant("x", -55),
|
||||||
|
|||||||
@ -105,3 +105,12 @@ ignore_errors = true
|
|||||||
[build-system]
|
[build-system]
|
||||||
requires = ['setuptools>=65.4.1', 'wheel', 'Cython>=3,<3.1.0', "poetry-core>=1.0.0"]
|
requires = ['setuptools>=65.4.1', 'wheel', 'Cython>=3,<3.1.0', "poetry-core>=1.0.0"]
|
||||||
build-backend = "poetry.core.masonry.api"
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
target-version = "py39"
|
||||||
|
line-length = 88
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
ignore = [
|
||||||
|
"F821", # undefined names are used for decorators
|
||||||
|
]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from typing import Dict, List, Optional, Tuple
|
from typing import Optional
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
|
|
||||||
from ..constants import BusType
|
from ..constants import BusType
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
from struct import Struct, error
|
from struct import Struct, error
|
||||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
from typing import Any, Callable, Optional, Union
|
||||||
|
|
||||||
from ..signature import SignatureType, Variant, get_signature_tree
|
from ..signature import SignatureType, Variant, get_signature_tree
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import socket
|
|||||||
import sys
|
import sys
|
||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
from struct import Struct
|
from struct import Struct
|
||||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
from typing import Any, Callable, Optional, Union
|
||||||
|
|
||||||
from ..constants import MESSAGE_FLAG_MAP, MESSAGE_TYPE_MAP, MessageFlag
|
from ..constants import MESSAGE_FLAG_MAP, MESSAGE_TYPE_MAP, MessageFlag
|
||||||
from ..errors import InvalidMessageError
|
from ..errors import InvalidMessageError
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import ast
|
import ast
|
||||||
import inspect
|
import inspect
|
||||||
from typing import Any, List, Tuple, Union
|
from typing import Any, Union
|
||||||
|
|
||||||
from ..signature import SignatureTree, Variant, get_signature_tree
|
from ..signature import SignatureTree, Variant, get_signature_tree
|
||||||
|
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
from .message_bus import MessageBus
|
from .message_bus import MessageBus as MessageBus
|
||||||
from .proxy_object import ProxyInterface, ProxyObject
|
from .proxy_object import ProxyInterface as ProxyInterface, ProxyObject as ProxyObject
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import socket
|
|||||||
from collections import deque
|
from collections import deque
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Any, Callable, List, Optional, Set, Tuple
|
from typing import Any, Callable, Optional
|
||||||
|
|
||||||
from .. import introspection as intr
|
from .. import introspection as intr
|
||||||
from ..auth import Authenticator, AuthExternal
|
from ..auth import Authenticator, AuthExternal
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from typing import TYPE_CHECKING, Any, List, Union
|
from typing import TYPE_CHECKING, Any, Union
|
||||||
|
|
||||||
from .. import introspection as intr
|
from .. import introspection as intr
|
||||||
from .._private.util import replace_fds_with_idx, replace_idx_with_fds
|
from .._private.util import replace_fds_with_idx, replace_idx_with_fds
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import enum
|
import enum
|
||||||
import os
|
import os
|
||||||
from typing import List, Optional, Tuple
|
from typing import Optional
|
||||||
|
|
||||||
from .errors import AuthError
|
from .errors import AuthError
|
||||||
|
|
||||||
|
|||||||
@ -53,9 +53,9 @@ class InvalidMemberNameError(TypeError):
|
|||||||
super().__init__(f"invalid member name: {member}")
|
super().__init__(f"invalid member name: {member}")
|
||||||
|
|
||||||
|
|
||||||
from .constants import ErrorType, MessageType
|
from .constants import ErrorType, MessageType # noqa: E402
|
||||||
from .message import Message
|
from .message import Message # noqa: E402
|
||||||
from .validators import assert_interface_name_valid
|
from .validators import assert_interface_name_valid # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
class DBusError(Exception):
|
class DBusError(Exception):
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
from .message_bus import MessageBus
|
from .message_bus import MessageBus as MessageBus
|
||||||
from .proxy_object import ProxyInterface, ProxyObject
|
from .proxy_object import ProxyInterface as ProxyInterface, ProxyObject as ProxyObject
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from typing import List, Union
|
from typing import Union
|
||||||
|
|
||||||
from .. import introspection as intr
|
from .. import introspection as intr
|
||||||
from ..constants import ErrorType
|
from ..constants import ErrorType
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from typing import List, Optional, Union
|
from typing import Optional, Union
|
||||||
|
|
||||||
from .constants import ArgDirection, PropertyAccess
|
from .constants import ArgDirection, PropertyAccess
|
||||||
from .errors import InvalidIntrospectionError
|
from .errors import InvalidIntrospectionError
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
from typing import Any, List, Optional, Union
|
from typing import Any, Optional, Union
|
||||||
|
|
||||||
from ._private.constants import LITTLE_ENDIAN, PROTOCOL_VERSION, HeaderField
|
from ._private.constants import LITTLE_ENDIAN, PROTOCOL_VERSION, HeaderField
|
||||||
from ._private.marshaller import Marshaller
|
from ._private.marshaller import Marshaller
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import socket
|
|||||||
import traceback
|
import traceback
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Any, Callable, Dict, List, Optional, Type, Union
|
from typing import Any, Callable, Optional, Union
|
||||||
|
|
||||||
from . import introspection as intr
|
from . import introspection as intr
|
||||||
from ._private.address import get_bus_address, parse_address
|
from ._private.address import get_bus_address, parse_address
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import xml.etree.ElementTree as ET
|
|||||||
from collections.abc import Coroutine
|
from collections.abc import Coroutine
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from typing import Callable, Dict, List, Optional, Type, Union
|
from typing import Callable, Optional, Union
|
||||||
|
|
||||||
from . import introspection as intr
|
from . import introspection as intr
|
||||||
from . import message_bus
|
from . import message_bus
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import traceback
|
import traceback
|
||||||
from types import TracebackType
|
from types import TracebackType
|
||||||
from typing import TYPE_CHECKING, Optional, Type
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
from .constants import ErrorType
|
from .constants import ErrorType
|
||||||
from .errors import DBusError
|
from .errors import DBusError
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import asyncio
|
|||||||
import copy
|
import copy
|
||||||
import inspect
|
import inspect
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Set, Tuple
|
from typing import TYPE_CHECKING, Any, Callable, Optional
|
||||||
|
|
||||||
from . import introspection as intr
|
from . import introspection as intr
|
||||||
from ._private.util import (
|
from ._private.util import (
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
from typing import Any, Callable, Optional, Union
|
||||||
|
|
||||||
from .errors import InvalidSignatureError, SignatureBodyMismatchError
|
from .errors import InvalidSignatureError, SignatureBodyMismatchError
|
||||||
from .validators import is_object_path_valid
|
from .validators import is_object_path_valid
|
||||||
|
|||||||
@ -40,7 +40,7 @@ class ExampleInterface(ServiceInterface):
|
|||||||
return [what1, what2, what3]
|
return [what1, what2, what3]
|
||||||
|
|
||||||
@method()
|
@method()
|
||||||
def GetComplex(self) -> "a{sv}":
|
def GetComplex(self) -> "a{sv}": # noqa: F722
|
||||||
"""Return complex output."""
|
"""Return complex output."""
|
||||||
return {"hello": Variant("s", "world")}
|
return {"hello": Variant("s", "world")}
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,7 @@ class ExampleInterface(ServiceInterface):
|
|||||||
return self._int64_property
|
return self._int64_property
|
||||||
|
|
||||||
@dbus_property(access=PropertyAccess.READ)
|
@dbus_property(access=PropertyAccess.READ)
|
||||||
def ComplexProperty(self) -> "a{sv}":
|
def ComplexProperty(self) -> "a{sv}": # noqa: F722
|
||||||
"""Return complex output."""
|
"""Return complex output."""
|
||||||
return {"hello": Variant("s", "world")}
|
return {"hello": Variant("s", "world")}
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,7 @@ class ExampleInterface(ServiceInterface):
|
|||||||
return ["hello", "world"]
|
return ["hello", "world"]
|
||||||
|
|
||||||
@signal()
|
@signal()
|
||||||
def SignalComplex(self) -> "a{sv}":
|
def SignalComplex(self) -> "a{sv}": # noqa: F722
|
||||||
"""Broadcast a complex signal."""
|
"""Broadcast a complex signal."""
|
||||||
return {"hello": Variant("s", "world")}
|
return {"hello": Variant("s", "world")}
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ class ExampleInterface(ServiceInterface):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@signal()
|
@signal()
|
||||||
def some_signal(self) -> "as":
|
def some_signal(self) -> "as": # noqa: F722
|
||||||
return ["result"]
|
return ["result"]
|
||||||
|
|
||||||
@signal(name="renamed_signal", disabled=True)
|
@signal(name="renamed_signal", disabled=True)
|
||||||
|
|||||||
@ -29,8 +29,12 @@ class ExampleInterface(ServiceInterface):
|
|||||||
|
|
||||||
@method()
|
@method()
|
||||||
def echo_containers(
|
def echo_containers(
|
||||||
self, array: "as", variant: "v", dict_entries: "a{sv}", struct: "(s(s(v)))"
|
self,
|
||||||
) -> "asva{sv}(s(s(v)))":
|
array: "as", # noqa: F722
|
||||||
|
variant: "v",
|
||||||
|
dict_entries: "a{sv}", # noqa: F722
|
||||||
|
struct: "(s(s(v)))", # noqa: F722
|
||||||
|
) -> "asva{sv}(s(s(v)))": # noqa: F722
|
||||||
assert type(self) is ExampleInterface
|
assert type(self) is ExampleInterface
|
||||||
return [array, variant, dict_entries, struct]
|
return [array, variant, dict_entries, struct]
|
||||||
|
|
||||||
@ -76,8 +80,12 @@ class AsyncInterface(ServiceInterface):
|
|||||||
|
|
||||||
@method()
|
@method()
|
||||||
async def echo_containers(
|
async def echo_containers(
|
||||||
self, array: "as", variant: "v", dict_entries: "a{sv}", struct: "(s(s(v)))"
|
self,
|
||||||
) -> "asva{sv}(s(s(v)))":
|
array: "as", # noqa: F722
|
||||||
|
variant: "v",
|
||||||
|
dict_entries: "a{sv}", # noqa: F722
|
||||||
|
struct: "(s(s(v)))", # noqa: F722
|
||||||
|
) -> "asva{sv}(s(s(v)))": # noqa: F722
|
||||||
assert type(self) is AsyncInterface
|
assert type(self) is AsyncInterface
|
||||||
return [array, variant, dict_entries, struct]
|
return [array, variant, dict_entries, struct]
|
||||||
|
|
||||||
|
|||||||
@ -63,8 +63,9 @@ def test_get_session_bus_address():
|
|||||||
with patch.dict(os.environ, DBUS_SESSION_BUS_ADDRESS="unix:path=/dog"):
|
with patch.dict(os.environ, DBUS_SESSION_BUS_ADDRESS="unix:path=/dog"):
|
||||||
assert get_session_bus_address() == "unix:path=/dog"
|
assert get_session_bus_address() == "unix:path=/dog"
|
||||||
assert get_bus_address(BusType.SESSION) == "unix:path=/dog"
|
assert get_bus_address(BusType.SESSION) == "unix:path=/dog"
|
||||||
with patch.dict(os.environ, DBUS_SESSION_BUS_ADDRESS="", DISPLAY=""), pytest.raises(
|
with (
|
||||||
InvalidAddressError
|
patch.dict(os.environ, DBUS_SESSION_BUS_ADDRESS="", DISPLAY=""),
|
||||||
|
pytest.raises(InvalidAddressError),
|
||||||
):
|
):
|
||||||
assert get_session_bus_address()
|
assert get_session_bus_address()
|
||||||
|
|
||||||
|
|||||||
@ -52,8 +52,9 @@ async def test_unexpected_disconnect(event_loop):
|
|||||||
await bus.connect()
|
await bus.connect()
|
||||||
assert bus.connected
|
assert bus.connected
|
||||||
|
|
||||||
with patch.object(bus._writer, "_write_without_remove_writer"), patch.object(
|
with (
|
||||||
bus._writer, "sock", FakeSocket()
|
patch.object(bus._writer, "_write_without_remove_writer"),
|
||||||
|
patch.object(bus._writer, "sock", FakeSocket()),
|
||||||
):
|
):
|
||||||
ping = bus.call(
|
ping = bus.call(
|
||||||
Message(
|
Message(
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import io
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, Dict
|
from typing import Any
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -101,8 +101,6 @@ def test_marshalling_with_table():
|
|||||||
|
|
||||||
@pytest.mark.parametrize("unmarshall_table", (table,))
|
@pytest.mark.parametrize("unmarshall_table", (table,))
|
||||||
def test_unmarshalling_with_table(unmarshall_table):
|
def test_unmarshalling_with_table(unmarshall_table):
|
||||||
from dbus_fast._private import unmarshaller
|
|
||||||
|
|
||||||
for item in unmarshall_table:
|
for item in unmarshall_table:
|
||||||
stream = io.BytesIO(bytes.fromhex(item["data"]))
|
stream = io.BytesIO(bytes.fromhex(item["data"]))
|
||||||
unmarshaller = Unmarshaller(stream)
|
unmarshaller = Unmarshaller(stream)
|
||||||
|
|||||||
@ -7,7 +7,7 @@ def check_gi_repository():
|
|||||||
if _has_gi is not None:
|
if _has_gi is not None:
|
||||||
return _has_gi
|
return _has_gi
|
||||||
try:
|
try:
|
||||||
from gi.repository import GLib
|
from gi.repository import GLib # noqa: F401
|
||||||
|
|
||||||
_has_gi = True
|
_has_gi = True
|
||||||
return _has_gi
|
return _has_gi
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user