Как я могу взломать код Python, вызываемый из Python.NET в отдельном потоке? - PullRequest
0 голосов
/ 25 сентября 2018

Я хотел бы использовать отладчик PyCharm для остановки на точке останова, установленной в методе Python, который вызывается из C # через Python.NET .Однако точка останова никогда не достигается, хотя код явно выполняется.

Кажется, проблема в том, что поток, в котором вызывается метод Python, был создан в C #.Я знаю, что для того, чтобы поток был виден отладчику, необходимо вызвать settrace.Я думал, что вызов его один раз за поток должен сделать свое дело, но это не так.Потому что после вызова settrace при первом выполнении метода Python мои контрольные точки в этом методе и любые методы, вызываемые им, сбиваются.Но после того, как управление перешло из этого метода обратно в C #, другой вызов метода Python в том же потоке не достигает точки останова.

Вот некоторые аспекты Python;представьте точки останова, установленные для двух операторов print.

import sys
import thread
import threading

sys.path.append(r'C:\Program Files (x86)\JetBrains\PyCharm Community Edition 2018.2.4\helpers\pydev')
import pydevd


import clr
from System.Threading import Thread
from System import Action


sys.path.append(r'C:\Code\PythonNetLib\bin\Debug')
clr.AddReference('MyLib')
from MyLib import MyThread, MyThreadBase


def init_debugging():
    pydevd.settrace(suspend=False)
    # breakpoint here is hit
    print 'debugging initialized in thread {0}, managed thread ID {1}'.format(thread.get_ident(), Thread.CurrentThread.ManagedThreadId)  


def hello():
    # pydevd.settrace(suspend=False)
    # breakpoint here is not hit unless pydevd.settrace is called again
    print 'Hello from thread {0}, managed thread ID {1}'.format(thread.get_ident(), Thread.CurrentThread.ManagedThreadId))  

t = MyThread()
t.ExecuteAsync(Action(init_debugging))
t.ExecuteAsync(Action(hello))
t.ExecuteAsync(Action(t.Stop))
t.Join()

Для иллюстрации приведена упрощенная версия библиотеки C #, упомянутой выше, которая содержит класс MyThread:

using System;
using System.Collections.Concurrent;
using System.Threading;

namespace MyLib
{
   public class MyThread: MyThreadBase
   {
       private Thread _thread;

       public MyThread()
       {
         _thread = new Thread(Execute);
         _thread.Start();
       }

       public void Join()
       {
          _thread.Join();
      }
    }

   public class MyThreadBase
   {
      private ConcurrentQueue<Action> _queue;
      private bool _stopped;

      public MyThreadBase()
      {
         _queue = new ConcurrentQueue<Action>();
      }

      public void Execute()
      {
         while (!_stopped)
         {
            if (_queue.TryDequeue(out Action action))
            {
               action();
            }
            else
            {
               Thread.Sleep(TimeSpan.FromSeconds(1));
            }
         }
      }

      public void ExecuteAsync(Action action)
      {
         _queue.Enqueue(action);
      }

      public void Stop()
      {
         _stopped = true;
      }
   }
}

Обходной путь: Я заметил, что мои точки останова работают как положено, когда я не создаю поток в C #, а вместо этого использую поток Python, например:

t = MyThreadBase()
tPy = threading.Thread(target=lambda: t.Execute())
tPy.start()
t.ExecuteAsync(Action(init_debugging))
t.ExecuteAsync(Action(hello))
t.ExecuteAsync(Action(t.Stop))
tPy.join()

Это опция вв некоторых случаях, но я не контролирую все создания потоков в библиотеках C #, которые я хочу использовать.

Вопрос: Что мне нужно сделать, чтобы сделать потоки C # известными отладчику Python навсегда такимичто отладчик не теряет из виду поток после того, как управление возвращается к C #.Повторный вызов settrace кажется немного сложным, особенно потому, что я не могу точно знать, какие методы могут быть вызваны из C #.

Я использую Python 2.7 и .NET Framework 4.6.1 на случай, еслиразница.

...