diff --git a/changelogs/fragments/55249-doc_fragments_from_collections.yml b/changelogs/fragments/55249-doc_fragments_from_collections.yml new file mode 100644 index 0000000000..9c2361fa6e --- /dev/null +++ b/changelogs/fragments/55249-doc_fragments_from_collections.yml @@ -0,0 +1,2 @@ +bugfixes: + - Fixed loading namespaced documentation fragments from collections. diff --git a/lib/ansible/plugins/loader.py b/lib/ansible/plugins/loader.py index f245b7b30f..80876081ad 100644 --- a/lib/ansible/plugins/loader.py +++ b/lib/ansible/plugins/loader.py @@ -325,15 +325,12 @@ class PluginLoader: package = splitname[0] resource = splitname[1] - append_plugin_type = self.class_name or self.subdir + append_plugin_type = self.subdir.replace('_plugins', '') - if append_plugin_type: - # only current non-class special case, module_utils don't use this loader method - if append_plugin_type == 'library': - append_plugin_type = 'modules' - elif append_plugin_type != 'module_utils': - append_plugin_type = get_plugin_class(append_plugin_type) - package += '.plugins.{0}'.format(append_plugin_type) + if append_plugin_type == 'library': + append_plugin_type = 'modules' + + package += '.plugins.{0}'.format(append_plugin_type) if extension: resource += extension diff --git a/lib/ansible/utils/plugin_docs.py b/lib/ansible/utils/plugin_docs.py index c54587b069..70689d04eb 100644 --- a/lib/ansible/utils/plugin_docs.py +++ b/lib/ansible/utils/plugin_docs.py @@ -50,14 +50,20 @@ def add_fragments(doc, filename, fragment_loader): # Allow the module to specify a var other than DOCUMENTATION # to pull the fragment from, using dot notation as a separator for fragment_slug in fragments: + fallback_name = None fragment_slug = fragment_slug.lower() if '.' in fragment_slug: - fragment_name, fragment_var = fragment_slug.split('.', 1) + fallback_name = fragment_slug + fragment_name, fragment_var = fragment_slug.rsplit('.', 1) fragment_var = fragment_var.upper() else: fragment_name, fragment_var = fragment_slug, 'DOCUMENTATION' fragment_class = fragment_loader.get(fragment_name) + if fragment_class is None and fallback_name: + fragment_class = fragment_loader.get(fallback_name) + fragment_var = 'DOCUMENTATION' + if fragment_class is None: raise AnsibleAssertionError('fragment_class is None') diff --git a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/doc_fragments/frag.py b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/doc_fragments/frag.py new file mode 100644 index 0000000000..0ff430b600 --- /dev/null +++ b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/doc_fragments/frag.py @@ -0,0 +1,14 @@ +class ModuleDocFragment(object): + DOCUMENTATION = r''' +options: + normal_doc_frag: + description: + - an option +''' + + OTHER_DOCUMENTATION = r''' +options: + other_doc_frag: + description: + - another option +''' diff --git a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/testmodule.py b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/testmodule.py index 58d3152f28..d0220af901 100644 --- a/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/testmodule.py +++ b/test/integration/targets/collections/collection_root_user/ansible_collections/testns/testcoll/plugins/modules/testmodule.py @@ -2,6 +2,14 @@ import json +DOCUMENTATION = r''' +module: testmodule +description: for testing +extends_documentation_fragment: + - testns.testcoll.frag + - testns.testcoll.frag.other_documentation +''' + def main(): print(json.dumps(dict(changed=False, source='user'))) diff --git a/test/integration/targets/collections/runme.sh b/test/integration/targets/collections/runme.sh index 44be497773..4735875269 100755 --- a/test/integration/targets/collections/runme.sh +++ b/test/integration/targets/collections/runme.sh @@ -20,6 +20,9 @@ fi # test callback ANSIBLE_CALLBACK_WHITELIST=testns.testcoll.usercallback ansible localhost -m ping | grep "usercallback says ok" +# test documentation +ansible-doc testns.testcoll.testmodule -vvv | grep -- "- normal_doc_frag" + # we need multiple plays, and conditional import_playbook is noisy and causes problems, so choose here which one to use... if [[ ${INVENTORY_PATH} == *.winrm ]]; then export TEST_PLAYBOOK=windows.yml