Jump to content

iqrf

Members
  • Content Count

    43
  • Joined

  • Last visited

Posts posted by iqrf


  1. Thank you, I got to that too
    var a := VarPythonCreate(pColor
    ).value

    the second way

    var valueObj := PyObject_GetAttrString(pColor, 'value');

    if Assigned(valueObj) then begin
       a := PyLong_AsLong(valueObj);
       Py_XDECREF(valueObj);
    end
    else
       PyErr_Print();  


  2. Hi,
    how to use PyArg_ParseTupleAndKeywords to get the value of a function parameter that is of type Python enum. E.g

    class color (enum):
             RED = 5
             GREEN = 8
             BLUE = 9
    
    TestFunction (Color.RED)

    var pColor: PPyObject;

    if (PyArg_ParseTupleAndKeywords(args, kwargs, '|O', @keyPointerArray[0], @pColor) <> 0) then begin

    PyEnum_Check(pColor)
    returns False why?

    How to get value 5 from pColor?

     

    I tried this too
        var a := VarPythonCreate(pColor);
        VarIsEnum(a)
    also returns False
    in 'a' is the string 'color.RED'
     

    Thank you for your help.


  3. OK 😞
    So I define an enum in python and use it in delphi, which works.
    Now I understand why it is like this:

    
    procedure TButtonsRegistration.DefineVars(APyDelphiWrapper: TPyDelphiWrapper);
    begin
      inherited;
      APyDelphiWrapper.DefineVar('bkCustom',      bkCustom);
      APyDelphiWrapper.DefineVar('bkOK',          bkOK);
      APyDelphiWrapper.DefineVar('bkCancel',      bkCancel);
      APyDelphiWrapper.DefineVar('bkHelp',        bkHelp);
      APyDelphiWrapper.DefineVar('bkYes',         bkYes);
      APyDelphiWrapper.DefineVar('bkNo',          bkNo);
      APyDelphiWrapper.DefineVar('bkClose',       bkClose);
      APyDelphiWrapper.DefineVar('bkAbort',       bkAbort);
      APyDelphiWrapper.DefineVar('bkRetry',       bkRetry);
      APyDelphiWrapper.DefineVar('bkIgnore',      bkIgnore);
      APyDelphiWrapper.DefineVar('bkAll',         bkAll);
    end;

    Thanks for your time.


  4. I don't want to define an enum in both Delphi and Python.
    I just want to use an enum defined in Delphi in Python.
    WrapDelphi I use both FPythonWrapper.DefineVar() and FPythonWrapper.Wrap(TDelphi, soOwned) and FPythonWrapper.RegisterDelphiWrapper(TPyClassWrapper<...>).Initialize
    But I have no idea how to use the same functionality for an enumeration type.


  5.  

    14 minutes ago, pyscripter said:

    There is no need to export anything.   Delphi enum values are converted to python strings and sets to lists of strings.

     

    I don't understand how I can write in Python a = TColor.RED without exporting to Python?

    var colorEnum := MainModule.Color;
    var Red := colorEnum.RED;
    result := Red;  E2010 Incompatible types: 'PPyObject' and 'Variant'

    But that's the variant type, I need to return a PPyObject.
    Thanks


  6. How to export from delphi to python

    type
    	TColor = (RED = 5, GREEN = 8, BLUE = 9)

    and in the return value of the delphi function called from Python to return a PPyObject of type TColor.
    Is it any other way than clumsily like this?

    FEngine.PyRun_SimpleString('from enum import Enum' + #13 +
      'Color = Enum(''Color'', {''RED'':5, ''GREEN'':8, ''BLUE'':9})');
    ...
    FEngine.ExecString(UTF8Encode(FScript), UTF8Encode(FCode.Path));
    ...
    //result my function
    mainModule, colorEnum, redColor: PPyObject;
    ....
    
    mainModule := PyImport_AddModule('__main__');
    colorEnum := PyObject_GetAttrString(mainModule, 'Color');
    redColor := PyObject_GetAttrString(colorEnum, 'RED');
    result := redColor;
    ...
    if mymodule.test_func() == Color.RED:
    ...

    Thanks for the ideas.


  7. Hi, not using exit() is not the solution for me. Users use exit() when debugging a script.

    If to ProcessSystemExit; i add Py_DecRef(errtraceback); so the memory leak problem will disappear. The destructor TTerminalLogSettings will already be called. I have no idea, but how is that possible.

    procedure TPythonEngine.CheckError(ACatchStopEx : Boolean = False);
      procedure ProcessSystemExit;
      var
        errtype, errvalue, errtraceback: PPyObject;
        SErrValue: string;
      begin
        PyErr_Fetch(errtype, errvalue, errtraceback);
        Traceback.Refresh(errtraceback);
        SErrValue := PyObjectAsString(errvalue);
        Py_DecRef(errtraceback);      // workaround
        PyErr_Clear;
        raise EPySystemExit.CreateResFmt(@SPyExcSystemError, [SErrValue]);
      end;

     

    • Thanks 1

  8. On 2/28/2024 at 4:27 PM, pyscripter said:

    To be able to create columns you need to also to customize the TColumn wrapper:

     

    
      TColumnWrapper = class(TPyClassWrapper<TColumn>)
        constructor CreateWith(APythonType: TPythonType; args, kwds: PPyObject); override;
      end;
    constructor TColumnWrapper.CreateWith(APythonType: TPythonType; args,
      kwds: PPyObject);
    var
      W: integer;
      P: PPyObject;
    begin
      Create(APythonType);
      GetPythonEngine.PyArg_ParseTuple(args, 'iO:CreateColumn', @W, @P);
      DelphiObject := TColumn.Create;
      DelphiObject.Width := W;
      DelphiObject.Visible := GetPythonEngine.PyObject_IsTrue(P) = 1;
    end;

    then register it in Form.Create

     

    
      PyDelphiWrapper.RegisterDelphiWrapper(TColumnWrapper).Initialize;
      PyDelphiWrapper.RegisterDelphiWrapper(TPyClassWrapper<TDictionary<string, TColumn>>).Initialize;  

    Then the following code works:

     

    
    from spam import myPoint, Column
    
    print(myPoint.ColumnA['Note'].Width)
    print(myPoint.ColumnA['Note'].Visible)
    
    new_column = Column(100, False)
    myPoint.ColumnA.Add('Note1', new_column)
    
    print(myPoint.ColumnA['Note1'].Width)
    print(myPoint.ColumnA['Note1'].Visible)


     

    It is possible to somehow distinguish whether the SetVisible function was called from

    new_column.visible = True

    or

    myPoint.ColumnA['Note1'].Visible = True
    procedure TColumn.SetVisible(const Value: Boolean);
    begin
      FVisible := Value;
      ...
    end;

    Thanks for the advice.


  9. Hi,

    i have this code

    TColumn = class(TPersistent)
    private
      FHeaderName: String;
      FVisible: Boolean;
      FWidth: Integer;
      FPosition: Integer;
      procedure SetHeaderName(const Value: String);
      procedure SetVisible(const Value: Boolean);
      procedure SetWidth(const Value: Integer);
      procedure SetPosition(const Value: Integer);
    public
      property HeaderName: String read FHeaderName write SetHeaderName;
      property Visible: Boolean read FVisible write SetVisible;
      property Width: Integer read FWidth write SetWidth;
      property Position: Integer read FPosition write SetPosition;
    end;
    
    TColumnWrapper = class(TPyClassWrapper<TColumn>)
      constructor CreateWith(APythonType: TPythonType; args, kwds: PPyObject); override;
    end;
    
    TTerminalLogSettings = class(TPersistent)
    private
      fColumn: TDictionary<String,TColumn>;
      function GetAutoScroll: Boolean;
      function GetSeparatorH: Boolean;
      function GetSeparatorV: Boolean;
      function GetViewData: Boolean;
      procedure SetAutoScroll(const Value: Boolean);
      procedure SetSeparatorH(const Value: Boolean);
      procedure SetSeparatorV(const Value: Boolean);
      procedure SetViewData(const Value: Boolean);
    public
      constructor Create;
      destructor Destroy; override;
      property AutoScroll: Boolean read GetAutoScroll write SetAutoScroll;
      property SeparatorH: Boolean read GetSeparatorH write SetSeparatorH;
      property SeparatorV: Boolean read GetSeparatorV write SetSeparatorV;
      property ViewData: Boolean read GetViewData write SetViewData;
      property Column: TDictionary<String, TColumn> read fColumn write fColumn;
    end;
    ...
    FPythonModule_TerminalLog := TPythonModule.Create(nil);
    FPythonModule_TerminalLog.Name := 'FPythonModule_TerminalLog';
    FPythonModule_TerminalLog.Engine := FEngine;
    FPythonModule_TerminalLog.ModuleName := 'terminal_log';
    FPythonModule_TerminalLog.OnInitialization := OnInitialization_TerminalLog;
    FPythonModule_TerminalLog.OnAfterInitialization := OnAfterInitialization_TerminalLog;
    ...
    FPythonVersion.AssignTo(FEngine);
    FEngine.LoadDll;
    ...  
    terminalLogSettings := TTerminalLogSettings.Create;
    pTerminalLogSettings := FPythonWrapper.Wrap(terminalLogSettings, soOwned);
    FPythonModule_TerminalLog.SetVar( 'settings', pTerminalLogSettings);
    FEngine.Py_DecRef(pTerminalLogSettings);
     
    FPythonWrapper.RegisterDelphiWrapper(TColumnWrapper).Initialize;
    FPythonWrapper.RegisterDelphiWrapper(TPyClassWrapper<TDictionary<string, TColumn>>).Initialize;
    ...

    if used in this way in the script, then after the script finishes I correctly call the TTerminalLogSettings destructor and free the memory

    ...
    iqrfide.terminal_log.settings.autoScroll = True
    ...
    
    settings_object = iqrfide.terminal_log.settings
    setattr(settings_object, 'autoScroll', True)
    ...

    If the script is interrupted using exit(), the destructor is not called and the memory is not freed. Any explanation?

    ...
    iqrfide.terminal_log.settings.autoScroll = True
    ...
    
    settings_object = iqrfide.terminal_log.settings
    setattr(settings_object, 'autoScroll', True)
    
    exit()
    ...

    Putting settings_object = None before exit solves the problem.


  10. HI,

    I modified the example of Demo 32. Added TColumn and fColumn: TDictionary<String,TColumn>;  property ColumnA: TDictionary<String,TColumn> read fColumn write fColumn;

      {$METHODINFO ON}
      TColumn = class(TPersistent)
      private
        FVisible: Boolean;
        FWidth: Integer;
      public
        property Visible: Boolean read FVisible write FVisible;
        property Width: Integer read FWidth write FWidth;
      end;
    
      TPoint = class(TPersistent)
        private
          fx, fy : Integer;
          fName : String;
          fColumn: TDictionary<String,TColumn>;
        public
          procedure OffsetBy( dx, dy : integer );
        published
          property x : integer read fx write fx;
          property y : integer read fy write fy;
          property Name : string read fName write fName;
          property ColumnA: TDictionary<String,TColumn> read fColumn write fColumn;
      end;
      {$METHODINFO OFF}
    procedure TForm1.Button1Click(Sender: TObject);
    var
      DelphiPoint : TPoint;
      p : PPyObject;
    begin
      // Here's how you can create/read Python vars from Delphi with
      // Delphi/Python objects.
      DelphiPoint := TPoint.Create;
      DelphiPoint.x := 10;
      DelphiPoint.y := 20;
    
      DelphiPoint.ColumnA := TDictionary<String,TColumn>.Create;
      var value := TColumn.Create;
      value.Visible := True;
      value.Width := 455;
      DelphiPoint.ColumnA.Add('Note', value);
        
      // DelphiPoint will be owned and eventually destroyed by Python
      p := PyDelphiWrapper.Wrap(DelphiPoint, soOwned);
    
      PythonModule1.SetVar( 'myPoint', p );
    
      // Note, that you must not free the delphi point yourself.
      // Instead use the GetPythonEngine.Py_XDECREF(obj) method,
      // because the object may be used by another Python object.
      PythonEngine1.Py_DecRef(p);
    
      // Excecute the script
      PythonEngine1.ExecStrings( memo1.Lines );
    
    end;

    if i coll

    print(spam.myPoint.ColumnA.Count)
    print(spam.myPoint.ColumnA.ContainsKey("Note"))

    OK 
    1
    True

    print(spam.myPoint.ColumnA["Note"])  #TypeError: 'Object' object is not subscriptable
    
    for key in spam.myPoint.ColumnA:	#TypeError: 'Object' object is not iterable
        print(key)

    How to solve this please.
    How to make this work

    a = spam.myPoint.ColumnA.Column()
    a.Visible = True
    a.Width = 111
    spam.myPoint.ColumnA.Add("Note1", a)

    Thanks


  11. Hi,
    I confirm that both CTRL+F and CTRL+C do not work under Delphi Alexandria + Win11 64b.
    Not working is:

    HtmlHelp(0, Application.HelpFile, HH_DISPLAY_TOC, 0);
    HtmlHelp(0, Application.HelpFile, HH_DISPLAY_INDEX, DWORD(PWideChar('')));
    HtmlHelp(0, Application.HelpFile, HH_DISPLAY_SEARCH, DWORD(@Query));
    Application.HelpContext()

    Calling HtmlHelpW instead of HtmlHelp works.
    A broken Application.HelpContext solved them like this:

    function TFormMain.ApplicationEvents1Help(Command: Word; Data: NativeInt;
       var CallHelp: Boolean): Boolean;
    begin
    	HtmlHelpW(0, PWideChar(Application.HelpFile), HH_HELP_CONTEXT, Data);
    	CallHelp := False;
    end;

    I used HH_HELP_CONTEXT because even when calling the Application.HelpContext the Command parameter contained 1 instead of 15.

    Of course, this solution does not work if HelpKeyword is used for components.

    A universal solution could be made by possibly recoding these constants.

    const
      { Commands to pass to WinHelp() }
      {$EXTERNALSYM HELP_CONTEXT}
      HELP_CONTEXT = 1;       { Display topic in ulTopic  }
      {$EXTERNALSYM HELP_QUIT}
      HELP_QUIT = 2;          { Terminate help  }
      {$EXTERNALSYM HELP_INDEX}
      HELP_INDEX = 3;         { Display index  }
      {$EXTERNALSYM HELP_CONTENTS}
      HELP_CONTENTS = 3;
      {$EXTERNALSYM HELP_HELPONHELP}
      HELP_HELPONHELP = 4;    { Display help on using help  }
      {$EXTERNALSYM HELP_SETINDEX}
      HELP_SETINDEX = 5;      { Set current Index for multi index help  }
      {$EXTERNALSYM HELP_SETCONTENTS}
      HELP_SETCONTENTS = 5;
      {$EXTERNALSYM HELP_CONTEXTPOPUP}
      HELP_CONTEXTPOPUP = 8;
      {$EXTERNALSYM HELP_FORCEFILE}
      HELP_FORCEFILE = 9;
      {$EXTERNALSYM HELP_KEY}
      HELP_KEY = 257;         { Display topic for keyword in offabData  }
      {$EXTERNALSYM HELP_COMMAND}
      HELP_COMMAND = 258;
      {$EXTERNALSYM HELP_PARTIALKEY}
      HELP_PARTIALKEY = 261;
      {$EXTERNALSYM HELP_MULTIKEY}
      HELP_MULTIKEY = 513;
      {$EXTERNALSYM HELP_SETWINPOS}
      HELP_SETWINPOS = 515;
      {$EXTERNALSYM HELP_CONTEXTMENU}
      HELP_CONTEXTMENU = 10;
      {$EXTERNALSYM HELP_FINDER}
      HELP_FINDER = 11;
      {$EXTERNALSYM HELP_WM_HELP}
      HELP_WM_HELP = 12;
      {$EXTERNALSYM HELP_SETPOPUP_POS}
      HELP_SETPOPUP_POS = 13;
    const
    
    {$EXTERNALSYM   HH_DISPLAY_TOPIC      }
      HH_DISPLAY_TOPIC      = 0;
    {$EXTERNALSYM   HH_HELP_FINDER         }
      HH_HELP_FINDER         =    0;
    {$EXTERNALSYM   HH_DISPLAY_TOC         }
      HH_DISPLAY_TOC         =    1;
    {$EXTERNALSYM   HH_DISPLAY_INDEX       }
      HH_DISPLAY_INDEX       =    2;
    {$EXTERNALSYM   HH_DISPLAY_SEARCH      }
      HH_DISPLAY_SEARCH      =    3;
    {$EXTERNALSYM   HH_SET_WIN_TYPE        }
      HH_SET_WIN_TYPE        =    4;
    {$EXTERNALSYM   HH_GET_WIN_TYPE        }
      HH_GET_WIN_TYPE        =    5;
    {$EXTERNALSYM   HH_GET_WIN_HANDLE      }
      HH_GET_WIN_HANDLE      =    6;
    {$EXTERNALSYM   HH_ENUM_INFO_TYPE      }
      HH_ENUM_INFO_TYPE      =    7;
    {$EXTERNALSYM   HH_SET_INFO_TYPE       }
      HH_SET_INFO_TYPE       =    8;
    {$EXTERNALSYM   HH_SYNC                }
      HH_SYNC                =    9;
    {$EXTERNALSYM   HH_KEYWORD_LOOKUP      }
      HH_KEYWORD_LOOKUP      =   $d;
    {$EXTERNALSYM   HH_DISPLAY_TEXT_POPUP  }
      HH_DISPLAY_TEXT_POPUP  =   $e;
    {$EXTERNALSYM   HH_HELP_CONTEXT        }
      HH_HELP_CONTEXT        =   $f;
    {$EXTERNALSYM   HH_TP_HELP_CONTEXTMENU }
      HH_TP_HELP_CONTEXTMENU =  $10;
    {$EXTERNALSYM   HH_TP_HELP_WM_HELP     }
      HH_TP_HELP_WM_HELP     =  $11;
    {$EXTERNALSYM   HH_CLOSE_ALL           }
      HH_CLOSE_ALL           =  $12;
    {$EXTERNALSYM   HH_ALINK_LOOKUP        }
      HH_ALINK_LOOKUP        =  $13;
    {$EXTERNALSYM   HH_GET_LAST_ERROR      }
      HH_GET_LAST_ERROR      =  $14;
    {$EXTERNALSYM   HH_ENUM_CATEGORY       }
      HH_ENUM_CATEGORY       =  $15;
    {$EXTERNALSYM   HH_ENUM_CATEGORY_IT    }
      HH_ENUM_CATEGORY_IT    =  $16;
    {$EXTERNALSYM   HH_RESET_IT_FILTER     }
      HH_RESET_IT_FILTER     =  $17;
    {$EXTERNALSYM   HH_SET_INCLUSIVE_FILTER }
      HH_SET_INCLUSIVE_FILTER = $18;
    {$EXTERNALSYM   HH_SET_EXCLUSIVE_FILTER }
      HH_SET_EXCLUSIVE_FILTER = $19;
    {$EXTERNALSYM   HH_INITIALIZE          }
      HH_INITIALIZE          =  $1c;
    {$EXTERNALSYM   HH_UNINITIALIZE        }
      HH_UNINITIALIZE        =  $1d;
    {$EXTERNALSYM   HH_SET_QUERYSERVICE    }
      HH_SET_QUERYSERVICE    =  $1e;
    {$EXTERNALSYM   HH_PRETRANSLATEMESSAGE }
      HH_PRETRANSLATEMESSAGE =  $fd;
    {$EXTERNALSYM   HH_GLOBALPROPERTY      }
      HH_GLOBALPROPERTY      =  $fc;

     

    • Thanks 1

  12. I will answer myself. To register a method, you need to use AddMethodWithKeywords instead of AddMethod. It was enough to look at WrapDelphi.pas. I think it would be beneficial if Demo32 included an example of such a method.

    14 hours ago, iqrf said:

    AddMethod('send_and_receive', @TPyDevice.DoSendAndReceive, 'device_manager.device.send_and_recive(data: bytes)');

    and still to fix

    Quote
    
    KeyPointerArray := [PAnsiChar(KeyArray[0]), PAnsiChar(KeyArray[1]), PAnsiChar(KeyArray[2])];

    KeyPointerArray := [PAnsiChar(KeyArray[0]), PAnsiChar(KeyArray[1]), PAnsiChar(KeyArray[2]), nil];


  13. Hi,
    I have a TPythonType object TPyDevice = class(TPyObject) and Interface methods in it
    function DoSendAndReceive(args: PPyObject): PPyObject; cdecl;

    function TPyDevice.DoSendAndReceive(args: PPyObject): PPyObject; cdecl;
    var
      data: TBytes;
      pyBytes: PPyObject;
      timeout: Single;
      logging: Boolean;
    
    begin
      with GetPythonEngine do begin
        Adjust(@Self);
    
        logging := False;
    
        if PyArg_ParseTuple( args, 'S|fp:device.send_and_receive', @pyBytes, @timeout, @logging) <> 0 then begin
          data := PyBytesAsBytes(pyBytes);
          var response := False;
           ....
           ....
    
          if response then begin
            pyBytes := PyBytes_FromStringAndSize(PAnsiChar(data), Length(data));
            Result := pyBytes;
          end
          else Result := ReturnNone;
    
        end
        else
          Result := nil;
      end;
    end;

    I use it like this 

    response = device_manager.device.send_and_receive(dat, 0, True) or 
    response = device_manager.device.send_and_receive(dat)
     

    I would like to call the function like this

    device_manager.device.send_and_receive(data=dat, timeout 0, logging=True) or
    device_manager.device.send_and_receive(data=dat,  logging=True)

    I edited it like this

    function TPyDevice.DoSendAndReceive(args, kwds: PPyObject): PPyObject; cdecl;
    var
      data: TBytes;
      pyBytes: PPyObject;
      timeout: Single;
      logging: Boolean;
      keyArray: array of AnsiString;
      keyPointerArray: array of PAnsiChar;
    
    
    begin
      with GetPythonEngine do begin
        Adjust(@Self);
    
        logging := False;
        KeyArray := ['data', 'timeout', 'logging'];
        KeyPointerArray := [PAnsiChar(KeyArray[0]), PAnsiChar(KeyArray[1]), PAnsiChar(KeyArray[2])];
        if PyArg_ParseTupleAndKeywords(args, kwds, 'S|fp:device.send_and_receive', @keyPointerArray[0], @pyBytes, @timeout, @logging) <> 0 then begin
          data := PyBytesAsBytes(pyBytes);
          var response := False;
          ....
          ....
    
          if response then begin
            pyBytes := PyBytes_FromStringAndSize(PAnsiChar(data), Length(data));
            Result := pyBytes;
          end
          else Result := ReturnNone;
        end
        else
          Result := nil;
      end;
    end;
    
    TPyDevice = class(TPyObject)
      ...
      constructor CreateWith( PythonType : TPythonType; args : PPyObject ); override;
        // Basic services
      function  Repr : PPyObject; override;
      function  Str : PPyObject; override;
      function  GetAttr(Key: PAnsiChar): PPyObject; override;
      function  SetAttr(Key: PAnsiChar; Value: PPyObject): Integer; override;
      
        // Methods of TPyDevice
      ...
        // Class methods
      class procedure RegisterMethods(PythonType: TPythonType); override;
      class procedure RegisterMembers(PythonType: TPythonType); override;
      class procedure RegisterGetSets(PythonType: TPythonType); override;
    
         // Interface methods
      function DoSendAndReceive(args, kwds: PPyObject): PPyObject; cdecl;
       ...
    end;
    
    class procedure TPyDevice.RegisterMethods(PythonType: TPythonType);
    begin
      inherited;
      with PythonType do begin
          AddMethod('send_and_receive', @TPyDevice.DoSendAndReceive, 'device_manager.device.send_and_recive(data: bytes)');
        ....
        end;
    end;
    
    
      FPythonType_Device := TPythonType.Create(nil);
      FPythonType_Device.Name := 'FPythonType_Device';
      FPythonType_Device.GenerateCreateFunction := False;
      FPythonType_Device.Engine := FEngine;
      FPythonType_Device.OnInitialization := OnInitialization_PythonType_Device;
      FPythonType_Device.TypeName := 'Device';
      FPythonType_Device.Prefix := '';
      FPythonType_Device.Services.Basic := [bsGetAttr, bsSetAttr, bsRepr, bsStr];
      FPythonType_Device.Module := FPythonModule_DeviceManager;
      
      pDevice := FPythonType_Device.CreateInstance;
      FEngine.CheckError;
    
      device := TPyDevice(PythonToDelphi(pDevice));
      FPythonModule_DeviceManager.SetVar('device', pDevice);
      FEngine.Py_DecRef(pDevice);

    But I get TypeError: send_and_receive() takes no keyword arguments. Execution of the send_and_receive function will not start at all. I looked at the Demo32 example, but there it is used with CreateWith which has parameters args, kwds: PPyObject already PythoEngine.pas

    Thank you in advance for your help.


  14. Couldn't this be done at the Python level using a decorator above the module?

    import types
    
    class MyModule(types.ModuleType):
        def __setattr__(self, name, value):
            print(f"Setting attribute {name} to value {value}")
            super().__setattr__(name, value)
    
    mymodule = MyModule('mymodule')
    mymodule.my_attribute = 42
    print(mymodule.my_attribute)

     


  15. Hi,
    Let's take Demo08 as an example where a myPoint object is created in Delphi. I need to detect on the Delphi side if the assignment spam.myPoint = None is made in Python. Cyclic polling using PythonModule1.GetVar('myPoint') is inappropriate for me. I would need something like OnChange in TPythonDelphiVar. I've studied PythonEngine.pas and can't think of anything.
    Thank you for your opinion.


  16. 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?

     

×