Reputation: 89
This is an ugly, high maintenance factory. I really just need a way to use the string to instantiate an object with a name that matches the string. I think metaclass is the answer but I can't figure out how to apply it:
from commands.shVersionCmd import shVersionCmd
from commands.shVRFCmd import shVRFCmd
def CommandFactory(commandnode):
if commandnode.attrib['name'] == 'shVersionCmd': return shVersionCmd(commandnode)
if commandnode.attrib['name'] == 'shVRFCmd': return shVRFCmd(commandnode)
Upvotes: 0
Views: 207
Reputation: 2079
My personal preference would be to turn the dependencies between the factory and the command implementations around, so that each command registers itself with the factory.
Example implementation:
File commands/__init__.py:
import pkgutil
import commands
_commands = {}
def command(commandCls):
_commands[commandCls.__name__] = commandCls
return commandCls
def CommandFactory(commandnode):
name = commandnode.attrib['name']
if name in _commands.keys():
return _commands[name](commandnode)
# Load all commands
for loader, module_name, is_pkg in pkgutil.walk_packages(commands.__path__):
if module_name!=__name__:
module = loader.find_module(module_name).load_module(module_name)
File commands/mycommand.py:
from commands import command
@command
class MyCommand(object):
def __init__(self, commandnode):
pass
Small test:
from commands import CommandFactory
# Stub node implementation
class Node(object):
def __init__(self, name):
self.attrib = { "name": name }
if __name__=='__main__':
cmd = CommandFactory(Node("MyCommand"))
assert cmd.__class__.__name__=="MyCommand", "New command is instance of MyCommand"
cmd = CommandFactory(Node("UnknownCommand"))
assert cmd is None, "Returns None for unknown command type"
Upvotes: 0
Reputation: 56921
eval
is your friend:
from commands import *
def CommandFactory(commandnode):
name=commandnode.attrib['name']
assert name in ( "shVersionCmd", "shVRFCmd" ), "illegal command"
return eval(name+"."+name)(commandnode)
Note that if you are sure that name
will never contain any illegal commands, you could remove the assert
and turn the function into a no-maintenance-delight. In case of doubt, leave it in and maintain the list in a single place.
Upvotes: 0
Reputation: 376052
You can look up global names with the globals()
function, which returns a dict:
from commands.shVersionCmd import shVersionCmd
from commands.shVRFCmd import shVRFCmd
# An explicit list of allowed commands to prevent malicious activity.
commands = ['shVersionCmd', 'shVRFCmd']
def CommandFactory(commandnode):
cmd = commandnode.attrib['name']
if cmd in commands:
fn = globals()[cmd]
fn(commandnode)
Upvotes: 2
Reputation: 34457
This answer How to make an anonymous function in Python without Christening it? discusses how to cleanly call blocks of code based on a key
Upvotes: 0