We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add tests for prototype utilities
Perhaps we should add tests for `sequence_to_str`, so as to ensure that our error message are properly formatted ?

Also nit but if we're going to have such a helper, we could handle the case `a, and b` -> `a and b` (i.e. remove the comma if len == 2

_Originally posted by @NicolasHug in https://github.com/pytorch/vision/pull/5187#discussion_r784852697_

cc @bjuncek

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `torchvision/prototype/utils/_internal.py`
Content:
```
1 import collections.abc
2 import difflib
3 import enum
4 import functools
5 import inspect
6 import os
7 import os.path
8 import textwrap
9 import warnings
10 from typing import Collection, Sequence, Callable, Any, Iterator, NoReturn, Mapping, TypeVar, Iterable, Tuple, cast
11 
12 __all__ = [
13     "StrEnum",
14     "sequence_to_str",
15     "add_suggestion",
16     "FrozenMapping",
17     "make_repr",
18     "FrozenBunch",
19     "kwonly_to_pos_or_kw",
20 ]
21 
22 
23 class StrEnumMeta(enum.EnumMeta):
24     def __getitem__(self, item):
25         return super().__getitem__(item.upper() if isinstance(item, str) else item)
26 
27 
28 class StrEnum(enum.Enum, metaclass=StrEnumMeta):
29     pass
30 
31 
32 def sequence_to_str(seq: Sequence, separate_last: str = "") -> str:
33     if len(seq) == 1:
34         return f"'{seq[0]}'"
35 
36     return f"""'{"', '".join([str(item) for item in seq[:-1]])}', {separate_last}'{seq[-1]}'"""
37 
38 
39 def add_suggestion(
40     msg: str,
41     *,
42     word: str,
43     possibilities: Collection[str],
44     close_match_hint: Callable[[str], str] = lambda close_match: f"Did you mean '{close_match}'?",
45     alternative_hint: Callable[
46         [Sequence[str]], str
47     ] = lambda possibilities: f"Can be {sequence_to_str(possibilities, separate_last='or ')}.",
48 ) -> str:
49     if not isinstance(possibilities, collections.abc.Sequence):
50         possibilities = sorted(possibilities)
51     suggestions = difflib.get_close_matches(word, possibilities, 1)
52     hint = close_match_hint(suggestions[0]) if suggestions else alternative_hint(possibilities)
53     if not hint:
54         return msg
55 
56     return f"{msg.strip()} {hint}"
57 
58 
59 K = TypeVar("K")
60 D = TypeVar("D")
61 
62 
63 class FrozenMapping(Mapping[K, D]):
64     def __init__(self, *args: Any, **kwargs: Any) -> None:
65         data = dict(*args, **kwargs)
66         self.__dict__["__data__"] = data
67         self.__dict__["__final_hash__"] = hash(tuple(data.items()))
68 
69     def __getitem__(self, item: K) -> D:
70         return cast(Mapping[K, D], self.__dict__["__data__"])[item]
71 
72     def __iter__(self) -> Iterator[K]:
73         return iter(self.__dict__["__data__"].keys())
74 
75     def __len__(self) -> int:
76         return len(self.__dict__["__data__"])
77 
78     def __immutable__(self) -> NoReturn:
79         raise RuntimeError(f"'{type(self).__name__}' object is immutable")
80 
81     def __setitem__(self, key: K, value: Any) -> NoReturn:
82         self.__immutable__()
83 
84     def __delitem__(self, key: K) -> NoReturn:
85         self.__immutable__()
86 
87     def __hash__(self) -> int:
88         return cast(int, self.__dict__["__final_hash__"])
89 
90     def __eq__(self, other: Any) -> bool:
91         if not isinstance(other, FrozenMapping):
92             return NotImplemented
93 
94         return hash(self) == hash(other)
95 
96     def __repr__(self) -> str:
97         return repr(self.__dict__["__data__"])
98 
99 
100 def make_repr(name: str, items: Iterable[Tuple[str, Any]]) -> str:
101     def to_str(sep: str) -> str:
102         return sep.join([f"{key}={value}" for key, value in items])
103 
104     prefix = f"{name}("
105     postfix = ")"
106     body = to_str(", ")
107 
108     line_length = int(os.environ.get("COLUMNS", 80))
109     body_too_long = (len(prefix) + len(body) + len(postfix)) > line_length
110     multiline_body = len(str(body).splitlines()) > 1
111     if not (body_too_long or multiline_body):
112         return prefix + body + postfix
113 
114     body = textwrap.indent(to_str(",\n"), " " * 2)
115     return f"{prefix}\n{body}\n{postfix}"
116 
117 
118 class FrozenBunch(FrozenMapping):
119     def __getattr__(self, name: str) -> Any:
120         try:
121             return self[name]
122         except KeyError as error:
123             raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'") from error
124 
125     def __setattr__(self, key: Any, value: Any) -> NoReturn:
126         self.__immutable__()
127 
128     def __delattr__(self, item: Any) -> NoReturn:
129         self.__immutable__()
130 
131     def __repr__(self) -> str:
132         return make_repr(type(self).__name__, self.items())
133 
134 
135 def kwonly_to_pos_or_kw(fn: Callable[..., D]) -> Callable[..., D]:
136     """Decorates a function that uses keyword only parameters to also allow them being passed as positionals.
137 
138     For example, consider the use case of changing the signature of ``old_fn`` into the one from ``new_fn``:
139 
140     .. code::
141 
142         def old_fn(foo, bar, baz=None):
143             ...
144 
145         def new_fn(foo, *, bar, baz=None):
146             ...
147 
148     Calling ``old_fn("foo", "bar, "baz")`` was valid, but the same call is no longer valid with ``new_fn``. To keep BC
149     and at the same time warn the user of the deprecation, this decorator can be used:
150 
151     .. code::
152 
153         @kwonly_to_pos_or_kw
154         def new_fn(foo, *, bar, baz=None):
155             ...
156 
157         new_fn("foo", "bar, "baz")
158     """
159     params = inspect.signature(fn).parameters
160 
161     try:
162         keyword_only_start_idx = next(
163             idx for idx, param in enumerate(params.values()) if param.kind == param.KEYWORD_ONLY
164         )
165     except StopIteration:
166         raise TypeError(f"Found no keyword-only parameter on function '{fn.__name__}'") from None
167 
168     keyword_only_params = tuple(inspect.signature(fn).parameters)[keyword_only_start_idx:]
169 
170     @functools.wraps(fn)
171     def wrapper(*args: Any, **kwargs: Any) -> D:
172         args, keyword_only_args = args[:keyword_only_start_idx], args[keyword_only_start_idx:]
173         if keyword_only_args:
174             keyword_only_kwargs = dict(zip(keyword_only_params, keyword_only_args))
175             warnings.warn(
176                 f"Using {sequence_to_str(tuple(keyword_only_kwargs.keys()), separate_last='and ')} as positional "
177                 f"parameter(s) is deprecated. Please use keyword parameter(s) instead."
178             )
179             kwargs.update(keyword_only_kwargs)
180 
181         return fn(*args, **kwargs)
182 
183     return wrapper
184
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/torchvision/prototype/utils/_internal.py b/torchvision/prototype/utils/_internal.py
--- a/torchvision/prototype/utils/_internal.py
+++ b/torchvision/prototype/utils/_internal.py
@@ -30,10 +30,15 @@
 
 
 def sequence_to_str(seq: Sequence, separate_last: str = "") -> str:
+    if not seq:
+        return ""
     if len(seq) == 1:
         return f"'{seq[0]}'"
 
-    return f"""'{"', '".join([str(item) for item in seq[:-1]])}', {separate_last}'{seq[-1]}'"""
+    head = "'" + "', '".join([str(item) for item in seq[:-1]]) + "'"
+    tail = f"{'' if separate_last and len(seq) == 2 else ','} {separate_last}'{seq[-1]}'"
+
+    return head + tail
 
 
 def add_suggestion(

{"golden_diff": "diff --git a/torchvision/prototype/utils/_internal.py b/torchvision/prototype/utils/_internal.py\n--- a/torchvision/prototype/utils/_internal.py\n+++ b/torchvision/prototype/utils/_internal.py\n@@ -30,10 +30,15 @@\n \n \n def sequence_to_str(seq: Sequence, separate_last: str = \"\") -> str:\n+    if not seq:\n+        return \"\"\n     if len(seq) == 1:\n         return f\"'{seq[0]}'\"\n \n-    return f\"\"\"'{\"', '\".join([str(item) for item in seq[:-1]])}', {separate_last}'{seq[-1]}'\"\"\"\n+    head = \"'\" + \"', '\".join([str(item) for item in seq[:-1]]) + \"'\"\n+    tail = f\"{'' if separate_last and len(seq) == 2 else ','} {separate_last}'{seq[-1]}'\"\n+\n+    return head + tail\n \n \n def add_suggestion(\n", "issue": "Add tests for prototype utilities\nPerhaps we should add tests for `sequence_to_str`, so as to ensure that our error message are properly formatted ?\r\n\r\nAlso nit but if we're going to have such a helper, we could handle the case `a, and b` -> `a and b` (i.e. remove the comma if len == 2\r\n\r\n_Originally posted by @NicolasHug in https://github.com/pytorch/vision/pull/5187#discussion_r784852697_\n\ncc @bjuncek\n", "before_files": [{"content": "import collections.abc\nimport difflib\nimport enum\nimport functools\nimport inspect\nimport os\nimport os.path\nimport textwrap\nimport warnings\nfrom typing import Collection, Sequence, Callable, Any, Iterator, NoReturn, Mapping, TypeVar, Iterable, Tuple, cast\n\n__all__ = [\n    \"StrEnum\",\n    \"sequence_to_str\",\n    \"add_suggestion\",\n    \"FrozenMapping\",\n    \"make_repr\",\n    \"FrozenBunch\",\n    \"kwonly_to_pos_or_kw\",\n]\n\n\nclass StrEnumMeta(enum.EnumMeta):\n    def __getitem__(self, item):\n        return super().__getitem__(item.upper() if isinstance(item, str) else item)\n\n\nclass StrEnum(enum.Enum, metaclass=StrEnumMeta):\n    pass\n\n\ndef sequence_to_str(seq: Sequence, separate_last: str = \"\") -> str:\n    if len(seq) == 1:\n        return f\"'{seq[0]}'\"\n\n    return f\"\"\"'{\"', '\".join([str(item) for item in seq[:-1]])}', {separate_last}'{seq[-1]}'\"\"\"\n\n\ndef add_suggestion(\n    msg: str,\n    *,\n    word: str,\n    possibilities: Collection[str],\n    close_match_hint: Callable[[str], str] = lambda close_match: f\"Did you mean '{close_match}'?\",\n    alternative_hint: Callable[\n        [Sequence[str]], str\n    ] = lambda possibilities: f\"Can be {sequence_to_str(possibilities, separate_last='or ')}.\",\n) -> str:\n    if not isinstance(possibilities, collections.abc.Sequence):\n        possibilities = sorted(possibilities)\n    suggestions = difflib.get_close_matches(word, possibilities, 1)\n    hint = close_match_hint(suggestions[0]) if suggestions else alternative_hint(possibilities)\n    if not hint:\n        return msg\n\n    return f\"{msg.strip()} {hint}\"\n\n\nK = TypeVar(\"K\")\nD = TypeVar(\"D\")\n\n\nclass FrozenMapping(Mapping[K, D]):\n    def __init__(self, *args: Any, **kwargs: Any) -> None:\n        data = dict(*args, **kwargs)\n        self.__dict__[\"__data__\"] = data\n        self.__dict__[\"__final_hash__\"] = hash(tuple(data.items()))\n\n    def __getitem__(self, item: K) -> D:\n        return cast(Mapping[K, D], self.__dict__[\"__data__\"])[item]\n\n    def __iter__(self) -> Iterator[K]:\n        return iter(self.__dict__[\"__data__\"].keys())\n\n    def __len__(self) -> int:\n        return len(self.__dict__[\"__data__\"])\n\n    def __immutable__(self) -> NoReturn:\n        raise RuntimeError(f\"'{type(self).__name__}' object is immutable\")\n\n    def __setitem__(self, key: K, value: Any) -> NoReturn:\n        self.__immutable__()\n\n    def __delitem__(self, key: K) -> NoReturn:\n        self.__immutable__()\n\n    def __hash__(self) -> int:\n        return cast(int, self.__dict__[\"__final_hash__\"])\n\n    def __eq__(self, other: Any) -> bool:\n        if not isinstance(other, FrozenMapping):\n            return NotImplemented\n\n        return hash(self) == hash(other)\n\n    def __repr__(self) -> str:\n        return repr(self.__dict__[\"__data__\"])\n\n\ndef make_repr(name: str, items: Iterable[Tuple[str, Any]]) -> str:\n    def to_str(sep: str) -> str:\n        return sep.join([f\"{key}={value}\" for key, value in items])\n\n    prefix = f\"{name}(\"\n    postfix = \")\"\n    body = to_str(\", \")\n\n    line_length = int(os.environ.get(\"COLUMNS\", 80))\n    body_too_long = (len(prefix) + len(body) + len(postfix)) > line_length\n    multiline_body = len(str(body).splitlines()) > 1\n    if not (body_too_long or multiline_body):\n        return prefix + body + postfix\n\n    body = textwrap.indent(to_str(\",\\n\"), \" \" * 2)\n    return f\"{prefix}\\n{body}\\n{postfix}\"\n\n\nclass FrozenBunch(FrozenMapping):\n    def __getattr__(self, name: str) -> Any:\n        try:\n            return self[name]\n        except KeyError as error:\n            raise AttributeError(f\"'{type(self).__name__}' object has no attribute '{name}'\") from error\n\n    def __setattr__(self, key: Any, value: Any) -> NoReturn:\n        self.__immutable__()\n\n    def __delattr__(self, item: Any) -> NoReturn:\n        self.__immutable__()\n\n    def __repr__(self) -> str:\n        return make_repr(type(self).__name__, self.items())\n\n\ndef kwonly_to_pos_or_kw(fn: Callable[..., D]) -> Callable[..., D]:\n    \"\"\"Decorates a function that uses keyword only parameters to also allow them being passed as positionals.\n\n    For example, consider the use case of changing the signature of ``old_fn`` into the one from ``new_fn``:\n\n    .. code::\n\n        def old_fn(foo, bar, baz=None):\n            ...\n\n        def new_fn(foo, *, bar, baz=None):\n            ...\n\n    Calling ``old_fn(\"foo\", \"bar, \"baz\")`` was valid, but the same call is no longer valid with ``new_fn``. To keep BC\n    and at the same time warn the user of the deprecation, this decorator can be used:\n\n    .. code::\n\n        @kwonly_to_pos_or_kw\n        def new_fn(foo, *, bar, baz=None):\n            ...\n\n        new_fn(\"foo\", \"bar, \"baz\")\n    \"\"\"\n    params = inspect.signature(fn).parameters\n\n    try:\n        keyword_only_start_idx = next(\n            idx for idx, param in enumerate(params.values()) if param.kind == param.KEYWORD_ONLY\n        )\n    except StopIteration:\n        raise TypeError(f\"Found no keyword-only parameter on function '{fn.__name__}'\") from None\n\n    keyword_only_params = tuple(inspect.signature(fn).parameters)[keyword_only_start_idx:]\n\n    @functools.wraps(fn)\n    def wrapper(*args: Any, **kwargs: Any) -> D:\n        args, keyword_only_args = args[:keyword_only_start_idx], args[keyword_only_start_idx:]\n        if keyword_only_args:\n            keyword_only_kwargs = dict(zip(keyword_only_params, keyword_only_args))\n            warnings.warn(\n                f\"Using {sequence_to_str(tuple(keyword_only_kwargs.keys()), separate_last='and ')} as positional \"\n                f\"parameter(s) is deprecated. Please use keyword parameter(s) instead.\"\n            )\n            kwargs.update(keyword_only_kwargs)\n\n        return fn(*args, **kwargs)\n\n    return wrapper\n", "path": "torchvision/prototype/utils/_internal.py"}], "after_files": [{"content": "import collections.abc\nimport difflib\nimport enum\nimport functools\nimport inspect\nimport os\nimport os.path\nimport textwrap\nimport warnings\nfrom typing import Collection, Sequence, Callable, Any, Iterator, NoReturn, Mapping, TypeVar, Iterable, Tuple, cast\n\n__all__ = [\n    \"StrEnum\",\n    \"sequence_to_str\",\n    \"add_suggestion\",\n    \"FrozenMapping\",\n    \"make_repr\",\n    \"FrozenBunch\",\n    \"kwonly_to_pos_or_kw\",\n]\n\n\nclass StrEnumMeta(enum.EnumMeta):\n    def __getitem__(self, item):\n        return super().__getitem__(item.upper() if isinstance(item, str) else item)\n\n\nclass StrEnum(enum.Enum, metaclass=StrEnumMeta):\n    pass\n\n\ndef sequence_to_str(seq: Sequence, separate_last: str = \"\") -> str:\n    if not seq:\n        return \"\"\n    if len(seq) == 1:\n        return f\"'{seq[0]}'\"\n\n    head = \"'\" + \"', '\".join([str(item) for item in seq[:-1]]) + \"'\"\n    tail = f\"{'' if separate_last and len(seq) == 2 else ','} {separate_last}'{seq[-1]}'\"\n\n    return head + tail\n\n\ndef add_suggestion(\n    msg: str,\n    *,\n    word: str,\n    possibilities: Collection[str],\n    close_match_hint: Callable[[str], str] = lambda close_match: f\"Did you mean '{close_match}'?\",\n    alternative_hint: Callable[\n        [Sequence[str]], str\n    ] = lambda possibilities: f\"Can be {sequence_to_str(possibilities, separate_last='or ')}.\",\n) -> str:\n    if not isinstance(possibilities, collections.abc.Sequence):\n        possibilities = sorted(possibilities)\n    suggestions = difflib.get_close_matches(word, possibilities, 1)\n    hint = close_match_hint(suggestions[0]) if suggestions else alternative_hint(possibilities)\n    if not hint:\n        return msg\n\n    return f\"{msg.strip()} {hint}\"\n\n\nK = TypeVar(\"K\")\nD = TypeVar(\"D\")\n\n\nclass FrozenMapping(Mapping[K, D]):\n    def __init__(self, *args: Any, **kwargs: Any) -> None:\n        data = dict(*args, **kwargs)\n        self.__dict__[\"__data__\"] = data\n        self.__dict__[\"__final_hash__\"] = hash(tuple(data.items()))\n\n    def __getitem__(self, item: K) -> D:\n        return cast(Mapping[K, D], self.__dict__[\"__data__\"])[item]\n\n    def __iter__(self) -> Iterator[K]:\n        return iter(self.__dict__[\"__data__\"].keys())\n\n    def __len__(self) -> int:\n        return len(self.__dict__[\"__data__\"])\n\n    def __immutable__(self) -> NoReturn:\n        raise RuntimeError(f\"'{type(self).__name__}' object is immutable\")\n\n    def __setitem__(self, key: K, value: Any) -> NoReturn:\n        self.__immutable__()\n\n    def __delitem__(self, key: K) -> NoReturn:\n        self.__immutable__()\n\n    def __hash__(self) -> int:\n        return cast(int, self.__dict__[\"__final_hash__\"])\n\n    def __eq__(self, other: Any) -> bool:\n        if not isinstance(other, FrozenMapping):\n            return NotImplemented\n\n        return hash(self) == hash(other)\n\n    def __repr__(self) -> str:\n        return repr(self.__dict__[\"__data__\"])\n\n\ndef make_repr(name: str, items: Iterable[Tuple[str, Any]]) -> str:\n    def to_str(sep: str) -> str:\n        return sep.join([f\"{key}={value}\" for key, value in items])\n\n    prefix = f\"{name}(\"\n    postfix = \")\"\n    body = to_str(\", \")\n\n    line_length = int(os.environ.get(\"COLUMNS\", 80))\n    body_too_long = (len(prefix) + len(body) + len(postfix)) > line_length\n    multiline_body = len(str(body).splitlines()) > 1\n    if not (body_too_long or multiline_body):\n        return prefix + body + postfix\n\n    body = textwrap.indent(to_str(\",\\n\"), \" \" * 2)\n    return f\"{prefix}\\n{body}\\n{postfix}\"\n\n\nclass FrozenBunch(FrozenMapping):\n    def __getattr__(self, name: str) -> Any:\n        try:\n            return self[name]\n        except KeyError as error:\n            raise AttributeError(f\"'{type(self).__name__}' object has no attribute '{name}'\") from error\n\n    def __setattr__(self, key: Any, value: Any) -> NoReturn:\n        self.__immutable__()\n\n    def __delattr__(self, item: Any) -> NoReturn:\n        self.__immutable__()\n\n    def __repr__(self) -> str:\n        return make_repr(type(self).__name__, self.items())\n\n\ndef kwonly_to_pos_or_kw(fn: Callable[..., D]) -> Callable[..., D]:\n    \"\"\"Decorates a function that uses keyword only parameters to also allow them being passed as positionals.\n\n    For example, consider the use case of changing the signature of ``old_fn`` into the one from ``new_fn``:\n\n    .. code::\n\n        def old_fn(foo, bar, baz=None):\n            ...\n\n        def new_fn(foo, *, bar, baz=None):\n            ...\n\n    Calling ``old_fn(\"foo\", \"bar, \"baz\")`` was valid, but the same call is no longer valid with ``new_fn``. To keep BC\n    and at the same time warn the user of the deprecation, this decorator can be used:\n\n    .. code::\n\n        @kwonly_to_pos_or_kw\n        def new_fn(foo, *, bar, baz=None):\n            ...\n\n        new_fn(\"foo\", \"bar, \"baz\")\n    \"\"\"\n    params = inspect.signature(fn).parameters\n\n    try:\n        keyword_only_start_idx = next(\n            idx for idx, param in enumerate(params.values()) if param.kind == param.KEYWORD_ONLY\n        )\n    except StopIteration:\n        raise TypeError(f\"Found no keyword-only parameter on function '{fn.__name__}'\") from None\n\n    keyword_only_params = tuple(inspect.signature(fn).parameters)[keyword_only_start_idx:]\n\n    @functools.wraps(fn)\n    def wrapper(*args: Any, **kwargs: Any) -> D:\n        args, keyword_only_args = args[:keyword_only_start_idx], args[keyword_only_start_idx:]\n        if keyword_only_args:\n            keyword_only_kwargs = dict(zip(keyword_only_params, keyword_only_args))\n            warnings.warn(\n                f\"Using {sequence_to_str(tuple(keyword_only_kwargs.keys()), separate_last='and ')} as positional \"\n                f\"parameter(s) is deprecated. Please use keyword parameter(s) instead.\"\n            )\n            kwargs.update(keyword_only_kwargs)\n\n        return fn(*args, **kwargs)\n\n    return wrapper\n", "path": "torchvision/prototype/utils/_internal.py"}]}