Jump to content

iqrf

Members
  • Content Count

    44
  • Joined

  • Last visited

Posts posted by iqrf


  1. I finally found a way to make a package

    import importlib
    import types
    import sys
    
    # Package name
    package_name = 'my_package'
    
    # List of modules to be part of the package
    module_names = ['module1', 'module2']
    
    # Create an empty package
    package = types.ModuleType(package_name)
    package.__path__ = []
    
    # Import and add individual modules to the package
    for module_name in module_names:
        module = importlib.import_module(module_name)
        setattr(package, module_name, module)
    
    # Set __all__ to the list of module names
    package.__all__ = module_names
    
    # Add the package to the list of imported modules
    sys.modules[package_name] = package

    Then it is possible

    import my_package
    
    from my_package import module1
    from my_package import module2 as T
    
    print(dir(module1))
    print(dir(my_package.module1))
    print(dir(T))

    Only this import my_package.module1 as T is not possible. 

    ModuleNotFoundError: No module named 'my_package.sub_module1'

    Does anyone know why?

     


  2. Hi,
    I finally solved it at the Python level.

    I have two TPython modules, one named main_module and the other sub_module.
    Just add to the InitScript
    import main_module
    import sub_module
    main_module.sub_module = sub_module

    And you can use in Python main_module.sub_module.function1()
    but of course, also sub_module.function1().

    In this way, a pseudo-hierarchical structure of modules can be created.
    Because this doesn't import main_module.sub_module as a test. main_module is not a package!


  3. Hello,
    I have such a dictionary

    ...  AddMethod( 'TestArray', @TPyPoint.DoTestArray, 'Point.TestArray()' );
    
    function TPyPoint.DoTestArray(args : PPyObject): PPyObject; cdecl;
    type
      TDevice = record
        ValueA: Integer;
        ValueB: String;
      end;
    
      TDevices = array of TDevice;
    
    var
      DeviceArray: TDevices;
      I: Integer;
      DeviceList: PPyObject;
      DeviceDict: PPyObject;
    
    function TDeviceToPyObject(const Device: TDevice): PPyObject;
    var
      DeviceDict: PPyObject;
    begin
      with GetPythonEngine do begin
        DeviceDict := PyDict_New;
    
        PyDict_SetItemString(DeviceDict, 'ValueA', PyLong_FromLong(Device.ValueA));
        PyDict_SetItemString(DeviceDict, 'ValueB', PyUnicodeFromString(PAnsiChar(AnsiString(Device.ValueB))));
    
        Result := DeviceDict;
      end;
    end;
    
    begin
      SetLength(DeviceArray, 2);
      DeviceArray[0].ValueA := 1;
      DeviceArray[0].ValueB := 'Device 1';
      DeviceArray[1].ValueA := 2;
      DeviceArray[1].ValueB := 'Device 2';
    
      with GetPythonEngine do begin
        Adjust(@Self);
        
        DeviceList := PyList_New(0);
    
        for I := 0 to High(DeviceArray) do begin
          DeviceDict := TDeviceToPyObject(DeviceArray[I]);
          PyList_Append(DeviceList, DeviceDict);
          Py_DecRef(DeviceDict);
        end;
    
        Result := DeviceList;
      end;
    end;

    Is there any possibility to make the data read-only in Python?

    I can only do this if I pass them as Tuples using ArrayToPyTuple(MyArray), but then it's just a one-dimensional array.

    And one more question, how to pass TDevice data to Python so that it can be written in Python

    instead of this one
    
    a = spam.myPoint.TestArray()
    print(a[0]['ValueA'])
    print(a[0]['ValueB'])
    
    this one
    
    a = spam.myPoint.TestArray()
    print(a[0].ValueA)
    print(a[0].ValueB)
    

    Thanks.


  4. Hello,
    is it possible to somehow create a submodule. Something like this

    MainModule := TPythonModule.Create(nil);
    MainModule.ModuleName := 'spam';
    
    SubModule := TPythonModule.Create(nil);
    SubModule.ModuleName := 'spam.test';

    and call in Python

    import spam
    import spam.test
    
    spam.function1()
    spam.test.function2()

    it reports an error

    image.png.06eed4676710138e90f788dcbf02231c.png

    if i do it like this it's fine

    import spam
    import spam.test as T
    
    spam.function1()
    T.function2()

    Can it be solved somehow?
    Thanks for the ideas.


  5. Thanks, it works.

     try
        MaskFPUExceptions(True);
        PythonEngine1.ExecStrings(Memo1.Lines);
    
      except
        errorMessage := MainModule.mystderr.getvalue();
        ErrorLog(errorMessage);
      end;

    I would just like to continuously catch errors if someone writes in the Python code for example print("Error example 1", file=sys.stderr)
    or
    sys.stderr.write("Error Example 2")
    The current solution only prints everything at once, with the same timestamp.

    Thanks.


  6. I will answer myself, this is functional

    import builtins
    
    def myinput(prompt):
        builtins._input_prompt = prompt
        return builtins._input(prompt)
    
    builtins._input = builtins.input
    builtins.input = myinput

    Thanks again, I'm learning Python and I'm still amazed at what it can do.

    • Thanks 1

  7. Thanks a lot, the text is passed, but the input value is always NoneType.

    import builtins
    
    def myinput(prompt):
        builtins._input_prompt = prompt
        builtins._input(prompt)
    
    builtins._input = builtins.input
    builtins.input = myinput
    
    nadr = int(input('Enter device address (0 - 239, 255): '))
    print(f'Node {nadr} is not bonded')
    procedure TForm1.PythonInputOutput1ReceiveUniData(Sender: TObject;
      var Data: string);
    var
      s: string;
    begin
      InputQuery( 'Query from Python', MainModule.builtins._input_prompt, s);
      Data := AnsiString(s);
    end;
    Enter device address (0 - 239, 255): 
    Traceback (most recent call last):
    
      File "<string>", line 11, in <module>
    
    TypeError
    : 
    int() argument must be a string, a bytes-like object or a real number, not 'NoneType'

     

    image.png


  8. Unfortunately SystemError: <DebugOutput.write function
    happens even if I catch the KeyboardInterrupt in a python script or in Delphi. I am attaching a test application.
    If I insert a sleep mode (0,1) before printing, the error does not appear.
    If I put sleep(0.08) sometimes it throws SystemError: <function DebugOutput.write sometimes
    SystemError: <builtin sleep function> returned a result with an exception set
    The value of sleep when the error does not occur will depend on the speed of the computer.
    Thanks

    Demo01.zip


  9. Yes, I end with

        GetPythonEngine.PyEval_AcquireThread(PyThread.ThreadState);
        GetPythonEngine.PyErr_SetString(GetPythonEngine.PyExc_KeyboardInterrupt^, 'Terminated');
        GetPythonEngine.PyEval_ReleaseThread(PyThread.ThreadState);

    I am having trouble terminating a thread using PyThread.Terminate. It doesn't work in the Demo33 example either. Why?

    I don't mean the KeyboardInterrupt: Terminated exception, that's ok.

    My point is that in the case of this script, where there is no wait time, another exception will be thrown

    while True:
        print(datetime.now())
    SystemError: <function DebugOutput.write at 0x02C56758> returned a result with an exception set
      
    ---------------------------
    Debugger Exception Notification
    ---------------------------
    Project Demo01.exe raised exception class EPySystemError with message 'SystemError: <function DebugOutput.write at 0x02C56758> returned a result with an exception set'.
    ---------------------------
    Break   Continue   Help   Copy   
    ---------------------------

    It looks like the python engine is trying to write to DebugOutput at the moment when the thread is already canceled. If there is a wait in the script, the error will not occur.

    Thanks a lot for your help.


  10. I modified as per the SimpleDemo but I still get the same error when exiting the thread 

    Quote
    procedure TForm1.PythonInputOutput1SendUniData(Sender: TObject;
      const Data: string);
    var
      ScheduleWrite: Boolean;
    begin
      if Data = '' then Exit;
    
      fCriticalSection.Enter;
      try
        ScheduleWrite := FOutputStream.Size = 0;
        FOutputStream.Write(Data[1], Length (Data) * 2);
    
        if ScheduleWrite then
    
        TThread.ForceQueue(nil, procedure
          var
            WS: string;
          begin
            fCriticalSection.Enter;
            try
              if fOutputStream.Size > 0 then begin
                SetLength(WS, fOutputStream.Size div 2);
                fOutputStream.Position := 0;
                fOutputStream.Read(WS[1], Length(WS) * 2);
                fOutputStream.Size := 0;
                if (Memo2.Lines.Count > 0) and (Memo2.Lines[Memo2.Lines.Count -1] <> '') then
                Memo2.Lines.BeginUpdate;
                try
                  Memo2.Lines.Add('');
                  Memo2.Text := Memo2.Text + WS;
                //  Memo2.Lines.GoToTextEnd;
                finally
                  Memo2.Lines.EndUpdate;
                end;
              end;
            finally
              fCriticalSection.Leave;
            end;
          end);
      finally
        fCriticalSection.Leave;
      end;
    
      Sleep(1);
    end;
    2023-04-24 18:00:05.560557
    
    2023-04-24 18:00:05.575824
    
    KeyboardInterrupt: Terminated
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
    
      File "<string>", line 8, in <module>
    
    SystemError: <function DebugOutput.write at 0x02C56758> returned a result with an exception set

     


  11. Hi,

    According to Demo33, we edited Demo01 to run the script in a thread using the Start and Stop buttons.. PythonGUIInputOutput1 replaced PythonInputOutput1.

    unit Unit1;
    
    interface
    
    uses
      Classes, SysUtils,
      Windows, Messages, Graphics, Controls, Forms, Dialogs,
      StdCtrls, ComCtrls, ExtCtrls, System.Math,System.SyncObjs,
      PythonEngine, Vcl.PythonGUIInputOutput;
    
    type
      TPyThread = class(TPythonThread)
      protected
        procedure ExecuteWithPython; override;
      end;
    
      TForm1 = class(TForm)
        PythonEngine1: TPythonEngine;
        Memo1: TMemo;
        Panel1: TPanel;
        Button1: TButton;
        Splitter1: TSplitter;
        Button2: TButton;
        Button3: TButton;
        OpenDialog1: TOpenDialog;
        SaveDialog1: TSaveDialog;
        PythonGUIInputOutput1: TPythonGUIInputOutput;
        Memo2: TMemo;
        PythonInputOutput1: TPythonInputOutput;
        Button4: TButton;
        Button5: TButton;
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
        procedure Button3Click(Sender: TObject);
        procedure PythonInputOutput1SendUniData(Sender: TObject;
          const Data: string);
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
        procedure Button4Click(Sender: TObject);
        procedure Button5Click(Sender: TObject);
      private
        FCriticalSection : TCriticalSection;
        PyThread: TPyThread;
        procedure ThreadDone(Sender: TObject);
        { Déclarations privées }
      public
        { Déclarations publiques }
      end;
    
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.DFM}
    
    procedure TPyThread.ExecuteWithPython;
    begin
      inherited;
    
      Form1.PythonEngine1.ExecString(UTF8Encode(Form1.Memo1.Text));
    end;
    
    procedure TForm1.ThreadDone(Sender: TObject);
    begin
      TPythonThread.Py_End_Allow_Threads;
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      s: String;
    begin
      MaskFPUExceptions(True);
      PythonEngine1.ExecStrings(Memo1.Lines);
     end;
    
    procedure TForm1.Button2Click(Sender: TObject);
    begin
      with OpenDialog1 do
        begin
          if Execute then
            Memo1.Lines.LoadFromFile( FileName );
        end;
    end;
    
    procedure TForm1.Button3Click(Sender: TObject);
    begin
      with SaveDialog1 do
        begin
          if Execute then
            Memo1.Lines.SaveToFile( FileName );
        end;
    end;
    
    procedure TForm1.Button4Click(Sender: TObject);
    begin
      TPythonThread.Py_Begin_Allow_Threads;
      PyThread := TPyThread.Create(True);
      PyThread.FreeOnTerminate := True;
      PyThread.OnTerminate := ThreadDone;
      PyThread.Start;
    end;
    
    procedure TForm1.Button5Click(Sender: TObject);
    begin
      if Assigned(PyThread) and (not PyThread.Finished) then begin
        GetPythonEngine.PyEval_AcquireThread(PyThread.ThreadState);
        GetPythonEngine.PyErr_SetString(GetPythonEngine.PyExc_KeyboardInterrupt^, 'Terminated');
        GetPythonEngine.PyEval_ReleaseThread(PyThread.ThreadState);
    
        while not PyThread.Finished do application.ProcessMessages;
      end;
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      FCriticalSection := TCriticalSection.Create;
    end;
    
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      if Assigned(PyThread) and (not PyThread.Finished) then Button5Click(Sender);
    
      FCriticalSection.Free;
    end;
    
    procedure TForm1.PythonInputOutput1SendUniData(Sender: TObject;
      const Data: string);
    begin
      fCriticalSection.Enter;
      try
      TThread.ForceQueue(nil, procedure
        begin
          memo2.Lines.Add(data);
        end);
      finally
        fCriticalSection.Leave;
      end;
    
      Sleep(1);
    end;
    
    end.

    If I run this script, everything is OK

    from datetime import datetime
    import time
    
    while True:
       time.sleep(0.1)
       print(datetime.now())
    2023-04-24 12:24:21.726342
    2023-04-24 12:24:21.834341
    2023-04-24 12:24:21.943236
    2023-04-24 12:24:22.051522
    Traceback (most recent call last):
      File "<string>", line 8, in <module>
    KeyboardInterrupt: Terminated

    If this one

    from datetime import datetime
    import time
    
    while True:
       print(datetime.now())

    so i get an error

    2023-04-24 12:44:57.749385
    2023-04-24 12:44:57.764722
    2023-04-24 12:44:57.780558
    2023-04-24 12:44:57.796288
    2023-04-24 12:44:57.811762
    KeyboardInterrupt: Terminated
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "<string>", line 5, in <module>
    SystemError: <function DebugOutput.write at 0x03FF6758> returned a result with an exception set

    Please how to get rid of this error.

    Thank you

     

     


  12. Hi,

    in demo34 there is an example of using a user Exception

    with PythonModule1.Errors.Add do begin
        Name := 'PointError';
        ErrorType := etClass;
      end;
    
    GetModule.RaiseError( 'PointError', 'this is an example of raising an error !' );

    Is there any way to use the definitions of exceptions from an external exception.py file and call them with GetModule.RaiseError?

    class RequestNadrInvalidError(ValueError):
        """
        Invalid NADR value.
    
        This error is raised whenever a request object receives
        address that is out of allowed range ([C]: 0, [N]: 1-239).
        """
        pass
    
    
    class RequestHwpidInvalidError(ValueError):
        """
        Invalid HWPID value.
    
        This error is raised whenever a request object receives
        HWPID that is out of allowed range (0-65535 / 0x00-0xFF).
        """
        pass
    
    
    class RequestParameterInvalidValueError(ValueError):
        """
        Invalid request parameter value.
    
        This error is raised whenever a request object receives
        a parameter with value outside of it's defined domain.
        """
        pass

    Thanks


  13. HI,

    I took inspiration from Demo34 and did what I needed. Everything works. I just want to ask why it is necessary to call Adjust(@Self);
    Do I have to worry about releasing the created PyBytes in TPyPoint.DoReceive using Py_XDECREF(pyBytes) or will Python take care of that?

    Thanks

    function TPyPoint.DoSend(args : PPyObject) : PPyObject;
    var
      data: TBytes;
      pyBytes: PPyObject;
    
      function PyBytesAsBytes(Obj: PPyObject): TBytes;
      var
        size: NativeInt;
        buffer: PAnsiChar;
        arr: TBytes;
      begin
        Result := Default(TBytes);
    
        with GetPythonEngine do
          begin
            if not PyBytes_Check(Obj) then Exit;
            PyBytes_AsStringAndSize(Obj, buffer, size);
            if (size = 0) then Exit;
            SetLength(Result, size);
            CopyMemory(Result, buffer, size);
          end;
      end;
    
    begin
      with GetPythonEngine do
        begin
          Adjust(@Self);
    
          if PyArg_ParseTuple( args, 'S:Point.Send', @pyBytes ) <> 0 then
            begin
              data := PyBytesAsBytes(pyBytes);
             // ....
              Result := ReturnNone;
            end
          else
            Result := nil;
        end;
    end;
    
    function TPyPoint.DoReceive(args : PPyObject) : PPyObject;
    var
      pyBytes: PPyObject;
      data: TBytes;
    
    begin
      with GetPythonEngine do
        begin
          Adjust(@Self);
    
          SetLength(data, 5);
          data[0] := 65;
          data[1] := 66;
          data[2] := 67;
          data[3] := 68;
          data[4] := 69;
    
          PyBytes := PyBytes_FromStringAndSize(PAnsiChar(data), Length(data));
    
          Result := pyBytes;
        end;
    end;
    import spam
    
    d = bytes([0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6])
    spam.myPoint.Send(d)
    print(list(d))
    
    d = spam.myPoint.Receive()
    print(list(d))

     


  14. Hi,

    I'm completely new to P4D and trying to pass a python object to delphi as a function parameter with no success.

     

    Python:

    import delphi

     

    class myclass:
      def __init__(self, x, y):
        self.x = x + y

      def increment(self):
        self.x += 1

      def get_x(self):
        return self.x

     

    if __name__ == '__main__':
        testC = myclass(1, 2)
        testC.increment()
        print(testC.get_x())


        test = delphi.test
        test.send(testC)   # raised exception class EPyTypeError with message 'TypeError: "Send" called with invalid arguments. Error: Could not find a method with compatible arguments'.


    Delphi:

     

    {$METHODINFO ON}
      TTest = class(TPersistent)
        private
        public
          procedure Send(AObject: PyObject);
        published
      end;

    {$METHODINFO OFF}
     

    procedure TTest.Send(AObject: PyObject);
    begin

      //I don't know how to get the x value of myclass here

      //showmessage(x.toString);

    end;
    ...

    ...

    var
      test: TTest;
      p: PPyObject;
    begin

      test := TTest.Create;

      p := PyDelphiWrapper.Wrap(test, soOwned);
      PythonModule.SetVar('test', p );

     

      GetPythonEngine.ExecString(UTF8Encode(sePythonCode.Text));

    end;

     

     

    Many thanks ahead.

    Petr

×