Wybór wersji interpretera na podstawie wersji skryptu pythona

0

Mam zainstalowane 2 wersje Pythona na swoim windowsie do którego prowadzą odpowiednie zmienne:

> python --version
Python 3.7.1
> python2 --version
Python 2.7.15

Mam też listę projektów w nie wiadomo jakich wersjach (2 lub 3) a ich kodu nie mogę modyfikować (więc rozwiązania typu dodanie python_version() odpadają). To co potrzebuję zrobić to wiedzieć w jakiej wersji został napisany każdy z projektów.
Jest na to jakieś sensowne rozwiązanie?

0

Jak masz te projekty zrobione? Tzn. masz kod źródłowy (chociaż nie możesz go modyfikować) w ogóle, czy tylko pyc? Jeszcze co innego?

Jak masz kod, to możesz go przejrzeć w poszukiwaniu struktur, które są inne w różnych wersjach tego języka (np. łatwym do zauważenia jest print czy xrange).

0

Nie mogę w ogóle ich modyfikować. Szukanie struktur i patrzenie jak implementowany jest print wymaga ode mnie ręcznego przeglądania tych projektów a tego robić nie chcę. Chcę napisać coś takiego że podklejam link do katalogu z projektami i na wyjściu dostaję wersję każdego z nich bez ich modyfikacji.

0

To takiej magii nie ma. Twórca kodu mógł wyrazić wprost, jakiej wersji Pythona używał (np. #!/usr/bin/env python3.7), ale nie musiał.

Najbliżej automatyzacji byłoby odpalanie z różnymi wersjami i patrzenie, która zadziała.

0

Sęk w tym że właśnie takiej magii potrzebuję ;) To założenie też mi nie pomaga bo nie mam pewności że ktoś to zdefiniował.
Póki co też jedyne co udało mi się uzyskać to odpalanie wszystkiego przez jeden i drugi interpreter i patrzenie co się wysypuje natomiast jest to dość czasochłonne rozwiązanie.
Udało mi się trafić jeszcze na to:
https://stackoverflow.com/questions/40886456/how-to-detect-if-code-is-python-3-compatible
Próbuję to zweryfikować ale jakoś tak działa mi jak chce i raz faktycznie w przypadku pythona 2 pokazał komunikat o błędzie ale teraz nie wiem dlaczego bez problemu w przypadku obu wersji kompiluje się bez żadnych problemów... Może ktoś ma jeszcze jakieś rozwiązanie?

0

Napisz skrypt w pythonie który:

  • Otwiera pliki źrodłowe
  • Testuje kilka znanych różnic (jak print czy xrange)

;]

0

Napisz sobie skrypt, który skanuje te pliki i szuka rzeczy typu: print <sth>, xrange, range, i markuje je zgodnie z wynikami.

0

Ok, to możę spróbuję jeszcze w te stronę pójść. @Shalom lub @lion137 jeśli przelecę sobie po tych plikach i znajdę np. coś takiego print 'Hello' to czy mogę wówczas uznać że cały projekt jest kompatybilny z Pythonem 2 i dalej nie ma sensu nic szukać? Pytanie może trochę głupie ale piszę w javie a to taki dodatkowy task z pythonem więc wolę się upewnić ;)

1

No nie wiadomo czy jest kompatybilny z pythonem 2 ale na pewno nie jest z pythonem 3 ;)

1

Zgadzam się z przedmówcą:), jeśli Znajdziesz co najmniej jedno print bez nawiasów albo xrange, to na pewno nie jest to trójka.

1

xrange nie koniecznie, bo np zdarza się, że projekty robią coś takiego:

try:
    xrange
except NameError:
    xrange = range

Ogólne rekomendacje są takie, by użyć modułu AST, by skutecznie wykryć zmiany składniowe (print bez nawiasów, dozwolona mieszana intendacja wskazują na dwójkę). Innymi słowy, użyj pythona 3 do parsowania, jak dostaniesz błąd składniowy, to wiesz że raczej działa na Pythonie 2. W innych przypadkach zostaje heurystyka.

0

Jak widać, niestety, nie jest to takie proste.

0
enedil napisał(a):

Ogólne rekomendacje są takie, by użyć modułu AST, by skutecznie wykryć zmiany składniowe (print bez nawiasów, dozwolona mieszana intendacja wskazują na dwójkę). Innymi słowy, użyj pythona 3 do parsowania, jak dostaniesz błąd składniowy, to wiesz że raczej działa na Pythonie 2.

Dokładnie w te stronę póki co postanowiłem pójść.
Na początek ma taki skrypt:

import ast


def code_compatible(code_data):
    try:
        ast.parse(code_data)
        return 'Python 3'
    except SyntaxError as exc:
        return 'Python 2'


filePath = 'somePath'
compatible = code_compatible(open(filePath).read())

print(compatible)

I okazuje się że radzi sobie dobrze. Nie piszę na codzień w Pythonie więc może są jakieś takie case'y o których nie wiem ale w przypadku range czy print radzi sobie wporządku.
Teraz żeby to miało sens wydaje mi się że trzeba by cały projekt prześledzić w poszukiwaniu plików *.py a następnie lecieć po kolei na kazdym takie wywołanie. Jeśli poleci Python 2 to wówczas można przerwać. Jeszcze tylko nie wymyśliłem jak to sensownie zrobić ale jest już jakiś punkt wyjścia ;)

0

Mały update. W Pythonie tak jak już pisałem nie piszę na co dzień więc powiedzcie mi proszę czy takie rozwiązanie ma sens:

import ast
import os


def file_compatibility(file_path):
    try:
        ast.parse(file_path)
        return 3
    except SyntaxError as exc:
        return 2


def project_compatibility(dir_path):
    for root, dirs, files in os.walk(dir_path):
        for file in files:
            if file.endswith(".py"):
                file_path = os.path.join(root, file)
                compatibility = file_compatibility(open(file_path).read())
                if compatibility == 2:
                    return 2
    return 3


dirPath = "../scripts"
print("Project compatible with Python " + str(project_compatibility(dirPath)))

Dodam tylko że generalnie działa, testowałem na printach i range i wyniki są takie jakie oczekuję... Są jakieś przypadki w których może się to wysypać albo w ogóle czy jest coś co może sprawić że nie ma to sensu?

0
import ast
import os


def file_compatibility(file_path):
    try:
        ast.parse(file_path)
        return 3
    except SyntaxError as exc:
        return 2


def project_compatibility(dir_path):
    for root, dirs, files in os.walk(dir_path):
        for file in files:
            if file.endswith(".py"):
                file_path = os.path.join(root, file)
                compatibility = file_compatibility(open(file_path).read())
                if compatibility == 2:
                    return 2
    return 3


dirPath = "../scripts"
print("Project compatible with Python " + str(project_compatibility(dirPath)))

Trochę nie widzę sensu, po co tutaj ta zmienna compatibility jest. Nie prościej od razu zwracać. Poza tym, uważaj na ten return 3, bo w ten sposób, dla wszystkich plików bez rozszerzenia *.py zwracasz, wartość 3. Poza tym jeszcze "ładniejszą" opcją, jest zastąpić tą końcówkę

 def project_compatibility(dir_path):
    for root, dirs, files in os.walk(dir_path):
         for file in files:
             if file.endswith(".py"):
                 file_path = os.path.join(root, file)
                 yield (file_path, file_compatibility(open(file_path).read()))

Bo tak, to zwracasz tylko dla pierwszego napotkanego pliku.

0
CorvusEtiam napisał(a):

Trochę nie widzę sensu, po co tutaj ta zmienna compatibility jest. Nie prościej od razu zwracać.

Właśnie wydaje mi się że nie. Trzeba przelecieć cały projekt wraz z podkatalogami sprawdzając wszystkie skrypty. Jeśli chociaż raz zwróci 2 to wówczas od razu można przerwać sprawdzanie i zwrócić 2. W przeciwnym wypadku trzeba szukać dalej.

CorvusEtiam napisał(a):

Poza tym, uważaj na ten return 3, bo w ten sposób, dla wszystkich plików bez rozszerzenia *.py zwracasz, wartość 3.

W sumie to chyba możemy przyjąć takie założenie. Chyba że są też inne elementy które nie są przechowywane w plikach *.py a mogą świadczyć o tym że skrypt jest kompatybilny tylko z Pythonem 2?

CorvusEtiam napisał(a):

Poza tym jeszcze "ładniejszą" opcją, jest zastąpić tą końcówkę

... twoj kod

Bo tak, to zwracasz tylko dla pierwszego napotkanego pliku.

No tak tylko że właśnie tak nie powinno być że jeśli znajdę chociaż 1 plik kompatybilny z Python 2 to projekt jest w Python 2 a w innym przypadku muszę przelecieć wszystkie pliki wraz z podkatalogami?

0

Ok, jeśli twój projekt jest dobrze zorganizowany, tak że na każdy folder, cała jego zawartość to pliki pod python3 lub 2 to oczywiście. Jeśli masz po prostu zbiór mało powiązanych skryptów to moje rozwiązanie jest moim zdaniem bezpieczniejsze.

Wyciągnij log dla wszystkich katalogów i przejrzyj wybiórczo, czy się zgadzają. To jedyna opcja. Chyba, że masz gdzieś projekt mieszany 2<>3. To wtedy, wracasz do punktu wyjścia. Poza tym, z tym returnem na samym końcu pętli, jeśli folder nie zawiera plików *.py to dostaniesz błędną informację, ale skoro nie masz takiego problemu, to myślę, że poza tym jest ok.
Zastanów się nad zwracaniem tupli ( nazwa_sprawdzonego_pliku, wersja_pythona), łatwiej później dojść, które pliki były skanowane.
Albo po prostu wrzuć moduł logging i zapisz do loga, te informacje, w razie jakby co nie działało.

* projekt1
* projekt2
     + projekt2a
     + projekt2b   

Gdzie, projekt2 i 2b mają różne wymagane wersje, też jest problemem, chyba, że u ciebie taka sytuacji nie występuje.

0
CorvusEtiam napisał(a):

Ok, jeśli twój projekt jest dobrze zorganizowany, tak że na każdy folder, cała jego zawartość to pliki pod python3 lub 2 to oczywiście. Jeśli masz po prostu zbiór mało powiązanych skryptów to moje rozwiązanie jest moim zdaniem bezpieczniejsze.

A to w ogóle możliwe?
Jeśli mamy projekt w którym wymieszane są skrypty z Python 2 i Python 3 to przecież do ich uruchomienia potrzebne są oba interpretety i trzeba wiedzieć dokładnie który jak urchamiać. Ktoś w ogóle tak pisze projekty? Przecież tego się pewnie utrzymać nie da.

1 użytkowników online, w tym zalogowanych: 0, gości: 1