scriptutils.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. """
  2. Various utilities for running/importing a script
  3. """
  4. import sys
  5. import win32ui
  6. import win32api
  7. import win32con
  8. import __main__
  9. from pywin.mfc import dialog
  10. from pywin.mfc.docview import TreeView
  11. import os
  12. import string
  13. import traceback
  14. import linecache
  15. import bdb
  16. from .cmdline import ParseArgs
  17. RS_DEBUGGER_NONE=0 # Dont run under the debugger.
  18. RS_DEBUGGER_STEP=1 # Start stepping under the debugger
  19. RS_DEBUGGER_GO=2 # Just run under the debugger, stopping only at break-points.
  20. RS_DEBUGGER_PM=3 # Dont run under debugger, but do post-mortem analysis on exception.
  21. debugging_options = """No debugging
  22. Step-through in the debugger
  23. Run in the debugger
  24. Post-Mortem of unhandled exceptions""".split("\n")
  25. byte_cr = "\r".encode("ascii")
  26. byte_lf = "\n".encode("ascii")
  27. byte_crlf = "\r\n".encode("ascii")
  28. # A dialog box for the "Run Script" command.
  29. class DlgRunScript(dialog.Dialog):
  30. "A class for the 'run script' dialog"
  31. def __init__(self, bHaveDebugger):
  32. dialog.Dialog.__init__(self, win32ui.IDD_RUN_SCRIPT )
  33. self.AddDDX(win32ui.IDC_EDIT1, "script")
  34. self.AddDDX(win32ui.IDC_EDIT2, "args")
  35. self.AddDDX(win32ui.IDC_COMBO1, "debuggingType", "i")
  36. self.HookCommand(self.OnBrowse, win32ui.IDC_BUTTON2)
  37. self.bHaveDebugger = bHaveDebugger
  38. def OnInitDialog(self):
  39. rc = dialog.Dialog.OnInitDialog(self)
  40. cbo = self.GetDlgItem(win32ui.IDC_COMBO1)
  41. for o in debugging_options:
  42. cbo.AddString(o)
  43. cbo.SetCurSel(self['debuggingType'])
  44. if not self.bHaveDebugger:
  45. cbo.EnableWindow(0)
  46. def OnBrowse(self, id, cmd):
  47. openFlags = win32con.OFN_OVERWRITEPROMPT|win32con.OFN_FILEMUSTEXIST
  48. dlg = win32ui.CreateFileDialog(1,None,None,openFlags, "Python Scripts (*.py)|*.py||", self)
  49. dlg.SetOFNTitle("Run Script")
  50. if dlg.DoModal()!=win32con.IDOK:
  51. return 0
  52. self['script'] = dlg.GetPathName()
  53. self.UpdateData(0)
  54. return 0
  55. def GetDebugger():
  56. """Get the default Python debugger. Returns the debugger, or None.
  57. It is assumed the debugger has a standard "pdb" defined interface.
  58. Currently always returns the 'pywin.debugger' debugger, or None
  59. (pdb is _not_ returned as it is not effective in this GUI environment)
  60. """
  61. try:
  62. import pywin.debugger
  63. return pywin.debugger
  64. except ImportError:
  65. return None
  66. def IsOnPythonPath(path):
  67. "Given a path only, see if it is on the Pythonpath. Assumes path is a full path spec."
  68. # must check that the command line arg's path is in sys.path
  69. for syspath in sys.path:
  70. try:
  71. # Python 1.5 and later allows an empty sys.path entry.
  72. if syspath and win32ui.FullPath(syspath)==path:
  73. return 1
  74. except win32ui.error as details:
  75. print("Warning: The sys.path entry '%s' is invalid\n%s" % (syspath, details))
  76. return 0
  77. def GetPackageModuleName(fileName):
  78. """Given a filename, return (module name, new path).
  79. eg - given "c:\a\b\c\my.py", return ("b.c.my",None) if "c:\a" is on sys.path.
  80. If no package found, will return ("my", "c:\a\b\c")
  81. """
  82. path, fname = os.path.split(fileName)
  83. path=origPath=win32ui.FullPath(path)
  84. fname = os.path.splitext(fname)[0]
  85. modBits = []
  86. newPathReturn = None
  87. if not IsOnPythonPath(path):
  88. # Module not directly on the search path - see if under a package.
  89. while len(path)>3: # ie 'C:\'
  90. path, modBit = os.path.split(path)
  91. modBits.append(modBit)
  92. # If on path, _and_ existing package of that name loaded.
  93. if IsOnPythonPath(path) and modBit in sys.modules and \
  94. (os.path.exists(os.path.join(path, modBit, '__init__.py')) or \
  95. os.path.exists(os.path.join(path, modBit, '__init__.pyc')) or \
  96. os.path.exists(os.path.join(path, modBit, '__init__.pyo')) \
  97. ):
  98. modBits.reverse()
  99. return ".".join(modBits) + "." + fname, newPathReturn
  100. # Not found - look a level higher
  101. else:
  102. newPathReturn = origPath
  103. return fname, newPathReturn
  104. def GetActiveView():
  105. """Gets the edit control (eg, EditView) with the focus, or None
  106. """
  107. try:
  108. childFrame, bIsMaximised = win32ui.GetMainFrame().MDIGetActive()
  109. return childFrame.GetActiveView()
  110. except win32ui.error:
  111. return None
  112. def GetActiveEditControl():
  113. view = GetActiveView()
  114. if view is None: return None
  115. if hasattr(view, "SCIAddText"): # Is it a scintilla control?
  116. return view
  117. try:
  118. return view.GetRichEditCtrl()
  119. except AttributeError:
  120. pass
  121. try:
  122. return view.GetEditCtrl()
  123. except AttributeError:
  124. pass
  125. def GetActiveEditorDocument():
  126. """Returns the active editor document and view, or (None,None) if no
  127. active document or its not an editor document.
  128. """
  129. view = GetActiveView()
  130. if view is None or isinstance(view, TreeView):
  131. return (None, None)
  132. doc = view.GetDocument()
  133. if hasattr(doc, "MarkerAdd"): # Is it an Editor document?
  134. return doc, view
  135. return (None, None)
  136. def GetActiveFileName(bAutoSave = 1):
  137. """Gets the file name for the active frame, saving it if necessary.
  138. Returns None if it cant be found, or raises KeyboardInterrupt.
  139. """
  140. pathName = None
  141. active = GetActiveView()
  142. if active is None:
  143. return None
  144. try:
  145. doc = active.GetDocument()
  146. pathName = doc.GetPathName()
  147. if bAutoSave and \
  148. (len(pathName)>0 or \
  149. doc.GetTitle()[:8]=="Untitled" or \
  150. doc.GetTitle()[:6]=="Script"): # if not a special purpose window
  151. if doc.IsModified():
  152. try:
  153. doc.OnSaveDocument(pathName)
  154. pathName = doc.GetPathName()
  155. # clear the linecache buffer
  156. linecache.clearcache()
  157. except win32ui.error:
  158. raise KeyboardInterrupt
  159. except (win32ui.error, AttributeError):
  160. pass
  161. if not pathName:
  162. return None
  163. return pathName
  164. lastScript = ''
  165. lastArgs = ''
  166. lastDebuggingType = RS_DEBUGGER_NONE
  167. def RunScript(defName=None, defArgs=None, bShowDialog = 1, debuggingType=None):
  168. global lastScript, lastArgs, lastDebuggingType
  169. _debugger_stop_frame_ = 1 # Magic variable so the debugger will hide me!
  170. # Get the debugger - may be None!
  171. debugger = GetDebugger()
  172. if defName is None:
  173. try:
  174. pathName = GetActiveFileName()
  175. except KeyboardInterrupt:
  176. return # User cancelled save.
  177. else:
  178. pathName = defName
  179. if not pathName:
  180. pathName = lastScript
  181. if defArgs is None:
  182. args = ''
  183. if pathName==lastScript:
  184. args = lastArgs
  185. else:
  186. args = defArgs
  187. if debuggingType is None: debuggingType = lastDebuggingType
  188. if not pathName or bShowDialog:
  189. dlg = DlgRunScript(debugger is not None)
  190. dlg['script'] = pathName
  191. dlg['args'] = args
  192. dlg['debuggingType'] = debuggingType
  193. if dlg.DoModal() != win32con.IDOK:
  194. return
  195. script=dlg['script']
  196. args=dlg['args']
  197. debuggingType = dlg['debuggingType']
  198. if not script: return
  199. if debuggingType == RS_DEBUGGER_GO and debugger is not None:
  200. # This may surprise users - they select "Run under debugger", but
  201. # it appears not to! Only warn when they pick from the dialog!
  202. # First - ensure the debugger is activated to pickup any break-points
  203. # set in the editor.
  204. try:
  205. # Create the debugger, but _dont_ init the debugger GUI.
  206. rd = debugger._GetCurrentDebugger()
  207. except AttributeError:
  208. rd = None
  209. if rd is not None and len(rd.breaks)==0:
  210. msg = "There are no active break-points.\r\n\r\nSelecting this debug option without any\r\nbreak-points is unlikely to have the desired effect\r\nas the debugger is unlikely to be invoked..\r\n\r\nWould you like to step-through in the debugger instead?"
  211. rc = win32ui.MessageBox(msg, win32ui.LoadString(win32ui.IDR_DEBUGGER), win32con.MB_YESNOCANCEL | win32con.MB_ICONINFORMATION)
  212. if rc == win32con.IDCANCEL:
  213. return
  214. if rc == win32con.IDYES:
  215. debuggingType = RS_DEBUGGER_STEP
  216. lastDebuggingType = debuggingType
  217. lastScript = script
  218. lastArgs = args
  219. else:
  220. script = pathName
  221. # try and open the script.
  222. if len(os.path.splitext(script)[1])==0: # check if no extension supplied, and give one.
  223. script = script + '.py'
  224. # If no path specified, try and locate the file
  225. path, fnameonly = os.path.split(script)
  226. if len(path)==0:
  227. try:
  228. os.stat(fnameonly) # See if it is OK as is...
  229. script = fnameonly
  230. except os.error:
  231. fullScript = LocatePythonFile(script)
  232. if fullScript is None:
  233. win32ui.MessageBox("The file '%s' can not be located" % script )
  234. return
  235. script = fullScript
  236. else:
  237. path = win32ui.FullPath(path)
  238. if not IsOnPythonPath(path): sys.path.append(path)
  239. # py3k fun: If we use text mode to open the file, we get \r\n
  240. # translated so Python allows the syntax (good!), but we get back
  241. # text already decoded from the default encoding (bad!) and Python
  242. # ignores any encoding decls (bad!). If we use binary mode we get
  243. # the raw bytes and Python looks at the encoding (good!) but \r\n
  244. # chars stay in place so Python throws a syntax error (bad!).
  245. # So: so the binary thing and manually normalize \r\n.
  246. try:
  247. f = open(script, 'rb')
  248. except IOError as exc:
  249. win32ui.MessageBox("The file could not be opened - %s (%d)" % (exc.strerror, exc.errno))
  250. return
  251. # Get the source-code - as above, normalize \r\n
  252. code = f.read().replace(byte_crlf, byte_lf).replace(byte_cr, byte_lf) + byte_lf
  253. # Remember and hack sys.argv for the script.
  254. oldArgv = sys.argv
  255. sys.argv = ParseArgs(args)
  256. sys.argv.insert(0, script)
  257. # sys.path[0] is the path of the script
  258. oldPath0 = sys.path[0]
  259. newPath0 = os.path.split(script)[0]
  260. if not oldPath0: # if sys.path[0] is empty
  261. sys.path[0] = newPath0
  262. insertedPath0 = 0
  263. else:
  264. sys.path.insert(0, newPath0)
  265. insertedPath0 = 1
  266. bWorked = 0
  267. win32ui.DoWaitCursor(1)
  268. base = os.path.split(script)[1]
  269. # Allow windows to repaint before starting.
  270. win32ui.PumpWaitingMessages()
  271. win32ui.SetStatusText('Running script %s...' % base,1 )
  272. exitCode = 0
  273. from pywin.framework import interact
  274. # Check the debugger flags
  275. if debugger is None and (debuggingType != RS_DEBUGGER_NONE):
  276. win32ui.MessageBox("No debugger is installed. Debugging options have been ignored!")
  277. debuggingType = RS_DEBUGGER_NONE
  278. # Get a code object - ignore the debugger for this, as it is probably a syntax error
  279. # at this point
  280. try:
  281. codeObject = compile(code, script, "exec")
  282. except:
  283. # Almost certainly a syntax error!
  284. _HandlePythonFailure("run script", script)
  285. # No code object which to run/debug.
  286. return
  287. __main__.__file__=script
  288. try:
  289. if debuggingType == RS_DEBUGGER_STEP:
  290. debugger.run(codeObject, __main__.__dict__, start_stepping=1)
  291. elif debuggingType == RS_DEBUGGER_GO:
  292. debugger.run(codeObject, __main__.__dict__, start_stepping=0)
  293. else:
  294. # Post mortem or no debugging
  295. exec(codeObject, __main__.__dict__)
  296. bWorked = 1
  297. except bdb.BdbQuit:
  298. # Dont print tracebacks when the debugger quit, but do print a message.
  299. print("Debugging session cancelled.")
  300. exitCode = 1
  301. bWorked = 1
  302. except SystemExit as code:
  303. exitCode = code
  304. bWorked = 1
  305. except KeyboardInterrupt:
  306. # Consider this successful, as we dont want the debugger.
  307. # (but we do want a traceback!)
  308. if interact.edit and interact.edit.currentView:
  309. interact.edit.currentView.EnsureNoPrompt()
  310. traceback.print_exc()
  311. if interact.edit and interact.edit.currentView:
  312. interact.edit.currentView.AppendToPrompt([])
  313. bWorked = 1
  314. except:
  315. if interact.edit and interact.edit.currentView:
  316. interact.edit.currentView.EnsureNoPrompt()
  317. traceback.print_exc()
  318. if interact.edit and interact.edit.currentView:
  319. interact.edit.currentView.AppendToPrompt([])
  320. if debuggingType == RS_DEBUGGER_PM:
  321. debugger.pm()
  322. del __main__.__file__
  323. sys.argv = oldArgv
  324. if insertedPath0:
  325. del sys.path[0]
  326. else:
  327. sys.path[0] = oldPath0
  328. f.close()
  329. if bWorked:
  330. win32ui.SetStatusText("Script '%s' returned exit code %s" %(script, exitCode))
  331. else:
  332. win32ui.SetStatusText('Exception raised while running script %s' % base)
  333. try:
  334. sys.stdout.flush()
  335. except AttributeError:
  336. pass
  337. win32ui.DoWaitCursor(0)
  338. def ImportFile():
  339. """ This code looks for the current window, and determines if it can be imported. If not,
  340. it will prompt for a file name, and allow it to be imported. """
  341. try:
  342. pathName = GetActiveFileName()
  343. except KeyboardInterrupt:
  344. pathName = None
  345. if pathName is not None:
  346. if os.path.splitext(pathName)[1].lower() not in ('.py','.pyw','.pyx'):
  347. pathName = None
  348. if pathName is None:
  349. openFlags = win32con.OFN_OVERWRITEPROMPT|win32con.OFN_FILEMUSTEXIST
  350. dlg = win32ui.CreateFileDialog(1,None,None,openFlags, "Python Scripts (*.py;*.pyw)|*.py;*.pyw;*.pyx||")
  351. dlg.SetOFNTitle("Import Script")
  352. if dlg.DoModal()!=win32con.IDOK:
  353. return 0
  354. pathName = dlg.GetPathName()
  355. # If already imported, dont look for package
  356. path, modName = os.path.split(pathName)
  357. modName, modExt = os.path.splitext(modName)
  358. newPath = None
  359. # note that some packages (*cough* email *cough*) use "lazy importers"
  360. # meaning sys.modules can change as a side-effect of looking at
  361. # module.__file__ - so we must take a copy (ie, items() in py2k,
  362. # list(items()) in py3k)
  363. for key, mod in list(sys.modules.items()):
  364. if getattr(mod, '__file__', None):
  365. fname = mod.__file__
  366. base, ext = os.path.splitext(fname)
  367. if ext.lower() in ['.pyo', '.pyc']:
  368. ext = '.py'
  369. fname = base + ext
  370. if win32ui.ComparePath(fname, pathName):
  371. modName = key
  372. break
  373. else: # for not broken
  374. modName, newPath = GetPackageModuleName(pathName)
  375. if newPath: sys.path.append(newPath)
  376. if modName in sys.modules:
  377. bNeedReload = 1
  378. what = "reload"
  379. else:
  380. what = "import"
  381. bNeedReload = 0
  382. win32ui.SetStatusText(what.capitalize()+'ing module...',1)
  383. win32ui.DoWaitCursor(1)
  384. # win32ui.GetMainFrame().BeginWaitCursor()
  385. try:
  386. # always do an import, as it is cheap if it's already loaded. This ensures
  387. # it is in our name space.
  388. codeObj = compile('import '+modName,'<auto import>','exec')
  389. except SyntaxError:
  390. win32ui.SetStatusText('Invalid filename for import: "' +modName+'"')
  391. return
  392. try:
  393. exec(codeObj, __main__.__dict__)
  394. mod = sys.modules.get(modName)
  395. if bNeedReload:
  396. from importlib import reload
  397. mod = reload(sys.modules[modName])
  398. win32ui.SetStatusText('Successfully ' + what + "ed module '"+modName+"': %s" % getattr(mod,'__file__',"<unkown file>"))
  399. except:
  400. _HandlePythonFailure(what)
  401. win32ui.DoWaitCursor(0)
  402. def CheckFile():
  403. """ This code looks for the current window, and gets Python to check it
  404. without actually executing any code (ie, by compiling only)
  405. """
  406. try:
  407. pathName = GetActiveFileName()
  408. except KeyboardInterrupt:
  409. return
  410. what = "check"
  411. win32ui.SetStatusText(what.capitalize()+'ing module...',1)
  412. win32ui.DoWaitCursor(1)
  413. try:
  414. f = open(pathName)
  415. except IOError as details:
  416. print("Cant open file '%s' - %s" % (pathName, details))
  417. return
  418. try:
  419. code = f.read() + "\n"
  420. finally:
  421. f.close()
  422. try:
  423. codeObj = compile(code, pathName,'exec')
  424. if RunTabNanny(pathName):
  425. win32ui.SetStatusText("Python and the TabNanny successfully checked the file '"+os.path.basename(pathName)+"'")
  426. except SyntaxError:
  427. _HandlePythonFailure(what, pathName)
  428. except:
  429. traceback.print_exc()
  430. _HandlePythonFailure(what)
  431. win32ui.DoWaitCursor(0)
  432. def RunTabNanny(filename):
  433. import io as io
  434. tabnanny = FindTabNanny()
  435. if tabnanny is None:
  436. win32ui.MessageBox("The TabNanny is not around, so the children can run amok!" )
  437. return
  438. # Capture the tab-nanny output
  439. newout = io.StringIO()
  440. old_out = sys.stderr, sys.stdout
  441. sys.stderr = sys.stdout = newout
  442. try:
  443. tabnanny.check(filename)
  444. finally:
  445. # Restore output
  446. sys.stderr, sys.stdout = old_out
  447. data = newout.getvalue()
  448. if data:
  449. try:
  450. lineno = data.split()[1]
  451. lineno = int(lineno)
  452. _JumpToPosition(filename, lineno)
  453. try: # Try and display whitespace
  454. GetActiveEditControl().SCISetViewWS(1)
  455. except:
  456. pass
  457. win32ui.SetStatusText("The TabNanny found trouble at line %d" % lineno)
  458. except (IndexError, TypeError, ValueError):
  459. print("The tab nanny complained, but I cant see where!")
  460. print(data)
  461. return 0
  462. return 1
  463. def _JumpToPosition(fileName, lineno, col = 1):
  464. JumpToDocument(fileName, lineno, col)
  465. def JumpToDocument(fileName, lineno=0, col = 1, nChars = 0, bScrollToTop = 0):
  466. # Jump to the position in a file.
  467. # If lineno is <= 0, dont move the position - just open/restore.
  468. # if nChars > 0, select that many characters.
  469. # if bScrollToTop, the specified line will be moved to the top of the window
  470. # (eg, bScrollToTop should be false when jumping to an error line to retain the
  471. # context, but true when jumping to a method defn, where we want the full body.
  472. # Return the view which is editing the file, or None on error.
  473. doc = win32ui.GetApp().OpenDocumentFile(fileName)
  474. if doc is None: return None
  475. frame = doc.GetFirstView().GetParentFrame()
  476. try:
  477. view = frame.GetEditorView()
  478. if frame.GetActiveView() != view:
  479. frame.SetActiveView(view)
  480. frame.AutoRestore()
  481. except AttributeError: # Not an editor frame??
  482. view = doc.GetFirstView()
  483. if lineno > 0:
  484. charNo = view.LineIndex(lineno-1)
  485. start = charNo + col - 1
  486. size = view.GetTextLength()
  487. try:
  488. view.EnsureCharsVisible(charNo)
  489. except AttributeError:
  490. print("Doesnt appear to be one of our views?")
  491. view.SetSel(min(start, size), min(start + nChars, size))
  492. if bScrollToTop:
  493. curTop = view.GetFirstVisibleLine()
  494. nScroll = (lineno-1) - curTop
  495. view.LineScroll(nScroll, 0)
  496. view.SetFocus()
  497. return view
  498. def _HandlePythonFailure(what, syntaxErrorPathName = None):
  499. typ, details, tb = sys.exc_info()
  500. if isinstance(details, SyntaxError):
  501. try:
  502. msg, (fileName, line, col, text) = details
  503. if (not fileName or fileName =="<string>") and syntaxErrorPathName:
  504. fileName = syntaxErrorPathName
  505. _JumpToPosition(fileName, line, col)
  506. except (TypeError, ValueError):
  507. msg = str(details)
  508. win32ui.SetStatusText('Failed to ' + what + ' - syntax error - %s' % msg)
  509. else:
  510. traceback.print_exc()
  511. win32ui.SetStatusText('Failed to ' + what + ' - ' + str(details) )
  512. tb = None # Clean up a cycle.
  513. # Find the Python TabNanny in either the standard library or the Python Tools/Scripts directory.
  514. def FindTabNanny():
  515. try:
  516. return __import__("tabnanny")
  517. except ImportError:
  518. pass
  519. # OK - not in the standard library - go looking.
  520. filename = "tabnanny.py"
  521. try:
  522. path = win32api.RegQueryValue(win32con.HKEY_LOCAL_MACHINE, "SOFTWARE\\Python\\PythonCore\\%s\\InstallPath" % (sys.winver))
  523. except win32api.error:
  524. print("WARNING - The Python registry does not have an 'InstallPath' setting")
  525. print(" The file '%s' can not be located" % (filename))
  526. return None
  527. fname = os.path.join(path, "Tools\\Scripts\\%s" % filename)
  528. try:
  529. os.stat(fname)
  530. except os.error:
  531. print("WARNING - The file '%s' can not be located in path '%s'" % (filename, path))
  532. return None
  533. tabnannyhome, tabnannybase = os.path.split(fname)
  534. tabnannybase = os.path.splitext(tabnannybase)[0]
  535. # Put tab nanny at the top of the path.
  536. sys.path.insert(0, tabnannyhome)
  537. try:
  538. return __import__(tabnannybase)
  539. finally:
  540. # remove the tab-nanny from the path
  541. del sys.path[0]
  542. def LocatePythonFile( fileName, bBrowseIfDir = 1 ):
  543. " Given a file name, return a fully qualified file name, or None "
  544. # first look for the exact file as specified
  545. if not os.path.isfile(fileName):
  546. # Go looking!
  547. baseName = fileName
  548. for path in sys.path:
  549. fileName = os.path.abspath(os.path.join(path, baseName))
  550. if os.path.isdir(fileName):
  551. if bBrowseIfDir:
  552. d=win32ui.CreateFileDialog(1, "*.py", None, 0, "Python Files (*.py)|*.py|All files|*.*")
  553. d.SetOFNInitialDir(fileName)
  554. rc=d.DoModal()
  555. if rc==win32con.IDOK:
  556. fileName = d.GetPathName()
  557. break
  558. else:
  559. return None
  560. else:
  561. fileName = fileName + ".py"
  562. if os.path.isfile(fileName):
  563. break # Found it!
  564. else: # for not broken out of
  565. return None
  566. return win32ui.FullPath(fileName)