Importing a python module without actually executing it

后端 未结 6 2077
抹茶落季
抹茶落季 2020-12-10 01:48

In the context of a complex application, I need to import user-supplied \'scripts\'. Ideally, a script would have

def init():
    blah

def execute():
    mo         


        
6条回答
  •  余生分开走
    2020-12-10 02:14

    Not sure if you'll consider this elegant, but it is somewhat intelligent in the sense that it recognizes when def init are tokens and not just part of a tricky multi-line string:

    '''
    def init does not define init...
    '''
    

    It will not recognize when init is defined in tricky alternate ways such as

    init = lambda ...
    

    or

    codestr='def  i'+'nit ...'
    exec(codestr)
    

    The only way to handle all such cases is to run the code (e.g. in a sandbox or by importing) and inspect the result.


    import tokenize
    import token
    import io
    import collections
    
    userscript = '''\
    def init():
        blah
    
    """
    def execute():
        more blah
    """
    
    yadda
    '''
    
    class Token(object):
        def __init__(self, tok):
            toknum, tokval, (srow, scol), (erow, ecol), line = tok
            self.toknum = toknum
            self.tokname = token.tok_name[toknum]
            self.tokval = tokval
            self.srow = srow
            self.scol = scol
            self.erow = erow
            self.ecol = ecol
            self.line = line    
    
    class Validator(object):
        def __init__(self, codestr):
            self.codestr = codestr
            self.toks = collections.deque(maxlen = 2)
            self.names = set()
        def validate(self):
            tokens = tokenize.generate_tokens(io.StringIO(self.codestr).readline)
            self.toks.append(Token(next(tokens)))
            for tok in tokens:
                self.toks.append(Token(tok))            
                if (self.toks[0].tokname == 'NAME'     # First token is a name
                    and self.toks[0].scol == 0         # First token starts at col 0
                    and self.toks[0].tokval == 'def'   # First token is 'def'
                    and self.toks[1].tokname == 'NAME' # Next token is a name
                    ):
                    self.names.add(self.toks[1].tokval)
            delta = set(['init', 'cleanup', 'execute']) - self.names
            if delta:
                raise ValueError('{n} not defined'.format(n = ' and '.join(delta)))
    
    v = Validator(userscript)
    v.validate()
    

    yields

    ValueError: execute and cleanup not defined
    

提交回复
热议问题