Repository: pydantic/pydantic

Issue:
Unexpected `Sequence[str]` behavior
### Initial Checks

- [X] I confirm that I'm using Pydantic V2 installed directly from the `main` branch, or equivalent

### Description

Consider the following model class

```python
import pydantic
import typing

class Model(pydantic.BaseModel):
    str_sequence: typing.Sequence[str]
    str_value: str
```

Passing correct values works as expected

```python
m = Model(str_sequence=['a', 'b', 'c'], str_value='abc')
repr(m)  # "Model(str_sequence=['a', 'b', 'c'], str_value='abc')"
```

Passing a list of strings into a string property fails as expected

```python
Model(str_sequence='abc', str_value=['a', 'b', 'c'])
```
```
pydantic_core._pydantic_core.ValidationError: 1 validation error for Model
str_value
  Input should be a valid string [type=string_type, input_value=['a', 'b', 'c'], input_type=list]
```

Passing a string into a `Sequence[str]` property results in the `str` value being accepted as a valid `Sequence[str]` value

```
m = Model(str_sequence='abc', str_value='abc')
repr(m)
"Model(str_sequence='abc', str_value='abc')"
```

This doesn't match `list` behavior

```python
class Model1(pydantic.BaseModel):
    str_list: list[str]

m1 = Model1(str_list='abc')
```
```
pydantic_core._pydantic_core.ValidationError: 1 validation error for Model1
str_list
  Input should be a valid list [type=list_type, input_value='abc', input_type=str]
```

Pydantic 1.10 also correctly raises a validation error in this case

```python
>>> import pydantic
>>> import typing
>>> class Model(pydantic.BaseModel):
...     str_sequence: typing.Sequence[str]
...     str_value: str
... 
>>> m = Model(str_sequence='abc', str_value='abc')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/lig/workspace/pydantic/pydantic1/pydantic/main.py", line 341, in __init__
    raise validation_error
pydantic.error_wrappers.ValidationError: 1 validation error for Model
str_sequence
  value is not a valid sequence (type=type_error.sequence)
```

### Example Code

```python
from pydantic import BaseModel
from typing import Sequence

class Model(BaseModel):
    str_sequence: Sequence[str]
    str_value: str

m = Model(str_sequence='abc', str_value='abc')
repr(m)  # "Model(str_sequence='abc', str_value='abc')"
```

### Python, Pydantic & OS Version

```Text
pydantic version: 2.0a3
        pydantic-core version: 0.25.0 release build profile
               python version: 3.10.11 (main, Apr  5 2023, 00:00:00) [GCC 13.0.1 20230404 (Red Hat 13.0.1-0)]
                     platform: Linux-6.2.12-300.fc38.x86_64-x86_64-with-glibc2.37
     optional deps. installed: ['devtools', 'email-validator', 'typing-extensions']
```



Discussion:

Resolving patch:
diff --git a/pydantic/_internal/_validators.py b/pydantic/_internal/_validators.py
--- a/pydantic/_internal/_validators.py
+++ b/pydantic/_internal/_validators.py
@@ -55,23 +55,21 @@ def sequence_validator(
     Validator for `Sequence` types, isinstance(v, Sequence) has already been called.
     """
     value_type = type(__input_value)
+
+    # We don't accept any plain string as a sequence
+    # Relevant issue: https://github.com/pydantic/pydantic/issues/5595
+    if issubclass(value_type, (str, bytes)):
+        raise PydanticCustomError(
+            'sequence_str',
+            "'{type_name}' instances are not allowed as a Sequence value",
+            {'type_name': value_type.__name__},
+        )
+
     v_list = validator(__input_value)
 
     # the rest of the logic is just re-creating the original type from `v_list`
     if value_type == list:
         return v_list
-    elif issubclass(value_type, str):
-        try:
-            return ''.join(v_list)
-        except TypeError:
-            # can happen if you pass a string like '123' to `Sequence[int]`
-            raise PydanticKnownError('string_type')
-    elif issubclass(value_type, bytes):
-        try:
-            return b''.join(v_list)
-        except TypeError:
-            # can happen if you pass a string like '123' to `Sequence[int]`
-            raise PydanticKnownError('bytes_type')
     elif issubclass(value_type, range):
         # return the list as we probably can't re-create the range
         return v_list


Test patch:
diff --git a/tests/test_edge_cases.py b/tests/test_edge_cases.py
--- a/tests/test_edge_cases.py
+++ b/tests/test_edge_cases.py
@@ -2448,3 +2448,48 @@ class C(BaseModel):
     assert exc_info.value.errors() == [
         {'input': C(), 'loc': ('B',), 'msg': 'Input should be a valid dictionary', 'type': 'dict_type'}
     ]
+
+
+@pytest.mark.skipif(sys.version_info < (3, 9), reason='cannot parametrize types before 3.9')
+@pytest.mark.parametrize(
+    ('sequence_type', 'input_data', 'expected_error_type', 'expected_error_msg', 'expected_error_ctx'),
+    [
+        pytest.param(List[str], '1bc', 'list_type', 'Input should be a valid list', None, id='list[str]'),
+        pytest.param(
+            Sequence[str],
+            '1bc',
+            'sequence_str',
+            "'str' instances are not allowed as a Sequence value",
+            {'type_name': 'str'},
+            id='Sequence[str]',
+        ),
+        pytest.param(
+            Sequence[bytes],
+            b'1bc',
+            'sequence_str',
+            "'bytes' instances are not allowed as a Sequence value",
+            {'type_name': 'bytes'},
+            id='Sequence[bytes]',
+        ),
+    ],
+)
+def test_sequences_str(sequence_type, input_data, expected_error_type, expected_error_msg, expected_error_ctx):
+    input_sequence = [input_data[:1], input_data[1:]]
+    expected_error = {
+        'type': expected_error_type,
+        'input': input_data,
+        'loc': ('str_sequence',),
+        'msg': expected_error_msg,
+    }
+    if expected_error_ctx is not None:
+        expected_error.update(ctx=expected_error_ctx)
+
+    class Model(BaseModel):
+        str_sequence: sequence_type
+
+    assert Model(str_sequence=input_sequence).str_sequence == input_sequence
+
+    with pytest.raises(ValidationError) as e:
+        Model(str_sequence=input_data)
+
+    assert e.value.errors() == [expected_error]