Package: auto-pytabs
AutoPyTabsAutomatically generate code examples for different Python versions inmkdocsorSphinxbased documentations, or a plainmarkdownworkflow, making use of thepymdown "tabbed"markdown extension for markdown,
andsphinx{design} tabsfor Sphinx.RationaleThe problemPython project documentation typically include code examples. Given that most of the time, a project will support
multiple versions of Python, it would be ideal to showcase the adjustments that can or need to be made for different
Python versions. This can be achieved by including several versions of the example code, conveniently displayed using
thepymdown "tabbed"extension for markdown, orsphinx{design} tabsfor Sphinx.This, however, raises several problems:Maintaining multiple versions of a single example is tedious and error-prone as they can easily
become out of syncFiguring out which examples need to be changed for which specific Python version is a labour intensive taskDropping or adding support for Python versions requires revisiting every example in the documentationChecking potentially ~4 versions of a single example into VCS creates unnecessary noiseGiven those, it's no surprise that the current standard is to only show examples for the lowest supported version of Python.The solutionAutoPyTabsaims to solve all of these problems by automatically generating versions (using the awsomeruffproject) of code examples, targeting different Python versionsat build-time, based on a base version (the lowest supported Python version).
This means that:There exists only one version of each example: The lowest supported version becomes the source of truth,
therefore preventing out-of-sync examples and reducing maintenance burdenDropping or adding support for Python versions can be done via a simple change in a configuration fileTable of contentsUsage with mkdocs / markdownConfigurationDifferences between the mkdocs plugin vs markdown extensionExamplesSelectively disableCompatibility withpymdownx.snippetsUsage with SphinxConfigurationDirectivesExamplesCompatibility with other extensionsInstallationFor mkdocs:pip install auto-pytabs[mkdocs]For markdown:pip install auto-pytabs[markdown]For sphinx:pip install auto-pytabs[sphinx]Usage with mkdocs / markdownConfigurationMkdocs pluginsite_name:My Docsmarkdown_extensions:-pymdownx.tabbed:plugins:-auto_pytabs:min_version:"3.7"# optionalmax_version:"3.11"# optionaltab_title_template:"Python{min_version}+"# optionalno_cache:false# optionaldefault_tab:"highest"# optionalreverse_order:false# optionalAvailable configuration optionsNameDefaultDescriptionmin_version(3, 7)Minimum python versionmax_version(3, 7)Maximum python versiontab_title_template"Python {min_version}+"Template for tab titlesno_cacheFalseDisable file system cachingdefault_tabhighest(highestorlowest) Version tab to preselectreverse_orderFalseReverse the order of tabs. Default is to go from lowest to highest versionMarkdown extensionimportmarkdownmd=markdown.Markdown(extensions=["auto_pytabs"],extension_configs={"auto_pytabs":{"min_version":"3.7",# optional"max_version":"3.11",# optional"tab_title_template":"Python{min_version}+",# optional"default_tab":"highest",# optional"reverse_order":False,# optional}},)Available configuration optionsNameDefaultDescriptionmin_version(3, 7)Minimum python version to generate code formax_version(3, 7)Maximum python version to generate code fortab_title_template"Python {min_version}+"Template for tab titlesdefault_tabhighest(highestorlowest) Version tab to preselectreverse_orderFalseReverse the order of tabs. Default is to go from lowest to highest versionDifferences between the mkdocs plugin and markdown extensionAutoPyTabs ships as a markdown extension and an mkdocs plugin, both of which can be used in mkdocs. The only difference
between them is that the mkdocs plugin supports caching, which can make subsequent builds faster (i.e. when usingmkdocs serve).
The reason why the markdown extension does not support caching is thatmarkdowndoes not have clearly defined build
steps with wich an extension could interact (like mkdocsplugin events),
making it impossible to know when to persist cached items to disk / evict unused items.If you are using mkdocs, the mkdocs plugin is recommended. If you have caching disabled, there will be no difference either way.Should you wish to integrate the markdown extension into a build process where you can manually persist the cache after the build,
you can explicitly pass it a cache:importmarkdownfromauto_pytabs.coreimportCachecache=Cache()md=markdown.Markdown(extensions=["auto_pytabs"],extension_configs={"auto_pytabs":{"cache":cache}},)defbuild_markdown()->None:md.convertFile("document.md","document.html")cache.persist()ExamplesInput```python
from typing import Optional, Dict

def foo(bar: Optional[str]) -> Dict[str, str]:
    ...
```Equivalent markdown=== "Python 3.7+"
    ```python
    from typing import Optional, Dict

    def foo(bar: Optional[str]) -> Dict[str, str]:
        ...
    ```

=== "Python 3.9+"
    ```python
    from typing import Optional
    
    
    def foo(bar: Optional[str]) -> dict[str, str]:
        ...
    ```

==== "Python 3.10+"
    ```python
    def foo(bar: str | None) -> dict[str, str]:
        ...
    ```Nested blocksNested tabs are supported as well:Input=== "Level 1-1"

    === "Level 2-1"

        ```python
        from typing import List
        x: List[str]
        ```

    === "Level 2-2"
    
        Hello, world!

=== "Level 1-2"

    Goodbye, world!Equivalent markdown=== "Level 1-1"

    === "Level 2-1"

        === "Python 3.7+"
            ```python
            from typing import List
            x: List[str]
            ```
        
        === "Python 3.9+"
            ```python
            x: list[str]
            ```

    === "Level 2-2"

        Goodbye, world!

=== "Level 1-2"
    Hello, world!Selectively disableYou can disable conversion for a single code block:<!-- autopytabs: disable-block -->
```python
from typing import Set, Optional

def bar(baz: Optional[str]) -> Set[str]:
    ...
```Or for whole sections / files<!-- autopytabs: disable -->
everything after this will be ignored
<!-- autopytabs: enable -->
re-enables conversion againCompatibility withpymdownx.snippetsIf thepymdownx.snippetsextension is used, make sure that it runsbeforeAutoPyTabUsage with SphinxAutPyTabs provides a Sphinx extensionauto_pytabs.sphinx_ext, enabling its functionality
for the.. code-blockand.. literalincludedirectives.ConfigurationExample configurationextensions=["auto_pytabs.sphinx_ext","sphinx_design"]auto_pytabs_min_version=(3,7)# optionalauto_pytabs_max_version=(3,11)# optionalauto_pytabs_tab_title_template="Python{min_version}+"# optional# auto_pytabs_no_cache = True  # disabled file system caching# auto_pytabs_compat_mode = True  # enable compatibility mode# auto_pytabs_default_tab = "lowest"  # Pre-select the tab with the lowest version# auto_pytabs_reverse_order = True  # reverse the order of tabs to highest > lowestAvailable configuration optionsNameDefaultDescriptionauto_pytabs_min_version(3, 7)Minimum python version to generate code forauto_pytabs_max_version(3, 7)Maximum python version to generate code forauto_pytabs_tab_title_template"Python {min_version}+"Template for tab titlesauto_pytabs_no_cacheFalseDisable file system cachingauto_pytabs_compat_modeFalseEnablecompatibility modeauto_pytabs_default_tabhighestEitherhighestorlowest. Version tab to preselectauto_pytabs_reverse_orderFalseReverse the order of tabs. Default is to go from lowest to highest versionExamplesInput..code-block::pythonfromtypingimportOptional,Dictdeffoo(bar:Optional[str])->Dict[str,str]:...Equivalent ReST..tab-set::..tab-item::Python 3.7+..code-block::pythonfromtypingimportOptional,Dictdeffoo(bar:Optional[str])->Dict[str,str]:.....tab-item::Python 3.9+..code-block::pythonfromtypingimportOptionaldeffoo(bar:Optional[str])->dict[str,str]:.....tab-item::Python 3.10+..code-block::pythondeffoo(bar:str|None)->dict[str,str]:...DirectivesAutoPyTabs overrides the built-incode-blockandliteral-includedirectives,
extending them with auto-upgrade and tabbing functionality, which means no special
directives, and therefore changes to existing documents are needed.Additionally, a:no-upgrade:option is added to the directives, which can be used to
selectively fall back the default behaviour.Two new directives are provided as well:.. pytabs-code-block::.. pytabs-literalinclude::which by default act exactly like.. code-blockand.. literalincluderespectively,
and are mainly to provide AutoPyTab's functionality incompatibility mode.Compatibility modeIf you don't want the default behaviour of directive overrides, and instead wish to use the.. pytabs-directives manually (e.g. because of compatibility issues with other extensions
or because you only want to apply it to select code blocks) you can make use AutoPyTabs' compatibility
mode. To enable it, simply use theauto_pytabs_compat_mode = Trueinconf.py. Now, only content within.. pytabs-directives will be upgraded.Compatibility with other extensionsNormally the directive overrides don't cause any problems and are very convenient,
since no changes to existing documents have to be made. However, if other extensions are included,
which themselves override one of those directives, one of them will inadvertently override the other,
depending on the order they're defined inextensions.To combat this, you can use thecompatibility modeextension instead, which
only includes the new directives.If you control the conflicting overrides, you can alternatively inherit fromauto_py_tabs.sphinx_ext.CodeBlockOverrideandauto_py_tabs.sphinx_ext.LiteralIncludeOverrideinstead ofsphinx.directives.code.CodeBlockandsphinx.directives.code.LiteralIncluderespectively.