@ -13,10 +13,10 @@ import os.path
import sys
import sys
import warnings
import warnings
from collections import defaultdict
from collections import defaultdict , namedtuple
from ansible import constants as C
from ansible import constants as C
from ansible . errors import AnsibleError , AnsiblePluginCircularRedirect , AnsiblePluginRemoved , AnsibleCollectionUnsupportedVersionError
from ansible . errors import AnsibleError , AnsiblePluginCircularRedirect , AnsiblePluginRemoved Error , AnsibleCollectionUnsupportedVersionError
from ansible . module_utils . _text import to_bytes , to_text , to_native
from ansible . module_utils . _text import to_bytes , to_text , to_native
from ansible . module_utils . compat . importlib import import_module
from ansible . module_utils . compat . importlib import import_module
from ansible . module_utils . six import string_types
from ansible . module_utils . six import string_types
@ -52,7 +52,7 @@ except ImportError:
display = Display ( )
display = Display ( )
_tombstones = None
get_with_context_result = namedtuple ( ' get_with_context_result ' , [ ' object ' , ' plugin_load_context ' ] )
def get_all_plugin_loaders ( ) :
def get_all_plugin_loaders ( ) :
@ -140,15 +140,9 @@ class PluginLoadContext(object):
if removal_date is not None :
if removal_date is not None :
removal_version = None
removal_version = None
if not warning_text :
if not warning_text :
if removal_date :
warning_text = ' {0} has been deprecated ' . format ( name )
warning_text = ' {0} has been deprecated and will be removed in a release of {2} after {1} ' . format (
name , removal_date , collection_name )
display . deprecated ( warning_text , date = removal_date , version = removal_version , collection_name = collection_name )
elif removal_version :
warning_text = ' {0} has been deprecated and will be removed in version {1} of {2} ' . format (
name , removal_version , collection_name )
else :
warning_text = ' {0} has been deprecated and will be removed in a future release of {2} ' . format (
name , collection_name )
self . deprecated = True
self . deprecated = True
if removal_date :
if removal_date :
@ -450,22 +444,19 @@ class PluginLoader:
tombstone = routing_metadata . get ( ' tombstone ' , None )
tombstone = routing_metadata . get ( ' tombstone ' , None )
# FIXME: clean up text gen
if tombstone :
if tombstone :
redirect = tombstone . get ( ' redirect ' , None )
removal_date = tombstone . get ( ' removal_date ' )
removal_date = tombstone . get ( ' removal_date ' )
removal_version = tombstone . get ( ' removal_version ' )
removal_version = tombstone . get ( ' removal_version ' )
if removal_date :
warning_text = tombstone . get ( ' warning_text ' ) or ' {0} has been removed. ' . format ( fq_name )
removed_msg = ' {0} was removed from {2} on {1} ' . format ( fq_name , removal_date , acr . collection )
removed_msg = display . get_deprecation_message ( msg = warning_text , version = removal_version ,
removal_version = None
date = removal_date , removed = True ,
elif removal_version :
collection_name = acr . collection )
removed_msg = ' {0} was removed in version {1} of {2} ' . format ( fq_name , removal_version , acr . collection )
else :
removed_msg = ' {0} was removed in a previous release of {1} ' . format ( fq_name , acr . collection )
plugin_load_context . removal_date = removal_date
plugin_load_context . removal_date = removal_date
plugin_load_context . removal_version = removal_version
plugin_load_context . removal_version = removal_version
plugin_load_context . resolved = True
plugin_load_context . resolved = True
plugin_load_context . exit_reason = removed_msg
plugin_load_context . exit_reason = removed_msg
r eturn plugin_load_context
r aise AnsiblePluginRemovedError ( removed_msg , plugin_load_context = plugin_load_context )
redirect = routing_metadata . get ( ' redirect ' , None )
redirect = routing_metadata . get ( ' redirect ' , None )
@ -545,10 +536,11 @@ class PluginLoader:
# TODO: display/return import_error_list? Only useful for forensics...
# TODO: display/return import_error_list? Only useful for forensics...
if plugin_load_context . deprecated and C . config . get_config_value ( ' DEPRECATION_WARNINGS ' ) :
# FIXME: store structured deprecation data in PluginLoadContext and use display.deprecate
for dw in plugin_load_context . deprecation_warnings :
# if plugin_load_context.deprecated and C.config.get_config_value('DEPRECATION_WARNINGS'):
# TODO: need to smuggle these to the controller if we're in a worker context
# for dw in plugin_load_context.deprecation_warnings:
display . warning ( ' [DEPRECATION WARNING] ' + dw )
# # TODO: need to smuggle these to the controller if we're in a worker context
# display.warning('[DEPRECATION WARNING] ' + dw)
return plugin_load_context
return plugin_load_context
@ -597,7 +589,7 @@ class PluginLoader:
plugin_load_context = self . _find_fq_plugin ( candidate_name , suffix , plugin_load_context = plugin_load_context )
plugin_load_context = self . _find_fq_plugin ( candidate_name , suffix , plugin_load_context = plugin_load_context )
if plugin_load_context . resolved or plugin_load_context . pending_redirect : # if we got an answer or need to chase down a redirect, return
if plugin_load_context . resolved or plugin_load_context . pending_redirect : # if we got an answer or need to chase down a redirect, return
return plugin_load_context
return plugin_load_context
except ( AnsiblePluginRemoved , AnsiblePluginCircularRedirect , AnsibleCollectionUnsupportedVersionError ) :
except ( AnsiblePluginRemoved Error , AnsiblePluginCircularRedirect , AnsibleCollectionUnsupportedVersionError ) :
# these are generally fatal, let them fly
# these are generally fatal, let them fly
raise
raise
except ImportError as ie :
except ImportError as ie :
@ -757,6 +749,9 @@ class PluginLoader:
setattr ( obj , ' _redirected_names ' , redirected_names or [ ] )
setattr ( obj , ' _redirected_names ' , redirected_names or [ ] )
def get ( self , name , * args , * * kwargs ) :
def get ( self , name , * args , * * kwargs ) :
return self . get_with_context ( name , * args , * * kwargs ) . object
def get_with_context ( self , name , * args , * * kwargs ) :
''' instantiates a plugin of the given name using arguments '''
''' instantiates a plugin of the given name using arguments '''
found_in_cache = True
found_in_cache = True
@ -767,7 +762,7 @@ class PluginLoader:
plugin_load_context = self . find_plugin_with_context ( name , collection_list = collection_list )
plugin_load_context = self . find_plugin_with_context ( name , collection_list = collection_list )
if not plugin_load_context . resolved or not plugin_load_context . plugin_resolved_path :
if not plugin_load_context . resolved or not plugin_load_context . plugin_resolved_path :
# FIXME: this is probably an error (eg removed plugin)
# FIXME: this is probably an error (eg removed plugin)
return None
return get_with_context_result ( None , plugin_load_context )
name = plugin_load_context . plugin_resolved_name
name = plugin_load_context . plugin_resolved_name
path = plugin_load_context . plugin_resolved_path
path = plugin_load_context . plugin_resolved_path
@ -787,9 +782,9 @@ class PluginLoader:
try :
try :
plugin_class = getattr ( module , self . base_class )
plugin_class = getattr ( module , self . base_class )
except AttributeError :
except AttributeError :
return None
return get_with_context_result ( None , plugin_load_context )
if not issubclass ( obj , plugin_class ) :
if not issubclass ( obj , plugin_class ) :
return None
return get_with_context_result ( None , plugin_load_context )
# FIXME: update this to use the load context
# FIXME: update this to use the load context
self . _display_plugin_load ( self . class_name , name , self . _searched_paths , path , found_in_cache = found_in_cache , class_only = class_only )
self . _display_plugin_load ( self . class_name , name , self . _searched_paths , path , found_in_cache = found_in_cache , class_only = class_only )
@ -806,11 +801,11 @@ class PluginLoader:
if " abstract " in e . args [ 0 ] :
if " abstract " in e . args [ 0 ] :
# Abstract Base Class. The found plugin file does not
# Abstract Base Class. The found plugin file does not
# fully implement the defined interface.
# fully implement the defined interface.
return None
return get_with_context_result ( None , plugin_load_context )
raise
raise
self . _update_object ( obj , name , path , redirected_names )
self . _update_object ( obj , name , path , redirected_names )
return obj
return get_with_context_result( obj, plugin_load_context )
def _display_plugin_load ( self , class_name , name , searched_paths , path , found_in_cache = None , class_only = None ) :
def _display_plugin_load ( self , class_name , name , searched_paths , path , found_in_cache = None , class_only = None ) :
''' formats data to display debug info for plugin loading, also avoids processing unless really needed '''
''' formats data to display debug info for plugin loading, also avoids processing unless really needed '''