Seite 1 von 1
logische Struktur anlegen
Verfasst: Freitag 18. März 2011, 18:25
von hypnoticum
Ich habe da auch noch ein etwas größeres Problem:
Ich schreibe sowohl in C als auch in Python.
Das resultierende Programm soll dazu dienen Geräte zu steuern.
Die Ansteuerung der Geräte erfolgt in C über die Extensions.
Ich will nun meine Projektstruktur so anlegen, daß ich eine Hirarchische Gliederung erhalte:
"... Gerätename.Block.Funktion",
wobei "Funktion" die Extension ist und über der Ebene Gerätename nur noch Python Module verwendet werden.
Das ganze soll dann eine Baumstruktur werden in der sich Verzeichnis- und Packagestruktur entsprechen.
Es soll halt möglichst übersichtlich und erweiterbar sein.
Gibts dafür irgendein Beispiel?
Ich nutze zB. im Moment ein Script "GenExt.py" pro Zweig/Ebene, das mir je ein Extension Modul aus einem C-File erstellt.
Das hat den zB. Nachteil, daß ich auf die Funktion einer Extension nur so zugreifen kann:
"... Gerätename.Block.ExtensionModul.Funktion",
ich will aber die "Funktionen" schon in getrennten Files verwalten.
Unter
http://docs.python.org/distutils/setupscript.html ist ein bisschen was beschrieben zum Thema Packages und Extensions.
Das hat mir aber noch nicht wirklich weitergeholfen
Habe es schon auch was versucht, aber scheinbar kann ich den PYTHONPATH nicht richtig ergänzen.
(Hab´s in Eclipse unter "Windows->Preferences-> ... versucht - dauert immer ewig bis es durch ist,
und auch über "Settings->ControlPanel->System->Advanced->EnviromentVariables-> ..."
mein Script "GenExt.py" enthält außerdem mehrere gleichgestaltete Blöcke
Code: Alles auswählen
modul = Extension("Gerät.Block.Funktion", sources = ["C/Funktion.c"],
include_dirs = ... ,
library_dirs = ...,
libraries = [ ..., ... ])
setup(
name = "...",
version = "1.0",
description = "Module for ...",
ext_modules = [modul]
)
(nebenbei: Die Zeilen mit den Optionen include_dirs, library_dirs und libraries sind immer gleich und ich frage mich, ob das nicht auch kürzer bzw. übersichtlicher geht indem man das nur einmal hinschreiben muss)
Die module versuche ich dann mit
Code: Alles auswählen
import Gerät.Block1
from Gerät.Block2 import (Funktion1,
Funktion2,
Funktion3)
import Gerät.Block3.Funktion1
einzubinden. Das geht wohl nicht weil dann noch der Name des "ExtensionModul" dazwischen müsste.
irgendeine Idee ?
Re: logische Struktur anlegen
Verfasst: Freitag 18. März 2011, 19:30
von BlackJack
@hypnoticum: Was ist denn `BlockX` jeweils? Kannst Du da nicht Deine Extension einfach so benennen?
Musst Du zwingend in C Programmieren? Das wäre für mich nur die aller-aller-letzte Zuflucht. Wenn aus irgend welchen Gründen `ctypes` oder `cython` (in der Reihenfolge) nicht in Frage kommen.
Re: logische Struktur anlegen
Verfasst: Freitag 18. März 2011, 21:39
von deets
@hynoticum
Nein, sowas geht nicht. Es gibt zwar fuer pure Python-Module die Moeglichkeit sogenannter Namespace-Pakete. Aber das klappt AFAIK nicht fuer Extensions.
Und ich denke auch nicht wirklich, dass deine Kriterien von Uebersichtlichkeit & Erweiterbarkeit durch eine Unzahl von Modulen erreicht werden.
Re: logische Struktur anlegen
Verfasst: Samstag 19. März 2011, 13:17
von hypnoticum
@BlackJack:
Mit ctypes habe ich es vorher gemacht. Der Weg über Extensions ist meiner Meinung für das "Projekt" der bessere weil schon Code und Dokumentation in C existiert.
Ich hatte zu Anfang auch überlegt für jeden Block eine Extension anzulegen. Aber ich habe nicht den Weg gefunden mehrere Funktionen in ein Modul zu kompilieren bzw. diese dann aufzurufen. Außerdem wärs halt nach meinem Empfinden nicht so schön eine immer weiter wachsende Datei zu haben.
@deets:
Schade, wenn es nicht geht. Unter dem von mir angegebenen link im Abschnitt "2.3.1. Extension names and packages" hatte es sich für mich allerdings so angehört, als wenn es möglich sein sollte.
Re: logische Struktur anlegen
Verfasst: Samstag 19. März 2011, 13:38
von BlackJack
@hypnoticum: Was meinst Du damit dass Du nicht mehrere Funktionen in ein Modul kompilieren kannst!?
Was spricht denn gegen Cython? Da brauchst Du Dich wenigstens nicht um das ganze Verwaltungsgeraffel zu kümmern wie Referenzzähler und vor allem auch Ausnahmen, oder die Strukturen um den Modulinhalt dem Python-Interpreter bekannt zu machen. Um Ausnahmen hast Du Dich in dem gezeigten Quelltext ja gar nicht gekümmert. Das ist ziemlich unsauber und wird früher oder später vielleicht auch zu einer Quelle für Abstürze.
Vorhandener C-Code spricht IMHO auch nur gegen `ctypes` wenn das schon eine Python-Erweiterung ist und selbst dann würde ich versuchen das über die Zeit in Richtung Cython oder `ctypes` zu verlagern. Spätestens wenn ich aus einer C-Erweiterung heraus `Tkinter` ansteuern müsste, würde ich das ganz dringend machen wollen.
Re: logische Struktur anlegen
Verfasst: Samstag 19. März 2011, 20:30
von hypnoticum
@BlackJack:
>>Was meinst Du damit dass Du nicht mehrere Funktionen in ein Modul kompilieren kannst!?
Ich meine das so wie ich es geschrieben habe: ich weiß nicht wie's geht - ganz einfach.
Cython habe ich mir noch nicht angesehen. Ich bin Einsteiger und habe mir meinen Weg gesucht. Den bin ich mit Extensions gegangen und auch recht zufrieden bis auf das hier genannte Problem, das ja eigentlich auch mehr von "kosmetischer"-Natur ist. Ich werde mir Cython mal ansehen.
Wegen dem Embedding von Python in den Extensions mach dir mal keine Sorgen: das ist nicht so umfangreich und eher eine Randerscheinung/Hilfe die nur in außergewöhnlichen Situationen genutzt werden soll (Fehlermeldung, Unterbrechung des Programms mit Intervention, Ausfall des Reaktorkühlsystems

) muss also nicht so zuverlässig sein.
Re: logische Struktur anlegen
Verfasst: Samstag 19. März 2011, 21:54
von BlackJack
@hypnoticum: Also ich kann echt nicht nachvollziehen wieso Du nicht mehr als eine Funktion in eine C-Erweiterung bekommst. Das ist ja nun echt nichts exotisches. Mir fällt im Gegenteil auf Anhieb keine C-Erweiterung ein, die nur eine Funktion enthält, also müsste so ziemlich jede Vorhandene als Beispiel dienen können, wie das geht.
Re: logische Struktur anlegen
Verfasst: Sonntag 20. März 2011, 17:25
von deets
@BlackJack
Ich verstehe das so, dass er genau das *Gegenteil* will. Lauter einzelne Funktionen, die aber im Grunde in einem Python-Modul stecken. Aus seinem Beispiel:
Gerätename.Block.Funktion
Block ist Python, Funktion dann aber schon eine C-Funktion.
Das geht natuerlich nicht, ausser, er wuerde endlich einsehen, ctypes zu verwenden

Re: logische Struktur anlegen
Verfasst: Sonntag 20. März 2011, 19:48
von BlackJack
@deets: Das verstehe ich nicht? Lauter einzelne Funktionen die in einem Modul stecken -- ob das nun ein Python-Modul oder eine C-Erweiterung ist -- geht doch. Oder was ist für Dich eine "einzelne Funktion"? Ich kann mir echt nicht im entferntesten vorstellen was hier eigentlich das Problem ist!? `Block` müsste halt einfach die C-Erweiterung sein und schon hat man was gewünscht ist.
Re: logische Struktur anlegen
Verfasst: Sonntag 20. März 2011, 22:05
von deets
@BlackJack:
Dann lies nochmal genau:
Ich nutze zB. im Moment ein Script "GenExt.py" pro Zweig/Ebene, das mir je ein Extension Modul aus einem C-File erstellt.
Das hat den zB. Nachteil, daß ich auf die Funktion einer Extension nur so zugreifen kann:
"... Gerätename.Block.ExtensionModul.Funktion",
ich will aber die "Funktionen" schon in getrennten Files verwalten.
Das lese ich so, dass eben eine Funktion ein modul sein soll, was dann gleichzeitig das callable "Funktion" ist.
Ausserdem schreibt er:
Ich will nun meine Projektstruktur so anlegen, daß ich eine Hirarchische Gliederung erhalte:
"... Gerätename.Block.Funktion",
wobei "Funktion" die Extension ist und über der Ebene Gerätename nur noch Python Module verwendet werden.
Jetzt kommt's ein bisschen darauf an, was "ueber der Ebene Geraetename" gemeint ist - die Ebene Block, oder die Ebene "...".
Alles in allem ein bisschen schwurbelig, so ganz 100%ig blicke ich da auch nicht durch.
Re: logische Struktur anlegen
Verfasst: Montag 21. März 2011, 08:13
von hypnoticum
Vielen Dank Jungs.
Eigentlich ist es schon so das die Extensions der unteren Ebene nur von Python aus der nächst höheren Ebene aufgerufen werden.
Bevor es zu kompliziert wird werde ich dann mal sehen, ob ich es auch anders machen kann.
Re: logische Struktur anlegen
Verfasst: Montag 21. März 2011, 09:01
von BlackJack
@hypnoticum: Bei Python-Modulen die sich auf C-Erweiterungen abstützen, welche vom Benutzer nicht direkt verwendet werden sollen, ist es ein durchaus übliches Muster beide auf der gleichen Verzeichnisebene zu haben und dem Erweiterungsmodul einen '_' voran zu stellen um es als nicht-öffentliche API zu kennzeichnen. Daraus ergeben sich ja letztendlich zwei sauber getrennte Namensräume für die öffentliche und die private API. Also als Verzeichnisbaum sähe dass dann so aus:
Code: Alles auswählen
geraet/
__init__.py
block1.py
_block1.so
block2.py
_block2.so
…
Diese sehr flache Verzeichnisstruktur bietet, soweit ich das verstanden habe, die Namensraumstruktur, die Du dem Benutzer der Bibliothek bieten möchtest: `geraet.block1.function1()`. Und im `block1`-Modul kannst Du auf die C-Erweiterung über `geraet._block1.internal_function1()` zugreifen, bzw. es relativ mit ``import _block1`` oder ``import ._block1`` importieren.
Um mal das Python-Zen zu zitieren (``import this``): `Flat is better than nested.`
Beim Planen muss man übrigens auch nicht zwingend berücksichtigen, dass so ein `block.py` mal so umfangreich wird, dass man es in mehrere Module aufteilen möchte und die der Übersicht halber in ein eigenes Package stecken möchte. Das kann man dann wenn es akut wird nämlich immer noch tun -- ohne dass sich nach aussen hin die API ändern muss! Beispiel:
Und nun wird die Implementierung von `spam` immer grösser und man möchte es gerne in zwei oder drei Module aufteilen:
Code: Alles auswählen
mypackage/
__init__.py
spam/
__init__.py <- Hier die gleiche API wie beim alten spam.py anbieten!
part1.py
part2.py
util.py
Aus Benutzersicht hat sich hier nichts wirklich verändert. Wenn man auch die API für den Benutzer ändern möchte, kann man weiterhin parallel die alte API in `spam/__init__.py` anbieten und die Aufrufe dort "wrappen", dass sie über das `warnings`-Modul jeweils eine `DeprecationWarning` absetzen, und den Benutzern ein paar Releases lang Zeit lassen ihren Quelltext anzupassen.
Re: logische Struktur anlegen
Verfasst: Mittwoch 23. März 2011, 09:10
von hypnoticum
Vielen Dank für die Antworten.
Ich lege jetzt eine C-Datei "Block" an, die als Modul kompiliert wird. In dieser C-Datei inkludiere ich die Funktionen, welche in anderen Dateien abgelegt sind. Von diesen Funktionen wiederum gemeinsame genutzte Routinen sind auch in der "Block"-Datei. Das ist etwa so wie ich es haben wollte.
Code: Alles auswählen
#include <Python.h>
#include <string.h>
#include <stdio.h>
PyObject * Routine(double Handle);
#include "cExtenFun1.c"
#include "cExtenFun2.c"
static PyObject *cExtenFun1(PyObject *self, PyObject *args);
static PyObject *cExtenFun2(PyObject *self, PyObject *args);
static PyMethodDef pyExten_methods[] = {
{"pyExtenFun1", cExtenFun1, METH_VARARGS, "Performs some Function"},
{"pyExtenFun2", cExtenFun2, METH_VARARGS, "Performs some other Function"},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC initExten(void){
Py_InitModule("Exten", pyExten_methods);
};
PyObject * Routine(double C_Handle){
PyObject *C_Module = NULL, *C_Class = NULL, *C_Instance = NULL;
Py_Handle = Py_BuildValue("(l)", C_Handle);
C_Module = PyImport_ImportModule("Py_Module");
if(C_Module){
C_Class = PyObject_GetAttrString(C_Module, "Py_Method");
if (PyCallable_Check(C_Class)){
C_Instance = PyObject_CallObject(C_Class, Py_Handle);
}
else
printf("Error: Py_Method not found\n");
}
else
printf("Error: Py_Module not found\n");
return C_Instance;
Py_XDECREF(C_Module);
}
Das Modul generiere ich mit diesem Script:
Code: Alles auswählen
"""
It might be useful to tell distutils to use mingw. To achieve this,
create a file named distutils.cfg (if you already don't have it) in
\PythonXY\Lib\distutils and add to it the following lines:
[build]
compiler = mingw32
"""
#[url]http://docs.python.org/distutils/setupscript.html[/url]
import sys
from distutils.core import setup, Extension
sys.argv.append('install')
modul = Extension("Block", sources = ["C/Block.c"])
setup(
name = "PyExten",
version = "1.0",
description = "Test C-Extension",
ext_package='Gerät',
ext_modules = [modul]
)
Ein Problem habe ich allerdings noch: Wenn ich etwas an der obigen Datei ändere zB anstelle von "Gerät" "Device" schreibe und nach der Erzeugung des Moduls diese Änderung wieder rückgängig mache, werden von nun an zwei Ordner in ".../Pythonxy/Lib/site-packages" angelegt. Die Erzeugung der Module wird also immer wieder unabhängig von dem Inhalt der Datei vorgenommen ... ?
Re: logische Struktur anlegen
Verfasst: Mittwoch 23. März 2011, 10:13
von BlackJack
@hypnoticum: Komplette C-Dateien per ``#include`` in andere zu importieren ist äusserst unschön. Normalerweise würde man nur Headerdateien mit den Deklarationen in der `C/Block.c` inkludieren und nicht den ganzen Quelltext.
Re: logische Struktur anlegen
Verfasst: Mittwoch 23. März 2011, 10:25
von hypnoticum
das soll jetzt bitte nicht patzig klingen - aber was bitte heisst "unschön"?
Ich bin eigentlich gerade froh eine Lösung gefunden zu haben, wie soll ich es sonst machen, wenn der Quelltext nicht an einem Stück in einer Datei vorhanden sein soll ...
(mal ganz nebenbei: ich bin kein Informatiker. ich lasse mir gerne helfen, aber letztendlich bin ich froh wenn's läuft)
Nachtrag:
Unschön ist zB. wie ich gerade feststellen musste, dass von mingw nicht neu kompiliert wird, wenn sich eine inkludierte *.c-Datei geändert hat.
Das kann im setup-script mit der option
depends = ['Datei1.c', 'Datei2.c', ...]
zum Glück noch behoben werden.
Re: logische Struktur anlegen
Verfasst: Mittwoch 23. März 2011, 10:57
von BlackJack
@hypnoticum: "Unschön" heisst, dass man das nicht macht, weil das früher oder später Probleme bereitet.
Der Quelltext muss nicht an einem Stück vorhanden sein. Das `source`-Argument nimmt doch eine Liste entgegen und nicht nur einen Dateinamen.
Nach den üblichen C-Konventionen sähe dass so aus:
Code: Alles auswählen
C/
Block.c
cExtenFun1.h
cExtenFun1.c
cExtenFun2.h
cExtenFun2.c
common.h
common.c
In `common.*` wären dann die gemeinsam genutzten Funktionen. Wenn man das nicht so auslagert, bekäme man eine zirkuläre Abhängigkeit bei den ``#include``\s.
Re: logische Struktur anlegen
Verfasst: Mittwoch 23. März 2011, 12:24
von deets
@hypnoticum
Der Grund ist der, dass jedes C-File eine 'compilation unit' ist. Und das zusammenfassen via linken geschieht. Wenn du das anders machst, musst du aufpassen, dass der Compilationsprozess niemals dasselbe source-file zweimal einbindet- sonst gibt's doppelte Symbole.
Darum BlackJack's sehr richtiger Ratschlag.
Re: logische Struktur anlegen
Verfasst: Mittwoch 23. März 2011, 16:41
von hypnoticum
Ist vielleicht unüblich, aber funktioniert erstmal.
Ich habe auch nicht den Überblick was beim Compilieren in welcher Reihenfolge abläuft, wie oft jedes File abgesucht wird, Symbole ersetzt werden ...
Sucht der Linker automatisch nach einem gleichnamigen Objekt-File wenn ich Funktionen einer inkludierten Header Datei verwende? Und wenn der zugehörige Quellcode noch gar nicht komnpiliert wurde?
was mich wundert ist, das ich keinen Fehler trotz mehrfach definiertem Label bekomme
Abschließend wärs nett wenn einer wüsste warum das so ist:
>>Wenn ich etwas an der obigen Datei ändere zB anstelle von "Gerät" "Device" schreibe und nach der Erzeugung des Moduls diese Änderung wieder rückgängig mache, werden von nun an zwei Ordner in ".../Pythonxy/Lib/site-packages" angelegt. Die Erzeugung der Module wird also immer wieder unabhängig von dem Inhalt der Datei vorgenommen ... ?
Re: logische Struktur anlegen
Verfasst: Mittwoch 23. März 2011, 17:41
von BlackJack
@hypnoticum: Die `distutils` "wissen" wie sie aus den angegebenen Dateien jeweils eine Object-Datei (*.o) machen und rufen am Ende den Linker mit allen Object-Dateien auf. Dann der Linker die Symbole alle auflösen, weil er die exportierten Symbole von allen Übersetzungseinheiten kennt.
Man kann in C ein und die selbe Sache beliebig oft deklarieren, solange die Deklaration immer gleich ist.