Compare commits
23 Commits
8.3.0.dev0
...
6.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09b1d7cc99 | ||
|
|
ea65ea877e | ||
|
|
f4f30d7073 | ||
|
|
309810ac2c | ||
|
|
e63fac3aec | ||
|
|
cb91c5033e | ||
|
|
9a879ee23e | ||
|
|
e9d18bd8ac | ||
|
|
912870d33e | ||
|
|
0115b716c0 | ||
|
|
9a91b67eeb | ||
|
|
79d0d3eff4 | ||
|
|
834f55eddb | ||
|
|
6110f84f78 | ||
|
|
5a339f0d74 | ||
|
|
022bff27a7 | ||
|
|
92af2e22d2 | ||
|
|
0307213254 | ||
|
|
df7b26704d | ||
|
|
1516780829 | ||
|
|
b945b39b0b | ||
|
|
2d5b8a85c2 | ||
|
|
8963644da3 |
@@ -1,2 +0,0 @@
|
||||
Fix pylint ``not-callable`` lint on ``pytest.mark.parametrize()`` and the other builtin marks:
|
||||
``skip``, ``skipif``, ``xfail``, ``usefixtures``, ``filterwarnings``.
|
||||
@@ -1 +0,0 @@
|
||||
Fix regression in plugins using ``TestReport.longreprtext`` (such as ``pytest-html``) when ``TestReport.longrepr`` is not a string.
|
||||
@@ -6,6 +6,8 @@ Release announcements
|
||||
:maxdepth: 2
|
||||
|
||||
|
||||
release-6.0.2
|
||||
release-6.0.1
|
||||
release-6.0.0
|
||||
release-6.0.0rc1
|
||||
release-5.4.3
|
||||
|
||||
21
doc/en/announce/release-6.0.1.rst
Normal file
21
doc/en/announce/release-6.0.1.rst
Normal file
@@ -0,0 +1,21 @@
|
||||
pytest-6.0.1
|
||||
=======================================
|
||||
|
||||
pytest 6.0.1 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Bruno Oliveira
|
||||
* Mattreex
|
||||
* Ran Benita
|
||||
* hp310780
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
19
doc/en/announce/release-6.0.2.rst
Normal file
19
doc/en/announce/release-6.0.2.rst
Normal file
@@ -0,0 +1,19 @@
|
||||
pytest-6.0.2
|
||||
=======================================
|
||||
|
||||
pytest 6.0.2 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
|
||||
|
||||
Thanks to all of the contributors to this release:
|
||||
|
||||
* Bruno Oliveira
|
||||
* Ran Benita
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
@@ -28,6 +28,45 @@ with advance notice in the **Deprecations** section of releases.
|
||||
|
||||
.. towncrier release notes start
|
||||
|
||||
pytest 6.0.2 (2020-09-04)
|
||||
=========================
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#7148 <https://github.com/pytest-dev/pytest/issues/7148>`_: Fixed ``--log-cli`` potentially causing unrelated ``print`` output to be swallowed.
|
||||
|
||||
|
||||
- `#7672 <https://github.com/pytest-dev/pytest/issues/7672>`_: Fixed log-capturing level restored incorrectly if ``caplog.set_level`` is called more than once.
|
||||
|
||||
|
||||
- `#7686 <https://github.com/pytest-dev/pytest/issues/7686>`_: Fixed `NotSetType.token` being used as the parameter ID when the parametrization list is empty.
|
||||
Regressed in pytest 6.0.0.
|
||||
|
||||
|
||||
- `#7707 <https://github.com/pytest-dev/pytest/issues/7707>`_: Fix internal error when handling some exceptions that contain multiple lines or the style uses multiple lines (``--tb=line`` for example).
|
||||
|
||||
|
||||
pytest 6.0.1 (2020-07-30)
|
||||
=========================
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#7394 <https://github.com/pytest-dev/pytest/issues/7394>`_: Passing an empty ``help`` value to ``Parser.add_option`` is now accepted instead of crashing when running ``pytest --help``.
|
||||
Passing ``None`` raises a more informative ``TypeError``.
|
||||
|
||||
|
||||
- `#7558 <https://github.com/pytest-dev/pytest/issues/7558>`_: Fix pylint ``not-callable`` lint on ``pytest.mark.parametrize()`` and the other builtin marks:
|
||||
``skip``, ``skipif``, ``xfail``, ``usefixtures``, ``filterwarnings``.
|
||||
|
||||
|
||||
- `#7559 <https://github.com/pytest-dev/pytest/issues/7559>`_: Fix regression in plugins using ``TestReport.longreprtext`` (such as ``pytest-html``) when ``TestReport.longrepr`` is not a string.
|
||||
|
||||
|
||||
- `#7569 <https://github.com/pytest-dev/pytest/issues/7569>`_: Fix logging capture handler's level not reset on teardown after a call to ``caplog.set_level()``.
|
||||
|
||||
|
||||
pytest 6.0.0 (2020-07-28)
|
||||
=========================
|
||||
|
||||
@@ -192,9 +231,9 @@ Breaking Changes
|
||||
|
||||
|
||||
- `#7224 <https://github.com/pytest-dev/pytest/issues/7224>`_: The `item.catch_log_handler` and `item.catch_log_handlers` attributes, set by the
|
||||
logging plugin and never meant to be public , are no longer available.
|
||||
logging plugin and never meant to be public, are no longer available.
|
||||
|
||||
The deprecated ``--no-print-logs`` option is removed. Use ``--show-capture`` instead.
|
||||
The deprecated ``--no-print-logs`` option and ``log_print`` ini option are removed. Use ``--show-capture`` instead.
|
||||
|
||||
|
||||
- `#7226 <https://github.com/pytest-dev/pytest/issues/7226>`_: Removed the unused ``args`` parameter from ``pytest.Function.__init__``.
|
||||
|
||||
@@ -51,9 +51,10 @@ a public API and may break in the future.
|
||||
.. versionremoved:: 6.0
|
||||
|
||||
|
||||
Option ``--no-print-logs`` is removed. If you used ``--no-print-logs``, please use ``--show-capture`` instead.
|
||||
The ``--no-print-logs`` option and ``log_print`` ini setting are removed. If
|
||||
you used them, please use ``--show-capture`` instead.
|
||||
|
||||
``--show-capture`` command-line option was added in ``pytest 3.5.0`` and allows to specify how to
|
||||
A ``--show-capture`` command-line option was added in ``pytest 3.5.0`` which allows to specify how to
|
||||
display captured output when tests fail: ``no``, ``stdout``, ``stderr``, ``log`` or ``all`` (the default).
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ Install ``pytest``
|
||||
.. code-block:: bash
|
||||
|
||||
$ pytest --version
|
||||
pytest 6.0.0
|
||||
pytest 6.0.2
|
||||
|
||||
.. _`simpletest`:
|
||||
|
||||
|
||||
@@ -783,12 +783,19 @@ ExceptionInfo
|
||||
:members:
|
||||
|
||||
|
||||
pytest.ExitCode
|
||||
~~~~~~~~~~~~~~~
|
||||
ExitCode
|
||||
~~~~~~~~
|
||||
|
||||
.. autoclass:: _pytest.config.ExitCode
|
||||
:members:
|
||||
|
||||
File
|
||||
~~~~
|
||||
|
||||
.. autoclass:: _pytest.nodes.File()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
FixtureDef
|
||||
~~~~~~~~~~
|
||||
@@ -1466,20 +1473,6 @@ passed multiple times. The expected format is ``name=value``. For example::
|
||||
For more information, see :ref:`logging`.
|
||||
|
||||
|
||||
.. confval:: log_print
|
||||
|
||||
|
||||
|
||||
If set to ``False``, will disable displaying captured logging messages for failed tests.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pytest]
|
||||
log_print = False
|
||||
|
||||
For more information, see :ref:`logging`.
|
||||
|
||||
|
||||
.. confval:: markers
|
||||
|
||||
When the ``--strict-markers`` or ``--strict`` command-line arguments are used,
|
||||
|
||||
@@ -192,8 +192,13 @@ You can override the default temporary directory setting like this:
|
||||
|
||||
pytest --basetemp=mydir
|
||||
|
||||
When distributing tests on the local machine, ``pytest`` takes care to
|
||||
configure a basetemp directory for the sub processes such that all temporary
|
||||
.. warning::
|
||||
|
||||
The contents of ``mydir`` will be completely removed, so make sure to use a directory
|
||||
for that purpose only.
|
||||
|
||||
When distributing tests on the local machine using ``pytest-xdist``, care is taken to
|
||||
automatically configure a basetemp directory for the sub processes such that all temporary
|
||||
data lands below a single per-test run basetemp directory.
|
||||
|
||||
.. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html
|
||||
|
||||
@@ -3,23 +3,20 @@ pytest-{version}
|
||||
|
||||
The pytest team is proud to announce the {version} release!
|
||||
|
||||
pytest is a mature Python testing tool with more than a 2000 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
This release contains new features, improvements, bug fixes, and breaking changes, so users
|
||||
are encouraged to take a look at the CHANGELOG carefully:
|
||||
|
||||
This release contains a number of bug fixes and improvements, so users are encouraged
|
||||
to take a look at the CHANGELOG:
|
||||
|
||||
https://docs.pytest.org/en/latest/changelog.html
|
||||
https://docs.pytest.org/en/stable/changelog.html
|
||||
|
||||
For complete documentation, please visit:
|
||||
|
||||
https://docs.pytest.org/en/latest/
|
||||
https://docs.pytest.org/en/stable/
|
||||
|
||||
As usual, you can upgrade from PyPI via:
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
Thanks to all of the contributors to this release:
|
||||
|
||||
{contributors}
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
Thanks to all of the contributors to this release:
|
||||
|
||||
{contributors}
|
||||
|
||||
|
||||
@@ -1038,25 +1038,21 @@ class ReprEntry(TerminalRepr):
|
||||
# such as "> assert 0"
|
||||
fail_marker = "{} ".format(FormattedExcinfo.fail_marker)
|
||||
indent_size = len(fail_marker)
|
||||
indents = []
|
||||
source_lines = []
|
||||
failure_lines = []
|
||||
seeing_failures = False
|
||||
for line in self.lines:
|
||||
is_source_line = not line.startswith(fail_marker)
|
||||
if is_source_line:
|
||||
assert not seeing_failures, (
|
||||
"Unexpected failure lines between source lines:\n"
|
||||
+ "\n".join(self.lines)
|
||||
)
|
||||
indents = [] # type: List[str]
|
||||
source_lines = [] # type: List[str]
|
||||
failure_lines = [] # type: List[str]
|
||||
for index, line in enumerate(self.lines):
|
||||
is_failure_line = line.startswith(fail_marker)
|
||||
if is_failure_line:
|
||||
# from this point on all lines are considered part of the failure
|
||||
failure_lines.extend(self.lines[index:])
|
||||
break
|
||||
else:
|
||||
if self.style == "value":
|
||||
source_lines.append(line)
|
||||
else:
|
||||
indents.append(line[:indent_size])
|
||||
source_lines.append(line[indent_size:])
|
||||
else:
|
||||
seeing_failures = True
|
||||
failure_lines.append(line)
|
||||
|
||||
tw._write_source(source_lines, indents)
|
||||
|
||||
|
||||
@@ -537,7 +537,7 @@ class MultiCapture:
|
||||
self._in_suspended = True
|
||||
|
||||
def resume_capturing(self) -> None:
|
||||
self._state = "resumed"
|
||||
self._state = "started"
|
||||
if self.out:
|
||||
self.out.resume()
|
||||
if self.err:
|
||||
@@ -558,6 +558,10 @@ class MultiCapture:
|
||||
if self.in_:
|
||||
self.in_.done()
|
||||
|
||||
def is_started(self) -> bool:
|
||||
"""Whether actively capturing -- not suspended or stopped."""
|
||||
return self._state == "started"
|
||||
|
||||
def readouterr(self) -> CaptureResult:
|
||||
if self.out:
|
||||
out = self.out.snap()
|
||||
@@ -697,11 +701,19 @@ class CaptureManager:
|
||||
@contextlib.contextmanager
|
||||
def global_and_fixture_disabled(self) -> Generator[None, None, None]:
|
||||
"""Context manager to temporarily disable global and current fixture capturing."""
|
||||
self.suspend()
|
||||
do_fixture = self._capture_fixture and self._capture_fixture._is_started()
|
||||
if do_fixture:
|
||||
self.suspend_fixture()
|
||||
do_global = self._global_capturing and self._global_capturing.is_started()
|
||||
if do_global:
|
||||
self.suspend_global_capture()
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self.resume()
|
||||
if do_global:
|
||||
self.resume_global_capture()
|
||||
if do_fixture:
|
||||
self.resume_fixture()
|
||||
|
||||
@contextlib.contextmanager
|
||||
def item_capture(self, when: str, item: Item) -> Generator[None, None, None]:
|
||||
@@ -810,6 +822,12 @@ class CaptureFixture:
|
||||
if self._capture is not None:
|
||||
self._capture.resume_capturing()
|
||||
|
||||
def _is_started(self) -> bool:
|
||||
"""Whether actively capturing -- not disabled or closed."""
|
||||
if self._capture is not None:
|
||||
return self._capture.is_started()
|
||||
return False
|
||||
|
||||
@contextlib.contextmanager
|
||||
def disabled(self) -> Generator[None, None, None]:
|
||||
"""Temporarily disables capture while inside the 'with' block."""
|
||||
|
||||
@@ -170,6 +170,8 @@ def showhelp(config: Config) -> None:
|
||||
help, type, default = config._parser._inidict[name]
|
||||
if type is None:
|
||||
type = "string"
|
||||
if help is None:
|
||||
raise TypeError("help argument cannot be None for {}".format(name))
|
||||
spec = "{} ({}):".format(name, type)
|
||||
tw.write(" %s" % spec)
|
||||
spec_len = len(spec)
|
||||
@@ -191,9 +193,10 @@ def showhelp(config: Config) -> None:
|
||||
tw.write(" " * (indent_len - spec_len - 2))
|
||||
wrapped = textwrap.wrap(help, columns - indent_len, break_on_hyphens=False)
|
||||
|
||||
tw.line(wrapped[0])
|
||||
for line in wrapped[1:]:
|
||||
tw.line(indent + line)
|
||||
if wrapped:
|
||||
tw.line(wrapped[0])
|
||||
for line in wrapped[1:]:
|
||||
tw.line(indent + line)
|
||||
|
||||
tw.line()
|
||||
tw.line("environment variables:")
|
||||
|
||||
@@ -437,7 +437,8 @@ class LogCaptureFixture:
|
||||
# save the original log-level to restore it during teardown
|
||||
self._initial_logger_levels.setdefault(logger, logger_obj.level)
|
||||
logger_obj.setLevel(level)
|
||||
self._initial_handler_level = self.handler.level
|
||||
if self._initial_handler_level is None:
|
||||
self._initial_handler_level = self.handler.level
|
||||
self.handler.setLevel(level)
|
||||
|
||||
@contextmanager
|
||||
|
||||
@@ -604,7 +604,10 @@ class FSCollector(Collector):
|
||||
|
||||
|
||||
class File(FSCollector):
|
||||
""" base class for collecting tests from a file. """
|
||||
"""Base class for collecting tests from a file.
|
||||
|
||||
:ref:`non-python tests`.
|
||||
"""
|
||||
|
||||
|
||||
class Item(Node):
|
||||
|
||||
@@ -1254,6 +1254,9 @@ def _idval(
|
||||
return str(val)
|
||||
elif isinstance(val, REGEX_TYPE):
|
||||
return ascii_escaped(val.pattern)
|
||||
elif val is NOTSET:
|
||||
# Fallback to default. Note that NOTSET is an enum.Enum.
|
||||
pass
|
||||
elif isinstance(val, enum.Enum):
|
||||
return str(val)
|
||||
elif isinstance(getattr(val, "__name__", None), str):
|
||||
|
||||
@@ -4,6 +4,8 @@ import os
|
||||
import queue
|
||||
import sys
|
||||
import textwrap
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
@@ -1045,28 +1047,34 @@ raise ValueError()
|
||||
@pytest.mark.parametrize(
|
||||
"reproptions",
|
||||
[
|
||||
{
|
||||
"style": style,
|
||||
"showlocals": showlocals,
|
||||
"funcargs": funcargs,
|
||||
"tbfilter": tbfilter,
|
||||
}
|
||||
for style in ("long", "short", "no")
|
||||
pytest.param(
|
||||
{
|
||||
"style": style,
|
||||
"showlocals": showlocals,
|
||||
"funcargs": funcargs,
|
||||
"tbfilter": tbfilter,
|
||||
},
|
||||
id="style={},showlocals={},funcargs={},tbfilter={}".format(
|
||||
style, showlocals, funcargs, tbfilter
|
||||
),
|
||||
)
|
||||
for style in ["long", "short", "line", "no", "native", "value", "auto"]
|
||||
for showlocals in (True, False)
|
||||
for tbfilter in (True, False)
|
||||
for funcargs in (True, False)
|
||||
],
|
||||
)
|
||||
def test_format_excinfo(self, importasmod, reproptions):
|
||||
mod = importasmod(
|
||||
"""
|
||||
def g(x):
|
||||
raise ValueError(x)
|
||||
def f():
|
||||
g(3)
|
||||
"""
|
||||
)
|
||||
excinfo = pytest.raises(ValueError, mod.f)
|
||||
def test_format_excinfo(self, reproptions: Dict[str, Any]) -> None:
|
||||
def bar():
|
||||
assert False, "some error"
|
||||
|
||||
def foo():
|
||||
bar()
|
||||
|
||||
# using inline functions as opposed to importasmod so we get source code lines
|
||||
# in the tracebacks (otherwise getinspect doesn't find the source code).
|
||||
with pytest.raises(AssertionError) as excinfo:
|
||||
foo()
|
||||
file = io.StringIO()
|
||||
tw = TerminalWriter(file=file)
|
||||
repr = excinfo.getrepr(**reproptions)
|
||||
|
||||
@@ -65,6 +65,7 @@ def test_change_level_undos_handler_level(testdir: Testdir) -> None:
|
||||
|
||||
def test1(caplog):
|
||||
assert caplog.handler.level == 0
|
||||
caplog.set_level(9999)
|
||||
caplog.set_level(41)
|
||||
assert caplog.handler.level == 41
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ from hypothesis import strategies
|
||||
import pytest
|
||||
from _pytest import fixtures
|
||||
from _pytest import python
|
||||
from _pytest.compat import NOTSET
|
||||
from _pytest.outcomes import fail
|
||||
from _pytest.pytester import Testdir
|
||||
from _pytest.python import _idval
|
||||
@@ -363,6 +364,14 @@ class TestMetafunc:
|
||||
for val, expected in values:
|
||||
assert _idval(val, "a", 6, None, nodeid=None, config=None) == expected
|
||||
|
||||
def test_notset_idval(self) -> None:
|
||||
"""Test that a NOTSET value (used by an empty parameterset) generates
|
||||
a proper ID.
|
||||
|
||||
Regression test for #7686.
|
||||
"""
|
||||
assert _idval(NOTSET, "a", 0, None, nodeid=None, config=None) == "a0"
|
||||
|
||||
def test_idmaker_autoname(self) -> None:
|
||||
"""#250"""
|
||||
result = idmaker(
|
||||
|
||||
@@ -16,6 +16,7 @@ from _pytest.capture import _get_multicapture
|
||||
from _pytest.capture import CaptureManager
|
||||
from _pytest.capture import MultiCapture
|
||||
from _pytest.config import ExitCode
|
||||
from _pytest.pytester import Testdir
|
||||
|
||||
# note: py.io capture tests where copied from
|
||||
# pylib 1.4.20.dev2 (rev 13d9af95547e)
|
||||
@@ -633,6 +634,34 @@ class TestCaptureFixture:
|
||||
else:
|
||||
result.stdout.no_fnmatch_line("*test_normal executed*")
|
||||
|
||||
def test_disabled_capture_fixture_twice(self, testdir: Testdir) -> None:
|
||||
"""Test that an inner disabled() exit doesn't undo an outer disabled().
|
||||
|
||||
Issue #7148.
|
||||
"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_disabled(capfd):
|
||||
print('captured before')
|
||||
with capfd.disabled():
|
||||
print('while capture is disabled 1')
|
||||
with capfd.disabled():
|
||||
print('while capture is disabled 2')
|
||||
print('while capture is disabled 1 after')
|
||||
print('captured after')
|
||||
assert capfd.readouterr() == ('captured before\\ncaptured after\\n', '')
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest_subprocess()
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*while capture is disabled 1",
|
||||
"*while capture is disabled 2",
|
||||
"*while capture is disabled 1 after",
|
||||
],
|
||||
consecutive=True,
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize("fixture", ["capsys", "capfd"])
|
||||
def test_fixture_use_by_other_fixtures(self, testdir, fixture):
|
||||
"""
|
||||
|
||||
@@ -38,6 +38,41 @@ def test_help(testdir):
|
||||
)
|
||||
|
||||
|
||||
def test_none_help_param_raises_exception(testdir):
|
||||
"""Tests a None help param raises a TypeError.
|
||||
"""
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
def pytest_addoption(parser):
|
||||
parser.addini("test_ini", None, default=True, type="bool")
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("--help")
|
||||
result.stderr.fnmatch_lines(
|
||||
["*TypeError: help argument cannot be None for test_ini*"]
|
||||
)
|
||||
|
||||
|
||||
def test_empty_help_param(testdir):
|
||||
"""Tests an empty help param is displayed correctly.
|
||||
"""
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
def pytest_addoption(parser):
|
||||
parser.addini("test_ini", "", default=True, type="bool")
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("--help")
|
||||
assert result.ret == 0
|
||||
lines = [
|
||||
" required_plugins (args):",
|
||||
" plugins that must be present for pytest to run*",
|
||||
" test_ini (bool):*",
|
||||
"environment variables:",
|
||||
]
|
||||
result.stdout.fnmatch_lines(lines, consecutive=True)
|
||||
|
||||
|
||||
def test_hookvalidation_unknown(testdir):
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user