Skip to content

Commit 9dee946

Browse files
authored
Merge pull request #1264 from moreati/ansible-2.19
Ansible 2.19 (review/sandbox)
2 parents 8ed4e9d + 0187418 commit 9dee946

20 files changed

+222
-31
lines changed

.github/workflows/tests.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,16 @@ jobs:
177177
- name: Ans_313_11
178178
python_version: '3.13'
179179
tox_env: py313-mode_ansible-ansible11
180+
- name: Ans_313_12
181+
python_version: '3.13'
182+
tox_env: py313-mode_ansible-ansible12
180183

181184
- name: Van_313_11
182185
python_version: '3.13'
183186
tox_env: py313-mode_ansible-ansible11-strategy_linear
187+
- name: Van_313_12
188+
python_version: '3.13'
189+
tox_env: py313-mode_ansible-ansible12-strategy_linear
184190

185191
- name: Mito_313
186192
python_version: '3.13'
@@ -273,6 +279,12 @@ jobs:
273279
- name: Van_313_11
274280
tox_env: py313-mode_localhost-ansible11-strategy_linear
275281

282+
- name: Loc_313_12
283+
tox_env: py313-mode_localhost-ansible12
284+
285+
- name: Van_313_12
286+
tox_env: py313-mode_localhost-ansible12-strategy_linear
287+
276288
steps:
277289
- uses: actions/checkout@v4
278290
- uses: actions/setup-python@v5

ansible_mitogen/connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ def _stack_from_spec(self, spec, stack=(), seen_names=()):
767767
C.BECOME_ALLOW_SAME_USER):
768768
stack += (CONNECTION_METHOD[spec.become_method()](spec),)
769769

770-
return stack
770+
return ansible_mitogen.utils.unsafe.cast(stack)
771771

772772
def _build_stack(self):
773773
"""

ansible_mitogen/mixins.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -402,15 +402,17 @@ def _execute_module(self, module_name=None, module_args=None, tmp=None,
402402
if not self._mitogen_rediscovered_interpreter:
403403
result['ansible_facts'][self._discovered_interpreter_key] = self._discovered_interpreter
404404

405-
if self._discovery_warnings:
405+
discovery_warnings = getattr(self, '_discovery_warnings', [])
406+
if discovery_warnings:
406407
if result.get('warnings') is None:
407408
result['warnings'] = []
408-
result['warnings'].extend(self._discovery_warnings)
409+
result['warnings'].extend(discovery_warnings)
409410

410-
if self._discovery_deprecation_warnings:
411+
discovery_deprecation_warnings = getattr(self, '_discovery_deprecation_warnings', [])
412+
if discovery_deprecation_warnings:
411413
if result.get('deprecations') is None:
412414
result['deprecations'] = []
413-
result['deprecations'].extend(self._discovery_deprecation_warnings)
415+
result['deprecations'].extend(discovery_deprecation_warnings)
414416

415417
return ansible.utils.unsafe_proxy.wrap_var(result)
416418

@@ -429,7 +431,10 @@ def _postprocess_response(self, result):
429431
"stderr": "stderr data"
430432
}
431433
"""
432-
data = self._parse_returned_data(result)
434+
if ansible_mitogen.utils.ansible_version[:2] >= (2, 19):
435+
data = self._parse_returned_data(result, profile='legacy')
436+
else:
437+
data = self._parse_returned_data(result)
433438

434439
# Cutpasted from the base implementation.
435440
if 'stdout' in data and 'stdout_lines' not in data:

ansible_mitogen/planner.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ def get_kwargs(self, **kwargs):
170170
"""
171171
binding = self._inv.connection.get_binding()
172172

173+
kwargs = ansible_mitogen.utils.unsafe.cast(kwargs)
173174
new = dict((mitogen.core.UnicodeType(k), kwargs[k])
174175
for k in kwargs)
175176
new.setdefault('good_temp_dir',
@@ -204,7 +205,7 @@ def get_kwargs(self, **kwargs):
204205
module=self._inv.module_name,
205206
path=self._inv.module_path,
206207
json_args=json.dumps(self._inv.module_args),
207-
env=self._inv.env,
208+
env=ansible_mitogen.utils.unsafe.cast(self._inv.env),
208209
**kwargs
209210
)
210211

@@ -546,7 +547,7 @@ def _invoke_async_task(invocation, planner):
546547
call_recv = context.call_async(
547548
ansible_mitogen.target.run_module_async,
548549
job_id=job_id,
549-
timeout_secs=invocation.timeout_secs,
550+
timeout_secs=ansible_mitogen.utils.unsafe.cast(invocation.timeout_secs),
550551
started_sender=started_recv.to_sender(),
551552
kwargs=planner.get_kwargs(),
552553
)

ansible_mitogen/runner.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
from io import StringIO
7474

7575
# Prevent accidental import of an Ansible module from hanging on stdin read.
76+
# FIXME Should probably be b'{}' or None. Ansible 2.19 has bytes | None = None.
7677
import ansible.module_utils.basic
7778
ansible.module_utils.basic._ANSIBLE_ARGS = '{}'
7879

@@ -635,6 +636,7 @@ def __init__(self, args, temp_dir):
635636
sys.stderr = StringIO()
636637
encoded = json.dumps({'ANSIBLE_MODULE_ARGS': args})
637638
ansible.module_utils.basic._ANSIBLE_ARGS = utf8(encoded)
639+
ansible.module_utils.basic._ANSIBLE_PROFILE = 'legacy'
638640
sys.stdin = StringIO(mitogen.core.to_text(encoded))
639641

640642
self.original_get_path = getattr(ansible.module_utils.basic,
@@ -649,7 +651,9 @@ def revert(self):
649651
sys.stdout = self.original_stdout
650652
sys.stderr = self.original_stderr
651653
sys.stdin = self.original_stdin
654+
# FIXME Should probably be b'{}' or None. Ansible 2.19 has bytes | None = None.
652655
ansible.module_utils.basic._ANSIBLE_ARGS = '{}'
656+
ansible.module_utils.basic._ANSIBLE_PROFILE = None
653657

654658

655659
class ProgramRunner(Runner):

ansible_mitogen/services.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import ansible_mitogen.loaders
5858
import ansible_mitogen.module_finder
5959
import ansible_mitogen.target
60+
import ansible_mitogen.utils
6061
import ansible_mitogen.utils.unsafe
6162

6263

@@ -338,7 +339,12 @@ def _on_context_disconnect(self, context):
338339
'ansible_mitogen.target',
339340
'mitogen.fork',
340341
'mitogen.service',
341-
)
342+
) + ((
343+
'ansible.module_utils._internal._json._profiles._module_legacy_c2m',
344+
'ansible.module_utils._internal._json._profiles._module_legacy_m2c',
345+
'ansible.module_utils._internal._json._profiles._module_modern_c2m',
346+
'ansible.module_utils._internal._json._profiles._module_legacy_m2c',
347+
) if ansible_mitogen.utils.ansible_version[:2] >= (2, 19) else ())
342348

343349
def _send_module_forwards(self, context):
344350
if hasattr(self.router.responder, 'forward_modules'):

ansible_mitogen/utils/unsafe.py

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616

1717
def _cast_to_dict(obj): return {cast(k): cast(v) for k, v in obj.items()}
1818
def _cast_to_list(obj): return [cast(v) for v in obj]
19+
def _cast_to_set(obj): return set(cast(v) for v in obj)
20+
def _cast_to_tuple(obj): return tuple(cast(v) for v in obj)
1921
def _cast_unsafe(obj): return obj._strip_unsafe()
2022
def _passthrough(obj): return obj
23+
def _untag(obj): return obj._native_copy()
2124

2225

2326
# A dispatch table to cast objects based on their exact type.
@@ -26,30 +29,64 @@ def _passthrough(obj): return obj
2629
bytes: bytes,
2730
dict: _cast_to_dict,
2831
list: _cast_to_list,
29-
tuple: _cast_to_list,
3032
mitogen.core.UnicodeType: mitogen.core.UnicodeType,
3133
}
3234
_CAST_DISPATCH.update({t: _passthrough for t in mitogen.utils.PASSTHROUGH})
3335

34-
if ansible_mitogen.utils.ansible_version[:2] >= (2, 19):
36+
_CAST_SUBTYPES = [
37+
dict,
38+
list,
39+
]
40+
41+
if hasattr(ansible.utils.unsafe_proxy, 'TrustedAsTemplate'):
42+
import datetime
43+
import ansible.module_utils._internal._datatag
3544
_CAST_DISPATCH.update({
45+
set: _cast_to_set,
46+
tuple: _cast_to_tuple,
47+
ansible.module_utils._internal._datatag._AnsibleTaggedBytes: _untag,
48+
ansible.module_utils._internal._datatag._AnsibleTaggedDate: _untag,
49+
ansible.module_utils._internal._datatag._AnsibleTaggedDateTime: _untag,
50+
ansible.module_utils._internal._datatag._AnsibleTaggedDict: _cast_to_dict,
51+
ansible.module_utils._internal._datatag._AnsibleTaggedFloat: _untag,
52+
ansible.module_utils._internal._datatag._AnsibleTaggedInt: _untag,
53+
ansible.module_utils._internal._datatag._AnsibleTaggedList: _cast_to_list,
54+
ansible.module_utils._internal._datatag._AnsibleTaggedSet: _cast_to_set,
55+
ansible.module_utils._internal._datatag._AnsibleTaggedStr: _untag,
56+
ansible.module_utils._internal._datatag._AnsibleTaggedTime: _untag,
57+
ansible.module_utils._internal._datatag._AnsibleTaggedTuple: _cast_to_tuple,
3658
ansible.utils.unsafe_proxy.AnsibleUnsafeBytes: bytes,
3759
ansible.utils.unsafe_proxy.AnsibleUnsafeText: mitogen.core.UnicodeType,
60+
datetime.date: _passthrough,
61+
datetime.datetime: _passthrough,
62+
datetime.time: _passthrough,
3863
})
64+
_CAST_SUBTYPES.extend([
65+
set,
66+
tuple,
67+
])
3968
elif hasattr(ansible.utils.unsafe_proxy.AnsibleUnsafeText, '_strip_unsafe'):
4069
_CAST_DISPATCH.update({
70+
tuple: _cast_to_list,
4171
ansible.utils.unsafe_proxy.AnsibleUnsafeBytes: _cast_unsafe,
4272
ansible.utils.unsafe_proxy.AnsibleUnsafeText: _cast_unsafe,
4373
ansible.utils.unsafe_proxy.NativeJinjaUnsafeText: _cast_unsafe,
4474
})
75+
_CAST_SUBTYPES.extend([
76+
tuple,
77+
])
4578
elif ansible_mitogen.utils.ansible_version[:2] <= (2, 16):
4679
_CAST_DISPATCH.update({
80+
tuple: _cast_to_list,
4781
ansible.utils.unsafe_proxy.AnsibleUnsafeBytes: bytes,
4882
ansible.utils.unsafe_proxy.AnsibleUnsafeText: mitogen.core.UnicodeType,
4983
})
84+
_CAST_SUBTYPES.extend([
85+
tuple,
86+
])
5087
else:
5188
mitogen_ver = '.'.join(str(v) for v in mitogen.__version__)
52-
raise ImportError("Mitogen %s can't unwrap Ansible %s AnsibleUnsafe objects"
89+
raise ImportError("Mitogen %s can't cast Ansible %s objects"
5390
% (mitogen_ver, ansible.__version__))
5491

5592

@@ -78,7 +115,9 @@ def cast(obj):
78115
return unwrapper(obj)
79116

80117
# Slow path: obj is some unknown subclass
81-
if isinstance(obj, dict): return _cast_to_dict(obj)
82-
if isinstance(obj, (list, tuple)): return _cast_to_list(obj)
118+
for typ_ in _CAST_SUBTYPES:
119+
if isinstance(obj, typ_):
120+
unwrapper = _CAST_DISPATCH[typ_]
121+
return unwrapper(obj)
83122

84123
return mitogen.utils.cast(obj)

docs/ansible_detailed.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@ Noteworthy Differences
141141
+-----------------+ 3.10 - 3.13 |
142142
| 10 | |
143143
+-----------------+-----------------+
144-
| 11 | 3.11 - 3.13 |
144+
| 11 | |
145+
+-----------------+ 3.11 - 3.13+ |
146+
| 12 | |
145147
+-----------------+-----------------+
146148

147149
Verify your installation is running one of these versions by checking

docs/changelog.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ In progress (unreleased)
2222
------------------------
2323

2424
* :gh:issue:`1258` Initial Ansible 12 (ansible-core 2.19) support
25+
* :gh:issue:`1258` :mod:`ansible_mitogen`: Initial Ansible datatag support
26+
(:gh:anspull:`84621`)
27+
* :gh:issue:`1258` :mod:`ansible_mitogen`: Ansible 12 (ansible-core 2.19) test
28+
jobs
2529

2630

2731
v0.3.24 (2025-05-29)

docs/conf.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ def changelog_version(path, encoding='utf-8'):
6464
'text': '#%s',
6565
'url': 'https://github.com/mitogen-hq/mitogen/pull/%s',
6666
},
67+
'gh:ansissue': {
68+
'text': 'Ansible #%s',
69+
'url': 'https://github.com/ansible/ansible/issues/%s',
70+
},
71+
'gh:anspull': {
72+
'text': 'Ansible #%s',
73+
'url': 'https://github.com/ansible/ansible/pull/%s',
74+
},
75+
6776
'ans:mod': {
6877
'text': '%s module',
6978
'url': 'https://docs.ansible.com/ansible/latest/modules/%s_module.html',

0 commit comments

Comments
 (0)