anbindung an sourceforge's CVS

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Wie einfach oder schwer ist es an sourceforge's CVS Daten herran zu kommen?

Ich fänd es nett, wenn man per CGI sich eine aktuelle build machen könnte. In meinem Fall ist das theoretisch recht einfach, weil ich nur alle Dateien auf dem letzten Stand vom CVS Server downloaden müßte. Dann das ganze zu einer ZIP zusammen packen und zum Client schicken...

Ist das CVS Protokoll kompliziert, oder gibt es da fertige Module für? Ich hab bisher nur CVStoys gefunden und schaue mir das mal an...

EDIT: CVSToys benötigt twisted, also kann ich das schonmal vergessen... Ich muß ja eigentlich auch keine gesicherte Verbindung zum CVS Server aufbauen, ein Anonymous Zugang würde doch reichen, oder?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

Warum machst du es nicht einfach mit den CVS Commandozeilen Tools? Die müssen doch sowieso installiert sein.

Code: Alles auswählen

Python 2.3.5 (#1, Sep 10 2005, 19:23:30) 
[GCC 3.3.5-20050130 (Gentoo Linux 3.3.5.20050130-r1, ssp-3.3.5.20050130-1, pie- on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.system( 'cvs co' )
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ich denke der befehl cvs export (oder lautet der anders), ist besser in dem Fall, da dann nicht in jedem Unterordner noch ein Ordner 'CVS' ist.

SVN hat von Haus aus Python-Bindings, noch ein Grund mehr, es einzusetzen :D
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

Leonidas hat geschrieben:SVN hat von Haus aus Python-Bindings [...]
Was meinst du mit von Haus aus?!? Für mich sieht es ehr so aus, als wäre pysvn ein von Subversion unabhängiges Projekt. :roll:
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ich meine dass auf Tigris.org die Python Bindings zu Subversion im gleichen Download-Bereich waren und daher habe ich sie als zu Sbversion zugehörig eingestuft.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

OK, die Kommandozeilen version wäre eine Möglichkeit... Direkt mit dem Server zu kommunizieren ist also nicht möglich?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Doch, sicher, wenn du eine andere Implementation des pserver Protokolls findest, die man von Python nutzen kann.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab rumgesucht, aber nix gefunden....
Hier ist das Protokoll beschrieben:
http://www.elegosoft.com/cvs/cvsclient.html

Werd mal versuchen das umzusetzten... Mal sehen wie weit ich komme :?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Setz dir noch einen CVS Server auf zum testen und nimm dir nen Paketsniffer. Ist manchmal wahnsinnig praktisch.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hast recht, ich stolper schon über das Passwort :? Ich weiß nicht was ich als Passwort senden muß, wenn ich als anonymous rein gehe... Ich erhalte nur:
cvs [pserver aborted]: descramble: unknown scrambling method
Hier mein Code:
(EDIT: Source gelöscht. Neuere Version weiter unten...)

Schau mit gerade http://www.networkchemistry.com/products/packetyzer/ an...
Zuletzt geändert von jens am Sonntag 18. September 2005, 21:52, insgesamt 1-mal geändert.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

jens hat geschrieben:Hast recht, ich stolper schon über das Passwort :? Ich weiß nicht was ich als Passwort senden muß, wenn ich als anonymous rein gehe... Ich erhalte nur:
cvs [pserver aborted]: descramble: unknown scrambling method
Steht doch hier.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Das hab ich auch schon gesehen... Ich wußte nur nicht, welches Passwort denn überhaupt bei anonymous genommen werden muß. Bei sf.net hab ich allerdings gerad nachgelesen das ein einfaches ENTER (also nix) genügt...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mal was ganz anderes: Warum schreibst du immer zwischen die Funktionsklammern Leerzeichen? Also zum Beispiel: self.sock.recv( 1024 ) statt self.sock.recv(1024). Ich kenne niemanden sonst, der das so schreibt.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Leonidas hat geschrieben:Mal was ganz anderes: Warum schreibst du immer zwischen die Funktionsklammern Leerzeichen? Also zum Beispiel: self.sock.recv( 1024 ) statt self.sock.recv(1024). Ich kenne niemanden sonst, der das so schreibt.
Hm... Weiß nicht... Hab ich mir so angewöhnt... Schlechter Stile?

Ich kann mich nun anmelden! Das Passwort wird anscheinend immer mit einem "A" eingeleitet. Also einfach "A\n" senden...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

jens hat geschrieben:
Leonidas hat geschrieben:Mal was ganz anderes: Warum schreibst du immer zwischen die Funktionsklammern Leerzeichen? Also zum Beispiel: self.sock.recv( 1024 ) statt self.sock.recv(1024). Ich kenne niemanden sonst, der das so schreibt.
Hm... Weiß nicht... Hab ich mir so angewöhnt... Schlechter Stile?
PEP 8 hat geschrieben:Guido hates whitespace in the following places:

- Immediately inside parentheses, brackets or braces, as in:
"spam( ham[ 1 ], { eggs: 2 } )". Always write this as
"spam(ham[1], {eggs: 2})".
Die meisten halten sich daran. Ich versuche mich auch an die meisten Sachen zu halten, nur eine Ausnahme mache ich mit voller Absicht (ich grupiere Module beim Import nach Herkuft, also stdlib, gtk usw. weil ich das sehr informativ finde, besonder für Leute die meinen Code lesen).
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Leonidas hat geschrieben:ich grupiere Module beim Import nach Herkuft, also stdlib, gtk usw.
Das finde ich auch besser so... Was ist eigentlich bei Funktions-def? Auch dort statt

Code: Alles auswählen

def send( self, command ):
besser:

Code: Alles auswählen

def send(self, command):
:?:

Zurück zu CVS... Ich kann mich nun connecten:

(EDIT: Altes Listing gelöscht, s. unten)

Der sf.net Server scheint, aber dauer überlastet zu sein... Ich installiere mir mal einen lokalen Server, dann kann ich besser testen... Ich weiß nur nicht, ob mein vorhaben überhaupt Erfolgversprechend ist, oder ob ich mich direkt das Kommandozeilentool "cvs" nutzten sollte... Wobei ich das auf dem WebServer natürlich nicht haben dürfte... Andererseits könnte ich das direkt auf sf.net-WebServer machen... Weiß nur nicht ob die das so toll finden würden :?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So, lokaler CVS läuft... Das Pycketyzer Progrämmchen ist auch recht cool! Kann ich weiterempfehlen...

Ich hab's nun soweit, das ich connecten und ein export zumindest anfangen kann... Nun hackelt es ein wenig... Dabei fällt mir ein, das ich bei Hosteurope, glaub ich, keine sockets öffnen darf :( Aber das muß ich mal checken...

Hier der aktuelle Code:

(EDIT: Code gelöscht...)

Bin kein Experte, im Client <-> Server Programmierung... Hat jemand verbesserungs Vorschläge für mich?

EDIT: Code aktualisiert:
Es hackelte, weil ich einfach nicht versucht hab, Daten weiter zu empfangen :roll:
Also ich bekomme nun die Daten geliefert, weiß allerdings noch nicht so ganz, wie ich diese vernünftig weiter verarbeiten kann :(
Zuletzt geändert von jens am Montag 19. September 2005, 08:51, insgesamt 1-mal geändert.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

jens hat geschrieben:Auch dort statt

Code: Alles auswählen

def send( self, command ):
besser:

Code: Alles auswählen

def send(self, command):
:?:
Ja.
jens hat geschrieben:Der sf.net Server scheint, aber dauer überlastet zu sein...
Ja, ich kenne das Problem.
jens hat geschrieben:So, lokaler CVS läuft... Das Pycketyzer Progrämmchen ist auch recht cool! Kann ich weiterempfehlen...
Klar, nutzt ja auch die exzellente Ethereal-Engine.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Also ich komm nicht ganz weiter... Ich habe es nun soweit, das ich die Daten bekomme... Aber mit der Auswertung ist es nicht ganz einfach...

Es scheint so, das die Daten auch immer wieder unterschiedlich eintreffen (Und das liegt nicht daran, das ich zwischenzeitlich auf dem CVS-Server was geändert hätte :roll: )

Der Dateiname wird mit "M U " eingeleitet (ob das immer so ist, weiß ich allerdings auch nicht)
Danach kommen ein paar Zeilen (alles getrennt mit "\n") mit ein paar CVS Daten...
Zum schluß kommt dann die Dateigröße in Bytes und danach die eigentlichen Nutzdaten...

Das verarbeiten der Daten klappt bei mir allerdings nicht wirklich.... Je nach Blockgröße erkenne ich mehr oder weniger Dateien :cry:

(EDIT: Code gelöscht)

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

So, der export funktioniert nun richtig! Die Dateien werden auf Platte geschrieben...

Hier der aktuelle Code:

Code: Alles auswählen

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

__author__  = "Jens Diemer (www.jensdiemer.de)"
__license__ = "GNU General Public License (GPL)"

"""
CVS-pserver-Client

* Only export implemented

pserver Protokoll Beschreibung:
http://www.elegosoft.com/cvs/cvsclient.html
"""

__version__="0.1.0"

__history__="""
    v0.1.0
        - erste Version, export Funktioniert.
"""

#~ print "Content-type: text/html; charset=utf-8\r\n"
#~ import cgitb;cgitb.enable()
import os, sys, socket


class pserver:
    def __init__(self, blocklen=1024, debug=False):
        self.blocklen = blocklen
        self.debug = debug

    def connect(self, host, port, timeout=30):
        print "connect...",
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.connect((host, port))

        try:
            self.sock.settimeout(timeout)
        except AttributeError:
            # Geht erst ab Python 2.3 :(
            pass

        print self.sock.getpeername(),
        print "OK"

    def auth_anonymous(self, repos_path):
        print "auth...",
        self.sock.send("BEGIN AUTH REQUEST\n")
        self.sock.send("%s\n" % repos_path) # Repository Path
        self.sock.send("anonymous\n")       # Username
        self.sock.send("A\n")               # Password
        self.sock.send("END AUTH REQUEST\n")
        result = self.recv_lines()
        if result[0] != "I LOVE YOU":
            # "login" nicht erfolgreich
            print "Error:"
            print "-"*80
            print "\n".join(result)
            print "-"*80
            sys.exit()
        else:
            print "OK"

    def print_valid_requests(self):
        result = self.process("valid-requests")[0]
        result = result.split()
        print "\n".join(result)

    def send(self, command):
        if self.debug: print "send '%s'..." % command,
        self.sock.send("%s\n" % command)
        if self.debug: print "OK"

    def recv_lines(self, buffer=1024):
        result = self.sock.recv(1024)
        return result.splitlines()

    def process(self, command, buffer=1024):
        self.send(command)
        return self.recv_lines(buffer)

    def close(self):
        print "close...",
        self.sock.close()
        print "OK"

    #__________________________________________________________________
    # Hight-level methods

    def export(self, module_name, directory, dest_path):
        self.root_dir = directory
        self.send("UseUnchanged")
        self.send("Root %s" % self.root_dir)
        #~ self.send("Global_option -q")
        #~ self.send("Argument %s" % module_name)
        #~ self.send("Directory .")
        #~ self.send(directory)
        #~ self.send("expand-modules")
        #~ print "export: ", self.recv_lines()
        #~ print

        self.send("Argument -N")
        self.send("Argument -r")
        self.send("Argument HEAD")
        self.send("Argument %s" % module_name)
        self.send("Directory .")
        self.send(directory)
        self.send("export")

        self.get_files(module_name, dest_path)

    def get_files(self, module_name, dest_path):
        print "-"*80
        block = ""
        while 1:
            new_data = self.sock.recv(self.blocklen)
            block += new_data

            while 1:
                try:
                    current, block = block.split("\n",1)
                except ValueError:
                    break

                if current[:4] == "M U ": # Dateinamen
                    file_name = current[4:]
                    continue

                if current[:2] == "E ": # neues Verzeichnis
                    continue

                if current.endswith("THEAD"): # Datei-Versionsnummer
                    #~ print current
                    continue

                if current.startswith( "u=" ): # Dateirechte
                    continue

                if current.startswith( "Updated" ):
                    # Verzeichnis, relativ
                    continue

                if current.startswith( self.root_dir ):
                    # absoluter CVS-Pfad der Datei
                    continue

                try:
                    # Der Header wird abgeschlossen mit das Angabe
                    # der Dateigröße, danach folgt der Dateiinhalt
                    file_len = int( current )
                    #~ print ">file_len:", file_len
                except:
                    # Die eigentlichen Daten kommen noch nicht
                    # Wir sind noch im Header
                    #~ print "\t",current
                    continue

                readed_bytes = len(block)
                rest_bytes = file_len - readed_bytes

                if rest_bytes<0:
                    # Alle Daten zur Datei sind schon im aktuellen Block
                    self.write_file( file_name, dest_path, block[:rest_bytes], file_len )
                    # Restlichen Bytes für den nächsten Durchlauf aufbewahren
                    block = block[rest_bytes:]
                    break

                current_blocklen = self.blocklen
                while 1:
                    # Restlichen Bytes in Blöcke einlesen
                    if rest_bytes<current_blocklen:
                        # Letzter Block ist kleiner als die normale Blockgröße
                        current_blocklen = rest_bytes

                    new_data = self.sock.recv(current_blocklen)
                    current_bytes = len(new_data)

                    readed_bytes    += current_bytes
                    rest_bytes      -= current_bytes

                    block += new_data

                    if rest_bytes<=0:
                        # Alle Daten gelesen
                        break

                self.write_file(file_name, dest_path, block, file_len)
                block = ""
                break

            if new_data == "ok\n":
                # Alle Dateien wurden empfangen
                print "ENDE"
                return

    def write_file( self, file_name, dest_path, content, file_len ):
        content_len = len(content)

        if content_len != file_len:
            print "+++ Error '%s' +++" % file_name
            print "content_len != file_len:", content_len, file_len
            return

        complete_path = os.path.join( dest_path, file_name )
        path = os.path.split( complete_path )[0]

        if not os.path.isdir(path):
            os.makedirs(path)

        print "write '%s' %s Bytes..." % (file_name, content_len),

        f=file(complete_path, "wb")
        f.write(content)
        f.close()

        print "OK"



host        = "cvs.sourceforge.net"
repos_path  = "/cvsroot/pylucid"
timeout=30

port = 2401

cvs = pserver(blocklen=1024, debug=True)
cvs.connect(host, port, timeout)
cvs.auth_anonymous(repos_path)

#~ print cvs.print_valid_requests()

cvs.export(
    module_name = "PyLucid_tarball",
    directory   = repos_path,
    dest_path   = r"d:\temp\test"
)

cvs.close()

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