pythonic equivalent of upvar?

D

David MacKay

Dear Greater Py,

<motivation note="reading this bit is optional">
I am writing a command-line reader for python.

I'm trying to write something with the same brevity
as perl's one-liner

eval "\$$1=\$2" while @ARGV && $ARGV[0]=~ /^(\w+)=(.*)/ && shift;

and with similar functionality. I've decided I don't like
getopt, because it seems to require me to write pages and pages
of elif's as I add new arguments. It slows me down, whereas the
perlism above liberates.

My solution is a twenty-line python chunk equivalent to the perl one-liner.
(Twenty lines because, to process a (name, value) pair, I have to find the
current type of the variable "name" before setting "name" to righttype("value").

I'm happy with this 20-line chunk and wish to re-use it in many python programs.
</motivation>

<question>
What is the pythonic way to embed a 20-line chunk of code into a function?
I'd love to define these 20 lines as a module, and reuse the module over and over.
but this chunk of code needs to have access to
the local variables of main, whatever they are called.

In tcl, IIRC, the command "upvar" allows one function to get access to its
parent function's local variables.

Is there a pythonic way to tell a function "you are allowed to see all your
parent's variables?" Or, to tell a chunk of code, "you are just a chunk of
code, not really a function"?
</question>

Thanks very much

David

PS -- example below illustrates the chunk of code, in case anyone is interested.

#!/usr/bin/env python
"""
This program commandline.py illustrates a command-line reader (David MacKay, license: GPL)
that works a little like perl's
eval "\$$1=\$2" while @ARGV && $ARGV[0]=~ /^(\w+)=(.*)/ && shift;
usage: (all arguments are optional)
commandline.py -bits 5 -decode 1 -file pimple -N 10000
"""
import sys
def usage(name):
print "Usage: %s -variableName value" % name
sys.exit()

def main():
## Define DEFAULTS that can be overridden on the command line
decode=0 ## are we decoding?
verbose=0 ## verbose output?
bits=7 ## how big are the blocks?
N = 10000 ## What is the file length?
file="blah" ## file name string
## End defaults

## Command-line reader: Reads pairs of the form
## " -variableName value "
## and sets variableName=value. Gives error if this syntax is violated.
while ( len(sys.argv) > 1 ):
if ( sys.argv[1][0] == "-" ):
name = sys.argv.pop(1)
name = name[1:len(name)]
if( len(sys.argv) <= 1 ):
print >> sys.stderr, "could not get value for name ", name
usage(sys.argv[0])
else:
value = sys.argv.pop(1)
# here, we should assert that this variable exists!
command = "ntype = type(%s)" % (name) # find type of this variable
exec command
# convert value to the right type
command = "%s = ntype(%s) # %s" % ( name,`value`, str(ntype) )
if verbose:
print >> sys.stderr , "setting value:", command
exec command
else:
usage(sys.argv[0])
## End of command-line reader

## defaults that cannot be overridden on command line
securityLevel = 1
## end defaults

## Main program starts here

print >> sys.stderr, " N =",N," bits =",bits, \
" file = ",file, " decode = ", decode

if __name__ == '__main__': main()
 
P

Peter Otten

David said:
I'm trying to write something with the same brevity
as perl's one-liner

eval "\$$1=\$2" while @ARGV && $ARGV[0]=~ ^(\w+)=(.*) && shift;

import optparse

parser = optparse.OptionParser("See the error of your ways and use
optparse")
parser.add_option("--decode", action="store_true")
parser.add_option("--verbose", action="store_true")
parser.add_option("--bits", type="int", default=7)
parser.add_option("-N", type="int", default=10000)
parser.add_option("--file", default="blah")

options, args = parser.parse_args()
if args:
parser.error("unexpected positional argument(s)")

# accessing option values
print options.bits

Peter
 
B

Bengt Richter

Dear Greater Py,

<motivation note="reading this bit is optional">
I am writing a command-line reader for python.

I'm trying to write something with the same brevity
as perl's one-liner

eval "\$$1=\$2" while @ARGV && $ARGV[0]=~ /^(\w+)=(.*)/ && shift;

and with similar functionality. I've decided I don't like
getopt, because it seems to require me to write pages and pages
of elif's as I add new arguments. It slows me down, whereas the
perlism above liberates.

My solution is a twenty-line python chunk equivalent to the perl one-liner.
(Twenty lines because, to process a (name, value) pair, I have to find the
current type of the variable "name" before setting "name" to righttype("value").

I'm happy with this 20-line chunk and wish to re-use it in many python programs.
</motivation>

<question>
What is the pythonic way to embed a 20-line chunk of code into a function?
I'd love to define these 20 lines as a module, and reuse the module over and over.
but this chunk of code needs to have access to
the local variables of main, whatever they are called.

In tcl, IIRC, the command "upvar" allows one function to get access to its
parent function's local variables.

Is there a pythonic way to tell a function "you are allowed to see all your
parent's variables?" Or, to tell a chunk of code, "you are just a chunk of
code, not really a function"?
</question>
You can see them, but you can't rebind them without nasty hacks.
I would suggest just instantiating an object that initializes the way
you want to contain the values you pre-specify, and optionally get
overridden by command line info as you specify. E.g., a quick hack (not
tested beyond what you see ;-)

----< clvalues.py >-----------------------------------------------------
class CLValues(dict):
"""
A dict whose initialization values are overridden by
-name value pairs in the overrides list, by default taken from
command line args sys.argv[1:], but a list may be passed.
Existing values get overridden by cmd line values converted
to the same type. New values are assumed str type. Existing
values may be specified non-overridable by prefixing an underscore,
which is removed after cmd line args have been processed.
Since keys are valid names, access is also provided via attribute syntax.
"""
def __init__(self, overrides=None, *args, **kw):
dict.__init__(self, *args, **kw)
if overrides is None: overrides = __import__('sys').argv[1:]
while overrides:
name = overrides.pop(0)
if not name.startswith('-') or not name[1:]:
raise ValueError, "Names must be non-null and prefixed with '-', unlike %r" % name
name = name[1:]
if '_'+name in self or name.startswith('_') and name in self:
raise ValueError, "%r may not be overridden" % name
if not overrides: raise ValueError, 'No value following %r'% '-'+name
value = overrides.pop(0)
self[name] = type(self.get(name, ''))(value) # default str type if not pre-existing
for k,v in self.items():
if k.startswith('_'): # make plain names for non-overridables
self[k[1:]] = v
del self[k]
def __getattr__(self, name): return self[name] # provide key access as attributes

def test():
clv = CLValues(
decode=0,
verbose=0,
bits=7,
N = 10000,
file="blah",
_securityLevel = 3)
for name, value in clv.items():
print '%15s: %r' %(name, value)
print 'N accessed as clv.N => %r' % clv.N

if __name__ == '__main__':
test()
------------------------------------------------------------------------

Running it to run the test:

[ 9:54] C:\pywk\ut>py24 clvalues.py -bits 8 -file other\path\and\file
decode: 0
securityLevel: 3
verbose: 0
file: 'other\\path\\and\\file'
bits: 8
N: 10000
N accessed as clv.N => 10000

[ 9:54] C:\pywk\ut>py24 clvalues.py -securityLevel 0
Traceback (most recent call last):
File "clvalues.py", line 44, in ?
test()
File "clvalues.py", line 38, in test
_securityLevel = 3)
File "clvalues.py", line 21, in __init__
raise ValueError, "%r may not be overridden" % name
ValueError: 'securityLevel' may not be overridden

Quoting command line arg to make a single arg with embedded spaces:

[ 9:54] C:\pywk\ut>py24 clvalues.py -added "should be string"
decode: 0
securityLevel: 3
added: 'should be string'
verbose: 0
file: 'blah'
bits: 7
N: 10000
N accessed as clv.N => 10000

Thanks very much
De nada.
David

PS -- example below illustrates the chunk of code, in case anyone is interested.
<snip code, from which I tried to translate the functionality>

Usage in a program would go something like
from clvalues import CLValues
clv = CLValues(
# ... like test above
)
# ...
# use values spelled clv.name or clv['name'] or clv.get('name') etc
#

Regards,
Bengt Richter
 
M

Mike Meyer

<motivation note="reading this bit is optional">
I am writing a command-line reader for python.

I'm trying to write something with the same brevity
as perl's one-liner

eval "\$$1=\$2" while @ARGV && $ARGV[0]=~ /^(\w+)=(.*)/ && shift;

and with similar functionality.

Why? Do you like to make the security hole in your code hard to find?
<question>
What is the pythonic way to embed a 20-line chunk of code into a function?
I'd love to define these 20 lines as a module, and reuse the module over and over.
but this chunk of code needs to have access to
the local variables of main, whatever they are called.

You can't do that. You don't want to do that, because it will cause
user typos to make your code break in strange and mysterious ways. Put
the variables in a dictionary, indexed by variable name.

Better yet, do what Peter suggests, and learn to use optparse.

<mike
 

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

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top