def main(args): # Retrieve the intersphinx information from the sphinx config file conf_dir = pathlib.Path(args.conf_file).parent conf_module_spec = importlib.util.spec_from_file_location( 'sphinxconf', args.conf_file) conf_module = importlib.util.module_from_spec(conf_module_spec) conf_module_spec.loader.exec_module(conf_module) intersphinx_mapping = conf_module.intersphinx_mapping for intersphinx_name, inventory in intersphinx_mapping.items(): if not is_iterable(inventory) or len(inventory) != 2: print('WARNING: The intersphinx entry for {0} must be' ' a two-tuple.\n{1}'.format(intersphinx_name, EXAMPLE_CONF)) continue url = cache_file = None for inv_source in inventory: if isinstance(inv_source, str) and url is None: url = inv_source elif is_iterable(inv_source) and cache_file is None: if len(inv_source) != 2: print( 'WARNING: The fallback entry for {0} should be a tuple of (None,' ' filename).\n{1}'.format(intersphinx_name, EXAMPLE_CONF)) continue cache_file = inv_source[1] else: print( 'WARNING: The configuration for {0} should be a tuple of one url and one' ' tuple for a fallback filename.\n{1}'.format( intersphinx_name, EXAMPLE_CONF)) continue if url is None or cache_file is None: print('WARNING: Could not figure out the url or fallback' ' filename for {0}.\n{1}'.format(intersphinx_name, EXAMPLE_CONF)) continue url = urllib.parse.urljoin(url, 'objects.inv') # Resolve any relative cache files to be relative to the conf file cache_file = conf_dir / cache_file # Retrieve the inventory and cache it # The jinja CDN seems to be blocking the default urllib User-Agent requestor = Request( headers={'User-Agent': 'Definitely Not Python ;-)'}) with requestor.open('GET', url) as source_file: with open(cache_file, 'wb') as f: f.write(source_file.read()) print( 'Download of new cache files complete. Remember to git commit -a the changes' ) return 0
def handle_aliases(argument_spec, params): """Return a two item tuple. The first is a dictionary of aliases, the second is a list of legal inputs.""" legal_inputs = ['_ansible_%s' % k for k in PASS_VARS] aliases_results = {} # alias:canon for (k, v) in argument_spec.items(): legal_inputs.append(k) aliases = v.get('aliases', None) default = v.get('default', None) required = v.get('required', False) if default is not None and required: # not alias specific but this is a good place to check this raise ValueError("internal error: required and default are mutually exclusive for %s" % k) if aliases is None: continue if not is_iterable(aliases) or isinstance(aliases, (binary_type, text_type)): raise TypeError('internal error: aliases must be a list or tuple') for alias in aliases: legal_inputs.append(alias) aliases_results[alias] = k if alias in params: params[k] = params[alias] return aliases_results, legal_inputs
def check_option_choices(v): # Check whether choices have the correct type v_choices = v.get('choices') if not is_iterable(v_choices): return v if v.get('type') == 'list': # choices for a list type means that every list element must be one of these choices type_checker, type_name = get_type_checker({'type': v.get('elements')}) else: type_checker, type_name = get_type_checker(v) if type_checker is None: return v for value in v_choices: try: type_checker(value) except Exception as exc: raise _add_ansible_error_code(Invalid( 'Argument defines choices as (%r) but this is incompatible with argument type %s: %s' % (value, type_name, exc)), error_code= 'doc-choices-incompatible-type') return v
def author(value): if not is_iterable(value): value = [value] for line in value: m = author_line.search(line) if not m: raise Invalid("Invalid author")
def _handle_aliases(argument_spec, parameters, alias_warnings=None, alias_deprecations=None): """Process aliases from an argument_spec including warnings and deprecations. Modify ``parameters`` by adding a new key for each alias with the supplied value from ``parameters``. If a list is provided to the alias_warnings parameter, it will be filled with tuples (option, alias) in every case where both an option and its alias are specified. If a list is provided to alias_deprecations, it will be populated with dictionaries, each containing deprecation information for each alias found in argument_spec. :param argument_spec: Dictionary of parameters, their type, and valid values. :type argument_spec: dict :param parameters: Dictionary of parameters. :type parameters: dict :param alias_warnings: :type alias_warnings: list :param alias_deprecations: :type alias_deprecations: list """ aliases_results = {} # alias:canon for (k, v) in argument_spec.items(): aliases = v.get('aliases', None) default = v.get('default', None) required = v.get('required', False) if alias_deprecations is not None: for alias in argument_spec[k].get('deprecated_aliases', []): if alias.get('name') in parameters: alias_deprecations.append(alias) if default is not None and required: # not alias specific but this is a good place to check this raise ValueError("internal error: required and default are mutually exclusive for %s" % k) if aliases is None: continue if not is_iterable(aliases) or isinstance(aliases, (binary_type, text_type)): raise TypeError('internal error: aliases must be a list or tuple') for alias in aliases: aliases_results[alias] = k if alias in parameters: if k in parameters and alias_warnings is not None: alias_warnings.append((k, alias)) parameters[k] = parameters[alias] return aliases_results
def count_terms(self, terms, module_parameters): """Count the number of occurrences of a key in a given dictionary :arg terms: String or iterable of values to check :arg module_parameters: Dictionary of module parameters :returns: An integer that is the number of occurrences of the terms values in the provided dictionary. """ if not is_iterable(terms): terms = [terms] return len(set(terms).intersection(module_parameters))
def author(value): if value is None: return value # let schema checks handle if not is_iterable(value): value = [value] for line in value: if not isinstance(line, string_types): continue # let schema checks handle m = author_line.search(line) if not m: raise Invalid("Invalid author") return value
def handle_aliases(argument_spec, parameters, alias_warnings=None, alias_deprecations=None): """Return a two item tuple. The first is a dictionary of aliases, the second is a list of legal inputs. Modify supplied parameters by adding a new key for each alias. If a list is provided to the alias_warnings parameter, it will be filled with tuples (option, alias) in every case where both an option and its alias are specified. If a list is provided to alias_deprecations, it will be populated with dictionaries, each containing deprecation information for each alias found in argument_spec. """ legal_inputs = ['_ansible_%s' % k for k in PASS_VARS] aliases_results = {} # alias:canon for (k, v) in argument_spec.items(): legal_inputs.append(k) aliases = v.get('aliases', None) default = v.get('default', None) required = v.get('required', False) if alias_deprecations is not None: for alias in argument_spec[k].get('deprecated_aliases', []): if alias.get('name') in parameters: alias_deprecations.append(alias) if default is not None and required: # not alias specific but this is a good place to check this raise ValueError("internal error: required and default are mutually exclusive for %s" % k) if aliases is None: continue if not is_iterable(aliases) or isinstance(aliases, (binary_type, text_type)): raise TypeError('internal error: aliases must be a list or tuple') for alias in aliases: legal_inputs.append(alias) aliases_results[alias] = k if alias in parameters: if k in parameters and alias_warnings is not None: alias_warnings.append((k, alias)) parameters[k] = parameters[alias] return aliases_results, legal_inputs
def _return_datastructure_name(obj): """ Return native stringified values from datastructures. For use with removing sensitive values pre-jsonification.""" if isinstance(obj, (text_type, binary_type)): if obj: yield to_native(obj, errors='surrogate_or_strict') return elif isinstance(obj, Mapping): for element in obj.items(): for subelement in _return_datastructure_name(element[1]): yield subelement elif is_iterable(obj): for element in obj: for subelement in _return_datastructure_name(element): yield subelement elif isinstance(obj, (bool, NoneType)): # This must come before int because bools are also ints return elif isinstance(obj, tuple(list(integer_types) + [float])): yield to_native(obj, nonstring='simplerepr') else: raise TypeError('Unknown parameter type: %s' % (type(obj)))
def main(): # the command module is the one ansible module that does not take key=value args # hence don't copy this one if you are looking to build others! module = AnsibleModule( argument_spec=dict( _raw_params=dict(), _uses_shell=dict(type='bool', default=False), argv=dict(type='list'), chdir=dict(type='path'), executable=dict(), creates=dict(type='path'), removes=dict(type='path'), # The default for this really comes from the action plugin warn=dict(type='bool', default=True), stdin=dict(required=False), ), supports_check_mode=True, ) shell = module.params['_uses_shell'] chdir = module.params['chdir'] executable = module.params['executable'] args = module.params['_raw_params'] argv = module.params['argv'] creates = module.params['creates'] removes = module.params['removes'] warn = module.params['warn'] stdin = module.params['stdin'] if not shell and executable: module.warn( "As of Ansible 2.4, the parameter 'executable' is no longer supported with the 'command' module. Not using '%s'." % executable) executable = None if (not args or args.strip() == '') and not argv: module.fail_json(rc=256, msg="no command given") if args and argv: module.fail_json(rc=256, msg="only command or argv can be given, not both") if not shell and args: args = shlex.split(args) args = args or argv # All args must be strings if is_iterable(args, include_strings=False): args = [ to_native(arg, errors='surrogate_or_strict', nonstring='simplerepr') for arg in args ] if chdir: chdir = os.path.abspath(chdir) os.chdir(chdir) if creates: # do not run the command if the line contains creates=filename # and the filename already exists. This allows idempotence # of command executions. if glob.glob(creates): module.exit_json(cmd=args, stdout="skipped, since %s exists" % creates, changed=False, rc=0) if removes: # do not run the command if the line contains removes=filename # and the filename does not exist. This allows idempotence # of command executions. if not glob.glob(removes): module.exit_json(cmd=args, stdout="skipped, since %s does not exist" % removes, changed=False, rc=0) if warn: check_command(module, args) startd = datetime.datetime.now() if not module.check_mode: rc, out, err = module.run_command(args, executable=executable, use_unsafe_shell=shell, encoding=None, data=stdin) elif creates or removes: rc = 0 out = err = b'Command would have run if not in check mode' else: module.exit_json(msg="skipped, running in check mode", skipped=True) endd = datetime.datetime.now() delta = endd - startd result = dict( cmd=args, stdout=out.rstrip(b"\r\n"), stderr=err.rstrip(b"\r\n"), rc=rc, start=str(startd), end=str(endd), delta=str(delta), changed=True, ) if rc != 0: module.fail_json(msg='non-zero return code', **result) module.exit_json(**result)
def main(): # the command module is the one ansible module that does not take key=value args # hence don't copy this one if you are looking to build others! # NOTE: ensure splitter.py is kept in sync for exceptions module = AnsibleModule( argument_spec=dict( _raw_params=dict(), _uses_shell=dict(type='bool', default=False), argv=dict(type='list', elements='str'), chdir=dict(type='path'), executable=dict(), creates=dict(type='path'), removes=dict(type='path'), # The default for this really comes from the action plugin stdin=dict(required=False), stdin_add_newline=dict(type='bool', default=True), strip_empty_ends=dict(type='bool', default=True), ), supports_check_mode=True, ) shell = module.params['_uses_shell'] chdir = module.params['chdir'] executable = module.params['executable'] args = module.params['_raw_params'] argv = module.params['argv'] creates = module.params['creates'] removes = module.params['removes'] stdin = module.params['stdin'] stdin_add_newline = module.params['stdin_add_newline'] strip = module.params['strip_empty_ends'] # we promissed these in 'always' ( _lines get autoaded on action plugin) r = { 'changed': False, 'stdout': '', 'stderr': '', 'rc': None, 'cmd': None, 'start': None, 'end': None, 'delta': None, 'msg': '' } if not shell and executable: module.warn( "As of Ansible 2.4, the parameter 'executable' is no longer supported with the 'command' module. Not using '%s'." % executable) executable = None if (not args or args.strip() == '') and not argv: r['rc'] = 256 r['msg'] = "no command given" module.fail_json(**r) if args and argv: r['rc'] = 256 r['msg'] = "only command or argv can be given, not both" module.fail_json(**r) if not shell and args: args = shlex.split(args) args = args or argv # All args must be strings if is_iterable(args, include_strings=False): args = [ to_native(arg, errors='surrogate_or_strict', nonstring='simplerepr') for arg in args ] r['cmd'] = args if chdir: chdir = to_bytes(chdir, errors='surrogate_or_strict') try: os.chdir(chdir) except (IOError, OSError) as e: r['msg'] = 'Unable to change directory before execution: %s' % to_text( e) module.fail_json(**r) # check_mode partial support, since it only really works in checking creates/removes if module.check_mode: shoulda = "Would" else: shoulda = "Did" # special skips for idempotence if file exists (assumes command creates) if creates: if glob.glob(creates): r['msg'] = "%s not run command since '%s' exists" % (shoulda, creates) r['stdout'] = "skipped, since %s exists" % creates # TODO: deprecate r['rc'] = 0 # special skips for idempotence if file does not exist (assumes command removes) if not r['msg'] and removes: if not glob.glob(removes): r['msg'] = "%s not run command since '%s' does not exist" % ( shoulda, removes) r['stdout'] = "skipped, since %s does not exist" % removes # TODO: deprecate r['rc'] = 0 if r['msg']: module.exit_json(**r) r['changed'] = True # actually executes command (or not ...) if not module.check_mode: r['start'] = datetime.datetime.now() r['rc'], r['stdout'], r['stderr'] = module.run_command( args, executable=executable, use_unsafe_shell=shell, encoding=None, data=stdin, binary_data=(not stdin_add_newline)) r['end'] = datetime.datetime.now() else: # this is partial check_mode support, since we end up skipping if we get here r['rc'] = 0 r['msg'] = "Command would have run if not in check mode" if creates is None and removes is None: r['skipped'] = True # skipped=True and changed=True are mutually exclusive r['changed'] = False # convert to text for jsonization and usability if r['start'] is not None and r['end'] is not None: # these are datetime objects, but need them as strings to pass back r['delta'] = to_text(r['end'] - r['start']) r['end'] = to_text(r['end']) r['start'] = to_text(r['start']) if strip: r['stdout'] = to_text(r['stdout']).rstrip("\r\n") r['stderr'] = to_text(r['stderr']).rstrip("\r\n") if r['rc'] != 0: r['msg'] = 'non-zero return code' module.fail_json(**r) module.exit_json(**r)
def path_list_checker(value): if not isinstance(value, string_types) and not is_iterable(value): raise ValueError('Value must be string or list of strings')
def test_iterable_including_strings(string_input): assert is_iterable(string_input, include_strings=True)
def test_iterable_negative(seq): assert not is_iterable(seq)
def test_iterable_positive(seq): assert is_iterable(seq)
def test_iterable_excluding_strings(string_input): assert not is_iterable(string_input, include_strings=False)