We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Black 24.1.0 breaks code formatting if wrap-string-literal is set
**Describe the bug**

Black [24.1.0](https://github.com/psf/black/releases/tag/24.1.0) was just released and removes support for the deprecated `--experimental-string-processing` flag (psf/black#4096). This breaks the code in [`format.py`](https://github.com/koxudaxi/datamodel-code-generator/blob/acc6bf604b13626f22fc123d72ae08ff0a114155/datamodel_code_generator/format.py#L146) that uses this option:

```
Traceback (most recent call last):
  File ".../python3.11/site-packages/datamodel_code_generator/__main__.py", line 429, in main
    generate(
  File ".../python3.11/site-packages/datamodel_code_generator/__init__.py", line 463, in generate
    results = parser.parse()
              ^^^^^^^^^^^^^^
  File ".../python3.11/site-packages/datamodel_code_generator/parser/base.py", line 1156, in parse
    code_formatter: Optional[CodeFormatter] = CodeFormatter(
                                              ^^^^^^^^^^^^^^
  File ".../python3.11/site-packages/datamodel_code_generator/format.py", line 152, in __init__
    self.black_mode = black.FileMode(
                      ^^^^^^^^^^^^^^^
TypeError: Mode.__init__() got an unexpected keyword argument 'experimental_string_processing'
```

**Expected behavior**

No crash.

**Version:**
 - OS: Linux
 - Python version: 3.11
 - datamodel-code-generator version: 0.25.2
 - black version: 0.24.1

**Additional context**

Possible mitigation:
- add a temporary upper bound to the `black` version spec in [pyproject.toml](https://github.com/koxudaxi/datamodel-code-generator/blob/acc6bf604b13626f22fc123d72ae08ff0a114155/pyproject.toml#L54)
- same, but in user environment definitions
- use `--preview --enable-unstable-feature string_processing` instead (as suggested by the black release notes).


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `datamodel_code_generator/format.py`
Content:
```
1 from __future__ import annotations
2 
3 from enum import Enum
4 from importlib import import_module
5 from pathlib import Path
6 from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence
7 from warnings import warn
8 
9 import black
10 import isort
11 
12 from datamodel_code_generator.util import cached_property, load_toml
13 
14 
15 class PythonVersion(Enum):
16     PY_36 = '3.6'
17     PY_37 = '3.7'
18     PY_38 = '3.8'
19     PY_39 = '3.9'
20     PY_310 = '3.10'
21     PY_311 = '3.11'
22     PY_312 = '3.12'
23 
24     @cached_property
25     def _is_py_38_or_later(self) -> bool:  # pragma: no cover
26         return self.value not in {self.PY_36.value, self.PY_37.value}  # type: ignore
27 
28     @cached_property
29     def _is_py_39_or_later(self) -> bool:  # pragma: no cover
30         return self.value not in {self.PY_36.value, self.PY_37.value, self.PY_38.value}  # type: ignore
31 
32     @cached_property
33     def _is_py_310_or_later(self) -> bool:  # pragma: no cover
34         return self.value not in {
35             self.PY_36.value,
36             self.PY_37.value,
37             self.PY_38.value,
38             self.PY_39.value,
39         }  # type: ignore
40 
41     @cached_property
42     def _is_py_311_or_later(self) -> bool:  # pragma: no cover
43         return self.value not in {
44             self.PY_36.value,
45             self.PY_37.value,
46             self.PY_38.value,
47             self.PY_39.value,
48             self.PY_310.value,
49         }  # type: ignore
50 
51     @property
52     def has_literal_type(self) -> bool:
53         return self._is_py_38_or_later
54 
55     @property
56     def has_union_operator(self) -> bool:  # pragma: no cover
57         return self._is_py_310_or_later
58 
59     @property
60     def has_annotated_type(self) -> bool:
61         return self._is_py_39_or_later
62 
63     @property
64     def has_typed_dict(self) -> bool:
65         return self._is_py_38_or_later
66 
67     @property
68     def has_typed_dict_non_required(self) -> bool:
69         return self._is_py_311_or_later
70 
71 
72 if TYPE_CHECKING:
73 
74     class _TargetVersion(Enum):
75         ...
76 
77     BLACK_PYTHON_VERSION: Dict[PythonVersion, _TargetVersion]
78 else:
79     BLACK_PYTHON_VERSION: Dict[PythonVersion, black.TargetVersion] = {
80         v: getattr(black.TargetVersion, f'PY{v.name.split("_")[-1]}')
81         for v in PythonVersion
82         if hasattr(black.TargetVersion, f'PY{v.name.split("_")[-1]}')
83     }
84 
85 
86 def is_supported_in_black(python_version: PythonVersion) -> bool:  # pragma: no cover
87     return python_version in BLACK_PYTHON_VERSION
88 
89 
90 def black_find_project_root(sources: Sequence[Path]) -> Path:
91     if TYPE_CHECKING:
92         from typing import Iterable, Tuple, Union
93 
94         def _find_project_root(
95             srcs: Union[Sequence[str], Iterable[str]],
96         ) -> Union[Tuple[Path, str], Path]:
97             ...
98 
99     else:
100         from black import find_project_root as _find_project_root
101     project_root = _find_project_root(tuple(str(s) for s in sources))
102     if isinstance(project_root, tuple):
103         return project_root[0]
104     else:  # pragma: no cover
105         return project_root
106 
107 
108 class CodeFormatter:
109     def __init__(
110         self,
111         python_version: PythonVersion,
112         settings_path: Optional[Path] = None,
113         wrap_string_literal: Optional[bool] = None,
114         skip_string_normalization: bool = True,
115         known_third_party: Optional[List[str]] = None,
116         custom_formatters: Optional[List[str]] = None,
117         custom_formatters_kwargs: Optional[Dict[str, Any]] = None,
118     ) -> None:
119         if not settings_path:
120             settings_path = Path().resolve()
121 
122         root = black_find_project_root((settings_path,))
123         path = root / 'pyproject.toml'
124         if path.is_file():
125             pyproject_toml = load_toml(path)
126             config = pyproject_toml.get('tool', {}).get('black', {})
127         else:
128             config = {}
129 
130         black_kwargs: Dict[str, Any] = {}
131         if wrap_string_literal is not None:
132             experimental_string_processing = wrap_string_literal
133         else:
134             experimental_string_processing = config.get(
135                 'experimental-string-processing'
136             )
137 
138         if experimental_string_processing is not None:  # pragma: no cover
139             if black.__version__.startswith('19.'):  # type: ignore
140                 warn(
141                     f"black doesn't support `experimental-string-processing` option"  # type: ignore
142                     f' for wrapping string literal in {black.__version__}'
143                 )
144             else:
145                 black_kwargs[
146                     'experimental_string_processing'
147                 ] = experimental_string_processing
148 
149         if TYPE_CHECKING:
150             self.black_mode: black.FileMode
151         else:
152             self.black_mode = black.FileMode(
153                 target_versions={BLACK_PYTHON_VERSION[python_version]},
154                 line_length=config.get('line-length', black.DEFAULT_LINE_LENGTH),
155                 string_normalization=not skip_string_normalization
156                 or not config.get('skip-string-normalization', True),
157                 **black_kwargs,
158             )
159 
160         self.settings_path: str = str(settings_path)
161 
162         self.isort_config_kwargs: Dict[str, Any] = {}
163         if known_third_party:
164             self.isort_config_kwargs['known_third_party'] = known_third_party
165 
166         if isort.__version__.startswith('4.'):
167             self.isort_config = None
168         else:
169             self.isort_config = isort.Config(
170                 settings_path=self.settings_path, **self.isort_config_kwargs
171             )
172 
173         self.custom_formatters_kwargs = custom_formatters_kwargs or {}
174         self.custom_formatters = self._check_custom_formatters(custom_formatters)
175 
176     def _load_custom_formatter(
177         self, custom_formatter_import: str
178     ) -> CustomCodeFormatter:
179         import_ = import_module(custom_formatter_import)
180 
181         if not hasattr(import_, 'CodeFormatter'):
182             raise NameError(
183                 f'Custom formatter module `{import_.__name__}` must contains object with name Formatter'
184             )
185 
186         formatter_class = import_.__getattribute__('CodeFormatter')
187 
188         if not issubclass(formatter_class, CustomCodeFormatter):
189             raise TypeError(
190                 f'The custom module {custom_formatter_import} must inherit from `datamodel-code-generator`'
191             )
192 
193         return formatter_class(formatter_kwargs=self.custom_formatters_kwargs)
194 
195     def _check_custom_formatters(
196         self, custom_formatters: Optional[List[str]]
197     ) -> List[CustomCodeFormatter]:
198         if custom_formatters is None:
199             return []
200 
201         return [
202             self._load_custom_formatter(custom_formatter_import)
203             for custom_formatter_import in custom_formatters
204         ]
205 
206     def format_code(
207         self,
208         code: str,
209     ) -> str:
210         code = self.apply_isort(code)
211         code = self.apply_black(code)
212 
213         for formatter in self.custom_formatters:
214             code = formatter.apply(code)
215 
216         return code
217 
218     def apply_black(self, code: str) -> str:
219         return black.format_str(
220             code,
221             mode=self.black_mode,
222         )
223 
224     if TYPE_CHECKING:
225 
226         def apply_isort(self, code: str) -> str:
227             ...
228 
229     else:
230         if isort.__version__.startswith('4.'):
231 
232             def apply_isort(self, code: str) -> str:
233                 return isort.SortImports(
234                     file_contents=code,
235                     settings_path=self.settings_path,
236                     **self.isort_config_kwargs,
237                 ).output
238 
239         else:
240 
241             def apply_isort(self, code: str) -> str:
242                 return isort.code(code, config=self.isort_config)
243 
244 
245 class CustomCodeFormatter:
246     def __init__(self, formatter_kwargs: Dict[str, Any]) -> None:
247         self.formatter_kwargs = formatter_kwargs
248 
249     def apply(self, code: str) -> str:
250         raise NotImplementedError
251
```
--- 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/datamodel_code_generator/format.py b/datamodel_code_generator/format.py
--- a/datamodel_code_generator/format.py
+++ b/datamodel_code_generator/format.py
@@ -7,6 +7,7 @@
 from warnings import warn
 
 import black
+import black.mode
 import isort
 
 from datamodel_code_generator.util import cached_property, load_toml
@@ -131,9 +132,15 @@
         if wrap_string_literal is not None:
             experimental_string_processing = wrap_string_literal
         else:
-            experimental_string_processing = config.get(
-                'experimental-string-processing'
-            )
+            if black.__version__ < '24.1.0':  # type: ignore
+                experimental_string_processing = config.get(
+                    'experimental-string-processing'
+                )
+            else:
+                experimental_string_processing = config.get('preview', False) and (
+                    config.get('unstable', False)
+                    or 'string_processing' in config.get('enable-unstable-feature', [])
+                )
 
         if experimental_string_processing is not None:  # pragma: no cover
             if black.__version__.startswith('19.'):  # type: ignore
@@ -141,10 +148,16 @@
                     f"black doesn't support `experimental-string-processing` option"  # type: ignore
                     f' for wrapping string literal in {black.__version__}'
                 )
-            else:
+            elif black.__version__ < '24.1.0':  # type: ignore
                 black_kwargs[
                     'experimental_string_processing'
                 ] = experimental_string_processing
+            elif experimental_string_processing:
+                black_kwargs['preview'] = True
+                black_kwargs['unstable'] = config.get('unstable', False)
+                black_kwargs['enabled_features'] = {
+                    black.mode.Preview.string_processing
+                }
 
         if TYPE_CHECKING:
             self.black_mode: black.FileMode

{"golden_diff": "diff --git a/datamodel_code_generator/format.py b/datamodel_code_generator/format.py\n--- a/datamodel_code_generator/format.py\n+++ b/datamodel_code_generator/format.py\n@@ -7,6 +7,7 @@\n from warnings import warn\n \n import black\n+import black.mode\n import isort\n \n from datamodel_code_generator.util import cached_property, load_toml\n@@ -131,9 +132,15 @@\n         if wrap_string_literal is not None:\n             experimental_string_processing = wrap_string_literal\n         else:\n-            experimental_string_processing = config.get(\n-                'experimental-string-processing'\n-            )\n+            if black.__version__ < '24.1.0':  # type: ignore\n+                experimental_string_processing = config.get(\n+                    'experimental-string-processing'\n+                )\n+            else:\n+                experimental_string_processing = config.get('preview', False) and (\n+                    config.get('unstable', False)\n+                    or 'string_processing' in config.get('enable-unstable-feature', [])\n+                )\n \n         if experimental_string_processing is not None:  # pragma: no cover\n             if black.__version__.startswith('19.'):  # type: ignore\n@@ -141,10 +148,16 @@\n                     f\"black doesn't support `experimental-string-processing` option\"  # type: ignore\n                     f' for wrapping string literal in {black.__version__}'\n                 )\n-            else:\n+            elif black.__version__ < '24.1.0':  # type: ignore\n                 black_kwargs[\n                     'experimental_string_processing'\n                 ] = experimental_string_processing\n+            elif experimental_string_processing:\n+                black_kwargs['preview'] = True\n+                black_kwargs['unstable'] = config.get('unstable', False)\n+                black_kwargs['enabled_features'] = {\n+                    black.mode.Preview.string_processing\n+                }\n \n         if TYPE_CHECKING:\n             self.black_mode: black.FileMode\n", "issue": "Black 24.1.0 breaks code formatting if wrap-string-literal is set\n**Describe the bug**\r\n\r\nBlack [24.1.0](https://github.com/psf/black/releases/tag/24.1.0) was just released and removes support for the deprecated `--experimental-string-processing` flag (psf/black#4096). This breaks the code in [`format.py`](https://github.com/koxudaxi/datamodel-code-generator/blob/acc6bf604b13626f22fc123d72ae08ff0a114155/datamodel_code_generator/format.py#L146) that uses this option:\r\n\r\n```\r\nTraceback (most recent call last):\r\n  File \".../python3.11/site-packages/datamodel_code_generator/__main__.py\", line 429, in main\r\n    generate(\r\n  File \".../python3.11/site-packages/datamodel_code_generator/__init__.py\", line 463, in generate\r\n    results = parser.parse()\r\n              ^^^^^^^^^^^^^^\r\n  File \".../python3.11/site-packages/datamodel_code_generator/parser/base.py\", line 1156, in parse\r\n    code_formatter: Optional[CodeFormatter] = CodeFormatter(\r\n                                              ^^^^^^^^^^^^^^\r\n  File \".../python3.11/site-packages/datamodel_code_generator/format.py\", line 152, in __init__\r\n    self.black_mode = black.FileMode(\r\n                      ^^^^^^^^^^^^^^^\r\nTypeError: Mode.__init__() got an unexpected keyword argument 'experimental_string_processing'\r\n```\r\n\r\n**Expected behavior**\r\n\r\nNo crash.\r\n\r\n**Version:**\r\n - OS: Linux\r\n - Python version: 3.11\r\n - datamodel-code-generator version: 0.25.2\r\n - black version: 0.24.1\r\n\r\n**Additional context**\r\n\r\nPossible mitigation:\r\n- add a temporary upper bound to the `black` version spec in [pyproject.toml](https://github.com/koxudaxi/datamodel-code-generator/blob/acc6bf604b13626f22fc123d72ae08ff0a114155/pyproject.toml#L54)\r\n- same, but in user environment definitions\r\n- use `--preview --enable-unstable-feature string_processing` instead (as suggested by the black release notes).\r\n\n", "before_files": [{"content": "from __future__ import annotations\n\nfrom enum import Enum\nfrom importlib import import_module\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence\nfrom warnings import warn\n\nimport black\nimport isort\n\nfrom datamodel_code_generator.util import cached_property, load_toml\n\n\nclass PythonVersion(Enum):\n    PY_36 = '3.6'\n    PY_37 = '3.7'\n    PY_38 = '3.8'\n    PY_39 = '3.9'\n    PY_310 = '3.10'\n    PY_311 = '3.11'\n    PY_312 = '3.12'\n\n    @cached_property\n    def _is_py_38_or_later(self) -> bool:  # pragma: no cover\n        return self.value not in {self.PY_36.value, self.PY_37.value}  # type: ignore\n\n    @cached_property\n    def _is_py_39_or_later(self) -> bool:  # pragma: no cover\n        return self.value not in {self.PY_36.value, self.PY_37.value, self.PY_38.value}  # type: ignore\n\n    @cached_property\n    def _is_py_310_or_later(self) -> bool:  # pragma: no cover\n        return self.value not in {\n            self.PY_36.value,\n            self.PY_37.value,\n            self.PY_38.value,\n            self.PY_39.value,\n        }  # type: ignore\n\n    @cached_property\n    def _is_py_311_or_later(self) -> bool:  # pragma: no cover\n        return self.value not in {\n            self.PY_36.value,\n            self.PY_37.value,\n            self.PY_38.value,\n            self.PY_39.value,\n            self.PY_310.value,\n        }  # type: ignore\n\n    @property\n    def has_literal_type(self) -> bool:\n        return self._is_py_38_or_later\n\n    @property\n    def has_union_operator(self) -> bool:  # pragma: no cover\n        return self._is_py_310_or_later\n\n    @property\n    def has_annotated_type(self) -> bool:\n        return self._is_py_39_or_later\n\n    @property\n    def has_typed_dict(self) -> bool:\n        return self._is_py_38_or_later\n\n    @property\n    def has_typed_dict_non_required(self) -> bool:\n        return self._is_py_311_or_later\n\n\nif TYPE_CHECKING:\n\n    class _TargetVersion(Enum):\n        ...\n\n    BLACK_PYTHON_VERSION: Dict[PythonVersion, _TargetVersion]\nelse:\n    BLACK_PYTHON_VERSION: Dict[PythonVersion, black.TargetVersion] = {\n        v: getattr(black.TargetVersion, f'PY{v.name.split(\"_\")[-1]}')\n        for v in PythonVersion\n        if hasattr(black.TargetVersion, f'PY{v.name.split(\"_\")[-1]}')\n    }\n\n\ndef is_supported_in_black(python_version: PythonVersion) -> bool:  # pragma: no cover\n    return python_version in BLACK_PYTHON_VERSION\n\n\ndef black_find_project_root(sources: Sequence[Path]) -> Path:\n    if TYPE_CHECKING:\n        from typing import Iterable, Tuple, Union\n\n        def _find_project_root(\n            srcs: Union[Sequence[str], Iterable[str]],\n        ) -> Union[Tuple[Path, str], Path]:\n            ...\n\n    else:\n        from black import find_project_root as _find_project_root\n    project_root = _find_project_root(tuple(str(s) for s in sources))\n    if isinstance(project_root, tuple):\n        return project_root[0]\n    else:  # pragma: no cover\n        return project_root\n\n\nclass CodeFormatter:\n    def __init__(\n        self,\n        python_version: PythonVersion,\n        settings_path: Optional[Path] = None,\n        wrap_string_literal: Optional[bool] = None,\n        skip_string_normalization: bool = True,\n        known_third_party: Optional[List[str]] = None,\n        custom_formatters: Optional[List[str]] = None,\n        custom_formatters_kwargs: Optional[Dict[str, Any]] = None,\n    ) -> None:\n        if not settings_path:\n            settings_path = Path().resolve()\n\n        root = black_find_project_root((settings_path,))\n        path = root / 'pyproject.toml'\n        if path.is_file():\n            pyproject_toml = load_toml(path)\n            config = pyproject_toml.get('tool', {}).get('black', {})\n        else:\n            config = {}\n\n        black_kwargs: Dict[str, Any] = {}\n        if wrap_string_literal is not None:\n            experimental_string_processing = wrap_string_literal\n        else:\n            experimental_string_processing = config.get(\n                'experimental-string-processing'\n            )\n\n        if experimental_string_processing is not None:  # pragma: no cover\n            if black.__version__.startswith('19.'):  # type: ignore\n                warn(\n                    f\"black doesn't support `experimental-string-processing` option\"  # type: ignore\n                    f' for wrapping string literal in {black.__version__}'\n                )\n            else:\n                black_kwargs[\n                    'experimental_string_processing'\n                ] = experimental_string_processing\n\n        if TYPE_CHECKING:\n            self.black_mode: black.FileMode\n        else:\n            self.black_mode = black.FileMode(\n                target_versions={BLACK_PYTHON_VERSION[python_version]},\n                line_length=config.get('line-length', black.DEFAULT_LINE_LENGTH),\n                string_normalization=not skip_string_normalization\n                or not config.get('skip-string-normalization', True),\n                **black_kwargs,\n            )\n\n        self.settings_path: str = str(settings_path)\n\n        self.isort_config_kwargs: Dict[str, Any] = {}\n        if known_third_party:\n            self.isort_config_kwargs['known_third_party'] = known_third_party\n\n        if isort.__version__.startswith('4.'):\n            self.isort_config = None\n        else:\n            self.isort_config = isort.Config(\n                settings_path=self.settings_path, **self.isort_config_kwargs\n            )\n\n        self.custom_formatters_kwargs = custom_formatters_kwargs or {}\n        self.custom_formatters = self._check_custom_formatters(custom_formatters)\n\n    def _load_custom_formatter(\n        self, custom_formatter_import: str\n    ) -> CustomCodeFormatter:\n        import_ = import_module(custom_formatter_import)\n\n        if not hasattr(import_, 'CodeFormatter'):\n            raise NameError(\n                f'Custom formatter module `{import_.__name__}` must contains object with name Formatter'\n            )\n\n        formatter_class = import_.__getattribute__('CodeFormatter')\n\n        if not issubclass(formatter_class, CustomCodeFormatter):\n            raise TypeError(\n                f'The custom module {custom_formatter_import} must inherit from `datamodel-code-generator`'\n            )\n\n        return formatter_class(formatter_kwargs=self.custom_formatters_kwargs)\n\n    def _check_custom_formatters(\n        self, custom_formatters: Optional[List[str]]\n    ) -> List[CustomCodeFormatter]:\n        if custom_formatters is None:\n            return []\n\n        return [\n            self._load_custom_formatter(custom_formatter_import)\n            for custom_formatter_import in custom_formatters\n        ]\n\n    def format_code(\n        self,\n        code: str,\n    ) -> str:\n        code = self.apply_isort(code)\n        code = self.apply_black(code)\n\n        for formatter in self.custom_formatters:\n            code = formatter.apply(code)\n\n        return code\n\n    def apply_black(self, code: str) -> str:\n        return black.format_str(\n            code,\n            mode=self.black_mode,\n        )\n\n    if TYPE_CHECKING:\n\n        def apply_isort(self, code: str) -> str:\n            ...\n\n    else:\n        if isort.__version__.startswith('4.'):\n\n            def apply_isort(self, code: str) -> str:\n                return isort.SortImports(\n                    file_contents=code,\n                    settings_path=self.settings_path,\n                    **self.isort_config_kwargs,\n                ).output\n\n        else:\n\n            def apply_isort(self, code: str) -> str:\n                return isort.code(code, config=self.isort_config)\n\n\nclass CustomCodeFormatter:\n    def __init__(self, formatter_kwargs: Dict[str, Any]) -> None:\n        self.formatter_kwargs = formatter_kwargs\n\n    def apply(self, code: str) -> str:\n        raise NotImplementedError\n", "path": "datamodel_code_generator/format.py"}], "after_files": [{"content": "from __future__ import annotations\n\nfrom enum import Enum\nfrom importlib import import_module\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence\nfrom warnings import warn\n\nimport black\nimport black.mode\nimport isort\n\nfrom datamodel_code_generator.util import cached_property, load_toml\n\n\nclass PythonVersion(Enum):\n    PY_36 = '3.6'\n    PY_37 = '3.7'\n    PY_38 = '3.8'\n    PY_39 = '3.9'\n    PY_310 = '3.10'\n    PY_311 = '3.11'\n    PY_312 = '3.12'\n\n    @cached_property\n    def _is_py_38_or_later(self) -> bool:  # pragma: no cover\n        return self.value not in {self.PY_36.value, self.PY_37.value}  # type: ignore\n\n    @cached_property\n    def _is_py_39_or_later(self) -> bool:  # pragma: no cover\n        return self.value not in {self.PY_36.value, self.PY_37.value, self.PY_38.value}  # type: ignore\n\n    @cached_property\n    def _is_py_310_or_later(self) -> bool:  # pragma: no cover\n        return self.value not in {\n            self.PY_36.value,\n            self.PY_37.value,\n            self.PY_38.value,\n            self.PY_39.value,\n        }  # type: ignore\n\n    @cached_property\n    def _is_py_311_or_later(self) -> bool:  # pragma: no cover\n        return self.value not in {\n            self.PY_36.value,\n            self.PY_37.value,\n            self.PY_38.value,\n            self.PY_39.value,\n            self.PY_310.value,\n        }  # type: ignore\n\n    @property\n    def has_literal_type(self) -> bool:\n        return self._is_py_38_or_later\n\n    @property\n    def has_union_operator(self) -> bool:  # pragma: no cover\n        return self._is_py_310_or_later\n\n    @property\n    def has_annotated_type(self) -> bool:\n        return self._is_py_39_or_later\n\n    @property\n    def has_typed_dict(self) -> bool:\n        return self._is_py_38_or_later\n\n    @property\n    def has_typed_dict_non_required(self) -> bool:\n        return self._is_py_311_or_later\n\n\nif TYPE_CHECKING:\n\n    class _TargetVersion(Enum):\n        ...\n\n    BLACK_PYTHON_VERSION: Dict[PythonVersion, _TargetVersion]\nelse:\n    BLACK_PYTHON_VERSION: Dict[PythonVersion, black.TargetVersion] = {\n        v: getattr(black.TargetVersion, f'PY{v.name.split(\"_\")[-1]}')\n        for v in PythonVersion\n        if hasattr(black.TargetVersion, f'PY{v.name.split(\"_\")[-1]}')\n    }\n\n\ndef is_supported_in_black(python_version: PythonVersion) -> bool:  # pragma: no cover\n    return python_version in BLACK_PYTHON_VERSION\n\n\ndef black_find_project_root(sources: Sequence[Path]) -> Path:\n    if TYPE_CHECKING:\n        from typing import Iterable, Tuple, Union\n\n        def _find_project_root(\n            srcs: Union[Sequence[str], Iterable[str]],\n        ) -> Union[Tuple[Path, str], Path]:\n            ...\n\n    else:\n        from black import find_project_root as _find_project_root\n    project_root = _find_project_root(tuple(str(s) for s in sources))\n    if isinstance(project_root, tuple):\n        return project_root[0]\n    else:  # pragma: no cover\n        return project_root\n\n\nclass CodeFormatter:\n    def __init__(\n        self,\n        python_version: PythonVersion,\n        settings_path: Optional[Path] = None,\n        wrap_string_literal: Optional[bool] = None,\n        skip_string_normalization: bool = True,\n        known_third_party: Optional[List[str]] = None,\n        custom_formatters: Optional[List[str]] = None,\n        custom_formatters_kwargs: Optional[Dict[str, Any]] = None,\n    ) -> None:\n        if not settings_path:\n            settings_path = Path().resolve()\n\n        root = black_find_project_root((settings_path,))\n        path = root / 'pyproject.toml'\n        if path.is_file():\n            pyproject_toml = load_toml(path)\n            config = pyproject_toml.get('tool', {}).get('black', {})\n        else:\n            config = {}\n\n        black_kwargs: Dict[str, Any] = {}\n        if wrap_string_literal is not None:\n            experimental_string_processing = wrap_string_literal\n        else:\n            if black.__version__ < '24.1.0':  # type: ignore\n                experimental_string_processing = config.get(\n                    'experimental-string-processing'\n                )\n            else:\n                experimental_string_processing = config.get('preview', False) and (\n                    config.get('unstable', False)\n                    or 'string_processing' in config.get('enable-unstable-feature', [])\n                )\n\n        if experimental_string_processing is not None:  # pragma: no cover\n            if black.__version__.startswith('19.'):  # type: ignore\n                warn(\n                    f\"black doesn't support `experimental-string-processing` option\"  # type: ignore\n                    f' for wrapping string literal in {black.__version__}'\n                )\n            elif black.__version__ < '24.1.0':  # type: ignore\n                black_kwargs[\n                    'experimental_string_processing'\n                ] = experimental_string_processing\n            elif experimental_string_processing:\n                black_kwargs['preview'] = True\n                black_kwargs['unstable'] = config.get('unstable', False)\n                black_kwargs['enabled_features'] = {\n                    black.mode.Preview.string_processing\n                }\n\n        if TYPE_CHECKING:\n            self.black_mode: black.FileMode\n        else:\n            self.black_mode = black.FileMode(\n                target_versions={BLACK_PYTHON_VERSION[python_version]},\n                line_length=config.get('line-length', black.DEFAULT_LINE_LENGTH),\n                string_normalization=not skip_string_normalization\n                or not config.get('skip-string-normalization', True),\n                **black_kwargs,\n            )\n\n        self.settings_path: str = str(settings_path)\n\n        self.isort_config_kwargs: Dict[str, Any] = {}\n        if known_third_party:\n            self.isort_config_kwargs['known_third_party'] = known_third_party\n\n        if isort.__version__.startswith('4.'):\n            self.isort_config = None\n        else:\n            self.isort_config = isort.Config(\n                settings_path=self.settings_path, **self.isort_config_kwargs\n            )\n\n        self.custom_formatters_kwargs = custom_formatters_kwargs or {}\n        self.custom_formatters = self._check_custom_formatters(custom_formatters)\n\n    def _load_custom_formatter(\n        self, custom_formatter_import: str\n    ) -> CustomCodeFormatter:\n        import_ = import_module(custom_formatter_import)\n\n        if not hasattr(import_, 'CodeFormatter'):\n            raise NameError(\n                f'Custom formatter module `{import_.__name__}` must contains object with name Formatter'\n            )\n\n        formatter_class = import_.__getattribute__('CodeFormatter')\n\n        if not issubclass(formatter_class, CustomCodeFormatter):\n            raise TypeError(\n                f'The custom module {custom_formatter_import} must inherit from `datamodel-code-generator`'\n            )\n\n        return formatter_class(formatter_kwargs=self.custom_formatters_kwargs)\n\n    def _check_custom_formatters(\n        self, custom_formatters: Optional[List[str]]\n    ) -> List[CustomCodeFormatter]:\n        if custom_formatters is None:\n            return []\n\n        return [\n            self._load_custom_formatter(custom_formatter_import)\n            for custom_formatter_import in custom_formatters\n        ]\n\n    def format_code(\n        self,\n        code: str,\n    ) -> str:\n        code = self.apply_isort(code)\n        code = self.apply_black(code)\n\n        for formatter in self.custom_formatters:\n            code = formatter.apply(code)\n\n        return code\n\n    def apply_black(self, code: str) -> str:\n        return black.format_str(\n            code,\n            mode=self.black_mode,\n        )\n\n    if TYPE_CHECKING:\n\n        def apply_isort(self, code: str) -> str:\n            ...\n\n    else:\n        if isort.__version__.startswith('4.'):\n\n            def apply_isort(self, code: str) -> str:\n                return isort.SortImports(\n                    file_contents=code,\n                    settings_path=self.settings_path,\n                    **self.isort_config_kwargs,\n                ).output\n\n        else:\n\n            def apply_isort(self, code: str) -> str:\n                return isort.code(code, config=self.isort_config)\n\n\nclass CustomCodeFormatter:\n    def __init__(self, formatter_kwargs: Dict[str, Any]) -> None:\n        self.formatter_kwargs = formatter_kwargs\n\n    def apply(self, code: str) -> str:\n        raise NotImplementedError\n", "path": "datamodel_code_generator/format.py"}]}