473,416 Members | 1,928 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,416 software developers and data experts.

PEP thought experiment: Unix style exec for function/method calls

Hi,
[ I'm calling this PEP thought experiment because I'm discussing language
ideas for python which if implemented would probably be quite powerful
and useful, but the increased risk of obfuscation when the ideas are
used outside my expected/desired problem domain probably massively
outweigh the benefits. (if you're wondering why, it's akin to adding
a structured goto with context)

However I think as a thought experiment it's quite useful, since any
language feature can be implemented in different ways, and I'm wondering
if anyone's tried this, or if it's come up before (I can't find either
if they have...). ]

I'm having difficulty finding any previous discussion on this -- I
keep finding people either having problems calling os.exec(lepev), or
with using python's exec statement. Neither of which I mean here.

Just for a moment, let's just take one definition for one of the
os.exec* commands:

execv(...)
execv(path, args)

Execute an executable path with arguments, replacing current
process.
path: path of executable file
args: tuple or list of strings

Also: Note that execv inherits the system environment.

Suppose we could do the same for a python function - suppose we could
call the python function but either /without/ creating a new stack
frame or /replacing/ the current stack frame with the new one.

Anyway, I've been thinking recently that the same capability in python
would be useful. However, almost any possible language feature:
* Has probably already been discussed to death in the past
* There's often a nice idiom working around the lack of said feature.

So I'm more on an exploratory forage than asking for a language change
here ;)

Since os.exec* exists and "exec" already exists in python, I need to
differentiate what I mean by a unix style exec from python. So for
convenience I'll call it "cexe".

Now, suppose I have:
----------
def set_name():
name = raw_input("Enter your name! > ")
cexe greet()

def greet():
print "hello", name

cexe set_name()
print "We don't reach here"
----------

This would execute, ask for the user's name, say hello to them and then
exit - not reaching the final print "We don't reach here" statement.

Let's ignore for the moment that this example sucks (and is a good example
of the danger of this as a language feature), what I want to do here is
use this to explain the meaning of "cexe".

There's two cases to consider:
cexe some_func_noargs()

This transfers execution to the function that would normally be called
if I simply called without using "cexe" some_func_noargs() . However,
unlike a function call, we're /replacing/ the current thread of
execution with the thread of execution in some_func_noargs(), rather
than stacking the current location, in order to come back to later.

ie, in the above this could also be viewed as "call without creating a
new return point" or "call without bothering to create a new stack
frame".

It's this last point why in the above example "name" leaks between the
two function calls - due to it being used as a cexe call.

Case 2:
given...
def some_func_withargs(colour,tone, *listopts, **dictopts)

consider...
cexe some_func_withargs(foo,bar, *argv, **argd)

This would be much the same as the previous case, except in the new
execution point, the name colour & tone map to the values foo & bar had
in the original context, whilst listopts and dictopts map the values
that argv & argd had in the original content)

One consequence here though is that in actual practice the final print
statement of the code above never actually gets executed. (Much like if
that was inside a function, writing something after "return foo", wouldn't
be executed)

The reason I'm curious here about previous discussion is because
conceptually there's obviously other semantics you can apply - such as
the current stack frame is /replaced/ by the new stack frame. This is
perhaps a more accurate mapping to the Unix exec call.

If that was the case, it would mean that locals would not "leak" between
functions (which is desirable), and our example above could be rewritten
as follows:

----------
def get_and_use_value_from_user(tag, callforward):
somevalue = raw_input(tag)
cexe callforward(name)

def greet(name):
print "hello", name

cexe get_and_use_value_from_user("Enter your name! > ", greet)
print "We don't reach here"
----------

OK, so this probably seems pretty pointless to many people, but I'm
curious about improving the tools to deal with state machines. Often
people use switch statements in other languages to deal with them, and
for certain classes of state machines you can use replace them with
generators. But that's not appropriate for everything...

My particular thought that started all this off actually stems from this:

Essentially by doing a cexe we're actually creating a composite function
out of disparate functions (perhaps shared or not shared local context).
ie ...
----------
def count():
print "Counting to 3!"
cexe one()

def one():
print "one!"
cexe two()

def two():
print "two!"
cexe three()

def three():
print "three!"

count() # Note I'm not doing cexe count() here
----------
.... essentially dynamically constructs an execution context similar to a
single function, ie the above collapses to something like:

----------
def count():
print "Counting to 3!"
print "one!"
print "two!"
print "three!"

count() # Note I'm not doing cexe count() here
----------
It's this recognition that made me wonder this:

This works well for state machines, and generators are a nice model for
dealing with resumable things (and a state machine can be viewed as a
resumable "thing").

Now suppose we take all that one stage further and provide said
composite generator, with some additional context in the way we do
with Kamaelia - cf http://kamaelia.sf.net/MiniAxon/ , we could
potentially do this:

(choosing something relatively substantial to show I'm not just
being whimsical, and to provide somthing perhaps more "real")

class TCP_StateMachine(Axon.Component.component):
def CLOSED(self):
if not self.anyReady(): yield self.pause()
event = self.recv("inbox")
if "appl passive open" == event.type: cexe self.LISTEN()
if "active open" == event.type:
self.send(SYN(event.payload), "network")
cexe self.SYN_SENT()

def LISTEN(self):
if not self.anyReady(): yield self.pause()
event = self.recv("inbox")
if "recv syn" == event.type:
self.send( , "network")
cexe self.SYN_RCVD()
if "appl send data" == event.type:
self.send( , "network")
cexe self.SYN_SENT()

def SYN_RCVD(self):
if not self.anyReady(): yield self.pause()
event = self.recv("inbox")
if "recv rst" == event.type: cexe self.LISTEN()
if "recv ack" == event.type: cexe self.ESTABLISHED()
if "appl close" == event.type:
self.send(FIN(event.payload), "network")
cexe self.FIN_WAIT1()

def SYN_SENT(self):
if not self.anyReady(): yield self.pause()
event = self.recv("inbox")
if "appl close" == event.type: cexe self.CLOSED()
if "timeout" == event.type: cexe self.CLOSED()
if "recv syn-ack" == event.type:
self.send(ACK(event.payload), "network")
cexe self.ESTABLISHED()

def ESTABLISHED(self):
# more complex than others, so skipped, has its own data transfer
# state etc, so would make more sense to model as a subcomponent.

def FIN_WAIT_1(self):
if not self.anyReady(): yield self.pause()
event = self.recv("inbox")
if "recv ack" == event.type: cexe self.FIN_WAIT_2()

if "recv fin" == event.type:
self.send(ACK(event.payload), "network")
cexe self.CLOSING()

if "recv fin, ack" == event.type:
self.send(ACK(event.payload), "network")
cexe self.TIME_WAIT()

def FIN_WAIT_2(self):
if not self.anyReady(): yield self.pause()
event = self.recv("inbox")
if "recv fin" == event.type:
self.send(ACK(event.payload), "network")
cexe self.TIME_WAIT()

def CLOSING(self):
if not self.anyReady(): yield self.pause()
event = self.recv("inbox")
if "recv ack" == event.type: cexe self.TIME_WAIT()

def TIME_WAIT(self):
if not self.anyReady(): yield self.pause()
event = self.recv("inbox")
if "timeout 2MSL" == event.type: cexe self.CLOSED()

Now obviously that's not particularly pretty, but the clear definition
of states as methods, and clear transitions between states via the cexe
calls, is relatively easy to follow through. ie it's fairly clear it's
implementing the standard TCP state machine.

(Incidentally if you're wondering what relevance this has outside of
just TCP, this sort of thing could be useful in games for modelling
complex behaviours)

What is less clear about this is that I'm working on the assumption that
as well as the language change making "cexe" work, is that this also
allows the above set of methods to be treated as if it's one large
generator that's split over multiple function definitions. This is
conceptually very similar to the idea that cexe would effectively
"join" functions together, as alluded to above.

This has a number of downsides for the main part of the language, so
I wouldn't suggest that these changes actually happen - consider it a
thought experiment if you like. (I think the single function/no wrapping
of yield IS actually a good thing)

However, I feel the above example is quite a compelling example of how
a unix style exec for python method calls could be useful, especially
when combined with generators. (note this is a thought experiment ;)

It also struck me that any sufficiently interesting idea is likely to
have already been implemented, though perhaps not looking quite like the
above, so I thought I'd ask the questions:

* Has anyone tried this sort of thing?

* Has anyone tried simply not creating a new stack frame when doing
a function call in python? (or perhaps replacing the current one with
a new one)

* Has anyone else tried modelling the unix system exec function in
python? If so what did you find?

* Since I can't find anything in the archives, I'm presuming my
searching abilities are bust today - can anyone suggest any better
search terms or threads to look at?

* Am I mad? :)

BTW, I'm aware that this has similarities to call with continuation,
and that you can use statesaver.c & generators to achieve something
vaguely similar to continuations, but I'm more after this specific
approach, rather than that general approach. (After all, even ruby
notes that their most common use for call/cc is to obfuscate code -
often accidentally - and I'm not particularly interested in that :)

Whereas the unix style exec is well understood by many people, and
when it's appropriate can be extremely useful. My suspicion is that
my ideasabove actually maps to a common idiom, but I'm curious to
find that commonidiom.

I'm fairly certain something like this could be implemented using
greenlets, and also fairly certain that Stackless has been down this
route in the past, but I'm not able to find something like this exec
style call there. (Which is after all more constrained than your usual
call with continuation approach)

So, sorry for the length of this, but if anyone has any thoughts, I'd be
very interested. If they don't, I hope it was interesting :)

Regards,
Michael.

Jun 25 '06 #1
4 2339
Michael wrote:
Suppose we could do the same for a python function - suppose we could
call the python function but either /without/ creating a new stack
frame or /replacing/ the current stack frame with the new one.
I'm confused about what you mean. I'm guessing by "not creating a new
stack frame" you wish to execute the bytecode from a different function
in the current stack frame, correct? As for replacing the current
stack frame, do you want to replace the whole stack, down to the
__main__ module entry point, or just replace the top frame? Your
comments and examples seem to be inconsistent about this. I'm going to
assume you just want to replace or reuse the top frame in my replies.
[snip] Since os.exec* exists and "exec" already exists in python, I need to
differentiate what I mean by a unix style exec from python. So for
convenience I'll call it "cexe".

Now, suppose I have:
----------
def set_name():
name = raw_input("Enter your name! > ")
cexe greet()

def greet():
print "hello", name

cexe set_name()
print "We don't reach here"
----------

This would execute, ask for the user's name, say hello to them and then
exit - not reaching the final print "We don't reach here" statement.
Only if you were to replace the whole stack. If you only replace or
reuse the top frame, I would think greet would exit and execution would
resume right after the point from which set_name was called. Or am I
misunderstanding what you want?

Let's ignore for the moment that this example sucks (and is a good example
of the danger of this as a language feature), what I want to do here is
use this to explain the meaning of "cexe".

There's two cases to consider:
cexe some_func_noargs()

This transfers execution to the function that would normally be called
if I simply called without using "cexe" some_func_noargs() . However,
unlike a function call, we're /replacing/ the current thread of
execution with the thread of execution in some_func_noargs(), rather
than stacking the current location, in order to come back to later.

ie, in the above this could also be viewed as "call without creating a
new return point" or "call without bothering to create a new stack
frame".

It's this last point why in the above example "name" leaks between the
two function calls - due to it being used as a cexe call.
I doubt this would be possible without a major change in how functions
work in Python. The most obvious problem is that functions refer to
local variables by an index, not by name. If you were to execute the
bytecode of one function in the stack frame of another, Bad Things
would happen.

Case 2:
given...
def some_func_withargs(colour,tone, *listopts, **dictopts)

consider...
cexe some_func_withargs(foo,bar, *argv, **argd)

This would be much the same as the previous case, except in the new
execution point, the name colour & tone map to the values foo & bar had
in the original context, whilst listopts and dictopts map the values
that argv & argd had in the original content)
This is a lot more doable, but I'm not sure a new statement is needed.
It seems like it wouldn't be terribly impossible to optimize the return
statement to do this (in fact there's a certain situation where it's
common to do just that, in other languages; more on that later). If
function A were to return the result of calling function B, the A could
possibly pop itself off the stack before calling B (but after
evaluating B's arguments). B would then return directly to A's caller
in place of A. Essentially, B has replaced A on the stack. Biggest
problem is you'd have to know whether an object is a Python function at
compile time--though that may not be so hard in the future.

(Now, if you're thinking that you'll replace the whole stack and not
just the top frame, Python already has the machinery to do this if you
can tolerate wrapping up your main function to catch an exception and
dispatching on it. For example:

class _Cexe(Exception):
def __init__(self,func,*args,**kwargs):
self.func = func
self.args = args
self.kwargs = kwargs

def cexe(func,*args,**kwargs):
raise _Cexe(func,*args,**kwargs)

def main_wrapper(*args,**kwargs):
func = main
while True:
try:
func(*args,**kwargs)
return
except _Cexe,e:
func = e.func
args = e.args
kwargs = e.kwargs

As an added bonus, you can do this higher up the stack if you want. A
cexe statement wouldn't improve this too much; it wouldn't even make it
more efficent. You can't just reset the stack pointer; you have to
clean things up. The only upside to cexe I can think of is it could
help reduce latency by delaying cleanup until garbage collection, but
it wouldn't help overall efficiency too much.)
[snip a lot] It also struck me that any sufficiently interesting idea is likely to
have already been implemented, though perhaps not looking quite like the
above, so I thought I'd ask the questions:

* Has anyone tried this sort of thing?
Yes. There is a major use case for what you seem to be proposing,
namely tail-recursive functions. A tail-recursive function is a
recursive function that immediately returns the result of calling
itself. A tail-recursive function never needs to keep its state around
once it's called itself, and it's very common in functional languages,
so it's an easy and common optimization.

However, what you seem to be proposing is a generalization of that. It
seems like it's possible, theoretically, to generalize this so that it
works when you return the result of any function call, recursive or
not. Theoretically. It wouldn't surprise me if functional languages
like Haskell do this already.
* Has anyone tried simply not creating a new stack frame when doing
a function call in python? (or perhaps replacing the current one with
a new one)
Doubt it.
* Has anyone else tried modelling the unix system exec function in
python? If so what did you find?

* Since I can't find anything in the archives, I'm presuming my
searching abilities are bust today - can anyone suggest any better
search terms or threads to look at?
Maybe look to see how tail-recursive optimization in languages such as
Scheme work, and whether it can be generalized.
* Am I mad? :)


Yep. :)
Carl Banks

Jun 25 '06 #2
This reminds me of an silly little optimization I used to use all the
times when coding in assembler on PIC MCUs.
A call followed by a return can be turned into jump. Saves one
instruction and one level on the call stack.
I think most optimizing compilers already do something of this sort, at
least in the embedded world :)

Jeethu Rao

Michael wrote:
Hi,
[ I'm calling this PEP thought experiment because I'm discussing language
ideas for python which if implemented would probably be quite powerful
and useful, but the increased risk of obfuscation when the ideas are
used outside my expected/desired problem domain probably massively
outweigh the benefits. (if you're wondering why, it's akin to adding
a structured goto with context)

However I think as a thought experiment it's quite useful, since any
language feature can be implemented in different ways, and I'm wondering
if anyone's tried this, or if it's come up before (I can't find either
if they have...). ]

I'm having difficulty finding any previous discussion on this -- I
keep finding people either having problems calling os.exec(lepev), or
with using python's exec statement. Neither of which I mean here.

Just for a moment, let's just take one definition for one of the
os.exec* commands:

execv(...)
execv(path, args)

Execute an executable path with arguments, replacing current
process.
path: path of executable file
args: tuple or list of strings

Also: Note that execv inherits the system environment.

Suppose we could do the same for a python function - suppose we could
call the python function but either /without/ creating a new stack
frame or /replacing/ the current stack frame with the new one.

Anyway, I've been thinking recently that the same capability in python
would be useful. However, almost any possible language feature:
* Has probably already been discussed to death in the past
* There's often a nice idiom working around the lack of said feature.

So I'm more on an exploratory forage than asking for a language change
here ;)

Since os.exec* exists and "exec" already exists in python, I need to
differentiate what I mean by a unix style exec from python. So for
convenience I'll call it "cexe".

Now, suppose I have:
----------
def set_name():
name = raw_input("Enter your name! > ")
cexe greet()

def greet():
print "hello", name

cexe set_name()
print "We don't reach here"
----------

This would execute, ask for the user's name, say hello to them and then
exit - not reaching the final print "We don't reach here" statement.

Let's ignore for the moment that this example sucks (and is a good example
of the danger of this as a language feature), what I want to do here is
use this to explain the meaning of "cexe".

There's two cases to consider:
cexe some_func_noargs()

This transfers execution to the function that would normally be called
if I simply called without using "cexe" some_func_noargs() . However,
unlike a function call, we're /replacing/ the current thread of
execution with the thread of execution in some_func_noargs(), rather
than stacking the current location, in order to come back to later.

ie, in the above this could also be viewed as "call without creating a
new return point" or "call without bothering to create a new stack
frame".

It's this last point why in the above example "name" leaks between the
two function calls - due to it being used as a cexe call.

Case 2:
given...
def some_func_withargs(colour,tone, *listopts, **dictopts)

consider...
cexe some_func_withargs(foo,bar, *argv, **argd)

This would be much the same as the previous case, except in the new
execution point, the name colour & tone map to the values foo & bar had
in the original context, whilst listopts and dictopts map the values
that argv & argd had in the original content)

One consequence here though is that in actual practice the final print
statement of the code above never actually gets executed. (Much like if
that was inside a function, writing something after "return foo", wouldn't
be executed)

The reason I'm curious here about previous discussion is because
conceptually there's obviously other semantics you can apply - such as
the current stack frame is /replaced/ by the new stack frame. This is
perhaps a more accurate mapping to the Unix exec call.

If that was the case, it would mean that locals would not "leak" between
functions (which is desirable), and our example above could be rewritten
as follows:

----------
def get_and_use_value_from_user(tag, callforward):
somevalue = raw_input(tag)
cexe callforward(name)

def greet(name):
print "hello", name

cexe get_and_use_value_from_user("Enter your name! > ", greet)
print "We don't reach here"
----------

OK, so this probably seems pretty pointless to many people, but I'm
curious about improving the tools to deal with state machines. Often
people use switch statements in other languages to deal with them, and
for certain classes of state machines you can use replace them with
generators. But that's not appropriate for everything...

My particular thought that started all this off actually stems from this:

Essentially by doing a cexe we're actually creating a composite function
out of disparate functions (perhaps shared or not shared local context).
ie ...
----------
def count():
print "Counting to 3!"
cexe one()

def one():
print "one!"
cexe two()

def two():
print "two!"
cexe three()

def three():
print "three!"

count() # Note I'm not doing cexe count() here
----------
... essentially dynamically constructs an execution context similar to a
single function, ie the above collapses to something like:

----------
def count():
print "Counting to 3!"
print "one!"
print "two!"
print "three!"

count() # Note I'm not doing cexe count() here
----------
It's this recognition that made me wonder this:

This works well for state machines, and generators are a nice model for
dealing with resumable things (and a state machine can be viewed as a
resumable "thing").

Now suppose we take all that one stage further and provide said
composite generator, with some additional context in the way we do
with Kamaelia - cf http://kamaelia.sf.net/MiniAxon/ , we could
potentially do this:

(choosing something relatively substantial to show I'm not just
being whimsical, and to provide somthing perhaps more "real")

class TCP_StateMachine(Axon.Component.component):
def CLOSED(self):
if not self.anyReady(): yield self.pause()
event = self.recv("inbox")
if "appl passive open" == event.type: cexe self.LISTEN()
if "active open" == event.type:
self.send(SYN(event.payload), "network")
cexe self.SYN_SENT()

def LISTEN(self):
if not self.anyReady(): yield self.pause()
event = self.recv("inbox")
if "recv syn" == event.type:
self.send( , "network")
cexe self.SYN_RCVD()
if "appl send data" == event.type:
self.send( , "network")
cexe self.SYN_SENT()

def SYN_RCVD(self):
if not self.anyReady(): yield self.pause()
event = self.recv("inbox")
if "recv rst" == event.type: cexe self.LISTEN()
if "recv ack" == event.type: cexe self.ESTABLISHED()
if "appl close" == event.type:
self.send(FIN(event.payload), "network")
cexe self.FIN_WAIT1()

def SYN_SENT(self):
if not self.anyReady(): yield self.pause()
event = self.recv("inbox")
if "appl close" == event.type: cexe self.CLOSED()
if "timeout" == event.type: cexe self.CLOSED()
if "recv syn-ack" == event.type:
self.send(ACK(event.payload), "network")
cexe self.ESTABLISHED()

def ESTABLISHED(self):
# more complex than others, so skipped, has its own data transfer
# state etc, so would make more sense to model as a subcomponent.

def FIN_WAIT_1(self):
if not self.anyReady(): yield self.pause()
event = self.recv("inbox")
if "recv ack" == event.type: cexe self.FIN_WAIT_2()

if "recv fin" == event.type:
self.send(ACK(event.payload), "network")
cexe self.CLOSING()

if "recv fin, ack" == event.type:
self.send(ACK(event.payload), "network")
cexe self.TIME_WAIT()

def FIN_WAIT_2(self):
if not self.anyReady(): yield self.pause()
event = self.recv("inbox")
if "recv fin" == event.type:
self.send(ACK(event.payload), "network")
cexe self.TIME_WAIT()

def CLOSING(self):
if not self.anyReady(): yield self.pause()
event = self.recv("inbox")
if "recv ack" == event.type: cexe self.TIME_WAIT()

def TIME_WAIT(self):
if not self.anyReady(): yield self.pause()
event = self.recv("inbox")
if "timeout 2MSL" == event.type: cexe self.CLOSED()

Now obviously that's not particularly pretty, but the clear definition
of states as methods, and clear transitions between states via the cexe
calls, is relatively easy to follow through. ie it's fairly clear it's
implementing the standard TCP state machine.

(Incidentally if you're wondering what relevance this has outside of
just TCP, this sort of thing could be useful in games for modelling
complex behaviours)

What is less clear about this is that I'm working on the assumption that
as well as the language change making "cexe" work, is that this also
allows the above set of methods to be treated as if it's one large
generator that's split over multiple function definitions. This is
conceptually very similar to the idea that cexe would effectively
"join" functions together, as alluded to above.

This has a number of downsides for the main part of the language, so
I wouldn't suggest that these changes actually happen - consider it a
thought experiment if you like. (I think the single function/no wrapping
of yield IS actually a good thing)

However, I feel the above example is quite a compelling example of how
a unix style exec for python method calls could be useful, especially
when combined with generators. (note this is a thought experiment ;)

It also struck me that any sufficiently interesting idea is likely to
have already been implemented, though perhaps not looking quite like the
above, so I thought I'd ask the questions:

* Has anyone tried this sort of thing?

* Has anyone tried simply not creating a new stack frame when doing
a function call in python? (or perhaps replacing the current one with
a new one)

* Has anyone else tried modelling the unix system exec function in
python? If so what did you find?

* Since I can't find anything in the archives, I'm presuming my
searching abilities are bust today - can anyone suggest any better
search terms or threads to look at?

* Am I mad? :)

BTW, I'm aware that this has similarities to call with continuation,
and that you can use statesaver.c & generators to achieve something
vaguely similar to continuations, but I'm more after this specific
approach, rather than that general approach. (After all, even ruby
notes that their most common use for call/cc is to obfuscate code -
often accidentally - and I'm not particularly interested in that :)

Whereas the unix style exec is well understood by many people, and
when it's appropriate can be extremely useful. My suspicion is that
my ideasabove actually maps to a common idiom, but I'm curious to
find that commonidiom.

I'm fairly certain something like this could be implemented using
greenlets, and also fairly certain that Stackless has been down this
route in the past, but I'm not able to find something like this exec
style call there. (Which is after all more constrained than your usual
call with continuation approach)

So, sorry for the length of this, but if anyone has any thoughts, I'd be
very interested. If they don't, I hope it was interesting :)

Regards,
Michael.


Jun 26 '06 #3
Carl Banks wrote:
Maybe look to see how tail-recursive optimization in languages such as
Scheme work, and whether it can be generalized.
Thanks for the feedback - I should've remembered tail recursion.
I doubt this would be possible without a major change in how functions
work in Python. The most obvious problem is that functions refer to
local variables by an index, not by name. If you were to execute the
bytecode of one function in the stack frame of another, Bad Things
would happen.
Oh that's a pity. I can see why you'd do that, but it is a pity. That would
tend to imply that _replacing_ rather than _reusing_ the top frame is the
most doable/likely/sensible approach. (It's also very obviously easier to
emulate)

(And yes, I was consider reusing or replacing *only* the top stack frame.
Replacing seems better with retrospect, even if reusing seemed like a fun
idea :-)
def set_name():
name = raw_input("Enter your name! ")
cexe greet()

def greet():
print "hello", name

cexe set_name()
print "We don't reach here"
----------

This would execute, ask for the user's name, say hello to them and then
exit - not reaching the final print "We don't reach here" statement.

Only if you were to replace the whole stack. If you only replace or
reuse the top frame, I would think greet would exit and execution would
resume right after the point from which set_name was called. Or am I
misunderstanding what you want?
I think you are.

In the hypothetical example, your code by definition gets called by
something. This leave a hypothetical return point. For the moment, lets
make things clearer by what I mean by changing the example to this:
1 def set_name():
2 name = raw_input("Enter your name! ")
3 cexe greet()
4
5 def greet():
6 print "hello", name
7
8 def main():
9 cexe set_name()
10 print "We don't reach here"
11
12 main()
13 print "see what I mean?"
----------
at line 12, we call 8. We can argue then our stack looks like this:
[ { "context" : "main", pc : 8 }, { "context" : "__main__", pc : 12 }, ]

(I'm going to push/pop at the front of the list)

We reach line 9, before we execute the code there:
[ { "context" : "main", pc : 9 }, { "context" : "__main__", pc : 12 }, ]

After we execute, it does a cexe call of set_name, not a normal call of
set_name. This causes the top stack frame to be _replaced_ :

[ { "context" : "set_name", pc : 1 }, { "context" : "__main__", pc : 12 }, ]

We then carry on, until we reach line 3, at which point before execution our
stack would look something like:

[ { "context" : "set_name", pc : 3 }, { "context" : "__main__", pc : 12 }, ]

And after:

[ { "context" : "greet", pc : 5 }, { "context" : "__main__", pc : 12 }, ]

We'd then execute line 6, and after executing that line, our stack would
look like this:

[ { "context" : "greet", pc : 6 }, { "context" : "__main__", pc : 12 }, ]

We're falling off the end of a function at that point, so we'd pop the top
stack frame, as follows:
[ { "context" : "__main__", pc : 12 }, ]

Which means we return to the line after 12, and continue on with line 13
print "see what I mean". That means the '''print "We don't reach here"'''
code isn't executed.
>* Am I mad? :)

Yep. :)
Thought so!

Thanks :-)
Michael.

Jul 2 '06 #4
Michael wrote:
def set_name():
name = raw_input("Enter your name! ")
cexe greet()
>
def greet():
print "hello", name
>
cexe set_name()
print "We don't reach here"
----------
>
This would execute, ask for the user's name, say hello to them and then
exit - not reaching the final print "We don't reach here" statement.
Only if you were to replace the whole stack. If you only replace or
reuse the top frame, I would think greet would exit and execution would
resume right after the point from which set_name was called. Or am I
misunderstanding what you want?

I think you are.
[snip]

I'm sorry; I didn't notice the use of cexe also on the call to
set_name. So now it makes sense.
Carl Banks

Jul 2 '06 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: Jonathan | last post by:
I'm puzzled by Python's behavior when binding local variables which are introduced within exec() or execfile() statements. First, consider this simple Python program: # main.py def f() : x = 1...
5
by: jrefactors | last post by:
when people say unix programmer, does it mean they write programs in unix environment,and those programs are run in unix platform? it is not necessary they are using unix function calls? I heard...
1
by: The Boss | last post by:
Hi All, I am trying to find a way to record when processes on a Unix/Linux system are started and when (i.e. new process ID, parent process ID, spawning user,time) as a way of modeling user...
5
by: markus | last post by:
Hi, I have a question that deals with the standard c library VS (Unix) system calls. The question is: which header files (and functions) are part of the C library and which header files (and...
36
by: lovecreatesbeauty | last post by:
In the C programming language, I/O operation functions are declared in stdio.h, for example, fopen(), fclose(), fwrite(), fread(), fseek() ... But another set of I/O functions are also defined in...
54
by: CoreyWhite | last post by:
The following experiment is a demonstration of TIME TRAVEL. When writing this program, and testing it out I found that sometimes the program would engage itself in time travel but other times it...
5
by: TPJ | last post by:
I have the following code: ----------------------------------- def f(): def g(): a = 'a' # marked line 1 exec 'a = "b"' in globals(), locals() print "g: a =", a
8
by: pamelafluente | last post by:
Hi guys, Is it possible to add "onload" (via Javascript) a new class to the <styleheader section? If yes, how would that be done ? <style type="text/css" media="screen"> .NewStyleClass{...
10
by: pamelafluente | last post by:
Hi, this time I am trying to add a style on the fly.I wish equivalency with this one (only the menuItemStyle line): <head> <style type="text/css" media="screen"> ... some static styles ......
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.