# Buggy code
def all(self, *args, **kwargs):
    """
    Iterate through all plugins of this type

    A plugin loader is initialized with a specific type.  This function is an iterator returning
    all of the plugins of that type to the caller.

    :kwarg path_only: If this is set to True, then we return the paths to where the plugins reside
        instead of an instance of the plugin.  This conflicts with class_only and both should
        not be set.
    :kwarg class_only: If this is set to True then we return the python class which implements
        a plugin rather than an instance of the plugin.  This conflicts with path_only and both
        should not be set.
    :kwarg _dedupe: By default, we only return one plugin per plugin name.  Deduplication happens
        in the same way as the :meth:`get` and :meth:`find_plugin` methods resolve which plugin
        should take precedence.  If this is set to False, then we return all of the plugins
        found, including those with duplicate names.  In the case of duplicates, the order in
        which they are returned is the one that would take precedence first, followed by the
        others  in decreasing precedence order.  This should only be used by subclasses which
        want to manage their own deduplication of the plugins.
    :*args: Any extra arguments are passed to each plugin when it is instantiated.
    :**kwargs: Any extra keyword arguments are passed to each plugin when it is instantiated.
    """
    # TODO: Change the signature of this method to:
    # def all(return_type='instance', args=None, kwargs=None):
    #     if args is None: args = []
    #     if kwargs is None: kwargs = {}
    #     return_type can be instance, class, or path.
    #     These changes will mean that plugin parameters won't conflict with our params and
    #     will also make it impossible to request both a path and a class at the same time.
    #
    #     Move _dedupe to be a class attribute, CUSTOM_DEDUPE, with subclasses for filters and
    #     tests setting it to True

    global _PLUGIN_FILTERS

    dedupe = kwargs.pop("_dedupe", True)
    path_only = kwargs.pop("path_only", False)
    class_only = kwargs.pop("class_only", False)
    # Having both path_only and class_only is a coding bug
    if path_only and class_only:
        raise AnsibleError(
            "Do not set both path_only and class_only when calling PluginLoader.all()"
        )

    all_matches = []
    found_in_cache = True

    for i in self._get_paths():
        all_matches.extend(glob.glob(os.path.join(i, "*.py")))

    loaded_modules = set()
    for path in sorted(all_matches, key=os.path.basename):
        name = os.path.splitext(path)[0]
        basename = os.path.basename(name)

        if basename == "__init__" or basename in _PLUGIN_FILTERS[self.package]:
            continue

        if dedupe and basename in loaded_modules:
            continue
        loaded_modules.add(basename)

        if path_only:
            yield path
            continue

        if path not in self._module_cache:
            try:
                module = self._load_module_source(name, path)
                self._load_config_defs(basename, module, path)
            except Exception as e:
                display.warning(
                    "Skipping plugin (%s) as it seems to be invalid: %s"
                    % (path, to_text(e))
                )
            self._module_cache[path] = module
            found_in_cache = False

        try:
            obj = getattr(self._module_cache[path], self.class_name)
        except AttributeError as e:
            display.warning(
                "Skipping plugin (%s) as it seems to be invalid: %s"
                % (path, to_text(e))
            )
            continue

        if self.base_class:
            # The import path is hardcoded and should be the right place,
            # so we are not expecting an ImportError.
            module = __import__(self.package, fromlist=[self.base_class])
            # Check whether this obj has the required base class.
            try:
                plugin_class = getattr(module, self.base_class)
            except AttributeError:
                continue
            if not issubclass(obj, plugin_class):
                continue

        self._display_plugin_load(
            self.class_name,
            basename,
            self._searched_paths,
            path,
            found_in_cache=found_in_cache,
            class_only=class_only,
        )

        if not class_only:
            try:
                obj = obj(*args, **kwargs)
            except TypeError as e:
                display.warning(
                    "Skipping plugin (%s) as it seems to be incomplete: %s"
                    % (path, to_text(e))
                )

        self._update_object(obj, basename, path)
        yield obj


# Traceback (UnboundLocalError)
[WARNING]: Skipping plugin (./plugins/foo.py) as it seems to be
invalid: invalid syntax (foo.py, line 7)
…
The full traceback is:
Traceback (most recent call last):
File "/Users/<redacted>/Development/ansible/lib/ansible/executor/task_executor.py", line 144, in run
res = self._execute()
File "/Users/<redacted>/Development/ansible/lib/ansible/executor/task_executor.py", line 645, in _execute
result = self._handler.run(task_vars=variables)
File "/Users/<redacted>/Development/ansible/lib/ansible/plugins/action/debug.py", line 52, in run
results = self._templar.template(self._task.args['var'], convert_bare=True, fail_on_undefined=True)
File "/Users/<redacted>/Development/ansible/lib/ansible/template/__init__.py", line 447, in template
disable_lookups=disable_lookups,
File "/Users/<redacted>/Development/ansible/lib/ansible/template/__init__.py", line 676, in do_template
myenv.filters.update(self._get_filters(myenv.filters))
File "/Users/<redacted>/Development/ansible/lib/ansible/template/__init__.py", line 340, in _get_filters
for fp in self._filter_loader.all():
File "/Users/<redacted>/Development/ansible/lib/ansible/plugins/loader.py", line 616, in all
plugins = [p for p in super(Jinja2Loader, self).all(*args, **kwargs)]
File "/Users/<redacted>/Development/ansible/lib/ansible/plugins/loader.py", line 616, in <listcomp>
plugins = [p for p in super(Jinja2Loader, self).all(*args, **kwargs)]
File "/Users/<redacted>/Development/ansible/lib/ansible/plugins/loader.py", line 543, in all
self._module_cache[path] = module
UnboundLocalError: local variable 'module' referenced before assignment

# Fixed code
def all(self, *args, **kwargs):
    """
    Iterate through all plugins of this type

    A plugin loader is initialized with a specific type.  This function is an iterator returning
    all of the plugins of that type to the caller.

    :kwarg path_only: If this is set to True, then we return the paths to where the plugins reside
        instead of an instance of the plugin.  This conflicts with class_only and both should
        not be set.
    :kwarg class_only: If this is set to True then we return the python class which implements
        a plugin rather than an instance of the plugin.  This conflicts with path_only and both
        should not be set.
    :kwarg _dedupe: By default, we only return one plugin per plugin name.  Deduplication happens
        in the same way as the :meth:`get` and :meth:`find_plugin` methods resolve which plugin
        should take precedence.  If this is set to False, then we return all of the plugins
        found, including those with duplicate names.  In the case of duplicates, the order in
        which they are returned is the one that would take precedence first, followed by the
        others  in decreasing precedence order.  This should only be used by subclasses which
        want to manage their own deduplication of the plugins.
    :*args: Any extra arguments are passed to each plugin when it is instantiated.
    :**kwargs: Any extra keyword arguments are passed to each plugin when it is instantiated.
    """
    # TODO: Change the signature of this method to:
    # def all(return_type='instance', args=None, kwargs=None):
    #     if args is None: args = []
    #     if kwargs is None: kwargs = {}
    #     return_type can be instance, class, or path.
    #     These changes will mean that plugin parameters won't conflict with our params and
    #     will also make it impossible to request both a path and a class at the same time.
    #
    #     Move _dedupe to be a class attribute, CUSTOM_DEDUPE, with subclasses for filters and
    #     tests setting it to True

    global _PLUGIN_FILTERS

    dedupe = kwargs.pop("_dedupe", True)
    path_only = kwargs.pop("path_only", False)
    class_only = kwargs.pop("class_only", False)
    # Having both path_only and class_only is a coding bug
    if path_only and class_only:
        raise AnsibleError(
            "Do not set both path_only and class_only when calling PluginLoader.all()"
        )

    all_matches = []
    found_in_cache = True

    for i in self._get_paths():
        all_matches.extend(glob.glob(os.path.join(i, "*.py")))

    loaded_modules = set()
    for path in sorted(all_matches, key=os.path.basename):
        name = os.path.splitext(path)[0]
        basename = os.path.basename(name)

        if basename == "__init__" or basename in _PLUGIN_FILTERS[self.package]:
            continue

        if dedupe and basename in loaded_modules:
            continue
        loaded_modules.add(basename)

        if path_only:
            yield path
            continue

        if path not in self._module_cache:
            try:
                module = self._load_module_source(name, path)
                self._load_config_defs(basename, module, path)
            except Exception as e:
                display.warning(
                    "Skipping plugin (%s) as it seems to be invalid: %s"
                    % (path, to_text(e))
                )
                continue
            self._module_cache[path] = module
            found_in_cache = False

        try:
            obj = getattr(self._module_cache[path], self.class_name)
        except AttributeError as e:
            display.warning(
                "Skipping plugin (%s) as it seems to be invalid: %s"
                % (path, to_text(e))
            )
            continue

        if self.base_class:
            # The import path is hardcoded and should be the right place,
            # so we are not expecting an ImportError.
            module = __import__(self.package, fromlist=[self.base_class])
            # Check whether this obj has the required base class.
            try:
                plugin_class = getattr(module, self.base_class)
            except AttributeError:
                continue
            if not issubclass(obj, plugin_class):
                continue

        self._display_plugin_load(
            self.class_name,
            basename,
            self._searched_paths,
            path,
            found_in_cache=found_in_cache,
            class_only=class_only,
        )

        if not class_only:
            try:
                obj = obj(*args, **kwargs)
            except TypeError as e:
                display.warning(
                    "Skipping plugin (%s) as it seems to be incomplete: %s"
                    % (path, to_text(e))
                )

        self._update_object(obj, basename, path)
        yield obj