We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
With pydantic v2 a field marked as Optional[conint] throws an exception
## Description

I experienced this issue with `Optional[conint]`, but I suspect this will happen with any builtin pydantic field whose type ends up being an `Annotated` type.  I was able to reproduce this in the existing tests within `tests/experimental/pydantic/test_fields.py`, test method `test_conint`.  Just replace `field: pydantic.conint(lt=100)` with `field: Optional[pydantic.conint(lt=100)]`.  The exception is copied below in the additional context.

At least in my case I can work around this by just creating a validator for my field instead of using `conint`.  I only have one field that uses it.  However, it's definitely not ideal.

Example code below to see the error:

```python
class Model(pydantic.BaseModel):
    field: Optional[pydantic.conint(lt=100)]

@strawberry.experimental.pydantic.type(model=Model)
class Type:
    field: strawberry.auto
```

## System Information

 - Operating system: Mac OS
 - Strawberry version:  Need to use a new enough version for pydantic v2 support.

## Additional Context

I'm guessing we need to add some additional logic inside `replace_types_recursively` that handles the `Annotated` type more gracefully?  One thing that was interesting was when I was debugging, the `Annotated` type only comes in with `Optional`. If I just directly use `conint`, the type comes in as just an `int`.  I don't have a lot of experience with python type code so I was afraid to try and put in a fix.

<!-- Add any other relevant information about the problem here. -->
```
tests/experimental/pydantic/test_fields.py:92 (test_conint)
@needs_pydantic_v2
    def test_conint():
        class Model(pydantic.BaseModel):
            field: Optional[pydantic.conint(lt=100)]
    
        @strawberry.experimental.pydantic.type(Model)
>       class Type:

test_fields.py:99: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../strawberry/experimental/pydantic/object_type.py:183: in wrap
    all_model_fields: List[DataclassCreationFields] = [
../../../strawberry/experimental/pydantic/object_type.py:184: in <listcomp>
    _build_dataclass_creation_fields(
../../../strawberry/experimental/pydantic/object_type.py:67: in _build_dataclass_creation_fields
    get_type_for_field(field, is_input)
../../../strawberry/experimental/pydantic/object_type.py:49: in get_type_for_field
    replaced_type = replace_types_recursively(outer_type, is_input)
../../../strawberry/experimental/pydantic/fields.py:172: in replace_types_recursively
    converted = tuple(
../../../strawberry/experimental/pydantic/fields.py:173: in <genexpr>
    replace_types_recursively(t, is_input=is_input) for t in get_args(replaced_type)
../../../strawberry/experimental/pydantic/fields.py:180: in replace_types_recursively
    replaced_type = replaced_type.copy_with(converted)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = typing.Annotated[int, None, Interval(gt=None, ge=None, lt=100, le=None), None]
params = (<class 'int'>, None, Interval(gt=None, ge=None, lt=100, le=None), None)

    def copy_with(self, params):
>       assert len(params) == 1
E       AssertionError
```

<!-- POLAR PLEDGE BADGE START -->
## Upvote & Fund

- We're using [Polar.sh](https://polar.sh/strawberry-graphql) so you can upvote and help fund this issue.
- We receive the funding once the issue is completed & confirmed by you.
- Thank you in advance for helping prioritize & fund our backlog.

<a href="https://polar.sh/strawberry-graphql/strawberry/issues/3107">
<picture>
  <source media="(prefers-color-scheme: dark)" srcset="https://polar.sh/api/github/strawberry-graphql/strawberry/issues/3107/pledge.svg?darkmode=1">
  <img alt="Fund with Polar" src="https://polar.sh/api/github/strawberry-graphql/strawberry/issues/3107/pledge.svg">
</picture>
</a>
<!-- POLAR PLEDGE BADGE END -->


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `strawberry/experimental/pydantic/fields.py`
Content:
```
1 import builtins
2 from decimal import Decimal
3 from typing import Any, Dict, List, Optional, Type, Union
4 from uuid import UUID
5 
6 import pydantic
7 from pydantic import BaseModel
8 
9 from strawberry.experimental.pydantic._compat import (
10     IS_PYDANTIC_V1,
11     get_args,
12     get_origin,
13     is_new_type,
14     lenient_issubclass,
15     new_type_supertype,
16 )
17 from strawberry.experimental.pydantic.exceptions import (
18     UnregisteredTypeException,
19     UnsupportedTypeError,
20 )
21 from strawberry.types.types import StrawberryObjectDefinition
22 
23 try:
24     from types import UnionType as TypingUnionType
25 except ImportError:
26     import sys
27 
28     if sys.version_info < (3, 10):
29         TypingUnionType = ()
30     else:
31         raise
32 
33 try:
34     from typing import GenericAlias as TypingGenericAlias  # type: ignore
35 except ImportError:
36     import sys
37 
38     # python < 3.9 does not have GenericAlias (list[int], tuple[str, ...] and so on)
39     # we do this under a conditional to avoid a mypy :)
40     if sys.version_info < (3, 9):
41         TypingGenericAlias = ()
42     else:
43         raise
44 
45 ATTR_TO_TYPE_MAP = {
46     "NoneStr": Optional[str],
47     "NoneBytes": Optional[bytes],
48     "StrBytes": None,
49     "NoneStrBytes": None,
50     "StrictStr": str,
51     "ConstrainedBytes": bytes,
52     "conbytes": bytes,
53     "ConstrainedStr": str,
54     "constr": str,
55     "EmailStr": str,
56     "PyObject": None,
57     "ConstrainedInt": int,
58     "conint": int,
59     "PositiveInt": int,
60     "NegativeInt": int,
61     "ConstrainedFloat": float,
62     "confloat": float,
63     "PositiveFloat": float,
64     "NegativeFloat": float,
65     "ConstrainedDecimal": Decimal,
66     "condecimal": Decimal,
67     "UUID1": UUID,
68     "UUID3": UUID,
69     "UUID4": UUID,
70     "UUID5": UUID,
71     "FilePath": None,
72     "DirectoryPath": None,
73     "Json": None,
74     "JsonWrapper": None,
75     "SecretStr": str,
76     "SecretBytes": bytes,
77     "StrictBool": bool,
78     "StrictInt": int,
79     "StrictFloat": float,
80     "PaymentCardNumber": None,
81     "ByteSize": None,
82     "AnyUrl": str,
83     "AnyHttpUrl": str,
84     "HttpUrl": str,
85     "PostgresDsn": str,
86     "RedisDsn": str,
87 }
88 
89 ATTR_TO_TYPE_MAP_Pydantic_V2 = {
90     "EmailStr": str,
91     "SecretStr": str,
92     "SecretBytes": bytes,
93     "AnyUrl": str,
94 }
95 
96 ATTR_TO_TYPE_MAP_Pydantic_Core_V2 = {
97     "MultiHostUrl": str,
98 }
99 
100 
101 def get_fields_map_for_v2() -> Dict[Any, Any]:
102     import pydantic_core
103 
104     fields_map = {
105         getattr(pydantic, field_name): type
106         for field_name, type in ATTR_TO_TYPE_MAP_Pydantic_V2.items()
107         if hasattr(pydantic, field_name)
108     }
109     fields_map.update(
110         {
111             getattr(pydantic_core, field_name): type
112             for field_name, type in ATTR_TO_TYPE_MAP_Pydantic_Core_V2.items()
113             if hasattr(pydantic_core, field_name)
114         }
115     )
116 
117     return fields_map
118 
119 
120 FIELDS_MAP = (
121     {
122         getattr(pydantic, field_name): type
123         for field_name, type in ATTR_TO_TYPE_MAP.items()
124         if hasattr(pydantic, field_name)
125     }
126     if IS_PYDANTIC_V1
127     else get_fields_map_for_v2()
128 )
129 
130 
131 def get_basic_type(type_: Any) -> Type[Any]:
132     if IS_PYDANTIC_V1:
133         # only pydantic v1 has these
134         if lenient_issubclass(type_, pydantic.ConstrainedInt):
135             return int
136         if lenient_issubclass(type_, pydantic.ConstrainedFloat):
137             return float
138         if lenient_issubclass(type_, pydantic.ConstrainedStr):
139             return str
140         if lenient_issubclass(type_, pydantic.ConstrainedList):
141             return List[get_basic_type(type_.item_type)]  # type: ignore
142 
143     if type_ in FIELDS_MAP:
144         type_ = FIELDS_MAP.get(type_)
145         if type_ is None:
146             raise UnsupportedTypeError()
147 
148     if is_new_type(type_):
149         return new_type_supertype(type_)
150 
151     return type_
152 
153 
154 def replace_pydantic_types(type_: Any, is_input: bool) -> Any:
155     if lenient_issubclass(type_, BaseModel):
156         attr = "_strawberry_input_type" if is_input else "_strawberry_type"
157         if hasattr(type_, attr):
158             return getattr(type_, attr)
159         else:
160             raise UnregisteredTypeException(type_)
161     return type_
162 
163 
164 def replace_types_recursively(type_: Any, is_input: bool) -> Any:
165     """Runs the conversions recursively into the arguments of generic types if any"""
166     basic_type = get_basic_type(type_)
167     replaced_type = replace_pydantic_types(basic_type, is_input)
168 
169     origin = get_origin(type_)
170     if not origin or not hasattr(type_, "__args__"):
171         return replaced_type
172 
173     converted = tuple(
174         replace_types_recursively(t, is_input=is_input) for t in get_args(replaced_type)
175     )
176 
177     if isinstance(replaced_type, TypingGenericAlias):
178         return TypingGenericAlias(origin, converted)
179     if isinstance(replaced_type, TypingUnionType):
180         return Union[converted]
181     replaced_type = replaced_type.copy_with(converted)
182 
183     if isinstance(replaced_type, StrawberryObjectDefinition):
184         # TODO: Not sure if this is necessary. No coverage in tests
185         # TODO: Unnecessary with StrawberryObject
186         replaced_type = builtins.type(
187             replaced_type.name,
188             (),
189             {"__strawberry_definition__": replaced_type},
190         )
191 
192     return replaced_type
193
```
--- 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/strawberry/experimental/pydantic/fields.py b/strawberry/experimental/pydantic/fields.py
--- a/strawberry/experimental/pydantic/fields.py
+++ b/strawberry/experimental/pydantic/fields.py
@@ -1,6 +1,7 @@
 import builtins
 from decimal import Decimal
 from typing import Any, Dict, List, Optional, Type, Union
+from typing_extensions import Annotated
 from uuid import UUID
 
 import pydantic
@@ -42,6 +43,7 @@
     else:
         raise
 
+
 ATTR_TO_TYPE_MAP = {
     "NoneStr": Optional[str],
     "NoneBytes": Optional[bytes],
@@ -178,6 +180,11 @@
         return TypingGenericAlias(origin, converted)
     if isinstance(replaced_type, TypingUnionType):
         return Union[converted]
+
+    # TODO: investigate if we could move the check for annotated to the top
+    if origin is Annotated and converted:
+        converted = (converted[0],)
+
     replaced_type = replaced_type.copy_with(converted)
 
     if isinstance(replaced_type, StrawberryObjectDefinition):

{"golden_diff": "diff --git a/strawberry/experimental/pydantic/fields.py b/strawberry/experimental/pydantic/fields.py\n--- a/strawberry/experimental/pydantic/fields.py\n+++ b/strawberry/experimental/pydantic/fields.py\n@@ -1,6 +1,7 @@\n import builtins\n from decimal import Decimal\n from typing import Any, Dict, List, Optional, Type, Union\n+from typing_extensions import Annotated\n from uuid import UUID\n \n import pydantic\n@@ -42,6 +43,7 @@\n     else:\n         raise\n \n+\n ATTR_TO_TYPE_MAP = {\n     \"NoneStr\": Optional[str],\n     \"NoneBytes\": Optional[bytes],\n@@ -178,6 +180,11 @@\n         return TypingGenericAlias(origin, converted)\n     if isinstance(replaced_type, TypingUnionType):\n         return Union[converted]\n+\n+    # TODO: investigate if we could move the check for annotated to the top\n+    if origin is Annotated and converted:\n+        converted = (converted[0],)\n+\n     replaced_type = replaced_type.copy_with(converted)\n \n     if isinstance(replaced_type, StrawberryObjectDefinition):\n", "issue": "With pydantic v2 a field marked as Optional[conint] throws an exception\n## Description\r\n\r\nI experienced this issue with `Optional[conint]`, but I suspect this will happen with any builtin pydantic field whose type ends up being an `Annotated` type.  I was able to reproduce this in the existing tests within `tests/experimental/pydantic/test_fields.py`, test method `test_conint`.  Just replace `field: pydantic.conint(lt=100)` with `field: Optional[pydantic.conint(lt=100)]`.  The exception is copied below in the additional context.\r\n\r\nAt least in my case I can work around this by just creating a validator for my field instead of using `conint`.  I only have one field that uses it.  However, it's definitely not ideal.\r\n\r\nExample code below to see the error:\r\n\r\n```python\r\nclass Model(pydantic.BaseModel):\r\n    field: Optional[pydantic.conint(lt=100)]\r\n\r\n@strawberry.experimental.pydantic.type(model=Model)\r\nclass Type:\r\n    field: strawberry.auto\r\n```\r\n\r\n## System Information\r\n\r\n - Operating system: Mac OS\r\n - Strawberry version:  Need to use a new enough version for pydantic v2 support.\r\n\r\n## Additional Context\r\n\r\nI'm guessing we need to add some additional logic inside `replace_types_recursively` that handles the `Annotated` type more gracefully?  One thing that was interesting was when I was debugging, the `Annotated` type only comes in with `Optional`. If I just directly use `conint`, the type comes in as just an `int`.  I don't have a lot of experience with python type code so I was afraid to try and put in a fix.\r\n\r\n<!-- Add any other relevant information about the problem here. -->\r\n```\r\ntests/experimental/pydantic/test_fields.py:92 (test_conint)\r\n@needs_pydantic_v2\r\n    def test_conint():\r\n        class Model(pydantic.BaseModel):\r\n            field: Optional[pydantic.conint(lt=100)]\r\n    \r\n        @strawberry.experimental.pydantic.type(Model)\r\n>       class Type:\r\n\r\ntest_fields.py:99: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\n../../../strawberry/experimental/pydantic/object_type.py:183: in wrap\r\n    all_model_fields: List[DataclassCreationFields] = [\r\n../../../strawberry/experimental/pydantic/object_type.py:184: in <listcomp>\r\n    _build_dataclass_creation_fields(\r\n../../../strawberry/experimental/pydantic/object_type.py:67: in _build_dataclass_creation_fields\r\n    get_type_for_field(field, is_input)\r\n../../../strawberry/experimental/pydantic/object_type.py:49: in get_type_for_field\r\n    replaced_type = replace_types_recursively(outer_type, is_input)\r\n../../../strawberry/experimental/pydantic/fields.py:172: in replace_types_recursively\r\n    converted = tuple(\r\n../../../strawberry/experimental/pydantic/fields.py:173: in <genexpr>\r\n    replace_types_recursively(t, is_input=is_input) for t in get_args(replaced_type)\r\n../../../strawberry/experimental/pydantic/fields.py:180: in replace_types_recursively\r\n    replaced_type = replaced_type.copy_with(converted)\r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\n\r\nself = typing.Annotated[int, None, Interval(gt=None, ge=None, lt=100, le=None), None]\r\nparams = (<class 'int'>, None, Interval(gt=None, ge=None, lt=100, le=None), None)\r\n\r\n    def copy_with(self, params):\r\n>       assert len(params) == 1\r\nE       AssertionError\r\n```\n\n<!-- POLAR PLEDGE BADGE START -->\n## Upvote & Fund\n\n- We're using [Polar.sh](https://polar.sh/strawberry-graphql) so you can upvote and help fund this issue.\n- We receive the funding once the issue is completed & confirmed by you.\n- Thank you in advance for helping prioritize & fund our backlog.\n\n<a href=\"https://polar.sh/strawberry-graphql/strawberry/issues/3107\">\n<picture>\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://polar.sh/api/github/strawberry-graphql/strawberry/issues/3107/pledge.svg?darkmode=1\">\n  <img alt=\"Fund with Polar\" src=\"https://polar.sh/api/github/strawberry-graphql/strawberry/issues/3107/pledge.svg\">\n</picture>\n</a>\n<!-- POLAR PLEDGE BADGE END -->\n\n", "before_files": [{"content": "import builtins\nfrom decimal import Decimal\nfrom typing import Any, Dict, List, Optional, Type, Union\nfrom uuid import UUID\n\nimport pydantic\nfrom pydantic import BaseModel\n\nfrom strawberry.experimental.pydantic._compat import (\n    IS_PYDANTIC_V1,\n    get_args,\n    get_origin,\n    is_new_type,\n    lenient_issubclass,\n    new_type_supertype,\n)\nfrom strawberry.experimental.pydantic.exceptions import (\n    UnregisteredTypeException,\n    UnsupportedTypeError,\n)\nfrom strawberry.types.types import StrawberryObjectDefinition\n\ntry:\n    from types import UnionType as TypingUnionType\nexcept ImportError:\n    import sys\n\n    if sys.version_info < (3, 10):\n        TypingUnionType = ()\n    else:\n        raise\n\ntry:\n    from typing import GenericAlias as TypingGenericAlias  # type: ignore\nexcept ImportError:\n    import sys\n\n    # python < 3.9 does not have GenericAlias (list[int], tuple[str, ...] and so on)\n    # we do this under a conditional to avoid a mypy :)\n    if sys.version_info < (3, 9):\n        TypingGenericAlias = ()\n    else:\n        raise\n\nATTR_TO_TYPE_MAP = {\n    \"NoneStr\": Optional[str],\n    \"NoneBytes\": Optional[bytes],\n    \"StrBytes\": None,\n    \"NoneStrBytes\": None,\n    \"StrictStr\": str,\n    \"ConstrainedBytes\": bytes,\n    \"conbytes\": bytes,\n    \"ConstrainedStr\": str,\n    \"constr\": str,\n    \"EmailStr\": str,\n    \"PyObject\": None,\n    \"ConstrainedInt\": int,\n    \"conint\": int,\n    \"PositiveInt\": int,\n    \"NegativeInt\": int,\n    \"ConstrainedFloat\": float,\n    \"confloat\": float,\n    \"PositiveFloat\": float,\n    \"NegativeFloat\": float,\n    \"ConstrainedDecimal\": Decimal,\n    \"condecimal\": Decimal,\n    \"UUID1\": UUID,\n    \"UUID3\": UUID,\n    \"UUID4\": UUID,\n    \"UUID5\": UUID,\n    \"FilePath\": None,\n    \"DirectoryPath\": None,\n    \"Json\": None,\n    \"JsonWrapper\": None,\n    \"SecretStr\": str,\n    \"SecretBytes\": bytes,\n    \"StrictBool\": bool,\n    \"StrictInt\": int,\n    \"StrictFloat\": float,\n    \"PaymentCardNumber\": None,\n    \"ByteSize\": None,\n    \"AnyUrl\": str,\n    \"AnyHttpUrl\": str,\n    \"HttpUrl\": str,\n    \"PostgresDsn\": str,\n    \"RedisDsn\": str,\n}\n\nATTR_TO_TYPE_MAP_Pydantic_V2 = {\n    \"EmailStr\": str,\n    \"SecretStr\": str,\n    \"SecretBytes\": bytes,\n    \"AnyUrl\": str,\n}\n\nATTR_TO_TYPE_MAP_Pydantic_Core_V2 = {\n    \"MultiHostUrl\": str,\n}\n\n\ndef get_fields_map_for_v2() -> Dict[Any, Any]:\n    import pydantic_core\n\n    fields_map = {\n        getattr(pydantic, field_name): type\n        for field_name, type in ATTR_TO_TYPE_MAP_Pydantic_V2.items()\n        if hasattr(pydantic, field_name)\n    }\n    fields_map.update(\n        {\n            getattr(pydantic_core, field_name): type\n            for field_name, type in ATTR_TO_TYPE_MAP_Pydantic_Core_V2.items()\n            if hasattr(pydantic_core, field_name)\n        }\n    )\n\n    return fields_map\n\n\nFIELDS_MAP = (\n    {\n        getattr(pydantic, field_name): type\n        for field_name, type in ATTR_TO_TYPE_MAP.items()\n        if hasattr(pydantic, field_name)\n    }\n    if IS_PYDANTIC_V1\n    else get_fields_map_for_v2()\n)\n\n\ndef get_basic_type(type_: Any) -> Type[Any]:\n    if IS_PYDANTIC_V1:\n        # only pydantic v1 has these\n        if lenient_issubclass(type_, pydantic.ConstrainedInt):\n            return int\n        if lenient_issubclass(type_, pydantic.ConstrainedFloat):\n            return float\n        if lenient_issubclass(type_, pydantic.ConstrainedStr):\n            return str\n        if lenient_issubclass(type_, pydantic.ConstrainedList):\n            return List[get_basic_type(type_.item_type)]  # type: ignore\n\n    if type_ in FIELDS_MAP:\n        type_ = FIELDS_MAP.get(type_)\n        if type_ is None:\n            raise UnsupportedTypeError()\n\n    if is_new_type(type_):\n        return new_type_supertype(type_)\n\n    return type_\n\n\ndef replace_pydantic_types(type_: Any, is_input: bool) -> Any:\n    if lenient_issubclass(type_, BaseModel):\n        attr = \"_strawberry_input_type\" if is_input else \"_strawberry_type\"\n        if hasattr(type_, attr):\n            return getattr(type_, attr)\n        else:\n            raise UnregisteredTypeException(type_)\n    return type_\n\n\ndef replace_types_recursively(type_: Any, is_input: bool) -> Any:\n    \"\"\"Runs the conversions recursively into the arguments of generic types if any\"\"\"\n    basic_type = get_basic_type(type_)\n    replaced_type = replace_pydantic_types(basic_type, is_input)\n\n    origin = get_origin(type_)\n    if not origin or not hasattr(type_, \"__args__\"):\n        return replaced_type\n\n    converted = tuple(\n        replace_types_recursively(t, is_input=is_input) for t in get_args(replaced_type)\n    )\n\n    if isinstance(replaced_type, TypingGenericAlias):\n        return TypingGenericAlias(origin, converted)\n    if isinstance(replaced_type, TypingUnionType):\n        return Union[converted]\n    replaced_type = replaced_type.copy_with(converted)\n\n    if isinstance(replaced_type, StrawberryObjectDefinition):\n        # TODO: Not sure if this is necessary. No coverage in tests\n        # TODO: Unnecessary with StrawberryObject\n        replaced_type = builtins.type(\n            replaced_type.name,\n            (),\n            {\"__strawberry_definition__\": replaced_type},\n        )\n\n    return replaced_type\n", "path": "strawberry/experimental/pydantic/fields.py"}], "after_files": [{"content": "import builtins\nfrom decimal import Decimal\nfrom typing import Any, Dict, List, Optional, Type, Union\nfrom typing_extensions import Annotated\nfrom uuid import UUID\n\nimport pydantic\nfrom pydantic import BaseModel\n\nfrom strawberry.experimental.pydantic._compat import (\n    IS_PYDANTIC_V1,\n    get_args,\n    get_origin,\n    is_new_type,\n    lenient_issubclass,\n    new_type_supertype,\n)\nfrom strawberry.experimental.pydantic.exceptions import (\n    UnregisteredTypeException,\n    UnsupportedTypeError,\n)\nfrom strawberry.types.types import StrawberryObjectDefinition\n\ntry:\n    from types import UnionType as TypingUnionType\nexcept ImportError:\n    import sys\n\n    if sys.version_info < (3, 10):\n        TypingUnionType = ()\n    else:\n        raise\n\ntry:\n    from typing import GenericAlias as TypingGenericAlias  # type: ignore\nexcept ImportError:\n    import sys\n\n    # python < 3.9 does not have GenericAlias (list[int], tuple[str, ...] and so on)\n    # we do this under a conditional to avoid a mypy :)\n    if sys.version_info < (3, 9):\n        TypingGenericAlias = ()\n    else:\n        raise\n\n\nATTR_TO_TYPE_MAP = {\n    \"NoneStr\": Optional[str],\n    \"NoneBytes\": Optional[bytes],\n    \"StrBytes\": None,\n    \"NoneStrBytes\": None,\n    \"StrictStr\": str,\n    \"ConstrainedBytes\": bytes,\n    \"conbytes\": bytes,\n    \"ConstrainedStr\": str,\n    \"constr\": str,\n    \"EmailStr\": str,\n    \"PyObject\": None,\n    \"ConstrainedInt\": int,\n    \"conint\": int,\n    \"PositiveInt\": int,\n    \"NegativeInt\": int,\n    \"ConstrainedFloat\": float,\n    \"confloat\": float,\n    \"PositiveFloat\": float,\n    \"NegativeFloat\": float,\n    \"ConstrainedDecimal\": Decimal,\n    \"condecimal\": Decimal,\n    \"UUID1\": UUID,\n    \"UUID3\": UUID,\n    \"UUID4\": UUID,\n    \"UUID5\": UUID,\n    \"FilePath\": None,\n    \"DirectoryPath\": None,\n    \"Json\": None,\n    \"JsonWrapper\": None,\n    \"SecretStr\": str,\n    \"SecretBytes\": bytes,\n    \"StrictBool\": bool,\n    \"StrictInt\": int,\n    \"StrictFloat\": float,\n    \"PaymentCardNumber\": None,\n    \"ByteSize\": None,\n    \"AnyUrl\": str,\n    \"AnyHttpUrl\": str,\n    \"HttpUrl\": str,\n    \"PostgresDsn\": str,\n    \"RedisDsn\": str,\n}\n\nATTR_TO_TYPE_MAP_Pydantic_V2 = {\n    \"EmailStr\": str,\n    \"SecretStr\": str,\n    \"SecretBytes\": bytes,\n    \"AnyUrl\": str,\n}\n\nATTR_TO_TYPE_MAP_Pydantic_Core_V2 = {\n    \"MultiHostUrl\": str,\n}\n\n\ndef get_fields_map_for_v2() -> Dict[Any, Any]:\n    import pydantic_core\n\n    fields_map = {\n        getattr(pydantic, field_name): type\n        for field_name, type in ATTR_TO_TYPE_MAP_Pydantic_V2.items()\n        if hasattr(pydantic, field_name)\n    }\n    fields_map.update(\n        {\n            getattr(pydantic_core, field_name): type\n            for field_name, type in ATTR_TO_TYPE_MAP_Pydantic_Core_V2.items()\n            if hasattr(pydantic_core, field_name)\n        }\n    )\n\n    return fields_map\n\n\nFIELDS_MAP = (\n    {\n        getattr(pydantic, field_name): type\n        for field_name, type in ATTR_TO_TYPE_MAP.items()\n        if hasattr(pydantic, field_name)\n    }\n    if IS_PYDANTIC_V1\n    else get_fields_map_for_v2()\n)\n\n\ndef get_basic_type(type_: Any) -> Type[Any]:\n    if IS_PYDANTIC_V1:\n        # only pydantic v1 has these\n        if lenient_issubclass(type_, pydantic.ConstrainedInt):\n            return int\n        if lenient_issubclass(type_, pydantic.ConstrainedFloat):\n            return float\n        if lenient_issubclass(type_, pydantic.ConstrainedStr):\n            return str\n        if lenient_issubclass(type_, pydantic.ConstrainedList):\n            return List[get_basic_type(type_.item_type)]  # type: ignore\n\n    if type_ in FIELDS_MAP:\n        type_ = FIELDS_MAP.get(type_)\n        if type_ is None:\n            raise UnsupportedTypeError()\n\n    if is_new_type(type_):\n        return new_type_supertype(type_)\n\n    return type_\n\n\ndef replace_pydantic_types(type_: Any, is_input: bool) -> Any:\n    if lenient_issubclass(type_, BaseModel):\n        attr = \"_strawberry_input_type\" if is_input else \"_strawberry_type\"\n        if hasattr(type_, attr):\n            return getattr(type_, attr)\n        else:\n            raise UnregisteredTypeException(type_)\n    return type_\n\n\ndef replace_types_recursively(type_: Any, is_input: bool) -> Any:\n    \"\"\"Runs the conversions recursively into the arguments of generic types if any\"\"\"\n    basic_type = get_basic_type(type_)\n    replaced_type = replace_pydantic_types(basic_type, is_input)\n\n    origin = get_origin(type_)\n    if not origin or not hasattr(type_, \"__args__\"):\n        return replaced_type\n\n    converted = tuple(\n        replace_types_recursively(t, is_input=is_input) for t in get_args(replaced_type)\n    )\n\n    if isinstance(replaced_type, TypingGenericAlias):\n        return TypingGenericAlias(origin, converted)\n    if isinstance(replaced_type, TypingUnionType):\n        return Union[converted]\n\n    # TODO: investigate if we could move the check for annotated to the top\n    if origin is Annotated and converted:\n        converted = (converted[0],)\n\n    replaced_type = replaced_type.copy_with(converted)\n\n    if isinstance(replaced_type, StrawberryObjectDefinition):\n        # TODO: Not sure if this is necessary. No coverage in tests\n        # TODO: Unnecessary with StrawberryObject\n        replaced_type = builtins.type(\n            replaced_type.name,\n            (),\n            {\"__strawberry_definition__\": replaced_type},\n        )\n\n    return replaced_type\n", "path": "strawberry/experimental/pydantic/fields.py"}]}