community.general/lib/ansible/arguments/context_objects.py
Toshio Kuratomi 7e92ff823e Split up the base_parser function
The goal of breaking apart the base_parser() function is to get rid of
a bunch of conditionals and parameters in the code and, instead, make
code look like simple composition.

When splitting, a choice had to be made as to whether this would operate
by side effect (modifying a passed in parser) or side effect-free
(returning a new parser everytime).

Making a version that's side-effect-free appears to be fighting with the
optparse API (it wants to work by creating a parser object, configuring
the object, and then parsing the arguments with it) so instead, make it
clear that our helper functions are modifying the passed in parser by
(1) not returning the parser and (2) changing the function names to be
more clear that it is operating by side-effect.

Also move all of the generic optparse code, along with the argument
context classes, into a new subdirectory.
2019-01-03 18:12:23 -08:00

92 lines
3.1 KiB
Python

# Copyright: (c) 2018, Toshio Kuratomi <tkuratomi@ansible.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
"""
Hold command line arguments for use in other modules
"""
from abc import ABCMeta
from ansible.module_utils.common._collections_compat import (Container, Mapping, Sequence, Set)
from ansible.module_utils.common.collections import ImmutableDict
from ansible.module_utils.six import add_metaclass, binary_type, text_type
from ansible.utils.singleton import Singleton
def _make_immutable(obj):
"""Recursively convert a container and objects inside of it into immutable data types"""
if isinstance(obj, (text_type, binary_type)):
# Strings first because they are also sequences
return obj
elif isinstance(obj, Mapping):
temp_dict = {}
for key, value in obj.items():
if isinstance(value, Container):
temp_dict[key] = _make_immutable(value)
else:
temp_dict[key] = value
return ImmutableDict(temp_dict)
elif isinstance(obj, Set):
temp_set = set()
for value in obj:
if isinstance(value, Container):
temp_set.add(_make_immutable(value))
else:
temp_set.add(value)
return frozenset(temp_set)
elif isinstance(obj, Sequence):
temp_sequence = []
for value in obj:
if isinstance(value, Container):
temp_sequence.append(_make_immutable(value))
else:
temp_sequence.append(value)
return tuple(temp_sequence)
return obj
class _ABCSingleton(Singleton, ABCMeta):
"""
Combine ABCMeta based classes with Singleton based classes
Combine Singleton and ABCMeta so we have a metaclass that unambiguously knows which can override
the other. Useful for making new types of containers which are also Singletons.
"""
pass
class CLIArgs(ImmutableDict):
"""
Hold a parsed copy of cli arguments
We have both this non-Singleton version and the Singleton, GlobalCLIArgs, version to leave us
room to implement a Context object in the future. Whereas there should only be one set of args
in a global context, individual Context objects might want to pretend that they have different
command line switches to trigger different behaviour when they run. So if we support Contexts
in the future, they would use CLIArgs instead of GlobalCLIArgs to store their version of command
line flags.
"""
def __init__(self, mapping):
toplevel = {}
for key, value in mapping.items():
toplevel[key] = _make_immutable(value)
super(CLIArgs, self).__init__(toplevel)
@classmethod
def from_options(cls, options):
return cls(vars(options))
@add_metaclass(_ABCSingleton)
class GlobalCLIArgs(CLIArgs):
"""
Globally hold a parsed copy of cli arguments.
Only one of these exist per program as it is for global context
"""
pass