codekingpro/portable-devtools
114k
1# This extension is used mainly for testing purposes - it is not2# designed to be a simple sample, but instead is a hotch-potch of things3# that attempts to exercise the framework.4 5import os6import stat7import sys8 9from isapi import isapicon10from isapi.simple import SimpleExtension11 12if hasattr(sys, "isapidllhandle"):13 import win32traceutil14 15# We use the same reload support as 'advanced.py' demonstrates.16import threading17 18import win32con19import win32event20import win32file21import winerror22 23from isapi import InternalReloadException24 25 26# A watcher thread that checks for __file__ changing.27# When it detects it, it simply sets "change_detected" to true.28class ReloadWatcherThread(threading.Thread):29 def __init__(self):30 self.change_detected = False31 self.filename = __file__32 if self.filename.endswith("c") or self.filename.endswith("o"):33 self.filename = self.filename[:-1]34 self.handle = win32file.FindFirstChangeNotification(35 os.path.dirname(self.filename),36 False, # watch tree?37 win32con.FILE_NOTIFY_CHANGE_LAST_WRITE,38 )39 threading.Thread.__init__(self)40 41 def run(self):42 last_time = os.stat(self.filename)[stat.ST_MTIME]43 while 1:44 try:45 rc = win32event.WaitForSingleObject(self.handle, win32event.INFINITE)46 win32file.FindNextChangeNotification(self.handle)47 except win32event.error as details:48 # handle closed - thread should terminate.49 if details.winerror != winerror.ERROR_INVALID_HANDLE:50 raise51 break52 this_time = os.stat(self.filename)[stat.ST_MTIME]53 if this_time != last_time:54 print("Detected file change - flagging for reload.")55 self.change_detected = True56 last_time = this_time57 58 def stop(self):59 win32file.FindCloseChangeNotification(self.handle)60 61 62def TransmitFileCallback(ecb, hFile, cbIO, errCode):63 print("Transmit complete!")64 ecb.close()65 66 67# The ISAPI extension - handles requests in our virtual dir, and sends the68# response to the client.69class Extension(SimpleExtension):70 "Python test Extension"71 72 def __init__(self):73 self.reload_watcher = ReloadWatcherThread()74 self.reload_watcher.start()75 76 def HttpExtensionProc(self, ecb):77 # NOTE: If you use a ThreadPoolExtension, you must still perform78 # this check in HttpExtensionProc - raising the exception from79 # The "Dispatch" method will just cause the exception to be80 # rendered to the browser.81 if self.reload_watcher.change_detected:82 print("Doing reload")83 raise InternalReloadException84 85 if ecb.GetServerVariable("UNICODE_URL").endswith("test.py"):86 file_flags = (87 win32con.FILE_FLAG_SEQUENTIAL_SCAN | win32con.FILE_FLAG_OVERLAPPED88 )89 hfile = win32file.CreateFile(90 __file__,91 win32con.GENERIC_READ,92 0,93 None,94 win32con.OPEN_EXISTING,95 file_flags,96 None,97 )98 flags = (99 isapicon.HSE_IO_ASYNC100 | isapicon.HSE_IO_DISCONNECT_AFTER_SEND101 | isapicon.HSE_IO_SEND_HEADERS102 )103 # We pass hFile to the callback simply as a way of keeping it alive104 # for the duration of the transmission105 try:106 ecb.TransmitFile(107 TransmitFileCallback,108 hfile,109 int(hfile),110 "200 OK",111 0,112 0,113 None,114 None,115 flags,116 )117 except:118 # Errors keep this source file open!119 hfile.Close()120 raise121 else:122 # default response123 ecb.SendResponseHeaders("200 OK", "Content-Type: text/html\r\n\r\n", 0)124 print("<HTML><BODY>", file=ecb)125 print("The root of this site is at", ecb.MapURLToPath("/"), file=ecb)126 print("</BODY></HTML>", file=ecb)127 ecb.close()128 return isapicon.HSE_STATUS_SUCCESS129 130 def TerminateExtension(self, status):131 self.reload_watcher.stop()132 133 134# The entry points for the ISAPI extension.135def __ExtensionFactory__():136 return Extension()137 138 139# Our special command line customization.140# Pre-install hook for our virtual directory.141def PreInstallDirectory(params, options):142 # If the user used our special '--description' option,143 # then we override our default.144 if options.description:145 params.Description = options.description146 147 148# Post install hook for our entire script149def PostInstall(params, options):150 print()151 print("The sample has been installed.")152 print("Point your browser to /PyISAPITest")153 154 155# Handler for our custom 'status' argument.156def status_handler(options, log, arg):157 "Query the status of something"158 print("Everything seems to be fine!")159 160 161custom_arg_handlers = {"status": status_handler}162 163if __name__ == "__main__":164 # If run from the command-line, install ourselves.165 from isapi.install import *166 167 params = ISAPIParameters(PostInstall=PostInstall)168 # Setup the virtual directories - this is a list of directories our169 # extension uses - in this case only 1.170 # Each extension has a "script map" - this is the mapping of ISAPI171 # extensions.172 sm = [ScriptMapParams(Extension="*", Flags=0)]173 vd = VirtualDirParameters(174 Name="PyISAPITest",175 Description=Extension.__doc__,176 ScriptMaps=sm,177 ScriptMapUpdate="replace",178 # specify the pre-install hook.179 PreInstall=PreInstallDirectory,180 )181 params.VirtualDirs = [vd]182 # Setup our custom option parser.183 from optparse import OptionParser184 185 parser = OptionParser("") # blank usage, so isapi sets it.186 parser.add_option(187 "",188 "--description",189 action="store",190 help="custom description to use for the virtual directory",191 )192 193 HandleCommandLine(194 params, opt_parser=parser, custom_arg_handlers=custom_arg_handlers195 )196 