# Using custom module\_utils-path for shared code

**URL:** <https://forum.ansible.com/t/using-custom-module-utils-path-for-shared-code/952>\
**Category:** Get Help\
**Tags:** module, callback\
**Created:** [September 20, 2023, 10:27am UTC](https://forum.ansible.com/t/using-custom-module-utils-path-for-shared-code/952 "2023-09-20T10:27:10Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![handflucht](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/handflucht/32/722_2.png) [@handflucht](https://forum.ansible.com/u/handflucht)\
**Post date:** [September 20, 2023, 10:27am UTC](https://forum.ansible.com/t/using-custom-module-utils-path-for-shared-code/952/1 "2023-09-20T10:27:10Z")

</div>

Hi all,

I wrote two callback-plugins which use an identical log-method, so I would like to share this code. Doing so, I moved that code to an external file which I would like to import.  
Currently, I always get an error, that this external file can not be found. Any help would be apricated.

My structure does look like this

```auto
workspace
 |- playbooks
     |- ansible.cfg
	 |- my.yml
	 |- playbooks.yml
 |- collections
 |- plugins
     |- action
	 |- callback
	     |- general
			 |- log_errors.py
			 |- log_hosts.py
	 |- filter
	 |- lookup
 |- roles
	 |- my
	 |- roles
	 |- ...

```

For using this structure, the ansible.cfg in the playbooks-dir has be modified:

```auto
roles_path = ../roles
collections_paths = ../collections
action_plugins = ../plugins/action
callback_plugins = ../plugins/callback
lookup_plugins = ../plugins/lookup
filter_plugins = ../plugins/filter

```

**The above works fine, also the custom callback-plugins work if I don’t share the code.**

For using the shared code I did the following changes.

Added the file containing the shared code into a new directory:

```auto
workspace
 |- module_utils
     |- logging.py

```

Updated the path in the ansible.cfg:

```auto
 module_utils = ../module_utils

```

In my callback-plugins I added the following line:  
`from ansible.module_utils.logging import write_log`

If I execute a playbook, I get the following warning:

> [WARNING]: Skipping plugin (\<some\_path\>/plugins/callback/general/log\_errors.py) as it seems to be  
> invalid: No module named ‘ansible.module\_utils.logging’

**If however, I copy the file from my customer folder module\_utils to /usr/local/lib/python3.8/dist-packages/ansible/module\_utils/logging.py it works.**

Any Ideas what I am doing wrong?

Update: Also renaming my custom module\_utils-folder to something else (while updating path in ansible-cfg and import) did not do the trick.

---

<div class="post-metadata">

**Author:** ![sean\_sullivan](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/sean_sullivan/32/274_2.png) [@sean\_sullivan](https://forum.ansible.com/u/sean_sullivan)\
**Post date:** [September 20, 2023, 1:56pm UTC](https://forum.ansible.com/t/using-custom-module-utils-path-for-shared-code/952/2 "2023-09-20T13:56:18Z")

</div>

Have you tried  
from …module\_utils.controller\_api import ControllerAPIModule

no slashes  
Here is where I pulled the reference

> <https://github.com/ansible/awx/blob/7b846e1e49874c64aac979619d2773c3bc3b384e/awx_collection/plugins/lookup/controller_api.py#L123C1-L123C62>

you can also import module utils from other collections  
from ansible\_collections.awx.awx.plugins.module\_utils.awxkit import ControllerAWXKitModule

> <https://github.com/redhat-cop/controller_configuration/blob/6e5c972db2472863862ea605e0c58f6408bc6bd6/plugins/modules/controller_export_diff.py#L210C18-L210C18>

---

<div class="post-metadata">

**Author:** ![handflucht](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/handflucht/32/722_2.png) [@handflucht](https://forum.ansible.com/u/handflucht)\
**Post date:** [September 20, 2023, 2:43pm UTC](https://forum.ansible.com/t/using-custom-module-utils-path-for-shared-code/952/3 "2023-09-20T14:43:19Z")

</div>

Interesting syntax, never seen before. Didn’t know that it could be valid.

If just tried

```auto
from ..module_utils.logging import write_log

```

and it returns

```auto
[WARNING]: Skipping plugin (<path>/plugins/callback/general/log_errors.py) as it seems to be invalid: No module named 'ansible.plugins.module_utils'

```

Maybe it works in the linked example because it is inside of a collection which somehow handles the scope differently. I will have a closer link to the repo and see if I can find something similar.

Additionally I also checked with dump to check if the value is set, and this seems also to be fine (also did changes to the ansible.cfg and the path gets updated accordingly):

```auto
$ ansible-config dump | grep util
DEFAULT_MODULE_UTILS_PATH(/<path>/playbooks/ansible.cfg) = ['/<path>/module_utils']

```

---

<div class="post-metadata">

**Author:** ![felixfontein](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/felixfontein/32/7530_2.png) [@felixfontein](https://forum.ansible.com/u/felixfontein)\
**Post date:** [September 20, 2023, 5:00pm UTC](https://forum.ansible.com/t/using-custom-module-utils-path-for-shared-code/952/4 "2023-09-20T17:00:05Z")

</div>

A similar question was recently asked in [https://groups.google.com/g/ansible-project/c/rBFuX3MCapk](https://groups.google.com/g/ansible-project/c/rBFuX3MCapk) - the answer is basically no. It would work if you put your plugin into a collection, though.

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [September 20, 2023, 7:12pm UTC](https://forum.ansible.com/t/using-custom-module-utils-path-for-shared-code/952/5 "2023-09-20T19:12:39Z")

</div>

Heh, that was me. (@felixfontein I still owe you some docs about shared code. I have not forgotten!)

In my case, I wrote a custom test - `sch_distinct` - and a custom filter - `sch_uniqcomb` - and I needed to use some code from the test in the filter. I eventually settled on this structure in a “local” (to my project) collection:

```txt
collections/ansible_collections/mw/tablinx/plugins
├── filter
│ └── tablinx.py
└── test
    └── tablinx.py

```

(And, yes, I gave all the files the same name. If we were all named Bob no-one would ever forget a name.)

So the filter uses the following import to pull in the test code’s `distinct` function:

```python
from ansible_collections.mw.tablinx.plugins.test.tablinx import distinct

```

The nice extra is that out in user land, it’s all nicely scoped to the `mw.tablinx` namespace. I’m starting to think about moving the bulk of my project-local bits into this local collection so everything gets scoped.

```yaml
schedules_all_uc: "{{ schedules_all | mw.tablinx.sch_uniqcomb }}"
whackamole: "{{ foo is mw.tablinx.sch_distinct }}" # The sch_distinct test uses distinct() on the back end.

```

---

<div class="post-metadata">

**Author:** ![handflucht](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/handflucht/32/722_2.png) [@handflucht](https://forum.ansible.com/u/handflucht)\
**Post date:** [September 21, 2023, 6:55am UTC](https://forum.ansible.com/t/using-custom-module-utils-path-for-shared-code/952/6 "2023-09-21T06:55:43Z")

</div>

Thanks a lot for your help!

So why do I then can modify the module\_utils-path at all? Would it work for actual modules but not for plugins (even without being part of a collection?)

---

<div class="post-metadata">

**Author:** ![felixfontein](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/felixfontein/32/7530_2.png) [@felixfontein](https://forum.ansible.com/u/felixfontein)\
**Post date:** [September 21, 2023, 10:38am UTC](https://forum.ansible.com/t/using-custom-module-utils-path-for-shared-code/952/7 "2023-09-21T10:38:18Z")

</div>

Local module\_utils can only be used in local modules.

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [September 21, 2023, 9:29pm UTC](https://forum.ansible.com/t/using-custom-module-utils-path-for-shared-code/952/8 "2023-09-21T21:29:19Z")

</div>

Wha…!? After all that, moments ago I found [7.7. Keep plugin entry files to a minimal size.](https://redhat-cop.github.io/automation-good-practices/#_keep_plugin_entry_files_to_a_minimal_size) in which it mentions

> Move reusable functions and classes, such as those for data validation or manipulation, to a [module\_utils/](https://docs.ansible.com/ansible/latest/dev_guide/developing_module_utilities.html) (for Ansible modules) or plugin\_utils/ (for all other plugin types) file and import them into plugins.

This is the first time I’ve heard about `plugin_utils/`. I’m happy with the local collection solution, but I’ve really got to investigate this `plugin_utils/` shared code functionality. _sigh_ It never ends!

---

<div class="post-metadata">

**Author:** ![felixfontein](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/felixfontein/32/7530_2.png) [@felixfontein](https://forum.ansible.com/u/felixfontein)\
**Post date:** [September 22, 2023, 5:22am UTC](https://forum.ansible.com/t/using-custom-module-utils-path-for-shared-code/952/9 "2023-09-22T05:22:31Z")

</div>

`plugin_utils` is an “officially reserved name” in collection `plugins/` directories, but it has no native support from ansible-core’s side. The “officially reserved name” basically means that everyone agrees on that `plugins/plugin_utils/` in collections is used for that, but you could also use `plugins/random_other_name/` instead (ansible-core doesn’t care how the folder is called, as long as it doesn’t collide with the special ones it supports for modules, module\_utils, and supported plugin types).

So `plugin_utils/` won’t work outside collections. And if you already use a local collection, you can also use `plugins/module_utils/`.

(The main reason `plugin_utils/` is recommended for plugin-specific code that isn’t used by modules is that you cannot import from `ansible.xxx` besides `ansible.module_utils.xxx` in Python code in `plugins/module_utils/`. So the idea is to put all code that needs to import other things from `ansible` in `plugins/plugin_utils/`, since that directory has no such restrictions.)

The official place where plugin\_utils is specified is in the requirements document for collections included in Ansible: [Ansible community package collections requirements — Ansible Documentation](https://docs.ansible.com/ansible/devel/community/collection_contributors/collection_requirements.html#modules-plugins)

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/ansible/original/1X/1882d9a01da5d8996a862359b0913cc568a39b84.svg) [@system](https://forum.ansible.com/u/system)\
**Post date:** [October 22, 2023, 5:22am UTC](https://forum.ansible.com/t/using-custom-module-utils-path-for-shared-code/952/10 "2023-10-22T05:22:47Z")

</div>

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
