backports für alte Python Version...

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
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Es gibt ja die utils von http://aima.cs.berkeley.edu/python/utils.html Diese bieten einige neue Features auch für ältere Python Versionen...

Das blöde ist nur, das man in jeder Datei ein from utils import * machen muß. Dumm ist, wenn man eh die neuste Version von Python hat und die ganze Reihe von Tests in in jedem Modul überflüssig sind.

Eine andere Möglichkeit ist __builtin__ zu erweitern. Dann ist nur ein einmaliger import der Backports nötig und alle Neuerungen sind überall aktiv. Hier zwei Beispiele:

Code: Alles auswählen

import __builtin__


try: basestring  ## Introduced in 2.3
except NameError:
    import types
    basestring = (types.StringType, types.UnicodeType)

    __builtin__.basestring = basestring


try: enumerate  ## Introduced in 2.3
except NameError:
    def enumerate(collection):
        """Return an iterator that enumerates pairs of (i, c[i]). PEP 279.
        >>> list(enumerate('abc'))
        [(0, 'a'), (1, 'b'), (2, 'c')]
        """
        ## Copied from PEP 279
        i = 0
        it = iter(collection)
        while 1:
            yield (i, it.next())
            i += 1

    __builtin__.enumerate = enumerate
Das Problem sind z.B. die Generatoren... Die gibt es zwar schon in Python 2.2, aber nur mit einem from __future__ import generators und das IMHO nur für das aktuelle Modul und nicht global.

z.B. erhalte ich einen SyntaxError bei der Verwendung von yield... yield ist wohl einer der wenigen Key-Wörter. Somit klappt der Trick mit den __builtin__ nicht...

Wie kann man yield global "aktivieren" ???

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Gromit
User
Beiträge: 51
Registriert: Montag 8. Mai 2006, 19:07

Man könnte einen __import__ hook schreiben, der selbst nach Modul-Dateien sucht, das marshall-Modul verwendet um den Code zu laden, diesen dann mit compile übersetzt und mit exec ausführt. Dabei muß man alle vorhandenen Flags an compile übergeben. Näheres hierzu in der Doku zu __future__: http://docs.python.org/lib/module-future.html

Zumindest für Python 2.2 funktionierts, da die compile finction bereits ein flags-Attribute hat, wie die Doku nahe legt: http://www.python.org/doc/2.2/lib/built ... tml#l2h-10

Bei Python 2.1 ist dies nicht möglich: http://www.python.org/doc/2.1/lib/built ... ml#l2h-165

Aber da konne man auch noch nicht aus der Zukunft importieren.

PS: Irgendwo im PyPy-Code ist sowas versteckt, um Module die PyPy-Builtins auf Interperter-Ebene implementieren, im Voraus zu kompilieren. (Das verbessert die PyPy-Startzeiten gewaltig.)
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ich kann dir zwar noch nicht so ganz folgen, aber das Problem ist doch nicht nur yield und Co., das geht ja zu Not mit den __future__ import...

Probleme gibt es z.B. bei strip([char]):
http://docs.python.org/lib/string-methods.html#l2h-205
Changed in version 2.2.2: Support for the chars argument.
Kann man mit deinem Verfahren da was machen???

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Oh ja, das l/rstrip-Problem habe ich auch gerade. Wenn's dafuer eine Loesung gaebe, waere das echt super.
Gromit
User
Beiträge: 51
Registriert: Montag 8. Mai 2006, 19:07

Nope, das war nur ne (Horror)-Lösung für "wie kann ich automatisch für alle Module alle Features von __future__ importieren?".
Gromit
User
Beiträge: 51
Registriert: Montag 8. Mai 2006, 19:07

Noch ne Antwort.

Man kann zwar auch in Python 2.2 eine Klasse von str ableiten und diese dann auch noch in __builtin__ (monkey)-patchen, aber die Attribute von __builtin__ interessieren die C-Library überhaupt nicht, d.h. fremde Funktionen aus fremden Libs liefern nach wie vor Instanzen von str.

Für die Zukunft wünsche ich mir of attribute wie:
__metastr__ die, wenn definiert vom Interpreter bei der Erzeuung vom
von neuen String-Objekten verwendet werden.

__metafloat__ war auch noch was. "from decimal import Decimal as __metafloat__" wäre die Lösung vieler Probleme.

Das ist zwar noch kein globaler Mechanismus, aber die C-Lib könnte ja auch noch in __builtin__ nachsehen. Leider ist beides ein Riesen-Performance-Problem.

Sorry, mehr fällt mir dazu auch nicht ein (vielleicht hälts ja zumindest ein paar Leute von vergeblichen Experimenten ab).
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Gromit hat geschrieben:die Attribute von __builtin__ interessieren die C-Library überhaupt nicht, d.h. fremde Funktionen aus fremden Libs liefern nach wie vor Instanzen von str.
Das ist doch mal eine Aussage, mit dem ich etwas anfangen kann!

Dann könnte ich das so Regeln, das ich leider auf alle neuen C-Lib-Features verzichten muß, die anderen mache ich dann über __features__ und den __builtin__ verfügbar, soweit wie es geht.

Ich möchte halt nicht nur mit Python 2.2.1 programmieren und den Anschluss an die schöne neue Python-Welt verlieren ;)

Was die strip() geschichte anbelangt, könnte ich mir halt eine strip()-Funktion bauen, die entweder auf das vorhandene neue C-Lib-strip zurück greift oder die aktion komplett zu Fuss macht... Diese Funktion pack ich dann immer in die __builtin__, dann kann ich zwar nicht normal s.rstrip("/") schreiben, sondern muß rstrip(s, "/") schreiben. Das könnte ungefähr so aussehen:

Code: Alles auswählen

import __builtin__

def strip(s, chars=None):
    try:
        return s.strip(chars)
    except:
        print 'NotImplemented!'

__builtin__.strip = strip


print strip("/jep/", "/")
Da ich nur eine zentral zu startendes Skript habe, (WSGI -> den Handler für CGI und Co.) ist das vorgehen mit dem zentralen __builtin__-Patch auch viel praktischer als das __features__-Ding. Ich könnte einfach zwei Handler erstellen, einer für alte Python und einer für ein aktuelles Python...

Für den features-Import hab ich extra einen Patcher geschrieben:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import os, sys

patch = """
# Automatic inserted import:
from __future__ import generators
"""


class Patcher:
    def __init__(self, path):
        print "Patch dir: '%s'" % path
        self.confirmation()

        fileList = self.readDir(path)
        for fileName in fileList:
            self.patchFile(fileName)

    def confirmation(self):
        choice = raw_input(
            "Press y and ENTER to patch all *.py Files ? "
        )
        choice = choice.lower()
        if choice != "y":
            sys.exit()

    def readDir(self, path):
        result = []
        for root, dirs, files in os.walk(path):
            for fileName in files:
                if not fileName.endswith(".py"):
                    continue
                if fileName == "__init__.py":
                    continue

                result.append(
                    os.path.join(root, fileName)
                )
        return result

    def patchFile(self, fileName):
        print fileName
        f = file(fileName, "rU")
        content = f.read()
        f.close()

        lines = content.split("\n")

        status = self.checkNecessary(lines)
        if status == False:
            print "future import not necessary!"
            return

        lines = self.patchFileContent(lines)
        print "Patched!"

        f = file(fileName, "w")
        f.write(
            "\n".join(lines)
        )
        f.close()

    def checkNecessary(self, lines):
        for line in lines:
            if line.startswith("from __future__") or \
                line.startswith("import __future__"):
                return False
            if "yield" in line:
                return True

        return False

    def patchFileContent(self, lines):

        for no, line in enumerate(lines):
            if not line.startswith("#"):
                break

        print no
        lines.insert(no, patch)

        test = lines[:no+5]
        print " -"*30
        print "\n".join(test)
        print " -"*30

        return lines


Patcher(os.getcwd())
Es wird also in den Dateien nur ein from __future__ import generators eingefügt, wenn irgendwo ein yield virkommt...

Der unnötige __future__ import bei einer aktuellen Python-Version soll allerdings auch so gut wie keine Zeit kosten. Weiß da jemand was näheres drüber???

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten