We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ini_file - Insert missing option line before blank lines at the end of the section
##### ISSUE TYPE
- Bug Report
##### COMPONENT NAME

ini_file
##### ANSIBLE VERSION

```
ansible 2.1.1.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
```
##### CONFIGURATION

N/A
##### OS / ENVIRONMENT

N/A
##### SUMMARY

<!--- Explain the problem briefly -->

New lines are added to an existing section after blank lines separating sections, but should instead be added before blank lines at the end of a section.
##### STEPS TO REPRODUCE

Use ini_file to add a new line to a file.

Given test.ini:

```
[sect1]
opt1 = val1

[sect2]
opt2 = val2
```

Run this test command:

```
ansible -c local -m ini_file -a 'dest=test.ini section=sect1 option=opt13 value=val13' localhost
```
##### EXPECTED RESULTS

test.ini should look like this:

```
[sect1]
opt1 = val1
opt3 = val3

[sect2]
opt2 = val2
```
##### ACTUAL RESULTS

This file is still technically correct but just looks a bit misleading as opt3 is grouped closer to [sect2].

```
[sect1]
opt1 = val1

opt3 = val3
[sect2]
opt2 = val2
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `files/ini_file.py`
Content:
```
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 
4 # (c) 2012, Jan-Piet Mens <jpmens () gmail.com>
5 # (c) 2015, Ales Nosek <anosek.nosek () gmail.com>
6 #
7 # This file is part of Ansible
8 #
9 # Ansible is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # Ansible is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
21 #
22 
23 DOCUMENTATION = '''
24 ---
25 module: ini_file
26 short_description: Tweak settings in INI files
27 extends_documentation_fragment: files
28 description:
29      - Manage (add, remove, change) individual settings in an INI-style file without having
30        to manage the file as a whole with, say, M(template) or M(assemble). Adds missing
31        sections if they don't exist.
32      - Before version 2.0, comments are discarded when the source file is read, and therefore will not show up in the destination file.
33 version_added: "0.9"
34 options:
35   dest:
36     description:
37       - Path to the INI-style file; this file is created if required
38     required: true
39     default: null
40   section:
41     description:
42       - Section name in INI file. This is added if C(state=present) automatically when
43         a single value is being set.
44     required: true
45     default: null
46   option:
47     description:
48       - if set (required for changing a I(value)), this is the name of the option.
49       - May be omitted if adding/removing a whole I(section).
50     required: false
51     default: null
52   value:
53     description:
54      - the string value to be associated with an I(option). May be omitted when removing an I(option).
55     required: false
56     default: null
57   backup:
58     description:
59       - Create a backup file including the timestamp information so you can get
60         the original file back if you somehow clobbered it incorrectly.
61     required: false
62     default: "no"
63     choices: [ "yes", "no" ]
64   others:
65      description:
66        - all arguments accepted by the M(file) module also work here
67      required: false
68   state:
69      description:
70        - If set to C(absent) the option or section will be removed if present instead of created.
71      required: false
72      default: "present"
73      choices: [ "present", "absent" ]
74   no_extra_spaces:
75      description:
76        - do not insert spaces before and after '=' symbol
77      required: false
78      default: false
79      version_added: "2.1"
80 notes:
81    - While it is possible to add an I(option) without specifying a I(value), this makes
82      no sense.
83    - A section named C(default) cannot be added by the module, but if it exists, individual
84      options within the section can be updated. (This is a limitation of Python's I(ConfigParser).)
85      Either use M(template) to create a base INI file with a C([default]) section, or use
86      M(lineinfile) to add the missing line.
87 requirements: [ ConfigParser ]
88 author:
89     - "Jan-Piet Mens (@jpmens)"
90     - "Ales Nosek (@noseka1)"
91 '''
92 
93 EXAMPLES = '''
94 # Ensure "fav=lemonade is in section "[drinks]" in specified file
95 - ini_file: dest=/etc/conf section=drinks option=fav value=lemonade mode=0600 backup=yes
96 
97 - ini_file: dest=/etc/anotherconf
98             section=drinks
99             option=temperature
100             value=cold
101             backup=yes
102 '''
103 
104 import os
105 import re
106 
107 # ==============================================================
108 # match_opt
109 
110 def match_opt(option, line):
111   option = re.escape(option)
112   return re.match('%s( |\t)*=' % option, line) \
113     or re.match('# *%s( |\t)*=' % option, line) \
114     or re.match('; *%s( |\t)*=' % option, line)
115 
116 # ==============================================================
117 # match_active_opt
118 
119 def match_active_opt(option, line):
120   option = re.escape(option)
121   return re.match('%s( |\t)*=' % option, line)
122 
123 # ==============================================================
124 # do_ini
125 
126 def do_ini(module, filename, section=None, option=None, value=None, state='present', backup=False, no_extra_spaces=False):
127 
128 
129     if not os.path.exists(filename):
130       try:
131         open(filename,'w').close()
132       except:
133         module.fail_json(msg="Destination file %s not writable" % filename)
134     ini_file = open(filename, 'r')
135     try:
136         ini_lines = ini_file.readlines()
137         # append a fake section line to simplify the logic
138         ini_lines.append('[')
139     finally:
140         ini_file.close()
141 
142     within_section = not section
143     section_start = 0
144     changed = False
145     if no_extra_spaces:
146         assignment_format = '%s=%s\n'
147     else:
148         assignment_format = '%s = %s\n'
149 
150     for index, line in enumerate(ini_lines):
151         if line.startswith('[%s]' % section):
152             within_section = True
153             section_start = index
154         elif line.startswith('['):
155             if within_section:
156                 if state == 'present':
157                     # insert missing option line at the end of the section
158                     ini_lines.insert(index, assignment_format % (option, value))
159                     changed = True
160                 elif state == 'absent' and not option:
161                     # remove the entire section
162                     del ini_lines[section_start:index]
163                     changed = True
164                 break
165         else:
166             if within_section and option:
167                 if state == 'present':
168                     # change the existing option line
169                     if match_opt(option, line):
170                         newline = assignment_format % (option, value)
171                         changed = ini_lines[index] != newline
172                         ini_lines[index] = newline
173                         if changed:
174                             # remove all possible option occurences from the rest of the section
175                             index = index + 1
176                             while index < len(ini_lines):
177                                 line = ini_lines[index]
178                                 if line.startswith('['):
179                                     break
180                                 if match_active_opt(option, line):
181                                     del ini_lines[index]
182                                 else:
183                                     index = index + 1
184                         break
185                 else:
186                     # comment out the existing option line
187                     if match_active_opt(option, line):
188                         ini_lines[index] = '#%s' % ini_lines[index]
189                         changed = True
190                         break
191 
192     # remove the fake section line
193     del ini_lines[-1:]
194 
195     if not within_section and option and state == 'present':
196         ini_lines.append('[%s]\n' % section)
197         ini_lines.append(assignment_format % (option, value))
198         changed = True
199 
200 
201     backup_file = None
202     if changed and not module.check_mode:
203         if backup:
204             backup_file = module.backup_local(filename)
205         ini_file = open(filename, 'w')
206         try:
207             ini_file.writelines(ini_lines)
208         finally:
209             ini_file.close()
210 
211     return (changed, backup_file)
212 
213 # ==============================================================
214 # main
215 
216 def main():
217 
218     module = AnsibleModule(
219         argument_spec = dict(
220             dest = dict(required=True),
221             section = dict(required=True),
222             option = dict(required=False),
223             value = dict(required=False),
224             backup = dict(default='no', type='bool'),
225             state = dict(default='present', choices=['present', 'absent']),
226             no_extra_spaces = dict(required=False, default=False, type='bool')
227         ),
228         add_file_common_args = True,
229         supports_check_mode = True
230     )
231 
232     dest = os.path.expanduser(module.params['dest'])
233     section = module.params['section']
234     option = module.params['option']
235     value = module.params['value']
236     state = module.params['state']
237     backup = module.params['backup']
238     no_extra_spaces = module.params['no_extra_spaces']
239 
240     (changed,backup_file) = do_ini(module, dest, section, option, value, state, backup, no_extra_spaces)
241 
242     file_args = module.load_file_common_arguments(module.params)
243     changed = module.set_fs_attributes_if_different(file_args, changed)
244 
245     results = { 'changed': changed, 'msg': "OK", 'dest': dest }
246     if backup_file is not None:
247         results['backup_file'] = backup_file
248 
249     # Mission complete
250     module.exit_json(**results)
251 
252 # import module snippets
253 from ansible.module_utils.basic import *
254 if __name__ == '__main__':
255     main()
256
```
--- 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/files/ini_file.py b/files/ini_file.py
--- a/files/ini_file.py
+++ b/files/ini_file.py
@@ -155,8 +155,12 @@
             if within_section:
                 if state == 'present':
                     # insert missing option line at the end of the section
-                    ini_lines.insert(index, assignment_format % (option, value))
-                    changed = True
+                    for i in range(index, 0, -1):
+                        # search backwards for previous non-blank or non-comment line
+                        if not re.match(r'^[ \t]*([#;].*)?$', ini_lines[i - 1]):
+                            ini_lines.insert(i, assignment_format % (option, value))
+                            changed = True
+                            break
                 elif state == 'absent' and not option:
                     # remove the entire section
                     del ini_lines[section_start:index]

{"golden_diff": "diff --git a/files/ini_file.py b/files/ini_file.py\n--- a/files/ini_file.py\n+++ b/files/ini_file.py\n@@ -155,8 +155,12 @@\n             if within_section:\n                 if state == 'present':\n                     # insert missing option line at the end of the section\n-                    ini_lines.insert(index, assignment_format % (option, value))\n-                    changed = True\n+                    for i in range(index, 0, -1):\n+                        # search backwards for previous non-blank or non-comment line\n+                        if not re.match(r'^[ \\t]*([#;].*)?$', ini_lines[i - 1]):\n+                            ini_lines.insert(i, assignment_format % (option, value))\n+                            changed = True\n+                            break\n                 elif state == 'absent' and not option:\n                     # remove the entire section\n                     del ini_lines[section_start:index]\n", "issue": "ini_file - Insert missing option line before blank lines at the end of the section\n##### ISSUE TYPE\n- Bug Report\n##### COMPONENT NAME\n\nini_file\n##### ANSIBLE VERSION\n\n```\nansible 2.1.1.0\n  config file = /etc/ansible/ansible.cfg\n  configured module search path = Default w/o overrides\n```\n##### CONFIGURATION\n\nN/A\n##### OS / ENVIRONMENT\n\nN/A\n##### SUMMARY\n\n<!--- Explain the problem briefly -->\n\nNew lines are added to an existing section after blank lines separating sections, but should instead be added before blank lines at the end of a section.\n##### STEPS TO REPRODUCE\n\nUse ini_file to add a new line to a file.\n\nGiven test.ini:\n\n```\n[sect1]\nopt1 = val1\n\n[sect2]\nopt2 = val2\n```\n\nRun this test command:\n\n```\nansible -c local -m ini_file -a 'dest=test.ini section=sect1 option=opt13 value=val13' localhost\n```\n##### EXPECTED RESULTS\n\ntest.ini should look like this:\n\n```\n[sect1]\nopt1 = val1\nopt3 = val3\n\n[sect2]\nopt2 = val2\n```\n##### ACTUAL RESULTS\n\nThis file is still technically correct but just looks a bit misleading as opt3 is grouped closer to [sect2].\n\n```\n[sect1]\nopt1 = val1\n\nopt3 = val3\n[sect2]\nopt2 = val2\n```\n\n", "before_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n\n# (c) 2012, Jan-Piet Mens <jpmens () gmail.com>\n# (c) 2015, Ales Nosek <anosek.nosek () gmail.com>\n#\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\n#\n\nDOCUMENTATION = '''\n---\nmodule: ini_file\nshort_description: Tweak settings in INI files\nextends_documentation_fragment: files\ndescription:\n     - Manage (add, remove, change) individual settings in an INI-style file without having\n       to manage the file as a whole with, say, M(template) or M(assemble). Adds missing\n       sections if they don't exist.\n     - Before version 2.0, comments are discarded when the source file is read, and therefore will not show up in the destination file.\nversion_added: \"0.9\"\noptions:\n  dest:\n    description:\n      - Path to the INI-style file; this file is created if required\n    required: true\n    default: null\n  section:\n    description:\n      - Section name in INI file. This is added if C(state=present) automatically when\n        a single value is being set.\n    required: true\n    default: null\n  option:\n    description:\n      - if set (required for changing a I(value)), this is the name of the option.\n      - May be omitted if adding/removing a whole I(section).\n    required: false\n    default: null\n  value:\n    description:\n     - the string value to be associated with an I(option). May be omitted when removing an I(option).\n    required: false\n    default: null\n  backup:\n    description:\n      - Create a backup file including the timestamp information so you can get\n        the original file back if you somehow clobbered it incorrectly.\n    required: false\n    default: \"no\"\n    choices: [ \"yes\", \"no\" ]\n  others:\n     description:\n       - all arguments accepted by the M(file) module also work here\n     required: false\n  state:\n     description:\n       - If set to C(absent) the option or section will be removed if present instead of created.\n     required: false\n     default: \"present\"\n     choices: [ \"present\", \"absent\" ]\n  no_extra_spaces:\n     description:\n       - do not insert spaces before and after '=' symbol\n     required: false\n     default: false\n     version_added: \"2.1\"\nnotes:\n   - While it is possible to add an I(option) without specifying a I(value), this makes\n     no sense.\n   - A section named C(default) cannot be added by the module, but if it exists, individual\n     options within the section can be updated. (This is a limitation of Python's I(ConfigParser).)\n     Either use M(template) to create a base INI file with a C([default]) section, or use\n     M(lineinfile) to add the missing line.\nrequirements: [ ConfigParser ]\nauthor:\n    - \"Jan-Piet Mens (@jpmens)\"\n    - \"Ales Nosek (@noseka1)\"\n'''\n\nEXAMPLES = '''\n# Ensure \"fav=lemonade is in section \"[drinks]\" in specified file\n- ini_file: dest=/etc/conf section=drinks option=fav value=lemonade mode=0600 backup=yes\n\n- ini_file: dest=/etc/anotherconf\n            section=drinks\n            option=temperature\n            value=cold\n            backup=yes\n'''\n\nimport os\nimport re\n\n# ==============================================================\n# match_opt\n\ndef match_opt(option, line):\n  option = re.escape(option)\n  return re.match('%s( |\\t)*=' % option, line) \\\n    or re.match('# *%s( |\\t)*=' % option, line) \\\n    or re.match('; *%s( |\\t)*=' % option, line)\n\n# ==============================================================\n# match_active_opt\n\ndef match_active_opt(option, line):\n  option = re.escape(option)\n  return re.match('%s( |\\t)*=' % option, line)\n\n# ==============================================================\n# do_ini\n\ndef do_ini(module, filename, section=None, option=None, value=None, state='present', backup=False, no_extra_spaces=False):\n\n\n    if not os.path.exists(filename):\n      try:\n        open(filename,'w').close()\n      except:\n        module.fail_json(msg=\"Destination file %s not writable\" % filename)\n    ini_file = open(filename, 'r')\n    try:\n        ini_lines = ini_file.readlines()\n        # append a fake section line to simplify the logic\n        ini_lines.append('[')\n    finally:\n        ini_file.close()\n\n    within_section = not section\n    section_start = 0\n    changed = False\n    if no_extra_spaces:\n        assignment_format = '%s=%s\\n'\n    else:\n        assignment_format = '%s = %s\\n'\n\n    for index, line in enumerate(ini_lines):\n        if line.startswith('[%s]' % section):\n            within_section = True\n            section_start = index\n        elif line.startswith('['):\n            if within_section:\n                if state == 'present':\n                    # insert missing option line at the end of the section\n                    ini_lines.insert(index, assignment_format % (option, value))\n                    changed = True\n                elif state == 'absent' and not option:\n                    # remove the entire section\n                    del ini_lines[section_start:index]\n                    changed = True\n                break\n        else:\n            if within_section and option:\n                if state == 'present':\n                    # change the existing option line\n                    if match_opt(option, line):\n                        newline = assignment_format % (option, value)\n                        changed = ini_lines[index] != newline\n                        ini_lines[index] = newline\n                        if changed:\n                            # remove all possible option occurences from the rest of the section\n                            index = index + 1\n                            while index < len(ini_lines):\n                                line = ini_lines[index]\n                                if line.startswith('['):\n                                    break\n                                if match_active_opt(option, line):\n                                    del ini_lines[index]\n                                else:\n                                    index = index + 1\n                        break\n                else:\n                    # comment out the existing option line\n                    if match_active_opt(option, line):\n                        ini_lines[index] = '#%s' % ini_lines[index]\n                        changed = True\n                        break\n\n    # remove the fake section line\n    del ini_lines[-1:]\n\n    if not within_section and option and state == 'present':\n        ini_lines.append('[%s]\\n' % section)\n        ini_lines.append(assignment_format % (option, value))\n        changed = True\n\n\n    backup_file = None\n    if changed and not module.check_mode:\n        if backup:\n            backup_file = module.backup_local(filename)\n        ini_file = open(filename, 'w')\n        try:\n            ini_file.writelines(ini_lines)\n        finally:\n            ini_file.close()\n\n    return (changed, backup_file)\n\n# ==============================================================\n# main\n\ndef main():\n\n    module = AnsibleModule(\n        argument_spec = dict(\n            dest = dict(required=True),\n            section = dict(required=True),\n            option = dict(required=False),\n            value = dict(required=False),\n            backup = dict(default='no', type='bool'),\n            state = dict(default='present', choices=['present', 'absent']),\n            no_extra_spaces = dict(required=False, default=False, type='bool')\n        ),\n        add_file_common_args = True,\n        supports_check_mode = True\n    )\n\n    dest = os.path.expanduser(module.params['dest'])\n    section = module.params['section']\n    option = module.params['option']\n    value = module.params['value']\n    state = module.params['state']\n    backup = module.params['backup']\n    no_extra_spaces = module.params['no_extra_spaces']\n\n    (changed,backup_file) = do_ini(module, dest, section, option, value, state, backup, no_extra_spaces)\n\n    file_args = module.load_file_common_arguments(module.params)\n    changed = module.set_fs_attributes_if_different(file_args, changed)\n\n    results = { 'changed': changed, 'msg': \"OK\", 'dest': dest }\n    if backup_file is not None:\n        results['backup_file'] = backup_file\n\n    # Mission complete\n    module.exit_json(**results)\n\n# import module snippets\nfrom ansible.module_utils.basic import *\nif __name__ == '__main__':\n    main()\n", "path": "files/ini_file.py"}], "after_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n\n# (c) 2012, Jan-Piet Mens <jpmens () gmail.com>\n# (c) 2015, Ales Nosek <anosek.nosek () gmail.com>\n#\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\n#\n\nDOCUMENTATION = '''\n---\nmodule: ini_file\nshort_description: Tweak settings in INI files\nextends_documentation_fragment: files\ndescription:\n     - Manage (add, remove, change) individual settings in an INI-style file without having\n       to manage the file as a whole with, say, M(template) or M(assemble). Adds missing\n       sections if they don't exist.\n     - Before version 2.0, comments are discarded when the source file is read, and therefore will not show up in the destination file.\nversion_added: \"0.9\"\noptions:\n  dest:\n    description:\n      - Path to the INI-style file; this file is created if required\n    required: true\n    default: null\n  section:\n    description:\n      - Section name in INI file. This is added if C(state=present) automatically when\n        a single value is being set.\n    required: true\n    default: null\n  option:\n    description:\n      - if set (required for changing a I(value)), this is the name of the option.\n      - May be omitted if adding/removing a whole I(section).\n    required: false\n    default: null\n  value:\n    description:\n     - the string value to be associated with an I(option). May be omitted when removing an I(option).\n    required: false\n    default: null\n  backup:\n    description:\n      - Create a backup file including the timestamp information so you can get\n        the original file back if you somehow clobbered it incorrectly.\n    required: false\n    default: \"no\"\n    choices: [ \"yes\", \"no\" ]\n  others:\n     description:\n       - all arguments accepted by the M(file) module also work here\n     required: false\n  state:\n     description:\n       - If set to C(absent) the option or section will be removed if present instead of created.\n     required: false\n     default: \"present\"\n     choices: [ \"present\", \"absent\" ]\n  no_extra_spaces:\n     description:\n       - do not insert spaces before and after '=' symbol\n     required: false\n     default: false\n     version_added: \"2.1\"\nnotes:\n   - While it is possible to add an I(option) without specifying a I(value), this makes\n     no sense.\n   - A section named C(default) cannot be added by the module, but if it exists, individual\n     options within the section can be updated. (This is a limitation of Python's I(ConfigParser).)\n     Either use M(template) to create a base INI file with a C([default]) section, or use\n     M(lineinfile) to add the missing line.\nrequirements: [ ConfigParser ]\nauthor:\n    - \"Jan-Piet Mens (@jpmens)\"\n    - \"Ales Nosek (@noseka1)\"\n'''\n\nEXAMPLES = '''\n# Ensure \"fav=lemonade is in section \"[drinks]\" in specified file\n- ini_file: dest=/etc/conf section=drinks option=fav value=lemonade mode=0600 backup=yes\n\n- ini_file: dest=/etc/anotherconf\n            section=drinks\n            option=temperature\n            value=cold\n            backup=yes\n'''\n\nimport os\nimport re\n\n# ==============================================================\n# match_opt\n\ndef match_opt(option, line):\n  option = re.escape(option)\n  return re.match('%s( |\\t)*=' % option, line) \\\n    or re.match('# *%s( |\\t)*=' % option, line) \\\n    or re.match('; *%s( |\\t)*=' % option, line)\n\n# ==============================================================\n# match_active_opt\n\ndef match_active_opt(option, line):\n  option = re.escape(option)\n  return re.match('%s( |\\t)*=' % option, line)\n\n# ==============================================================\n# do_ini\n\ndef do_ini(module, filename, section=None, option=None, value=None, state='present', backup=False, no_extra_spaces=False):\n\n\n    if not os.path.exists(filename):\n      try:\n        open(filename,'w').close()\n      except:\n        module.fail_json(msg=\"Destination file %s not writable\" % filename)\n    ini_file = open(filename, 'r')\n    try:\n        ini_lines = ini_file.readlines()\n        # append a fake section line to simplify the logic\n        ini_lines.append('[')\n    finally:\n        ini_file.close()\n\n    within_section = not section\n    section_start = 0\n    changed = False\n    if no_extra_spaces:\n        assignment_format = '%s=%s\\n'\n    else:\n        assignment_format = '%s = %s\\n'\n\n    for index, line in enumerate(ini_lines):\n        if line.startswith('[%s]' % section):\n            within_section = True\n            section_start = index\n        elif line.startswith('['):\n            if within_section:\n                if state == 'present':\n                    # insert missing option line at the end of the section\n                    for i in range(index, 0, -1):\n                        # search backwards for previous non-blank or non-comment line\n                        if not re.match(r'^[ \\t]*([#;].*)?$', ini_lines[i - 1]):\n                            ini_lines.insert(i, assignment_format % (option, value))\n                            changed = True\n                            break\n                elif state == 'absent' and not option:\n                    # remove the entire section\n                    del ini_lines[section_start:index]\n                    changed = True\n                break\n        else:\n            if within_section and option:\n                if state == 'present':\n                    # change the existing option line\n                    if match_opt(option, line):\n                        newline = assignment_format % (option, value)\n                        changed = ini_lines[index] != newline\n                        ini_lines[index] = newline\n                        if changed:\n                            # remove all possible option occurences from the rest of the section\n                            index = index + 1\n                            while index < len(ini_lines):\n                                line = ini_lines[index]\n                                if line.startswith('['):\n                                    break\n                                if match_active_opt(option, line):\n                                    del ini_lines[index]\n                                else:\n                                    index = index + 1\n                        break\n                else:\n                    # comment out the existing option line\n                    if match_active_opt(option, line):\n                        ini_lines[index] = '#%s' % ini_lines[index]\n                        changed = True\n                        break\n\n    # remove the fake section line\n    del ini_lines[-1:]\n\n    if not within_section and option and state == 'present':\n        ini_lines.append('[%s]\\n' % section)\n        ini_lines.append(assignment_format % (option, value))\n        changed = True\n\n\n    backup_file = None\n    if changed and not module.check_mode:\n        if backup:\n            backup_file = module.backup_local(filename)\n        ini_file = open(filename, 'w')\n        try:\n            ini_file.writelines(ini_lines)\n        finally:\n            ini_file.close()\n\n    return (changed, backup_file)\n\n# ==============================================================\n# main\n\ndef main():\n\n    module = AnsibleModule(\n        argument_spec = dict(\n            dest = dict(required=True),\n            section = dict(required=True),\n            option = dict(required=False),\n            value = dict(required=False),\n            backup = dict(default='no', type='bool'),\n            state = dict(default='present', choices=['present', 'absent']),\n            no_extra_spaces = dict(required=False, default=False, type='bool')\n        ),\n        add_file_common_args = True,\n        supports_check_mode = True\n    )\n\n    dest = os.path.expanduser(module.params['dest'])\n    section = module.params['section']\n    option = module.params['option']\n    value = module.params['value']\n    state = module.params['state']\n    backup = module.params['backup']\n    no_extra_spaces = module.params['no_extra_spaces']\n\n    (changed,backup_file) = do_ini(module, dest, section, option, value, state, backup, no_extra_spaces)\n\n    file_args = module.load_file_common_arguments(module.params)\n    changed = module.set_fs_attributes_if_different(file_args, changed)\n\n    results = { 'changed': changed, 'msg': \"OK\", 'dest': dest }\n    if backup_file is not None:\n        results['backup_file'] = backup_file\n\n    # Mission complete\n    module.exit_json(**results)\n\n# import module snippets\nfrom ansible.module_utils.basic import *\nif __name__ == '__main__':\n    main()\n", "path": "files/ini_file.py"}]}