lib/pycall/import.rb in pycall-0.1.0.alpha.20170502 vs lib/pycall/import.rb in pycall-0.1.0.alpha.20170711

- old
+ new

@@ -25,30 +25,63 @@ raise PyError.fetch unless mod define_singleton_method(as) { mod } end + # This function is implemented as a mimic of `import_from` function defined in `Python/ceval.c`. def pyfrom(mod_name, import: nil) raise ArgumentError, "missing identifiers to be imported" unless import - mod = PyCall.import_module(mod_name) - raise PyError.fetch unless mod + mod_name = mod_name.to_str if mod_name.respond_to? :to_str + mod_name = mod_name.to_s if mod_name.is_a? Symbol - case import - when Hash - import.each do |attr, as| - val = PyCall.getattr(mod, attr) - define_name(as, val) + import = Array(import) + fromlist = LibPython.PyTuple_New(import.length) + import.each_with_index do |import_name, i| + name = case import_name + when assoc_array_matcher + import_name[0] + when Symbol, String + import_name + end + LibPython.PyTuple_SetItem(fromlist, i, Conversions.from_ruby(name)) + end + + main_dict = PyCall::Eval.__send__ :main_dict + globals = main_dict.__pyobj__ # FIXME: this should mimic to `import_name` function defined in `Python/ceval.c`. + locals = main_dict.__pyobj__ # FIXME: this should mimic to `import_name` function defined in `Python/ceval.c`. + level = 0 # TODO: support prefixed dots (#25) + mod = LibPython.PyImport_ImportModuleLevel(mod_name, globals, locals, fromlist, level) + raise PyError.fetch if mod.null? + mod = mod.to_ruby + + import.each do |import_name| + case import_name + when assoc_array_matcher + name, asname = *import_name + when Symbol, String + name, asname = import_name, import_name end - when Array - import.each do |attr| - val = PyCall.getattr(mod, attr) - define_name(attr, val) + + if PyCall.hasattr?(mod, name) + pyobj = PyCall.getattr(mod, name) + define_name(asname, pyobj) + next end - when Symbol, String - val = PyCall.getattr(mod, import) - define_name(import, val) + + if PyCall.hasattr?(mod, :__name__) + pkgname = PyCall.getattr(mod, :__name__) + fullname = "#{pkgname}.#{name}" + module_dict = LibPython.PyImport_GetModuleDict() + if PyCall.getattr(module_dict, fullname) + pyobj = PyCall.getattr(module_dict, fullname) + define_name(asname, pyobj) + next + end + end + + raise ArgumentError, "cannot import name #{fullname}" unless pyobj end end private @@ -68,9 +101,15 @@ def check_valid_module_variable_name(mod_name, var_name) var_name = var_name.to_s if var_name.kind_of? Symbol if var_name.include?('.') raise ArgumentError, "#{var_name} is not a valid module variable name, use pyimport #{mod_name}, as: <name>" + end + end + + def assoc_array_matcher + @assoc_array_matcher ||= ->(ary) do + ary.is_a?(Array) && ary.length == 2 end end end end