Code Search for Developers
 
 
  

cPyDebugger.pas from pyscripter at Krugle


Show cPyDebugger.pas syntax highlighted

{-----------------------------------------------------------------------------
 Unit Name: cPyDebugger
 Author:    Kiriakos Vlahos
 Date:      23-Feb-2005
 Purpose:   Interpreter and Debugger based on P4D PythonEngine
 History:   Origianlly Based on SynEdit IDE Demo debugger
-----------------------------------------------------------------------------}

unit cPyDebugger;

interface

uses
  Windows, SysUtils, Classes, uEditAppIntfs, PythonEngine, Forms, Contnrs,
  cPyBaseDebugger;

type
  TFrameInfo = class(TBaseFrameInfo)
  private
    fPyFrame : Variant;
  protected
  // Implementation of the Base class for the internal debugger
    function GetFunctionName : string; override;
    function GetFileName : string; override;
    function GetLine : integer; override;
  public
    constructor Create(Frame : Variant);
    property PyFrame : Variant read fPyFrame;
  end;

  TNameSpaceItem = class(TBaseNameSpaceItem)
  // Implementation of the Base class for the internal debugger
  private
    fPyObject : Variant;
    fChildNodes : TStringList;
    fName : string;
  protected
    function GetName : string; override;
    function GetObjectType : string; override;
    function GetValue : string; override;
    function GetDocString : string; override;
    function GetChildCount : integer; override;
    function GetChildNode(Index: integer): TBaseNameSpaceItem; override;
  public
    constructor Create(aName : string; aPyObject : Variant);
    destructor Destroy; override;
    function IsClass : Boolean; override;
    function IsDict : Boolean; override;
    function IsModule : Boolean; override;
    function IsFunction : Boolean; override;
    function IsMethod : Boolean; override;
    function Has__dict__ : Boolean; override;
    function IndexOfChild(AName : string): integer; override;
    procedure GetChildNodes; override;
  end;

  TPyInternalInterpreter = class(TPyBaseInterpreter)
  private
    fII : Variant;  // Python VarPyth wrapper to the interactive interpreter
    fDebugger : Variant;
    fOldargv : Variant;
  public
    constructor Create(II : Variant);
    function SysPathAdd(const Path : WideString) : boolean; override;
    function SysPathRemove(const Path : WideString) : boolean; override;
    procedure SysPathToStrings(Strings : TStrings); override;
    procedure StringsToSysPath(Strings : TStrings); override;
    function Compile(Editor : IEditor) : Variant;
    function GetGlobals : TBaseNameSpaceItem; override;
    function NameSpaceFromExpression(const Expr : string) : TBaseNameSpaceItem; override;
    function CallTipFromExpression(const Expr : string;
      var DisplayString, DocString : string) : Boolean; override;
    procedure SetCommandLine(const ScriptName : string); override;
    procedure RestoreCommandLine; override;
    function ImportModule(Editor : IEditor) : Variant; override;
    procedure RunNoDebug(Editor : IEditor); override;
    function SyntaxCheck(Editor : IEditor; Quiet : Boolean = False) : Boolean;
    function RunSource(const Source, FileName : string) : boolean; override;
    property Debugger : Variant read fDebugger;
    property PyInteractiveInterpreter : Variant read fII;
  end;

  TPyInternalDebugger = class(TPyBaseDebugger)
  // pdb based internal debugger
  private
    fDebuggerCommand : TDebuggerCommand;
    fCurrentFrame : Variant;
    fLineCache : Variant;
  protected
    procedure SetCommandLine(const ScriptName : string); override;
    procedure RestoreCommandLine; override;
    procedure SetDebuggerBreakpoints; override;
    procedure LoadLineCache;
    procedure UserCall(Sender: TObject; PSelf, Args: PPyObject; var Result: PPyObject);
    procedure UserLine(Sender: TObject; PSelf, Args: PPyObject; var Result: PPyObject);
    procedure UserReturn(Sender: TObject; PSelf, Args: PPyObject; var Result: PPyObject);
    procedure UserException(Sender: TObject; PSelf, Args: PPyObject; var Result: PPyObject);
    procedure UserYield(Sender: TObject; PSelf, Args: PPyObject; var Result: PPyObject);
    property CurrentFrame : Variant read fCurrentFrame;
  public
    constructor Create;

    function SysPathAdd(const Path : WideString) : boolean; override;
    function SysPathRemove(const Path : WideString) : boolean; override;
    procedure Run(Editor : IEditor; InitStepIn : Boolean = False); override;
    procedure RunToCursor(Editor : IEditor; ALine: integer); override;
    procedure StepInto(Editor : IEditor); override;
    procedure StepOver; override;
    procedure StepOut; override;
    procedure Pause; override;
    procedure Abort; override;
    procedure Evaluate(const Expr : string; out ObjType, Value : string); override;
    function RunSource(Const Source, FileName : string) : boolean; override;
    procedure GetCallStack(CallStackList : TObjectList); override;
    function GetFrameGlobals(Frame : TBaseFrameInfo) : TBaseNameSpaceItem; override;
    function GetFrameLocals(Frame : TBaseFrameInfo) : TBaseNameSpaceItem; override;
    function NameSpaceFromExpression(const Expr : string) : TBaseNameSpaceItem; override;
  end;

var
  InternalInterpreter : TPyInternalInterpreter = nil;

implementation

uses dmCommands, frmPythonII, VarPyth, frmMessages, frmPyIDEMain,
  MMSystem, Math, JvDockControlForm, JclFileUtils, Dialogs, uCommonFunctions,
  cParameters, JclSysUtils, StringResources;

{ TFrameInfo }

constructor TFrameInfo.Create(Frame: Variant);
begin
  fPyFrame := Frame;
  inherited Create;
end;

function TFrameInfo.GetFileName: string;
begin
  Result := fPyFrame.f_code.co_filename;
end;

function TFrameInfo.GetFunctionName: string;
begin
  Result := fPyFrame.f_code.co_name;
end;

function TFrameInfo.GetLine: integer;
begin
  Result := fPyFrame.f_lineno;
end;

{ TNameSpaceItem }

constructor TNameSpaceItem.Create(aName : string; aPyObject: Variant);
begin
  fName := aName;
  fPyObject := aPyObject;
end;

destructor TNameSpaceItem.Destroy;
Var
  i : integer;
begin
  if Assigned(fChildNodes) then begin;
    for i := 0 to fChildNodes.Count - 1 do
      fChildNodes.Objects[i].Free;
    fChildNodes.Free;
  end;
  inherited;
end;

function TNameSpaceItem.GetChildCount: integer;
var
  i : integer;
  SupressOutput : IInterface;
begin
  if Assigned(fChildNodes) then
    Result := fChildNodes.Count
  else begin
    SupressOutput := PythonIIForm.OutputSuppressor; // Do not show errors
    try
      if VarIsPythonDict(fPyObject) then
        Result := len(fPyObject)
      else if SortedIdentToInt(_type(fPyObject).__name__, i, CommonTypes, True) then
        Result := 0  // do not expand common types
      else if Has__dict__ then
        Result := len(fPyObject.__dict__)
      else begin
        Result := len(Import('inspect').getmembers(fPyObject));
      end;
    except
      Result := 0;
    end;
  end;
end;

function TNameSpaceItem.GetChildNode(Index: integer): TBaseNameSpaceItem;
begin
  Assert(Index >= 0, 'TNameSpaceItem.GetChildNode');
  if not Assigned(fChildNodes) then
    GetChildNodes;
  Assert(Index < fChildNodes.Count, 'TNameSpaceItem.GetChildNode');
  Result := fChildNodes.Objects[Index] as TBaseNameSpaceItem;
end;

procedure TNameSpaceItem.GetChildNodes;
Var
  Dict, DictKeys : Variant;
  Count, i : integer;
  Name : string;
  NameSpaceItem : TNameSpaceItem;
  SupressOutput : IInterface;
begin
  if not Assigned(fChildNodes) then begin
    SupressOutput := PythonIIForm.OutputSuppressor; // Do not show errors
    try
      if VarIsPythonDict(fPyObject) then begin
        Dict := fPyObject;
        Count := len(fPyObject);
      end else if SortedIdentToInt(_type(fPyObject).__name__, i, CommonTypes, True) then
        Count := 0  // do not expand common types
      else begin
        Dict := BuiltinModule.dict(Import('inspect').getmembers(fPyObject));
        Count := len(Dict);
      end;

      if Count > 0 then begin
        fChildNodes := TStringList.Create;
        fChildNodes.CaseSensitive := True;
        fChildNodes.Duplicates := dupAccept;
        DictKeys := Dict.keys();
        for i := 0 to Count - 1 do begin
          Name :=  DictKeys.GetItem(i);
          NameSpaceItem := TNameSpaceItem.Create(Name,
            Dict.GetItem(DictKeys.GetItem(i)));
          fChildNodes.AddObject(Name, NameSpaceItem);
        end;
        fChildNodes.CustomSort(ComparePythonIdents);
        GotChildNodes := True;
      end;
    except
      // fail quietly
    end;
  end;
end;

function TNameSpaceItem.GetDocString: string;
Var
  SupressOutput : IInterface;
begin
  SupressOutput := PythonIIForm.OutputSuppressor; // Do not show errors
  try
    Result := Import('inspect').getdoc(fPyObject);
  except
    Result := '';
  end;
end;

function TNameSpaceItem.GetName: string;
begin
  Result := fName;
end;

function TNameSpaceItem.GetObjectType: string;
Var
  SupressOutput : IInterface;
begin
  SupressOutput := PythonIIForm.OutputSuppressor; // Do not show errors
  try
    Result := _type(fPyObject).__name__;
  except
    Result := '';
  end;
end;

function TNameSpaceItem.GetValue: string;
Var
  SupressOutput : IInterface;
begin
  SupressOutput := PythonIIForm.OutputSuppressor; // Do not show errors
  try
    Result :=  InternalInterpreter.PyInteractiveInterpreter.saferepr(fPyObject);
  except
    Result := '';
  end;
end;

function TNameSpaceItem.Has__dict__: Boolean;
begin
  Result := (GetPythonEngine.PyObject_HasAttrString(
        ExtractPythonObjectFrom(fPyObject), '__dict__') = 1) and
        VarIsPythonDict(fPyObject.__dict__) // it can be None with extensions!;
end;

function TNameSpaceItem.IndexOfChild(AName: string): integer;
begin
  if not Assigned(fChildNodes) then
    GetChildNodes;
  if Assigned(fChildNodes) then
    Result := fChildNodes.IndexOf(AName)
  else
    Result := -1;
end;

function TNameSpaceItem.IsClass: Boolean;
begin
  Result := VarIsPythonClass(fPyObject);
end;

function TNameSpaceItem.IsDict: Boolean;
begin
  Result := VarIsPythonDict(fPyObject);
end;

function TNameSpaceItem.IsFunction: Boolean;
begin
  Result := VarIsPythonFunction(fPyObject);
end;

function TNameSpaceItem.IsMethod: Boolean;
begin
  Result := VarIsPythonMethod(fPyObject);
end;

function TNameSpaceItem.IsModule: Boolean;
begin
  Result := VarIsPythonModule(fPyObject);
end;

{ TPyDebugger }

constructor TPyInternalDebugger.Create;
begin
  inherited Create;
  fLineCache := Import('linecache');
  fDebuggerCommand := dcNone;
end;

procedure TPyInternalDebugger.Evaluate(const Expr : string; out ObjType, Value : string);
Var
  SupressOutput : IInterface;
  V : Variant;
begin
  ObjType := SNotAvailable;
  Value := SNotAvailable;
  if PyControl.DebuggerState = dsPaused then begin
    SupressOutput := PythonIIForm.OutputSuppressor; // Do not show errors
    try
      V := BuiltInModule.eval(Expr,
             CurrentFrame.f_globals, CurrentFrame.f_locals);
      ObjType := BuiltInModule.type(V).__name__;
      Value := InternalInterpreter.PyInteractiveInterpreter.saferepr(V);
    except
      // fail quitely
    end;
  end;
end;

procedure TPyInternalDebugger.GetCallStack(CallStackList: TObjectList);
Var
  Frame : Variant;
begin
  CallStackList.Clear;
  if PyControl.DebuggerState <> dsPaused then
    Exit;
  Frame := CurrentFrame;
  if VarIsPython(Frame) then
    // We do not show the last two frames
    while not VarIsNone(Frame.f_back) and not VarIsNone(Frame.f_back.f_back) do begin
      CallStackList.Add(TFrameInfo.Create(Frame));
      Frame := Frame.f_back;
    end;
end;

function TPyInternalDebugger.GetFrameGlobals(Frame: TBaseFrameInfo): TBaseNameSpaceItem;
begin
  Result := nil;
  if PyControl.DebuggerState <> dsPaused then
    Exit;
  Result := TNameSpaceItem.Create('globals', (Frame as TFrameInfo).fPyFrame.f_globals);
end;

function TPyInternalDebugger.GetFrameLocals(Frame: TBaseFrameInfo): TBaseNameSpaceItem;
begin
  Result := nil;
  if PyControl.DebuggerState <> dsPaused then
    Exit;
  Result := TNameSpaceItem.Create('locals', (Frame as TFrameInfo).fPyFrame.f_locals);
end;

procedure TPyInternalDebugger.Pause;
// Not pausible to implement with the debugger running in the main thread
begin
  if PyControl.DebuggerState = dsRunning then
    PyControl.DoStateChange(dsPaused);
end;

// Timer callback function
procedure TPyInternalDebugger.RestoreCommandLine;
begin
  InternalInterpreter.RestoreCommandLine;
end;

procedure TPyInternalDebugger.Run(Editor : IEditor; InitStepIn : Boolean = False);
var
  OldPS1, OldPS2 : string;
  Code : Variant;
  Path, OldPath : string;
  PythonPathAdder : IInterface;
begin
  // Repeat here to make sure it is set right
  MaskFPUExceptions(CommandsDataModule.PyIDEOptions.MaskFPUExceptions);

  fDebuggerCommand := dcRun;
  if (PyControl.DebuggerState <> dsInactive) then exit; // pass control to user_line

  //Compile
  Code := InternalInterpreter.Compile(Editor);

  if VarIsPython(Code) then with PythonIIForm do begin
    Path := ExtractFilePath(Editor.FileName);
    OldPath := GetCurrentDir;
    if Length(Path) > 1 then begin
      Path := PathRemoveSeparator(Path);
      // Add the path of the executed file to the Python path - Will be automatically removed
      PythonPathAdder := AddPathToPythonPath(Path);

      // Change the current path
      try
        ChDir(Path)
      except
        MessageDlg('Could not set the current directory to the script path', mtWarning, [mbOK], 0);
      end;
    end;

    PyControl.DoStateChange(dsRunning);

    // Set the layout to the Debug layout is it exists
    MessagesWindow.ClearMessages;
    if PyIDEMainForm.Layouts.IndexOf('Debug') >= 0 then begin
      PyIDEMainForm.SaveLayout('Current');
      PyIDEMainForm.LoadLayout('Debug');
      Application.ProcessMessages;
    end else
      ShowDockForm(PythonIIForm);

    try
      OldPS1 := PS1;
      PS1 := '[Dbg]' + PS1;
      OldPS2 := PS2;
      PS2 := '[Dbg]' + PS2;
      AppendText(sLineBreak+PS1);
      //attach debugger callback routines
      DebugIDE.Events.Items[0].OnExecute := UserCall;
      DebugIDE.Events.Items[1].OnExecute := UserLine;
      DebugIDE.Events.Items[2].OnExecute := UserReturn;
      DebugIDE.Events.Items[3].OnExecute := UserException;
      DebugIDE.Events.Items[4].OnExecute := UserYield;
      //set breakpoints
      SetDebuggerBreakPoints;

      // New Line for output
      PythonIIForm.AppendText(sLineBreak);

      // Set the command line parameters
      SetCommandLine(Editor.GetFileNameOrTitle);

      InternalInterpreter.Debugger.InitStepIn := InitStepIn;
      try
        InternalInterpreter.Debugger.run(Code);
      except
        // CheckError already called by VarPyth
        on E: EPyException do begin
          InternalInterpreter.HandlePyException(E);
          MessageDlg(E.Message, mtError, [mbOK], 0);
          SysUtils.Abort;
        end;
      end;

    finally
      PS1 := OldPS1;
      PS2 := OldPS2;
      AppendText(sLineBreak+PS1);

      // Restore the command line parameters
      RestoreCommandLine;

      // Change the back current path
      ChDir(OldPath);

      if PyIDEMainForm.Layouts.IndexOf('Debug') >= 0 then
        PyIDEMainForm.LoadLayout('Current');

      PyControl.DoStateChange(dsInactive);
    end;
  end;
end;

function TPyInternalDebugger.RunSource(const Source, FileName: string): boolean;
// The internal interpreter RunSource calls II.runsource which differs
// according to whether we debugging or not
begin
  Result := InternalInterpreter.RunSource(Source, FileName);
end;

procedure TPyInternalDebugger.RunToCursor(Editor : IEditor; ALine: integer);
Var
  FName : string;
begin
  // Set Temporary breakpoint
  SetDebuggerBreakPoints;  // So that this one is not cleared
  FName := Editor.FileName;
  if FName = '' then
    FName := '<'+Editor.FileTitle+'>';
  InternalInterpreter.Debugger.set_break(FName, ALine, 1);

  if PyControl.DebuggerState = dsInactive then
    Run(Editor)
  else
    fDebuggerCommand := dcRunToCursor;
end;

procedure TPyInternalDebugger.StepInto(Editor : IEditor);
begin
  if PyControl.DebuggerState = dsInactive then
    Run(Editor, True)
  else
    fDebuggerCommand := dcStepInto;
end;

procedure TPyInternalDebugger.StepOver;
begin
  fDebuggerCommand := dcStepOver;
end;

function TPyInternalDebugger.SysPathAdd(const Path: WideString): boolean;
begin
  Result := InternalInterpreter.SysPathAdd(Path);
end;

function TPyInternalDebugger.SysPathRemove(const Path: WideString): boolean;
begin
  Result := InternalInterpreter.SysPathRemove(Path);
end;

procedure TPyInternalDebugger.StepOut;
begin
  fDebuggerCommand := dcStepOut;
end;

procedure TPyInternalDebugger.Abort;
begin
  fDebuggerCommand := dcAbort;
end;

procedure TPyInternalDebugger.UserCall(Sender: TObject; PSelf, Args: PPyObject;
  var Result: PPyObject);
begin
   // PythonIIForm.AppendText('UserCall'+sLineBreak);
   Result := GetPythonEngine.ReturnNone;
end;

procedure TPyInternalDebugger.UserException(Sender: TObject; PSelf,
  Args: PPyObject; var Result: PPyObject);
begin
   // PythonIIForm.AppendText('UserException'+sLineBreak);
   Result := GetPythonEngine.ReturnNone;
end;

procedure TPyInternalDebugger.UserLine(Sender: TObject; PSelf, Args: PPyObject;
  var Result: PPyObject);
Var
  Frame : Variant;
  FName : string;
begin
   Result := GetPythonEngine.ReturnNone;

   Frame := VarPythonCreate(Args).GetItem(0);
   FName := Frame.f_code.co_filename;
   if (FName[1] ='<') and (FName[Length(FName)] = '>') then
     FName :=  Copy(FName, 2, Length(FName)-2);
   // PythonIIForm.AppendText('UserLine '+ FName + ' ' + IntToStr(Frame.f_lineno) +sLineBreak);

   if PyIDEMainForm.ShowFilePosition(FName, Frame.f_lineno, 1) and
     (Frame.f_lineno > 0) then
   begin
     PyControl.CurrentPos.Editor := GI_EditorFactory.GetEditorByNameOrTitle(FName);
     PyControl.CurrentPos.Line := Frame.f_lineno;
     FCurrentFrame := Frame;

     Pause;
     FDebuggerCommand := dcNone;
     While  FDebuggerCommand = dcNone do
       PyControl.DoYield(True);

     VarClear(fCurrentFrame);
     if PyControl.BreakPointsChanged then SetDebuggerBreakpoints;

     PyControl.DoStateChange(dsRunning);
   end;

   case fDebuggerCommand of
     dcRun         : InternalInterpreter.Debugger.set_continue();
     dcStepInto    : InternalInterpreter.Debugger.set_step();
     dcStepOver    : InternalInterpreter.Debugger.set_next(Frame);
     dcStepOut     : InternalInterpreter.Debugger.set_return(Frame);
     dcRunToCursor : InternalInterpreter.Debugger.set_continue;
     dcAbort       : begin
                       InternalInterpreter.Debugger.set_quit();
                       MessagesWindow.AddMessage('Debugging Aborted');
                       MessagesWindow.ShowWindow;
                     end;
   end;

end;

procedure TPyInternalDebugger.UserReturn(Sender: TObject; PSelf, Args: PPyObject;
  var Result: PPyObject);
begin
   // PythonIIForm.AppendText('UserReturn'+sLineBreak);
   Result := GetPythonEngine.ReturnNone;
end;

procedure TPyInternalDebugger.UserYield(Sender: TObject; PSelf, Args: PPyObject;
  var Result: PPyObject);
begin
  PyControl.DoYield(False);
  if fDebuggerCommand = dcAbort then begin
    InternalInterpreter.Debugger.set_quit();
    MessagesWindow.AddMessage('Debugging Aborted');
    MessagesWindow.ShowWindow;
  end;
  Result := GetPythonEngine.ReturnNone;
end;

procedure TPyInternalDebugger.SetCommandLine(const ScriptName: string);
begin
  InternalInterpreter.SetCommandLine(ScriptName);
end;

procedure TPyInternalDebugger.SetDebuggerBreakpoints;
var
  i, j : integer;
  FName : string;
  V : Variant;
begin
  if not PyControl.BreakPointsChanged then Exit;
  LoadLineCache;
  InternalInterpreter.Debugger.clear_all_breaks();
  for i := 0 to GI_EditorFactory.Count - 1 do
    with GI_EditorFactory.Editor[i] do begin
      FName := FileName;
      if FName = '' then
        FName := '<'+FileTitle+'>';
      for j := 0 to BreakPoints.Count - 1 do begin
        V := InternalInterpreter.Debugger.set_break;
        if TBreakPoint(BreakPoints[j]).Condition <> '' then begin
          GetPythonEngine.EvalFunction(ExtractPythonObjectFrom(V),
            [FName, TBreakPoint(BreakPoints[j]).LineNo,
            0, TBreakPoint(BreakPoints[j]).Condition]);
           //  To avoid the implicit conversion of the filename to unicode
           //  which causes problems due to the strange behaviour of os.path.normcase
          //InternalInterpreter.Debugger.set_break(FName, TBreakPoint(BreakPoints[j]).LineNo,
          //  0, TBreakPoint(BreakPoints[j]).Condition);
        end else
          GetPythonEngine.EvalFunction(ExtractPythonObjectFrom(V),
            [FName, TBreakPoint(BreakPoints[j]).LineNo]);
          //InternalInterpreter.Debugger.set_break(FName, TBreakPoint(BreakPoints[j]).LineNo);
      end;
    end;
  PyControl.BreakPointsChanged := False;
end;

procedure TPyInternalDebugger.LoadLineCache;
Var
  i : integer;
  Source : string;
begin
  // inject unsaved code into LineCache
  fLineCache.cache.clear();
  for i := 0 to GI_EditorFactory.Count - 1 do
    with GI_EditorFactory.Editor[i] do
      if HasPythonfile and (FileName = '') then
      begin
        Source := CleanEOLs(EncodedText)+#10;
        fLineCache.cache.SetItem('<'+FileTitle+'>',
          VarPythonCreate([0,0,Source, '<'+FileTitle+'>'], stTuple));
      end;
end;

function TPyInternalDebugger.NameSpaceFromExpression(
  const Expr: string): TBaseNameSpaceItem;
//  The internal interpreter knows whether we are debugging and
//  adjusts accordingly (in II.debugger.evalcode)
begin
  Result := InternalInterpreter.NameSpaceFromExpression(Expr);
end;

{ TPyInternalInterpreter }

constructor TPyInternalInterpreter.Create(II: Variant);
begin
  fII := II;
  fDebugger := II.debugger;
end;

function TPyInternalInterpreter.CallTipFromExpression(const Expr: string;
  var DisplayString, DocString: string) : Boolean;
var
  LookupObj, PyDocString: Variant;
  SupressOutput : IInterface;
begin
  Result := False;
  DisplayString := '';
  DocString := '';
  if Expr = '' then Exit;

  SupressOutput := PythonIIForm.OutputSuppressor; // Do not show errors
  try
    //Evaluate the lookup expression and get the hint text
    LookupObj := fII.evalcode(Expr);
    DisplayString := fII.get_arg_text(LookupObj);
    Result := True;

    PyDocString := Import('inspect').getdoc(LookupObj);
    if not VarIsNone(PyDocString) then
      DocString := GetNthLine(PyDocString, 1)
    else
      DocString := '';
  except
  end;
end;

function TPyInternalInterpreter.Compile(Editor: IEditor): Variant;
Var
  co : PPyObject;
  FName, Source : string;
begin
  VarClear(Result);
  PyControl.ErrorPos.Clear;
  PyControl.DoErrorPosChanged;

  MessagesWindow.ClearMessages;

  FName := Editor.FileName;
  if FName = '' then FName := '<'+Editor.FileTitle+'>';
  Source := CleanEOLs(Editor.EncodedText)+#10;

  with GetPythonEngine do begin
    co := Py_CompileString(PChar(Source), PChar(FName), file_input );
    if not Assigned( co ) then begin
      // New Line for output
      PythonIIForm.AppendText(sLineBreak);
      try
      // Print and throw exception for the error
       CheckError;
      except
        on E: EPySyntaxError do begin
          MessagesWindow.ShowPythonSyntaxError(E);
          if PyIDEMainForm.ShowFilePosition(E.EFileName, E.ELineNumber, E.EOffset) and
            Assigned(GI_ActiveEditor)
          then begin
            PyControl.ErrorPos.Editor := GI_ActiveEditor;
            PyControl.ErrorPos.Line := E.ELineNumber;
            PyControl.ErrorPos.Char := E.EOffset;
            PyControl.ErrorPos.IsSyntax := True;
            PyControl.DoErrorPosChanged;
          end;
          MessageDlg(E.Message, mtError, [mbOK], 0);
          SysUtils.Abort;
        end;
        on E: EPyException do begin  //may raise OverflowError or ValueError
          HandlePyException(E);
          MessageDlg(E.Message, mtError, [mbOK], 0);
          SysUtils.Abort;
        end;
      end;
    end else begin
      Result := VarPythonCreate(co);
      // Cleanup ByteCode
      GetPythonEngine.Py_XDECREF(co);
    end;
  end;
end;

function TPyInternalInterpreter.GetGlobals: TBaseNameSpaceItem;
begin
  Result := TNameSpaceItem.Create('globals', VarPythonEval('globals()'));
end;

function TPyInternalInterpreter.ImportModule(Editor: IEditor) : Variant;
{
  Imports Editor text without saving the file.
  Does not add the module name to the locals()
  of the interpreter.
}
Var
  Code : Variant;
  Path, NameOfModule : string;
  PyObject, Module : PPyObject;
  PythonPathAdder : IInterface;
begin
  VarClear(Result);
  //Compile
  Code := Compile(Editor);

  Path := ExtractFileDir(Editor.FileName);
  if Length(Path) > 1 then begin
    // Add the path of the executed file to the Python path
    PythonPathAdder := AddPathToPythonPath(Path, False);
  end;

  if Editor.FileName <> '' then
    NameOfModule := FileNameToModuleName(Editor.FileName)
  else
    NameOfModule := PathRemoveExtension(Editor.FileTitle);
  PyObject := ExtractPythonObjectFrom(Code);

  PyControl.DoStateChange(dsRunningNoDebug);

  try
    with GetPythonEngine do begin
      Py_XINCREF(PyObject);
      try
        try
          Module := PyImport_ExecCodeModule(NameOfModule, PyObject);
          Result := VarPythonCreate(Module);
          Py_XDECREF(Module);
        finally
          Py_XDECREF(PyObject);
        end;
        CheckError;
      except
        on E: EPyException do begin
          HandlePyException(E);
          Result := None;
        end;
      end;
    end;
    if VarIsNone(Result) then begin
      MessageDlg('Error in importing module', mtError, [mbOK], 0);
      SysUtils.Abort;
    end;
  finally
    PyControl.DoStateChange(dsInactive);
  end;
end;

function TPyInternalInterpreter.NameSpaceFromExpression(
  const Expr: string): TBaseNameSpaceItem;
var
  InspectModule, LookupObj, ItemsDict: Variant;
  SupressOutput : IInterface;
begin
  SupressOutput := PythonIIForm.OutputSuppressor; // Do not show errors
  InspectModule := Import('inspect');
  ItemsDict := NewPythonDict;
  try
    if Expr <> '' then begin
        //Evaluate the lookup expression and get the hint text
        LookupObj := fII.evalcode(Expr);
        ItemsDict := BuiltinModule.dict(InspectModule.getmembers(LookupObj));
    end else begin
      ItemsDict.update(fII.evalcode('vars()'));
      ItemsDict.update(BuiltinModule.__dict__);
    end;
  except
    // Ignore exceptions
  end;
  Result := TNameSpaceItem.Create(Expr, ItemsDict);
end;

procedure TPyInternalInterpreter.RestoreCommandLine;
begin
  SysModule.argv := fOldargv;
end;

procedure TPyInternalInterpreter.SetCommandLine(const ScriptName : string);
var
  SysMod : Variant;
  S, Param : string;
  P : PChar;
//  List : TStringList;
//  I: integer;
begin
  SysMod := SysModule;
  fOldargv := SysMod.argv;
  SysMod.argv := NewPythonList;
  // Workaround due to PREFER_UNICODE flag to make sure
  // no conversion to Unicode and back will take place
  SysMod.argv.append(VarPythonCreate(ScriptName));
  S := iff(CommandsDataModule.PyIDEOptions.UseCommandLine,
     CommandsDataModule.PyIDEOptions.CommandLine, '');
  if S <> '' then begin
    S := Parameters.ReplaceInText(S);
    P := GetParamStr(PChar(S), Param);
    while Param <> '' do begin
      SysMod.argv.append(VarPythonCreate(Param));
      P := GetParamStr(P, Param);
    end;
    PythonIIForm.AppendText(Format(SCommandLineMsg, [S]));
  end;
//  List := TStringList.Create;
//  try
//    ExtractStrings([' '], [' '], PChar(S), List);
//    for I := 0 to List.Count - 1 do
//      SysMod.argv.append(AnsiDequotedStr(List[i], '"'));
//  finally
//    List.Free;
//  end;
end;

procedure TPyInternalInterpreter.StringsToSysPath(Strings: TStrings);
var
  i: Integer;
  PythonPath: Variant;
begin
  PythonPath := NewPythonList;
  for i := 0 to Strings.Count - 1 do
    PythonPath.append(Strings[i]);
  SysModule.path := PythonPath;
end;

procedure TimeCallBack(TimerID, Msg: Uint; dwUser, dw1, dw2: DWORD); stdcall;
begin
  if PLongWord(dwUser)^ = 0 then Exit;
  if (MessageBox(0, 'The Python Script has timed out.  Do you want to interrupt it?',
  'ScriptTimeOut', MB_ICONWARNING or MB_YESNO) = idYes) then begin
    GetPythonEngine.PyErr_SetInterrupt;  //Generate Keyboard interrupt signal
    TimeKillEvent(PLongWord(dwUser)^);
    PLongWord(dwUser)^ := 0;
  end;
end;

procedure TPyInternalInterpreter.RunNoDebug(Editor: IEditor);
Var
  Code : Variant;
  mmResult,Resolution : LongWord;
  tc : TTimeCaps;
  Path, OldPath : string;
  PythonPathAdder : IInterface;
begin
  // Repeat here to make sure it is set right
  MaskFPUExceptions(CommandsDataModule.PyIDEOptions.MaskFPUExceptions);

  //Compile
  Code := Compile(Editor);

  PyControl.DoStateChange(dsRunningNoDebug);

  // New Line for output
  PythonIIForm.AppendText(sLineBreak);

  mmResult := 0;
  Resolution := 100;

  Path := ExtractFileDir(Editor.FileName);
  OldPath := GetCurrentDir;
  if Length(Path) > 1 then begin
    // Add the path of the executed file to the Python path - Will be automatically removed
    PythonPathAdder := AddPathToPythonPath(Path);

    // Change the current path
    try
      ChDir(Path)
    except
      MessageDlg('Could not set the current directory to the script path', mtWarning, [mbOK], 0);
    end;
  end;

  // Set the command line parameters
  SetCommandLine(Editor.GetFileNameOrTitle);

  ShowDockForm(PythonIIForm);

  try
    // Set Multimedia Timer
    if (CommandsDataModule.PyIDEOptions.TimeOut > 0) and
      (timeGetDevCaps(@tc, SizeOf(tc))=TIMERR_NOERROR) then
    begin
      Resolution := Min(Resolution,tc.wPeriodMax);
      TimeBeginPeriod(Resolution);
      mmResult := TimeSetEvent(CommandsDataModule.PyIDEOptions.TimeOut, resolution,
        @TimeCallBack, DWORD(@mmResult), TIME_PERIODIC or 256);
    end;
    try
      Debugger.run_nodebug(Code);
    except
      // CheckError already called by VarPyth
      on E: EPyException do begin
        HandlePyException(E);
        MessageDlg(E.Message, mtError, [mbOK], 0);
        SysUtils.Abort;
      end;
    end;
  finally
    if CommandsDataModule.PyIDEOptions.TimeOut > 0 then begin
      if (mmResult <> 0) then TimeKillEvent(mmResult);
      TimeEndPeriod(Resolution);
    end;
    PythonIIForm.AppendText(sLineBreak+PythonIIForm.PS1);

    // Restore the command line parameters
    RestoreCommandLine;

    // Change the back current path
    ChDir(OldPath);

    PyControl.DoStateChange(dsInactive);
  end;
end;

function TPyInternalInterpreter.RunSource(const Source, FileName : string): boolean;
begin
  // Workaround due to PREFER_UNICODE flag to make sure
  // no conversion to Unicode and back will take place
  Result := fII.runsource(VarPythonCreate(Source), FileName);
end;

function TPyInternalInterpreter.SyntaxCheck(Editor: IEditor; Quiet : Boolean = False): Boolean;
Var
  FName, Source : string;
  tmp: PPyObject;
  PyErrType, PyErrValue, PyErrTraceback, PyErrValueTuple : PPyObject;
  SupressOutput : IInterface;
begin
  PyControl.ErrorPos.Clear;
  PyControl.DoErrorPosChanged;

  MessagesWindow.ClearMessages;

  FName := Editor.FileName;
  if FName = '' then FName := '<'+Editor.FileTitle+'>';
  Source := CleanEOLs(Editor.EncodedText)+#10;

  with GetPythonEngine do begin
    if Quiet then
      SupressOutput := PythonIIForm.OutputSuppressor; // Do not show errors
    Result := CheckExecSyntax(Source);
    if not Result then begin
      if Quiet then begin
        if Assigned(PyErr_Occurred()) then begin
          if (PyErr_ExceptionMatches(PyExc_SyntaxError^) = 1) then begin
            PyErr_Fetch(@PyErrType, @PyErrValue, @PyErrTraceback);  // Clears the Error
            if Assigned(PyErrValue) then begin
            // Sometimes there's a tuple instead of instance...
              if PyTuple_Check( PyErrValue )  and (PyTuple_Size( PyErrValue) >= 2) then
              begin
                PyControl.ErrorPos.ErrorMsg := PyString_AsString(PyTuple_GetItem( PyErrValue, 0));
                PyErrValueTuple := PyTuple_GetItem( PyErrValue, 1);
                if PyTuple_Check( PyErrValueTuple )  and (PyTuple_Size( PyErrValueTuple) >= 4) then
                begin
                  PyControl.ErrorPos.Line := PyInt_AsLong(PyTuple_GetItem( PyErrValueTuple, 1));
                  PyControl.ErrorPos.Char := PyInt_AsLong(PyTuple_GetItem( PyErrValueTuple, 2));
                end;
              end else
                // Is it an instance of the SyntaxError class ?
              if PyInstance_Check( PyErrValue ) or (PyType_IsSubtype(PyErrValue^.ob_type,
                PPyTypeObject(GetPythonEngine.PyExc_SyntaxError^)) = 1) then
              begin
                // Get the text containing the error, cut of carriage return
                tmp := PyObject_GetAttrString(PyErrValue, 'text');
                if Assigned(tmp) and PyString_Check(tmp) then
                  PyControl.ErrorPos.ErrorMsg := Trim(PyString_AsString(tmp));
                Py_XDECREF(tmp);
                // Get the offset where the error should appear
                tmp := PyObject_GetAttrString(PyErrValue, 'offset' );
                if Assigned(tmp) and PyInt_Check(tmp) then
                  PyControl.ErrorPos.Char := PyInt_AsLong(tmp);
                Py_XDECREF(tmp);
                // Get the line number of the error
                tmp := PyObject_GetAttrString(PyErrValue, 'lineno' );
                if Assigned(tmp) and PyInt_Check(tmp) then
                  PyControl.ErrorPos.Line := PyInt_AsLong(tmp);
                Py_XDECREF(tmp);
                PyErr_Clear;
              end;
              PyControl.ErrorPos.Editor := Editor;
              PyControl.ErrorPos.IsSyntax := True;
              PyControl.DoErrorPosChanged;
            end;
            Py_XDECREF(PyErrType);
            Py_XDECREF(PyErrValue);
            Py_XDECREF(PyErrTraceback);
          end else
            PyErr_Clear;
        end;
      end else begin
        // Display error
        // New Line for output
        PythonIIForm.AppendText(sLineBreak);
        try
          // Print and throw exception for the error
          CheckError;
        except
          on E: EPySyntaxError do begin
            E.EFileName := FName;  // add the filename
            MessagesWindow.ShowPythonSyntaxError(E);
            if PyIDEMainForm.ShowFilePosition(E.EFileName, E.ELineNumber, E.EOffset) and
              Assigned(GI_ActiveEditor)
            then begin
              PyControl.ErrorPos.Editor := GI_ActiveEditor;
            end;
            PyControl.ErrorPos.Line := E.ELineNumber;
            PyControl.ErrorPos.Char := E.EOffset;
            PyControl.ErrorPos.IsSyntax := True;
            PyControl.DoErrorPosChanged;
            if Not Quiet then MessageDlg(E.Message, mtError, [mbOK], 0);
          end;
        end;
      end;
    end;
  end;
end;

function TPyInternalInterpreter.SysPathAdd(const Path: WideString): boolean;
begin
  if SysModule.path.contains(Path) then
    Result := false
  else begin
    SysModule.path.insert(0, Path);
    Result := true;
  end;
end;

function TPyInternalInterpreter.SysPathRemove(const Path: WideString): boolean;
begin
  if SysModule.path.contains(Path) then begin
    Result := True;
    SysModule.path.remove(Path);
  end else
    Result := False;
end;

procedure TPyInternalInterpreter.SysPathToStrings(Strings: TStrings);
var
  i: Integer;
begin
  for i := 0 to Len(SysModule.path) - 1  do
    Strings.Add(SysModule.path.GetItem(i));
end;

end.












See more files for this project here

pyscripter

PyScripter is a free and open-source Python Integrated Development Environment (IDE) created with the ambition to become competitive in functionality with commercial Windows-based IDEs available for other languages. Being built in a compiled language is rather snappier than some of the other Python IDEs and provides an extensive blend of features that make it a productive Python development environment.

Project homepage: http://code.google.com/p/pyscripter/
Programming language(s): Pascal
License: mit

  Components/
  FastMM4Options.inc
  Install.txt
  JvAppIniStorage.pas
  JvAppInst.pas
  JvAppStorage.pas
  JvChangeNotify.pas
  JvCreateProcess.pas
  JvDockControlForm.pas
  JvDockInfo.pas
  JvDockVSNetStyle.pas
  JvProgramVersionCheck.pas
  JvTabBar.pas
  JvThread.pas
  PyScripter Logo.bmp
  PyScripter.bdsproj
  PyScripter.bdsproj.local
  PyScripter.dpr
  PyScripter.ico
  PyScripter.res
  Readme.txt
  StoHtmlHelp.pas
  StringResources.pas
  SynCompletionProposal.pas
  SynEdit.pas
  SynEditKeyCmds.pas
  SynHighlighterPython.pas
  cCodeHint.pas
  cFilePersist.pas
  cFileSearch.pas
  cFileTemplates.pas
  cFindInFiles.pas
  cParameters.pas
  cPyBaseDebugger.pas
  cPyDebugger.pas
  cPyRemoteDebugger.pas
  cPythonSourceScanner.pas
  cRefactoring.pas
  cTools.pas
  dlgAboutPyScripter.dfm
  dlgAboutPyScripter.pas
  dlgAskParam.dfm
  dlgAskParam.pas
  dlgCodeTemplates.dfm
  dlgCodeTemplates.pas
  dlgCommandLine.dfm
  dlgCommandLine.pas
  dlgConfigureTools.dfm
  dlgConfigureTools.pas
  dlgConfirmReplace.dfm
  dlgConfirmReplace.pas
  dlgCustomParams.dfm
  dlgCustomParams.pas
  dlgCustomShortcuts.dfm
  dlgCustomShortcuts.pas
  dlgDirectoryList.dfm
  dlgDirectoryList.pas
  dlgExceptionMail.dfm
  dlgExceptionMail.pas
  dlgFileTemplates.dfm
  dlgFileTemplates.pas
  dlgFindInFiles.dfm
  dlgFindInFiles.pas
  dlgFindResultsOptions.dfm
  dlgFindResultsOptions.pas
  dlgNewFile.dfm
  dlgNewFile.pas
  dlgOptionsEditor.dfm
  dlgOptionsEditor.pas
  dlgPickList.dfm
  dlgPickList.pas
  dlgReplaceInFiles.dfm
  dlgReplaceInFiles.pas
  dlgReplaceText.dfm
  dlgReplaceText.pas
  dlgSearchText.dfm
  dlgSearchText.pas
  dlgSynEditOptions.dfm
  dlgSynEditOptions.pas
  dlgSynPageSetup.dfm
  dlgSynPageSetup.pas
  dlgSynPrintPreview.dfm
  dlgSynPrintPreview.pas
  dlgToDoOptions.dfm
  dlgToDoOptions.pas
  dlgToolProperties.dfm
  dlgToolProperties.pas
  dlgUnitTestWizard.dfm
  dlgUnitTestWizard.pas
  dmCommands.dfm
  dmCommands.pas
  frmBreakPoints.dfm
  frmBreakPoints.pas
  frmCallStack.dfm
  frmCallStack.pas
  frmCodeExplorer.dfm
  frmCodeExplorer.pas
  frmCommandOutput.dfm
  frmCommandOutput.pas
  frmDisassemlyView.dfm
  frmDisassemlyView.pas
  frmDocView.dfm
  frmDocView.pas
  frmEditor.dfm
  frmEditor.pas
  frmFileExplorer.dfm
  frmFileExplorer.pas
  frmFindResults.dfm
  frmFindResults.pas
  frmFunctionList.dfm
  frmFunctionList.pas
  frmIDEDockWin.dfm
  frmIDEDockWin.pas
  frmMessages.dfm
  frmMessages.pas
  frmPyIDEMain.dfm
  frmPyIDEMain.pas
  frmPythonII.dfm
  frmPythonII.pas
  frmRegExpTester.dfm
  frmRegExpTester.pas
  frmToDo.dfm
  frmToDo.pas
  frmUnitTests.dfm
  frmUnitTests.pas
  frmVariables.dfm
  frmVariables.pas
  frmWatches.dfm
  frmWatches.pas
  uCommonFunctions.pas
  uEditAppIntfs.pas
  uHighlighterProcs.pas
  uMMMXP_MainService.pas
  uParams.pas