diff --git a/docs/source/users/configuration.rst b/docs/source/users/configuration.rst index a43d7d95..8983e592 100644 --- a/docs/source/users/configuration.rst +++ b/docs/source/users/configuration.rst @@ -502,6 +502,17 @@ Fields Displays all constraints that are associated with the given pydantic field. +.. config_description:: autopydantic_model + :title: Show Examples + :path: target.configuration.FieldShowExamples + :confpy: autodoc_pydantic_field_show_examples + :directive_option: field-show-examples + :enable: members, field-doc-policy=docstring + :values: True, False + + Displays all examples that are associated with the given pydantic field. + + .. config_description:: autopydantic_model :title: Show Alias :path: target.configuration.FieldShowAlias diff --git a/sphinxcontrib/autodoc_pydantic/application.py b/sphinxcontrib/autodoc_pydantic/application.py index 4972f6fe..759c4da3 100644 --- a/sphinxcontrib/autodoc_pydantic/application.py +++ b/sphinxcontrib/autodoc_pydantic/application.py @@ -254,6 +254,11 @@ def full_name(self) -> str: default='field', types=str, ), + Config( + name='field_show_examples', + default=True, + types=bool, + ), # general Config( diff --git a/sphinxcontrib/autodoc_pydantic/directives/autodocumenters.py b/sphinxcontrib/autodoc_pydantic/directives/autodocumenters.py index ce9445f1..98dc17da 100644 --- a/sphinxcontrib/autodoc_pydantic/directives/autodocumenters.py +++ b/sphinxcontrib/autodoc_pydantic/directives/autodocumenters.py @@ -826,6 +826,24 @@ def add_content( if self.pydantic.options.is_true('field-list-validators'): self.add_validators() + if self.pydantic.options.is_true('field-show-examples'): + self.add_examples() + + def add_examples(self) -> None: + """Add section showing all defined examples for field.""" + + field_name = self.pydantic_field_name + examples = self.pydantic.inspect.fields.get_examples(field_name) + + if examples: + source_name = self.get_sourcename() + self.add_line(':Examples:', source_name) + for value in examples: + line = f' - {value}' + self.add_line(line, source_name) + + self.add_line('', source_name) + def add_constraints(self) -> None: """Adds section showing all defined constraints.""" diff --git a/sphinxcontrib/autodoc_pydantic/directives/options/definition.py b/sphinxcontrib/autodoc_pydantic/directives/options/definition.py index b0984c33..ad91890d 100644 --- a/sphinxcontrib/autodoc_pydantic/directives/options/definition.py +++ b/sphinxcontrib/autodoc_pydantic/directives/options/definition.py @@ -28,6 +28,7 @@ 'field-list-validators': option_default_true, 'field-swap-name-and-alias': option_default_true, 'field-doc-policy': option_one_of_factory(OptionsFieldDocPolicy.values()), + 'field-show-examples': option_default_true, '__doc_disable_except__': option_list_like, } """Represents added directive options for :class:`PydanticFieldDocumenter`.""" diff --git a/sphinxcontrib/autodoc_pydantic/inspection.py b/sphinxcontrib/autodoc_pydantic/inspection.py index 00dd3750..66821c71 100644 --- a/sphinxcontrib/autodoc_pydantic/inspection.py +++ b/sphinxcontrib/autodoc_pydantic/inspection.py @@ -164,6 +164,17 @@ def get_constraints(self, field_name: str) -> dict[str, Any]: if getattr(meta, key) is not None } + def has_examples(self, field_name:str) -> bool: + """Check if examples are provided in field info.""" + + return self.get_property_from_field_info(field_name, "examples") is not None + + def get_examples(self, field_name: str) -> list[Any]: + """Get examples for given `field_name`.""" + + if self.has_examples(field_name): + return self.get_property_from_field_info(field_name, 'examples') + def is_required(self, field_name: str) -> bool: """Check if a given pydantic field is required/mandatory. Returns True, if a value for this field needs to provided upon model creation. diff --git a/tests/conftest.py b/tests/conftest.py index 9b259ead..bfd45c65 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -50,6 +50,7 @@ 'autodoc_pydantic_validator_list_fields': False, 'autodoc_pydantic_field_list_validators': False, 'autodoc_pydantic_field_show_constraints': False, + 'autodoc_pydantic_field_show_examples': False, 'autodoc_pydantic_field_show_alias': False, 'autodoc_pydantic_field_show_required': False, 'autodoc_pydantic_field_show_optional': False, diff --git a/tests/roots/test-base/target/configuration.py b/tests/roots/test-base/target/configuration.py index d2f2deb4..7d6d44d7 100644 --- a/tests/roots/test-base/target/configuration.py +++ b/tests/roots/test-base/target/configuration.py @@ -587,6 +587,20 @@ class FieldSwapNameAndAlias(BaseModel): """Field1""" +class FieldShowExamples(BaseModel): + """FieldShowExamples.""" + + field: int = Field(1, examples=[2, 3]) + """Field.""" + + +class FieldShowExamplesExtra(BaseModel): + """FieldShowExamplesExtra.""" + + field: int = Field(1, examples=[2, 3], json_schema_extra=dict(examples=[4, 5])) + """Field.""" + + class ModelErdanticFigureRelated(ModelShowFieldSummary): """ModelErdanticFigureRelated.""" diff --git a/tests/roots/test-base/target/usage_automodule.py b/tests/roots/test-base/target/usage_automodule.py index 015fadf2..e06ed892 100644 --- a/tests/roots/test-base/target/usage_automodule.py +++ b/tests/roots/test-base/target/usage_automodule.py @@ -21,6 +21,10 @@ class AutoModuleSettings(BaseSettings): default=5, ge=0, le=100, description='Shows constraints within doc string.' ) + field_with_examples: int = Field(0, examples=[123, 546, 789]) + """Shows examples within doc string.""" + + @field_validator('field_with_validator_and_alias', 'field_plain_with_validator') def check_max_length_ten(cls, v): """Show corresponding field with link/anchor.""" diff --git a/tests/test_configuration_fields.py b/tests/test_configuration_fields.py index 1d390792..37816175 100644 --- a/tests/test_configuration_fields.py +++ b/tests/test_configuration_fields.py @@ -1632,3 +1632,95 @@ def test_autodoc_pydantic_field_swap_name_and_alias_true_directive_global(parse_ }, ) assert_node(doctree, output_nodes) + + +def test_autodoc_pydantic_field_show_examples_true(autodocument): + kwargs = dict( + object_path='target.configuration.FieldShowExamples.field', **KWARGS + ) + + result = [ + '', + '.. py:pydantic_field:: FieldShowExamples.field', + ' :module: target.configuration', + ' :type: int', + '', + ' Field.', + '', + ' :Examples:', + ' - 2', + ' - 3', + '', + ] + + # explicit local + actual = autodocument(options_doc={'field-show-examples': True}, **kwargs) + assert result == actual + + # explicit local overwrite global + actual = autodocument( + options_app={'autodoc_pydantic_field_show_examples': False}, + options_doc={'field-show-examples': True}, + **kwargs, + ) + assert result == actual + + +def test_autodoc_pydantic_field_show_examples_false(autodocument): + kwargs = dict( + object_path='target.configuration.FieldShowExamples.field', **KWARGS + ) + + result = [ + '', + '.. py:pydantic_field:: FieldShowExamples.field', + ' :module: target.configuration', + ' :type: int', + '', + ' Field.', + '', + ] + + # explicit local + actual = autodocument(options_doc={'field-show-examples': False}, **kwargs) + assert result == actual + + # explicit local overwrite global + actual = autodocument( + options_app={'autodoc_pydantic_field_show_examples': True}, + options_doc={'field-show-examples': False}, + **kwargs, + ) + assert result == actual + + +def test_autodoc_pydantic_field_show_examples_ignore_extra(autodocument): + kwargs = dict( + object_path='target.configuration.FieldShowExamplesExtra.field', **KWARGS + ) + + result = [ + '', + '.. py:pydantic_field:: FieldShowExamplesExtra.field', + ' :module: target.configuration', + ' :type: int', + '', + ' Field.', + '', + ' :Examples:', + ' - 2', + ' - 3', + '', + ] + + # explicit local + actual = autodocument(options_doc={'field-show-examples': True}, **kwargs) + assert result == actual + + # explicit local overwrite global + actual = autodocument( + options_app={'autodoc_pydantic_field_show_examples': False}, + options_doc={'field-show-examples': True}, + **kwargs, + ) + assert result == actual \ No newline at end of file