689 lines
23 KiB
Python
689 lines
23 KiB
Python
|
"""
|
||
|
Various utilities for running/importing a script
|
||
|
"""
|
||
|
import bdb
|
||
|
import linecache
|
||
|
import os
|
||
|
import sys
|
||
|
import traceback
|
||
|
|
||
|
import __main__
|
||
|
import win32api
|
||
|
import win32con
|
||
|
import win32ui
|
||
|
from pywin.mfc import dialog
|
||
|
from pywin.mfc.docview import TreeView
|
||
|
|
||
|
from .cmdline import ParseArgs
|
||
|
|
||
|
RS_DEBUGGER_NONE = 0 # Dont run under the debugger.
|
||
|
RS_DEBUGGER_STEP = 1 # Start stepping under the debugger
|
||
|
RS_DEBUGGER_GO = 2 # Just run under the debugger, stopping only at break-points.
|
||
|
RS_DEBUGGER_PM = 3 # Dont run under debugger, but do post-mortem analysis on exception.
|
||
|
|
||
|
debugging_options = """No debugging
|
||
|
Step-through in the debugger
|
||
|
Run in the debugger
|
||
|
Post-Mortem of unhandled exceptions""".split(
|
||
|
"\n"
|
||
|
)
|
||
|
|
||
|
byte_cr = "\r".encode("ascii")
|
||
|
byte_lf = "\n".encode("ascii")
|
||
|
byte_crlf = "\r\n".encode("ascii")
|
||
|
|
||
|
|
||
|
# A dialog box for the "Run Script" command.
|
||
|
class DlgRunScript(dialog.Dialog):
|
||
|
"A class for the 'run script' dialog"
|
||
|
|
||
|
def __init__(self, bHaveDebugger):
|
||
|
dialog.Dialog.__init__(self, win32ui.IDD_RUN_SCRIPT)
|
||
|
self.AddDDX(win32ui.IDC_EDIT1, "script")
|
||
|
self.AddDDX(win32ui.IDC_EDIT2, "args")
|
||
|
self.AddDDX(win32ui.IDC_COMBO1, "debuggingType", "i")
|
||
|
self.HookCommand(self.OnBrowse, win32ui.IDC_BUTTON2)
|
||
|
self.bHaveDebugger = bHaveDebugger
|
||
|
|
||
|
def OnInitDialog(self):
|
||
|
rc = dialog.Dialog.OnInitDialog(self)
|
||
|
cbo = self.GetDlgItem(win32ui.IDC_COMBO1)
|
||
|
for o in debugging_options:
|
||
|
cbo.AddString(o)
|
||
|
cbo.SetCurSel(self["debuggingType"])
|
||
|
if not self.bHaveDebugger:
|
||
|
cbo.EnableWindow(0)
|
||
|
|
||
|
def OnBrowse(self, id, code):
|
||
|
if code != 0: # BN_CLICKED
|
||
|
return 1
|
||
|
openFlags = win32con.OFN_OVERWRITEPROMPT | win32con.OFN_FILEMUSTEXIST
|
||
|
dlg = win32ui.CreateFileDialog(
|
||
|
1, None, None, openFlags, "Python Scripts (*.py)|*.py||", self
|
||
|
)
|
||
|
dlg.SetOFNTitle("Run Script")
|
||
|
if dlg.DoModal() != win32con.IDOK:
|
||
|
return 0
|
||
|
self["script"] = dlg.GetPathName()
|
||
|
self.UpdateData(0)
|
||
|
return 0
|
||
|
|
||
|
|
||
|
def GetDebugger():
|
||
|
"""Get the default Python debugger. Returns the debugger, or None.
|
||
|
|
||
|
It is assumed the debugger has a standard "pdb" defined interface.
|
||
|
Currently always returns the 'pywin.debugger' debugger, or None
|
||
|
(pdb is _not_ returned as it is not effective in this GUI environment)
|
||
|
"""
|
||
|
try:
|
||
|
import pywin.debugger
|
||
|
|
||
|
return pywin.debugger
|
||
|
except ImportError:
|
||
|
return None
|
||
|
|
||
|
|
||
|
def IsOnPythonPath(path):
|
||
|
"Given a path only, see if it is on the Pythonpath. Assumes path is a full path spec."
|
||
|
# must check that the command line arg's path is in sys.path
|
||
|
for syspath in sys.path:
|
||
|
try:
|
||
|
# Python 1.5 and later allows an empty sys.path entry.
|
||
|
if syspath and win32ui.FullPath(syspath) == path:
|
||
|
return 1
|
||
|
except win32ui.error as details:
|
||
|
print(
|
||
|
"Warning: The sys.path entry '%s' is invalid\n%s" % (syspath, details)
|
||
|
)
|
||
|
return 0
|
||
|
|
||
|
|
||
|
def GetPackageModuleName(fileName):
|
||
|
"""Given a filename, return (module name, new path).
|
||
|
eg - given "c:\a\b\c\my.py", return ("b.c.my",None) if "c:\a" is on sys.path.
|
||
|
If no package found, will return ("my", "c:\a\b\c")
|
||
|
"""
|
||
|
path, fname = os.path.split(fileName)
|
||
|
path = origPath = win32ui.FullPath(path)
|
||
|
fname = os.path.splitext(fname)[0]
|
||
|
modBits = []
|
||
|
newPathReturn = None
|
||
|
if not IsOnPythonPath(path):
|
||
|
# Module not directly on the search path - see if under a package.
|
||
|
while len(path) > 3: # ie 'C:\'
|
||
|
path, modBit = os.path.split(path)
|
||
|
modBits.append(modBit)
|
||
|
# If on path, _and_ existing package of that name loaded.
|
||
|
if (
|
||
|
IsOnPythonPath(path)
|
||
|
and modBit in sys.modules
|
||
|
and (
|
||
|
os.path.exists(os.path.join(path, modBit, "__init__.py"))
|
||
|
or os.path.exists(os.path.join(path, modBit, "__init__.pyc"))
|
||
|
or os.path.exists(os.path.join(path, modBit, "__init__.pyo"))
|
||
|
)
|
||
|
):
|
||
|
modBits.reverse()
|
||
|
return ".".join(modBits) + "." + fname, newPathReturn
|
||
|
# Not found - look a level higher
|
||
|
else:
|
||
|
newPathReturn = origPath
|
||
|
|
||
|
return fname, newPathReturn
|
||
|
|
||
|
|
||
|
def GetActiveView():
|
||
|
"""Gets the edit control (eg, EditView) with the focus, or None"""
|
||
|
try:
|
||
|
childFrame, bIsMaximised = win32ui.GetMainFrame().MDIGetActive()
|
||
|
return childFrame.GetActiveView()
|
||
|
except win32ui.error:
|
||
|
return None
|
||
|
|
||
|
|
||
|
def GetActiveEditControl():
|
||
|
view = GetActiveView()
|
||
|
if view is None:
|
||
|
return None
|
||
|
if hasattr(view, "SCIAddText"): # Is it a scintilla control?
|
||
|
return view
|
||
|
try:
|
||
|
return view.GetRichEditCtrl()
|
||
|
except AttributeError:
|
||
|
pass
|
||
|
try:
|
||
|
return view.GetEditCtrl()
|
||
|
except AttributeError:
|
||
|
pass
|
||
|
|
||
|
|
||
|
def GetActiveEditorDocument():
|
||
|
"""Returns the active editor document and view, or (None,None) if no
|
||
|
active document or its not an editor document.
|
||
|
"""
|
||
|
view = GetActiveView()
|
||
|
if view is None or isinstance(view, TreeView):
|
||
|
return (None, None)
|
||
|
doc = view.GetDocument()
|
||
|
if hasattr(doc, "MarkerAdd"): # Is it an Editor document?
|
||
|
return doc, view
|
||
|
return (None, None)
|
||
|
|
||
|
|
||
|
def GetActiveFileName(bAutoSave=1):
|
||
|
"""Gets the file name for the active frame, saving it if necessary.
|
||
|
|
||
|
Returns None if it cant be found, or raises KeyboardInterrupt.
|
||
|
"""
|
||
|
pathName = None
|
||
|
active = GetActiveView()
|
||
|
if active is None:
|
||
|
return None
|
||
|
try:
|
||
|
doc = active.GetDocument()
|
||
|
pathName = doc.GetPathName()
|
||
|
|
||
|
if bAutoSave and (
|
||
|
len(pathName) > 0
|
||
|
or doc.GetTitle()[:8] == "Untitled"
|
||
|
or doc.GetTitle()[:6] == "Script"
|
||
|
): # if not a special purpose window
|
||
|
if doc.IsModified():
|
||
|
try:
|
||
|
doc.OnSaveDocument(pathName)
|
||
|
pathName = doc.GetPathName()
|
||
|
|
||
|
# clear the linecache buffer
|
||
|
linecache.clearcache()
|
||
|
|
||
|
except win32ui.error:
|
||
|
raise KeyboardInterrupt
|
||
|
|
||
|
except (win32ui.error, AttributeError):
|
||
|
pass
|
||
|
if not pathName:
|
||
|
return None
|
||
|
return pathName
|
||
|
|
||
|
|
||
|
lastScript = ""
|
||
|
lastArgs = ""
|
||
|
lastDebuggingType = RS_DEBUGGER_NONE
|
||
|
|
||
|
|
||
|
def RunScript(defName=None, defArgs=None, bShowDialog=1, debuggingType=None):
|
||
|
global lastScript, lastArgs, lastDebuggingType
|
||
|
_debugger_stop_frame_ = 1 # Magic variable so the debugger will hide me!
|
||
|
|
||
|
# Get the debugger - may be None!
|
||
|
debugger = GetDebugger()
|
||
|
|
||
|
if defName is None:
|
||
|
try:
|
||
|
pathName = GetActiveFileName()
|
||
|
except KeyboardInterrupt:
|
||
|
return # User cancelled save.
|
||
|
else:
|
||
|
pathName = defName
|
||
|
if not pathName:
|
||
|
pathName = lastScript
|
||
|
if defArgs is None:
|
||
|
args = ""
|
||
|
if pathName == lastScript:
|
||
|
args = lastArgs
|
||
|
else:
|
||
|
args = defArgs
|
||
|
if debuggingType is None:
|
||
|
debuggingType = lastDebuggingType
|
||
|
|
||
|
if not pathName or bShowDialog:
|
||
|
dlg = DlgRunScript(debugger is not None)
|
||
|
dlg["script"] = pathName
|
||
|
dlg["args"] = args
|
||
|
dlg["debuggingType"] = debuggingType
|
||
|
if dlg.DoModal() != win32con.IDOK:
|
||
|
return
|
||
|
script = dlg["script"]
|
||
|
args = dlg["args"]
|
||
|
debuggingType = dlg["debuggingType"]
|
||
|
if not script:
|
||
|
return
|
||
|
if debuggingType == RS_DEBUGGER_GO and debugger is not None:
|
||
|
# This may surprise users - they select "Run under debugger", but
|
||
|
# it appears not to! Only warn when they pick from the dialog!
|
||
|
# First - ensure the debugger is activated to pickup any break-points
|
||
|
# set in the editor.
|
||
|
try:
|
||
|
# Create the debugger, but _dont_ init the debugger GUI.
|
||
|
rd = debugger._GetCurrentDebugger()
|
||
|
except AttributeError:
|
||
|
rd = None
|
||
|
if rd is not None and len(rd.breaks) == 0:
|
||
|
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?"
|
||
|
rc = win32ui.MessageBox(
|
||
|
msg,
|
||
|
win32ui.LoadString(win32ui.IDR_DEBUGGER),
|
||
|
win32con.MB_YESNOCANCEL | win32con.MB_ICONINFORMATION,
|
||
|
)
|
||
|
if rc == win32con.IDCANCEL:
|
||
|
return
|
||
|
if rc == win32con.IDYES:
|
||
|
debuggingType = RS_DEBUGGER_STEP
|
||
|
|
||
|
lastDebuggingType = debuggingType
|
||
|
lastScript = script
|
||
|
lastArgs = args
|
||
|
else:
|
||
|
script = pathName
|
||
|
|
||
|
# try and open the script.
|
||
|
if (
|
||
|
len(os.path.splitext(script)[1]) == 0
|
||
|
): # check if no extension supplied, and give one.
|
||
|
script = script + ".py"
|
||
|
# If no path specified, try and locate the file
|
||
|
path, fnameonly = os.path.split(script)
|
||
|
if len(path) == 0:
|
||
|
try:
|
||
|
os.stat(fnameonly) # See if it is OK as is...
|
||
|
script = fnameonly
|
||
|
except os.error:
|
||
|
fullScript = LocatePythonFile(script)
|
||
|
if fullScript is None:
|
||
|
win32ui.MessageBox("The file '%s' can not be located" % script)
|
||
|
return
|
||
|
script = fullScript
|
||
|
else:
|
||
|
path = win32ui.FullPath(path)
|
||
|
if not IsOnPythonPath(path):
|
||
|
sys.path.append(path)
|
||
|
|
||
|
# py3k fun: If we use text mode to open the file, we get \r\n
|
||
|
# translated so Python allows the syntax (good!), but we get back
|
||
|
# text already decoded from the default encoding (bad!) and Python
|
||
|
# ignores any encoding decls (bad!). If we use binary mode we get
|
||
|
# the raw bytes and Python looks at the encoding (good!) but \r\n
|
||
|
# chars stay in place so Python throws a syntax error (bad!).
|
||
|
# So: so the binary thing and manually normalize \r\n.
|
||
|
try:
|
||
|
f = open(script, "rb")
|
||
|
except IOError as exc:
|
||
|
win32ui.MessageBox(
|
||
|
"The file could not be opened - %s (%d)" % (exc.strerror, exc.errno)
|
||
|
)
|
||
|
return
|
||
|
|
||
|
# Get the source-code - as above, normalize \r\n
|
||
|
code = f.read().replace(byte_crlf, byte_lf).replace(byte_cr, byte_lf) + byte_lf
|
||
|
|
||
|
# Remember and hack sys.argv for the script.
|
||
|
oldArgv = sys.argv
|
||
|
sys.argv = ParseArgs(args)
|
||
|
sys.argv.insert(0, script)
|
||
|
# sys.path[0] is the path of the script
|
||
|
oldPath0 = sys.path[0]
|
||
|
newPath0 = os.path.split(script)[0]
|
||
|
if not oldPath0: # if sys.path[0] is empty
|
||
|
sys.path[0] = newPath0
|
||
|
insertedPath0 = 0
|
||
|
else:
|
||
|
sys.path.insert(0, newPath0)
|
||
|
insertedPath0 = 1
|
||
|
bWorked = 0
|
||
|
win32ui.DoWaitCursor(1)
|
||
|
base = os.path.split(script)[1]
|
||
|
# Allow windows to repaint before starting.
|
||
|
win32ui.PumpWaitingMessages()
|
||
|
win32ui.SetStatusText("Running script %s..." % base, 1)
|
||
|
exitCode = 0
|
||
|
from pywin.framework import interact
|
||
|
|
||
|
# Check the debugger flags
|
||
|
if debugger is None and (debuggingType != RS_DEBUGGER_NONE):
|
||
|
win32ui.MessageBox(
|
||
|
"No debugger is installed. Debugging options have been ignored!"
|
||
|
)
|
||
|
debuggingType = RS_DEBUGGER_NONE
|
||
|
|
||
|
# Get a code object - ignore the debugger for this, as it is probably a syntax error
|
||
|
# at this point
|
||
|
try:
|
||
|
codeObject = compile(code, script, "exec")
|
||
|
except:
|
||
|
# Almost certainly a syntax error!
|
||
|
_HandlePythonFailure("run script", script)
|
||
|
# No code object which to run/debug.
|
||
|
return
|
||
|
__main__.__file__ = script
|
||
|
try:
|
||
|
if debuggingType == RS_DEBUGGER_STEP:
|
||
|
debugger.run(codeObject, __main__.__dict__, start_stepping=1)
|
||
|
elif debuggingType == RS_DEBUGGER_GO:
|
||
|
debugger.run(codeObject, __main__.__dict__, start_stepping=0)
|
||
|
else:
|
||
|
# Post mortem or no debugging
|
||
|
exec(codeObject, __main__.__dict__)
|
||
|
bWorked = 1
|
||
|
except bdb.BdbQuit:
|
||
|
# Dont print tracebacks when the debugger quit, but do print a message.
|
||
|
print("Debugging session cancelled.")
|
||
|
exitCode = 1
|
||
|
bWorked = 1
|
||
|
except SystemExit as code:
|
||
|
exitCode = code
|
||
|
bWorked = 1
|
||
|
except KeyboardInterrupt:
|
||
|
# Consider this successful, as we dont want the debugger.
|
||
|
# (but we do want a traceback!)
|
||
|
if interact.edit and interact.edit.currentView:
|
||
|
interact.edit.currentView.EnsureNoPrompt()
|
||
|
traceback.print_exc()
|
||
|
if interact.edit and interact.edit.currentView:
|
||
|
interact.edit.currentView.AppendToPrompt([])
|
||
|
bWorked = 1
|
||
|
except:
|
||
|
if interact.edit and interact.edit.currentView:
|
||
|
interact.edit.currentView.EnsureNoPrompt()
|
||
|
traceback.print_exc()
|
||
|
if interact.edit and interact.edit.currentView:
|
||
|
interact.edit.currentView.AppendToPrompt([])
|
||
|
if debuggingType == RS_DEBUGGER_PM:
|
||
|
debugger.pm()
|
||
|
del __main__.__file__
|
||
|
sys.argv = oldArgv
|
||
|
if insertedPath0:
|
||
|
del sys.path[0]
|
||
|
else:
|
||
|
sys.path[0] = oldPath0
|
||
|
f.close()
|
||
|
if bWorked:
|
||
|
win32ui.SetStatusText("Script '%s' returned exit code %s" % (script, exitCode))
|
||
|
else:
|
||
|
win32ui.SetStatusText("Exception raised while running script %s" % base)
|
||
|
try:
|
||
|
sys.stdout.flush()
|
||
|
except AttributeError:
|
||
|
pass
|
||
|
|
||
|
win32ui.DoWaitCursor(0)
|
||
|
|
||
|
|
||
|
def ImportFile():
|
||
|
"""This code looks for the current window, and determines if it can be imported. If not,
|
||
|
it will prompt for a file name, and allow it to be imported."""
|
||
|
try:
|
||
|
pathName = GetActiveFileName()
|
||
|
except KeyboardInterrupt:
|
||
|
pathName = None
|
||
|
|
||
|
if pathName is not None:
|
||
|
if os.path.splitext(pathName)[1].lower() not in (".py", ".pyw", ".pyx"):
|
||
|
pathName = None
|
||
|
|
||
|
if pathName is None:
|
||
|
openFlags = win32con.OFN_OVERWRITEPROMPT | win32con.OFN_FILEMUSTEXIST
|
||
|
dlg = win32ui.CreateFileDialog(
|
||
|
1, None, None, openFlags, "Python Scripts (*.py;*.pyw)|*.py;*.pyw;*.pyx||"
|
||
|
)
|
||
|
dlg.SetOFNTitle("Import Script")
|
||
|
if dlg.DoModal() != win32con.IDOK:
|
||
|
return 0
|
||
|
|
||
|
pathName = dlg.GetPathName()
|
||
|
|
||
|
# If already imported, dont look for package
|
||
|
path, modName = os.path.split(pathName)
|
||
|
modName, modExt = os.path.splitext(modName)
|
||
|
newPath = None
|
||
|
# note that some packages (*cough* email *cough*) use "lazy importers"
|
||
|
# meaning sys.modules can change as a side-effect of looking at
|
||
|
# module.__file__ - so we must take a copy (ie, items() in py2k,
|
||
|
# list(items()) in py3k)
|
||
|
for key, mod in list(sys.modules.items()):
|
||
|
if getattr(mod, "__file__", None):
|
||
|
fname = mod.__file__
|
||
|
base, ext = os.path.splitext(fname)
|
||
|
if ext.lower() in (".pyo", ".pyc"):
|
||
|
ext = ".py"
|
||
|
fname = base + ext
|
||
|
if win32ui.ComparePath(fname, pathName):
|
||
|
modName = key
|
||
|
break
|
||
|
else: # for not broken
|
||
|
modName, newPath = GetPackageModuleName(pathName)
|
||
|
if newPath:
|
||
|
sys.path.append(newPath)
|
||
|
|
||
|
if modName in sys.modules:
|
||
|
bNeedReload = 1
|
||
|
what = "reload"
|
||
|
else:
|
||
|
what = "import"
|
||
|
bNeedReload = 0
|
||
|
|
||
|
win32ui.SetStatusText(what.capitalize() + "ing module...", 1)
|
||
|
win32ui.DoWaitCursor(1)
|
||
|
# win32ui.GetMainFrame().BeginWaitCursor()
|
||
|
|
||
|
try:
|
||
|
# always do an import, as it is cheap if it's already loaded. This ensures
|
||
|
# it is in our name space.
|
||
|
codeObj = compile("import " + modName, "<auto import>", "exec")
|
||
|
except SyntaxError:
|
||
|
win32ui.SetStatusText('Invalid filename for import: "' + modName + '"')
|
||
|
return
|
||
|
try:
|
||
|
exec(codeObj, __main__.__dict__)
|
||
|
mod = sys.modules.get(modName)
|
||
|
if bNeedReload:
|
||
|
from importlib import reload
|
||
|
|
||
|
mod = reload(sys.modules[modName])
|
||
|
win32ui.SetStatusText(
|
||
|
"Successfully "
|
||
|
+ what
|
||
|
+ "ed module '"
|
||
|
+ modName
|
||
|
+ "': %s" % getattr(mod, "__file__", "<unkown file>")
|
||
|
)
|
||
|
except:
|
||
|
_HandlePythonFailure(what)
|
||
|
win32ui.DoWaitCursor(0)
|
||
|
|
||
|
|
||
|
def CheckFile():
|
||
|
"""This code looks for the current window, and gets Python to check it
|
||
|
without actually executing any code (ie, by compiling only)
|
||
|
"""
|
||
|
try:
|
||
|
pathName = GetActiveFileName()
|
||
|
except KeyboardInterrupt:
|
||
|
return
|
||
|
|
||
|
what = "check"
|
||
|
win32ui.SetStatusText(what.capitalize() + "ing module...", 1)
|
||
|
win32ui.DoWaitCursor(1)
|
||
|
try:
|
||
|
f = open(pathName)
|
||
|
except IOError as details:
|
||
|
print("Cant open file '%s' - %s" % (pathName, details))
|
||
|
return
|
||
|
try:
|
||
|
code = f.read() + "\n"
|
||
|
finally:
|
||
|
f.close()
|
||
|
try:
|
||
|
codeObj = compile(code, pathName, "exec")
|
||
|
if RunTabNanny(pathName):
|
||
|
win32ui.SetStatusText(
|
||
|
"Python and the TabNanny successfully checked the file '"
|
||
|
+ os.path.basename(pathName)
|
||
|
+ "'"
|
||
|
)
|
||
|
except SyntaxError:
|
||
|
_HandlePythonFailure(what, pathName)
|
||
|
except:
|
||
|
traceback.print_exc()
|
||
|
_HandlePythonFailure(what)
|
||
|
win32ui.DoWaitCursor(0)
|
||
|
|
||
|
|
||
|
def RunTabNanny(filename):
|
||
|
import io as io
|
||
|
|
||
|
tabnanny = FindTabNanny()
|
||
|
if tabnanny is None:
|
||
|
win32ui.MessageBox("The TabNanny is not around, so the children can run amok!")
|
||
|
return
|
||
|
|
||
|
# Capture the tab-nanny output
|
||
|
newout = io.StringIO()
|
||
|
old_out = sys.stderr, sys.stdout
|
||
|
sys.stderr = sys.stdout = newout
|
||
|
try:
|
||
|
tabnanny.check(filename)
|
||
|
finally:
|
||
|
# Restore output
|
||
|
sys.stderr, sys.stdout = old_out
|
||
|
data = newout.getvalue()
|
||
|
if data:
|
||
|
try:
|
||
|
lineno = data.split()[1]
|
||
|
lineno = int(lineno)
|
||
|
_JumpToPosition(filename, lineno)
|
||
|
try: # Try and display whitespace
|
||
|
GetActiveEditControl().SCISetViewWS(1)
|
||
|
except:
|
||
|
pass
|
||
|
win32ui.SetStatusText("The TabNanny found trouble at line %d" % lineno)
|
||
|
except (IndexError, TypeError, ValueError):
|
||
|
print("The tab nanny complained, but I cant see where!")
|
||
|
print(data)
|
||
|
return 0
|
||
|
return 1
|
||
|
|
||
|
|
||
|
def _JumpToPosition(fileName, lineno, col=1):
|
||
|
JumpToDocument(fileName, lineno, col)
|
||
|
|
||
|
|
||
|
def JumpToDocument(fileName, lineno=0, col=1, nChars=0, bScrollToTop=0):
|
||
|
# Jump to the position in a file.
|
||
|
# If lineno is <= 0, dont move the position - just open/restore.
|
||
|
# if nChars > 0, select that many characters.
|
||
|
# if bScrollToTop, the specified line will be moved to the top of the window
|
||
|
# (eg, bScrollToTop should be false when jumping to an error line to retain the
|
||
|
# context, but true when jumping to a method defn, where we want the full body.
|
||
|
# Return the view which is editing the file, or None on error.
|
||
|
doc = win32ui.GetApp().OpenDocumentFile(fileName)
|
||
|
if doc is None:
|
||
|
return None
|
||
|
frame = doc.GetFirstView().GetParentFrame()
|
||
|
try:
|
||
|
view = frame.GetEditorView()
|
||
|
if frame.GetActiveView() != view:
|
||
|
frame.SetActiveView(view)
|
||
|
frame.AutoRestore()
|
||
|
except AttributeError: # Not an editor frame??
|
||
|
view = doc.GetFirstView()
|
||
|
if lineno > 0:
|
||
|
charNo = view.LineIndex(lineno - 1)
|
||
|
start = charNo + col - 1
|
||
|
size = view.GetTextLength()
|
||
|
try:
|
||
|
view.EnsureCharsVisible(charNo)
|
||
|
except AttributeError:
|
||
|
print("Doesnt appear to be one of our views?")
|
||
|
view.SetSel(min(start, size), min(start + nChars, size))
|
||
|
if bScrollToTop:
|
||
|
curTop = view.GetFirstVisibleLine()
|
||
|
nScroll = (lineno - 1) - curTop
|
||
|
view.LineScroll(nScroll, 0)
|
||
|
view.SetFocus()
|
||
|
return view
|
||
|
|
||
|
|
||
|
def _HandlePythonFailure(what, syntaxErrorPathName=None):
|
||
|
typ, details, tb = sys.exc_info()
|
||
|
if isinstance(details, SyntaxError):
|
||
|
try:
|
||
|
msg, (fileName, line, col, text) = details
|
||
|
if (not fileName or fileName == "<string>") and syntaxErrorPathName:
|
||
|
fileName = syntaxErrorPathName
|
||
|
_JumpToPosition(fileName, line, col)
|
||
|
except (TypeError, ValueError):
|
||
|
msg = str(details)
|
||
|
win32ui.SetStatusText("Failed to " + what + " - syntax error - %s" % msg)
|
||
|
else:
|
||
|
traceback.print_exc()
|
||
|
win32ui.SetStatusText("Failed to " + what + " - " + str(details))
|
||
|
tb = None # Clean up a cycle.
|
||
|
|
||
|
|
||
|
# Find the Python TabNanny in either the standard library or the Python Tools/Scripts directory.
|
||
|
def FindTabNanny():
|
||
|
try:
|
||
|
return __import__("tabnanny")
|
||
|
except ImportError:
|
||
|
pass
|
||
|
# OK - not in the standard library - go looking.
|
||
|
filename = "tabnanny.py"
|
||
|
try:
|
||
|
path = win32api.RegQueryValue(
|
||
|
win32con.HKEY_LOCAL_MACHINE,
|
||
|
"SOFTWARE\\Python\\PythonCore\\%s\\InstallPath" % (sys.winver),
|
||
|
)
|
||
|
except win32api.error:
|
||
|
print("WARNING - The Python registry does not have an 'InstallPath' setting")
|
||
|
print(" The file '%s' can not be located" % (filename))
|
||
|
return None
|
||
|
fname = os.path.join(path, "Tools\\Scripts\\%s" % filename)
|
||
|
try:
|
||
|
os.stat(fname)
|
||
|
except os.error:
|
||
|
print(
|
||
|
"WARNING - The file '%s' can not be located in path '%s'" % (filename, path)
|
||
|
)
|
||
|
return None
|
||
|
|
||
|
tabnannyhome, tabnannybase = os.path.split(fname)
|
||
|
tabnannybase = os.path.splitext(tabnannybase)[0]
|
||
|
# Put tab nanny at the top of the path.
|
||
|
sys.path.insert(0, tabnannyhome)
|
||
|
try:
|
||
|
return __import__(tabnannybase)
|
||
|
finally:
|
||
|
# remove the tab-nanny from the path
|
||
|
del sys.path[0]
|
||
|
|
||
|
|
||
|
def LocatePythonFile(fileName, bBrowseIfDir=1):
|
||
|
"Given a file name, return a fully qualified file name, or None"
|
||
|
# first look for the exact file as specified
|
||
|
if not os.path.isfile(fileName):
|
||
|
# Go looking!
|
||
|
baseName = fileName
|
||
|
for path in sys.path:
|
||
|
fileName = os.path.abspath(os.path.join(path, baseName))
|
||
|
if os.path.isdir(fileName):
|
||
|
if bBrowseIfDir:
|
||
|
d = win32ui.CreateFileDialog(
|
||
|
1, "*.py", None, 0, "Python Files (*.py)|*.py|All files|*.*"
|
||
|
)
|
||
|
d.SetOFNInitialDir(fileName)
|
||
|
rc = d.DoModal()
|
||
|
if rc == win32con.IDOK:
|
||
|
fileName = d.GetPathName()
|
||
|
break
|
||
|
else:
|
||
|
return None
|
||
|
else:
|
||
|
fileName = fileName + ".py"
|
||
|
if os.path.isfile(fileName):
|
||
|
break # Found it!
|
||
|
|
||
|
else: # for not broken out of
|
||
|
return None
|
||
|
return win32ui.FullPath(fileName)
|