1

I have a long list of possible files I need to import. I will only ever need 1 of them and they all have the same interface. (Choosing a payment gateway to process a payment)

Suppose I have a dictionary that represents the name of all the gateway files.

i.e.

gateways = {
   '1' : 'authorize',
   '2' : 'paysimple',
   '3' : 'braintreepayments',
   '4' : 'etc',
}

I know the keys to this dictionary based on information from a database. So, if I receive a payment process request with a gateway value of 1 I know it needs to be handled by Authorize.net. A 2 is to be processed by Pay Simple. Etc.

I'd like to be able to create an import statement that is built with the information I know rather than a horrible list of elif statements.

Consider the simple method below:

# For the purposes of this example assume payment_gateway is defined
# elsewhere and represents the key to the dictionary
gateway_file = gateways.get(payment_gateway)

import_str = "from gateway_interface.%s import process" % gateway_file
gogo(import_str)

Where gogo is a way to cause the import statement to actually import.

Is such a thing possible?

Rico
  • 5,692
  • 8
  • 46
  • 63
  • 2
    Are you sure your process only handles one payment and then exits? That sounds unusual. – Ned Batchelder Jul 10 '12 at 00:16
  • I'm not sure how that is unusual. If you do online shopping and request a checkout why would you process the transaction with more than one gateway? – Rico Jul 10 '12 at 15:42
  • You would only use one at a time, but typically online shopping would be handled by a long-running process that handles many requests over its lifetime. – Ned Batchelder Jul 10 '12 at 17:32
  • @Ned Batchelder Can you elaborate on that idea? I'm not sure I understand why leaving the process running would be advantageous. We are using Django and posting to a view, which will spawn this process. Do you suggest a better way to handle this? – Rico Jul 10 '12 at 17:49

3 Answers3

5

Simplest

process = __import__('gateway_interface.'+gateway_file,fromlist=['foo']).process

EDIT: 'foo' in the fromlist can be anything, as long as fromlist is not an emptylist. That little bit of strangeness is explained in Why does Python's __import__ require fromlist?.

I also had to edit because in my first post __import__ didn't work as expected as further described in Python's __import__ doesn't work as expected.

if you have python 2.7

import importlib
process = importlib.import_module('gateway_interface.'+gateway_file).process

WAAAAY cool would be to use package_tools (e.g. from pkg_resources import iter_entry_points)

That could give you a solution to find the right function even if they are in odd packages not under gateway_interface. If they are all on one place and you don't need the sytle-point that is overkill so ... yeah just __import__

Community
  • 1
  • 1
Phil Cooper
  • 5,747
  • 1
  • 25
  • 41
  • This isn't quite working for me, and I'm not sure what I've done wrong. I'm getting the following error: `'module' object has no attribute 'process'` – Rico Jul 10 '12 at 15:39
  • I'll test, not sure which of the two approaches you tried. is there an `__init__.py` in the gateway_file subdir? – Phil Cooper Jul 10 '12 at 15:49
  • Everything is within the same module `gateway_interface`. The file I need to import has a class called `process` that I need to instantiate. Yes, `gateway_interface` has `__init__.py`. – Rico Jul 10 '12 at 16:30
  • I fixed the getattr version. I also added the fromlist keyword to `__import__`. I'll post an edit in the answer to explain why that is needed but it's the difference between a package vs a module import. The current edit should work. – Phil Cooper Jul 10 '12 at 16:46
  • Yes, awesome! I actually tried this both pre-2.7 and the `importlib` way. I like the latter. :) – Rico Jul 10 '12 at 17:29
2

Take a look at the imp module which allows you to access the internals of the import statement, or the __import__ method itself - either of these should allow you to achieve what you describe I think.

Mark Streatfield
  • 3,189
  • 1
  • 22
  • 19
1

The builtin __import__ method should work:

process = __import__(gateways.get(payment_gateway)).process
Arkady
  • 14,305
  • 8
  • 42
  • 46