Turn of globals in a function?

R

Ron_Adam

Is there a way to hide global names from a function or class?

I want to be sure that a function doesn't use any global variables by
mistake. So hiding them would force a name error in the case that I
omit an initialization step. This might be a good way to quickly
catch some hard to find, but easy to fix, errors in large code blocks.

Examples:

def a(x):
# ...
x = y # x is assigned to global y unintentionally.
# ...
return x

def b(x):
# hide globals somehow
# ...
x = y # Cause a name error
# ...
return x


y = True
*** name error here ***


Ron_Adam
 
M

Michael Spencer

Ron_Adam said:
Is there a way to hide global names from a function or class?

I want to be sure that a function doesn't use any global variables by
mistake. So hiding them would force a name error in the case that I
omit an initialization step. This might be a good way to quickly
catch some hard to find, but easy to fix, errors in large code blocks.

Examples:

def a(x):
# ...
x = y # x is assigned to global y unintentionally.
# ...
return x

def b(x):
# hide globals somehow
# ...
x = y # Cause a name error
# ...
return x


y = True



*** name error here ***


Ron_Adam
For testing, you could simply execute the function in an empty dict:
... print a
... Traceback (most recent call last):
File "<input>", line 1, in ?

This would get more complicated when you wanted to test calling with parameters,
so with a little more effort, you can create a new function where the globals
binding is to an empty dict:
Traceback (most recent call last):
File "<input>", line 1, in ?

HTH

Michael
 
B

Bengt Richter

Is there a way to hide global names from a function or class?

I want to be sure that a function doesn't use any global variables by
mistake. So hiding them would force a name error in the case that I
omit an initialization step. This might be a good way to quickly
catch some hard to find, but easy to fix, errors in large code blocks.

Examples:

def a(x):
# ...
x = y # x is assigned to global y unintentionally.
# ...
return x

def b(x):
# hide globals somehow
# ...
x = y # Cause a name error
# ...
return x
If you put the above def b in e.g. a_module.py,
and do a (untested ;-)

from a_module import b

instead of defining it locally, then the global references
from b (and whatever else you import from a_module)
should be to the global dict defined for a_module (i.e., its
outermost scope), not to the globals where you do the import.
y = True

True
Should work if you define a in place having same scope as the y assignment
*** name error here ***
UIAM it should do this if you import b as above.

Regards,
Bengt Richter
 
R

Ron_Adam

For testing, you could simply execute the function in an empty dict:

... print a
...
Traceback (most recent call last):
File "<input>", line 1, in ?

I didn't know you could do that. Interesting. :)

I was hoping for something in line that could use with an assert
statement. But this is good too, I'll have to play around with it a
bit. Thanks.

Ron
 
R

Ron_Adam

If you put the above def b in e.g. a_module.py,
and do a (untested ;-)

from a_module import b

instead of defining it locally, then the global references
from b (and whatever else you import from a_module)
should be to the global dict defined for a_module (i.e., its
outermost scope), not to the globals where you do the import.

Should work if you define a in place having same scope as the y assignment
UIAM it should do this if you import b as above.

Regards,
Bengt Richter

Good suggestion. Thanks. I was somewhat aware of the modular scope,
but was looking for way to apply it on a more local level. Michael's
suggestion looks interesting for that.

Ron_Adam
 
O

Oren Tirosh

Ron_Adam said:
Is there a way to hide global names from a function or class?

I want to be sure that a function doesn't use any global variables by
mistake. So hiding them would force a name error in the case that I
omit an initialization step. This might be a good way to quickly
catch some hard to find, but easy to fix, errors in large code blocks.

def noglobals(f):
.. import new
.. return new.function(
.. f.func_code,
.. {'__builtins__':__builtins__},
.. f.func_name,
.. f.func_defaults,
.. f.func_closure
.. )

You can use it with the Python 2.4 @decorator syntax:

@noglobals
def a(...):
.. # code here

Doing this for a class is a little more work. You will need dig inside
to perform this treatment on each method separately and handle new and
old-style classes a bit differently.

Note that this kind of function may declare globals. They will be
persistent but private to the function.

Oren
 
R

Ron_Adam

def noglobals(f):
. import new
. return new.function(
. f.func_code,
. {'__builtins__':__builtins__},
. f.func_name,
. f.func_defaults,
. f.func_closure
. )

You can use it with the Python 2.4 @decorator syntax:

@noglobals
def a(...):
. # code here

Cool! I haven't played with decorators yet. :)

I noticed the 'new' module is depreciated. It referred me to call the
object type directly instead. So this is probably the better way.

def noglobals(f):
return type(f)(
f.func_code,
{'__builtins__':__builtins__},
f.func_name,
f.func_defaults,
f.func_closure )

@noglobals
def a():
global x
try: x
except: x=0
x += 1
return x

x = 5
for n in range(10):
print a()
print x # x is still 5


So this is another, but longer, way to do a generator.

function(code, globals[, name[, argdefs[, closure]]])

Create a function object from a code object and a dictionary.
The optional name string overrides the name from the code object.
The optional argdefs tuple specifies the default argument values.
The optional closure tuple supplies the bindings for free variables.
What are 'free variables'?

And is there a way to directly read what names in a function are set
with the global statement? (Other than looking at the monitor. ;)

Ron_Adam
 
G

Greg Ewing

Oren said:
def noglobals(f):
. import new
. return new.function(
. f.func_code,
. {'__builtins__':__builtins__},
. f.func_name,
. f.func_defaults,
. f.func_closure
. )

Be aware that this will render the function incapable
of seeing *any* globals at all, including other
functions and classes defined in the same module --
which you may find rather inconvenient!
 
R

Ron_Adam

Be aware that this will render the function incapable
of seeing *any* globals at all, including other
functions and classes defined in the same module --
which you may find rather inconvenient!


Developing this idea further...

This allows a programmer to specify what globals to allow read and or
writes.


Cheers,
Ron


#---start---
# useglobals.py

"""
A function to specify what globals and builtins
a function may access.
Author: Ronald Adam
"""

def useglobals(rw_globals=None, r_globals=None, builtins=True):
#import dis
import sys
write_list = []
read_list = []
if rw_globals != None:
rw_globals = rw_globals.replace(' ','')
write_list = rw_globals.split(',')
if r_globals != None:
r_globals = r_globals.replace(' ','')
read_list = r_globals.split(',')
if builtins == True:
read_list.extend(dir(__builtins__))
elif builtins != None:
builtins = builtins.replace(' ','')
read_list.extend(builtins.split(','))
# Add own name to read list.
read_list.append(sys._getframe(0).f_code.co_name)
read_list.extend(write_list)
#print read_list, write_list
names = sys._getframe(1).f_code.co_names
code = sys._getframe(1).f_code.co_code
#print dis.disassemble(sys._getframe(1).f_code)
i = 0
while i < len(code):
#print ord(code)
op = ord(code)
if op == 116: # dis.opmap['LOAD_GLOBAL']
oparg = ord(code[i+1]) + ord(code[i+2]) * 256
if str(names[oparg]) not in read_list:
raise NameError, "read from global name %s, detected"
% names[oparg]
elif op == 97: # dis.opmap['STORE_GLOBAL']
oparg = ord(code[i+1]) + ord(code[i+2]) * 256
if names[oparg] not in write_list:
raise NameError, "write to global name %s, detected" %
names[oparg]
if op >= 90: # dis.HAVE_ARGUMENT
i += 3 # Not sure if this is always the same?
else:
i += 1


if __name__ == '__main__':
"""
Test useglobals() function. Change values to test
for error catching.
"""

def a():
useglobals(rw_globals='x', r_globals='y,b')
# This function can read or write 'x',
# Can read 'y', and function 'b',
# and can access all builtins.
global x
y = 5
x += y
x = b(x)
return x

def b(g):
useglobals('','y,c','int')
# This function can only read 'y' and
# function 'c' in globals, and can
# only access 'int' in builtins.
g = g+y
c(int(g))
return g

def c(w):
useglobals(builtins=None)
# This function has no builtins or globals.
w = w**2
return w

y = 4
x = 5
z = 6
print a(),x,y,z

#---end---
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top