Frage zu subprocess

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benthausn
User
Beiträge: 9
Registriert: Sonntag 19. April 2020, 12:08

Servus,

ich hoffe ich bin hier unter allgemeine Fragen mit meiner Frage richtig und hoffe ich benutze die Code-Funktion hier richtig. Falls nicht bitte etwas Nachsicht, da das hier mein erster Post ist. :D

Ich möchte mit Hilfe von Python ein Programm (OpenSCAD, ohne GUI) öffnen, welches dann eine Datei einliest (.csg) und diese in .stl konvertiert. Das funktioniert bei mir soweit auch alles, allerdings lese ich zur Zeit eine Batch-Datei über das os-Modul ein.

Code: Alles auswählen

@echo off   
cd C:\Program Files\OpenSCAD
openscad -o C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\test.stl C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\test.csg 
Kurz zur Erklärung: mit "openscad -o" wird OpenSCAD ohne GUI geöffnet und "C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\test.stl " gibt an, dass test.csg im angegebenen Pfad (C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\test.csg) unter dem angegebenen Pfad in test.stl konvertiert werden soll. Aufrufen tu ich die Batch-Datei dann mit:

Code: Alles auswählen

os.system(r'C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\OpenSCAD.bat')
Nun möchte ich allerdings nicht mehr eine externe Batch-Datei haben, sondern das einfach direkt im Pythonskript erledigen. Ich habe gelesen, dass das sowohl über das os-Modul als auch über das subprocess-Modul funktioniert. Anscheinend ist aber subprocess neuer, also habe ich beschlossen das zu verwenden. Wie man das Programm öffnet habe ich auch schon gegoogelt und ich denke das passt auch. Zumindest wirft mir Python keine Fehlermeldung.

Code: Alles auswählen

subprocess.Popen([r'C:\Program Files\OpenSCAD\openscad', '-o'], stdout=subprocess.PIPE)
Nun will ich allerdings die Konvertierung machen und bekomme es einfach nicht hin. Ich weiß nicht so recht, wie ich Python das mitteilen soll. Ich habe es folgendermaßen probiert, bekomme jedoch immer einen Fehler was die Codierung angeht. Ich hab es schon mit decodieren versucht, aber bei dem Thema kenne ich mich leider viel zu wenig aus und habe es auch nicht hinbekommen bzw. bin mir auch nicht sicher, ob das da wirklich überhaupt notwendig ist.

Meine Versuche, die nicht funktionieren:

Code: Alles auswählen

subprocess.Popen([r'C:\Program Files\OpenSCAD\openscad', '-o', 'C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\test.stl C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\test.csg'], stdout=subprocess.PIPE)
und

Code: Alles auswählen

#subprocess.Popen([r'C:\Program Files\OpenSCAD\openscad', '-o C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\test.stl C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\test.csg'], stdout=subprocess.PIPE)
Ah die Fehlermeldung könnte ich natürlich auch noch dazu tun:

Code: Alles auswählen

runfile('C:/Users/micha/Desktop/LCM/K12306_STIWA_OpenSCAD/OpenSCAD_oeffnen.py', wdir='C:/Users/micha/Desktop/LCM/K12306_STIWA_OpenSCAD')
Traceback (most recent call last):

  File "C:\Users\micha\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 3343, in run_code
    self.showtraceback(running_compiled_code=True)

  File "C:\Users\micha\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2026, in showtraceback
    self.showsyntaxerror(filename, running_compiled_code)

  File "C:\Users\micha\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2088, in showsyntaxerror
    stb = self.SyntaxTB.structured_traceback(etype, value, elist)

  File "C:\Users\micha\Anaconda3\lib\site-packages\IPython\core\ultratb.py", line 1420, in structured_traceback
    newtext = linecache.getline(value.filename, value.lineno)

  File "C:\Users\micha\Anaconda3\lib\linecache.py", line 16, in getline
    lines = getlines(filename, module_globals)

  File "C:\Users\micha\Anaconda3\lib\linecache.py", line 47, in getlines
    return updatecache(filename, module_globals)

  File "C:\Users\micha\Anaconda3\lib\linecache.py", line 137, in updatecache
    lines = fp.readlines()

  File "C:\Users\micha\Anaconda3\lib\codecs.py", line 322, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd6 in position 264: invalid continuation byte
Sorry für den langen Text und hoffe, dass mir irgendwer weiter helfen kann. Vielen Dank schon mal!! :)

Liebe Grüße
Michi
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Benthausn: Du hast zum einen ein Problem mit \-Escape-Sequenzen. Der \ hat in Zeichenkettenliteralen eine besondere Bedeutung, zumindest wenn darauf bestimmte Zeichen folgen. "K12306_STIWA_OpenSCAD\test" ist beispielseise *kein* \ und am Ende nicht das Wort "test" weil die Kombination "\t" ein Tabulatorzeichen ist. Du musst entweder für jeden \ im Zeichenkettenliteral "\\" schreiben, weil das für *einen* \ steht, oder ”rohe” Zeichenkettenliterale verwenden, mit einem r davor, damit \ keine besondere Bedeutung für den Compiler hat.

Und dann trennst Du die Argument falsch. Du übergibst die beiden Pfade als *ein* Argument, das sind aber zwei.

Zuguterletzt fehlt dann noch das `cwd`-Argument um das ``CD`` aus dem Batch-Skript auch umzusetzen.

Code: Alles auswählen

    subprocess.run(
        [
            "openscad",
            "-o",
            r"C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\test.stl",
            r"C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\test.csg",
        ],
        cwd=r"C:\Program Files\OpenSCAD",
        check=True,
    )
Ich habe aber den Verdacht Du hast schon davor ein Problem damit das in `OpenSCAD_oeffnen.py` im Quelltext ein Ö steht und die Datei nicht UTF-8 kodiert ist.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benthausn
User
Beiträge: 9
Registriert: Sonntag 19. April 2020, 12:08

@__blackjack__:

Schon mal viele Dank für deine schnelle Antwort. :) Mit den \-Escape-Sequenzen hast du mir auf jeden Fall schon mal sehr weiter geholfen und auch damit, dass ich die Pfade getrennt übergeben muss. In der Batch-Datei funktioniert das irgendwie wenn man es hintereinander schreibt, deswegen hab ich geglaubt das geht in Python dann auch so, da es ja cmd in beiden Fällen verstehen muss. Aber gut, man lernt dazu. :D

Den Namen des Skripts hab ich jetzt in OpenSCAD.py geändert, damit ich das Problem mit dem "ö" nicht mehr habe und meinen Code durch deinen ersetzt. Allerdings bekomme ich immer noch die Fehlermeldung (falls es überhaupt die selbe ist):

Code: Alles auswählen

runfile('C:/Users/micha/Desktop/LCM/K12306_STIWA_OpenSCAD/OpenSCAD.py', wdir='C:/Users/micha/Desktop/LCM/K12306_STIWA_OpenSCAD')
Traceback (most recent call last):

  File "C:\Users\micha\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 3343, in run_code
    self.showtraceback(running_compiled_code=True)

  File "C:\Users\micha\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 2043, in showtraceback
    value, tb, tb_offset=tb_offset)

  File "C:\Users\micha\Anaconda3\lib\site-packages\IPython\core\ultratb.py", line 1385, in structured_traceback
    self, etype, value, tb, tb_offset, number_of_lines_of_context)

  File "C:\Users\micha\Anaconda3\lib\site-packages\IPython\core\ultratb.py", line 1297, in structured_traceback
    elist = self._extract_tb(tb)

  File "C:\Users\micha\Anaconda3\lib\site-packages\IPython\core\ultratb.py", line 1278, in _extract_tb
    return traceback.extract_tb(tb)

  File "C:\Users\micha\Anaconda3\lib\traceback.py", line 72, in extract_tb
    return StackSummary.extract(walk_tb(tb), limit=limit)

  File "C:\Users\micha\Anaconda3\lib\traceback.py", line 363, in extract
    f.line

  File "C:\Users\micha\Anaconda3\lib\traceback.py", line 285, in line
    self._line = linecache.getline(self.filename, self.lineno).strip()

  File "C:\Users\micha\Anaconda3\lib\linecache.py", line 16, in getline
    lines = getlines(filename, module_globals)

  File "C:\Users\micha\Anaconda3\lib\linecache.py", line 47, in getlines
    return updatecache(filename, module_globals)

  File "C:\Users\micha\Anaconda3\lib\linecache.py", line 137, in updatecache
    lines = fp.readlines()

  File "C:\Users\micha\Anaconda3\lib\codecs.py", line 322, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd6 in position 273: invalid continuation byte
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Benthausn: In der Batch-Dareu steht auch openscad und -o einfach so hintereinander. Weder Batch-Skript noch Python hat irgend eine Ahnung davon was der Unterschied zwischen -o und einem Pfad ist wenn das da einfach so steht. Das sind einfach nur Zeichenketten die als einzelne Argumente an das zu startende Programm übergeben werden. Die cmd-Shell teilt so etwas an Leerzeichen in einzelne Argumente auf. Mit ein paar Sonderregeln, zum Beispiel was Anführungszeichen angeht, damit man auch Argumente mit Leerzeichen angeben kann, an denen Dann *nicht* getrennt wird. Das -o eine Option ist und was die Bedeutet oder das die beiden weiteren Argumente als Pfade interpretiert werden, ist dann die Sache von ``openscad``.

Im Falle von Python und `subprocess` muss das keine cmd-Shell verstehen. Deswegen trennt man die Argumente ja selbst, weil hier ``openscad`` direkt, ohne zusätzliche, unnötige Shell dazwischen, gestartet wird.

Im *Namen* ``OpenSCAD_oeffnen.py`` ist doch gar kein Ö. Das muss irgendwo im *Quelltext* stehen, also *in* der Datei. Und zwar wirklich ein grosses Ö. Die Datei ist nicht UTF-8 kodiert gespeichert worden, wie es aussieht. Wobei es auch ein anderes Zeichen sein kann, falls die Datei auch nicht als CP1252 kodiert vorliegt.

Womit schreibst Du denn den Quelltext? Da musst Du darauf achten, das der als UTF-8 gespeichert wird und nicht als CP1252, ”ANSI”, oder irgend etwas anderes.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benthausn
User
Beiträge: 9
Registriert: Sonntag 19. April 2020, 12:08

@__blackjack__:

Ich befürchte leider, dass ich meine Lage eher verschlechter als verbessert habe. Ich wollte wegen dem UTF-8 sichergehen, dass ich keine veralteten versionen habe und wollte mir die neuste Pythonversion installieren. Ich weiß nicht was das Problem ist, jedenfalls fehlt mir jetzt die das subprocess Modul, wenn ich die Fehlermeldung richitg verstehe. Daraufhin hab ich anacondra3 deinstalliert und neu installiert, natürlich auch den Computer neu gestartet und die Fehlermeldung ist noch immer da. Ich kann mir irgendwie nicht erklären was da passiert sein soll, das Problem hab ich ja vorher nicht gehabt.

Code: Alles auswählen

runfile('C:/Users/micha/Desktop/LCM/K12306_STIWA_OpenSCAD/OpenSCAD_neu.py', wdir='C:/Users/micha/Desktop/LCM/K12306_STIWA_OpenSCAD')
Traceback (most recent call last):

  File "C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\OpenSCAD_neu.py", line 54, in <module>
    check=True,

  File "C:\Users\micha\anaconda3\lib\subprocess.py", line 488, in run
    with Popen(*popenargs, **kwargs) as process:

  File "C:\Users\micha\anaconda3\lib\site-packages\spyder_kernels\customize\spydercustomize.py", line 104, in __init__
    super(SubprocessPopen, self).__init__(*args, **kwargs)

  File "C:\Users\micha\anaconda3\lib\subprocess.py", line 800, in __init__
    restore_signals, start_new_session)

  File "C:\Users\micha\anaconda3\lib\subprocess.py", line 1207, in _execute_child
    startupinfo)

FileNotFoundError: [WinError 2] Das System kann die angegebene Datei nicht finden
So dann zu UFT-8:

Ich benutze jetzt Spyder4 / Anacondra3 und Python 3.7. Wenn ich ein neues Pythonskript öffne, wird mir automatisch folgende Zeile reinkopiert.

Code: Alles auswählen

# -*- coding: utf-8 -*-
Wenn ich das richtig im Internet gelesen habe, müsste das auch reichen oder? Ich hab dich vorhin falsch verstanden, ich hab geglaubt du meinst das Oe wird irgendwie als Ö umgewandelt und da wären dann Probleme. :lol:

Ö hatte ich nur in den Kommentaren, ich hab gedacht das wäre wurscht, hab aber alles geändert und es ist trotzdem nicht gegangen. Ich kopier jetzt einfach mal meinen ganzen Code hier rein, das ist nicht viel und vielleicht liegt der Fehler ja irgendwo, wo ich ihn gar nicht vermute,

Code: Alles auswählen

# -*- coding: utf-8 -*-

# Skript erzeugt in OpenSCAD .csg-File und konvertiert dieses automatisch in .stl-File.

import os # operating system modul -> Inteface (Schnittstelle) zum Betriebssystem
import subprocess  # as sub

### Datei mit der Endung .csg erstellen: ###

csg_file = open('test.csg','w')  

# Geometrie in Datei schreiben:

csg_file.write(
'l = 15;\n'
'w = 15;\n'
'h = 5;\n'
'rotate(a=90, v=[1, 0, 0]){\n'
'  difference(){\n'
'    cube([10,10,10]);\n'
'    translate([-1,-3,-1])\n'    
'      polyhedron(\n'
'        points = [[0,0,0], [l,0,0],[l,w,0], [0,w,0],[0,w,h], [l,w,h]],\n'
'        faces= [[0,1,2,3],[5,4,3,2],[0,4,5,1],[0,3,4],[5,2,1]]\n'
'      );\n'
'  }\n'
'}'
)

csg_file.close()   # Damit Datei in Python nicht geoeffnet bleibt.


### OpenSCAD oeffnen (ohne GUI) & .stl-File erzeugen: ###

# OpenSCAD über Batch-Datei oeffnen:

#os.system(r'C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\OpenSCAD.bat')  # r -> Alle nachfolgenden Zeichen gehören zum String (auch z.B. Backslash)

# OpenSCAD direkt ueber Python/ subprocess-Modul oeffnen:

subprocess.run(
        ["openscad",
            "-o",
            r"C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\test.stl",  # r davor -> \ haben keine Bedeutung für Compiler
            r"C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\test.csg",
        ],
        cwd=r"C:\Program Files\OpenSCAD",
        check=True,
)
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das subprocess-Modul ist da. Du gibst nicht den richtigen Pfad an, das ist alles.
Benutzeravatar
__blackjack__
User
Beiträge: 14052
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Benthausn: Der Kodierungskommentar reicht nicht, die Kodierung der Datei muss auch tatsächlich zu dem Kommentar passen. Und das tut sie nicht, beziehungsweise tat sie nicht. Bei Python 3 ist UTF-8 auch die Kodierung die angenommen wird wenn kein Kodierungskommentar vorhanden ist.

Da stehen überflüssige Kommentare drin. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da ja bereits als Code, sondern warum er das so macht. Sofern das nicht offensichtlich ist. Unter offensichtlich zählt auch was in der Dokumentation der verwendeten Sprachkonstrukte und Module steht, denn es macht keinen Sinn diese Dokumentation noch mal in den Quelltext zu schreiben.

Dateien sollte man wo es geht mit der ``with``-Anweisung öffnen und Textdateien immer mit einer expliziten Kodierungsangabe. Wobei in diesem Fall auch `pathlib.Path` nützlich wäre, weil die Klasse eine Methode hat die öffnen, schreiben, und schliessen übernimmt.

`Path` ist zudem nützlich um die Wiederholung des Pfades zu den beiden Dateien zu vermeiden.

Natürlich ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
#
# Skript erzeugt in OpenSCAD .csg-File und konvertiert dieses automatisch in
# .stl-File.
#
import subprocess
from pathlib import Path

BASE_PATH = Path(r"C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD")

CSG_FILE_CONTENT = """\
l = 15;
w = 15;
h = 5;
rotate(a=90, v=[1, 0, 0]){
  difference(){
    cube([10,10,10]);
    translate([-1,-3,-1])
      polyhedron(
        points = [[0,0,0], [l,0,0],[l,w,0], [0,w,0],[0,w,h], [l,w,h]],
        faces= [[0,1,2,3],[5,4,3,2],[0,4,5,1],[0,3,4],[5,2,1]]
      );
  }
}
"""


def main():
    csg_file_path = BASE_PATH / "test.csg"
    csg_file_path.write_text(CSG_FILE_CONTENT, encoding="ascii")

    subprocess.run(
        ["openscad", "-o", str(BASE_PATH / "test.stl"), str(csg_file_path)],
        cwd=r"C:\Program Files\OpenSCAD",
        check=True,
    )


if __name__ == "__main__":
    main()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benthausn
User
Beiträge: 9
Registriert: Sonntag 19. April 2020, 12:08

Ok vielen Dank, ich schau mir den Code dann in Ruhe an. :) Ich kann ihn im Moment nur halt leider nicht mal testen, da anscheinend ja irgendein Pfad bei mir nicht richtig angegeben ist. Ich weiß aber leider nicht welcher (also vermutlich der von subprocess) und ich weiß aber auch nicht, wo ich den angeben soll. Mir ist da einfach nicht ganz klar, was da bei meiner Neuinstallation oder wo auch immer schief gegangen ist.
Benthausn
User
Beiträge: 9
Registriert: Sonntag 19. April 2020, 12:08

Falls nochmal irgendwer das selbe Problem hat und sich den Thread hier durchliest. Ein bisschen anders (und es ist wahrscheinlich nicht die beste Lösung) funktioniert es jetzt, auch wenn ich dann leider nur teilweise den Code von __blackjack__ übernehmen kann. :(

Code: Alles auswählen

subprocess.call(
        ["openscad",
            "-o",
            r"C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\test.stl",  
            r"C:\Users\micha\Desktop\LCM\K12306_STIWA_OpenSCAD\test.csg",
        ],
        cwd=r"C:\Program Files\OpenSCAD",
        shell=True,
)
@__blackjack__ : Auf jeden Fall vielen Dank für die ganze Hilfe, hat mir total geholfen!! :)
Antworten