From 105b314a0d01a8db6a7ab05d6fd4519a6a82c195 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Sat, 3 Oct 2020 22:34:28 +0200 Subject: [PATCH 01/14] Add collection versions. --- antsibull/cli/doc_commands/plugin.py | 2 +- antsibull/cli/doc_commands/stable.py | 22 +++++---- antsibull/data/docsite/plugin.rst.j2 | 2 +- .../data/docsite/plugins_by_collection.rst.j2 | 3 ++ antsibull/docs_parsing/__init__.py | 18 +++++++ antsibull/docs_parsing/ansible_doc.py | 47 +++++++++++++++---- antsibull/docs_parsing/ansible_internal.py | 17 +++---- antsibull/docs_parsing/parsing.py | 10 ++-- antsibull/write_docs.py | 33 ++++++++++--- 9 files changed, 110 insertions(+), 44 deletions(-) diff --git a/antsibull/cli/doc_commands/plugin.py b/antsibull/cli/doc_commands/plugin.py index 0ec156a4..45333d9d 100644 --- a/antsibull/cli/doc_commands/plugin.py +++ b/antsibull/cli/doc_commands/plugin.py @@ -103,7 +103,7 @@ def generate_docs() -> int: error_tmpl = env.get_template('plugin-error.rst.j2') asyncio_run(write_rst( - '.'.join([namespace, collection]), plugin, plugin_type, + '.'.join([namespace, collection]), None, plugin, plugin_type, plugin_info, errors, plugin_tmpl, error_tmpl, '', path_override=output_path)) flog.debug('Finished writing plugin docs') diff --git a/antsibull/cli/doc_commands/stable.py b/antsibull/cli/doc_commands/stable.py index 4546d21b..ff3ec0be 100644 --- a/antsibull/cli/doc_commands/stable.py +++ b/antsibull/cli/doc_commands/stable.py @@ -268,24 +268,26 @@ def generate_docs_for_all_collections(venv: t.Union[VenvRunner, FakeVenvRunner], """ # Get the info from the plugins - plugin_info = asyncio_run(get_ansible_plugin_info( + collection_docs = asyncio_run(get_ansible_plugin_info( venv, collection_dir, collection_names=collection_names)) - flog.notice('Finished parsing info from plugins') - # flog.fields(plugin_info=plugin_info).debug('Plugin data') + flog.notice('Finished parsing info from plugins and collections') + # flog.fields(plugin_info=collection_docs.plugins).debug('Plugin data') + # flog.fields( + # collection_versions=collection_docs.collection_versions).debug('Collection versions') """ # Turn these into some sort of decorator that will choose to dump or load the values # if a command line arg is specified. with open('dump_raw_plugin_info.json', 'w') as f: import json - json.dump(plugin_info, f) - flog.debug('Finished dumping raw plugin_info') + json.dump(collection_docs.plugins, f) + flog.debug('Finished dumping raw collection_docs.plugins') with open('dump_formatted_plugin_info.json', 'r') as f: import json - plugin_info = json.load(f) + collection_docs.plugins = json.load(f) """ - plugin_info, nonfatal_errors = asyncio_run(normalize_all_plugin_info(plugin_info)) + plugin_info, nonfatal_errors = asyncio_run(normalize_all_plugin_info(collection_docs.plugins)) flog.fields(errors=len(nonfatal_errors)).notice('Finished data validation') augment_docs(plugin_info) flog.notice('Finished calculating new data') @@ -321,12 +323,14 @@ def generate_docs_for_all_collections(venv: t.Union[VenvRunner, FakeVenvRunner], asyncio_run(output_plugin_indexes(plugin_contents, dest_dir)) flog.notice('Finished writing plugin indexes') - asyncio_run(output_indexes(collection_info, dest_dir, squash_hierarchy=squash_hierarchy)) + asyncio_run(output_indexes(collection_info, dest_dir, squash_hierarchy=squash_hierarchy, + collection_versions=collection_docs.collection_versions)) flog.notice('Finished writing indexes') asyncio_run(output_all_plugin_rst(collection_info, plugin_info, nonfatal_errors, dest_dir, - squash_hierarchy=squash_hierarchy)) + squash_hierarchy=squash_hierarchy, + collection_versions=collection_docs.collection_versions)) flog.debug('Finished writing plugin docs') diff --git a/antsibull/data/docsite/plugin.rst.j2 b/antsibull/data/docsite/plugin.rst.j2 index 0df49509..3917b1a3 100644 --- a/antsibull/data/docsite/plugin.rst.j2 +++ b/antsibull/data/docsite/plugin.rst.j2 @@ -52,7 +52,7 @@ the same module name. {% else %} .. note:: - This plugin is part of the `@{collection}@ collection `_. + This plugin is part of the `@{collection}@ collection `_{% if collection_version %} (version @{ collection_version }@){% endif %}. To install it use: :code:`ansible-galaxy collection install @{collection}@`. diff --git a/antsibull/data/docsite/plugins_by_collection.rst.j2 b/antsibull/data/docsite/plugins_by_collection.rst.j2 index 557a78e3..28ade839 100644 --- a/antsibull/data/docsite/plugins_by_collection.rst.j2 +++ b/antsibull/data/docsite/plugins_by_collection.rst.j2 @@ -6,6 +6,9 @@ Plugin Index ============ These are the plugins in the @{collection_name}@ collection +{% if collection_version %} +(version @{ collection_version }@) +{% endif %} .. toctree:: :maxdepth: 1 diff --git a/antsibull/docs_parsing/__init__.py b/antsibull/docs_parsing/__init__.py index 2dca2807..ded509a5 100644 --- a/antsibull/docs_parsing/__init__.py +++ b/antsibull/docs_parsing/__init__.py @@ -62,3 +62,21 @@ def _get_environment(collection_dir: t.Optional[str]) -> t.Dict[str, str]: if env_var in os.environ: env[env_var] = os.environ[env_var] return env + + +class AnsibleCollectionDocs: + # A nested directory structure that looks like: + # plugin_type: + # plugin_name: # Includes namespace and collection. + # {information from ansible-doc --json. See the ansible-doc documentation for more + # info.} + plugins: t.Dict[str, t.Dict[str, t.Any]] + + # Maps collection name to collection version + collection_versions: t.Dict[str, str] + + def __init__(self, + plugins: t.Dict[str, t.Dict[str, t.Any]], + collection_versions: t.Dict[str, str]): + self.plugins = plugins + self.collection_versions = collection_versions diff --git a/antsibull/docs_parsing/ansible_doc.py b/antsibull/docs_parsing/ansible_doc.py index d4cbad3b..cb3b19d2 100644 --- a/antsibull/docs_parsing/ansible_doc.py +++ b/antsibull/docs_parsing/ansible_doc.py @@ -18,7 +18,7 @@ from ..logging import log from ..vendored.json_utils import _filter_non_json_lines from .fqcn import get_fqcn_parts -from . import _get_environment, ParsingError +from . import _get_environment, ParsingError, AnsibleCollectionDocs if t.TYPE_CHECKING: from ..venv import VenvRunner, FakeVenvRunner @@ -158,10 +158,41 @@ async def _get_plugin_info(plugin_type: str, ansible_doc: 'sh.Command', return results +def get_collection_versions(venv: t.Union['VenvRunner', 'FakeVenvRunner'], + collection_dir: t.Optional[str], + collection_names: t.Optional[t.List[str]], + env: t.Dict[str, str], + ) -> t.Dict[str, str]: + collection_versions = {} + + # Obtain ansible.builtin version + if collection_names is None or 'ansible.builtin' in collection_names: + venv_ansible = venv.get_command('ansible') + ansible_version_cmd = venv_ansible('--version', _env=env) + raw_result = ansible_version_cmd.stdout.decode('utf-8', errors='surrogateescape') + for line in raw_result.splitlines(): + if line.startswith('ansible '): + collection_versions['ansible.builtin'] = line[len('ansible '):] + + # Obtain collection versions + venv_ansible_galaxy = venv.get_command('ansible-galaxy') + ansible_collection_list_cmd = venv_ansible_galaxy('collection', 'list', _env=env) + raw_result = ansible_collection_list_cmd.stdout.decode('utf-8', errors='surrogateescape') + for line in raw_result.splitlines(): + parts = line.split() + if len(parts) >= 2: + collection_name = parts[0] + version = parts[1] + if '.' in collection_name: + collection_versions[collection_name] = version + + return collection_versions + + async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], collection_dir: t.Optional[str], collection_names: t.Optional[t.List[str]] = None - ) -> t.Dict[str, t.Dict[str, t.Any]]: + ) -> AnsibleCollectionDocs: """ Retrieve information about all of the Ansible Plugins. @@ -171,12 +202,7 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], search path for Ansible. :arg collection_names: Optional list of collections. If specified, will only collect information for plugins in these collections. - :returns: A nested directory structure that looks like:: - - plugin_type: - plugin_name: # Includes namespace and collection. - {information from ansible-doc --json. See the ansible-doc documentation for more - info.} + :returns: An AnsibleCollectionDocs object. """ flog = mlog.fields(func='get_ansible_plugin_info') flog.debug('Enter') @@ -246,5 +272,8 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], # done so, we want to then fail by raising one of the exceptions. raise ParsingError('Parsing of plugins failed') + flog.debug('Retrieving collection versions') + collection_versions = get_collection_versions(venv, collection_dir, collection_names, env) + flog.debug('Leave') - return plugin_map + return AnsibleCollectionDocs(plugin_map, collection_versions) diff --git a/antsibull/docs_parsing/ansible_internal.py b/antsibull/docs_parsing/ansible_internal.py index 985ad7d1..6bc7a4a9 100644 --- a/antsibull/docs_parsing/ansible_internal.py +++ b/antsibull/docs_parsing/ansible_internal.py @@ -11,7 +11,7 @@ from ..logging import log from ..utils.get_pkg_data import get_antsibull_data from ..vendored.json_utils import _filter_non_json_lines -from . import _get_environment +from . import _get_environment, AnsibleCollectionDocs if t.TYPE_CHECKING: from ..venv import VenvRunner, FakeVenvRunner @@ -23,7 +23,7 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], collection_dir: t.Optional[str], collection_names: t.Optional[t.List[str]] = None - ) -> t.Dict[str, t.Dict[str, t.Any]]: + ) -> AnsibleCollectionDocs: """ Retrieve information about all of the Ansible Plugins. @@ -33,12 +33,7 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], search path for Ansible. :arg collection_names: Optional list of collections. If specified, will only collect information for plugins in these collections. - :returns: A nested directory structure that looks like:: - - plugin_type: - plugin_name: # Includes namespace and collection. - {information from ansible-doc --json. See the ansible-doc documentation for more - info.} + :returns: An AnsibleCollectionDocs object. """ flog = mlog.fields(func='get_ansible_plugin_info') flog.debug('Enter') @@ -72,7 +67,9 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], plugin_log.fields(error=plugin_data['error']).error( 'Error while extracting documentation. Will not document this plugin.') - # TODO: use result['collections'] + collection_versions = {} + for collection_name, collection_data in result['collections'].items(): + collection_versions[collection_name] = collection_data.get('version') flog.debug('Leave') - return plugin_map + return AnsibleCollectionDocs(plugin_map, collection_versions) diff --git a/antsibull/docs_parsing/parsing.py b/antsibull/docs_parsing/parsing.py index cf066f5b..daef5de1 100644 --- a/antsibull/docs_parsing/parsing.py +++ b/antsibull/docs_parsing/parsing.py @@ -9,6 +9,7 @@ from ..logging import log from .ansible_doc import get_ansible_plugin_info as ansible_doc_get_ansible_plugin_info from .ansible_internal import get_ansible_plugin_info as ansible_internal_get_ansible_plugin_info +from . import AnsibleCollectionDocs if t.TYPE_CHECKING: from ..venv import VenvRunner, FakeVenvRunner @@ -20,7 +21,7 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], collection_dir: t.Optional[str], collection_names: t.Optional[t.List[str]] = None - ) -> t.Dict[str, t.Dict[str, t.Any]]: + ) -> AnsibleCollectionDocs: """ Retrieve information about all of the Ansible Plugins. @@ -30,12 +31,7 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], search path for Ansible. :arg collection_names: Optional list of collections. If specified, will only collect information for plugins in these collections. - :returns: A nested directory structure that looks like:: - - plugin_type: - plugin_name: # Includes namespace and collection. - {information from ansible-doc --json. See the ansible-doc documentation for more - info.} + :returns: An AnsibleCollectionDocs object. """ lib_ctx = app_context.lib_ctx.get() diff --git a/antsibull/write_docs.py b/antsibull/write_docs.py index 2e4724d9..453189fc 100644 --- a/antsibull/write_docs.py +++ b/antsibull/write_docs.py @@ -33,7 +33,8 @@ PluginCollectionInfoT = t.Mapping[str, t.Mapping[str, t.Mapping[str, str]]] -async def write_rst(collection_name: str, plugin_short_name: str, plugin_type: str, +async def write_rst(collection_name: str, collection_version: t.Optional[str], + plugin_short_name: str, plugin_type: str, plugin_record: t.Dict[str, t.Any], nonfatal_errors: t.Sequence[str], plugin_tmpl: Template, error_tmpl: Template, dest_dir: str, path_override: t.Optional[str] = None, @@ -42,6 +43,7 @@ async def write_rst(collection_name: str, plugin_short_name: str, plugin_type: s Write the rst page for one plugin. :arg collection_name: Dotted colection name. + :arg collection_version: Collection version (optional). :arg plugin_short_name: short name for the plugin. :arg plugin_type: The type of the plugin. (module, inventory, etc) :arg plugin_record: The record for the plugin. doc, examples, and return are the @@ -72,6 +74,7 @@ async def write_rst(collection_name: str, plugin_short_name: str, plugin_type: s plugin_contents = error_tmpl.render( plugin_type=plugin_type, plugin_name=plugin_name, collection=collection_name, + collection_version=collection_version, nonfatal_errors=nonfatal_errors) else: if nonfatal_errors: @@ -82,6 +85,7 @@ async def write_rst(collection_name: str, plugin_short_name: str, plugin_type: s plugin_name=plugin_name) plugin_contents = plugin_tmpl.render( collection=collection_name, + collection_version=collection_version, plugin_type=plugin_type, plugin_name=plugin_name, doc=plugin_record['doc'], @@ -112,7 +116,8 @@ async def output_all_plugin_rst(collection_info: CollectionInfoT, plugin_info: t.Dict[str, t.Any], nonfatal_errors: PluginErrorsT, dest_dir: str, - squash_hierarchy: bool = False) -> None: + squash_hierarchy: bool = False, + collection_versions: t.Optional[t.Dict[str, str]] = None) -> None: """ Output rst files for each plugin. @@ -125,6 +130,7 @@ async def output_all_plugin_rst(collection_info: CollectionInfoT, :arg squash_hierarchy: If set to ``True``, no directory hierarchy will be used. Undefined behavior if documentation for multiple collections are created. + :arg collection_versions: Optional dictionary mapping collection names to collection versions """ # Setup the jinja environment env = doc_environment(('antsibull.data', 'docsite')) @@ -132,6 +138,9 @@ async def output_all_plugin_rst(collection_info: CollectionInfoT, plugin_tmpl = env.get_template('plugin.rst.j2') error_tmpl = env.get_template('plugin-error.rst.j2') + if collection_versions is None: + collection_versions = {} + writers = [] lib_ctx = app_context.lib_ctx.get() async with asyncio_pool.AioPool(size=lib_ctx.thread_max) as pool: @@ -140,7 +149,9 @@ async def output_all_plugin_rst(collection_info: CollectionInfoT, for plugin_short_name, dummy_ in plugins.items(): plugin_name = '.'.join((collection_name, plugin_short_name)) writers.append(await pool.spawn( - write_rst(collection_name, plugin_short_name, plugin_type, + write_rst(collection_name, + collection_versions.get(collection_name), + plugin_short_name, plugin_type, plugin_info[plugin_type].get(plugin_name), nonfatal_errors[plugin_type][plugin_name], plugin_tmpl, error_tmpl, dest_dir, squash_hierarchy=squash_hierarchy))) @@ -191,7 +202,8 @@ async def write_plugin_type_index(plugin_type: str, async def write_plugin_lists(collection_name: str, plugin_maps: t.Mapping[str, t.Mapping[str, str]], template: Template, - dest_dir: str) -> None: + dest_dir: str, + collection_version: t.Optional[str]) -> None: """ Write an index page for each collection. @@ -200,10 +212,12 @@ async def write_plugin_lists(collection_name: str, :arg plugin_maps: Mapping of plugin_type to Mapping of plugin_name to short_description. :arg template: A template to render the collection index. :arg dest_dir: The destination directory to output the index into. + :arg collection_version: The collection's version """ index_contents = template.render( collection_name=collection_name, - plugin_maps=plugin_maps) + plugin_maps=plugin_maps, + collection_version=collection_version) # This is only safe because we made sure that the top of the directory tree we're writing to # (docs/docsite/rst) is only writable by us. @@ -283,7 +297,8 @@ async def output_plugin_indexes(plugin_info: PluginCollectionInfoT, async def output_indexes(collection_info: CollectionInfoT, dest_dir: str, - squash_hierarchy: bool = False) -> None: + squash_hierarchy: bool = False, + collection_versions: t.Optional[t.Dict[str, str]] = None) -> None: """ Generate collection-level index pages for the collections. @@ -293,10 +308,14 @@ async def output_indexes(collection_info: CollectionInfoT, :arg squash_hierarchy: If set to ``True``, no directory hierarchy will be used. Undefined behavior if documentation for multiple collections are created. + :arg collection_versions: Optional dictionary mapping collection names to collection versions """ flog = mlog.fields(func='output_indexes') flog.debug('Enter') + if collection_versions is None: + collection_versions = {} + env = doc_environment(('antsibull.data', 'docsite')) # Get the templates collection_plugins_tmpl = env.get_template('plugins_by_collection.rst.j2') @@ -322,7 +341,7 @@ async def output_indexes(collection_info: CollectionInfoT, collection_dir = collection_toplevel writers.append(await pool.spawn( write_plugin_lists(collection_name, plugin_maps, collection_plugins_tmpl, - collection_dir))) + collection_dir, collection_versions.get(collection_name)))) await asyncio.gather(*writers) From 0307b2889f118ac28f75ad75b81d2b8bee9cb24a Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Tue, 3 Nov 2020 17:39:00 +0100 Subject: [PATCH 02/14] Linting. --- antsibull/docs_parsing/__init__.py | 8 ++++---- antsibull/write_docs.py | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/antsibull/docs_parsing/__init__.py b/antsibull/docs_parsing/__init__.py index ded509a5..adb49d75 100644 --- a/antsibull/docs_parsing/__init__.py +++ b/antsibull/docs_parsing/__init__.py @@ -70,13 +70,13 @@ class AnsibleCollectionDocs: # plugin_name: # Includes namespace and collection. # {information from ansible-doc --json. See the ansible-doc documentation for more # info.} - plugins: t.Dict[str, t.Dict[str, t.Any]] + plugins: t.Mapping[str, t.Mapping[str, t.Any]] # Maps collection name to collection version - collection_versions: t.Dict[str, str] + collection_versions: t.Mapping[str, str] def __init__(self, - plugins: t.Dict[str, t.Dict[str, t.Any]], - collection_versions: t.Dict[str, str]): + plugins: t.Mapping[str, t.Mapping[str, t.Any]], + collection_versions: t.Mapping[str, str]): self.plugins = plugins self.collection_versions = collection_versions diff --git a/antsibull/write_docs.py b/antsibull/write_docs.py index 453189fc..4c755428 100644 --- a/antsibull/write_docs.py +++ b/antsibull/write_docs.py @@ -117,7 +117,8 @@ async def output_all_plugin_rst(collection_info: CollectionInfoT, nonfatal_errors: PluginErrorsT, dest_dir: str, squash_hierarchy: bool = False, - collection_versions: t.Optional[t.Dict[str, str]] = None) -> None: + collection_versions: t.Optional[ + t.Mapping[str, str]] = None) -> None: """ Output rst files for each plugin. @@ -298,7 +299,7 @@ async def output_plugin_indexes(plugin_info: PluginCollectionInfoT, async def output_indexes(collection_info: CollectionInfoT, dest_dir: str, squash_hierarchy: bool = False, - collection_versions: t.Optional[t.Dict[str, str]] = None) -> None: + collection_versions: t.Optional[t.Mapping[str, str]] = None) -> None: """ Generate collection-level index pages for the collections. From 28d41309d9e97458aaae8e58d39aa7716442b35e Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Thu, 19 Nov 2020 22:23:43 +0100 Subject: [PATCH 03/14] Also retrieve collection paths. --- antsibull/cli/doc_commands/stable.py | 6 +-- antsibull/docs_parsing/__init__.py | 20 ++++++++-- antsibull/docs_parsing/ansible_doc.py | 45 ++++++++++++++-------- antsibull/docs_parsing/ansible_internal.py | 10 +++-- antsibull/write_docs.py | 38 +++++++++--------- 5 files changed, 74 insertions(+), 45 deletions(-) diff --git a/antsibull/cli/doc_commands/stable.py b/antsibull/cli/doc_commands/stable.py index ff3ec0be..79665976 100644 --- a/antsibull/cli/doc_commands/stable.py +++ b/antsibull/cli/doc_commands/stable.py @@ -273,7 +273,7 @@ def generate_docs_for_all_collections(venv: t.Union[VenvRunner, FakeVenvRunner], flog.notice('Finished parsing info from plugins and collections') # flog.fields(plugin_info=collection_docs.plugins).debug('Plugin data') # flog.fields( - # collection_versions=collection_docs.collection_versions).debug('Collection versions') + # collection_infos=collection_docs.collection_infos).debug('Collection infos') """ # Turn these into some sort of decorator that will choose to dump or load the values @@ -324,13 +324,13 @@ def generate_docs_for_all_collections(venv: t.Union[VenvRunner, FakeVenvRunner], flog.notice('Finished writing plugin indexes') asyncio_run(output_indexes(collection_info, dest_dir, squash_hierarchy=squash_hierarchy, - collection_versions=collection_docs.collection_versions)) + collection_infos=collection_docs.collection_infos)) flog.notice('Finished writing indexes') asyncio_run(output_all_plugin_rst(collection_info, plugin_info, nonfatal_errors, dest_dir, squash_hierarchy=squash_hierarchy, - collection_versions=collection_docs.collection_versions)) + collection_infos=collection_docs.collection_infos)) flog.debug('Finished writing plugin docs') diff --git a/antsibull/docs_parsing/__init__.py b/antsibull/docs_parsing/__init__.py index adb49d75..0109b924 100644 --- a/antsibull/docs_parsing/__init__.py +++ b/antsibull/docs_parsing/__init__.py @@ -64,6 +64,18 @@ def _get_environment(collection_dir: t.Optional[str]) -> t.Dict[str, str]: return env +class AnsibleCollectionInfo: + path: str + version: t.Optional[str] + + def __init__(self, path: str, version: t.Optional[str]): + self.path = path + self.version = version + + def __repr__(self): + return 'AnsibleCollectionInfo({0}, {1})'.format(repr(self.path), repr(self.version)) + + class AnsibleCollectionDocs: # A nested directory structure that looks like: # plugin_type: @@ -73,10 +85,10 @@ class AnsibleCollectionDocs: plugins: t.Mapping[str, t.Mapping[str, t.Any]] # Maps collection name to collection version - collection_versions: t.Mapping[str, str] + collection_infos: t.Mapping[str, AnsibleCollectionInfo] def __init__(self, - plugins: t.Mapping[str, t.Mapping[str, t.Any]], - collection_versions: t.Mapping[str, str]): + plugins: t.Mapping[str, t.Dict[str, t.Any]], + collection_infos: t.Mapping[str, AnsibleCollectionInfo]): self.plugins = plugins - self.collection_versions = collection_versions + self.collection_infos = collection_infos diff --git a/antsibull/docs_parsing/ansible_doc.py b/antsibull/docs_parsing/ansible_doc.py index cb3b19d2..dcb56454 100644 --- a/antsibull/docs_parsing/ansible_doc.py +++ b/antsibull/docs_parsing/ansible_doc.py @@ -6,6 +6,7 @@ import asyncio import json import sys +import os import traceback import typing as t from concurrent.futures import ThreadPoolExecutor @@ -18,7 +19,7 @@ from ..logging import log from ..vendored.json_utils import _filter_non_json_lines from .fqcn import get_fqcn_parts -from . import _get_environment, ParsingError, AnsibleCollectionDocs +from . import _get_environment, ParsingError, AnsibleCollectionDocs, AnsibleCollectionInfo if t.TYPE_CHECKING: from ..venv import VenvRunner, FakeVenvRunner @@ -158,35 +159,47 @@ async def _get_plugin_info(plugin_type: str, ansible_doc: 'sh.Command', return results -def get_collection_versions(venv: t.Union['VenvRunner', 'FakeVenvRunner'], - collection_dir: t.Optional[str], - collection_names: t.Optional[t.List[str]], - env: t.Dict[str, str], - ) -> t.Dict[str, str]: - collection_versions = {} +def get_collection_infos(venv: t.Union['VenvRunner', 'FakeVenvRunner'], + collection_dir: t.Optional[str], + collection_names: t.Optional[t.List[str]], + env: t.Dict[str, str], + ) -> t.Dict[str, AnsibleCollectionInfo]: + collection_infos = {} # Obtain ansible.builtin version if collection_names is None or 'ansible.builtin' in collection_names: venv_ansible = venv.get_command('ansible') ansible_version_cmd = venv_ansible('--version', _env=env) raw_result = ansible_version_cmd.stdout.decode('utf-8', errors='surrogateescape') + path: t.Optional[str] = None + version: t.Optional[str] = None for line in raw_result.splitlines(): + if line.strip().startswith('ansible python module location'): + path = line.split('=', 2)[1].strip() if line.startswith('ansible '): - collection_versions['ansible.builtin'] = line[len('ansible '):] + version = line[len('ansible '):] + collection_infos['ansible.builtin'] = AnsibleCollectionInfo(path=path, version=version) # Obtain collection versions venv_ansible_galaxy = venv.get_command('ansible-galaxy') ansible_collection_list_cmd = venv_ansible_galaxy('collection', 'list', _env=env) raw_result = ansible_collection_list_cmd.stdout.decode('utf-8', errors='surrogateescape') + current_base_path = None for line in raw_result.splitlines(): parts = line.split() if len(parts) >= 2: - collection_name = parts[0] - version = parts[1] - if '.' in collection_name: - collection_versions[collection_name] = version + if parts[0] == '#': + current_base_path = parts[1] + else: + collection_name = parts[0] + version = parts[1] + if '.' in collection_name: + namespace, name = collection_name.split('.', 2) + collection_infos[collection_name] = AnsibleCollectionInfo( + path=os.path.join(current_base_path, namespace, name), + version=None if version == '*' else version) - return collection_versions + return collection_infos async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], @@ -272,8 +285,8 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], # done so, we want to then fail by raising one of the exceptions. raise ParsingError('Parsing of plugins failed') - flog.debug('Retrieving collection versions') - collection_versions = get_collection_versions(venv, collection_dir, collection_names, env) + flog.debug('Retrieving collection infos') + collection_infos = get_collection_infos(venv, collection_dir, collection_names, env) flog.debug('Leave') - return AnsibleCollectionDocs(plugin_map, collection_versions) + return AnsibleCollectionDocs(plugin_map, collection_infos) diff --git a/antsibull/docs_parsing/ansible_internal.py b/antsibull/docs_parsing/ansible_internal.py index 6bc7a4a9..068c0d9f 100644 --- a/antsibull/docs_parsing/ansible_internal.py +++ b/antsibull/docs_parsing/ansible_internal.py @@ -11,7 +11,7 @@ from ..logging import log from ..utils.get_pkg_data import get_antsibull_data from ..vendored.json_utils import _filter_non_json_lines -from . import _get_environment, AnsibleCollectionDocs +from . import _get_environment, AnsibleCollectionDocs, AnsibleCollectionInfo if t.TYPE_CHECKING: from ..venv import VenvRunner, FakeVenvRunner @@ -67,9 +67,11 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], plugin_log.fields(error=plugin_data['error']).error( 'Error while extracting documentation. Will not document this plugin.') - collection_versions = {} + collection_infos = {} for collection_name, collection_data in result['collections'].items(): - collection_versions[collection_name] = collection_data.get('version') + collection_infos[collection_name] = AnsibleCollectionInfo( + path=collection_data['path'], + version=collection_data.get('version')) flog.debug('Leave') - return AnsibleCollectionDocs(plugin_map, collection_versions) + return AnsibleCollectionDocs(plugin_map, collection_infos) diff --git a/antsibull/write_docs.py b/antsibull/write_docs.py index 4c755428..7d43c8e6 100644 --- a/antsibull/write_docs.py +++ b/antsibull/write_docs.py @@ -16,6 +16,7 @@ from . import app_context from .jinja2.environment import doc_environment from .logging import log +from .docs_parsing import AnsibleCollectionInfo mlog = log.fields(mod=__name__) @@ -33,7 +34,7 @@ PluginCollectionInfoT = t.Mapping[str, t.Mapping[str, t.Mapping[str, str]]] -async def write_rst(collection_name: str, collection_version: t.Optional[str], +async def write_rst(collection_name: str, collection_info: t.Optional[AnsibleCollectionInfo], plugin_short_name: str, plugin_type: str, plugin_record: t.Dict[str, t.Any], nonfatal_errors: t.Sequence[str], plugin_tmpl: Template, error_tmpl: Template, dest_dir: str, @@ -43,7 +44,7 @@ async def write_rst(collection_name: str, collection_version: t.Optional[str], Write the rst page for one plugin. :arg collection_name: Dotted colection name. - :arg collection_version: Collection version (optional). + :arg collection_info: Collection information object (optional). :arg plugin_short_name: short name for the plugin. :arg plugin_type: The type of the plugin. (module, inventory, etc) :arg plugin_record: The record for the plugin. doc, examples, and return are the @@ -74,7 +75,7 @@ async def write_rst(collection_name: str, collection_version: t.Optional[str], plugin_contents = error_tmpl.render( plugin_type=plugin_type, plugin_name=plugin_name, collection=collection_name, - collection_version=collection_version, + collection_version=collection_info.version if collection_info else None, nonfatal_errors=nonfatal_errors) else: if nonfatal_errors: @@ -85,7 +86,7 @@ async def write_rst(collection_name: str, collection_version: t.Optional[str], plugin_name=plugin_name) plugin_contents = plugin_tmpl.render( collection=collection_name, - collection_version=collection_version, + collection_version=collection_info.version if collection_info else None, plugin_type=plugin_type, plugin_name=plugin_name, doc=plugin_record['doc'], @@ -117,8 +118,8 @@ async def output_all_plugin_rst(collection_info: CollectionInfoT, nonfatal_errors: PluginErrorsT, dest_dir: str, squash_hierarchy: bool = False, - collection_versions: t.Optional[ - t.Mapping[str, str]] = None) -> None: + collection_infos: t.Optional[ + t.Mapping[str, AnsibleCollectionInfo]] = None) -> None: """ Output rst files for each plugin. @@ -131,7 +132,7 @@ async def output_all_plugin_rst(collection_info: CollectionInfoT, :arg squash_hierarchy: If set to ``True``, no directory hierarchy will be used. Undefined behavior if documentation for multiple collections are created. - :arg collection_versions: Optional dictionary mapping collection names to collection versions + :arg collection_infos: Optional dictionary mapping collection names to collection info objects. """ # Setup the jinja environment env = doc_environment(('antsibull.data', 'docsite')) @@ -139,8 +140,8 @@ async def output_all_plugin_rst(collection_info: CollectionInfoT, plugin_tmpl = env.get_template('plugin.rst.j2') error_tmpl = env.get_template('plugin-error.rst.j2') - if collection_versions is None: - collection_versions = {} + if collection_infos is None: + collection_infos = {} writers = [] lib_ctx = app_context.lib_ctx.get() @@ -151,7 +152,7 @@ async def output_all_plugin_rst(collection_info: CollectionInfoT, plugin_name = '.'.join((collection_name, plugin_short_name)) writers.append(await pool.spawn( write_rst(collection_name, - collection_versions.get(collection_name), + collection_infos.get(collection_name), plugin_short_name, plugin_type, plugin_info[plugin_type].get(plugin_name), nonfatal_errors[plugin_type][plugin_name], plugin_tmpl, @@ -204,7 +205,7 @@ async def write_plugin_lists(collection_name: str, plugin_maps: t.Mapping[str, t.Mapping[str, str]], template: Template, dest_dir: str, - collection_version: t.Optional[str]) -> None: + collection_info: t.Optional[AnsibleCollectionInfo]) -> None: """ Write an index page for each collection. @@ -213,12 +214,12 @@ async def write_plugin_lists(collection_name: str, :arg plugin_maps: Mapping of plugin_type to Mapping of plugin_name to short_description. :arg template: A template to render the collection index. :arg dest_dir: The destination directory to output the index into. - :arg collection_version: The collection's version + :arg collection_info: Information on the collection. """ index_contents = template.render( collection_name=collection_name, plugin_maps=plugin_maps, - collection_version=collection_version) + collection_version=collection_info.version if collection_info else None) # This is only safe because we made sure that the top of the directory tree we're writing to # (docs/docsite/rst) is only writable by us. @@ -299,7 +300,8 @@ async def output_plugin_indexes(plugin_info: PluginCollectionInfoT, async def output_indexes(collection_info: CollectionInfoT, dest_dir: str, squash_hierarchy: bool = False, - collection_versions: t.Optional[t.Mapping[str, str]] = None) -> None: + collection_infos: t.Optional[t.Mapping[str, AnsibleCollectionInfo]] = None + ) -> None: """ Generate collection-level index pages for the collections. @@ -309,13 +311,13 @@ async def output_indexes(collection_info: CollectionInfoT, :arg squash_hierarchy: If set to ``True``, no directory hierarchy will be used. Undefined behavior if documentation for multiple collections are created. - :arg collection_versions: Optional dictionary mapping collection names to collection versions + :arg collection_infos: Optional dictionary mapping collection names to collection info objects """ flog = mlog.fields(func='output_indexes') flog.debug('Enter') - if collection_versions is None: - collection_versions = {} + if collection_infos is None: + collection_infos = {} env = doc_environment(('antsibull.data', 'docsite')) # Get the templates @@ -342,7 +344,7 @@ async def output_indexes(collection_info: CollectionInfoT, collection_dir = collection_toplevel writers.append(await pool.spawn( write_plugin_lists(collection_name, plugin_maps, collection_plugins_tmpl, - collection_dir, collection_versions.get(collection_name)))) + collection_dir, collection_infos.get(collection_name)))) await asyncio.gather(*writers) From b60e8409186d369c4b275c4631dc70a94fbbf41e Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Tue, 24 Nov 2020 21:27:24 +0100 Subject: [PATCH 04/14] Address review comments. --- antsibull/docs_parsing/ansible_doc.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/antsibull/docs_parsing/ansible_doc.py b/antsibull/docs_parsing/ansible_doc.py index dcb56454..f3461f77 100644 --- a/antsibull/docs_parsing/ansible_doc.py +++ b/antsibull/docs_parsing/ansible_doc.py @@ -160,9 +160,8 @@ async def _get_plugin_info(plugin_type: str, ansible_doc: 'sh.Command', def get_collection_infos(venv: t.Union['VenvRunner', 'FakeVenvRunner'], - collection_dir: t.Optional[str], - collection_names: t.Optional[t.List[str]], env: t.Dict[str, str], + collection_names: t.Optional[t.List[str]] = None, ) -> t.Dict[str, AnsibleCollectionInfo]: collection_infos = {} @@ -194,10 +193,11 @@ def get_collection_infos(venv: t.Union['VenvRunner', 'FakeVenvRunner'], collection_name = parts[0] version = parts[1] if '.' in collection_name: - namespace, name = collection_name.split('.', 2) - collection_infos[collection_name] = AnsibleCollectionInfo( - path=os.path.join(current_base_path, namespace, name), - version=None if version == '*' else version) + if collection_names is None or collection_name in collection_names: + namespace, name = collection_name.split('.', 2) + collection_infos[collection_name] = AnsibleCollectionInfo( + path=os.path.join(current_base_path, namespace, name), + version=None if version == '*' else version) return collection_infos @@ -286,7 +286,7 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], raise ParsingError('Parsing of plugins failed') flog.debug('Retrieving collection infos') - collection_infos = get_collection_infos(venv, collection_dir, collection_names, env) + collection_infos = get_collection_infos(venv, env, collection_names) flog.debug('Leave') return AnsibleCollectionDocs(plugin_map, collection_infos) From ef068c68adf6c07bf6860db726dcc10eca8509d1 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Tue, 24 Nov 2020 21:35:08 +0100 Subject: [PATCH 05/14] collection_info -> collection_to_plugin_info --- antsibull/cli/doc_commands/stable.py | 12 +++++++----- antsibull/write_docs.py | 24 ++++++++++++------------ 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/antsibull/cli/doc_commands/stable.py b/antsibull/cli/doc_commands/stable.py index 79665976..fc9cb943 100644 --- a/antsibull/cli/doc_commands/stable.py +++ b/antsibull/cli/doc_commands/stable.py @@ -28,6 +28,7 @@ from ...schemas.docs import DOCS_SCHEMAS from ...venv import VenvRunner, FakeVenvRunner from ...write_docs import ( + CollectionInfoT, output_all_plugin_rst, output_collection_index, output_indexes, @@ -221,7 +222,7 @@ def get_plugin_contents(plugin_info: t.Mapping[str, t.Mapping[str, t.Any]], def get_collection_contents(plugin_content: t.Mapping[str, t.Mapping[str, t.Mapping[str, str]]], - ) -> t.DefaultDict[str, t.Dict[str, t.Mapping[str, str]]]: + ) -> CollectionInfoT: """ Return the plugins which are in each collection. @@ -313,21 +314,22 @@ def generate_docs_for_all_collections(venv: t.Union[VenvRunner, FakeVenvRunner], """ plugin_contents = get_plugin_contents(plugin_info, nonfatal_errors) - collection_info = get_collection_contents(plugin_contents) + collection_to_plugin_info = get_collection_contents(plugin_contents) flog.debug('Finished getting collection data') # Only build top-level index if requested if create_indexes: - asyncio_run(output_collection_index(collection_info, dest_dir)) + asyncio_run(output_collection_index(collection_to_plugin_info, dest_dir)) flog.notice('Finished writing collection index') asyncio_run(output_plugin_indexes(plugin_contents, dest_dir)) flog.notice('Finished writing plugin indexes') - asyncio_run(output_indexes(collection_info, dest_dir, squash_hierarchy=squash_hierarchy, + asyncio_run(output_indexes(collection_to_plugin_info, dest_dir, + squash_hierarchy=squash_hierarchy, collection_infos=collection_docs.collection_infos)) flog.notice('Finished writing indexes') - asyncio_run(output_all_plugin_rst(collection_info, plugin_info, + asyncio_run(output_all_plugin_rst(collection_to_plugin_info, plugin_info, nonfatal_errors, dest_dir, squash_hierarchy=squash_hierarchy, collection_infos=collection_docs.collection_infos)) diff --git a/antsibull/write_docs.py b/antsibull/write_docs.py index 7d43c8e6..1a28b5b9 100644 --- a/antsibull/write_docs.py +++ b/antsibull/write_docs.py @@ -113,7 +113,7 @@ async def write_rst(collection_name: str, collection_info: t.Optional[AnsibleCol flog.debug('Leave') -async def output_all_plugin_rst(collection_info: CollectionInfoT, +async def output_all_plugin_rst(collection_to_plugin_info: CollectionInfoT, plugin_info: t.Dict[str, t.Any], nonfatal_errors: PluginErrorsT, dest_dir: str, @@ -123,8 +123,8 @@ async def output_all_plugin_rst(collection_info: CollectionInfoT, """ Output rst files for each plugin. - :arg collection_info: Mapping of collection_name to Mapping of plugin_type to Mapping of - collection_name to short_description. + :arg collection_to_plugin_info: Mapping of collection_name to Mapping of plugin_type to Mapping + of collection_name to short_description. :arg plugin_info: Documentation information for all of the plugins. :arg nonfatal_errors: Mapping of plugins to nonfatal errors. Using this to note on the docs pages when documentation wasn't formatted such that we could use it. @@ -146,7 +146,7 @@ async def output_all_plugin_rst(collection_info: CollectionInfoT, writers = [] lib_ctx = app_context.lib_ctx.get() async with asyncio_pool.AioPool(size=lib_ctx.thread_max) as pool: - for collection_name, plugins_by_type in collection_info.items(): + for collection_name, plugins_by_type in collection_to_plugin_info.items(): for plugin_type, plugins in plugins_by_type.items(): for plugin_short_name, dummy_ in plugins.items(): plugin_name = '.'.join((collection_name, plugin_short_name)) @@ -230,13 +230,13 @@ async def write_plugin_lists(collection_name: str, await f.write(index_contents) -async def output_collection_index(collection_info: CollectionInfoT, +async def output_collection_index(collection_to_plugin_info: CollectionInfoT, dest_dir: str) -> None: """ Generate top-level collection index page for the collections. - :arg collection_info: Mapping of collection_name to Mapping of plugin_type to Mapping of - collection_name to short_description. + :arg collection_to_plugin_info: Mapping of collection_name to Mapping of plugin_type to Mapping + of collection_name to short_description. :arg dest_dir: The directory to place the documentation in. """ flog = mlog.fields(func='output_collection_index') @@ -253,7 +253,7 @@ async def output_collection_index(collection_info: CollectionInfoT, # (docs/docsite/rst) is only writable by us. os.makedirs(collection_toplevel, mode=0o755, exist_ok=True) - await write_collection_list(collection_info.keys(), collection_list_tmpl, + await write_collection_list(collection_to_plugin_info.keys(), collection_list_tmpl, collection_toplevel) flog.debug('Leave') @@ -297,7 +297,7 @@ async def output_plugin_indexes(plugin_info: PluginCollectionInfoT, flog.debug('Leave') -async def output_indexes(collection_info: CollectionInfoT, +async def output_indexes(collection_to_plugin_info: CollectionInfoT, dest_dir: str, squash_hierarchy: bool = False, collection_infos: t.Optional[t.Mapping[str, AnsibleCollectionInfo]] = None @@ -305,8 +305,8 @@ async def output_indexes(collection_info: CollectionInfoT, """ Generate collection-level index pages for the collections. - :arg collection_info: Mapping of collection_name to Mapping of plugin_type to Mapping of - collection_name to short_description. + :arg collection_to_plugin_info: Mapping of collection_name to Mapping of plugin_type to Mapping + of collection_name to short_description. :arg dest_dir: The directory to place the documentation in. :arg squash_hierarchy: If set to ``True``, no directory hierarchy will be used. Undefined behavior if documentation for multiple collections are @@ -337,7 +337,7 @@ async def output_indexes(collection_info: CollectionInfoT, collection_toplevel = dest_dir async with asyncio_pool.AioPool(size=lib_ctx.thread_max) as pool: - for collection_name, plugin_maps in collection_info.items(): + for collection_name, plugin_maps in collection_to_plugin_info.items(): if not squash_hierarchy: collection_dir = os.path.join(collection_toplevel, *(collection_name.split('.'))) else: From e041575bbf7b6a6163ede20f8e474075aed73a4c Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Tue, 24 Nov 2020 21:38:23 +0100 Subject: [PATCH 06/14] collection_infos -> collection_metadata --- antsibull/cli/doc_commands/stable.py | 6 ++--- antsibull/docs_parsing/__init__.py | 12 ++++----- antsibull/docs_parsing/ansible_doc.py | 25 ++++++++--------- antsibull/docs_parsing/ansible_internal.py | 8 +++--- antsibull/write_docs.py | 31 ++++++++++++---------- 5 files changed, 43 insertions(+), 39 deletions(-) diff --git a/antsibull/cli/doc_commands/stable.py b/antsibull/cli/doc_commands/stable.py index fc9cb943..d54af552 100644 --- a/antsibull/cli/doc_commands/stable.py +++ b/antsibull/cli/doc_commands/stable.py @@ -274,7 +274,7 @@ def generate_docs_for_all_collections(venv: t.Union[VenvRunner, FakeVenvRunner], flog.notice('Finished parsing info from plugins and collections') # flog.fields(plugin_info=collection_docs.plugins).debug('Plugin data') # flog.fields( - # collection_infos=collection_docs.collection_infos).debug('Collection infos') + # collection_metadata=collection_docs.collection_metadata).debug('Collection infos') """ # Turn these into some sort of decorator that will choose to dump or load the values @@ -326,13 +326,13 @@ def generate_docs_for_all_collections(venv: t.Union[VenvRunner, FakeVenvRunner], asyncio_run(output_indexes(collection_to_plugin_info, dest_dir, squash_hierarchy=squash_hierarchy, - collection_infos=collection_docs.collection_infos)) + collection_metadata=collection_docs.collection_metadata)) flog.notice('Finished writing indexes') asyncio_run(output_all_plugin_rst(collection_to_plugin_info, plugin_info, nonfatal_errors, dest_dir, squash_hierarchy=squash_hierarchy, - collection_infos=collection_docs.collection_infos)) + collection_metadata=collection_docs.collection_metadata)) flog.debug('Finished writing plugin docs') diff --git a/antsibull/docs_parsing/__init__.py b/antsibull/docs_parsing/__init__.py index 0109b924..0a9b6a8e 100644 --- a/antsibull/docs_parsing/__init__.py +++ b/antsibull/docs_parsing/__init__.py @@ -64,7 +64,7 @@ def _get_environment(collection_dir: t.Optional[str]) -> t.Dict[str, str]: return env -class AnsibleCollectionInfo: +class AnsibleCollectionMetadata: path: str version: t.Optional[str] @@ -73,7 +73,7 @@ def __init__(self, path: str, version: t.Optional[str]): self.version = version def __repr__(self): - return 'AnsibleCollectionInfo({0}, {1})'.format(repr(self.path), repr(self.version)) + return 'AnsibleCollectionMetadata({0}, {1})'.format(repr(self.path), repr(self.version)) class AnsibleCollectionDocs: @@ -84,11 +84,11 @@ class AnsibleCollectionDocs: # info.} plugins: t.Mapping[str, t.Mapping[str, t.Any]] - # Maps collection name to collection version - collection_infos: t.Mapping[str, AnsibleCollectionInfo] + # Maps collection name to metadata + collection_metadata: t.Mapping[str, AnsibleCollectionMetadata] def __init__(self, plugins: t.Mapping[str, t.Dict[str, t.Any]], - collection_infos: t.Mapping[str, AnsibleCollectionInfo]): + collection_metadata: t.Mapping[str, AnsibleCollectionMetadata]): self.plugins = plugins - self.collection_infos = collection_infos + self.collection_metadata = collection_metadata diff --git a/antsibull/docs_parsing/ansible_doc.py b/antsibull/docs_parsing/ansible_doc.py index f3461f77..ef64467c 100644 --- a/antsibull/docs_parsing/ansible_doc.py +++ b/antsibull/docs_parsing/ansible_doc.py @@ -19,7 +19,7 @@ from ..logging import log from ..vendored.json_utils import _filter_non_json_lines from .fqcn import get_fqcn_parts -from . import _get_environment, ParsingError, AnsibleCollectionDocs, AnsibleCollectionInfo +from . import _get_environment, ParsingError, AnsibleCollectionDocs, AnsibleCollectionMetadata if t.TYPE_CHECKING: from ..venv import VenvRunner, FakeVenvRunner @@ -159,11 +159,11 @@ async def _get_plugin_info(plugin_type: str, ansible_doc: 'sh.Command', return results -def get_collection_infos(venv: t.Union['VenvRunner', 'FakeVenvRunner'], - env: t.Dict[str, str], - collection_names: t.Optional[t.List[str]] = None, - ) -> t.Dict[str, AnsibleCollectionInfo]: - collection_infos = {} +def get_collection_metadata(venv: t.Union['VenvRunner', 'FakeVenvRunner'], + env: t.Dict[str, str], + collection_names: t.Optional[t.List[str]] = None, + ) -> t.Dict[str, AnsibleCollectionMetadata]: + collection_metadata = {} # Obtain ansible.builtin version if collection_names is None or 'ansible.builtin' in collection_names: @@ -177,7 +177,8 @@ def get_collection_infos(venv: t.Union['VenvRunner', 'FakeVenvRunner'], path = line.split('=', 2)[1].strip() if line.startswith('ansible '): version = line[len('ansible '):] - collection_infos['ansible.builtin'] = AnsibleCollectionInfo(path=path, version=version) + collection_metadata['ansible.builtin'] = AnsibleCollectionMetadata( + path=path, version=version) # Obtain collection versions venv_ansible_galaxy = venv.get_command('ansible-galaxy') @@ -195,11 +196,11 @@ def get_collection_infos(venv: t.Union['VenvRunner', 'FakeVenvRunner'], if '.' in collection_name: if collection_names is None or collection_name in collection_names: namespace, name = collection_name.split('.', 2) - collection_infos[collection_name] = AnsibleCollectionInfo( + collection_metadata[collection_name] = AnsibleCollectionMetadata( path=os.path.join(current_base_path, namespace, name), version=None if version == '*' else version) - return collection_infos + return collection_metadata async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], @@ -285,8 +286,8 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], # done so, we want to then fail by raising one of the exceptions. raise ParsingError('Parsing of plugins failed') - flog.debug('Retrieving collection infos') - collection_infos = get_collection_infos(venv, env, collection_names) + flog.debug('Retrieving collection metadata') + collection_metadata = get_collection_metadata(venv, env, collection_names) flog.debug('Leave') - return AnsibleCollectionDocs(plugin_map, collection_infos) + return AnsibleCollectionDocs(plugin_map, collection_metadata) diff --git a/antsibull/docs_parsing/ansible_internal.py b/antsibull/docs_parsing/ansible_internal.py index 068c0d9f..f6eb0698 100644 --- a/antsibull/docs_parsing/ansible_internal.py +++ b/antsibull/docs_parsing/ansible_internal.py @@ -11,7 +11,7 @@ from ..logging import log from ..utils.get_pkg_data import get_antsibull_data from ..vendored.json_utils import _filter_non_json_lines -from . import _get_environment, AnsibleCollectionDocs, AnsibleCollectionInfo +from . import _get_environment, AnsibleCollectionDocs, AnsibleCollectionMetadata if t.TYPE_CHECKING: from ..venv import VenvRunner, FakeVenvRunner @@ -67,11 +67,11 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], plugin_log.fields(error=plugin_data['error']).error( 'Error while extracting documentation. Will not document this plugin.') - collection_infos = {} + collection_metadata = {} for collection_name, collection_data in result['collections'].items(): - collection_infos[collection_name] = AnsibleCollectionInfo( + collection_metadata[collection_name] = AnsibleCollectionMetadata( path=collection_data['path'], version=collection_data.get('version')) flog.debug('Leave') - return AnsibleCollectionDocs(plugin_map, collection_infos) + return AnsibleCollectionDocs(plugin_map, collection_metadata) diff --git a/antsibull/write_docs.py b/antsibull/write_docs.py index 1a28b5b9..0c8113e1 100644 --- a/antsibull/write_docs.py +++ b/antsibull/write_docs.py @@ -16,7 +16,7 @@ from . import app_context from .jinja2.environment import doc_environment from .logging import log -from .docs_parsing import AnsibleCollectionInfo +from .docs_parsing import AnsibleCollectionMetadata mlog = log.fields(mod=__name__) @@ -34,7 +34,7 @@ PluginCollectionInfoT = t.Mapping[str, t.Mapping[str, t.Mapping[str, str]]] -async def write_rst(collection_name: str, collection_info: t.Optional[AnsibleCollectionInfo], +async def write_rst(collection_name: str, collection_info: t.Optional[AnsibleCollectionMetadata], plugin_short_name: str, plugin_type: str, plugin_record: t.Dict[str, t.Any], nonfatal_errors: t.Sequence[str], plugin_tmpl: Template, error_tmpl: Template, dest_dir: str, @@ -118,8 +118,8 @@ async def output_all_plugin_rst(collection_to_plugin_info: CollectionInfoT, nonfatal_errors: PluginErrorsT, dest_dir: str, squash_hierarchy: bool = False, - collection_infos: t.Optional[ - t.Mapping[str, AnsibleCollectionInfo]] = None) -> None: + collection_metadata: t.Optional[ + t.Mapping[str, AnsibleCollectionMetadata]] = None) -> None: """ Output rst files for each plugin. @@ -132,7 +132,8 @@ async def output_all_plugin_rst(collection_to_plugin_info: CollectionInfoT, :arg squash_hierarchy: If set to ``True``, no directory hierarchy will be used. Undefined behavior if documentation for multiple collections are created. - :arg collection_infos: Optional dictionary mapping collection names to collection info objects. + :arg collection_metadata: Optional dictionary mapping collection names to collection info + objects. """ # Setup the jinja environment env = doc_environment(('antsibull.data', 'docsite')) @@ -140,8 +141,8 @@ async def output_all_plugin_rst(collection_to_plugin_info: CollectionInfoT, plugin_tmpl = env.get_template('plugin.rst.j2') error_tmpl = env.get_template('plugin-error.rst.j2') - if collection_infos is None: - collection_infos = {} + if collection_metadata is None: + collection_metadata = {} writers = [] lib_ctx = app_context.lib_ctx.get() @@ -152,7 +153,7 @@ async def output_all_plugin_rst(collection_to_plugin_info: CollectionInfoT, plugin_name = '.'.join((collection_name, plugin_short_name)) writers.append(await pool.spawn( write_rst(collection_name, - collection_infos.get(collection_name), + collection_metadata.get(collection_name), plugin_short_name, plugin_type, plugin_info[plugin_type].get(plugin_name), nonfatal_errors[plugin_type][plugin_name], plugin_tmpl, @@ -205,7 +206,7 @@ async def write_plugin_lists(collection_name: str, plugin_maps: t.Mapping[str, t.Mapping[str, str]], template: Template, dest_dir: str, - collection_info: t.Optional[AnsibleCollectionInfo]) -> None: + collection_info: t.Optional[AnsibleCollectionMetadata]) -> None: """ Write an index page for each collection. @@ -300,7 +301,8 @@ async def output_plugin_indexes(plugin_info: PluginCollectionInfoT, async def output_indexes(collection_to_plugin_info: CollectionInfoT, dest_dir: str, squash_hierarchy: bool = False, - collection_infos: t.Optional[t.Mapping[str, AnsibleCollectionInfo]] = None + collection_metadata: t.Optional[ + t.Mapping[str, AnsibleCollectionMetadata]] = None ) -> None: """ Generate collection-level index pages for the collections. @@ -311,13 +313,14 @@ async def output_indexes(collection_to_plugin_info: CollectionInfoT, :arg squash_hierarchy: If set to ``True``, no directory hierarchy will be used. Undefined behavior if documentation for multiple collections are created. - :arg collection_infos: Optional dictionary mapping collection names to collection info objects + :arg collection_metadata: Optional dictionary mapping collection names to collection info + objects. """ flog = mlog.fields(func='output_indexes') flog.debug('Enter') - if collection_infos is None: - collection_infos = {} + if collection_metadata is None: + collection_metadata = {} env = doc_environment(('antsibull.data', 'docsite')) # Get the templates @@ -344,7 +347,7 @@ async def output_indexes(collection_to_plugin_info: CollectionInfoT, collection_dir = collection_toplevel writers.append(await pool.spawn( write_plugin_lists(collection_name, plugin_maps, collection_plugins_tmpl, - collection_dir, collection_infos.get(collection_name)))) + collection_dir, collection_metadata.get(collection_name)))) await asyncio.gather(*writers) From 8521bdc0c4b2b43fc835367db27c297abbcbf476 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Tue, 24 Nov 2020 21:49:13 +0100 Subject: [PATCH 07/14] Remove optionals. --- antsibull/cli/doc_commands/plugin.py | 3 ++- antsibull/cli/doc_commands/stable.py | 8 +++---- antsibull/docs_parsing/__init__.py | 4 ++++ antsibull/write_docs.py | 34 ++++++++++++---------------- 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/antsibull/cli/doc_commands/plugin.py b/antsibull/cli/doc_commands/plugin.py index 45333d9d..1328b5fa 100644 --- a/antsibull/cli/doc_commands/plugin.py +++ b/antsibull/cli/doc_commands/plugin.py @@ -15,6 +15,7 @@ from ... import app_context from ...augment_docs import augment_docs from ...compat import asyncio_run +from ...docs_parsing import AnsibleCollectionMetadata from ...docs_parsing.fqcn import get_fqcn_parts, is_fqcn from ...jinja2.environment import doc_environment from ...logging import log @@ -103,7 +104,7 @@ def generate_docs() -> int: error_tmpl = env.get_template('plugin-error.rst.j2') asyncio_run(write_rst( - '.'.join([namespace, collection]), None, plugin, plugin_type, + '.'.join([namespace, collection]), AnsibleCollectionMetadata.empty(), plugin, plugin_type, plugin_info, errors, plugin_tmpl, error_tmpl, '', path_override=output_path)) flog.debug('Finished writing plugin docs') diff --git a/antsibull/cli/doc_commands/stable.py b/antsibull/cli/doc_commands/stable.py index d54af552..2b4af0d9 100644 --- a/antsibull/cli/doc_commands/stable.py +++ b/antsibull/cli/doc_commands/stable.py @@ -325,14 +325,14 @@ def generate_docs_for_all_collections(venv: t.Union[VenvRunner, FakeVenvRunner], flog.notice('Finished writing plugin indexes') asyncio_run(output_indexes(collection_to_plugin_info, dest_dir, - squash_hierarchy=squash_hierarchy, - collection_metadata=collection_docs.collection_metadata)) + collection_metadata=collection_docs.collection_metadata, + squash_hierarchy=squash_hierarchy)) flog.notice('Finished writing indexes') asyncio_run(output_all_plugin_rst(collection_to_plugin_info, plugin_info, nonfatal_errors, dest_dir, - squash_hierarchy=squash_hierarchy, - collection_metadata=collection_docs.collection_metadata)) + collection_metadata=collection_docs.collection_metadata, + squash_hierarchy=squash_hierarchy)) flog.debug('Finished writing plugin docs') diff --git a/antsibull/docs_parsing/__init__.py b/antsibull/docs_parsing/__init__.py index 0a9b6a8e..a9abdbd5 100644 --- a/antsibull/docs_parsing/__init__.py +++ b/antsibull/docs_parsing/__init__.py @@ -75,6 +75,10 @@ def __init__(self, path: str, version: t.Optional[str]): def __repr__(self): return 'AnsibleCollectionMetadata({0}, {1})'.format(repr(self.path), repr(self.version)) + @classmethod + def empty(cls, path='.'): + return cls(path=path, version=None) + class AnsibleCollectionDocs: # A nested directory structure that looks like: diff --git a/antsibull/write_docs.py b/antsibull/write_docs.py index 0c8113e1..a835fc3c 100644 --- a/antsibull/write_docs.py +++ b/antsibull/write_docs.py @@ -34,7 +34,7 @@ PluginCollectionInfoT = t.Mapping[str, t.Mapping[str, t.Mapping[str, str]]] -async def write_rst(collection_name: str, collection_info: t.Optional[AnsibleCollectionMetadata], +async def write_rst(collection_name: str, collection_meta: AnsibleCollectionMetadata, plugin_short_name: str, plugin_type: str, plugin_record: t.Dict[str, t.Any], nonfatal_errors: t.Sequence[str], plugin_tmpl: Template, error_tmpl: Template, dest_dir: str, @@ -44,7 +44,7 @@ async def write_rst(collection_name: str, collection_info: t.Optional[AnsibleCol Write the rst page for one plugin. :arg collection_name: Dotted colection name. - :arg collection_info: Collection information object (optional). + :arg collection_meta: Collection metadata object. :arg plugin_short_name: short name for the plugin. :arg plugin_type: The type of the plugin. (module, inventory, etc) :arg plugin_record: The record for the plugin. doc, examples, and return are the @@ -75,7 +75,7 @@ async def write_rst(collection_name: str, collection_info: t.Optional[AnsibleCol plugin_contents = error_tmpl.render( plugin_type=plugin_type, plugin_name=plugin_name, collection=collection_name, - collection_version=collection_info.version if collection_info else None, + collection_version=collection_meta.version, nonfatal_errors=nonfatal_errors) else: if nonfatal_errors: @@ -86,7 +86,7 @@ async def write_rst(collection_name: str, collection_info: t.Optional[AnsibleCol plugin_name=plugin_name) plugin_contents = plugin_tmpl.render( collection=collection_name, - collection_version=collection_info.version if collection_info else None, + collection_version=collection_meta.version, plugin_type=plugin_type, plugin_name=plugin_name, doc=plugin_record['doc'], @@ -117,9 +117,8 @@ async def output_all_plugin_rst(collection_to_plugin_info: CollectionInfoT, plugin_info: t.Dict[str, t.Any], nonfatal_errors: PluginErrorsT, dest_dir: str, - squash_hierarchy: bool = False, - collection_metadata: t.Optional[ - t.Mapping[str, AnsibleCollectionMetadata]] = None) -> None: + collection_metadata: t.Mapping[str, AnsibleCollectionMetadata], + squash_hierarchy: bool = False) -> None: """ Output rst files for each plugin. @@ -129,11 +128,10 @@ async def output_all_plugin_rst(collection_to_plugin_info: CollectionInfoT, :arg nonfatal_errors: Mapping of plugins to nonfatal errors. Using this to note on the docs pages when documentation wasn't formatted such that we could use it. :arg dest_dir: The directory to place the documentation in. + :arg collection_metadata: Dictionary mapping collection names to collection metadata objects. :arg squash_hierarchy: If set to ``True``, no directory hierarchy will be used. Undefined behavior if documentation for multiple collections are created. - :arg collection_metadata: Optional dictionary mapping collection names to collection info - objects. """ # Setup the jinja environment env = doc_environment(('antsibull.data', 'docsite')) @@ -141,9 +139,6 @@ async def output_all_plugin_rst(collection_to_plugin_info: CollectionInfoT, plugin_tmpl = env.get_template('plugin.rst.j2') error_tmpl = env.get_template('plugin-error.rst.j2') - if collection_metadata is None: - collection_metadata = {} - writers = [] lib_ctx = app_context.lib_ctx.get() async with asyncio_pool.AioPool(size=lib_ctx.thread_max) as pool: @@ -153,7 +148,8 @@ async def output_all_plugin_rst(collection_to_plugin_info: CollectionInfoT, plugin_name = '.'.join((collection_name, plugin_short_name)) writers.append(await pool.spawn( write_rst(collection_name, - collection_metadata.get(collection_name), + collection_metadata.get(collection_name) or + AnsibleCollectionMetadata.empty(), plugin_short_name, plugin_type, plugin_info[plugin_type].get(plugin_name), nonfatal_errors[plugin_type][plugin_name], plugin_tmpl, @@ -206,7 +202,7 @@ async def write_plugin_lists(collection_name: str, plugin_maps: t.Mapping[str, t.Mapping[str, str]], template: Template, dest_dir: str, - collection_info: t.Optional[AnsibleCollectionMetadata]) -> None: + collection_meta: AnsibleCollectionMetadata) -> None: """ Write an index page for each collection. @@ -215,12 +211,12 @@ async def write_plugin_lists(collection_name: str, :arg plugin_maps: Mapping of plugin_type to Mapping of plugin_name to short_description. :arg template: A template to render the collection index. :arg dest_dir: The destination directory to output the index into. - :arg collection_info: Information on the collection. + :arg collection_meta: Metadata for the collection. """ index_contents = template.render( collection_name=collection_name, plugin_maps=plugin_maps, - collection_version=collection_info.version if collection_info else None) + collection_version=collection_meta.version) # This is only safe because we made sure that the top of the directory tree we're writing to # (docs/docsite/rst) is only writable by us. @@ -300,9 +296,8 @@ async def output_plugin_indexes(plugin_info: PluginCollectionInfoT, async def output_indexes(collection_to_plugin_info: CollectionInfoT, dest_dir: str, + collection_metadata: t.Mapping[str, AnsibleCollectionMetadata], squash_hierarchy: bool = False, - collection_metadata: t.Optional[ - t.Mapping[str, AnsibleCollectionMetadata]] = None ) -> None: """ Generate collection-level index pages for the collections. @@ -310,11 +305,10 @@ async def output_indexes(collection_to_plugin_info: CollectionInfoT, :arg collection_to_plugin_info: Mapping of collection_name to Mapping of plugin_type to Mapping of collection_name to short_description. :arg dest_dir: The directory to place the documentation in. + :arg collection_metadata: Dictionary mapping collection names to collection metadata objects. :arg squash_hierarchy: If set to ``True``, no directory hierarchy will be used. Undefined behavior if documentation for multiple collections are created. - :arg collection_metadata: Optional dictionary mapping collection names to collection info - objects. """ flog = mlog.fields(func='output_indexes') flog.debug('Enter') From 1f2dc20baed5dc5c07bb6179ee94752dc28da2dd Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Tue, 24 Nov 2020 21:52:06 +0100 Subject: [PATCH 08/14] Fix mistake. --- antsibull/write_docs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/antsibull/write_docs.py b/antsibull/write_docs.py index a835fc3c..cc0414a0 100644 --- a/antsibull/write_docs.py +++ b/antsibull/write_docs.py @@ -123,7 +123,7 @@ async def output_all_plugin_rst(collection_to_plugin_info: CollectionInfoT, Output rst files for each plugin. :arg collection_to_plugin_info: Mapping of collection_name to Mapping of plugin_type to Mapping - of collection_name to short_description. + of plugin_name to short_description. :arg plugin_info: Documentation information for all of the plugins. :arg nonfatal_errors: Mapping of plugins to nonfatal errors. Using this to note on the docs pages when documentation wasn't formatted such that we could use it. @@ -233,7 +233,7 @@ async def output_collection_index(collection_to_plugin_info: CollectionInfoT, Generate top-level collection index page for the collections. :arg collection_to_plugin_info: Mapping of collection_name to Mapping of plugin_type to Mapping - of collection_name to short_description. + of plugin_name to short_description. :arg dest_dir: The directory to place the documentation in. """ flog = mlog.fields(func='output_collection_index') @@ -303,7 +303,7 @@ async def output_indexes(collection_to_plugin_info: CollectionInfoT, Generate collection-level index pages for the collections. :arg collection_to_plugin_info: Mapping of collection_name to Mapping of plugin_type to Mapping - of collection_name to short_description. + of plugin_name to short_description. :arg dest_dir: The directory to place the documentation in. :arg collection_metadata: Dictionary mapping collection names to collection metadata objects. :arg squash_hierarchy: If set to ``True``, no directory hierarchy will be used. From 0996b65bc9c37f45cd7269266cda2cef022b6ce7 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Tue, 24 Nov 2020 22:00:34 +0100 Subject: [PATCH 09/14] Convert AnsibleCollectionDocs into a NamedTuple. --- antsibull/docs_parsing/__init__.py | 26 +++++++++------------- antsibull/docs_parsing/ansible_doc.py | 2 +- antsibull/docs_parsing/ansible_internal.py | 2 +- antsibull/docs_parsing/parsing.py | 2 +- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/antsibull/docs_parsing/__init__.py b/antsibull/docs_parsing/__init__.py index a9abdbd5..8af97b2f 100644 --- a/antsibull/docs_parsing/__init__.py +++ b/antsibull/docs_parsing/__init__.py @@ -80,19 +80,13 @@ def empty(cls, path='.'): return cls(path=path, version=None) -class AnsibleCollectionDocs: - # A nested directory structure that looks like: - # plugin_type: - # plugin_name: # Includes namespace and collection. - # {information from ansible-doc --json. See the ansible-doc documentation for more - # info.} - plugins: t.Mapping[str, t.Mapping[str, t.Any]] - - # Maps collection name to metadata - collection_metadata: t.Mapping[str, AnsibleCollectionMetadata] - - def __init__(self, - plugins: t.Mapping[str, t.Dict[str, t.Any]], - collection_metadata: t.Mapping[str, AnsibleCollectionMetadata]): - self.plugins = plugins - self.collection_metadata = collection_metadata +AnsibleCollectionDocs = t.NamedTuple( + 'AnsibleCollectionDocs', [ + # A nested directory structure that looks like: + # plugin_type: + # plugin_name: # Includes namespace and collection. + # {information from ansible-doc --json. See the ansible-doc documentation + # for more info.} + ('plugins', t.Mapping[str, t.Mapping[str, t.Any]]), + # Maps collection name to metadata + ('collection_metadata', t.Mapping[str, AnsibleCollectionMetadata])]) diff --git a/antsibull/docs_parsing/ansible_doc.py b/antsibull/docs_parsing/ansible_doc.py index ef64467c..9f11c456 100644 --- a/antsibull/docs_parsing/ansible_doc.py +++ b/antsibull/docs_parsing/ansible_doc.py @@ -216,7 +216,7 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], search path for Ansible. :arg collection_names: Optional list of collections. If specified, will only collect information for plugins in these collections. - :returns: An AnsibleCollectionDocs object. + :returns: An AnsibleCollectionDocs tuple. """ flog = mlog.fields(func='get_ansible_plugin_info') flog.debug('Enter') diff --git a/antsibull/docs_parsing/ansible_internal.py b/antsibull/docs_parsing/ansible_internal.py index f6eb0698..cc5d7d13 100644 --- a/antsibull/docs_parsing/ansible_internal.py +++ b/antsibull/docs_parsing/ansible_internal.py @@ -33,7 +33,7 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], search path for Ansible. :arg collection_names: Optional list of collections. If specified, will only collect information for plugins in these collections. - :returns: An AnsibleCollectionDocs object. + :returns: An AnsibleCollectionDocs tuple. """ flog = mlog.fields(func='get_ansible_plugin_info') flog.debug('Enter') diff --git a/antsibull/docs_parsing/parsing.py b/antsibull/docs_parsing/parsing.py index daef5de1..9b66da31 100644 --- a/antsibull/docs_parsing/parsing.py +++ b/antsibull/docs_parsing/parsing.py @@ -31,7 +31,7 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], search path for Ansible. :arg collection_names: Optional list of collections. If specified, will only collect information for plugins in these collections. - :returns: An AnsibleCollectionDocs object. + :returns: An AnsibleCollectionDocs tuple. """ lib_ctx = app_context.lib_ctx.get() From 3a29a34b84c39fb36749cb047ccb1f6f40cbb68f Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Tue, 24 Nov 2020 22:07:47 +0100 Subject: [PATCH 10/14] Add collection name as title. Fixes #219. --- .../data/docsite/plugins_by_collection.rst.j2 | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/antsibull/data/docsite/plugins_by_collection.rst.j2 b/antsibull/data/docsite/plugins_by_collection.rst.j2 index 28ade839..9eb2c7a4 100644 --- a/antsibull/data/docsite/plugins_by_collection.rst.j2 +++ b/antsibull/data/docsite/plugins_by_collection.rst.j2 @@ -2,25 +2,29 @@ .. _plugins_in_@{collection_name}@: -Plugin Index -============ +@{collection_name.title()}@ +@{ '=' * (collection_name | length) }@ -These are the plugins in the @{collection_name}@ collection {% if collection_version %} -(version @{ collection_version }@) +Collection version @{ collection_version }@ {% endif %} .. toctree:: :maxdepth: 1 +Plugin Index +------------ + +These are the plugins in the @{collection_name}@ collection + {% for category, plugins in plugin_maps.items() | sort %} {% if category == 'module' %} Modules -------- +~~~~~~~ {% else %} @{ category | capitalize }@ Plugins -@{ '-' * ((category | length) + 8) }@ +@{ '~' * ((category | length) + 8) }@ {% endif %} {% for name, desc in plugins.items() | sort %} From 892998d49d2f0d32bccd07b7e5831d131e0d22c4 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Tue, 24 Nov 2020 22:44:59 +0100 Subject: [PATCH 11/14] Replace NamedTuple with plain tuple. --- antsibull/cli/doc_commands/stable.py | 18 +++++++++--------- antsibull/docs_parsing/__init__.py | 12 ------------ antsibull/docs_parsing/ansible_doc.py | 17 +++++++++++++---- antsibull/docs_parsing/ansible_internal.py | 17 +++++++++++++---- antsibull/docs_parsing/parsing.py | 15 ++++++++++++--- 5 files changed, 47 insertions(+), 32 deletions(-) diff --git a/antsibull/cli/doc_commands/stable.py b/antsibull/cli/doc_commands/stable.py index 2b4af0d9..8be61f1f 100644 --- a/antsibull/cli/doc_commands/stable.py +++ b/antsibull/cli/doc_commands/stable.py @@ -269,26 +269,26 @@ def generate_docs_for_all_collections(venv: t.Union[VenvRunner, FakeVenvRunner], """ # Get the info from the plugins - collection_docs = asyncio_run(get_ansible_plugin_info( + plugin_info, collection_metadata = asyncio_run(get_ansible_plugin_info( venv, collection_dir, collection_names=collection_names)) flog.notice('Finished parsing info from plugins and collections') - # flog.fields(plugin_info=collection_docs.plugins).debug('Plugin data') + # flog.fields(plugin_info=plugin_info).debug('Plugin data') # flog.fields( - # collection_metadata=collection_docs.collection_metadata).debug('Collection infos') + # collection_metadata=collection_metadata).debug('Collection metadata') """ # Turn these into some sort of decorator that will choose to dump or load the values # if a command line arg is specified. with open('dump_raw_plugin_info.json', 'w') as f: import json - json.dump(collection_docs.plugins, f) - flog.debug('Finished dumping raw collection_docs.plugins') + json.dump(plugin_info, f) + flog.debug('Finished dumping raw plugin_info') with open('dump_formatted_plugin_info.json', 'r') as f: import json - collection_docs.plugins = json.load(f) + plugin_info = json.load(f) """ - plugin_info, nonfatal_errors = asyncio_run(normalize_all_plugin_info(collection_docs.plugins)) + plugin_info, nonfatal_errors = asyncio_run(normalize_all_plugin_info(plugin_info)) flog.fields(errors=len(nonfatal_errors)).notice('Finished data validation') augment_docs(plugin_info) flog.notice('Finished calculating new data') @@ -325,13 +325,13 @@ def generate_docs_for_all_collections(venv: t.Union[VenvRunner, FakeVenvRunner], flog.notice('Finished writing plugin indexes') asyncio_run(output_indexes(collection_to_plugin_info, dest_dir, - collection_metadata=collection_docs.collection_metadata, + collection_metadata=collection_metadata, squash_hierarchy=squash_hierarchy)) flog.notice('Finished writing indexes') asyncio_run(output_all_plugin_rst(collection_to_plugin_info, plugin_info, nonfatal_errors, dest_dir, - collection_metadata=collection_docs.collection_metadata, + collection_metadata=collection_metadata, squash_hierarchy=squash_hierarchy)) flog.debug('Finished writing plugin docs') diff --git a/antsibull/docs_parsing/__init__.py b/antsibull/docs_parsing/__init__.py index 8af97b2f..1ccbb5f1 100644 --- a/antsibull/docs_parsing/__init__.py +++ b/antsibull/docs_parsing/__init__.py @@ -78,15 +78,3 @@ def __repr__(self): @classmethod def empty(cls, path='.'): return cls(path=path, version=None) - - -AnsibleCollectionDocs = t.NamedTuple( - 'AnsibleCollectionDocs', [ - # A nested directory structure that looks like: - # plugin_type: - # plugin_name: # Includes namespace and collection. - # {information from ansible-doc --json. See the ansible-doc documentation - # for more info.} - ('plugins', t.Mapping[str, t.Mapping[str, t.Any]]), - # Maps collection name to metadata - ('collection_metadata', t.Mapping[str, AnsibleCollectionMetadata])]) diff --git a/antsibull/docs_parsing/ansible_doc.py b/antsibull/docs_parsing/ansible_doc.py index 9f11c456..37f1ae5e 100644 --- a/antsibull/docs_parsing/ansible_doc.py +++ b/antsibull/docs_parsing/ansible_doc.py @@ -19,7 +19,7 @@ from ..logging import log from ..vendored.json_utils import _filter_non_json_lines from .fqcn import get_fqcn_parts -from . import _get_environment, ParsingError, AnsibleCollectionDocs, AnsibleCollectionMetadata +from . import _get_environment, ParsingError, AnsibleCollectionMetadata if t.TYPE_CHECKING: from ..venv import VenvRunner, FakeVenvRunner @@ -206,7 +206,9 @@ def get_collection_metadata(venv: t.Union['VenvRunner', 'FakeVenvRunner'], async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], collection_dir: t.Optional[str], collection_names: t.Optional[t.List[str]] = None - ) -> AnsibleCollectionDocs: + ) -> t.Tuple[ + t.Mapping[str, t.Mapping[str, t.Any]], + t.Mapping[str, t.Any]]: """ Retrieve information about all of the Ansible Plugins. @@ -216,7 +218,14 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], search path for Ansible. :arg collection_names: Optional list of collections. If specified, will only collect information for plugins in these collections. - :returns: An AnsibleCollectionDocs tuple. + :returns: An tuple. The first component is a nested directory structure that looks like: + + plugin_type: + plugin_name: # Includes namespace and collection. + {information from ansible-doc --json. See the ansible-doc documentation + for more info.} + + The second component is a Mapping of collection names to metadata. """ flog = mlog.fields(func='get_ansible_plugin_info') flog.debug('Enter') @@ -290,4 +299,4 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], collection_metadata = get_collection_metadata(venv, env, collection_names) flog.debug('Leave') - return AnsibleCollectionDocs(plugin_map, collection_metadata) + return (plugin_map, collection_metadata) diff --git a/antsibull/docs_parsing/ansible_internal.py b/antsibull/docs_parsing/ansible_internal.py index cc5d7d13..0acc6e9c 100644 --- a/antsibull/docs_parsing/ansible_internal.py +++ b/antsibull/docs_parsing/ansible_internal.py @@ -11,7 +11,7 @@ from ..logging import log from ..utils.get_pkg_data import get_antsibull_data from ..vendored.json_utils import _filter_non_json_lines -from . import _get_environment, AnsibleCollectionDocs, AnsibleCollectionMetadata +from . import _get_environment, AnsibleCollectionMetadata if t.TYPE_CHECKING: from ..venv import VenvRunner, FakeVenvRunner @@ -23,7 +23,9 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], collection_dir: t.Optional[str], collection_names: t.Optional[t.List[str]] = None - ) -> AnsibleCollectionDocs: + ) -> t.Tuple[ + t.Mapping[str, t.Mapping[str, t.Any]], + t.Mapping[str, t.Any]]: """ Retrieve information about all of the Ansible Plugins. @@ -33,7 +35,14 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], search path for Ansible. :arg collection_names: Optional list of collections. If specified, will only collect information for plugins in these collections. - :returns: An AnsibleCollectionDocs tuple. + :returns: An tuple. The first component is a nested directory structure that looks like: + + plugin_type: + plugin_name: # Includes namespace and collection. + {information from ansible-doc --json. See the ansible-doc documentation + for more info.} + + The second component is a Mapping of collection names to metadata. """ flog = mlog.fields(func='get_ansible_plugin_info') flog.debug('Enter') @@ -74,4 +83,4 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], version=collection_data.get('version')) flog.debug('Leave') - return AnsibleCollectionDocs(plugin_map, collection_metadata) + return (plugin_map, collection_metadata) diff --git a/antsibull/docs_parsing/parsing.py b/antsibull/docs_parsing/parsing.py index 9b66da31..2fadf243 100644 --- a/antsibull/docs_parsing/parsing.py +++ b/antsibull/docs_parsing/parsing.py @@ -9,7 +9,6 @@ from ..logging import log from .ansible_doc import get_ansible_plugin_info as ansible_doc_get_ansible_plugin_info from .ansible_internal import get_ansible_plugin_info as ansible_internal_get_ansible_plugin_info -from . import AnsibleCollectionDocs if t.TYPE_CHECKING: from ..venv import VenvRunner, FakeVenvRunner @@ -21,7 +20,9 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], collection_dir: t.Optional[str], collection_names: t.Optional[t.List[str]] = None - ) -> AnsibleCollectionDocs: + ) -> t.Tuple[ + t.Mapping[str, t.Mapping[str, t.Any]], + t.Mapping[str, t.Any]]: """ Retrieve information about all of the Ansible Plugins. @@ -31,7 +32,15 @@ async def get_ansible_plugin_info(venv: t.Union['VenvRunner', 'FakeVenvRunner'], search path for Ansible. :arg collection_names: Optional list of collections. If specified, will only collect information for plugins in these collections. - :returns: An AnsibleCollectionDocs tuple. + :returns: An tuple. The first component is a nested directory structure that looks like: + + plugin_type: + plugin_name: # Includes namespace and collection. + {information from ansible-doc --json. See the ansible-doc documentation + for more info.} + + The second component is a Mapping of collection names to metadata. + """ lib_ctx = app_context.lib_ctx.get() From 9f0a1caba53382cc080f9f33afd0d5c659274ec3 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Wed, 25 Nov 2020 07:10:37 +0100 Subject: [PATCH 12/14] Crash if collection has no metadata. --- antsibull/write_docs.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/antsibull/write_docs.py b/antsibull/write_docs.py index cc0414a0..62a3dde7 100644 --- a/antsibull/write_docs.py +++ b/antsibull/write_docs.py @@ -148,8 +148,7 @@ async def output_all_plugin_rst(collection_to_plugin_info: CollectionInfoT, plugin_name = '.'.join((collection_name, plugin_short_name)) writers.append(await pool.spawn( write_rst(collection_name, - collection_metadata.get(collection_name) or - AnsibleCollectionMetadata.empty(), + collection_metadata[collection_name], plugin_short_name, plugin_type, plugin_info[plugin_type].get(plugin_name), nonfatal_errors[plugin_type][plugin_name], plugin_tmpl, @@ -341,7 +340,7 @@ async def output_indexes(collection_to_plugin_info: CollectionInfoT, collection_dir = collection_toplevel writers.append(await pool.spawn( write_plugin_lists(collection_name, plugin_maps, collection_plugins_tmpl, - collection_dir, collection_metadata.get(collection_name)))) + collection_dir, collection_metadata[collection_name]))) await asyncio.gather(*writers) From 54ed7ceedd120d1968a028f733ff269101eab08e Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Wed, 25 Nov 2020 07:12:04 +0100 Subject: [PATCH 13/14] Use concrete type. --- antsibull/cli/doc_commands/stable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/antsibull/cli/doc_commands/stable.py b/antsibull/cli/doc_commands/stable.py index 8be61f1f..817ee3b4 100644 --- a/antsibull/cli/doc_commands/stable.py +++ b/antsibull/cli/doc_commands/stable.py @@ -222,7 +222,7 @@ def get_plugin_contents(plugin_info: t.Mapping[str, t.Mapping[str, t.Any]], def get_collection_contents(plugin_content: t.Mapping[str, t.Mapping[str, t.Mapping[str, str]]], - ) -> CollectionInfoT: + ) -> t.DefaultDict[str, t.Dict[str, t.Mapping[str, str]]]: """ Return the plugins which are in each collection. From 298eafbefa503b3e4efb74b97b7b231e6de3b855 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Wed, 25 Nov 2020 07:13:40 +0100 Subject: [PATCH 14/14] Linting. --- antsibull/cli/doc_commands/stable.py | 1 - 1 file changed, 1 deletion(-) diff --git a/antsibull/cli/doc_commands/stable.py b/antsibull/cli/doc_commands/stable.py index 817ee3b4..c5569999 100644 --- a/antsibull/cli/doc_commands/stable.py +++ b/antsibull/cli/doc_commands/stable.py @@ -28,7 +28,6 @@ from ...schemas.docs import DOCS_SCHEMAS from ...venv import VenvRunner, FakeVenvRunner from ...write_docs import ( - CollectionInfoT, output_all_plugin_rst, output_collection_index, output_indexes,