ansible-core 2.19.2__py3-none-any.whl → 2.20.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ansible-core might be problematic. Click here for more details.

Files changed (202) hide show
  1. ansible/_internal/__init__.py +1 -4
  2. ansible/_internal/_ansiballz/_builder.py +1 -3
  3. ansible/_internal/_collection_proxy.py +7 -9
  4. ansible/_internal/_display_utils.py +145 -0
  5. ansible/_internal/_json/__init__.py +3 -4
  6. ansible/_internal/_templating/_engine.py +1 -1
  7. ansible/_internal/_templating/_jinja_plugins.py +1 -2
  8. ansible/_internal/_wrapt.py +105 -301
  9. ansible/cli/__init__.py +11 -10
  10. ansible/cli/adhoc.py +1 -2
  11. ansible/cli/arguments/option_helpers.py +1 -1
  12. ansible/cli/config.py +5 -6
  13. ansible/cli/doc.py +67 -67
  14. ansible/cli/galaxy.py +15 -24
  15. ansible/cli/inventory.py +0 -1
  16. ansible/cli/playbook.py +0 -1
  17. ansible/cli/pull.py +0 -1
  18. ansible/cli/scripts/ansible_connection_cli_stub.py +1 -1
  19. ansible/config/base.yml +1 -25
  20. ansible/config/manager.py +0 -2
  21. ansible/executor/play_iterator.py +42 -20
  22. ansible/executor/playbook_executor.py +0 -9
  23. ansible/executor/powershell/async_watchdog.ps1 +24 -4
  24. ansible/executor/task_executor.py +32 -22
  25. ansible/executor/task_queue_manager.py +1 -3
  26. ansible/galaxy/api.py +33 -80
  27. ansible/galaxy/collection/__init__.py +4 -17
  28. ansible/galaxy/dependency_resolution/dataclasses.py +0 -10
  29. ansible/galaxy/dependency_resolution/providers.py +1 -2
  30. ansible/galaxy/role.py +1 -33
  31. ansible/inventory/manager.py +2 -3
  32. ansible/keyword_desc.yml +0 -3
  33. ansible/module_utils/_internal/_datatag/__init__.py +2 -10
  34. ansible/module_utils/_internal/_no_six.py +86 -0
  35. ansible/module_utils/_text.py +28 -8
  36. ansible/module_utils/ansible_release.py +2 -2
  37. ansible/module_utils/basic.py +27 -24
  38. ansible/module_utils/common/_collections_compat.py +11 -2
  39. ansible/module_utils/common/collections.py +8 -3
  40. ansible/module_utils/common/dict_transformations.py +1 -2
  41. ansible/module_utils/common/network.py +4 -2
  42. ansible/module_utils/common/parameters.py +32 -41
  43. ansible/module_utils/common/text/converters.py +109 -23
  44. ansible/module_utils/common/text/formatters.py +6 -2
  45. ansible/module_utils/common/validation.py +11 -9
  46. ansible/module_utils/connection.py +8 -3
  47. ansible/module_utils/facts/hardware/linux.py +23 -7
  48. ansible/module_utils/facts/hardware/netbsd.py +1 -1
  49. ansible/module_utils/facts/hardware/sunos.py +2 -1
  50. ansible/module_utils/facts/packages.py +6 -2
  51. ansible/module_utils/facts/system/distribution.py +2 -1
  52. ansible/module_utils/facts/system/env.py +6 -3
  53. ansible/module_utils/facts/system/local.py +3 -1
  54. ansible/module_utils/parsing/convert_bool.py +6 -2
  55. ansible/module_utils/service.py +2 -3
  56. ansible/module_utils/six/__init__.py +11 -6
  57. ansible/module_utils/urls.py +6 -2
  58. ansible/module_utils/yumdnf.py +0 -5
  59. ansible/modules/apt.py +18 -13
  60. ansible/modules/apt_repository.py +1 -1
  61. ansible/modules/assemble.py +5 -9
  62. ansible/modules/blockinfile.py +39 -23
  63. ansible/modules/cron.py +26 -35
  64. ansible/modules/deb822_repository.py +83 -12
  65. ansible/modules/dnf.py +3 -7
  66. ansible/modules/dnf5.py +4 -6
  67. ansible/modules/expect.py +0 -3
  68. ansible/modules/find.py +1 -2
  69. ansible/modules/get_url.py +1 -1
  70. ansible/modules/git.py +4 -5
  71. ansible/modules/include_vars.py +1 -1
  72. ansible/modules/lineinfile.py +71 -63
  73. ansible/modules/package_facts.py +1 -1
  74. ansible/modules/pip.py +8 -2
  75. ansible/modules/replace.py +6 -6
  76. ansible/modules/service.py +3 -4
  77. ansible/modules/stat.py +20 -0
  78. ansible/modules/uri.py +9 -10
  79. ansible/modules/user.py +1 -2
  80. ansible/modules/wait_for.py +2 -2
  81. ansible/modules/wait_for_connection.py +2 -1
  82. ansible/modules/yum_repository.py +1 -16
  83. ansible/parsing/dataloader.py +24 -31
  84. ansible/parsing/mod_args.py +3 -0
  85. ansible/parsing/vault/__init__.py +1 -2
  86. ansible/playbook/base.py +8 -56
  87. ansible/playbook/block.py +0 -60
  88. ansible/playbook/collectionsearch.py +1 -2
  89. ansible/playbook/handler.py +1 -7
  90. ansible/playbook/helpers.py +0 -7
  91. ansible/playbook/included_file.py +1 -1
  92. ansible/playbook/play.py +103 -37
  93. ansible/playbook/play_context.py +4 -0
  94. ansible/playbook/role/__init__.py +10 -65
  95. ansible/playbook/role/definition.py +3 -4
  96. ansible/playbook/role/include.py +2 -3
  97. ansible/playbook/role/metadata.py +1 -12
  98. ansible/playbook/role/requirement.py +1 -2
  99. ansible/playbook/role_include.py +1 -2
  100. ansible/playbook/taggable.py +16 -5
  101. ansible/playbook/task.py +51 -55
  102. ansible/plugins/action/__init__.py +20 -19
  103. ansible/plugins/action/add_host.py +1 -2
  104. ansible/plugins/action/fetch.py +2 -4
  105. ansible/plugins/action/group_by.py +1 -2
  106. ansible/plugins/action/include_vars.py +20 -22
  107. ansible/plugins/action/script.py +1 -3
  108. ansible/plugins/action/template.py +1 -2
  109. ansible/plugins/action/uri.py +4 -2
  110. ansible/plugins/cache/__init__.py +1 -0
  111. ansible/plugins/callback/__init__.py +13 -6
  112. ansible/plugins/connection/__init__.py +3 -7
  113. ansible/plugins/connection/local.py +2 -3
  114. ansible/plugins/connection/psrp.py +0 -2
  115. ansible/plugins/connection/ssh.py +2 -7
  116. ansible/plugins/connection/winrm.py +0 -2
  117. ansible/plugins/doc_fragments/result_format_callback.py +15 -0
  118. ansible/plugins/filter/core.py +4 -5
  119. ansible/plugins/filter/encryption.py +3 -27
  120. ansible/plugins/filter/mathstuff.py +1 -2
  121. ansible/plugins/filter/to_nice_yaml.yml +31 -3
  122. ansible/plugins/filter/to_yaml.yml +29 -12
  123. ansible/plugins/inventory/__init__.py +1 -2
  124. ansible/plugins/inventory/script.py +2 -1
  125. ansible/plugins/inventory/toml.py +3 -6
  126. ansible/plugins/inventory/yaml.py +1 -2
  127. ansible/plugins/list.py +10 -3
  128. ansible/plugins/loader.py +6 -6
  129. ansible/plugins/lookup/password.py +1 -2
  130. ansible/plugins/lookup/subelements.py +2 -3
  131. ansible/plugins/lookup/url.py +1 -1
  132. ansible/plugins/lookup/varnames.py +1 -2
  133. ansible/plugins/shell/__init__.py +9 -4
  134. ansible/plugins/shell/powershell.py +8 -24
  135. ansible/plugins/strategy/__init__.py +6 -3
  136. ansible/plugins/test/core.py +4 -1
  137. ansible/plugins/test/regex.yml +18 -6
  138. ansible/release.py +2 -2
  139. ansible/template/__init__.py +3 -7
  140. ansible/utils/collection_loader/_collection_config.py +5 -0
  141. ansible/utils/collection_loader/_collection_finder.py +11 -14
  142. ansible/utils/context_objects.py +7 -4
  143. ansible/utils/display.py +28 -167
  144. ansible/utils/encrypt.py +0 -5
  145. ansible/utils/helpers.py +6 -2
  146. ansible/utils/jsonrpc.py +7 -3
  147. ansible/utils/plugin_docs.py +49 -38
  148. ansible/utils/ssh_functions.py +0 -19
  149. ansible/utils/unsafe_proxy.py +7 -7
  150. ansible/vars/clean.py +2 -3
  151. ansible/vars/manager.py +27 -20
  152. ansible/vars/plugins.py +1 -31
  153. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/METADATA +3 -3
  154. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/RECORD +200 -200
  155. ansible_test/_data/completion/docker.txt +7 -7
  156. ansible_test/_data/completion/network.txt +0 -1
  157. ansible_test/_data/completion/remote.txt +4 -4
  158. ansible_test/_data/requirements/ansible-test.txt +1 -1
  159. ansible_test/_data/requirements/sanity.changelog.txt +1 -1
  160. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  161. ansible_test/_data/requirements/sanity.pylint.txt +4 -4
  162. ansible_test/_internal/cache.py +2 -5
  163. ansible_test/_internal/cli/compat.py +1 -1
  164. ansible_test/_internal/commands/coverage/combine.py +1 -3
  165. ansible_test/_internal/commands/integration/__init__.py +3 -7
  166. ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
  167. ansible_test/_internal/commands/integration/coverage.py +1 -3
  168. ansible_test/_internal/commands/integration/filters.py +5 -10
  169. ansible_test/_internal/commands/sanity/validate_modules.py +1 -5
  170. ansible_test/_internal/commands/units/__init__.py +1 -13
  171. ansible_test/_internal/completion.py +2 -5
  172. ansible_test/_internal/config.py +2 -7
  173. ansible_test/_internal/coverage_util.py +1 -1
  174. ansible_test/_internal/delegation.py +2 -0
  175. ansible_test/_internal/docker_util.py +1 -1
  176. ansible_test/_internal/host_profiles.py +6 -11
  177. ansible_test/_internal/provider/__init__.py +2 -5
  178. ansible_test/_internal/provisioning.py +2 -5
  179. ansible_test/_internal/pypi_proxy.py +1 -1
  180. ansible_test/_internal/target.py +2 -6
  181. ansible_test/_internal/thread.py +1 -4
  182. ansible_test/_internal/util.py +9 -14
  183. ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +14 -19
  184. ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +30 -27
  185. ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +31 -18
  186. ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +1 -2
  187. ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +59 -71
  188. ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -2
  189. ansible_test/_util/target/cli/ansible_test_cli_stub.py +4 -2
  190. ansible_test/_util/target/common/constants.py +2 -2
  191. ansible_test/_util/target/setup/bootstrap.sh +0 -6
  192. ansible/utils/py3compat.py +0 -27
  193. ansible_test/_data/pytest/config/legacy.ini +0 -4
  194. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/WHEEL +0 -0
  195. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/entry_points.txt +0 -0
  196. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/COPYING +0 -0
  197. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  198. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  199. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  200. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  201. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  202. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/top_level.txt +0 -0
ansible/modules/dnf.py CHANGED
@@ -213,13 +213,6 @@ options:
213
213
  type: bool
214
214
  default: "no"
215
215
  version_added: "2.7"
216
- install_repoquery:
217
- description:
218
- - This is effectively a no-op in DNF as it is not needed with DNF.
219
- - This option is deprecated and will be removed in ansible-core 2.20.
220
- type: bool
221
- default: "yes"
222
- version_added: "2.7"
223
216
  download_only:
224
217
  description:
225
218
  - Only download the packages, do not install them.
@@ -544,6 +537,9 @@ class DnfModule(YumDnf):
544
537
  conf.sslverify = sslverify
545
538
 
546
539
  # Set installroot
540
+ if not os.path.isdir(installroot):
541
+ self.module.fail_json(msg=f"Installroot {installroot} must be a directory")
542
+
547
543
  conf.installroot = installroot
548
544
 
549
545
  # Load substitutions from the filesystem
ansible/modules/dnf5.py CHANGED
@@ -182,12 +182,6 @@ options:
182
182
  using this in combination with wildcard characters in O(name) may result in an unexpected results.
183
183
  type: bool
184
184
  default: "no"
185
- install_repoquery:
186
- description:
187
- - This is effectively a no-op in DNF as it is not needed with DNF.
188
- - This option is deprecated and will be removed in ansible-core 2.20.
189
- type: bool
190
- default: "yes"
191
185
  download_only:
192
186
  description:
193
187
  - Only download the packages, do not install them.
@@ -601,6 +595,10 @@ class Dnf5Module(YumDnf):
601
595
  conf.localpkg_gpgcheck = not self.disable_gpg_check
602
596
  conf.sslverify = self.sslverify
603
597
  conf.clean_requirements_on_remove = self.autoremove
598
+
599
+ if not os.path.isdir(self.installroot):
600
+ self.module.fail_json(msg=f"Installroot {self.installroot} must be a directory")
601
+
604
602
  conf.installroot = self.installroot
605
603
  conf.use_host_config = True # needed for installroot
606
604
  conf.cacheonly = "all" if self.cacheonly else "none"
ansible/modules/expect.py CHANGED
@@ -249,9 +249,6 @@ def main():
249
249
  end_date = datetime.datetime.now()
250
250
  delta = end_date - start_date
251
251
 
252
- if b_out is None:
253
- b_out = b''
254
-
255
252
  result = dict(
256
253
  cmd=args,
257
254
  stdout=to_native(b_out).rstrip('\r\n'),
ansible/modules/find.py CHANGED
@@ -291,7 +291,6 @@ import time
291
291
 
292
292
  from ansible.module_utils.common.text.converters import to_text, to_native
293
293
  from ansible.module_utils.basic import AnsibleModule
294
- from ansible.module_utils.six import string_types
295
294
 
296
295
 
297
296
  class _Object:
@@ -496,7 +495,7 @@ def main():
496
495
 
497
496
  params = module.params
498
497
 
499
- if params['mode'] and not isinstance(params['mode'], string_types):
498
+ if params['mode'] and not isinstance(params['mode'], str):
500
499
  module.fail_json(
501
500
  msg="argument 'mode' is not a string and conversion is not allowed, value is of type %s" % params['mode'].__class__.__name__
502
501
  )
@@ -374,9 +374,9 @@ import shutil
374
374
  import tempfile
375
375
 
376
376
  from datetime import datetime, timezone
377
+ from urllib.parse import urlsplit
377
378
 
378
379
  from ansible.module_utils.basic import AnsibleModule
379
- from ansible.module_utils.six.moves.urllib.parse import urlsplit
380
380
  from ansible.module_utils.common.text.converters import to_native
381
381
  from ansible.module_utils.urls import fetch_url, url_argument_spec
382
382
 
ansible/modules/git.py CHANGED
@@ -343,7 +343,6 @@ from ansible.module_utils.common.text.converters import to_native, to_text
343
343
  from ansible.module_utils.basic import AnsibleModule
344
344
  from ansible.module_utils.common.locale import get_best_parsable_locale
345
345
  from ansible.module_utils.common.process import get_bin_path
346
- from ansible.module_utils.six import b, string_types
347
346
 
348
347
 
349
348
  def relocate_repo(module, result, repo_dir, old_repo_dir, worktree_dir):
@@ -443,12 +442,12 @@ def write_ssh_wrapper(module):
443
442
  fd, wrapper_path = tempfile.mkstemp()
444
443
 
445
444
  # use existing git_ssh/ssh_command, fallback to 'ssh'
446
- template = b("""#!/bin/sh
445
+ template = """#!/bin/sh
447
446
  %s $GIT_SSH_OPTS "$@"
448
- """ % os.environ.get('GIT_SSH', os.environ.get('GIT_SSH_COMMAND', 'ssh')))
447
+ """ % os.environ.get('GIT_SSH', os.environ.get('GIT_SSH_COMMAND', 'ssh'))
449
448
 
450
449
  # write it
451
- with os.fdopen(fd, 'w+b') as fh:
450
+ with os.fdopen(fd, 'w') as fh:
452
451
  fh.write(template)
453
452
 
454
453
  # set execute
@@ -1257,7 +1256,7 @@ def main():
1257
1256
 
1258
1257
  # evaluate and set the umask before doing anything else
1259
1258
  if umask is not None:
1260
- if not isinstance(umask, string_types):
1259
+ if not isinstance(umask, str):
1261
1260
  module.fail_json(msg="umask must be defined as a quoted octal integer")
1262
1261
  try:
1263
1262
  umask = int(umask, 8)
@@ -66,7 +66,7 @@ options:
66
66
  description:
67
67
  - Ignore unknown file extensions within the directory.
68
68
  - This allows users to specify a directory containing vars files that are intermingled with non-vars files extension types
69
- (e.g. a directory with a README in it and vars files).
69
+ (for example, a directory with a README in it and vars files).
70
70
  type: bool
71
71
  default: no
72
72
  version_added: "2.7"
@@ -123,6 +123,13 @@ options:
123
123
  type: bool
124
124
  default: no
125
125
  version_added: "2.5"
126
+ encoding:
127
+ description:
128
+ - The character set in which the target file is encoded.
129
+ - For a list of available built-in encodings, see U(https://docs.python.org/3/library/codecs.html#standard-encodings)
130
+ type: str
131
+ default: utf-8
132
+ version_added: "2.20"
126
133
  extends_documentation_fragment:
127
134
  - action_common_attributes
128
135
  - action_common_attributes.files
@@ -250,11 +257,11 @@ from ansible.module_utils.basic import AnsibleModule
250
257
  from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
251
258
 
252
259
 
253
- def write_changes(module, b_lines, dest):
260
+ def write_changes(module, lines, dest, encoding=None):
254
261
 
255
262
  tmpfd, tmpfile = tempfile.mkstemp(dir=module.tmpdir)
256
- with os.fdopen(tmpfd, 'wb') as f:
257
- f.writelines(b_lines)
263
+ with os.fdopen(tmpfd, 'w', encoding=encoding) as f:
264
+ f.writelines(lines)
258
265
 
259
266
  validate = module.params.get('validate', None)
260
267
  valid = not validate
@@ -293,6 +300,7 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
293
300
  'before_header': '%s (content)' % dest,
294
301
  'after_header': '%s (content)' % dest}
295
302
 
303
+ encoding = module.params.get('encoding', None)
296
304
  b_dest = to_bytes(dest, errors='surrogate_or_strict')
297
305
  if not os.path.exists(b_dest):
298
306
  if not create:
@@ -304,30 +312,29 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
304
312
  except Exception as e:
305
313
  module.fail_json(msg='Error creating %s (%s)' % (to_text(b_destpath), to_text(e)))
306
314
 
307
- b_lines = []
315
+ lines = []
308
316
  else:
309
- with open(b_dest, 'rb') as f:
310
- b_lines = f.readlines()
317
+ with open(b_dest, 'r', encoding=encoding) as f:
318
+ lines = f.readlines()
311
319
 
312
320
  if module._diff:
313
- diff['before'] = to_native(b''.join(b_lines))
321
+ diff['before'] = ''.join(lines)
314
322
 
315
323
  if regexp is not None:
316
- bre_m = re.compile(to_bytes(regexp, errors='surrogate_or_strict'))
324
+ re_m = re.compile(regexp)
317
325
 
318
326
  if insertafter not in (None, 'BOF', 'EOF'):
319
- bre_ins = re.compile(to_bytes(insertafter, errors='surrogate_or_strict'))
327
+ re_ins = re.compile(insertafter)
320
328
  elif insertbefore not in (None, 'BOF'):
321
- bre_ins = re.compile(to_bytes(insertbefore, errors='surrogate_or_strict'))
329
+ re_ins = re.compile(insertbefore)
322
330
  else:
323
- bre_ins = None
331
+ re_ins = None
324
332
 
325
333
  # index[0] is the line num where regexp has been found
326
334
  # index[1] is the line num where insertafter/insertbefore has been found
327
335
  index = [-1, -1]
328
336
  match = None
329
337
  exact_line_match = False
330
- b_line = to_bytes(line, errors='surrogate_or_strict')
331
338
 
332
339
  # The module's doc says
333
340
  # "If regular expressions are passed to both regexp and
@@ -339,8 +346,8 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
339
346
  # Given the above:
340
347
  # 1. First check that there is no match for regexp:
341
348
  if regexp is not None:
342
- for lineno, b_cur_line in enumerate(b_lines):
343
- match_found = bre_m.search(b_cur_line)
349
+ for lineno, cur_line in enumerate(lines):
350
+ match_found = re_m.search(cur_line)
344
351
  if match_found:
345
352
  index[0] = lineno
346
353
  match = match_found
@@ -349,8 +356,8 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
349
356
 
350
357
  # 2. Second check that there is no match for search_string:
351
358
  if search_string is not None:
352
- for lineno, b_cur_line in enumerate(b_lines):
353
- match_found = to_bytes(search_string, errors='surrogate_or_strict') in b_cur_line
359
+ for lineno, cur_line in enumerate(lines):
360
+ match_found = search_string in cur_line
354
361
  if match_found:
355
362
  index[0] = lineno
356
363
  match = match_found
@@ -360,12 +367,12 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
360
367
  # 3. When no match found on the previous step,
361
368
  # parse for searching insertafter/insertbefore:
362
369
  if not match:
363
- for lineno, b_cur_line in enumerate(b_lines):
364
- if b_line == b_cur_line.rstrip(b'\r\n'):
370
+ for lineno, cur_line in enumerate(lines):
371
+ if line == cur_line.rstrip('\r\n'):
365
372
  index[0] = lineno
366
373
  exact_line_match = True
367
374
 
368
- elif bre_ins is not None and bre_ins.search(b_cur_line):
375
+ elif re_ins is not None and re_ins.search(cur_line):
369
376
  if insertafter:
370
377
  # + 1 for the next line
371
378
  index[1] = lineno + 1
@@ -380,17 +387,17 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
380
387
 
381
388
  msg = ''
382
389
  changed = False
383
- b_linesep = to_bytes(os.linesep, errors='surrogate_or_strict')
390
+ linesep = os.linesep
384
391
  # Exact line or Regexp matched a line in the file
385
392
  if index[0] != -1:
386
393
  if backrefs and match:
387
- b_new_line = match.expand(b_line)
394
+ new_line = match.expand(line)
388
395
  else:
389
396
  # Don't do backref expansion if not asked.
390
- b_new_line = b_line
397
+ new_line = line
391
398
 
392
- if not b_new_line.endswith(b_linesep):
393
- b_new_line += b_linesep
399
+ if not new_line.endswith(linesep):
400
+ new_line += linesep
394
401
 
395
402
  # If no regexp or search_string was given and no line match is found anywhere in the file,
396
403
  # insert the line appropriately if using insertbefore or insertafter
@@ -400,18 +407,18 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
400
407
  if insertafter and insertafter != 'EOF':
401
408
  # Ensure there is a line separator after the found string
402
409
  # at the end of the file.
403
- if b_lines and not b_lines[-1][-1:] in (b'\n', b'\r'):
404
- b_lines[-1] = b_lines[-1] + b_linesep
410
+ if lines and not lines[-1][-1:] in ('\n', '\r'):
411
+ lines[-1] = lines[-1] + linesep
405
412
 
406
413
  # If the line to insert after is at the end of the file
407
414
  # use the appropriate index value.
408
- if len(b_lines) == index[1]:
409
- if b_lines[index[1] - 1].rstrip(b'\r\n') != b_line:
410
- b_lines.append(b_line + b_linesep)
415
+ if len(lines) == index[1]:
416
+ if lines[index[1] - 1].rstrip('\r\n') != line:
417
+ lines.append(line + linesep)
411
418
  msg = 'line added'
412
419
  changed = True
413
- elif b_lines[index[1]].rstrip(b'\r\n') != b_line:
414
- b_lines.insert(index[1], b_line + b_linesep)
420
+ elif lines[index[1]].rstrip('\r\n') != line:
421
+ lines.insert(index[1], line + linesep)
415
422
  msg = 'line added'
416
423
  changed = True
417
424
 
@@ -419,18 +426,18 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
419
426
  # If the line to insert before is at the beginning of the file
420
427
  # use the appropriate index value.
421
428
  if index[1] <= 0:
422
- if b_lines[index[1]].rstrip(b'\r\n') != b_line:
423
- b_lines.insert(index[1], b_line + b_linesep)
429
+ if lines[index[1]].rstrip('\r\n') != line:
430
+ lines.insert(index[1], line + linesep)
424
431
  msg = 'line added'
425
432
  changed = True
426
433
 
427
- elif b_lines[index[1] - 1].rstrip(b'\r\n') != b_line:
428
- b_lines.insert(index[1], b_line + b_linesep)
434
+ elif lines[index[1] - 1].rstrip('\r\n') != line:
435
+ lines.insert(index[1], line + linesep)
429
436
  msg = 'line added'
430
437
  changed = True
431
438
 
432
- elif b_lines[index[0]] != b_new_line:
433
- b_lines[index[0]] = b_new_line
439
+ elif lines[index[0]] != new_line:
440
+ lines[index[0]] = new_line
434
441
  msg = 'line replaced'
435
442
  changed = True
436
443
 
@@ -440,7 +447,7 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
440
447
  pass
441
448
  # Add it to the beginning of the file
442
449
  elif insertbefore == 'BOF' or insertafter == 'BOF':
443
- b_lines.insert(0, b_line + b_linesep)
450
+ lines.insert(0, line + linesep)
444
451
  msg = 'line added'
445
452
  changed = True
446
453
  # Add it to the end of the file if requested or
@@ -449,10 +456,10 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
449
456
  elif insertafter == 'EOF' or index[1] == -1:
450
457
 
451
458
  # If the file is not empty then ensure there's a newline before the added line
452
- if b_lines and not b_lines[-1][-1:] in (b'\n', b'\r'):
453
- b_lines.append(b_linesep)
459
+ if lines and not lines[-1][-1:] in ('\n', '\r'):
460
+ lines.append(linesep)
454
461
 
455
- b_lines.append(b_line + b_linesep)
462
+ lines.append(line + linesep)
456
463
  msg = 'line added'
457
464
  changed = True
458
465
 
@@ -460,30 +467,30 @@ def present(module, dest, regexp, search_string, line, insertafter, insertbefore
460
467
 
461
468
  # Don't insert the line if it already matches at the index.
462
469
  # If the line to insert after is at the end of the file use the appropriate index value.
463
- if len(b_lines) == index[1]:
464
- if b_lines[index[1] - 1].rstrip(b'\r\n') != b_line:
465
- b_lines.append(b_line + b_linesep)
470
+ if len(lines) == index[1]:
471
+ if lines[index[1] - 1].rstrip('\r\n') != line:
472
+ lines.append(line + linesep)
466
473
  msg = 'line added'
467
474
  changed = True
468
- elif b_line != b_lines[index[1]].rstrip(b'\n\r'):
469
- b_lines.insert(index[1], b_line + b_linesep)
475
+ elif line != lines[index[1]].rstrip('\n\r'):
476
+ lines.insert(index[1], line + linesep)
470
477
  msg = 'line added'
471
478
  changed = True
472
479
 
473
480
  # insert matched, but not the regexp or search_string
474
481
  else:
475
- b_lines.insert(index[1], b_line + b_linesep)
482
+ lines.insert(index[1], line + linesep)
476
483
  msg = 'line added'
477
484
  changed = True
478
485
 
479
486
  if module._diff:
480
- diff['after'] = to_native(b''.join(b_lines))
487
+ diff['after'] = ''.join(lines)
481
488
 
482
489
  backupdest = ""
483
490
  if changed and not module.check_mode:
484
491
  if backup and os.path.exists(b_dest):
485
492
  backupdest = module.backup_local(dest)
486
- write_changes(module, b_lines, dest)
493
+ write_changes(module, lines, dest, encoding)
487
494
 
488
495
  if module.check_mode and not os.path.exists(b_dest):
489
496
  module.exit_json(changed=changed, msg=msg, backup=backupdest, diff=diff)
@@ -510,40 +517,40 @@ def absent(module, dest, regexp, search_string, line, backup):
510
517
  'before_header': '%s (content)' % dest,
511
518
  'after_header': '%s (content)' % dest}
512
519
 
513
- with open(b_dest, 'rb') as f:
514
- b_lines = f.readlines()
520
+ encoding = module.params['encoding']
521
+
522
+ with open(b_dest, 'r', encoding=encoding) as f:
523
+ lines = f.readlines()
515
524
 
516
525
  if module._diff:
517
- diff['before'] = to_native(b''.join(b_lines))
526
+ diff['before'] = ''.join(lines)
518
527
 
519
528
  if regexp is not None:
520
- bre_c = re.compile(to_bytes(regexp, errors='surrogate_or_strict'))
529
+ re_c = re.compile(regexp)
521
530
  found = []
522
531
 
523
- b_line = to_bytes(line, errors='surrogate_or_strict')
524
-
525
- def matcher(b_cur_line):
532
+ def matcher(cur_line):
526
533
  if regexp is not None:
527
- match_found = bre_c.search(b_cur_line)
534
+ match_found = re_c.search(cur_line)
528
535
  elif search_string is not None:
529
- match_found = to_bytes(search_string, errors='surrogate_or_strict') in b_cur_line
536
+ match_found = search_string in cur_line
530
537
  else:
531
- match_found = b_line == b_cur_line.rstrip(b'\r\n')
538
+ match_found = line == cur_line.rstrip('\r\n')
532
539
  if match_found:
533
- found.append(b_cur_line)
540
+ found.append(cur_line)
534
541
  return not match_found
535
542
 
536
- b_lines = [l for l in b_lines if matcher(l)]
543
+ lines = [l for l in lines if matcher(l)]
537
544
  changed = len(found) > 0
538
545
 
539
546
  if module._diff:
540
- diff['after'] = to_native(b''.join(b_lines))
547
+ diff['after'] = ''.join(lines)
541
548
 
542
549
  backupdest = ""
543
550
  if changed and not module.check_mode:
544
551
  if backup:
545
552
  backupdest = module.backup_local(dest)
546
- write_changes(module, b_lines, dest)
553
+ write_changes(module, lines, dest, encoding)
547
554
 
548
555
  if changed:
549
556
  msg = "%s line(s) removed" % len(found)
@@ -567,6 +574,7 @@ def main():
567
574
  regexp=dict(type='str', aliases=['regex']),
568
575
  search_string=dict(type='str'),
569
576
  line=dict(type='str', aliases=['value']),
577
+ encoding=dict(type='str', default='utf-8'),
570
578
  insertafter=dict(type='str'),
571
579
  insertbefore=dict(type='str'),
572
580
  backrefs=dict(type='bool', default=False),
@@ -18,7 +18,7 @@ options:
18
18
  This is a list and can support multiple package managers per system, since version 2.8.
19
19
  - The V(portage) and V(pkg) options were added in version 2.8.
20
20
  - The V(apk) option was added in version 2.11.
21
- - The V(pkg_info)' option was added in version 2.13.
21
+ - The V(pkg_info) option was added in version 2.13.
22
22
  - Aliases were added in 2.18, to support using C(manager={{ansible_facts['pkg_mgr']}})
23
23
  default: ['auto']
24
24
  choices:
ansible/modules/pip.py CHANGED
@@ -608,7 +608,7 @@ def setup_virtualenv(module, env, chdir, out, err):
608
608
  err += err_venv
609
609
  if rc != 0:
610
610
  _fail(module, cmd, out, err)
611
- return out, err
611
+ return out, err, cmd
612
612
 
613
613
 
614
614
  class Package:
@@ -741,11 +741,12 @@ def main():
741
741
 
742
742
  err = ''
743
743
  out = ''
744
+ venv_cmd = ''
744
745
 
745
746
  if env:
746
747
  if not os.path.exists(os.path.join(env, 'bin', 'activate')):
747
748
  venv_created = True
748
- out, err = setup_virtualenv(module, env, chdir, out, err)
749
+ out, err, venv_cmd = setup_virtualenv(module, env, chdir, out, err)
749
750
  py_bin = os.path.join(env, 'bin', 'python')
750
751
  else:
751
752
  py_bin = module.params['executable'] or sys.executable
@@ -811,6 +812,11 @@ def main():
811
812
  cmd.extend(to_native(p) for p in packages)
812
813
  elif requirements:
813
814
  cmd.extend(['-r', requirements])
815
+ elif venv_created and not name and not requirements:
816
+ # ONLY creating an empty venv
817
+ module.exit_json(changed=venv_created, cmd=venv_cmd, name=name, version=version,
818
+ state=state, requirements=requirements, virtualenv=env,
819
+ stdout=out, stderr=err)
814
820
  else:
815
821
  module.warn("No valid name or requirements file found.")
816
822
  module.exit_json(changed=False)
@@ -183,14 +183,14 @@ import os
183
183
  import re
184
184
  import tempfile
185
185
 
186
- from ansible.module_utils.common.text.converters import to_text, to_bytes
186
+ from ansible.module_utils.common.text.converters import to_text
187
187
  from ansible.module_utils.basic import AnsibleModule
188
188
 
189
189
 
190
- def write_changes(module, contents, path):
190
+ def write_changes(module, contents, path, encoding='utf-8'):
191
191
 
192
192
  tmpfd, tmpfile = tempfile.mkstemp(dir=module.tmpdir)
193
- with os.fdopen(tmpfd, 'wb') as f:
193
+ with os.fdopen(tmpfd, 'w', encoding=encoding) as f:
194
194
  f.write(contents)
195
195
 
196
196
  validate = module.params.get('validate', None)
@@ -254,8 +254,8 @@ def main():
254
254
  module.fail_json(rc=257, msg='Path %s does not exist !' % path)
255
255
  else:
256
256
  try:
257
- with open(path, 'rb') as f:
258
- contents = to_text(f.read(), errors='surrogate_or_strict', encoding=encoding)
257
+ with open(path, 'r', encoding=encoding) as f:
258
+ contents = f.read()
259
259
  except OSError as ex:
260
260
  raise Exception(f"Unable to read the contents of {path!r}.") from ex
261
261
 
@@ -307,7 +307,7 @@ def main():
307
307
  res_args['backup_file'] = module.backup_local(path)
308
308
  # We should always follow symlinks so that we change the real file
309
309
  path = os.path.realpath(path)
310
- write_changes(module, to_bytes(result[0], encoding=encoding), path)
310
+ write_changes(module, result[0], path, encoding=encoding)
311
311
 
312
312
  res_args['msg'], res_args['changed'] = check_file_attrs(module, changed, msg)
313
313
  module.exit_json(**res_args)
@@ -180,7 +180,6 @@ from ansible.module_utils.basic import AnsibleModule
180
180
  from ansible.module_utils.common.locale import get_best_parsable_locale
181
181
  from ansible.module_utils.common.sys_info import get_platform_subclass
182
182
  from ansible.module_utils.service import fail_if_missing, is_systemd_managed
183
- from ansible.module_utils.six import b
184
183
 
185
184
 
186
185
  class Service(object):
@@ -292,8 +291,8 @@ class Service(object):
292
291
  # chkconfig localizes messages and we're screen scraping so make
293
292
  # sure we use the C locale
294
293
  p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=lang_env, preexec_fn=lambda: os.close(pipe[1]))
295
- stdout = b("")
296
- stderr = b("")
294
+ stdout = b""
295
+ stderr = b""
297
296
  fds = [p.stdout, p.stderr]
298
297
  # Wait for all output, or until the main process is dead and its output is done.
299
298
  while fds:
@@ -322,7 +321,7 @@ class Service(object):
322
321
  os.close(pipe[1])
323
322
  os.waitpid(pid, 0)
324
323
  # Wait for data from daemon process and process it.
325
- data = b("")
324
+ data = b""
326
325
  while True:
327
326
  rfd, wfd, efd = select.select([pipe[0]], [], [pipe[0]])
328
327
  if pipe[0] in rfd:
ansible/modules/stat.py CHANGED
@@ -44,6 +44,14 @@ options:
44
44
  version_added: "2.3"
45
45
  get_checksum:
46
46
  version_added: "1.8"
47
+ get_selinux_context:
48
+ description:
49
+ - Get file SELinux context in a list V([user, role, type, range]),
50
+ and will get V([None, None, None, None]) if it is not possible to retrieve the context,
51
+ either because it does not exist or some other issue.
52
+ type: bool
53
+ default: no
54
+ version_added: '2.20'
47
55
  extends_documentation_fragment:
48
56
  - action_common_attributes
49
57
  - checksum_common
@@ -346,6 +354,12 @@ stat:
346
354
  type: list
347
355
  sample: [ immutable, extent ]
348
356
  version_added: 2.3
357
+ selinux_context:
358
+ description: The SELinux context of a path
359
+ returned: success, path exists and user can execute the path
360
+ type: list
361
+ sample: [ user, role, type, range ]
362
+ version_added: '2.20'
349
363
  version:
350
364
  description: The version/generation attribute of a file according to the filesystem
351
365
  returned: success, path exists, user can execute the path, lsattr is available and filesystem supports
@@ -434,6 +448,7 @@ def main():
434
448
  get_checksum=dict(type='bool', default=True),
435
449
  get_mime=dict(type='bool', default=True, aliases=['mime', 'mime_type', 'mime-type']),
436
450
  get_attributes=dict(type='bool', default=True, aliases=['attr', 'attributes']),
451
+ get_selinux_context=dict(type='bool', default=False),
437
452
  checksum_algorithm=dict(type='str', default='sha1',
438
453
  choices=['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512'],
439
454
  aliases=['checksum', 'checksum_algo']),
@@ -448,6 +463,7 @@ def main():
448
463
  get_attr = module.params.get('get_attributes')
449
464
  get_checksum = module.params.get('get_checksum')
450
465
  checksum_algorithm = module.params.get('checksum_algorithm')
466
+ get_selinux_context = module.params.get('get_selinux_context')
451
467
 
452
468
  # main stat data
453
469
  try:
@@ -515,6 +531,10 @@ def main():
515
531
  if x in out:
516
532
  output[x] = out[x]
517
533
 
534
+ # try to get SELinux context
535
+ if get_selinux_context:
536
+ output['selinux_context'] = module.selinux_context(b_path)
537
+
518
538
  module.exit_json(changed=False, stat=output)
519
539
 
520
540