Alexandr
Alexandr

Reputation: 160

How to check in python if some class (by string name) exists?

Is it possible to check if some class exists? I have class names in a json config file. I know that I can simply try to create an object by class name string but that is actually a bad idea because the class constructor can do some unexpected stuff while at this point in time I just want to check if my config is valid and all mentioned classes are available.

Is there any way to do it?

EDIT: Also I do understand that u can get all the methods from some module, in my case I am not sure and don't actually care from what module comes the method. It can be from any import statement and I probably don't know where exactly from.

Upvotes: 7

Views: 9598

Answers (3)

Wolf
Wolf

Reputation: 10238

I tried the built-in type function, which worked for me, but there is maybe a more pythonic way to test for the existence of a class:

import types

def class_exist(className):
    result = False
    try:
        result = (eval("type("+className+")") == types.ClassType)
    except NameError:
        pass
    return result

# this is a test class, it's only purpose is pure existence:
class X: 
    pass

print class_exist('X')
print class_exist('Y')

The output is

True
False

Of course, this is a basic solution which should be used only with well-known input: the eval function can be a great a back door opener. There is a more reliable (but also compact) solution by wenzul.

Upvotes: 3

wenzul
wenzul

Reputation: 4058

Using eval() leaves the door open for arbitrary code execution, for security's sake it should be avoided.

Especially if you ask for a solution for such a problem here. Then we can assume that you do not know these risks sufficiently.

import sys

def str_to_class(str):
    return reduce(getattr, str.split("."), sys.modules[__name__])

try:
    cls = str_to_class(<json-fragment-here>)
except AttributeError:
    cls = None

if cls:
    obj = cls(...)
else:
    # fight against this

This avoids using eval and is approved by several SO users. Solution is similar to Convert string to Python class object?.

Upvotes: 3

Padraic Cunningham
Padraic Cunningham

Reputation: 180441

You can parse the source to get all the class names:

from ast import ClassDef, parse
import importlib
import inspect

mod = "test"
mod = importlib.import_module(mod)
p = parse(inspect.getsource(mod))
names = [kls.name for kls in p.body if isinstance(kls, ClassDef)]

Input:

class Foo(object):
   pass

class Bar(object):
    pass

Output:

['Foo', 'Bar']

Just compare the class names from the config to the names returned.

{set of names in config}.difference(names)

If you want to include imported names you can parse the module it was imported from but depending on how it was imported you can still find cases that won't work:

from ast import ClassDef, parse, ImportFrom
import importlib
import inspect

mod = "test"
mod = importlib.import_module(mod)
p = parse(inspect.getsource(mod))
names = []
for node in p.body:
    if isinstance(node, ClassDef):
        names.append(node.name)
    elif isinstance(node, ImportFrom):
        names.extend(imp.name for imp in node.names)

print(names)

Input:

from test2 import Foobar, Barbar, foo  

class Foo(object):
   pass

class Bar(object):
    pass

test2:

foo = 123

class Foobar(object):
    pass

class Barbar(object):
    pass

Output:

['Foobar', 'Barbar', 'Foo', 'Bar']

Upvotes: 3

Related Questions