class Puppet::Pops::Lookup::LookupAdapter

A LookupAdapter is a specialized DataAdapter that uses its hash to store data providers. It also remembers the compiler that it is attached to and maintains a cache of _lookup options_ retrieved from the data providers associated with the compiler's environment.

@api private

Constants

CONVERT_TO
GLOBAL_ENV_MERGE
HASH
LOOKUP_OPTIONS_PATTERN_START
LOOKUP_OPTIONS_PREFIX
MERGE
NEW
PROVIDER_STACK

Public Class Methods

create_adapter(compiler) click to toggle source
   # File lib/puppet/pops/lookup/lookup_adapter.rb
23 def self.create_adapter(compiler)
24   new(compiler)
25 end
new(compiler) click to toggle source
Calls superclass method Puppet::Pops::Lookup::DataAdapter::new
   # File lib/puppet/pops/lookup/lookup_adapter.rb
27 def initialize(compiler)
28   super()
29   @compiler = compiler
30   @lookup_options = {}
31   # Get a KeyRecorder from context, and set a "null recorder" if not defined
32   @key_recorder = Puppet.lookup(:lookup_key_recorder) { KeyRecorder.singleton }
33 end

Public Instance Methods

convert_result(key, lookup_options, lookup_invocation, the_lookup) click to toggle source

Performs a possible conversion of the result of calling `the_lookup` lambda The conversion takes place if there is a 'convert_to' key in the lookup_options If there is no conversion, the result of calling `the_lookup` is returned otherwise the successfully converted value. Errors are raised if the convert_to is faulty (bad type string, or if a call to new(T, <args>) fails.

@param key [String] The key to lookup @param lookup_options [Hash] a hash of options @param lookup_invocation [Invocation] the lookup invocation @param the_lookup [Lambda] zero arg lambda that performs the lookup of a value @return [Object] the looked up value, or converted value if there was conversion @throw :no_such_key when the object is not found (if thrown by `the_lookup`)

    # File lib/puppet/pops/lookup/lookup_adapter.rb
 98 def convert_result(key, lookup_options, lookup_invocation, the_lookup)
 99   result = the_lookup.call
100   convert_to = lookup_options[CONVERT_TO]
101   return result if convert_to.nil?
102 
103   convert_to = convert_to.is_a?(Array) ? convert_to : [convert_to]
104   if convert_to[0].is_a?(String)
105     begin
106       convert_to[0] = Puppet::Pops::Types::TypeParser.singleton.parse(convert_to[0])
107     rescue StandardError => e
108       raise Puppet::DataBinding::LookupError,
109         _("Invalid data type in lookup_options for key '%{key}' could not parse '%{source}', error: '%{msg}") %
110           { key: key, source: convert_to[0], msg: e.message}
111     end
112   end
113   begin
114     result = lookup_invocation.scope.call_function(NEW, [convert_to[0], result, *convert_to[1..-1]])
115     # TRANSLATORS 'lookup_options', 'convert_to' and args_string variable should not be translated,
116     args_string = Puppet::Pops::Types::StringConverter.singleton.convert(convert_to)
117     lookup_invocation.report_text { _("Applying convert_to lookup_option with arguments %{args}") % { args: args_string } }
118   rescue StandardError => e
119     raise Puppet::DataBinding::LookupError,
120       _("The convert_to lookup_option for key '%{key}' raised error: %{msg}") %
121         { key: key, msg: e.message}
122   end
123   result
124 end
extract_lookup_options_for_key(key, options) click to toggle source
    # File lib/puppet/pops/lookup/lookup_adapter.rb
246 def extract_lookup_options_for_key(key, options)
247   return nil if options.nil?
248 
249   rk = key.root_key
250   key_opts = options[0]
251   unless key_opts.nil?
252     key_opt = key_opts[rk]
253     return key_opt unless key_opt.nil?
254   end
255 
256   patterns = options[1]
257   patterns.each_pair { |pattern, value| return value if pattern =~ rk } unless patterns.nil?
258   nil
259 end
global_hiera_config_path() click to toggle source

@return [Pathname] the full path of the hiera.yaml config file

    # File lib/puppet/pops/lookup/lookup_adapter.rb
269 def global_hiera_config_path
270   @global_hiera_config_path ||= Pathname.new(Puppet.settings[:hiera_config])
271 end
global_only?() click to toggle source
    # File lib/puppet/pops/lookup/lookup_adapter.rb
280 def global_only?
281   instance_variable_defined?(:@global_only) ? @global_only : false
282 end
has_environment_data_provider?(lookup_invocation) click to toggle source

@param lookup_invocation [Puppet::Pops::Lookup::Invocation] the lookup invocation @return [Boolean] `true` if an environment data provider version 5 is configured

    # File lib/puppet/pops/lookup/lookup_adapter.rb
263 def has_environment_data_provider?(lookup_invocation)
264   ep = env_provider(lookup_invocation)
265   ep.nil? ? false : ep.config(lookup_invocation).version >= 5
266 end
lookup(key, lookup_invocation, merge) click to toggle source

Performs a lookup using global, environment, and module data providers. Merge the result using the given merge strategy. If the merge strategy is nil, then an attempt is made to find merge options in the `lookup_options` hash for an entry associated with the key. If no options are found, the no merge is performed and the first found entry is returned.

@param key [String] The key to lookup @param lookup_invocation [Invocation] the lookup invocation @param merge [MergeStrategy,String,Hash{String => Object},nil] Merge strategy, merge strategy name, strategy and options hash, or nil (implies “first found”) @return [Object] the found object @throw :no_such_key when the object is not found

   # File lib/puppet/pops/lookup/lookup_adapter.rb
46 def lookup(key, lookup_invocation, merge)
47   # The 'lookup_options' key is reserved and not found as normal data
48   if key == LOOKUP_OPTIONS || key.start_with?(LOOKUP_OPTIONS_PREFIX)
49     lookup_invocation.with(:invalid_key, LOOKUP_OPTIONS) do
50       throw :no_such_key
51     end
52   end
53 
54   # Record that the key was looked up. This will record all keys for which a lookup is performed
55   # except 'lookup_options' (since that is illegal from a user perspective,
56   # and from an impact perspective is always looked up).
57   @key_recorder.record(key)
58 
59   key = LookupKey.new(key)
60   lookup_invocation.lookup(key, key.module_name) do
61     if lookup_invocation.only_explain_options?
62       catch(:no_such_key) { do_lookup(LookupKey::LOOKUP_OPTIONS, lookup_invocation, HASH) }
63       nil
64     else
65       lookup_options = lookup_lookup_options(key, lookup_invocation) || {}
66 
67       if merge.nil?
68         # Used cached lookup_options
69         # merge = lookup_merge_options(key, lookup_invocation)
70         merge = lookup_options[MERGE]
71         lookup_invocation.report_merge_source(LOOKUP_OPTIONS) unless merge.nil?
72       end
73       convert_result(key.to_s, lookup_options, lookup_invocation, lambda do
74         lookup_invocation.with(:data, key.to_s) do
75           catch(:no_such_key) { return do_lookup(key, lookup_invocation, merge) }
76           throw :no_such_key if lookup_invocation.global_only?
77           key.dig(lookup_invocation, lookup_default_in_module(key, lookup_invocation))
78         end
79       end)
80     end
81   end
82 end
lookup_default_in_module(key, lookup_invocation) click to toggle source
    # File lib/puppet/pops/lookup/lookup_adapter.rb
179 def lookup_default_in_module(key, lookup_invocation)
180   module_name = lookup_invocation.module_name
181 
182   # Do not attempt to do a lookup in a module unless the name is qualified.
183   throw :no_such_key if module_name.nil?
184 
185   provider = module_provider(lookup_invocation, module_name)
186   throw :no_such_key if provider.nil? || !provider.config(lookup_invocation).has_default_hierarchy?
187 
188   lookup_invocation.with(:scope, "Searching default_hierarchy of module \"#{module_name}\"") do
189     merge_strategy = nil
190     if merge_strategy.nil?
191       @module_default_lookup_options ||= {}
192       options = @module_default_lookup_options.fetch(module_name) do |k|
193         meta_invocation = Invocation.new(lookup_invocation.scope)
194         meta_invocation.lookup(LookupKey::LOOKUP_OPTIONS, k) do
195           opts = nil
196           lookup_invocation.with(:scope, "Searching for \"#{LookupKey::LOOKUP_OPTIONS}\"") do
197             catch(:no_such_key) do
198             opts = compile_patterns(
199               validate_lookup_options(
200                 provider.key_lookup_in_default(LookupKey::LOOKUP_OPTIONS, meta_invocation, MergeStrategy.strategy(HASH)), k))
201             end
202           end
203           @module_default_lookup_options[k] = opts
204         end
205       end
206       lookup_options = extract_lookup_options_for_key(key, options)
207       merge_strategy = lookup_options[MERGE] unless lookup_options.nil?
208     end
209 
210     lookup_invocation.with(:scope, "Searching for \"#{key}\"") do
211       provider.key_lookup_in_default(key, lookup_invocation, merge_strategy)
212     end
213   end
214 end
lookup_global(key, lookup_invocation, merge_strategy) click to toggle source
    # File lib/puppet/pops/lookup/lookup_adapter.rb
126 def lookup_global(key, lookup_invocation, merge_strategy)
127   # hiera_xxx will always use global_provider regardless of data_binding_terminus setting
128   terminus = lookup_invocation.hiera_xxx_call? ? :hiera : Puppet[:data_binding_terminus]
129   case terminus
130   when :hiera, 'hiera'
131     provider = global_provider(lookup_invocation)
132     throw :no_such_key if provider.nil?
133     provider.key_lookup(key, lookup_invocation, merge_strategy)
134   when :none, 'none', '', nil
135     # If global lookup is disabled, immediately report as not found
136     lookup_invocation.report_not_found(key)
137     throw :no_such_key
138   else
139     lookup_invocation.with(:global, terminus) do
140       catch(:no_such_key) do
141         return lookup_invocation.report_found(key, Puppet::DataBinding.indirection.find(key.root_key,
142           {:environment => environment, :variables => lookup_invocation.scope, :merge => merge_strategy}))
143       end
144       lookup_invocation.report_not_found(key)
145       throw :no_such_key
146     end
147   end
148 rescue Puppet::DataBinding::LookupError => detail
149   raise detail unless detail.issue_code.nil?
150   error = Puppet::Error.new(_("Lookup of key '%{key}' failed: %{detail}") % { key: lookup_invocation.top_key, detail: detail.message })
151   error.set_backtrace(detail.backtrace)
152   raise error
153 end
lookup_in_environment(key, lookup_invocation, merge_strategy) click to toggle source
    # File lib/puppet/pops/lookup/lookup_adapter.rb
155 def lookup_in_environment(key, lookup_invocation, merge_strategy)
156   provider = env_provider(lookup_invocation)
157   throw :no_such_key if provider.nil?
158   provider.key_lookup(key, lookup_invocation, merge_strategy)
159 end
lookup_in_module(key, lookup_invocation, merge_strategy) click to toggle source
    # File lib/puppet/pops/lookup/lookup_adapter.rb
161 def lookup_in_module(key, lookup_invocation, merge_strategy)
162   module_name = lookup_invocation.module_name
163 
164   # Do not attempt to do a lookup in a module unless the name is qualified.
165   throw :no_such_key if module_name.nil?
166 
167   provider = module_provider(lookup_invocation, module_name)
168   if provider.nil?
169     if environment.module(module_name).nil?
170       lookup_invocation.report_module_not_found(module_name)
171     else
172       lookup_invocation.report_module_provider_not_found(module_name)
173     end
174     throw :no_such_key
175   end
176   provider.key_lookup(key, lookup_invocation, merge_strategy)
177 end
lookup_lookup_options(key, lookup_invocation) click to toggle source

Retrieve the lookup options that match the given `name`.

@param key [LookupKey] The key for which we want lookup options @param lookup_invocation [Puppet::Pops::Lookup::Invocation] the lookup invocation @return [String,Hash,nil] The found lookup options or nil

    # File lib/puppet/pops/lookup/lookup_adapter.rb
233 def lookup_lookup_options(key, lookup_invocation)
234   module_name = key.module_name
235 
236   # Retrieve the options for the module. We use nil as a key in case we have no module
237   if !@lookup_options.include?(module_name)
238     options = retrieve_lookup_options(module_name, lookup_invocation, MergeStrategy.strategy(HASH))
239     @lookup_options[module_name] = options
240   else
241     options = @lookup_options[module_name]
242   end
243   extract_lookup_options_for_key(key, options)
244 end
lookup_merge_options(key, lookup_invocation) click to toggle source

Retrieve the merge options that match the given `name`.

@param key [LookupKey] The key for which we want merge options @param lookup_invocation [Invocation] the lookup invocation @return [String,Hash,nil] The found merge options or nil

    # File lib/puppet/pops/lookup/lookup_adapter.rb
222 def lookup_merge_options(key, lookup_invocation)
223   lookup_options = lookup_lookup_options(key, lookup_invocation)
224   lookup_options.nil? ? nil : lookup_options[MERGE]
225 end
set_global_hiera_config_path(path) click to toggle source

@param path [String] the absolute path name of the global hiera.yaml file. @return [LookupAdapter] self

    # File lib/puppet/pops/lookup/lookup_adapter.rb
275 def set_global_hiera_config_path(path)
276   @global_hiera_config_path = Pathname.new(path)
277   self
278 end
set_global_only() click to toggle source

Instructs the lookup framework to only perform lookups in the global layer @return [LookupAdapter] self

    # File lib/puppet/pops/lookup/lookup_adapter.rb
286 def set_global_only
287   @global_only = true
288   self
289 end

Private Instance Methods

compile_patterns(options) click to toggle source
    # File lib/puppet/pops/lookup/lookup_adapter.rb
314 def compile_patterns(options)
315   return nil if options.nil?
316   key_options = {}
317   pattern_options = {}
318   options.each_pair do |key, value|
319     if key.start_with?(LOOKUP_OPTIONS_PATTERN_START)
320       pattern_options[Regexp.compile(key)] = value
321     else
322       key_options[key] = value
323     end
324   end
325   [key_options.empty? ? nil : key_options, pattern_options.empty? ? nil : pattern_options]
326 end
do_lookup(key, lookup_invocation, merge) click to toggle source
    # File lib/puppet/pops/lookup/lookup_adapter.rb
328 def do_lookup(key, lookup_invocation, merge)
329   if lookup_invocation.global_only?
330     key.dig(lookup_invocation, lookup_global(key, lookup_invocation, merge))
331   else
332     merge_strategy = Puppet::Pops::MergeStrategy.strategy(merge)
333     key.dig(lookup_invocation,
334       merge_strategy.lookup(PROVIDER_STACK, lookup_invocation) { |m| send(m, key, lookup_invocation, merge_strategy) })
335   end
336 end
env_lookup_options(lookup_invocation, merge_strategy) click to toggle source

Retrieve and cache lookup options specific to the environment of the compiler that this adapter is attached to (i.e. a merge of global and environment lookup options).

    # File lib/puppet/pops/lookup/lookup_adapter.rb
381 def env_lookup_options(lookup_invocation, merge_strategy)
382   if !instance_variable_defined?(:@env_lookup_options)
383     global_options = global_lookup_options(lookup_invocation, merge_strategy)
384     @env_only_lookup_options = nil
385     catch(:no_such_key) { @env_only_lookup_options = validate_lookup_options(lookup_in_environment(LookupKey::LOOKUP_OPTIONS, lookup_invocation, merge_strategy), nil) }
386     if global_options.nil?
387       @env_lookup_options = @env_only_lookup_options
388     elsif @env_only_lookup_options.nil?
389       @env_lookup_options = global_options
390     else
391       @env_lookup_options = merge_strategy.merge(global_options, @env_only_lookup_options)
392     end
393   end
394   @env_lookup_options
395 end
env_provider(lookup_invocation) click to toggle source
    # File lib/puppet/pops/lookup/lookup_adapter.rb
402 def env_provider(lookup_invocation)
403   @env_provider = initialize_env_provider(lookup_invocation) unless instance_variable_defined?(:@env_provider)
404   @env_provider
405 end
environment() click to toggle source

@return [Puppet::Node::Environment] the environment of the compiler that this adapter is associated with

    # File lib/puppet/pops/lookup/lookup_adapter.rb
519 def environment
520   @compiler.environment
521 end
global_lookup_options(lookup_invocation, merge_strategy) click to toggle source

Retrieve and cache the global lookup options

    # File lib/puppet/pops/lookup/lookup_adapter.rb
371 def global_lookup_options(lookup_invocation, merge_strategy)
372   if !instance_variable_defined?(:@global_lookup_options)
373     @global_lookup_options = nil
374     catch(:no_such_key) { @global_lookup_options = validate_lookup_options(lookup_global(LookupKey::LOOKUP_OPTIONS, lookup_invocation, merge_strategy), nil) }
375   end
376   @global_lookup_options
377 end
global_provider(lookup_invocation) click to toggle source
    # File lib/puppet/pops/lookup/lookup_adapter.rb
397 def global_provider(lookup_invocation)
398   @global_provider = GlobalDataProvider.new unless instance_variable_defined?(:@global_provider)
399   @global_provider
400 end
initialize_env_provider(lookup_invocation) click to toggle source
    # File lib/puppet/pops/lookup/lookup_adapter.rb
463 def initialize_env_provider(lookup_invocation)
464   env_conf = environment.configuration
465   return nil if env_conf.nil? || env_conf.path_to_env.nil?
466 
467   # Get the name of the data provider from the environment's configuration
468   provider_name = env_conf.environment_data_provider
469   env_path = Pathname(env_conf.path_to_env)
470   config_path = env_path + HieraConfig::CONFIG_FILE_NAME
471 
472   ep = nil
473   if config_path.exist?
474     ep = EnvironmentDataProvider.new
475     # A version 5 hiera.yaml trumps any data provider setting in the environment.conf
476     ep_config = ep.config(lookup_invocation)
477     if ep_config.nil?
478       ep = nil
479     elsif ep_config.version >= 5
480       unless provider_name.nil? || Puppet[:strict] == :off
481         Puppet.warn_once('deprecations', 'environment.conf#data_provider',
482           _("Defining environment_data_provider='%{provider_name}' in environment.conf is deprecated") % { provider_name: provider_name }, env_path + 'environment.conf')
483 
484         unless provider_name == 'hiera'
485           Puppet.warn_once('deprecations', 'environment.conf#data_provider_overridden',
486             _("The environment_data_provider='%{provider_name}' setting is ignored since '%{config_path}' version >= 5") % { provider_name: provider_name, config_path: config_path }, env_path + 'environment.conf')
487         end
488       end
489       provider_name = nil
490     end
491   end
492 
493   if provider_name.nil?
494     ep
495   else
496     unless Puppet[:strict] == :off
497       msg = _("Defining environment_data_provider='%{provider_name}' in environment.conf is deprecated.") % { provider_name: provider_name }
498       msg += " " + _("A '%{hiera_config}' file should be used instead") % { hiera_config: HieraConfig::CONFIG_FILE_NAME } if ep.nil?
499       Puppet.warn_once('deprecations', 'environment.conf#data_provider', msg, env_path + 'environment.conf')
500     end
501 
502     case provider_name
503     when 'none'
504       nil
505     when 'hiera'
506       # Use hiera.yaml or default settings if it is missing
507       ep || EnvironmentDataProvider.new
508     when 'function'
509       ep = EnvironmentDataProvider.new
510       ep.config = HieraConfigV5.v4_function_config(env_path, 'environment::data', ep)
511       ep
512     else
513       raise Puppet::Error.new(_("Environment '%{env}', cannot find environment_data_provider '%{provider}'") % { env: environment.name, provider: provider_name })
514     end
515   end
516 end
initialize_module_provider(lookup_invocation, module_name) click to toggle source
    # File lib/puppet/pops/lookup/lookup_adapter.rb
416 def initialize_module_provider(lookup_invocation, module_name)
417   mod = environment.module(module_name)
418   return nil if mod.nil?
419 
420   metadata = mod.metadata
421   provider_name = metadata.nil? ? nil : metadata['data_provider']
422 
423   mp = nil
424   if mod.has_hiera_conf?
425     mp = ModuleDataProvider.new(module_name)
426     # A version 5 hiera.yaml trumps a data provider setting in the module
427     mp_config = mp.config(lookup_invocation)
428     if mp_config.nil?
429       mp = nil
430     elsif mp_config.version >= 5
431       unless provider_name.nil? || Puppet[:strict] == :off
432         Puppet.warn_once('deprecations', "metadata.json#data_provider-#{module_name}",
433           _("Defining \"data_provider\": \"%{name}\" in metadata.json is deprecated. It is ignored since a '%{config}' with version >= 5 is present") % { name: provider_name, config: HieraConfig::CONFIG_FILE_NAME }, mod.metadata_file)
434       end
435       provider_name = nil
436     end
437   end
438 
439   if provider_name.nil?
440     mp
441   else
442     unless Puppet[:strict] == :off
443       msg = _("Defining \"data_provider\": \"%{name}\" in metadata.json is deprecated.") % { name: provider_name }
444       msg += " " + _("A '%{hiera_config}' file should be used instead") % { hiera_config: HieraConfig::CONFIG_FILE_NAME } if mp.nil?
445       Puppet.warn_once('deprecations', "metadata.json#data_provider-#{module_name}", msg, mod.metadata_file)
446     end
447 
448     case provider_name
449     when 'none'
450       nil
451     when 'hiera'
452       mp || ModuleDataProvider.new(module_name)
453     when 'function'
454       mp = ModuleDataProvider.new(module_name)
455       mp.config = HieraConfig.v4_function_config(Pathname(mod.path), "#{module_name}::data", mp)
456       mp
457     else
458       raise Puppet::Error.new(_("Environment '%{env}', cannot find module_data_provider '%{provider}'")) % { env: environment.name, provider: provider_name }
459     end
460   end
461 end
module_provider(lookup_invocation, module_name) click to toggle source
    # File lib/puppet/pops/lookup/lookup_adapter.rb
407 def module_provider(lookup_invocation, module_name)
408   # Test if the key is present for the given module_name. It might be there even if the
409   # value is nil (which indicates that no module provider is configured for the given name)
410   unless self.include?(module_name)
411     self[module_name] = initialize_module_provider(lookup_invocation, module_name)
412   end
413   self[module_name]
414 end
retrieve_lookup_options(module_name, lookup_invocation, merge_strategy) click to toggle source

Retrieve lookup options that applies when using a specific module (i.e. a merge of the pre-cached `env_lookup_options` and the module specific data)

    # File lib/puppet/pops/lookup/lookup_adapter.rb
342 def retrieve_lookup_options(module_name, lookup_invocation, merge_strategy)
343   meta_invocation = Invocation.new(lookup_invocation.scope)
344   meta_invocation.lookup(LookupKey::LOOKUP_OPTIONS, lookup_invocation.module_name) do
345     meta_invocation.with(:meta, LOOKUP_OPTIONS) do
346       if meta_invocation.global_only?
347         compile_patterns(global_lookup_options(meta_invocation, merge_strategy))
348       else
349         opts = env_lookup_options(meta_invocation, merge_strategy)
350         unless module_name.nil?
351           # Store environment options at key nil. This removes the need for an additional lookup for keys that are not prefixed.
352           @lookup_options[nil] = compile_patterns(opts) unless @lookup_options.include?(nil)
353           catch(:no_such_key) do
354             module_opts = validate_lookup_options(lookup_in_module(LookupKey::LOOKUP_OPTIONS, meta_invocation, merge_strategy), module_name)
355             opts = if opts.nil?
356               module_opts
357             elsif module_opts
358               merge_strategy.lookup([GLOBAL_ENV_MERGE, "Module #{lookup_invocation.module_name}"], meta_invocation) do |n|
359                 meta_invocation.with(:scope, n) { meta_invocation.report_found(LOOKUP_OPTIONS,  n == GLOBAL_ENV_MERGE ? opts : module_opts) }
360               end
361             end
362           end
363         end
364         compile_patterns(opts)
365       end
366     end
367   end
368 end
validate_lookup_options(options, module_name) click to toggle source
    # File lib/puppet/pops/lookup/lookup_adapter.rb
295 def validate_lookup_options(options, module_name)
296   return nil if options.nil?
297   raise Puppet::DataBinding::LookupError.new(_("value of %{opts} must be a hash") % { opts: LOOKUP_OPTIONS }) unless options.is_a?(Hash)
298   return options if module_name.nil?
299 
300   pfx = "#{module_name}::"
301   options.each_pair do |key, value|
302     if key.start_with?(LOOKUP_OPTIONS_PATTERN_START)
303       unless key[1..pfx.length] == pfx
304         raise Puppet::DataBinding::LookupError.new(_("all %{opts} patterns must match a key starting with module name '%{module_name}'") % { opts: LOOKUP_OPTIONS, module_name: module_name })
305       end
306     else
307       unless key.start_with?(pfx)
308         raise Puppet::DataBinding::LookupError.new(_("all %{opts} keys must start with module name '%{module_name}'") % { opts: LOOKUP_OPTIONS, module_name: module_name })
309       end
310     end
311   end
312 end