exec with partial globals

Discussion in 'Python' started by Helmut Jarausch, Oct 30, 2012.

  1. Hi,

    I'd like to give the user the ability to enter code which may only rebind
    a given set of names but not all ones.
    This does NOT work
    A=1
    B=2
    Code=compile('A=7','','exec')
    exec(Code,{'A':0})
    print("I've got A={}".format(A)) # prints 1


    How can 'filter' the gobal namespace such that modifying 'A' is allowed
    but any attempt to modify 'B' should give an exception.


    Many thanks for a hint,
    Helmut.
    Helmut Jarausch, Oct 30, 2012
    #1
    1. Advertising

  2. On Tue, Oct 30, 2012 at 11:00 PM, Helmut Jarausch
    <-aachen.de> wrote:
    > Hi,
    >
    > I'd like to give the user the ability to enter code which may only rebind
    > a given set of names but not all ones.
    >
    > How can 'filter' the gobal namespace such that modifying 'A' is allowed
    > but any attempt to modify 'B' should give an exception.


    I don't know of any way to do that _as such_, but you can simply
    follow up the exec with direct retrieval from the globals.

    >>> a=1; b=2
    >>> code=compile("a=7",'','exec')
    >>> ns={'a':0}
    >>> exec(code,ns)
    >>> a=ns["a"]


    (Incidentally, it's normal to use lower case for most names, reserving
    the leading uppercase letter for types/classes.)

    The exec'd code gets its own namespace (defined by the dictionary,
    same as you were doing - note that the 'a' inside that namespace has
    nothing to do with the 'a' outside it), and then you explicitly fetch
    the values you want.

    A slightly more sophisticated example might include a list of shared
    variables, for example:

    shared = ['a']
    outer = globals()
    ns = {v:eek:uter[v] for v in shared}
    exec(code,ns);
    for v in shared: outer[v]=ns[v]

    Untested but should work. (Is there any way to directly apply filter()
    to a dictionary? That's what I'm really looking for here.)

    ChrisA
    Chris Angelico, Oct 30, 2012
    #2
    1. Advertising

  3. Helmut Jarausch

    Dave Angel Guest

    On 10/30/2012 08:00 AM, Helmut Jarausch wrote:
    > Hi,
    >
    > I'd like to give the user the ability to enter code which may only rebind
    > a given set of names but not all ones.
    > This does NOT work
    > A=1
    > B=2
    > Code=compile('A=7','','exec')
    > exec(Code,{'A':0})
    > print("I've got A={}".format(A)) # prints 1
    >
    >
    > How can 'filter' the gobal namespace such that modifying 'A' is allowed
    > but any attempt to modify 'B' should give an exception.
    >
    >
    > Many thanks for a hint,
    > Helmut.


    A=1
    B=2
    Code=compile('A=7','','exec')
    vars = {'A':A}
    exec(Code, vars)
    A = vars["A"]
    print("I've got A={}".format(A)) # prints 1

    That now prints "I've got A=7"

    More generally, you could write a loop, copying globals into vars, and
    another one, copying them back.

    No idea what you're really after; this is one of the more dangerous
    things to try.

    Although you can constrain the globals seen by the code, that code can
    still use builtins, do imports, delete files, etc.

    Further, if your user is clever enough, he can do:

    Code=compile('A=7; print("howdy"); import __main__;
    __main__.B=42','','exec')

    What are you really trying to permit him to do? Initialize some
    variables for you? How about an .ini file ?




    --

    DaveA
    Dave Angel, Oct 30, 2012
    #3
  4. On Tue, 30 Oct 2012 08:33:38 -0400, Dave Angel wrote:

    > On 10/30/2012 08:00 AM, Helmut Jarausch wrote:
    >> Hi,
    >>
    >> I'd like to give the user the ability to enter code which may only rebind
    >> a given set of names but not all ones.
    >> This does NOT work
    >> A=1
    >> B=2
    >> Code=compile('A=7','','exec')
    >> exec(Code,{'A':0})
    >> print("I've got A={}".format(A)) # prints 1
    >>
    >>
    >> How can 'filter' the gobal namespace such that modifying 'A' is allowed
    >> but any attempt to modify 'B' should give an exception.
    >>
    >>
    >> Many thanks for a hint,
    >> Helmut.

    >
    > A=1
    > B=2
    > Code=compile('A=7','','exec')
    > vars = {'A':A}
    > exec(Code, vars)
    > A = vars["A"]
    > print("I've got A={}".format(A)) # prints 1
    >
    > That now prints "I've got A=7"
    >
    > More generally, you could write a loop, copying globals into vars, and
    > another one, copying them back.
    >
    > No idea what you're really after; this is one of the more dangerous
    > things to try.
    >
    > Although you can constrain the globals seen by the code, that code can
    > still use builtins, do imports, delete files, etc.
    >
    > Further, if your user is clever enough, he can do:
    >
    > Code=compile('A=7; print("howdy"); import __main__;
    > __main__.B=42','','exec')
    >
    > What are you really trying to permit him to do? Initialize some
    > variables for you? How about an .ini file ?


    Many thanks Chris, many thanks to Dave.

    First, in my application only trusted people will use it.

    Here my example. I'd like to update a spreadsheet by data given by another
    spreadsheet. I like to allow to modify only certain columns of the first
    spreadsheet. The update formula and possible conditions are entered at
    run time and the available fields are only known once the spreadsheets
    have been read.

    Given spreadsheet S (Source) and D (Destination) as objects (wrapping a
    dictionary) a possible (legal) input would be

    D.price= D.price-S.discount

    No other fields of 'D' should be modifiable.

    Again, I don't need to take actions against a malicious user,
    just take care about a typo or mistake

    Thanks again,
    Helmut.
    Helmut Jarausch, Oct 30, 2012
    #4
  5. On Tue, Oct 30, 2012 at 11:57 PM, Helmut Jarausch
    <-aachen.de> wrote:
    > Given spreadsheet S (Source) and D (Destination) as objects (wrapping a
    > dictionary) a possible (legal) input would be
    >
    > D.price= D.price-S.discount
    >
    > No other fields of 'D' should be modifiable.


    That's a bit harder. What you're describing, using exec with specific
    globals, would not be able to do this. You'd need to build a proxy
    object that decides whether or not to pass on the change.

    ChrisA
    Chris Angelico, Oct 30, 2012
    #5
  6. Helmut Jarausch

    Dave Angel Guest

    On 10/30/2012 08:57 AM, Helmut Jarausch wrote:
    > On Tue, 30 Oct 2012 08:33:38 -0400, Dave Angel wrote:
    >
    >> On 10/30/2012 08:00 AM, Helmut Jarausch wrote:
    >>> Hi,
    >>>
    >>> I'd like to give the user the ability to enter code which may only rebind
    >>> a given set of names but not all ones.
    >>> This does NOT work
    >>> A=1
    >>> B=2
    >>> Code=compile('A=7','','exec')
    >>> exec(Code,{'A':0})
    >>> print("I've got A={}".format(A)) # prints 1
    >>>
    >>>
    >>> How can 'filter' the gobal namespace such that modifying 'A' is allowed
    >>> but any attempt to modify 'B' should give an exception.
    >>>
    >>>
    >>> Many thanks for a hint,
    >>> Helmut.

    >> A=1
    >> B=2
    >> Code=compile('A=7','','exec')
    >> vars = {'A':A}
    >> exec(Code, vars)
    >> A = vars["A"]
    >> print("I've got A={}".format(A)) # prints 1
    >>
    >> That now prints "I've got A=7"
    >>
    >> More generally, you could write a loop, copying globals into vars, and
    >> another one, copying them back.
    >>
    >> No idea what you're really after; this is one of the more dangerous
    >> things to try.
    >>
    >> Although you can constrain the globals seen by the code, that code can
    >> still use builtins, do imports, delete files, etc.
    >>
    >> Further, if your user is clever enough, he can do:
    >>
    >> Code=compile('A=7; print("howdy"); import __main__;
    >> __main__.B=42','','exec')
    >>
    >> What are you really trying to permit him to do? Initialize some
    >> variables for you? How about an .ini file ?

    > Many thanks Chris, many thanks to Dave.
    >
    > First, in my application only trusted people will use it.
    >
    > Here my example. I'd like to update a spreadsheet by data given by another
    > spreadsheet. I like to allow to modify only certain columns of the first
    > spreadsheet. The update formula and possible conditions are entered at
    > run time and the available fields are only known once the spreadsheets
    > have been read.
    >
    > Given spreadsheet S (Source) and D (Destination) as objects (wrapping a
    > dictionary) a possible (legal) input would be
    >
    > D.price= D.price-S.discount
    >
    > No other fields of 'D' should be modifiable.
    >
    > Again, I don't need to take actions against a malicious user,
    > just take care about a typo or mistake
    >
    > Thanks again,
    > Helmut.
    >


    If the user will only be modifying fields of specific class instances,
    then generate those instances with property wrappers around the readonly
    attributes.

    If the class were statically defined, you'd do something like:


    class MyClass(object):
    def __init__(self, data1, data2, data3):
    self._data1 = data1
    self._data2 = data2
    self.data3 = data3 #this one is writable, directly

    @property
    def data1(self): #this is readonly
    return self._data1

    @property
    def data2(self): #this is readonly
    return self._data2

    mysrc = MyClass(1,2,3)
    mydest = MyClass(4,5,6)

    print( mydest.data1, mydest.data2, mydest.data3, "\n") #prints 4,5,6

    vars = {"src": mysrc, "dest": mydest}

    Code=compile('dest.data3=17','','exec')
    exec(Code, vars)

    print( mydest.data1, mydest.data2, mydest.data3, "\n") #prints 4,5,17


    Now, once you see how to do it statically, you can try to do it dynamically, deciding which attributes need property wrappers, and which ones are plain data.


    --

    DaveA
    Dave Angel, Oct 30, 2012
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Giles Brown

    Question about "exec in globals, locals"

    Giles Brown, Jul 4, 2003, in forum: Python
    Replies:
    2
    Views:
    344
    Adrien Di Mascio
    Jul 4, 2003
  2. tedsuzman
    Replies:
    2
    Views:
    7,063
    Michel Claveau, résurectionné d'outre-bombe inform
    Jul 21, 2004
  3. Ted
    Replies:
    1
    Views:
    451
    Duncan Booth
    Jul 22, 2004
  4. Replies:
    2
    Views:
    354
  5. Jonathan S
    Replies:
    5
    Views:
    442
    Jonathan S
    Dec 9, 2010
Loading...

Share This Page