Style guide#
This chapter is the documentation writing style guide for icalendar. Refer to this guide when you need examples of language, terminology, code, or markup. It applies to both narrative and API reference documentation. Differences and exceptions for both types of documentation are noted for each.
Voice and tone#
The icalendar community is warm and friendly, direct and clear, and helpful. icalendar’s documentation reflects its community.
For professional technical documentation writing guidance, the icalendar community selected the Microsoft Writing Style Guide because it aligns with its spirit.
Key concepts from that guide include the following.
Documentation should be informational, but friendly.
Address the reader by using “you” and “your” instead of “the user” or “the user’s.”
When giving instructions and in headings, use the imperative mood of verbs.
Use the active voice whenever possible, avoiding the passive voice.
Headings should be “Sentence cased,” not “Title Cased.”
Keep sentences short and understandable.
Note
The style guide is mostly enforced through the use of Vale, but only in the narrative documentation at this time.
Specific guidelines#
The icalendar developers have adopted additional guidelines.
For narrative documentation only, use one sentence per line.
For API reference documentation only, line length is automatically enforced by a Python code formatter.
Use dashes
-in filenames and avoid underscores_.Images should be no wider than 740 pixels to fit within the documentation’s main view port.
Organization and structure#
icalendar documentation uses the Diátaxis framework, a systematic approach to technical documentation authoring. Rather than attempt to organize documentation toward a specific role such as developer or user, the Diátaxis framework organizes documentation into the four categories of tutorials, how-to guides, explanation, and reference. In this way, your title or role does not matter. Instead, what you want to achieve matters. By keeping each page focused on one category, readers can focus on getting work done, understanding, or experimenting.
Markup and formatting#
When writing documentation, use the reStructuredText markup syntax.
When writing API reference documentation, write docstrings inside the Python code following the Google Python Style Guide, Comments and Docstrings format. These docstrings get rendered into the API reference documentation.
Sphinx and its extensions enhance core reStructuredText with additional features.
automatic API documentation file generation in reStructuredText through sphinx-apidoc
rendering of API documentation files and Python docstrings to HTML through sphinx.ext.apidoc, sphinx.ext.napoleon, and sphinx_autodoc_typehints
rendering of Python source files to HTML through sphinx.ext.viewcode
hyperlinking to internal and external documentation through sphinx.ext.intersphinx
display and one-click copying of code blocks through sphinx_copybutton
user interface enhancements, including tabular interfaces, cards, and buttons through sphinx_design
redirects for moved files through sphinx_reredirects
404 not found page through notfound.extension
For configuration of these features, see Configure a package.
Markup examples#
All of the following markup examples will work in both narrative and API documentation. API documentation has additional syntax usage.
General cross-references#
Sphinx supports various cross-referencing roles to create hyperlinks to other elements in the documentation.
In icalendar’s documentation, the most frequently used roles are the following.
docto link to a filerefto link to an arbitrary labeltermto link to a glossary termrfcto link to an RFC
When referencing a specific section in an RFC, copy the anchor name from the URL, that is, the part of the URL including and after the pound sign #, and use the following syntax.
:rfc:`number#anchor`
The following example shows how to link to RFC 6350, Section 6.2.2.
:rfc:`6350#section-6.2.2`
Which renders as shown.
See also
Cross-reference Python objects#
In addition to the general cross-references, Sphinx supports various cross-referencing roles to create hyperlinks to Python objects. As with all cross-references, these forms consist of a role, a target, an optional custom label, and an optional modifier.
In icalendar’s documentation, the most frequently used roles in the Python domain are the following.
The target must be either a full dotted Python path or use a shortcut to disambiguate to which object it should be hyperlinked.
The following reStructuredText source examples show which form to use to get the preferred display. These examples are ordered from most to least preferred.
- object only
Use this form only when the object exists in the rendered HTML page. Thus the object’s context is clear to be that within its Python module or class. To do so, use the tilde character
~as a modifier, along with the role and target, to display only the object when rendered.:class:`~icalendar.prop.vXmlReference`
The above example will render as shown.
- full dotted Python path
Use this form only when the object exists outside the rendered HTML page, such as in a superclass. To do so, use the following syntax consisting of a role and target only.
:class:`icalendar.prop.vXmlReference`
The above example will render as shown.
- custom text label
As a compromise to showing the full dotted Python path, while retaining sufficient context of the object’s location when displayed, use a custom text label. Use this form only when horizontal space is constrained when rendered to HTML.
:class:`prop.vXmlReference <icalendar.prop.vXmlReference>`
The above example will render as shown.
See also
Note
Because icalendar uses the Python domain exclusively, it’s safe to omit the :py prefix when creating references to Python objects.
Target shortcut#
As well as the above forms, you can use a shortcut to avoid repeating the full dotted path in the target.
In the page in which the reference appears, you may use the currentmodule directive and omit the name of the current module.
.. currentmodule:: icalendar.prop
The class :class:`vXmlReference` is useful.
The above example will render as shown.
The class
vXmlReferenceis useful.
Python docstrings#
Python docstrings provide clear explanations of what code does, making it easier for people to use and maintain it. Sphinx generates documentation automatically from docstrings into the icalendar API reference guide, enhancing code readability and usability.
Python docstrings typically include reStructuredText markup, often including cross-references to narrative and API documentation.
PEP 257 describes core docstring conventions. To enhance the display of its API documentation, icalendar uses the Sphinx extensions sphinx.ext.napoleon and sphinx_autodoc_typehints. The former extension supports Google style docstrings, which are easier to write and read, especially when Sphinx renders them to HTML. The latter extension supports Python 3 annotations, or type hints, for documenting acceptable argument types and return value types of functions.
See also
Google Python Style Guide for more examples of docstrings and their format.
Docstring structure#
In addition to the structure of docstrings as defined by PEP 257, icalendar has adopted conventions from the Google Python Style Guide.
A docstring consists of a summary, followed by a possible description, then any helpful sections usually ordered by inputs then outputs.
To create a docstring section, use either one of the supported section headers or any string for a custom section header, followed by a colon :, followed by a block of indented text.
Supported section headers enhance formatting, such as structuring method arguments or code examples, whereas custom section headers render as just a heading and content.
All items should terminate with a period.
The following docstrings items are the most frequently used in icalendar, although they may be others.
- Summary
Docstrings must begin with a one-line summary of the Python object, terminated by a period.
- Description
When the one-line summary is insufficient to describe the Python object, then write an overall description of what it does, without going into details of how it does it. Leave implementation details to the code, and optionally inline code comments. Separate the summary and description with a blank line.
AttributesEach attribute should consist of its name and a brief description. By virtue of Sphinx extensions and the use of type hints for the Python object, you may omit the argument’s type, allowing Sphinx to automatically render it for you.
ParametersEach parameter should consist of its name and a brief description. By virtue of Sphinx extensions and the use of type hints for the Python object, you may omit the parameter’s type, allowing Sphinx to automatically render it for you.
ReturnsThe return value consists of its return type and a brief description.
RaisesThe
Raisessection is a list of all exceptions that are relevant to the interface.ExamplesUsage examples of the Python object. These must be valid examples, including imports, as they must pass tests. Not only are these helpful to developers, but they’re fun to write, and rewarding when they pass.
See also
Docstring examples#
The following examples are actual docstrings in icalendar that will render properly.
Event.new()#
See the rendered view of this class method at Event.new().
@classmethod
def new(
cls,
/,
attendees: list[vCalAddress] | None = None,
categories: Sequence[str] = (),
classification: CLASS | None = None,
color: str | None = None,
comments: list[str] | str | None = None,
concepts: CONCEPTS_TYPE_SETTER = None,
conferences: list[Conference] | None = None,
contacts: list[str] | str | None = None,
created: date | None = None,
description: str | None = None,
end: date | datetime | None = None,
last_modified: date | None = None,
links: LINKS_TYPE_SETTER = None,
location: str | None = None,
organizer: vCalAddress | str | None = None,
priority: int | None = None,
refids: list[str] | str | None = None,
related_to: RELATED_TO_TYPE_SETTER = None,
sequence: int | None = None,
stamp: date | None = None,
start: date | datetime | None = None,
status: STATUS | None = None,
transparency: TRANSP | None = None,
summary: str | None = None,
uid: str | uuid.UUID | None = None,
url: str | None = None,
):
"""Create a new event with all required properties.
This creates a new Event in accordance with :rfc:`5545`.
Parameters:
attendees: The :attr:`attendees` of the event.
categories: The :attr:`categories` of the event.
classification: The :attr:`classification` of the event.
color: The :attr:`color` of the event.
comments: The :attr:`~icalendar.Component.comments` of the event.
concepts: The :attr:`~icalendar.Component.concepts` of the event.
conferences: The :attr:`conferences` of the event.
created: The :attr:`~icalendar.Component.created` of the event.
description: The :attr:`description` of the event.
end: The :attr:`end` of the event.
last_modified: The :attr:`~icalendar.Component.last_modified` of the event.
links: The :attr:`~icalendar.Component.links` of the event.
location: The :attr:`location` of the event.
organizer: The :attr:`organizer` of the event.
priority: The :attr:`priority` of the event.
refids: :attr:`~icalendar.Component.refids` of the event.
related_to: :attr:`~icalendar.Component.related_to` of the event.
sequence: The :attr:`sequence` of the event.
stamp: The :attr:`~icalendar.Component.stamp` of the event.
If None, this is set to the current time.
start: The :attr:`start` of the event.
status: The :attr:`status` of the event.
summary: The :attr:`summary` of the event.
transparency: The :attr:`transparency` of the event.
uid: The :attr:`uid` of the event.
If None, this is set to a new :func:`uuid.uuid4`.
url: The :attr:`url` of the event.
Returns:
:class:`Event`
Raises:
~error.InvalidCalendar: If the content is not valid according to :rfc:`5545`.
.. warning:: As time progresses, we will be stricter with the validation.
"""
event: Event = super().new(
stamp=stamp if stamp is not None else cls._utc_now(),
created=created,
last_modified=last_modified,
comments=comments,
links=links,
related_to=related_to,
refids=refids,
concepts=concepts,
)
event.summary = summary
event.description = description
event.uid = uid if uid is not None else uuid.uuid4()
event.start = start
event.end = end
event.color = color
event.categories = categories
event.sequence = sequence
event.classification = classification
event.url = url
event.organizer = organizer
event.location = location
event.priority = priority
event.transparency = transparency
event.contacts = contacts
event.status = status
event.attendees = attendees
event.conferences = conferences
if cls._validate_new:
cls._validate_start_and_end(start, end)
return event
Component.register#
See the rendered view of this class method at Component.register.
@classmethod
def register(cls, component_class: type[Component]) -> None:
"""Register a custom component class.
Parameters:
component_class: Component subclass to register. Must have a ``name`` attribute.
Raises:
ValueError: If ``component_class`` has no ``name`` attribute.
ValueError: If a component with this name is already registered.
Examples:
Create a custom icalendar component with the name ``X-EXAMPLE``:
.. code-block:: pycon
>>> from icalendar import Component
>>> class XExample(Component):
... name = "X-EXAMPLE"
... def custom_method(self):
... return "custom"
>>> Component.register(XExample)
"""
if not hasattr(component_class, "name") or component_class.name is None:
raise ValueError(f"{component_class} must have a 'name' attribute")
if cls._components_factory is None:
cls._components_factory = ComponentFactory()
# Check if already registered
existing = cls._components_factory.get(component_class.name)
if existing is not None and existing is not component_class:
raise ValueError(
f"Component '{component_class.name}' is already registered as {existing}"
)
cls._components_factory.add_component_class(component_class)
parser.split_on_unescaped_semicolon#
See the rendered view of this class method at parser.split_on_unescaped_semicolon.
def split_on_unescaped_semicolon(text: str) -> list[str]:
r"""Split text on unescaped semicolons and unescape each part.
Splits only on semicolons not preceded by a backslash.
After splitting, unescapes backslash sequences in each part.
Used by vCard structured properties (ADR, N, ORG) per :rfc:`6350`.
Parameters:
text: Text with potential escaped semicolons (e.g., "field1\\;with;field2")
Returns:
List of unescaped field strings
Examples:
.. code-block:: pycon
>>> from icalendar.parser import split_on_unescaped_semicolon
>>> split_on_unescaped_semicolon(r"field1\;with;field2")
['field1;with', 'field2']
>>> split_on_unescaped_semicolon("a;b;c")
['a', 'b', 'c']
>>> split_on_unescaped_semicolon(r"a\;b\;c")
['a;b;c']
>>> split_on_unescaped_semicolon(r"PO Box 123\;Suite 200;City")
['PO Box 123;Suite 200', 'City']
"""
if not text:
return [""]
result = []
current = []
i = 0
while i < len(text):
if text[i] == "\\" and i + 1 < len(text):
# Escaped character - keep both backslash and next char
current.append(text[i])
current.append(text[i + 1])
i += 2
elif text[i] == ";":
# Unescaped semicolon - split point
result.append(unescape_backslash("".join(current)))
current = []
i += 1
else:
current.append(text[i])
i += 1
# Add final part
result.append(unescape_backslash("".join(current)))
return result
Style and quality checks#
When making a contribution to documentation, style and quality checks must pass both locally while developing and in continuous integration after opening a pull request.
See also
See Build and check documentation for how to set up and run the builds and checks on documentation.
In addition to automated checks, perform visual checks to ensure that documentation renders as intended.
icalendar is configured with Read the Docs to build pull request previews of documentation. When a pull request is opened on a branch and there are changes to any documentation files, Read the Docs will build a preview of the documentation, and insert a link to the build in the pull request description. If there’s no link, either there are no changes to documentation files or the branch isn’t in the icalendar repository. All pull request preview builds are listed on Read the Docs at icalendar builds.
Always pay attention to errors and continuous integration failures, and attempt to resolve them. Warnings may provide helpful information.
Link checks#
All links must be valid.
See also
See how to check links in Check links.
Spelling and grammar#
Vale is a linter for narrative text. It checks spelling, English grammar and syntax, and style guides. icalendar uses American English.
Because it’s difficult to automate good American English grammar and syntax, it’s not strictly enforced. It’s understood that contributors might not be fluent in English. If you’re more comfortable writing in your preferred language, then you may submit a pull request written in your language, and the icalendar team will use translation tools to translate it to English. You’re encouraged to make a reasonable effort, and to request a review of their pull request from community members who are fluent in English to fix grammar and syntax. Please ask!
Note
More corrections to spellings and Vale’s configuration are welcome by submitting a pull request. This is an easy way to become a contributor to icalendar. See Vale configuration for details.
See also
See how to use Vale.