2011-10-10 5 views
5

Piszę trochę Pythona IDE i chcę dodać proste debugowanie. Nie potrzebuję wszystkich funkcji Winpdb. Jak uruchomić program Pythona (według nazwy pliku) z punktem przerwania ustawionym na numerze wiersza, aby działał do tego numeru linii i zatrzymywał się? Zauważ, że nie chcę tego robić z wiersza poleceń i nie chcę edytować źródła (wstawiając np. Set_trace). I nie chcę, żeby zatrzymywał się w pierwszej linii, więc muszę uruchomić debugger. Próbowałem wszystkich oczywistych sposobów z pdb i bdb, ale muszę czegoś brakuje.Uruchamianie sesji debugowania Pythona z poziomu programu, a nie z konsoli

+0

Więc nie chcesz używać wiersza poleceń, a nie chcesz edytować źródła ... o czym myślisz w inny sposób? – Amber

Odpowiedz

7

Prawie jedyny możliwy sposób na zrobienie tego (o ile wiem) to uruchomienie Pythona jako podprocesu z twojego IDE. Pozwala to uniknąć "zanieczyszczenia" z aktualnego interpretera Pythona, co sprawia, że ​​dość prawdopodobne jest, że program będzie działał w taki sam sposób, jak gdyby był uruchamiany niezależnie. (Jeśli masz problemy z tym, należy sprawdzić subprocess środowiska). W ten sposób można uruchomić skrypt w „tryb debugowania” używając

p = subprocess.Popen(args=[sys.executable, '-m', 'pdb', 'scriptname.py', 'arg1'], 
        stdin=subprocess.PIPE, 
        stdout=subprocess.PIPE, 
        stderr=subprocess.PIPE) 

Pozwoli to uruchomić Pythona w wierszu debugera. trzeba uruchomić niektórych poleceń debugera ustawić punkty przerwania, które można zrobić tak:

o,e = p.communicate('break scriptname.py:lineno') 

Jeśli to działa, o powinno być normalne wyjście interpretera Pythona po ustawia punkt przerwania, a e powinien być pustym. Sugeruję, abyś się tym zajmował i dodał kilka sprawdzeń w swoim kodzie, aby upewnić się, czy punkty przerwania zostały poprawnie ustawione.

Po tym, można uruchomić program działa z

p.communicate('continue') 

W tym momencie, że prawdopodobnie chcesz zaczepić wejścia, wyjścia i strumieni błędzie do konsoli, że jesteś wtłaczania w swoje IDE. Prawdopodobnie będzie trzeba to zrobić za pomocą pętli zdarzeń, z grubsza tak:

while p.returncode is None: 
    o,e = p.communicate(console.read()) 
    console.write(o) 
    console.write(e) 

Należy uznać, że fragment skutecznie być pseudokod, gdyż w zależności od tego, jak dokładnie Twoje prace konsoli, będzie to prawdopodobnie zajmie trochę do majsterkowania Zrób to dobrze.

Jeśli wydaje się zbyt brudny, prawdopodobnie można uprościć proces nieco korzystania z funkcji pdb i bdb modułów Pythona (zgaduję „python debugger”, a podstawową debuger”odpowiednio). Najlepszym odniesienia, jak to zrobić jest to kod źródłowy samego modułu pdb. Zasadniczo sposób podziału modułów jest taki, że bdb obsługuje funkcje debugowania "pod maską", takie jak ustawianie punktów przerwania lub zatrzymywanie i restartowanie wykonywania; który obsługuje interakcje użytkownika, tj. odczytywanie poleceń i wyświetlanie danych wyjściowych:

Dla zintegrowanego z IDE debuggera warto t zachowanie modułu pdb na dwa sposoby, że mogę myśleć:

  1. mieć to automatycznie ustawić punkty przerwania podczas inicjalizacji, bez konieczności jawnie wysłać tekstowych poleceń zrobić
  2. zrobić to wziąć wejście od i wysłać dane wyjściowe do konsoli IDE za

Tylko te dwie zmiany powinny być łatwe do wdrożenia przez instacji pdb.Pdb.Można utworzyć podklasę którego inicjator pobiera listę przerwań jako dodatkowy argument:

class MyPDB(pdb.Pdb): 
    def __init__(self, breakpoints, completekey='tab', 
       stdin=None, stdout=None, skip=None): 
     pdb.Pdb.__init__(self, completekey, stdin, stdout, skip) 
     self._breakpoints = breakpoints 

Logicznym miejscem faktycznie ustawić punkty przerwania tylko po debugger czyta swój plik .pdbrc, która występuje w metodzie pdb.Pdb.setup. W celu przeprowadzenia rzeczywistego ustawienia, należy zastosować metodę set_break odziedziczone z bdb.Bdb:

def setInitialBreakpoints(self): 
     _breakpoints = self._breakpoints 
     self._breakpoints = None # to avoid setting breaks twice 
     for bp in _breakpoints: 
      self.set_break(filename=bp.filename, line=bp.line, 
          temporary=bp.temporary, conditional=bp.conditional, 
          funcname=bp.funcname) 

    def setup(self, f, t): 
     pdb.Pdb.setup(self, f, t) 
     self.setInitialBreakpoints() 

Ten fragment kodu, który działa dla każdego zakresu wartości są przekazywane jako np nazwana krotka. Można również eksperymentować z bezpośrednim budowaniem instancji bdb.Breakpoint, ale nie jestem pewien, czy to działałoby poprawnie, ponieważ bdb.Bdb zachowuje własne informacje o punktach przerwania.

Następnie należy utworzyć nową metodę main dla swojego modułu, która uruchamia ten sam sposób uruchamiania pdb. Do pewnego stopnia możesz skopiować metodę main z pdb (i oczywiście oświadczenie if __name__ == '__main__'), ale będziesz musiał uzupełnić ją w jakiś sposób, aby przekazać informacje o twoich dodatkowych punktach przerwania. Co sugeruję pisze punkty przerwania do pliku tymczasowego z Twojego IDE i przepuszczenie nazwę tego pliku jako drugi argument:

tmpfilename = ... 
# write breakpoint info 
p = subprocess.Popen(args=[sys.executable, '-m', 'mypdb', tmpfilename, ...], ...) 
# delete the temporary file 

Następnie w mypdb.main(), należy dodać coś takiego:

def main(): 
    # code excerpted from pdb.main() 
    ... 
    del sys.argv[0] 

    # add this 
    bpfilename = sys.argv[0] 
    with open(bpfilename) as f: 
     # read breakpoint info 
     breakpoints = ... 
    del sys.argv[0] 
    # back to excerpt from pdb.main() 

    sys.path[0] = os.path.dirname(mainpyfile) 

    pdb = Pdb(breakpoints) # modified 

Teraz możesz użyć nowego modułu debuggera, tak jak byś użył pdb, z tym że nie musisz jawnie wysyłać poleceń break przed rozpoczęciem procesu. Ma to tę zaletę, że można bezpośrednio podłączyć standardowe wejście i wyjście podprocesu Python do konsoli, jeśli pozwala to na to.

+0

Dzięki za wejście. Ale otrzymuję ValueError: operacja I/O na zamkniętym pliku, jak tylko dotrze do p.communicate ("kontynuuj"). Używanie close_fds = Fałsz w Popen nie pomaga ... –

+0

@antwin: pamiętaj, że to wszystko jest w istocie pseudokodą. Jeśli po prostu uruchomisz go tak, jak jest, dostaniesz wszelkiego rodzaju błędy. Musisz dostosować go do własnego projektu w sposób, o którym nie mogłem powiedzieć nic, bez dostępu do twojego kodu źródłowego. –

+0

Rzeczywiście! Więc wyodrębniłem twoją sugestię do następującego pliku #!/Usr/bin/python import sys, subprocess fileName = '/tmp/test.py' lineno = 4 p = podproces.Popen (args = [sys.executable , '-m', 'pdb', fileName], stdin = podproces.PIPE, stdout = podproces.PIPE, stderr = podproces.STDOUT) cmd = 'przerwa% s:% d'% (nazwa pliku, bielizna) print cmd o, e = p.communicate (cmd) p.communicate ('kontynuuj') Powinno działać, a ja użyłem podobnego wcześniej, ale nadal daje ValueError: Operacja I/O w zamkniętym pliku po pierwszym komunikacie ... –

Powiązane problemy