Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #32481 > unrolled thread

exec with partial globals

Started byHelmut Jarausch <jarausch@igpm.rwth-aachen.de>
First post2012-10-30 12:00 +0000
Last post2012-10-30 09:39 -0400
Articles 6 — 3 participants

Back to article view | Back to comp.lang.python


Contents

  exec with partial globals Helmut Jarausch <jarausch@igpm.rwth-aachen.de> - 2012-10-30 12:00 +0000
    Re: exec with partial globals Chris Angelico <rosuav@gmail.com> - 2012-10-30 23:28 +1100
    Re: exec with partial globals Dave Angel <d@davea.name> - 2012-10-30 08:33 -0400
      Re: exec with partial globals Helmut Jarausch <jarausch@igpm.rwth-aachen.de> - 2012-10-30 12:57 +0000
        Re: exec with partial globals Chris Angelico <rosuav@gmail.com> - 2012-10-31 00:18 +1100
        Re: exec with partial globals Dave Angel <d@davea.name> - 2012-10-30 09:39 -0400

#32481 — exec with partial globals

FromHelmut Jarausch <jarausch@igpm.rwth-aachen.de>
Date2012-10-30 12:00 +0000
Subjectexec with partial globals
Message-ID<af9tq3FnmkaU1@mid.dfncis.de>
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.

[toc] | [next] | [standalone]


#32482

FromChris Angelico <rosuav@gmail.com>
Date2012-10-30 23:28 +1100
Message-ID<mailman.3086.1351600127.27098.python-list@python.org>
In reply to#32481
On Tue, Oct 30, 2012 at 11:00 PM, Helmut Jarausch
<jarausch@igpm.rwth-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:outer[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

[toc] | [prev] | [next] | [standalone]


#32483

FromDave Angel <d@davea.name>
Date2012-10-30 08:33 -0400
Message-ID<mailman.3087.1351600438.27098.python-list@python.org>
In reply to#32481
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

[toc] | [prev] | [next] | [standalone]


#32485

FromHelmut Jarausch <jarausch@igpm.rwth-aachen.de>
Date2012-10-30 12:57 +0000
Message-ID<afa15qFnmkaU2@mid.dfncis.de>
In reply to#32483
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.

[toc] | [prev] | [next] | [standalone]


#32487

FromChris Angelico <rosuav@gmail.com>
Date2012-10-31 00:18 +1100
Message-ID<mailman.3088.1351603139.27098.python-list@python.org>
In reply to#32485
On Tue, Oct 30, 2012 at 11:57 PM, Helmut Jarausch
<jarausch@igpm.rwth-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

[toc] | [prev] | [next] | [standalone]


#32488

FromDave Angel <d@davea.name>
Date2012-10-30 09:39 -0400
Message-ID<mailman.3089.1351604375.27098.python-list@python.org>
In reply to#32485
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

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web