VS Code, Pylance findet Module nicht

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
mechanicalStore
User
Beiträge: 166
Registriert: Dienstag 29. Dezember 2009, 00:09

Hallo Zusammen,

gegeben ist in folgendem einfachen Beispielcode main.py und auf gleicher Ebene ein Verzeichnis helper_libs mit 3 Modulen:

./main.py

Code: Alles auswählen

import helper_libs.first as first
import helper_libs.second as second
import helper_libs.third as third

def main():
    a = first.A()
    a.word = 'Bar'
    b =  second.B()
    b.sentence = 'Bar'
    c = third.C()
    c.last = 'das letzte'

if __name__ == '__main__':
    main()
./helper_libs/first.py

Code: Alles auswählen

class A:
    def __init__(self):
        self.word = ''
./helper_libs/second.py

Code: Alles auswählen

class B:
    def __init__(self):
        self.sentence = ''
./helper_libs/third.py

Code: Alles auswählen

import first
import second

class C:
    def __init__(self):
        self.last = ''

def test():
    f = first.A()
    g = second.B()
    g.sentence = 'xxx'
Wieso bekomme ich im Editor bei third.py die Meldung 'Import first could not be resolved from source Pylance', bei second aber nicht? Ebenso werden bei first die Instanzvariablen nicht erkannt, bei second aber schon.

Danke und Gruss
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mechanicalStore: Vorweg: In ``./helper_libs/`` sollte es auf jeden Fall eine `__init__.py` geben. Leider ”funktioniert” das mittlerweile auch ohne, aber dann ist das ein Namespace-Package, was manchmal komische Folgen haben kann, weil dann auch Module aus anderen Verzeichnissen da drin auftauchen können.

``as`` beim Importieren ist zum *umbenennen* da, aber die Module werden ja gar nicht umbenannt. Also:

Code: Alles auswählen

from helper_libs import first, second, third
In `third` wird versucht `first` und `second` zu importieren, diese Module gibt es aber überhaupt nicht. Beziehungsweise wenn das funktionieren würde, dann wären das andere Module als `helper_libs.first` und `helper_libs.second`. Man muss die in `third` auch über das Package ansprechen, oder explizit machen, dass das ein relativer Import sein soll.

Code: Alles auswählen

from helper_libs import first, second

# Oder

import .first
import .second
Dieses Arrangement das `main.py` in einem Verzeichnis liegt wo auch ein Package liegt, funktioniert übrigens nur, wenn das aktuelle Arbeitsbverzeichnis immer das ist wo `main.py` liegt. Man kann `main.py` dann nicht von einem anderen Verzeichnis aus aufrufen.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
mechanicalStore
User
Beiträge: 166
Registriert: Dienstag 29. Dezember 2009, 00:09

@__blackjack__: Danke fuer die Erklaerungen!
__blackjack__ hat geschrieben: Samstag 7. Dezember 2024, 11:50

Code: Alles auswählen

import .first
import .second
Hier kommt allerdings die Meldung 'Relative imports cannot be used with "import .a" form; use "from . import a" instead Pylance'. Das wird dann sogar vom Interpreter moniert.
__blackjack__ hat geschrieben: Samstag 7. Dezember 2024, 11:50 Dieses Arrangement das `main.py` in einem Verzeichnis liegt wo auch ein Package liegt, funktioniert übrigens nur, wenn das aktuelle Arbeitsbverzeichnis immer das ist wo `main.py` liegt. Man kann `main.py` dann nicht von einem anderen Verzeichnis aus aufrufen.
Wo soll sie denn sonst liegen? Und dazu noch die Frage; wie mache ich einen vernuenftigen Deploy? Bzw. wie sieht dessen Verzeichnisstruktur dann aus? Solange ich das Zeug nur fuer mich benutze, ist das ja erst mal egal, aber sobald Andere das nutzen wollen?!

Gruss und Danke.
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich würde das alles ins Package tun und das ordentlich installieren, mit Startskripten die über Entry-Points definiert sind.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
mechanicalStore
User
Beiträge: 166
Registriert: Dienstag 29. Dezember 2009, 00:09

__blackjack__ hat geschrieben: Samstag 7. Dezember 2024, 13:02 Ich würde das alles ins Package tun und das ordentlich installieren, mit Startskripten die über Entry-Points definiert sind.
Kannst Du grob die Zusammenhaenge beschreiben? Ich habe zwar die Doku gelesen, werde aber nicht so richtig schlau draus.

Danke und Gruss
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mechanicalStore: Was genau ist denn das Problem? Das ist ja alles ein bisschen umfangreicher. Mehr als auf die Dokumentation und die Tutorials darin zu den verschiedenen Themen verweisen kann ich da ohne konkrete Frage(n) auch nicht. Die Packaging-Dokumentation fängt ja mit einem Überblick über das Thema an.

An welchem Punkt hängt es, was hast Du konkret gemacht, was funktioniert konkret nicht? Wie lautet die Fehlermeldung? Wie weicht das Verhalten von den Erwartungen ab?
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
mechanicalStore
User
Beiträge: 166
Registriert: Dienstag 29. Dezember 2009, 00:09

__blackjack__ hat geschrieben: Freitag 20. Dezember 2024, 11:27 @mechanicalStore: Was genau ist denn das Problem?
Naja, mir fehlt das Grundverständnis. Ich habe mich hier https://setuptools.pypa.io/en/latest/u ... start.html umgesehen. Der Beschreibung entnehme ich, dass man damit Pakete baut, die man danach als installationspaket verwenden (oder nach pypi hochladen) kann. Um es dann wiederum in anderen Programmen zu importieren.
Was ich will ist eigentlich das gleiche, nur ohne Installation, d.h. ein Paket welches sich direkt starten lässt und das alle Abhängigkeiten beinhaltet. Dazu fehlt mir aber generell der grobe Überblick, was überhaupt geht (bzw. welche Möglichkeiten es gibt) und was nicht. Daher fällt es auch schwer, eine gezielte Frage zu stellen. Was ich brauche ist ein grober Überblick, um dann den richtigen Ansatz zu finden und an der richtigen Stelle zu lesen (und dann bei Bedarf entsprechende Fragen zu stellen).

Danke und Gruß
Benutzeravatar
grubenfox
User
Beiträge: 593
Registriert: Freitag 2. Dezember 2022, 15:49

    Ist zwar plötzlich schon ein paar Jahre her dass ich mir die Links heraus gesucht hatte und irgendwie bin ich nicbt dazu gekommen mich damit so zu beschäftigen wie ich mal wollte... Daher ohne Kommentare:
    Benutzeravatar
    __blackjack__
    User
    Beiträge: 13919
    Registriert: Samstag 2. Juni 2018, 10:21
    Wohnort: 127.0.0.1
    Kontaktdaten:

    @mechanicalStore: Lass dieses „ohne Installation“ weg, denn Du willst das mit Installation, sonst hast Du eben genau diese Probleme was man tun muss, damit auch immer alles gefunden wird, insbesondere ohne vom aktuellen Arbeitsverzeichnis abhängig zu sein.
    “Java is a DSL to transform big Xml documents into long exception stack traces.”
    — Scott Bellware
    mechanicalStore
    User
    Beiträge: 166
    Registriert: Dienstag 29. Dezember 2009, 00:09

    __blackjack__ hat geschrieben: Samstag 21. Dezember 2024, 16:02 @mechanicalStore: Lass dieses „ohne Installation“ weg, denn Du willst das mit Installation, sonst hast Du eben genau diese Probleme was man tun muss, damit auch immer alles gefunden wird, insbesondere ohne vom aktuellen Arbeitsverzeichnis abhängig zu sein.
    Danke für den Tipp. Mit sehr viel lesen (und sogar einem recht hilfreichen udemy video) bin ich jetzt weiter gekommen. Jedoch hängt es noch an einer Stelle. Folgende Struktur (und Programm, welches eigentlich nur Blödsinn macht, es dient rein dem Ausprobieren verschiedener Dinge und hat nichts Produktives...). (Einen Tree kann ich ja nicht her malen, daher schreibe ich Pfade und Dateinamen mit in den Code, müsste zum Testen halt entsprechend angelegt werden, falls es nicht zuviel Umstand ist).

    Code: Alles auswählen

    ## ./config.json
    
    {
        "server" : "http://localhost",
        "user" : "ich_selbst",
        "passwd" : "meins"
    }
    
    .

    Code: Alles auswählen

    ## ./lib_work_json/test.py
    
    #/usr/bin/env python
    
    from lib_work_json import WorkJson
    
    def main():
        
        WorkJson().load_json()
        WorkJson().show_example()
        
    if __name__ == '__main__':
        main()

    Code: Alles auswählen

    ## ./lib_work_json/setup.py
    
    from setuptools import setup, find_packages
    
    setup(
        name = 'lib_work_json',
        version = '0.0.1',
        packages = find_packages(),
        install_requires = ['PyQt6==6.8.0', 'PyQt6_sip==13.9.1'],
        entry_points = {
            'console_scripts' : [
                'work_json = lib_work_json.__main__:main',
                'example = lib_work_json.__main__:show_example'
            ]
        }
    )

    Code: Alles auswählen

    ## ./lib_work_json/requirements.txt
    
    PyQt6==6.8.0
    PyQt6_sip==13.9.1
    
    

    Code: Alles auswählen

    ## ./lib_work_json/lib_work_json/__init__.py
    
    from .sub_dir import WorkJson

    Code: Alles auswählen

    ## ./lib_work_json/lib_work_json/__main__.py
    
    from .sub_dir import WorkJson
    
    def main():
        WorkJson().load_json()
        
    def example():
        WorkJson().show_example()
    
    if __name__ == '__main__':
        main()

    Code: Alles auswählen

    ## ./lib_work_json/lib_work_json/sub_dir/__init__.py
    
    from .example import WorkJson

    Code: Alles auswählen

    #/usr/bin/env python
    ## ./lib_work_json/lib_work_json/sub_dir/example.py
    
    from .helper import TESTVAR
    
    import json
    import sys
    from pathlib import Path
    from pprint import pprint
    
    from PyQt6.QtCore import QDate
    
    class WorkJson:
        def __init__(self):
            pass
        
        def load_json(self):
            args = sys.argv
            if len(args) != 2:
                print("Usage <prog> <config-file>")
                sys.exit(1)
    
            # filename = args[1]
            # handle = Path(__file__).parent.joinpath(filename)
            
            # alternative
            handle = args[1]
    
            with open(handle, 'r', encoding='utf-8') as f:
                _data = json.load(f)
            print(_data)
            
            d1 = QDate(1995, 5, 17)
            d2 = QDate(2025, 1, 1)
            print(d1)
            print(d2)
            print(d1.daysTo(d2))
            
            print(TESTVAR)
            
        def show_example(self):
            print('Das ist ein Example')

    Code: Alles auswählen

    ## ./lib_work_json/lib_work_json/sub_dir/helper.py
    
    TESTVAR = 'Das ist ein importierter String'
    Das Ganze läuft in venv Umgebung, alles unter Linux.

    Stehe ich nun in

    Code: Alles auswählen

    ./lib_work_json
    und rufe da

    Code: Alles auswählen

    python test.py ../config.json
    auf, tut es was es soll. Wenn ich im gleichen Pfad mit

    Code: Alles auswählen

    python setup.py bdist_wheel 
    ein Paket baue, wir das auch in

    Code: Alles auswählen

    ./lib_work_json/dist/<paketname.whl> 
    abgelegt und lässt sich mit

    Code: Alles auswählen

    pip install <paketname>.whl
    installieren. pip list zeigt das auch an.
    Zum Test gehe ich nun nach ./ (um zu verhindern, dass nicht der source code gestartet wird) und starte

    Code: Alles auswählen

    dort work_json config.json
    , funktioniert das, wie erwartet. Will ich aber nun den anderen entrypoint (example) starten, kommt die Meldung:

    Code: Alles auswählen

    (.venv) [***@rechnername python]$ example 
    Traceback (most recent call last):
      File "/home/***/develop/git/python/.venv/bin/example", line 5, in <module>
        from lib_work_json.__main__ import show_example
    ImportError: cannot import name 'show_example' from 'lib_work_json.__main__' (/home/***/develop/git/python/.venv/lib/python3.13/site-packages/lib_work_json/__main__.py)
    
    Warum wird der zweite entry point nicht erreicht?

    Danke und Gruß
    Benutzeravatar
    __blackjack__
    User
    Beiträge: 13919
    Registriert: Samstag 2. Juni 2018, 10:21
    Wohnort: 127.0.0.1
    Kontaktdaten:

    @mechanicalStore: Die Funktion `show_example()` gibt's halt einfach nicht. Da ist eine Funktion `example()` — wolltest Du *die* vielleicht verwenden‽
    “Java is a DSL to transform big Xml documents into long exception stack traces.”
    — Scott Bellware
    mechanicalStore
    User
    Beiträge: 166
    Registriert: Dienstag 29. Dezember 2009, 00:09

    __blackjack__ hat geschrieben: Sonntag 12. Januar 2025, 13:01 @mechanicalStore: Die Funktion `show_example()` gibt's halt einfach nicht. Da ist eine Funktion `example()` — wolltest Du *die* vielleicht verwenden‽
    Oh! Vielen Dank. Manchmal sieht man den Wald vor Bäumen nicht... Es funktioniert jetzt.

    Trotzdem die Frage, kann man als Entrypoints auch die Methoden der Klasse direkt angeben, z.B. wenn diese statisch wären?

    Und kann man das Ganze auch per cronjob laufen lassen, in dem man z.B. mit einem bashscript die venv aktiviert, das script startet und danach die venv wieder deaktiviert (wobei die shell ja sowieso abgeschossen würde bei Beendigung), oder gibt es da Einscbränkungen?

    Und unabhängig von der Pfadstruktur im Sourcecode, oder auch nach Installieren des Package, zeigt Path(__file__) immer dahin, wo ich den Aufruf mache, oder dahin, wo das Package installiert wurde?

    Danke und Gruß
    Benutzeravatar
    __blackjack__
    User
    Beiträge: 13919
    Registriert: Samstag 2. Juni 2018, 10:21
    Wohnort: 127.0.0.1
    Kontaktdaten:

    @mechanicalStore: Ich vermute statische Methoden kann man als Einstiegspunkt verwenden, aber da würde ich mich dann schon ernsthaft fragen warum das eine statische ”Methode” ist und nicht einfach eine Funktion.

    Man braucht das venv nicht vorher aktivieren wenn man Programme aus dessen bin/-Ordner aufruft. Die verwenden den Python-Interpreter aus dem venv und damit auch das venv.

    `__file__` enthält den Pfad der Moduldatei.
    “Java is a DSL to transform big Xml documents into long exception stack traces.”
    — Scott Bellware
    mechanicalStore
    User
    Beiträge: 166
    Registriert: Dienstag 29. Dezember 2009, 00:09

    __blackjack__ hat geschrieben: Sonntag 12. Januar 2025, 15:15 @mechanicalStore: Ich vermute statische Methoden kann man als Einstiegspunkt verwenden, aber da würde ich mich dann schon ernsthaft fragen warum das eine statische ”Methode” ist und nicht einfach eine Funktion.
    Weil ich ja die Klasse WorkJson importiere (und sozusagen ueber saemtliche __init__.py's bis obenhin durchreiche), deren Methoden ich dann aufrufe. Ansonsten wuesste ich nicht, was ich importieren soll, wenn das keine Klasse waere.
    __blackjack__ hat geschrieben: Sonntag 12. Januar 2025, 15:15 Man braucht das venv nicht vorher aktivieren wenn man Programme aus dessen bin/-Ordner aufruft. Die verwenden den Python-Interpreter aus dem venv und damit auch das venv.
    Mit den passenden Pfadstrukturen? Ich dachte, dass diese erst durch das aktivieren des venv passend gesetzt wwerden?!
    __blackjack__ hat geschrieben: Sonntag 12. Januar 2025, 15:15 `__file__` enthält den Pfad der Moduldatei.
    Hmmm. Was muesste ich denn dann tun, wenn die config.json da liegt, wo ich den Aufruf tue (also an komplett beliebiger Position im Filesystem), ohne diese als Argument im Aufruf uebergeben (und per args[1]) zu muessen? Und ohne einen absoluten Pfad angeben zu muessen?
    Sirius3
    User
    Beiträge: 18216
    Registriert: Sonntag 21. Oktober 2012, 17:20

    Dass Du nirgendwo self benutzt, ist ja schon ein Zeichen dafür, dass Du keine Klasse brauchst. Und auch wenn Du in einem richtigen Projekt eine Klasse brauchst, weil Du ja anscheinend eine GUI bauen willst, würde ich immer irgendwo eine einfache main-Funktion schreiben, die man als Entry-Point benutzen kann. Außerhalb einer main-Funktion sollte es auch kein sys.exit geben. Eine Funktion load_json, die mehr macht als ein json zu laden, ist komisch. lib sollte nicht im Namen einer Library vorkommen, sonst müßte man ja auch jede Funktion mit func_ anfangen lassen.
    Statt setup.py benutzt man heutzutage pyproject.yaml
    mechanicalStore
    User
    Beiträge: 166
    Registriert: Dienstag 29. Dezember 2009, 00:09

    Ok, danke Euch Beiden für die hilfreichen Infos.

    Gruß
    Antworten