Format: text/x-python
⬇ Download File (6.4 KB)
   1  # This extension is used mainly for testing purposes - it is not
   2  # designed to be a simple sample, but instead is a hotch-potch of things
   3  # that attempts to exercise the framework.
   4  
   5  import os
   6  import stat
   7  import sys
   8  
   9  from isapi import isapicon
  10  from isapi.simple import SimpleExtension
  11  
  12  if hasattr(sys, "isapidllhandle"):
  13      import win32traceutil
  14  
  15  # We use the same reload support as 'advanced.py' demonstrates.
  16  import threading
  17  
  18  import win32con
  19  import win32event
  20  import win32file
  21  import winerror
  22  
  23  from isapi import InternalReloadException
  24  
  25  
  26  # A watcher thread that checks for __file__ changing.
  27  # When it detects it, it simply sets "change_detected" to true.
  28  class ReloadWatcherThread(threading.Thread):
  29      def __init__(self):
  30          self.change_detected = False
  31          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                      raise
  51                  break
  52              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 = True
  56                  last_time = this_time
  57  
  58      def stop(self):
  59          win32file.FindCloseChangeNotification(self.handle)
  60  
  61  
  62  def 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 the
  68  # response to the client.
  69  class 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 perform
  78          # this check in HttpExtensionProc - raising the exception from
  79          # The "Dispatch" method will just cause the exception to be
  80          # rendered to the browser.
  81          if self.reload_watcher.change_detected:
  82              print("Doing reload")
  83              raise InternalReloadException
  84  
  85          if ecb.GetServerVariable("UNICODE_URL").endswith("test.py"):
  86              file_flags = (
  87                  win32con.FILE_FLAG_SEQUENTIAL_SCAN | win32con.FILE_FLAG_OVERLAPPED
  88              )
  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_ASYNC
 100                  | isapicon.HSE_IO_DISCONNECT_AFTER_SEND
 101                  | isapicon.HSE_IO_SEND_HEADERS
 102              )
 103              # We pass hFile to the callback simply as a way of keeping it alive
 104              # for the duration of the transmission
 105              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                  raise
 121          else:
 122              # default response
 123              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_SUCCESS
 129  
 130      def TerminateExtension(self, status):
 131          self.reload_watcher.stop()
 132  
 133  
 134  # The entry points for the ISAPI extension.
 135  def __ExtensionFactory__():
 136      return Extension()
 137  
 138  
 139  # Our special command line customization.
 140  # Pre-install hook for our virtual directory.
 141  def 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.description
 146  
 147  
 148  # Post install hook for our entire script
 149  def 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.
 156  def status_handler(options, log, arg):
 157      "Query the status of something"
 158      print("Everything seems to be fine!")
 159  
 160  
 161  custom_arg_handlers = {"status": status_handler}
 162  
 163  if __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 our
 169      # extension uses - in this case only 1.
 170      # Each extension has a "script map" - this is the mapping of ISAPI
 171      # 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 OptionParser
 184  
 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_handlers
 195      )
 196