dev_appserver.py on Windows won't shut down properly

223 views
Skip to first unread message

Bartholomew Furrow

unread,
Apr 24, 2016, 1:17:38 AM4/24/16
to Google App Engine
I'm developing for App Engine on Windows 10, and I'm having difficulty getting dev_appserver.py to shut down properly when I run it from the commandline (specifically from cygwin).

Here are a couple of things I've tried:

From cygwin, the following command starts the server, but ctrl+c doesn't print the nice "shutting down and saving datastore changes" message I remember from Linux, and leaves one of two python.exe processes running:
/cygdrive/c/python27/pythonw.exe C:\\Program\ Files\ \(x86\)\\Google\\google_appengine\\dev_appserver.py --skip_sdk_update_check=yes --port=8080 --admin_port=8000 app/app.yaml app/long-running.yaml app/dispatch.yaml

From cmd.exe, the following command just starts two pythonw.exe processes, but immediately returns to the commandline to let me do more things, and I don't know how to kill the processes short of taskkill or task manager:
c:\Python27\pythonw.exe "c:\Program Files (x86)\Google\google_appengine\dev_appserver.py" --skip_sdk_update_check=yes --port=8080 --admin_port=8000 app/app.yaml app/long-running.yaml app/dispatch.yaml

Any thoughts about how to make dev_appserver.py terminate gracefully and clean up properly? In case anyone's wondering why I would want to do this, it's partly for end-to-end testing with Protractor -- I want a few active servers to start up, talk to Protractor and then die.

Thanks,
Bartholomew

Alexey

unread,
Apr 24, 2016, 3:43:15 PM4/24/16
to Google App Engine
I'm an App Engine user.  Used to do a lot of work for it on Windows 7 with Cygwin.  I've always had the same problem with dev server not shutting down fully from Ctrl+C.  The way to fully shut it down for me was to kill the main Java process (for a Java app) that ran in the background.  That brought down the whole thing and returned me to command prompt, if I rand dev_server.exe in the foreground.  Never had a problem with that approach, though it's admittedly a little annoying.  To kill the process, I used Task Manager.

Adam (Cloud Platform Support)

unread,
Apr 24, 2016, 4:00:07 PM4/24/16
to Google App Engine
Is there a reason why you need to use pythonw.exe? On Windows, pythonw.exe doesn't attach to the console and runs in the background. You'll need to manually kill the task eg. using taskkill /PID or from the task manager which won't call the shutdown handler. If you use python.exe, Ctrl+C should cleanly shut down the development server as expected.


On Sunday, April 24, 2016 at 1:17:38 AM UTC-4, Bartholomew Furrow wrote:

Bartholomew Furrow

unread,
Apr 25, 2016, 12:29:08 AM4/25/16
to Google App Engine
Adam,

That works for cmd.exe! FYI, python.exe wasn't working for me when I sent that message, but I apparently overwrote it with a 0B file; once restored, it did what you said. The command I listed above (sans w) works in cmd.exe and in a batch file run from cmd.exe. Thank you for pointing out my problem!

My next challenge, if you're willing to keep diving into madness with me, is how to make this work from cygwin. If I run the cygwin command I pasted above (with python.exe rather than pythonw.exe):
1. If I ctrl+c then there is no clean shutdown, and two python.exe processes keep running.
2. If I run cmd.exe /c the_batch_file_that_works_from_cmd.bat, then ctrl+c it, or kill -SIGINT it, then there is no clean shutdown, and one python.exe processes keep running.
3. I can kill the process with taskkill /f /t, then there is no clean shutdown, and no python.exe processes keep running. So I guess this is the best so far!

Any ideas on how to make the clean shutdown happen?

I've included three example invocations to make this more clear.

Example invocation: run from a batch file, then ctrl+c. Result: neither Python process is killed.

Bartholomew@Bartholomew2014 ~/dev/orders
$ ./serve_app_internal.bat

C:\Users\Bartholomew\Documents\dev\orders>c:\Python27\python.exe "c:\Program Files (x86)\Google\google_appengine\dev_appserver.py" --skip_sdk_update_check=yes --port=8080 --admin_port=8000 app/app.yaml app/long-running.yaml app/dispatch.yaml
INFO     2016-04-25 04:13:48,437 devappserver2.py:769] Skipping SDK update check.
<snip more startup stuff>
INFO     2016-04-25 04:13:48,585 admin_server.py:116] Starting admin server at: http://localhost:8000
<here I wait for a minute, then hit Ctrl+C>

Bartholomew@Bartholomew2014 ~/dev/orders
$


Example invocation: run from cygwin in the background, then kill the process with sigint. Result: One python process is killed.

Bartholomew@Bartholomew2014 ~/dev/orders
$ /cygdrive/c/python27/python.exe C:\\Program\ Files\ \(x86\)\\Google\\google_appengine\\dev_appserver.py     --skip_sdk_update_check=yes --port=$port --admin_port=$admin_port app/app.yaml app/long-running.yaml app/dispatch.yaml &
[1] 8588

Bartholomew@Bartholomew2014 ~/dev/orders
$ INFO     2016-04-25 04:30:00,427 devappserver2.py:769] Skipping SDK update check.
WARNING  2016-04-25 04:30:00,562 simple_search_stub.py:1126] Could not read search indexes from c:\cygwin64\tmp\appengine.paumpa-orders\search_indexes
<snip>
INFO     2016-04-25 04:30:00,578 admin_server.py:116] Starting admin server at: http://localhost:8000

<hit enter just to give me a prompt>
Bartholomew@Bartholomew2014 ~/dev/orders
$ kill -SIGINT 8588

Bartholomew@Bartholomew2014 ~/dev/orders
$ <hit enter so I can see the interrupt>
[1]+  Interrupt               /cygdrive/c/python27/python.exe C:\\Program\ Files\ \(x86\)\\Google\\google_appengine\\dev_appserver.py --skip_sdk_update_check=yes --port=$port --admin_port=$admin_port app/app.yaml app/long-running.yaml app/dispatch.yaml


Example invocation: run from cygwin in the background, then kill the process with taskkill /f /t. Result: All processes die, but the shutdown is not graceful

Bartholomew@Bartholomew2014 ~/dev/orders
$ /cygdrive/c/python27/python.exe C:\\Program\ Files\ \(x86\)\\Google\\google_appengine\\dev_appserver.py     --skip_sdk_update_check=yes --port=$port --admin_port=$admin_port app/app.yaml app/long-running.yaml app/dispatch.yaml &
[2] 9644

Bartholomew@Bartholomew2014 ~/dev/orders
$ INFO     2016-04-25 05:25:09,456 devappserver2.py:769] Skipping SDK update check.
<snip>
INFO     2016-04-25 05:25:09,604 admin_server.py:116] Starting admin server at: http://localhost:8000


Bartholomew@Bartholomew2014 ~/dev/orders
$ taskkill /PID 9644 /f /t
SUCCESS: The process with PID 8664 (child process of PID 9560) has been terminated.
SUCCESS: The process with PID 9560 (child process of PID 9644) has been terminated.
SUCCESS: The process with PID 9644 (child process of PID 2604) has been terminated.
[2]+  Exit 1                  /cygdrive/c/python27/python.exe C:\\Program\ Files\ \(x86\)\\Google\\google_appengine\\dev_appserver.py --skip_sdk_update_check=yes --port=$port --admin_port=$admin_port app/app.yaml app/long-running.yaml app/dispatch.yaml

Bartholomew@Bartholomew2014 ~/dev/orders
$

Adam (Cloud Platform Support)

unread,
Apr 26, 2016, 3:45:11 PM4/26/16
to Google App Engine
This relates to a known issue with the development server on Windows: It relies on KeyboardInterrupt to shutdown cleanly, as there is no equivalent to POSIX SIGINT on Windows (see here, here and here for some referenced public issues). Even if you use 'kill -SIGINT <pid>' in Cywin, the 'SIGINT' doesn't translate to native Windows processes.

One thing you could try is to install Python using the Cygwin installer (run from /usr/bin/python) and install the App Engine Python SDK for Linux / other platforms inside of your Cygwin environment.

If that doesn't work, another more involved and hacky option would be to call google.appengine.tools.devappserver2.api_server.cleanup_stubs() from your own handler in your application (eg. do 'curl http://localhost:8080/cleanup_stubs_handler' immediately before killing the process).

I haven't tested any of this myself, so take this information with a grain of salt.

Bartholomew Furrow

unread,
Apr 28, 2016, 4:22:02 PM4/28/16
to google-a...@googlegroups.com
Adam,

Thanks a lot for following up with me again, especially on a problem that isn't intrinsic to App Engine. It sounds like there isn't a perfect solution, and I really appreciate you explaining what the challenges are.

Since I've spent a bunch of time on this as well, I'll include a couple more musings for people who find this thread later down the road. Some of it, like your response, may be generally useful cygwin/bash stuff. Please feel free to ignore the rest of this email. :-)

Bartholomew

- Running dev_appserver.py in a .sh file in Cygwin spawns the following processes: bash.exe; python.exe (with bash.exe as its parent); python.exe (with the other python.exe as its parent). To be clear, the bash.exe process is spawned when your script hits the line "$pypath/python.exe $gaepath/dev_appserver.py ...", and it's in addition to whatever shell you're running. You can't use 'wait' or 'kill' on either python process because they aren't considered to belong to the bash shell running python.exe, and not the one running your script.

- To find out the PID of the bash.exe that's spawned to run, you can do something like this:
$pypath/python.exe $gaepath\\dev_appserver.py \
    --skip_sdk_update_check=yes --port=$port --admin_port=$admin_port app/app.yaml & echo $!
wait

- If you want to do something with the output of dev_appserver.py, like run it through grep -v so you don't see a bunch of spam, you can do something like this:
pid_file=`mktemp`
$pypath/python.exe $gaepath\\dev_appserver.py \
    --skip_sdk_update_check=yes --port=$port --admin_port=$admin_port app/app.yaml & echo $! >&4) 4>$pid_file 3>&1 1>&2 2>&3 3>&- |
    grep -vE "recording.py|module.py:787.*GET /([^a]|a[^/]).*(304|200)" 3>&1 1>&2 2>&3 3>&-

This says: "In a shell, run dev_appserver in the background, and echo the process ID into stream 4. Take the output of that shell (now in streams 1, 2 and 4). Put stream 4 into $pid_file. Swap stdout with stderr (that's 3>&1 1>&2 2>&3 3>&-). Grep out all requests (formerly appearing in stderr) for static files and all appstats information. Swap stdout with stderr again.

- To find out the python processes, you can then run commands like:
bashpid=`cat $pid_file`
pypid1=`wmic process where (parentprocessid=$bashpid) get processid`
pypid2=`wmic process where (parentprocessid=$pypid1) get processid`

- To wait for a process to terminate when wait isn't an option, you can use a command like:
while [ -e /proc/$pid ]; do sleep 0.5; done;

- Even if you trap sigint, for some reason cygwin immediately passes it on to the process. So something like:
trap kill_all_jobs sigint
function kill_all_jobs() {
  taskkill /pid $bashpid /f /t
}
...won't work, because $bashpid died the second you hit ctrl+c. It even took $pypid1 with it, but not $pypid2. You could, however, put this in kill_all_jobs:
taskkill /pid $pypid2 /f /t
...and that will complete the cleanup, though without the smooth shutdown. I don't know how to make the smooth shutdown happen on cygwin with ctrl+c because I don't know how to avoid the bash.exe and pypid1 processes being shut down before the trigger hits.

--
You received this message because you are subscribed to a topic in the Google Groups "Google App Engine" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/google-appengine/je2q7_s30QY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to google-appengi...@googlegroups.com.
To post to this group, send email to google-a...@googlegroups.com.
Visit this group at https://groups.google.com/group/google-appengine.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-appengine/94d7fa5d-2e4e-4ca8-8112-059849a67dd7%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages