Diagnosis of errors in case of duck failure

I have a "factory" that instantiates classes using a tuple of arguments:

def factory(cls, arg):
  return cls(*arg)

class Foo(object):
  def __init__(self, a, b):
    pass

a = (1,2)
f = factory(Foo, a)

This works well, so I decided (for various reasons beyond the scope of this question) to add support for classes that do not accept any arguments as backups so that they can be used more widely within existing code. So I needed to discover the availability of the constructor with 2-arg vs 0-arg and the corresponding return. Duck typing seems like a "Reply to Python" and works great:

def factory(cls, arg):
  try:
    return cls(*arg)
  except TypeError:
    print("Missing 2-arg, falling back to 0-arg ctor")
    return cls()

class Foo(object):
  def __init__(self,a,b):
    pass

class Bar(object):
  def __init__(self,a):
    pass

a = (1,2)
f = factory(Foo, a)
b = factory(Bar, a)

The problem arises, however, if there is an error inside one of the functions __init__. I tried to be useful and warn when neither 0-arg nor 2-arg exist __init__:

def factory(cls, arg):
  try:
    return cls(*arg)
  except TypeError:
    print("%s Missing 2-arg, falling back to 0-arg ctor" % str(cls))
    return cls()

class Foo(object):
  def __init__(self,a,b):
    iter(a) # Oops, TypeError

a = (1,2)
try:
  f = factory(Foo, a)
except TypeError: # 0-arg must be missing too
  print("Neither 2-arg nor 0-arg ctor exist, that all we support, sorry")

- a TypeError, , __init__, TypeError, - __init__.

Foo Bar, "factory" .

, TypeError ? , , - , .

+3
1

, __init__, .

class Foo(object):
    def __init__(self,a,b):
        pass

class Bar(object):
    def __init__(self,a):
        pass

class Baz(object):
    def __init__(self):
        pass

def ctr_arg_count(cls):
    fn = getattr(cls, '__init__')
    return len(inspect.getargspec(fn).args) - 1

def factory(cls, *args):
    if len(args) != ctr_arg_count(cls):
        print("Can't initialize %s - wrong number of arguments" % cls)
        return None
    return cls(*args)

print factory(Foo, 1, 2)
print factory(Bar, 1)
print factory(Baz)
print factory(Foo, 1)

>>> <__main__.Foo object at 0x100483d10>
    <__main__.Bar object at 0x100483d10>
    <__main__.Baz object at 0x100483d10>
    Can't initialize <class '__main__.Foo'> - wrong number of arguments
    None
+1

All Articles