Help with changes in traceback stack from Python 2.7 to Python 3.x

2014-04-25 Thread Andrew Konstantaras

I wrote the following code that works in Python 2.7 that takes the variables 
passed to the function into a dictionary.  The following call:

strA = 'a'
intA = 1
dctA = makeDict(strA, intA)

produces the following dictionary:

 {'strA':'a', 'intA':1}

To access the names passed into the function, I had to find the information by 
parsing through the stack.  The code that used to work is:

from traceback import extract_stack

def makeDict(*args):
   strAllStack = str(extract_stack())
   intNumLevels = len(extract_stack())
   intLevel = 0
   blnFinished = False
   while not blnFinished:
   strStack = str(extract_stack()[intLevel])
   if strStack.find("makeDict(")>0:
   blnFinished = True
   intLevel += 1
   if intLevel >= intNumLevels:
   blnFinished = True
   strStartText = "= makeDict("
   intLen = len(strStartText)
   intOpenParenLoc = strStack.find(strStartText)
   intCloseParenLoc = strStack.find(")", intOpenParenLoc)
   strArgs = strStack[ intOpenParenLoc+intLen : intCloseParenLoc ].strip()
   lstVarNames = strArgs.split(",")
   lstVarNames = [ s.strip() for s in lstVarNames ]  
   if len(lstVarNames) == len(args):

   tplArgs = map(None, lstVarNames, args)
   newDict = dict(tplArgs)
   return newDict
   else:
   return "Error, argument name-value mismatch in function 'makeDict'. lstVarNames: " 
+ str(lstVarNames) + "\n args: " + str(args), strAllStack

The same code does not work in Python 3.3.4.  I have tried parsing through the 
stack information and frames and I can't find any reference to the names of the 
arguments passed to the function.  I have tried inspecting the function and 
other functions in the standard modules, but I can't seem to find anything that 
will provide this information.

Can anyone point me in the direction to find this information?  Any help is 
appreciated.

---Andrew

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Help with changes in traceback stack from Python 2.7 to Python 3.x

2014-04-27 Thread Andrew Konstantaras

Thanks for the response and I can certainly see that this old code can be improved, but I 
respectfully disagree on the utility of this function.  The flexibility of passing any 
number of arguments to the function and returning a dictionary is much easier than 
writing out dict(x=x, y=y, ...n=n).  I also believe that "makeDict" makes my 
code very readable.

My question is around finding the names of the variables passed to a function 
from within the function.  I have spent many hours looking on the web looking 
for where in the frames and stacks this information is stored.  Does anyone 
have any insight where/how I can find the variables associated with the 
arguments in

 dctA = makeDict(strA, intA)

from within the function

 def makeDict(*args):
    #need to find the variable names "strA" and "intA" in this 
context

---Andrew


On Apr 26, 2014, at 05:10 AM, Ned Batchelder  wrote:


On 4/26/14 1:50 AM, Andrew Konstantaras wrote:
   > I wrote the following code that works in Python 2.7 that takes the
   > variables passed to the function into a dictionary. The following call:
   >
   > strA = 'a'
   > intA = 1
   > dctA = makeDict(strA, intA)
   >
   > produces the following dictionary:
   >
   > {'strA':'a', 'intA':1}
   >
   > To access the names passed into the function, I had to find the
   > information by parsing through the stack. The code that used to work 
is:
   >
   > from traceback import extract_stack
   >
   > def makeDict(*args):
   > strAllStack = str(extract_stack())
   > intNumLevels = len(extract_stack())
   > intLevel = 0
   > blnFinished = False
   > while not blnFinished:
   > strStack = str(extract_stack()[intLevel])
   > if strStack.find("makeDict(")  >0:
   > blnFinished = True
   > intLevel += 1
   > if intLevel>= intNumLevels:
   > blnFinished = True
   > strStartText = "= makeDict("
   > intLen = len(strStartText)
   > intOpenParenLoc = strStack.find(strStartText)
   > intCloseParenLoc = strStack.find(")", intOpenParenLoc)
   > strArgs = strStack[ intOpenParenLoc+intLen : intCloseParenLoc ].strip()
   > lstVarNames = strArgs.split(",")
   > lstVarNames = [ s.strip() for s in lstVarNames ]
   > if len(lstVarNames) == len(args):
   > tplArgs = map(None, lstVarNames, args)
   > newDict = dict(tplArgs)
   > return newDict
   > else:
   > return "Error, argument name-value mismatch in function 'makeDict'.
   > lstVarNames: " + str(lstVarNames) + "\n args: " + str(args), 
strAllStack
   >
   > The same code does not work in Python 3.3.4. I have tried parsing
   > through the stack information and frames and I can't find any reference
   > to the names of the arguments passed to the function. I have tried
   > inspecting the function and other functions in the standard modules, 
but
   > I can't seem to find anything that will provide this information.

1) This is a very strange function. Instead of going to all this
trouble, why not use:

dctA = dict(strA=strA, intA=intA)

Yes, you have to repeat the names, but you'd be done by now, it works on
both versions of Python, and people reading your code would understand
what it does.

2) Why is your code examining the entire stack? The frame you want is
the one just above you. Why are you stringifying the tuple produced by
extract_stack when the source line you want is the fourth element? Why
are you using a while loop and a counter to iterate over elements of a list?

3) In the error case you return a tuple when the caller is expecting a
dict? Why not raise an exception?

4) Your code only works if makeDict is assigned to a name. When I first
tried it, I used "print(makeDict(...))", and it failed completely.

5) You haven't mentioned what goes wrong on Python 3, but when I tried
it, I got:

Traceback (most recent call last):
File "foo3.py", line 34, in 
d = makeDict(a, b)
File "foo3.py", line 27, in makeDict
newDict = dict(list(tplArgs))
TypeError: 'NoneType' object is not callable

Looking at your code, I see:

tplArgs = map(None, lstVarNames, args)

I didn't realize map accepted a callable of None (TIL!), but it no
longer does in Python 3. You'll have to do this a different way.

But seriously: just use dict() and be done with it. There's a lot here
that can be much simpler if you learn to use Python as it was meant to
be used. You are swimming upstream, there are easier ways.

   >
   > Can anyone point me in the direction to find this information? Any help
   > is appreciated.
   >
   > ---Andrew
   >
   >
   >


--
Ned Batchelder, http://nedbatchelder.com

--
https://mail.python.org/mailman/listinfo/python-list
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Help with changes in traceback stack from Python 2.7 to Python 3.x

2014-04-27 Thread Andrew Konstantaras

I guess I am missing something big as I am looking for a shorthand way of doing 
the following:

  dctA = dict(x=x, y=y, ... n=n)

This is, as I understand it a very natural way of using a dictionary.  It seems that this syntax is unnecessarily redundant and hence my goal of writing something more compact.  Perhaps the way I am doing it is a little unorthodox, but the ultimate use of a dictionary is, as I understand it, completely in line with how dictionaries were designed to be used.  In my other code, I often use these dictionaries to pass arguments to functions and return results.  It allows me great flexibility without breaking existing code.  I pack a dictionary before passing and unpack when retrieving. 


I will give the locals approach a try, it seems a little more clumsy than 
simply passing the variables to the function.

Thanks again for your input.

---Andrew

On Apr 27, 2014, at 01:18 PM, Chris Angelico  wrote:


On Mon, Apr 28, 2014 at 4:56 AM, Andrew Konstantaras  
wrote:
   > Thanks for the response and I can certainly see that this old code can 
be
   > improved, but I respectfully disagree on the utility of this function. 
The
   > flexibility of passing any number of arguments to the function and 
returning
   > a dictionary is much easier than writing out dict(x=x, y=y, ...n=n). I 
also
   > believe that "makeDict" makes my code very readable.

There is another option. You could pass a set of strings and a
dictionary of all locals, and have it fetch them. However, I
personally disagree about the readability; yes, it's nice and clean,
but it does something which 99% of Python programmers won't expect.
It's like writing a numeric type in C++ and then overloading the +
operator to concatenate the decimal representations.

   > My question is around finding the names of the variables passed to a
   > function from within the function. I have spent many hours looking on 
the
   > web looking for where in the frames and stacks this information is 
stored.
   > Does anyone have any insight where/how I can find the variables 
associated
   > with the arguments in
   >
   > dctA = makeDict(strA, intA)
   >
   > from within the function
   >
   > def makeDict(*args):
   >  #need to find the variable names "strA" and "intA" in this
   > context

It isn't stored. What would be the names provided if, for instance, you do this:

dctA = makeDict("literal", intA + intB)

? There is no name for a literal or an expression. One of the
principles of Python is that an object is itself, regardless of the
name used to reference it. You're violating that by peeking into the
source code; you're fundamentally going against what every Python
programmer will expect.

Please don't do this. Take a step back, see what problem you're really
trying to solve, and solve that problem another way. Maybe a simple
DSL will help here, or possibly even just string interpolation,
depending on what you're trying to do. If you need help, show us some
of the code that uses makeDict, and we can advise.

Just please don't inflict makeDict onto any subsequent maintainer.

ChrisA
--
https://mail.python.org/mailman/listinfo/python-list
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Help with changes in traceback stack from Python 2.7 to Python 3.x

2014-04-28 Thread Andrew Konstantaras



On Apr 28, 2014, at 12:36 PM, Ned Batchelder  wrote:


On 4/27/14 5:51 PM, Andrew Konstantaras wrote:
   > I guess I am missing something big as I am looking for a shorthand way
   > of doing the following:
   >
   > dctA = dict(x=x, y=y, ... n=n)
   >

Yes, your makeDict(x, y) is a shorthand for dict(x=x, y=y), but there
are many things you can do with dict that you can't do with makeDict.
What is the makeDict equivalent of:

dict(x=12, y=self.y, z=a+b)

The code you have allows you more compact expression, but it brings
fragility and surprise.

   > This is, as I understand it a very natural way of using a dictionary.
   > It seems that this syntax is unnecessarily redundant and hence my goal
   > of writing something more compact. Perhaps the way I am doing it is a
   > little unorthodox, but the ultimate use of a dictionary is, as I
   > understand it, completely in line with how dictionaries were designed 
to
   > be used. In my other code, I often use these dictionaries to pass
   > arguments to functions and return results. It allows me great
   > flexibility without breaking existing code. I pack a dictionary before
   > passing and unpack when retrieving.

Perhaps you want to create a class instead? If you find yourself
passing more than a handful of arguments to a function, and especially
more than a handful of values returned from a function, then a class
with methods might be a better way to combine state and behavior.

Also, keep in mind that you can return a tuple from a function if you
want to return two or three values and assign them to names:

x, y, z = compute_xyz()

You mention unpacking your dictionary after the function call. How do
you do that? Isn't that a cumbersome and repetitive operation?
One of the reasons I like using the dictionary over the tuple is that if I have a function that grows in functionality (i.e., I decide to return more data for new scenarios), I can just add the new object to the dictionary and I don't have to worry about changing the code every where else that used to call the function. 

Actually, that is one of the nice features of using a dictionary, I can check if the key is there and if it is pull it out.  As I was dusting off this old code, I considered trying to implement this functionality through by creating a class.  I never liked going through the stack, it seemed hacky, but I came to love using dictionaries as they have some great built in features. 


Again, thanks for the advice and I will focus on the class implementation.



   >
   > I will give the locals approach a try, it seems a little more clumsy
   > than simply passing the variables to the function.
   >
   > Thanks again for your input.
   >
   > ---Andrew

BTW, it's a little easier to follow the threads of conversation if you
put your responses after the text you are responding to. This is known
as bottom-posting, and is preferred to top-posting as you did here.


--
Ned Batchelder, http://nedbatchelder.com

--
https://mail.python.org/mailman/listinfo/python-list
-- 
https://mail.python.org/mailman/listinfo/python-list