Combining Sphinx Autodoc with Manual documentation - python

I am currently working on a reference documentation that was initially documented manually with .. class:: All classes in the modules where documented this way.
However, it was necessary recently to allow some automation to the documentation, such as show-inheritance of each class, but the problem now is how to combine the current manual documentation with autodoc.
I have tried to just simply replace .. class:: with .. autoclass:: but this resulted in a output where the description and parameters descriptions that were manually typed into the documentation (as against being generated from docstrings) to not be rendered in the output. Can anyone advise on what to do to solve this?
So here is more details:
Take a look at this:
.. autoclass:: wagtail.blocks.CharBlock
:show-inheritance:
.. class:: CharBlock
A single-line text input. The following keyword arguments are accepted in addition to the standard ones:
:param required: If true (the default), the field cannot be left blank.
:param max_length: The maximum allowed length of the field.
:param min_length: The minimum allowed length of the field.
:param help_text: Help text to display alongside the field.
:param validators: A list of validation functions for the field (see `Django Validators <https://docs.djangoproject.com/en/stable/ref/validators/>`__).
:param form_classname: A value to add to the form field's ``class`` attribute when rendered on the page editing form.
The output:
enter image description here
The issue is I have to repeat the class name, which would be rather confusing for the user of the documentation. autoclass would not render the parameters, hence the need for class. I need autoclass to be able to show the inheritance of the class(Bases). So I don't know if there is a directive that can perform the functions of class without me having to mention the class name, or is there any way I can force class not to take in any arguments?
I hope this clearer. Thanks

It is possible to use autodoc for some things (in this case it's just :show-inheritance:) and "manual" documentation (content that does not come from a docstring) for others, within the same directive.
Remove .. class:: and ensure that all lines after the .. autoclass:: line have the same indentation. I think this should produce the output that you want.
.. autoclass:: wagtail.blocks.CharBlock
:show-inheritance:
A single-line text input. The following keyword arguments are accepted in addition to the standard ones:
:param required: If true (the default), the field cannot be left blank.
:param max_length: The maximum allowed length of the field.
:param min_length: The minimum allowed length of the field.
:param help_text: Help text to display alongside the field.
:param validators: A list of validation functions for the field (see `Django Validators <https://docs.djangoproject.com/en/stable/ref/validators/>`__).
:param form_classname: A value to add to the form field's ``class`` attribute when rendered on the page editing form.

Related

Variable format changed to ~class.attribute in docs

So I've been working on some docs for some weeks now and they always appeared fine until last night all the variables started coming up in the format ~Class.Attribute. I attempted to rollback to multiple previous versions that had been working but they all show the Variables like this. Is there something I'm missing to get these to only show the attribute name without the ~Class..
class Certification(TMDbObj):
""" Represents a single Certification.
Attributes:
certification (str): Certification text.
meaning (str): Certification meaning.
order (int): Certification Order.
"""
This is a bug in the Sphinx napoleon extension it was reported in issue #10181 and will be fixed in the next Sphinx 4.5.0 release.
It happens for variables that aren't prefixed in the docstring with the class name or cls or self.

flask_admin change inline_models behaviour

I want to change some existing Python code that uses flask_admin. One of the views uses inline_models with the (ClassName, Options) declaration pattern. The inlined class has, amongst others, a text field.
I want to change the flask_admin default behaviour in the following ways:
I want to make the text field read-only. I.e. still display it, but prevent the user from changing existing content.
I do not want to allow users to delete instances of the inlined class, i.e. I want to get rid of the "Delete?" checkbox next to every entry.
I want to override the default "Add Item" button behaviour with some custom JavaScript.
I did some Googling around but anything that looked potentially promising also looked very non-trivial. I'm hoping for some reasonably straight forward way to achieve this.
Your help would be much appreciated.
Yeesh. It looks like we're out in poorly-documented territory, here. It's hard to know if I'm improving on what you've already found, but I'll hope you're looking for something easier than writing a custom administrative view template.
Following the calls, it looks like the options dictionary eventually gets passed to the constructor of InlineBaseFormAdmin where the various form_* keys are extracted and applied (not sure all are respected, but I see at least form_base_class, form_columns, form_excluded_columns, form_args, form_extra_fields, form_rules, form_label, form_column_labels, form_widget_args). I think you can accomplish what you need via form_widget_args, but you can probably also get there via form_rules or by overriding InlineBaseFormAdmin's get_form or postprocess_form methods:
class SomeModelView(MyBaseModelView):
...
inline_models = [(db.SomeOtherModel, {
"form_widget_args": {
"uneditable_field_name": {"readonly": True}
}
})]
...
The delete option can be controlled by providing your own inline form model to override display_row_controls:
from flask_admin.contrib.sqla.form import InlineModelConverter
from flask_admin.contrib.sqla.fields import InlineModelFormList
class CrouchingTigerHiddenModelFormList(InlineModelFormList):
def display_row_controls(self, field): return False
class MyInlineModelConverter(InlineModelConverter):
inline_field_list_type = CustomInlineModelFormList
#adding to above example
class SomeModelView(MyBaseModelView):
...
inline_model_form_converter = MyInlineModelConverter
inline_models = [(db.SomeOtherModel, {
"form_widget_args": {
"uneditable_field_name": {"readonly": True}
}
})]
...
NOTE: The widget args, such as readonly, are getting passed on to wtforms as render_kw, but at a blush the WTForms docs aren't clear that these get expressed as attributes in the resulting HTML input element (so any HTML input element attributes are valid here).
It looks like form.js controls this behavior, so you should be able to monkey-patch its addInlineField method to execute your own code before or after the model addition. You could override the create and/or edit templates for this--but if you're using flask-admin 1.5.0+, this might be as simple as adding extra_js = ["your-custom.js"] to the view class (caution: it looks like this script gets included on every page for this view).

What is the standard Sphinx documentation format for a method?

I am trying to document my method using a standard format, but in my search I found many "standard" ways of documenting methods. My method is:
#staticmethod
def validateMasterAttribute(attribute):
...
and according to this official Python documentation I should document it like this:
#staticmethod
def validateMasterAttribute(attribute):
""" Validate that the entered master attribute has all the required nodes
Keyword arguments:
attribute -- type lxml.etree._Element, it is the xml node for the master attribute
Return:
Nothing if the node contains all the required sub nodes. Otherwise, it throws a TagNotFoundException exception
"""
...
however, it is written in this question that I should document it like:
#staticmethod
def validateMasterAttribute(attribute):
"""
Validate that the entered master attribute has all the required nodes
:attribute: type lxml.etree._Element, it is the xml node for the master attribute
return: Nothing if the node contains all the required sub nodes. Otherwise, it throws a TagNotFoundException exception
"""
...
I also found another docstring format, which seems old. What is the format that Sphinx can parse and generate web pages from?
You might want to consult Documenting Your Project Using Sphinx.
Alternatively you can use Napoleon which is an extension supporting google syntax for doctrings.
Choose one. Be consistent.

What is the difference between a mongoengine.DynamicEmbeddedDocument vs mongoengine.DictField?

A mongoengine.DynamicEmbeddedDocument can be used to leverage MongoDB's flexible schema-less design. It's expandable and doesn't apply type constraints to the fields, afaik.
A mongoengine.DictField similarly allows for use of MongoDB's schema-less nature. In the documentation they simply say (w.r.t. the DictField)
This is similar to an embedded document, but the structure is not defined.
Does that mean, then, the mongoengine.fields.DictField and the mongoengine.DynamicEmbeddedDocument are completely interchangeable?
EDIT (for more information):
mongoengine.DynamicEmbeddedDocument inherits from mongoengine.EmbeddedDocument which, from the code is:
A mongoengine.Document that isn't stored in its own collection. mongoengine.EmbeddedDocuments should be used as fields on mongoengine.Documents through the mongoengine.EmbeddedDocumentField field type.
A mongoengine.fields.EmbeddedDocumentField is
An embedded document field - with a declared document_type. Only valid values are subclasses of EmbeddedDocument.
Does this mean the only thing that makes the DictField and DynamicEmbeddedDocument not totally interchangeable is that the DynamicEmbeddedDocument has to be defined through the EmbeddedDocumentField field type?
From what I’ve seen, the two are similar, but not entirely interchangeable. Each approach may have a slight advantage based on your needs. First of all, as you point out, the two approaches require differing definitions in the document, as shown below.
class ExampleDynamicEmbeddedDoc(DynamicEmbeddedDocument):
pass
class ExampleDoc(Document):
dict_approach = DictField()
dynamic_doc_approach = EmbeddedDocumentField(ExampleDynamicEmbeddedDoc, default = ExampleDynamicEmbeddedDoc())
Note: The default is not required, but the dynamic_doc_approach field will need to be set to a ExampleDynamicEmbeddedDoc object in order to save. (i.e. trying to save after setting example_doc_instance.dynamic_doc_approach = {} would throw an exception). Also, you could use the GenericEmbeddedDocumentField if you don’t want to tie the field to a specific type of EmbeddedDocument, but the field would still need to be point to an object subclassed from EmbeddedDocument in order to save.
Once set up, the two are functionally similar in that you can save data to them as needed and without restrictions:
e = ExampleDoc()
e.dict_approach["test"] = 10
e.dynamic_doc_approach.test = 10
However, the one main difference that I’ve seen is that you can query against any values added to a DictField, whereas you cannot with a DynamicEmbeddedDoc.
ExampleDoc.objects(dict_approach__test = 10) # Returns a QuerySet containing our entry.
ExampleDoc.objects(dynamic_doc_approach__test = 10) # Throws an exception.
That being said, using an EmbeddedDocument has the advantage of validating fields which you know will be present in the document. (We simply would need to add them to the ExampleDynamicEmbeddedDoc definition). Because of this, I think it is best to use a DynamicEmbeddedDocument when you have a good idea of a schema for the field and only anticipate adding fields minimally (which you will not need to query against). However, if you are not concerned about validation or anticipate adding a lot of fields which you’ll query against, go with a DictField.

Limiting one Content item per Member in a Folder on Plone 4

I have created a custom Archetypes content type called "Résumé" and would like to enforce a limitation that lets a Member add only one item of this type inside a folder. Even better would be redirecting the member to the edit page of his or her item, if it already exists in that folder.
How can I enforce this limitation and provide this extra functionality?
A solution to a similar usecase for Plone 3 can be found in the eestec.base. We did it by overriding the createObject.cpy and adding a special check for this.
Code is in the collective SVN, at http://dev.plone.org/collective/browser/eestec.base/trunk/eestec/base/skins/eestec_base_templates/createObject.cpy, lines 20-32.
Well, it is a sort of validation constraint, so maybe add a validator to the title field that in reality does not bother about the title, but checks the user etc.? (I think a field validator is passed enough information to pull this off, if not, overriding the post_validate method or listening to the corresponding event should work.)
If you try this, bear in mind that post_validate is already called while the user is editing (ie on moving focus out of a field).
I dont't know if it's best practice, but you can hook up on def at_post_create_script from base object on creation and manage_beforeDelete on delete.
for example:
from Products.ATContentTypes.lib import constraintypes
class YourContentype(folder.ATFolder)
[...]
def at_post_create(self):
origin = aq_parent(aq_inner(self))
origin.setConstrainTypesMode(constraintypes.ENABLED) # enable constrain types
org_types = origin.getLocallyAllowedTypes() # returns an immutable tuple
new_types = [x for x in org_types if x! = self.portal_type] # filter tuple into a list
origin.setLocallyAllowedTypes(new_types)
def manage_beforeDelete(self, item, container)
BaseObject.manage_beforeDelete(self, item, container) # from baseObject
self._v_cp_refs = None # from baseObject
origin = aq_parent(aq_inner(self))
origin.setConstrainTypesMode(constraintypes.ENABLED) # enable constrain types
org_types = origin.getLocallyAllowedTypes() # returns an immutable tuple
new_types = [x for x in org_types].append(self.portal_type)
origin.setLocallyAllowedTypes(new_types)
Note: There is also a method called setImmediatelyAddableTypes(), which you may want to explore.
Note II: This does not survive content migration.

Categories