Repository: pydantic/pydantic

Issue:
Typed dict JSON schema does not pay attention to `extra_behavior` argument
### Initial Checks

- [X] I confirm that I'm using Pydantic V2

### Description

When creating a JSON schema from a typed dict core schema, the core schema uses the attribute `extra_behavior` to determine whether additional unspecified fields are allowed. However the code for generating JSON schemas, at https://github.com/pydantic/pydantic/blob/v2.4.2/pydantic/json_schema.py#L1194, instead checks for the key `extra`, so the setting is ignored.

### Example Code

```Python
from pydantic.json_schema import GenerateJsonSchema
import pydantic.version
import pydantic_core.core_schema as core_schema

schema = core_schema.typed_dict_schema(
        {'a': core_schema.typed_dict_field(core_schema.str_schema())},
        extra_behavior='forbid',
        )

json_schema = GenerateJsonSchema().generate(schema)

try:
    assert json_schema['additionalProperties'] == False
except (KeyError, AssertionError):
    print('Generated JSON schema missing correct extra_behavior output')
    print('Input schema:')
    print(repr(schema))
    print('Generated schema:')
    print(repr(json_schema))
    print('Version info:')
    print(pydantic.version.version_info())
```


### Python, Pydantic & OS Version

```Text
pydantic version: 2.4.2
        pydantic-core version: 2.10.1
          pydantic-core build: profile=release pgo=false
                 install path: /home/adam/.local/lib/python3.11/site-packages/pydantic
               python version: 3.11.2 (main, Mar 13 2023, 12:18:29) [GCC 12.2.0]
                     platform: Linux-6.1.21-v8+-aarch64-with-glibc2.36
             related packages: typing_extensions-4.8.0 email-validator-2.0.0.post2 typing_extensions-4.4.0 mypy-1.0.1
```



Discussion:

Resolving patch:
diff --git a/pydantic/json_schema.py b/pydantic/json_schema.py
--- a/pydantic/json_schema.py
+++ b/pydantic/json_schema.py
@@ -1195,7 +1195,9 @@ def typed_dict_schema(self, schema: core_schema.TypedDictSchema) -> JsonSchemaVa
         with self._config_wrapper_stack.push(config):
             json_schema = self._named_required_fields_schema(named_required_fields)
 
-        extra = config.get('extra', 'ignore')
+        extra = schema.get('extra_behavior')
+        if extra is None:
+            extra = config.get('extra', 'ignore')
         if extra == 'forbid':
             json_schema['additionalProperties'] = False
         elif extra == 'allow':


Test patch:
diff --git a/tests/test_json_schema.py b/tests/test_json_schema.py
--- a/tests/test_json_schema.py
+++ b/tests/test_json_schema.py
@@ -2406,6 +2406,23 @@ class Model(TypedDict):
     }
 
 
+def test_typeddict_with_extra_behavior_allow():
+    class Model:
+        @classmethod
+        def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema:
+            return core_schema.typed_dict_schema(
+                {'a': core_schema.typed_dict_field(core_schema.str_schema())},
+                extra_behavior='allow',
+            )
+
+    assert TypeAdapter(Model).json_schema() == {
+        'type': 'object',
+        'properties': {'a': {'title': 'A', 'type': 'string'}},
+        'required': ['a'],
+        'additionalProperties': True,
+    }
+
+
 def test_typeddict_with_extra_ignore():
     class Model(TypedDict):
         __pydantic_config__ = ConfigDict(extra='ignore')  # type: ignore
@@ -2419,6 +2436,22 @@ class Model(TypedDict):
     }
 
 
+def test_typeddict_with_extra_behavior_ignore():
+    class Model:
+        @classmethod
+        def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema:
+            return core_schema.typed_dict_schema(
+                {'a': core_schema.typed_dict_field(core_schema.str_schema())},
+                extra_behavior='ignore',
+            )
+
+    assert TypeAdapter(Model).json_schema() == {
+        'type': 'object',
+        'properties': {'a': {'title': 'A', 'type': 'string'}},
+        'required': ['a'],
+    }
+
+
 def test_typeddict_with_extra_forbid():
     @pydantic.dataclasses.dataclass
     class Model:
@@ -2434,6 +2467,23 @@ class Model:
     }
 
 
+def test_typeddict_with_extra_behavior_forbid():
+    class Model:
+        @classmethod
+        def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema:
+            return core_schema.typed_dict_schema(
+                {'a': core_schema.typed_dict_field(core_schema.str_schema())},
+                extra_behavior='forbid',
+            )
+
+    assert TypeAdapter(Model).json_schema() == {
+        'type': 'object',
+        'properties': {'a': {'title': 'A', 'type': 'string'}},
+        'required': ['a'],
+        'additionalProperties': False,
+    }
+
+
 @pytest.mark.parametrize(
     'annotation,kwargs,field_schema',
     [