We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
xfconf fails to set double value when LC_NUMERIC is set to nb_NO.UTF-8
### Summary

In https://github.com/ansible-collections/community.general/pull/744 `LANGUAGE` is used to force `xfconf-query` to return doubles using the expected format. This fails when `LC_NUMERIC` is set. From the article linked to in https://github.com/ansible-collections/community.general/pull/744, it seems like setting `LANGUAGE` should override `LC_NUMERIC`, but that isn't actually the case.

The correct variable to use in this case is probably `LC_ALL`.

I've attached a terminal recording showing the results. You'll notice that in the first run, the `previous_value` is `0,200000`, while after setting `LC_ALL=C`, the `previous_value` becomes `0.200000` which matches the input and no change is needed.

I've also attached the test-play I used (with a `.txt` extension because github doesn't like `.yml`).

[recording.txt](https://github.com/ansible-collections/community.general/files/6597487/recording.txt)

[test_play.yml](https://github.com/ansible-collections/community.general/files/6597488/test_play.yml.txt)


### Issue Type

Bug Report

### Component Name

xfconf

### Ansible Version

```console (paste below)
$ ansible --version
ansible 2.10.5
  config file = /home/mortenlj/.ansible.cfg
  configured module search path = ['/home/mortenlj/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.9.5 (default, May 11 2021, 08:20:37) [GCC 10.3.0]
```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed
DEFAULT_GATHERING(/home/mortenlj/.ansible.cfg) = smart
DEFAULT_HOST_LIST(/home/mortenlj/.ansible.cfg) = ['/home/mortenlj/code/personal/ansible/hosts']
DEFAULT_VAULT_PASSWORD_FILE(/home/mortenlj/.ansible.cfg) = /home/mortenlj/.ansible/vault-pass
```


### OS / Environment

```
$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="21.04 (Hirsute Hippo)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 21.04"
VERSION_ID="21.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=hirsute
UBUNTU_CODENAME=hirsute

$ apt list xfce4-settings
Listing... Done
xfce4-settings/hirsute,now 4.16.0-1ubuntu1 amd64 [installed,automatic]
```


### Steps to Reproduce

See summary for links.

### Expected Results

I expect the play to not "change" the xfconf property on every run, because the new value should match the already set value.

### Actual Results

See summary. I realise I only used `-vvv`, but I don't think the extra `v` would make it any clearer.


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `plugins/module_utils/mh/mixins/cmd.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 # (c) 2020, Alexei Znamensky <russoz@gmail.com>
3 # Copyright: (c) 2020, Ansible Project
4 # Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
5 
6 from __future__ import absolute_import, division, print_function
7 __metaclass__ = type
8 
9 from functools import partial
10 
11 
12 class ArgFormat(object):
13     """
14     Argument formatter for use as a command line parameter. Used in CmdMixin.
15     """
16     BOOLEAN = 0
17     PRINTF = 1
18     FORMAT = 2
19 
20     @staticmethod
21     def stars_deco(num):
22         if num == 1:
23             def deco(f):
24                 return lambda v: f(*v)
25             return deco
26         elif num == 2:
27             def deco(f):
28                 return lambda v: f(**v)
29             return deco
30 
31         return lambda f: f
32 
33     def __init__(self, name, fmt=None, style=FORMAT, stars=0):
34         """
35         Creates a CLI-formatter for one specific argument. The argument may be a module parameter or just a named parameter for
36         the CLI command execution.
37         :param name: Name of the argument to be formatted
38         :param fmt: Either a str to be formatted (using or not printf-style) or a callable that does that
39         :param style: Whether arg_format (as str) should use printf-style formatting.
40                              Ignored if arg_format is None or not a str (should be callable).
41         :param stars: A int with 0, 1 or 2 value, indicating to formatting the value as: value, *value or **value
42         """
43         def printf_fmt(_fmt, v):
44             try:
45                 return [_fmt % v]
46             except TypeError as e:
47                 if e.args[0] != 'not all arguments converted during string formatting':
48                     raise
49                 return [_fmt]
50 
51         _fmts = {
52             ArgFormat.BOOLEAN: lambda _fmt, v: ([_fmt] if bool(v) else []),
53             ArgFormat.PRINTF: printf_fmt,
54             ArgFormat.FORMAT: lambda _fmt, v: [_fmt.format(v)],
55         }
56 
57         self.name = name
58         self.stars = stars
59 
60         if fmt is None:
61             fmt = "{0}"
62             style = ArgFormat.FORMAT
63 
64         if isinstance(fmt, str):
65             func = _fmts[style]
66             self.arg_format = partial(func, fmt)
67         elif isinstance(fmt, list) or isinstance(fmt, tuple):
68             self.arg_format = lambda v: [_fmts[style](f, v)[0] for f in fmt]
69         elif hasattr(fmt, '__call__'):
70             self.arg_format = fmt
71         else:
72             raise TypeError('Parameter fmt must be either: a string, a list/tuple of '
73                             'strings or a function: type={0}, value={1}'.format(type(fmt), fmt))
74 
75         if stars:
76             self.arg_format = (self.stars_deco(stars))(self.arg_format)
77 
78     def to_text(self, value):
79         if value is None:
80             return []
81         func = self.arg_format
82         return [str(p) for p in func(value)]
83 
84 
85 class CmdMixin(object):
86     """
87     Mixin for mapping module options to running a CLI command with its arguments.
88     """
89     command = None
90     command_args_formats = {}
91     run_command_fixed_options = {}
92     check_rc = False
93     force_lang = "C"
94 
95     @property
96     def module_formats(self):
97         result = {}
98         for param in self.module.params.keys():
99             result[param] = ArgFormat(param)
100         return result
101 
102     @property
103     def custom_formats(self):
104         result = {}
105         for param, fmt_spec in self.command_args_formats.items():
106             result[param] = ArgFormat(param, **fmt_spec)
107         return result
108 
109     def _calculate_args(self, extra_params=None, params=None):
110         def add_arg_formatted_param(_cmd_args, arg_format, _value):
111             args = list(arg_format.to_text(_value))
112             return _cmd_args + args
113 
114         def find_format(_param):
115             return self.custom_formats.get(_param, self.module_formats.get(_param))
116 
117         extra_params = extra_params or dict()
118         cmd_args = list([self.command]) if isinstance(self.command, str) else list(self.command)
119         try:
120             cmd_args[0] = self.module.get_bin_path(cmd_args[0], required=True)
121         except ValueError:
122             pass
123         param_list = params if params else self.vars.keys()
124 
125         for param in param_list:
126             if isinstance(param, dict):
127                 if len(param) != 1:
128                     raise self.ModuleHelperException("run_command parameter as a dict must "
129                                                      "contain only one key: {0}".format(param))
130                 _param = list(param.keys())[0]
131                 fmt = find_format(_param)
132                 value = param[_param]
133             elif isinstance(param, str):
134                 if param in self.vars.keys():
135                     fmt = find_format(param)
136                     value = self.vars[param]
137                 elif param in extra_params:
138                     fmt = find_format(param)
139                     value = extra_params[param]
140                 else:
141                     self.module.deprecate("Cannot determine value for parameter: {0}. "
142                                           "From version 4.0.0 onwards this will generate an exception".format(param),
143                                           version="4.0.0", collection_name="community.general")
144                     continue
145 
146             else:
147                 raise self.ModuleHelperException("run_command parameter must be either a str or a dict: {0}".format(param))
148             cmd_args = add_arg_formatted_param(cmd_args, fmt, value)
149 
150         return cmd_args
151 
152     def process_command_output(self, rc, out, err):
153         return rc, out, err
154 
155     def run_command(self, extra_params=None, params=None, process_output=None, *args, **kwargs):
156         self.vars.cmd_args = self._calculate_args(extra_params, params)
157         options = dict(self.run_command_fixed_options)
158         env_update = dict(options.get('environ_update', {}))
159         options['check_rc'] = options.get('check_rc', self.check_rc)
160         if self.force_lang:
161             env_update.update({'LANGUAGE': self.force_lang})
162             self.update_output(force_lang=self.force_lang)
163             options['environ_update'] = env_update
164         options.update(kwargs)
165         rc, out, err = self.module.run_command(self.vars.cmd_args, *args, **options)
166         self.update_output(rc=rc, stdout=out, stderr=err)
167         if process_output is None:
168             _process = self.process_command_output
169         else:
170             _process = process_output
171 
172         return _process(rc, out, err)
173
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/plugins/module_utils/mh/mixins/cmd.py b/plugins/module_utils/mh/mixins/cmd.py
--- a/plugins/module_utils/mh/mixins/cmd.py
+++ b/plugins/module_utils/mh/mixins/cmd.py
@@ -155,13 +155,16 @@
     def run_command(self, extra_params=None, params=None, process_output=None, *args, **kwargs):
         self.vars.cmd_args = self._calculate_args(extra_params, params)
         options = dict(self.run_command_fixed_options)
-        env_update = dict(options.get('environ_update', {}))
         options['check_rc'] = options.get('check_rc', self.check_rc)
+        options.update(kwargs)
+        env_update = dict(options.get('environ_update', {}))
         if self.force_lang:
-            env_update.update({'LANGUAGE': self.force_lang})
+            env_update.update({
+                'LANGUAGE': self.force_lang,
+                'LC_ALL': self.force_lang,
+            })
             self.update_output(force_lang=self.force_lang)
             options['environ_update'] = env_update
-        options.update(kwargs)
         rc, out, err = self.module.run_command(self.vars.cmd_args, *args, **options)
         self.update_output(rc=rc, stdout=out, stderr=err)
         if process_output is None:

{"golden_diff": "diff --git a/plugins/module_utils/mh/mixins/cmd.py b/plugins/module_utils/mh/mixins/cmd.py\n--- a/plugins/module_utils/mh/mixins/cmd.py\n+++ b/plugins/module_utils/mh/mixins/cmd.py\n@@ -155,13 +155,16 @@\n     def run_command(self, extra_params=None, params=None, process_output=None, *args, **kwargs):\n         self.vars.cmd_args = self._calculate_args(extra_params, params)\n         options = dict(self.run_command_fixed_options)\n-        env_update = dict(options.get('environ_update', {}))\n         options['check_rc'] = options.get('check_rc', self.check_rc)\n+        options.update(kwargs)\n+        env_update = dict(options.get('environ_update', {}))\n         if self.force_lang:\n-            env_update.update({'LANGUAGE': self.force_lang})\n+            env_update.update({\n+                'LANGUAGE': self.force_lang,\n+                'LC_ALL': self.force_lang,\n+            })\n             self.update_output(force_lang=self.force_lang)\n             options['environ_update'] = env_update\n-        options.update(kwargs)\n         rc, out, err = self.module.run_command(self.vars.cmd_args, *args, **options)\n         self.update_output(rc=rc, stdout=out, stderr=err)\n         if process_output is None:\n", "issue": "xfconf fails to set double value when LC_NUMERIC is set to nb_NO.UTF-8\n### Summary\r\n\r\nIn https://github.com/ansible-collections/community.general/pull/744 `LANGUAGE` is used to force `xfconf-query` to return doubles using the expected format. This fails when `LC_NUMERIC` is set. From the article linked to in https://github.com/ansible-collections/community.general/pull/744, it seems like setting `LANGUAGE` should override `LC_NUMERIC`, but that isn't actually the case.\r\n\r\nThe correct variable to use in this case is probably `LC_ALL`.\r\n\r\nI've attached a terminal recording showing the results. You'll notice that in the first run, the `previous_value` is `0,200000`, while after setting `LC_ALL=C`, the `previous_value` becomes `0.200000` which matches the input and no change is needed.\r\n\r\nI've also attached the test-play I used (with a `.txt` extension because github doesn't like `.yml`).\r\n\r\n[recording.txt](https://github.com/ansible-collections/community.general/files/6597487/recording.txt)\r\n\r\n[test_play.yml](https://github.com/ansible-collections/community.general/files/6597488/test_play.yml.txt)\r\n\r\n\r\n### Issue Type\r\n\r\nBug Report\r\n\r\n### Component Name\r\n\r\nxfconf\r\n\r\n### Ansible Version\r\n\r\n```console (paste below)\r\n$ ansible --version\r\nansible 2.10.5\r\n  config file = /home/mortenlj/.ansible.cfg\r\n  configured module search path = ['/home/mortenlj/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']\r\n  ansible python module location = /usr/lib/python3/dist-packages/ansible\r\n  executable location = /usr/bin/ansible\r\n  python version = 3.9.5 (default, May 11 2021, 08:20:37) [GCC 10.3.0]\r\n```\r\n\r\n\r\n### Configuration\r\n\r\n```console (paste below)\r\n$ ansible-config dump --only-changed\r\nDEFAULT_GATHERING(/home/mortenlj/.ansible.cfg) = smart\r\nDEFAULT_HOST_LIST(/home/mortenlj/.ansible.cfg) = ['/home/mortenlj/code/personal/ansible/hosts']\r\nDEFAULT_VAULT_PASSWORD_FILE(/home/mortenlj/.ansible.cfg) = /home/mortenlj/.ansible/vault-pass\r\n```\r\n\r\n\r\n### OS / Environment\r\n\r\n```\r\n$ cat /etc/os-release \r\nNAME=\"Ubuntu\"\r\nVERSION=\"21.04 (Hirsute Hippo)\"\r\nID=ubuntu\r\nID_LIKE=debian\r\nPRETTY_NAME=\"Ubuntu 21.04\"\r\nVERSION_ID=\"21.04\"\r\nHOME_URL=\"https://www.ubuntu.com/\"\r\nSUPPORT_URL=\"https://help.ubuntu.com/\"\r\nBUG_REPORT_URL=\"https://bugs.launchpad.net/ubuntu/\"\r\nPRIVACY_POLICY_URL=\"https://www.ubuntu.com/legal/terms-and-policies/privacy-policy\"\r\nVERSION_CODENAME=hirsute\r\nUBUNTU_CODENAME=hirsute\r\n\r\n$ apt list xfce4-settings\r\nListing... Done\r\nxfce4-settings/hirsute,now 4.16.0-1ubuntu1 amd64 [installed,automatic]\r\n```\r\n\r\n\r\n### Steps to Reproduce\r\n\r\nSee summary for links.\r\n\r\n### Expected Results\r\n\r\nI expect the play to not \"change\" the xfconf property on every run, because the new value should match the already set value.\r\n\r\n### Actual Results\r\n\r\nSee summary. I realise I only used `-vvv`, but I don't think the extra `v` would make it any clearer.\r\n\r\n\r\n### Code of Conduct\r\n\r\n- [X] I agree to follow the Ansible Code of Conduct\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n# (c) 2020, Alexei Znamensky <russoz@gmail.com>\n# Copyright: (c) 2020, Ansible Project\n# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)\n\nfrom __future__ import absolute_import, division, print_function\n__metaclass__ = type\n\nfrom functools import partial\n\n\nclass ArgFormat(object):\n    \"\"\"\n    Argument formatter for use as a command line parameter. Used in CmdMixin.\n    \"\"\"\n    BOOLEAN = 0\n    PRINTF = 1\n    FORMAT = 2\n\n    @staticmethod\n    def stars_deco(num):\n        if num == 1:\n            def deco(f):\n                return lambda v: f(*v)\n            return deco\n        elif num == 2:\n            def deco(f):\n                return lambda v: f(**v)\n            return deco\n\n        return lambda f: f\n\n    def __init__(self, name, fmt=None, style=FORMAT, stars=0):\n        \"\"\"\n        Creates a CLI-formatter for one specific argument. The argument may be a module parameter or just a named parameter for\n        the CLI command execution.\n        :param name: Name of the argument to be formatted\n        :param fmt: Either a str to be formatted (using or not printf-style) or a callable that does that\n        :param style: Whether arg_format (as str) should use printf-style formatting.\n                             Ignored if arg_format is None or not a str (should be callable).\n        :param stars: A int with 0, 1 or 2 value, indicating to formatting the value as: value, *value or **value\n        \"\"\"\n        def printf_fmt(_fmt, v):\n            try:\n                return [_fmt % v]\n            except TypeError as e:\n                if e.args[0] != 'not all arguments converted during string formatting':\n                    raise\n                return [_fmt]\n\n        _fmts = {\n            ArgFormat.BOOLEAN: lambda _fmt, v: ([_fmt] if bool(v) else []),\n            ArgFormat.PRINTF: printf_fmt,\n            ArgFormat.FORMAT: lambda _fmt, v: [_fmt.format(v)],\n        }\n\n        self.name = name\n        self.stars = stars\n\n        if fmt is None:\n            fmt = \"{0}\"\n            style = ArgFormat.FORMAT\n\n        if isinstance(fmt, str):\n            func = _fmts[style]\n            self.arg_format = partial(func, fmt)\n        elif isinstance(fmt, list) or isinstance(fmt, tuple):\n            self.arg_format = lambda v: [_fmts[style](f, v)[0] for f in fmt]\n        elif hasattr(fmt, '__call__'):\n            self.arg_format = fmt\n        else:\n            raise TypeError('Parameter fmt must be either: a string, a list/tuple of '\n                            'strings or a function: type={0}, value={1}'.format(type(fmt), fmt))\n\n        if stars:\n            self.arg_format = (self.stars_deco(stars))(self.arg_format)\n\n    def to_text(self, value):\n        if value is None:\n            return []\n        func = self.arg_format\n        return [str(p) for p in func(value)]\n\n\nclass CmdMixin(object):\n    \"\"\"\n    Mixin for mapping module options to running a CLI command with its arguments.\n    \"\"\"\n    command = None\n    command_args_formats = {}\n    run_command_fixed_options = {}\n    check_rc = False\n    force_lang = \"C\"\n\n    @property\n    def module_formats(self):\n        result = {}\n        for param in self.module.params.keys():\n            result[param] = ArgFormat(param)\n        return result\n\n    @property\n    def custom_formats(self):\n        result = {}\n        for param, fmt_spec in self.command_args_formats.items():\n            result[param] = ArgFormat(param, **fmt_spec)\n        return result\n\n    def _calculate_args(self, extra_params=None, params=None):\n        def add_arg_formatted_param(_cmd_args, arg_format, _value):\n            args = list(arg_format.to_text(_value))\n            return _cmd_args + args\n\n        def find_format(_param):\n            return self.custom_formats.get(_param, self.module_formats.get(_param))\n\n        extra_params = extra_params or dict()\n        cmd_args = list([self.command]) if isinstance(self.command, str) else list(self.command)\n        try:\n            cmd_args[0] = self.module.get_bin_path(cmd_args[0], required=True)\n        except ValueError:\n            pass\n        param_list = params if params else self.vars.keys()\n\n        for param in param_list:\n            if isinstance(param, dict):\n                if len(param) != 1:\n                    raise self.ModuleHelperException(\"run_command parameter as a dict must \"\n                                                     \"contain only one key: {0}\".format(param))\n                _param = list(param.keys())[0]\n                fmt = find_format(_param)\n                value = param[_param]\n            elif isinstance(param, str):\n                if param in self.vars.keys():\n                    fmt = find_format(param)\n                    value = self.vars[param]\n                elif param in extra_params:\n                    fmt = find_format(param)\n                    value = extra_params[param]\n                else:\n                    self.module.deprecate(\"Cannot determine value for parameter: {0}. \"\n                                          \"From version 4.0.0 onwards this will generate an exception\".format(param),\n                                          version=\"4.0.0\", collection_name=\"community.general\")\n                    continue\n\n            else:\n                raise self.ModuleHelperException(\"run_command parameter must be either a str or a dict: {0}\".format(param))\n            cmd_args = add_arg_formatted_param(cmd_args, fmt, value)\n\n        return cmd_args\n\n    def process_command_output(self, rc, out, err):\n        return rc, out, err\n\n    def run_command(self, extra_params=None, params=None, process_output=None, *args, **kwargs):\n        self.vars.cmd_args = self._calculate_args(extra_params, params)\n        options = dict(self.run_command_fixed_options)\n        env_update = dict(options.get('environ_update', {}))\n        options['check_rc'] = options.get('check_rc', self.check_rc)\n        if self.force_lang:\n            env_update.update({'LANGUAGE': self.force_lang})\n            self.update_output(force_lang=self.force_lang)\n            options['environ_update'] = env_update\n        options.update(kwargs)\n        rc, out, err = self.module.run_command(self.vars.cmd_args, *args, **options)\n        self.update_output(rc=rc, stdout=out, stderr=err)\n        if process_output is None:\n            _process = self.process_command_output\n        else:\n            _process = process_output\n\n        return _process(rc, out, err)\n", "path": "plugins/module_utils/mh/mixins/cmd.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n# (c) 2020, Alexei Znamensky <russoz@gmail.com>\n# Copyright: (c) 2020, Ansible Project\n# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)\n\nfrom __future__ import absolute_import, division, print_function\n__metaclass__ = type\n\nfrom functools import partial\n\n\nclass ArgFormat(object):\n    \"\"\"\n    Argument formatter for use as a command line parameter. Used in CmdMixin.\n    \"\"\"\n    BOOLEAN = 0\n    PRINTF = 1\n    FORMAT = 2\n\n    @staticmethod\n    def stars_deco(num):\n        if num == 1:\n            def deco(f):\n                return lambda v: f(*v)\n            return deco\n        elif num == 2:\n            def deco(f):\n                return lambda v: f(**v)\n            return deco\n\n        return lambda f: f\n\n    def __init__(self, name, fmt=None, style=FORMAT, stars=0):\n        \"\"\"\n        Creates a CLI-formatter for one specific argument. The argument may be a module parameter or just a named parameter for\n        the CLI command execution.\n        :param name: Name of the argument to be formatted\n        :param fmt: Either a str to be formatted (using or not printf-style) or a callable that does that\n        :param style: Whether arg_format (as str) should use printf-style formatting.\n                             Ignored if arg_format is None or not a str (should be callable).\n        :param stars: A int with 0, 1 or 2 value, indicating to formatting the value as: value, *value or **value\n        \"\"\"\n        def printf_fmt(_fmt, v):\n            try:\n                return [_fmt % v]\n            except TypeError as e:\n                if e.args[0] != 'not all arguments converted during string formatting':\n                    raise\n                return [_fmt]\n\n        _fmts = {\n            ArgFormat.BOOLEAN: lambda _fmt, v: ([_fmt] if bool(v) else []),\n            ArgFormat.PRINTF: printf_fmt,\n            ArgFormat.FORMAT: lambda _fmt, v: [_fmt.format(v)],\n        }\n\n        self.name = name\n        self.stars = stars\n\n        if fmt is None:\n            fmt = \"{0}\"\n            style = ArgFormat.FORMAT\n\n        if isinstance(fmt, str):\n            func = _fmts[style]\n            self.arg_format = partial(func, fmt)\n        elif isinstance(fmt, list) or isinstance(fmt, tuple):\n            self.arg_format = lambda v: [_fmts[style](f, v)[0] for f in fmt]\n        elif hasattr(fmt, '__call__'):\n            self.arg_format = fmt\n        else:\n            raise TypeError('Parameter fmt must be either: a string, a list/tuple of '\n                            'strings or a function: type={0}, value={1}'.format(type(fmt), fmt))\n\n        if stars:\n            self.arg_format = (self.stars_deco(stars))(self.arg_format)\n\n    def to_text(self, value):\n        if value is None:\n            return []\n        func = self.arg_format\n        return [str(p) for p in func(value)]\n\n\nclass CmdMixin(object):\n    \"\"\"\n    Mixin for mapping module options to running a CLI command with its arguments.\n    \"\"\"\n    command = None\n    command_args_formats = {}\n    run_command_fixed_options = {}\n    check_rc = False\n    force_lang = \"C\"\n\n    @property\n    def module_formats(self):\n        result = {}\n        for param in self.module.params.keys():\n            result[param] = ArgFormat(param)\n        return result\n\n    @property\n    def custom_formats(self):\n        result = {}\n        for param, fmt_spec in self.command_args_formats.items():\n            result[param] = ArgFormat(param, **fmt_spec)\n        return result\n\n    def _calculate_args(self, extra_params=None, params=None):\n        def add_arg_formatted_param(_cmd_args, arg_format, _value):\n            args = list(arg_format.to_text(_value))\n            return _cmd_args + args\n\n        def find_format(_param):\n            return self.custom_formats.get(_param, self.module_formats.get(_param))\n\n        extra_params = extra_params or dict()\n        cmd_args = list([self.command]) if isinstance(self.command, str) else list(self.command)\n        try:\n            cmd_args[0] = self.module.get_bin_path(cmd_args[0], required=True)\n        except ValueError:\n            pass\n        param_list = params if params else self.vars.keys()\n\n        for param in param_list:\n            if isinstance(param, dict):\n                if len(param) != 1:\n                    raise self.ModuleHelperException(\"run_command parameter as a dict must \"\n                                                     \"contain only one key: {0}\".format(param))\n                _param = list(param.keys())[0]\n                fmt = find_format(_param)\n                value = param[_param]\n            elif isinstance(param, str):\n                if param in self.vars.keys():\n                    fmt = find_format(param)\n                    value = self.vars[param]\n                elif param in extra_params:\n                    fmt = find_format(param)\n                    value = extra_params[param]\n                else:\n                    self.module.deprecate(\"Cannot determine value for parameter: {0}. \"\n                                          \"From version 4.0.0 onwards this will generate an exception\".format(param),\n                                          version=\"4.0.0\", collection_name=\"community.general\")\n                    continue\n\n            else:\n                raise self.ModuleHelperException(\"run_command parameter must be either a str or a dict: {0}\".format(param))\n            cmd_args = add_arg_formatted_param(cmd_args, fmt, value)\n\n        return cmd_args\n\n    def process_command_output(self, rc, out, err):\n        return rc, out, err\n\n    def run_command(self, extra_params=None, params=None, process_output=None, *args, **kwargs):\n        self.vars.cmd_args = self._calculate_args(extra_params, params)\n        options = dict(self.run_command_fixed_options)\n        options['check_rc'] = options.get('check_rc', self.check_rc)\n        options.update(kwargs)\n        env_update = dict(options.get('environ_update', {}))\n        if self.force_lang:\n            env_update.update({\n                'LANGUAGE': self.force_lang,\n                'LC_ALL': self.force_lang,\n            })\n            self.update_output(force_lang=self.force_lang)\n            options['environ_update'] = env_update\n        rc, out, err = self.module.run_command(self.vars.cmd_args, *args, **options)\n        self.update_output(rc=rc, stdout=out, stderr=err)\n        if process_output is None:\n            _process = self.process_command_output\n        else:\n            _process = process_output\n\n        return _process(rc, out, err)\n", "path": "plugins/module_utils/mh/mixins/cmd.py"}]}