Changing behaviour of namespaces

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Mikael Olofsson

    #1

    Changing behaviour of namespaces

    Hi!

    This is in Python 2.3.4 under WinXP.

    I have a situation where I think changing the behaviour of a namespace
    would be very nice. The goal is to be able to run a python file from
    another in a separate namespace in such a way that a NameError is not
    raised if a non-existing variable is used in the file. The file that I
    want to run is taken out of context, and some needed variables may be
    missing that are normally there when used in the intended context. OK!
    When I run it out of context, I do not want the execution to stop
    half-way through the file based on NameErrors, instead a predefined
    dummy variable should be used. The question is: Can I achieve that?

    Google didn't give me much, so I experimented a bit: First, I created a
    dummy object, which is meant to be used when a variable is missing:

    class dummyClass(obje ct):
    pass
    dummyObject = dummyClass()

    The actual dummy object is supposed to be a bit more complicated than
    that. The next step was to subclass dict:

    class forgivingDict(d ict):
    def __init__(self,* args,**kwargs):
    dict.__init__(s elf,*args,**kwa rgs)
    def __getitem__(sel f,key):
    print ' Getting', key
    try:
    return dict.__getitem_ _(self,key)
    except KeyError:
    return dummyObject
    def __setitem__(sel f,key,value):
    print ' Setting', key, 'to', value
    dict.__setitem_ _(self,key,valu e)
    def __contains__(se lf,item):
    return True
    def __repr__(self):
    return 'forgivingDict( %s)' % (dict.__repr__( self),)

    Then I tried to execute a file using a forgivingDict() as namespace:

    ns = forgivingDict()
    execfile(filePa th, ns)

    A file containing the following passes perfectly OK:

    a = 0
    b = a

    But a file containing the following produces a NameError:

    b = a

    The error traceback for the interested:

    Traceback (most recent call last):
    File "//charm/mikael/My
    Documents/Programmering/Python/Tester/voidTest.py", line 130, in ?
    execfile('test2 .py', ns)
    File "test2.py", line 1, in ?
    b = a
    NameError: name 'a' is not defined

    Now, that is exactly what is to expect if ns is a dict(), but I was
    hoping that b would have ended up as dummyObject when ns is a
    forgivingDict() . One thing: Nothing is printed out when execfile-ing the
    files, which means that the methods forgivingDict._ _getitem__ and
    forgivingDict._ _setitem__ are never used. My guess was that the
    execution done by execfile uses dict.__getitem_ _(ns, key) and
    dict.__setitem_ _(ns,key,value) instead of ns[key] and ns[key]=value when
    getting and setting variables. That is probably intentional from the
    developers and done that way for a good reason.

    Alternative test: I've tried to replace __builtin__.dic t with my
    forgivingDict, slightly modified to make sure that the inheritance still
    works, which didn't change anything. So it seams that the execution done
    by execfile does *not* use dict.__getitem_ _(ns, key) and
    dict.__setitem_ _(ns,key,value) after all, but something else. Probably
    the original dict is around somewhere else, and execfile uses the
    corresponding methods of that.

    My approach seems flawed. Any ideas? Can I somehow execute a complete
    file even if there are non-existent variables used somewhere? A
    try-except around the execfile statement is not an alternative, since
    that stops the execution where the error occurs.

    Should I perhaps read up on the compiler module? I mean: Using the
    compiler module to analyze the content of the file, and based on that
    updating my namespace before executing the file.

    /MiO
  • Peter Otten

    #2
    Re: Changing behaviour of namespaces

    Mikael Olofsson wrote:
    This is in Python 2.3.4 under WinXP.
    To feed an arbitrary mapping object to execfile() you need to upgrade to
    Python 2.4.



    Peter

    Comment

    • Mikael Olofsson

      #3
      Re: Changing behaviour of namespaces - solved, I think

      Peter Otten wrote:
      To feed an arbitrary mapping object to execfile() you need to upgrade to
      Python 2.4.
      Thanks! As clear as possible. I will do that.

      FYI: I think I managed to achieve what I want in Py2.3 using the
      compiler module:

      def getNamesFromAst Node(node,varSe t):
      if node.__class__ in (compiler.ast.G lobal,compiler. ast.Import):
      varSet.union_up date(node.names )
      if node.__class__ in (compiler.ast.N ame,):
      varSet.add(node .name)
      for subNode in node.getChildNo des():
      getNamesFromAst Node(subNode,va rSet)

      # Get all variable names that are referenced in the file:
      varSet = sets.Set()
      mainNode = compiler.parseF ile(filePath)
      getNamesFromAst Node(mainNode,v arSet)

      # Define a namespace and update it:
      ns = {}
      for varName in varSet:
      ns[varName] = dummyObject

      # Execute the file without NameErrors:
      execfile(filePa th,ns)

      Batteries included!

      /MiO

      Comment

      • Peter Otten

        #4
        Re: Changing behaviour of namespaces - solved, I think

        Mikael Olofsson wrote:
        Peter Otten wrote:
        >To feed an arbitrary mapping object to execfile() you need to upgrade to
        >Python 2.4.
        >
        Thanks! As clear as possible. I will do that.
        >
        FYI: I think I managed to achieve what I want in Py2.3 using the
        compiler module:
        >
        def getNamesFromAst Node(node,varSe t):
        if node.__class__ in (compiler.ast.G lobal,compiler. ast.Import):
        varSet.union_up date(node.names )
        if node.__class__ in (compiler.ast.N ame,):
        varSet.add(node .name)
        for subNode in node.getChildNo des():
        getNamesFromAst Node(subNode,va rSet)
        >
        # Get all variable names that are referenced in the file:
        varSet = sets.Set()
        mainNode = compiler.parseF ile(filePath)
        getNamesFromAst Node(mainNode,v arSet)
        >
        # Define a namespace and update it:
        ns = {}
        for varName in varSet:
        ns[varName] = dummyObject
        >
        # Execute the file without NameErrors:
        execfile(filePa th,ns)
        >
        Batteries included!
        Clearly more elegant than:
        >>print open(filename). read()
        b = a
        >>dummy = 42
        >>names = []
        >>while 1:
        .... ns = dict((n, dummy) for n in names)
        .... try:
        .... execfile(filena me, ns)
        .... except NameError, e:
        .... names.append(e[0][6:-16])
        .... else:
        .... break
        ....
        >>names
        ['a']

        :-)

        Peter

        Comment

        • Mikael Olofsson

          #5
          Re: Changing behaviour of namespaces - solved, I think

          Peter Otten wrote:
          Clearly more elegant than:
          >
          >
          >>>print open(filename). read()
          >>>>
          b = a
          >
          >>>dummy = 42
          >>>names = []
          >>>while 1:
          >>>>
          ... ns = dict((n, dummy) for n in names)
          ... try:
          ... execfile(filena me, ns)
          ... except NameError, e:
          ... names.append(e[0][6:-16])
          ... else:
          ... break
          ...
          >
          >>>names
          >>>>
          ['a']
          >
          That sure is nicer. My solution looks like shooting mosquitoes with an
          elephant rifle i comparison. Thanks.

          /MiO

          Comment

          Working...