Skip to content

Commit 9d5be4e

Browse files
authored
Merge pull request #42 from insspb/fix-empty-string
Fix: Empty string enum rendering + Add configurable amount of choices to display
2 parents db7325a + eb13e40 commit 9d5be4e

File tree

7 files changed

+98
-18
lines changed

7 files changed

+98
-18
lines changed

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Unreleased
55
----------
66

77
* [ `#39 <https://github.com/edoburu/sphinxcontrib-django/issues/39>`_ ] Fix table names of abstract models (`@insspb <https://github.com/insspb>`__)
8+
* [ `#41 <https://github.com/edoburu/sphinxcontrib-django/issues/41>`_ ] Fix rendering of iterable choices (`@insspb <https://github.com/insspb>`__)
89

910

1011
Version 2.3 (2023-04-12)

README.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ Optionally, you can include the table names of your models in their docstrings w
8484
# Add abstract database tables names (only takes effect if django_show_db_tables is True)
8585
django_show_db_tables_abstract = True # Boolean, default: False
8686
87+
Optionally, you can extend amount of displayed choices in model fields with them:
88+
89+
.. code-block:: python
90+
91+
# Integer amount of model field choices to show, default 10
92+
django_choices_to_show = 10
8793
8894
Advanced Usage
8995
--------------

sphinxcontrib_django/docstrings/__init__.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from .. import __version__
2424
from .attributes import improve_attribute_docstring
2525
from .classes import improve_class_docstring
26-
from .config import EXCLUDE_MEMBERS, INCLUDE_MEMBERS
26+
from .config import CHOICES_LIMIT, EXCLUDE_MEMBERS, INCLUDE_MEMBERS
2727
from .data import improve_data_docstring
2828
from .methods import improve_method_docstring
2929

@@ -64,7 +64,8 @@ def setup(app):
6464
app.add_config_value("django_show_db_tables", False, True)
6565
# Set default of django_show_db_tables_abstract to False
6666
app.add_config_value("django_show_db_tables_abstract", False, True)
67-
67+
# Integer amount of model field choices to show
68+
app.add_config_value("django_choices_to_show", CHOICES_LIMIT, True)
6869
# Setup Django after config is initialized
6970
app.connect("config-inited", setup_django)
7071

@@ -111,7 +112,7 @@ def setup_django(app, config):
111112
raise ConfigError(
112113
"The module you specified in the configuration 'django_settings' in your"
113114
" conf.py cannot be imported. Make sure the module path is correct and the"
114-
" source directoy is added to sys.path."
115+
" source directory is added to sys.path."
115116
) from e
116117
os.environ["DJANGO_SETTINGS_MODULE"] = config.django_settings
117118
django.setup()
@@ -182,7 +183,7 @@ def improve_docstring(app, what, name, obj, options, lines):
182183
if what == "class":
183184
improve_class_docstring(app, obj, lines)
184185
elif what == "attribute":
185-
improve_attribute_docstring(obj, name, lines)
186+
improve_attribute_docstring(app, obj, name, lines)
186187
elif what == "method":
187188
improve_method_docstring(name, lines)
188189
elif what == "data":

sphinxcontrib_django/docstrings/attributes.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from django.utils.module_loading import import_string
1010
from sphinx.util.docstrings import prepare_docstring
1111

12-
from .config import CHOICES_LIMIT
1312
from .field_utils import get_field_type, get_field_verbose_name
1413

1514
FIELD_DESCRIPTORS = (FileDescriptor, related_descriptors.ForwardManyToOneDescriptor)
@@ -23,12 +22,15 @@
2322
PhoneNumberDescriptor = None
2423

2524

26-
def improve_attribute_docstring(attribute, name, lines):
25+
def improve_attribute_docstring(app, attribute, name, lines):
2726
"""
2827
Improve the documentation of various model fields.
2928
3029
This improves the navigation between related objects.
3130
31+
:param app: The Sphinx application object
32+
:type app: ~sphinx.application.Sphinx
33+
3234
:param attribute: The instance of the object to document
3335
:type attribute: object
3436
@@ -56,21 +58,21 @@ def improve_attribute_docstring(attribute, name, lines):
5658
f"Internal field, use :class:`~{cls_path}.{field.name}` instead."
5759
)
5860
else:
59-
lines.extend(get_field_details(field))
61+
lines.extend(get_field_details(app, field))
6062
elif isinstance(attribute, FIELD_DESCRIPTORS):
6163
# Display a reasonable output for forward descriptors (foreign key and one to one fields).
62-
lines.extend(get_field_details(attribute.field))
64+
lines.extend(get_field_details(app, attribute.field))
6365
elif isinstance(attribute, related_descriptors.ManyToManyDescriptor):
6466
# Check this case first since ManyToManyDescriptor inherits from ReverseManyToOneDescriptor
6567
# This descriptor is used for both forward and reverse relationships
6668
if attribute.reverse:
67-
lines.extend(get_field_details(attribute.rel))
69+
lines.extend(get_field_details(app, attribute.rel))
6870
else:
69-
lines.extend(get_field_details(attribute.field))
71+
lines.extend(get_field_details(app, attribute.field))
7072
elif isinstance(attribute, related_descriptors.ReverseManyToOneDescriptor):
71-
lines.extend(get_field_details(attribute.rel))
73+
lines.extend(get_field_details(app, attribute.rel))
7274
elif isinstance(attribute, related_descriptors.ReverseOneToOneDescriptor):
73-
lines.extend(get_field_details(attribute.related))
75+
lines.extend(get_field_details(app, attribute.related))
7476
elif isinstance(attribute, (models.Manager, ManagerDescriptor)):
7577
# Somehow the 'objects' manager doesn't pass through the docstrings.
7678
module, model_name, field_name = name.rsplit(".", 2)
@@ -92,17 +94,22 @@ def improve_attribute_docstring(attribute, name, lines):
9294
lines.extend(docstring_lines[:-1])
9395

9496

95-
def get_field_details(field):
97+
def get_field_details(app, field):
9698
"""
9799
This function returns the detail docstring of a model field.
98100
It includes the field type and the verbose name of the field.
99101
102+
:param app: The Sphinx application object
103+
:type app: ~sphinx.application.Sphinx
104+
100105
:param field: The field
101106
:type field: ~django.db.models.Field
102107
103108
:return: The field details as list of strings
104109
:rtype: list [ str ]
105110
"""
111+
choices_limit = app.config.django_choices_to_show
112+
106113
field_details = [
107114
f"Type: {get_field_type(field)}",
108115
"",
@@ -111,13 +118,16 @@ def get_field_details(field):
111118
if hasattr(field, "choices") and field.choices:
112119
field_details.extend(["", "Choices:", ""])
113120
field_details.extend(
114-
[f"* ``{key}``" for key, value in field.choices[:CHOICES_LIMIT]]
121+
[
122+
f"* ``{key}``" if key != "" else "* ``''`` (Empty string)"
123+
for key, value in field.choices[:choices_limit]
124+
]
115125
)
116126
# Check if list has been truncated
117-
if len(field.choices) > CHOICES_LIMIT:
127+
if len(field.choices) > choices_limit:
118128
# If only one element has been truncated, just list it as well
119-
if len(field.choices) == CHOICES_LIMIT + 1:
129+
if len(field.choices) == choices_limit + 1:
120130
field_details.append(f"* ``{field.choices[-1][0]}``")
121131
else:
122-
field_details.append(f"* and {len(field.choices) - CHOICES_LIMIT} more")
132+
field_details.append(f"* and {len(field.choices) - choices_limit} more")
123133
return field_details

sphinxcontrib_django/docstrings/config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@
2222
"polymorphic_super_sub_accessors_replaced",
2323
}
2424

25-
#: How many choices should be shown for model fields
25+
#: How many choices should be shown for model fields by default,
26+
#: used as default for ``django_choices_to_show`` option
2627
CHOICES_LIMIT = 10

tests/roots/test-docstrings/dummy_django_app/models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ class ChoiceModel(models.Model):
9595
choice_limit_above = models.IntegerField(
9696
choices=[(i, i) for i in range(CHOICES_LIMIT + 2)]
9797
)
98+
choice_with_empty = models.CharField(
99+
choices=[("", "Empty"), ("Something", "Not empty")]
100+
)
98101

99102

100103
class TaggedItem(models.Model):

tests/test_attribute_docstrings.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,64 @@ def test_choice_field_limit_above(app, do_autodoc):
371371
]
372372

373373

374+
@pytest.mark.sphinx(
375+
"html", testroot="docstrings", confoverrides={"django_choices_to_show": 15}
376+
)
377+
def test_choice_field_custom_limit(app, do_autodoc):
378+
actual = do_autodoc(
379+
app, "attribute", "dummy_django_app.models.ChoiceModel.choice_limit_above"
380+
)
381+
print(actual)
382+
assert list(actual) == [
383+
"",
384+
".. py:attribute:: ChoiceModel.choice_limit_above",
385+
" :module: dummy_django_app.models",
386+
"",
387+
" Type: :class:`~django.db.models.IntegerField`",
388+
"",
389+
" Choice limit above",
390+
"",
391+
" Choices:",
392+
"",
393+
" * ``0``",
394+
" * ``1``",
395+
" * ``2``",
396+
" * ``3``",
397+
" * ``4``",
398+
" * ``5``",
399+
" * ``6``",
400+
" * ``7``",
401+
" * ``8``",
402+
" * ``9``",
403+
" * ``10``",
404+
" * ``11``",
405+
"",
406+
]
407+
408+
409+
@pytest.mark.sphinx("html", testroot="docstrings")
410+
def test_choice_field_empty(app, do_autodoc):
411+
actual = do_autodoc(
412+
app, "attribute", "dummy_django_app.models.ChoiceModel.choice_with_empty"
413+
)
414+
print(actual)
415+
assert list(actual) == [
416+
"",
417+
".. py:attribute:: ChoiceModel.choice_with_empty",
418+
" :module: dummy_django_app.models",
419+
"",
420+
" Type: :class:`~django.db.models.CharField`",
421+
"",
422+
" Choice with empty",
423+
"",
424+
" Choices:",
425+
"",
426+
" * ``''`` (Empty string)",
427+
" * ``Something``",
428+
"",
429+
]
430+
431+
374432
if PHONENUMBER:
375433

376434
@pytest.mark.sphinx("html", testroot="docstrings")

0 commit comments

Comments
 (0)