diff options
-rw-r--r-- | CHANGES | 7 | ||||
-rw-r--r-- | RepSys/log.py | 6 | ||||
-rw-r--r-- | RepSys/mirror.py | 2 | ||||
-rw-r--r-- | RepSys/pexpect.py | 1052 | ||||
-rw-r--r-- | RepSys/rpmutil.py | 27 | ||||
-rw-r--r-- | RepSys/svn.py | 79 |
6 files changed, 44 insertions, 1129 deletions
@@ -1,4 +1,11 @@ * 1.6.20 +- dropped all authenticated access support: subversion authentication has + been broken for a long time and the workarounds weren't decent. It will + be back in 1.7.x. +- added configuration option svn-command in the global section, allowing + to replace the default svn command +- force svn+ssh:// URLs to be in BatchMode, in order to not have any + interactivity at all with ssh - fixed bad url used when using -v in getsrpm - make 'repsys submit' without package name or revision number work again - the fix for the unreleased commits problem in the previous release was diff --git a/RepSys/log.py b/RepSys/log.py index 730c674..df7bc9f 100644 --- a/RepSys/log.py +++ b/RepSys/log.py @@ -53,7 +53,7 @@ def getrelease(pkgdirurl, rev=None, macros=[], exported=None): will be used. """ from RepSys.rpmutil import rpm_macros_defs - svn = SVN(baseurl=pkgdirurl) + svn = SVN() pkgcurrenturl = os.path.join(pkgdirurl, "current") specurl = os.path.join(pkgcurrenturl, "SPECS") if exported is None: @@ -377,7 +377,7 @@ def svn2rpm(pkgdirurl, rev=None, size=None, submit=False, template=None, macros=[], exported=None): concat = config.get("log", "concat", "").split() revoffset = get_revision_offset() - svn = SVN(baseurl=pkgdirurl) + svn = SVN() pkgreleasesurl = os.path.join(pkgdirurl, "releases") pkgcurrenturl = os.path.join(pkgdirurl, "current") releaseslog = svn.log(pkgreleasesurl, noerror=1) @@ -471,7 +471,7 @@ def specfile_svn2rpm(pkgdirurl, specfile, rev=None, size=None, # Merge old changelog, if available oldurl = config.get("log", "oldurl") if oldurl: - svn = SVN(baseurl=pkgdirurl) + svn = SVN() tmpdir = tempfile.mktemp() try: pkgname = RepSysTree.pkgname(pkgdirurl) diff --git a/RepSys/mirror.py b/RepSys/mirror.py index 0c45c9e..4fb40ce 100644 --- a/RepSys/mirror.py +++ b/RepSys/mirror.py @@ -45,7 +45,7 @@ def enabled(wcurl=None): return enabled def mirror_relocate(oldparent, newparent, url, wcpath): - svn = SVN(noauth=True) + svn = SVN() newurl = relocate_path(oldparent, newparent, url) svn.switch(newurl, url, path=wcpath, relocate="True") return newurl diff --git a/RepSys/pexpect.py b/RepSys/pexpect.py deleted file mode 100644 index 02a14f5..0000000 --- a/RepSys/pexpect.py +++ /dev/null @@ -1,1052 +0,0 @@ -""" -Pexpect is a Python module for spawning child applications; -controlling them; and responding to expected patterns in their output. -Pexpect can be used for automating interactive applications such as -ssh, ftp, passwd, telnet, etc. It can be used to a automate setup scripts -for duplicating software package installations on different servers. -It can be used for automated software testing. Pexpect is in the spirit of -Don Libes' Expect, but Pexpect is pure Python. Other Expect-like -modules for Python require TCL and Expect or require C extensions to -be compiled. Pexpect does not use C, Expect, or TCL extensions. It -should work on any platform that supports the standard Python pty -module. The Pexpect interface focuses on ease of use so that simple -tasks are easy. - -Pexpect is Open Source, Free, and all Good that stuff. -License: Python Software Foundation License - http://www.opensource.org/licenses/PythonSoftFoundation.html - -Noah Spurrier - -$Revision$ -$Date$ -""" - - -try: - import os, sys - import select - import string - import re - import struct - import resource - from types import * - import pty - import tty - import termios - import fcntl -except ImportError, e: - raise ImportError, str(e) + """ -A critical module was not found. Probably this OS does not support it. -Currently pexpect is intended for UNIX operating systems.""" - - - -__version__ = '0.99' -__revision__ = '$Revision$' -__all__ = ['ExceptionPexpect', 'EOF', 'TIMEOUT', 'spawn', 'run', - '__version__', '__revision__'] - - - -# Exception classes used by this module. -class ExceptionPexpect(Exception): - """Base class for all exceptions raised by this module.""" - def __init__(self, value): - self.value = value - def __str__(self): - return `self.value` -class EOF(ExceptionPexpect): - """Raised when EOF is read from a child.""" -class TIMEOUT(ExceptionPexpect): - """Raised when a read time exceeds the timeout.""" -##class MAXBUFFER(ExceptionPexpect): -## """Raised when a scan buffer fills before matching an expected pattern.""" - - - -def run (command, args=[], timeout=30): - """This runs a command; waits for it to finish; then returns - all output as a string. This is a utility interface around - the spawn class. - """ - child = spawn(command, args, timeout) - child.expect (EOF) - return child.before - -class spawn: - """This is the main class interface for Pexpect. Use this class to - start and control child applications. - """ - - def __init__(self, command, args=[], timeout=30): - """This is the constructor. The command parameter may be a string - that includes a command and any arguments to the command. For example: - p = pexpect.spawn ('/usr/bin/ftp') - p = pexpect.spawn ('/usr/bin/ssh user@example.com') - p = pexpect.spawn ('ls -latr /tmp') - You may also construct it with a list of arguments like so: - p = pexpect.spawn ('/usr/bin/ftp', []) - p = pexpect.spawn ('/usr/bin/ssh', ['user@example.com']) - p = pexpect.spawn ('ls', ['-latr', '/tmp']) - After this the child application will be created and - will be ready to talk to. For normal use, see expect() and - send() and sendline(). - - If the command parameter is an integer AND a valid file descriptor - then spawn will talk to the file descriptor instead. This can be - used to act expect features to any file descriptor. For example: - fd = os.open ('somefile.txt', os.O_RDONLY) - s = pexpect.spawn (fd) - The original creator of the file descriptor is responsible - for closing it. Spawn will not try to close it and spawn will - raise an exception if you try to call spawn.close(). - """ - - self.STDIN_FILENO = sys.stdin.fileno() - self.STDOUT_FILENO = sys.stdout.fileno() - self.STDERR_FILENO = sys.stderr.fileno() - - self.stdin = sys.stdin - self.stdout = sys.stdout - self.stderr = sys.stderr - - self.timeout = timeout - self.child_fd = -1 # initially closed - self.__child_fd_owner = None - self.exitstatus = None - self.pid = None - self.log_file = None - self.before = None - self.after = None - self.match = None - self.softspace = 0 # File-like object. - self.name = '' # File-like object. - self.flag_eof = 0 - - # NEW -- to support buffering -- the ability to read more than one - # byte from a TTY at a time. See setmaxread() method. - self.buffer = '' - self.maxread = 1 # Maximum to read at a time - ### IMPLEMENT THIS FEATURE!!! - # anything before maxsearchsize point is preserved, but not searched. - #self.maxsearchsize = 1000 - - # If command is an int type then it must represent an open file descriptor. - if type (command) == type(0): - try: # Command is an int, so now check if it is a file descriptor. - os.fstat(command) - except OSError: - raise ExceptionPexpect, 'Command is an int type, yet is not a valid file descriptor.' - self.pid = -1 - self.child_fd = command - self.__child_fd_owner = 0 # Sets who is reponsible for the child_fd - self.args = None - self.command = None - self.name = '<file descriptor>' - return - - if type (args) != type([]): - raise TypeError, 'The second argument, args, must be a list.' - - if args == []: - self.args = _split_command_line(command) - self.command = self.args[0] - else: - self.args = args - self.args.insert (0, command) - self.command = command - self.name = '<' + reduce(lambda x, y: x+' '+y, self.args) + '>' - - self.__spawn() - - def __del__(self): - """This makes sure that no system resources are left open. - Python only garbage collects Python objects. OS file descriptors - are not Python objects, so they must be handled explicitly. - If the child file descriptor was opened outside of this class - (passed to the constructor) then this does not close it. - """ - try: # CNC:2003-09-23 - if self.__child_fd_owner: - self.close() - except OSError: - pass - - def __spawn(self): - """This starts the given command in a child process. This does - all the fork/exec type of stuff for a pty. This is called by - __init__. The args parameter is a list, command is a string. - """ - # The pid and child_fd of this object get set by this method. - # Note that it is difficult for this method to fail. - # You cannot detect if the child process cannot start. - # So the only way you can tell if the child process started - # or not is to try to read from the file descriptor. If you get - # EOF immediately then it means that the child is already dead. - # That may not necessarily be bad, because you may haved spawned a child - # that performs some task; creates no stdout output; and then dies. - # It is a fuzzy edge case. Any child process that you are likely to - # want to interact with Pexpect would probably not fall into this - # category. - # FYI, This essentially does a fork/exec operation. - - assert self.pid == None, 'The pid member is not None.' - assert self.command != None, 'The command member is None.' - - if _which(self.command) == None: - raise ExceptionPexpect ('The command was not found or was not executable: %s.' % self.command) - - try: - self.pid, self.child_fd = pty.fork() - except OSError, e: - raise ExceptionPexpect('Pexpect: pty.fork() failed: ' + str(e)) - - if self.pid == 0: # Child - try: # Some platforms do not like setwinsize (Cygwin). - self.child_fd = sys.stdout.fileno() - self.setwinsize(24, 80) - except: - pass - # Do not allow child to inherit open file descriptors from parent. - max_fd = resource.getrlimit(resource.RLIMIT_NOFILE)[0] - for i in range (3, max_fd): - try: - os.close (i) - except OSError: - pass - - os.environ["LANG"] = "C" - os.environ["LANGUAGE"] = "C" - os.environ["LC_ALL"] = "C" - os.execvp(self.command, self.args) - - # Parent - self.__child_fd_owner = 1 - - def fileno (self): # File-like object. - """This returns the file descriptor of the pty for the child.""" - return self.child_fd - - def close (self, wait=1): # File-like object. - """ This closes the connection with the child application. - It makes no attempt to actually kill the child or wait for its status. - If the file descriptor was set by passing a file descriptor - to the constructor then this method raises an exception. - Note that calling close() more than once is valid. - This emulates standard Python behavior with files. - If wait is set to True then close will wait - for the exit status of the process. Doing a wait is a blocking call, - but this usually takes almost no time at all. Generally, - you don't have to worry about this. If you are - creating lots of children then you usually want to call wait. - Only set wait to false if you know the child will - continue to run after closing the controlling TTY. - Otherwise you will end up with defunct (zombie) processes. - """ - if self.child_fd != -1: - if not self.__child_fd_owner: - raise ExceptionPexpect ('This file descriptor cannot be closed because it was not created by spawn. The original creator is responsible for closing it.') - self.flush() - os.close (self.child_fd) - if wait: - os.waitpid (self.pid, 0) - self.child_fd = -1 - self.__child_fd_owner = None - - def flush (self): # File-like object. - """This does nothing. It is here to support the interface for a File-like object. - """ - pass - - def isatty (self): # File-like object. - """This returns 1 if the file descriptor is open and - connected to a tty(-like) device, else 0. - """ - return os.isatty(self.child_fd) - - def setecho (self, on): - """This sets the terminal echo mode on or off.""" - new = termios.tcgetattr(self.child_fd) - if on: - new[3] = new[3] | termios.ECHO # lflags - else: - new[3] = new[3] & ~termios.ECHO # lflags - termios.tcsetattr(self.child_fd, termios.TCSADRAIN, new) - - def setlog (self, fileobject): - """This sets logging output to go to the given fileobject. - Set fileobject to None to stop logging. - Example: - child = pexpect.spawn('some_command') - fout = file('mylog.txt','w') - child.setlog (fout) - ... - """ - self.log_file = fileobject - - def setmaxread (self, maxread): - """This sets the maximum number of bytes to read from a TTY at one time. - This is used to change the read buffer size. When a pexpect.spawn - object is created the default maxread is 1 (unbuffered). - Set this value higher to turn on buffer. This should help performance - in cases where large amounts of output are read back from the child. - """ - self.maxread = maxread - - def read_nonblocking (self, size = 1, timeout = None): - """ - This reads at most size characters from the child application. - It includes a timeout. If the read does not complete within the - timeout period then a TIMEOUT exception is raised. - If the end of file is read then an EOF exception will be raised. - If a log file was set using setlog() then all data will - also be written to the log file. - - Notice that if this method is called with timeout=None - then it actually may block. - - This is a non-blocking wrapper around os.read(). - It uses select.select() to implement a timeout. - """ - - if self.child_fd == -1: - raise ValueError ('I/O operation on closed file') - - # Note that some systems like Solaris don't seem to ever give - # an EOF when the child dies. In fact, you can still try to read - # from the child_fd -- it will block forever or until TIMEOUT. - # For this case, I test isalive() before doing any reading. - # If isalive() is false, then I pretend that this is the same as EOF. - if not self.isalive(): - r, w, e = select.select([self.child_fd], [], [], 0) - if not r: - self.flag_eof = 1 - raise EOF ('End Of File (EOF) in read(). Braindead platform.') - - r, w, e = select.select([self.child_fd], [], [], timeout) - if not r: - raise TIMEOUT('Timeout exceeded in read().') - - if self.child_fd in r: - try: - s = os.read(self.child_fd, size) - except OSError, e: - self.flag_eof = 1 - raise EOF('End Of File (EOF) in read(). Exception style platform.') - if s == '': - self.flag_eof = 1 - raise EOF('End Of File (EOF) in read(). Empty string style platform.') - - if self.log_file != None: - self.log_file.write (s) - self.log_file.flush() - - return s - - raise ExceptionPexpect('Reached an unexpected state in read().') - - def read (self, size = -1): # File-like object. - """This reads at most size bytes from the file - (less if the read hits EOF before obtaining size bytes). - If the size argument is negative or omitted, - read all data until EOF is reached. - The bytes are returned as a string object. - An empty string is returned when EOF is encountered immediately. - """ - if size == 0: - return '' - if size < 0: - self.expect (EOF) - return self.before - - # I could have done this more directly by not using expect(), but - # I deliberately decided to couple read() to expect() so that - # I would catch any bugs early and ensure consistant behavior. - # It's a little less efficient, but there is less for me to - # worry about if I have to later modify read() or expect(). - cre = re.compile('.{%d}' % size, re.DOTALL) - index = self.expect ([cre, EOF]) - if index == 0: - return self.after ### self.before should be ''. Should I assert this? - return self.before - - def readline (self, size = -1): # File-like object. - """This reads and returns one entire line. A trailing newline is kept in - the string, but may be absent when a file ends with an incomplete line. - Note: This readline() looks for a \r\n pair even on UNIX because this is - what the pseudo tty device returns. So contrary to what you may be used to - you will get a newline as \r\n. - An empty string is returned when EOF is hit immediately. - Currently, the size agument is mostly ignored, so this behavior is not - standard for a file-like object. - """ - if size == 0: - return '' - index = self.expect (['\r\n', EOF]) - if index == 0: - return self.before + '\r\n' - else: - return self.before - - def __iter__ (self): - """This is to support interators over a file-like object. - """ - return self - def next (self): - """This is to support iterators over a file-like object. - """ - result = self.readline() - if result == "": - raise StopIteration - return result - - def readlines (self, sizehint = -1): # File-like object. - """This reads until EOF using readline() and returns a list containing - the lines thus read. The optional sizehint argument is ignored. - """ - lines = [] - while 1: - line = self.readline() - if not line: - break - lines.append(line) - return lines - - def write(self, str): # File-like object. - """This is similar to send() except that there is no return value. - """ - self.send (str) - - def writelines (self, sequence): # File-like object. - """This calls write() for each element in the sequence. - The sequence can be any iterable object producing strings, - typically a list of strings. This does not add line separators - There is no return value. - """ - for str in sequence: - self.write (str) - - def send(self, str): - """This sends a string to the child process. - This returns the number of bytes written. - """ - return os.write(self.child_fd, str) - - def sendline(self, str=''): - """This is like send(), but it adds a line feed (os.linesep). - This returns the number of bytes written. - """ - n = self.send(str) - n = n + self.send (os.linesep) - return n - - def sendeof(self): - """This sends an EOF to the child. - This sends a character which causes the pending parent output - buffer to be sent to the waiting child program without - waiting for end-of-line. If it is the first character of the - line, the read() in the user program returns 0, which - signifies end-of-file. This means to work as expected - a sendeof() has to be called at the begining of a line. - This method does not send a newline. It is the responsibility - of the caller to ensure the eof is sent at the beginning of a line. - """ - ### Hmmm... how do I send an EOF? - ###C if ((m = write(pty, *buf, p - *buf)) < 0) - ###C return (errno == EWOULDBLOCK) ? n : -1; - fd = sys.stdin.fileno() - old = termios.tcgetattr(fd) # remember current state - new = termios.tcgetattr(fd) - new[3] = new[3] | termios.ICANON # lflags - # use try/finally to ensure state gets restored - try: - # EOF is recognized when ICANON is set, so make sure it is set. - termios.tcsetattr(fd, termios.TCSADRAIN, new) - os.write (self.child_fd, '%c' % termios.CEOF) - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old) # restore state - - def eof (self): - """This returns 1 if the EOF exception was raised at some point. - """ - return self.flag_eof - - def isalive(self): - """This tests if the child process is running or not. - This returns 1 if the child process appears to be running or 0 if not. - This also sets the exitstatus attribute. - It can take literally SECONDS for Solaris to return the right status. - This is the most wiggly part of Pexpect, but I think I've almost got - it nailed down. - """ - # I can't use signals. Signals on UNIX suck and they - # mess up Python pipes (setting SIGCHLD to SIGIGNORE). - - # If this class was created from an existing file descriptor then - # I just check to see if the file descriptor is still valid. - if self.pid == -1 and not self.__child_fd_owner: - try: - os.fstat(self.child_fd) - return 1 - except: - return 0 - - try: - pid, status = os.waitpid(self.pid, os.WNOHANG) - except OSError: - return 0 - - # I have to do this twice for Solaris. - # I can't even believe that I figured this out... - if pid == 0 and status == 0: - try: - pid, status = os.waitpid(self.pid, os.WNOHANG) - #print 'Solaris sucks' - except OSError: # This is crufty. When does this happen? - return 0 - # If pid and status is still 0 after two calls to waitpid() then - # the process really is alive. This seems to work on all platforms. - if pid == 0 and status == 0: - return 1 - - # I do not OR this together because I want hooks for debugging. - if os.WIFEXITED (status): - self.exitstatus = os.WEXITSTATUS(status) - return 0 - elif os.WIFSTOPPED (status): - return 0 - elif os.WIFSIGNALED (status): - return 0 - else: - return 0 # Can I ever get here? - - def kill(self, sig): - """This sends the given signal to the child application. - In keeping with UNIX tradition it has a misleading name. - It does not necessarily kill the child unless - you send the right signal. - """ - # Same as os.kill, but the pid is given for you. - if self.isalive(): - os.kill(self.pid, sig) - - def compile_pattern_list(self, patterns): - """This compiles a pattern-string or a list of pattern-strings. - Patterns must be a StringType, EOF, TIMEOUT, SRE_Pattern, or - a list of those. - - This is used by expect() when calling expect_list(). - Thus expect() is nothing more than:: - cpl = self.compile_pattern_list(pl) - return self.expect_list(clp, timeout) - - If you are using expect() within a loop it may be more - efficient to compile the patterns first and then call expect_list(). - This avoid calls in a loop to compile_pattern_list(): - cpl = self.compile_pattern_list(my_pattern) - while some_condition: - ... - i = self.expect_list(clp, timeout) - ... - """ - if type(patterns) is not ListType: - patterns = [patterns] - - compiled_pattern_list = [] - for p in patterns: - if type(p) is StringType: - compiled_pattern_list.append(re.compile(p, re.DOTALL)) - elif p is EOF: - compiled_pattern_list.append(EOF) - elif p is TIMEOUT: - compiled_pattern_list.append(TIMEOUT) - elif type(p) is type(re.compile('')): - compiled_pattern_list.append(p) - else: - raise TypeError, 'Argument must be one of StringType, EOF, TIMEOUT, SRE_Pattern, or a list of those type. %s' % str(type(p)) - - return compiled_pattern_list - - def expect(self, pattern, timeout = -1): - """This seeks through the stream until a pattern is matched. - The pattern is overloaded and may take several types including a list. - The pattern can be a StringType, EOF, a compiled re, or - a list of those types. Strings will be compiled to re types. - This returns the index into the pattern list. If the pattern was - not a list this returns index 0 on a successful match. - This may raise exceptions for EOF or TIMEOUT. - To avoid the EOF or TIMEOUT exceptions add EOF or TIMEOUT to - the pattern list. - - After a match is found the instance attributes - 'before', 'after' and 'match' will be set. - You can see all the data read before the match in 'before'. - You can see the data that was matched in 'after'. - The re.MatchObject used in the re match will be in 'match'. - If an error occured then 'before' will be set to all the - data read so far and 'after' and 'match' will be None. - - If timeout is -1 then timeout will be set to the self.timeout value. - - Note: A list entry may be EOF or TIMEOUT instead of a string. - This will catch these exceptions and return the index - of the list entry instead of raising the exception. - The attribute 'after' will be set to the exception type. - The attribute 'match' will be None. - This allows you to write code like this: - index = p.expect (['good', 'bad', pexpect.EOF, pexpect.TIMEOUT]) - if index == 0: - do_something() - elif index == 1: - do_something_else() - elif index == 2: - do_some_other_thing() - elif index == 3: - do_something_completely_different() - instead of code like this: - try: - index = p.expect (['good', 'bad']) - if index == 0: - do_something() - elif index == 1: - do_something_else() - except EOF: - do_some_other_thing() - except TIMEOUT: - do_something_completely_different() - These two forms are equivalent. It all depends on what you want. - You can also just expect the EOF if you are waiting for all output - of a child to finish. For example: - p = pexpect.spawn('/bin/ls') - p.expect (pexpect.EOF) - print p.before - """ - compiled_pattern_list = self.compile_pattern_list(pattern) - return self.expect_list(compiled_pattern_list, timeout) - - def expect_exact (self, pattern_list, timeout = -1): - """This is similar to expect() except that it takes - list of plain strings instead of regular expressions. - The idea is that this should be much faster. It could also be - useful when you don't want to have to worry about escaping - regular expression characters that you want to match. - You may also pass just a string without a list and the single - string will be converted to a list. - If timeout is -1 then timeout will be set to the self.timeout value. - """ - ### This is dumb. It shares most of the code with expect_list. - ### The only different is the comparison method and that - ### self.match is always None after calling this. - if timeout == -1: - timeout = self.timeout - - if type(pattern_list) is StringType: - pattern_list = [pattern_list] - - try: - #ED# incoming = '' - incoming = self.buffer - while 1: # Keep reading until exception or return. - #ED# c = self.read_nonblocking (1, timeout) - #ED# incoming = incoming + c - - # Sequence through the list of patterns and look for a match. - index = -1 - for str_target in pattern_list: - index = index + 1 - if str_target is EOF or str_target is TIMEOUT: - continue # The Exception patterns are handled differently. - match_index = incoming.find (str_target) - if match_index >= 0: - self.before = incoming [ : match_index] - self.after = incoming [match_index : ] - self.buffer = incoming [match_index + len(str_target):] - self.match = None - return index - c = self.read_nonblocking (self.maxread, timeout) - incoming = incoming + c - - except EOF: - if EOF in pattern_list: - self.before = incoming - self.after = EOF - self.buffer = '' - return pattern_list.index(EOF) - else: - raise - except TIMEOUT: - if TIMEOUT in pattern_list: - self.before = incoming - self.after = TIMEOUT - self.buffer = '' - return pattern_list.index(TIMEOUT) - else: - raise - except Exception: - self.before = incoming - self.after = None - self.match = None - self.buffer = '' - raise - - def expect_list(self, pattern_list, timeout = -1): - """ - This takes a list of compiled regular expressions and returns - the index into the pattern_list that matched the child's output. - This is called by expect(). It is similar to the expect() method - except that expect_list() is not overloaded. You must not pass - anything except a list of compiled regular expressions. - If timeout is -1 then timeout will be set to the self.timeout value. - """ - - if timeout == -1: - timeout = self.timeout - - try: - #ED# incoming = '' - incoming = self.buffer - while 1: # Keep reading until exception or return. - #ED# c = self.read_nonblocking (1, timeout) - #ED# incoming = incoming + c - - # Sequence through the list of patterns and look for a match. - index = -1 - for cre in pattern_list: - index = index + 1 - if cre is EOF or cre is TIMEOUT: - continue # The patterns for PexpectExceptions are handled differently. - match = cre.search(incoming) - if match is not None: - self.before = incoming[ : match.start()] - self.after = incoming[match.start() : ] - self.match = match - self.buffer = incoming[match.end() : ] - return index - # Read more data - c = self.read_nonblocking (self.maxread, timeout) - incoming = incoming + c - - except EOF: - if EOF in pattern_list: - self.before = incoming - self.after = EOF - self.buffer = '' - return pattern_list.index(EOF) - else: - raise - except TIMEOUT: - if TIMEOUT in pattern_list: - self.before = incoming - self.after = TIMEOUT - self.buffer = '' - return pattern_list.index(TIMEOUT) - else: - raise - except Exception: - self.before = incoming - self.after = None - self.match = None - self.buffer = '' - raise - - def getwinsize(self): - """ - This returns the window size of the child tty. - The return value is a tuple of (rows, cols). - """ - - s = struct.pack('HHHH', 0, 0, 0, 0) - x = fcntl.ioctl(self.fileno(), termios.TIOCGWINSZ, s) - return struct.unpack('HHHH', x)[0:2] - - def setwinsize(self, r, c): - """ - This sets the windowsize of the child tty. - This will cause a SIGWINCH signal to be sent to the child. - This does not change the physical window size. - It changes the size reported to TTY-aware applications like - vi or curses -- applications that respond to the SIGWINCH signal. - """ - # Check for buggy platforms. Some Python versions on some platforms - # (notably OSF1 Alpha and RedHat 7.1) truncate the value for - # termios.TIOCSWINSZ. It is not clear why this happens. - # These platforms don't seem to handle the signed int very well; - # yet other platforms like OpenBSD have a large negative value for - # TIOCSWINSZ and they don't have a truncate problem. - # Newer versions of Linux have totally different values for TIOCSWINSZ. - # - # Note that this fix is a hack. - TIOCSWINSZ = termios.TIOCSWINSZ - if TIOCSWINSZ == 2148037735L: # L is not required in Python >= 2.2. - TIOCSWINSZ = -2146929561 # Same bits, but with sign. - - # Note, assume ws_xpixel and ws_ypixel are zero. - s = struct.pack('HHHH', r, c, 0, 0) - fcntl.ioctl(self.fileno(), TIOCSWINSZ, s) - - def interact(self, escape_character = chr(29)): - """This gives control of the child process to the interactive user - (the human at the keyboard). - Keystrokes are sent to the child process, and the stdout and stderr - output of the child process is printed. - When the user types the escape_character this method will stop. - The default for escape_character is ^] (ASCII 29). - This simply echos the child stdout and child stderr to the real - stdout and it echos the real stdin to the child stdin. - """ - # Flush the buffer. - self.stdout.write (self.buffer) - self.buffer = '' - self.stdout.flush() - mode = tty.tcgetattr(self.STDIN_FILENO) - tty.setraw(self.STDIN_FILENO) - try: - self.__interact_copy(escape_character) - finally: - tty.tcsetattr(self.STDIN_FILENO, tty.TCSAFLUSH, mode) - - def __interact_writen(self, fd, data): - """This is used by the interact() method. - """ - while data != '' and self.isalive(): - n = os.write(fd, data) - data = data[n:] - def __interact_read(self, fd): - """This is used by the interact() method. - """ - return os.read(fd, 1000) - def __interact_copy(self, escape_character = None): - """This is used by the interact() method. - """ - while self.isalive(): - r, w, e = select.select([self.child_fd, self.STDIN_FILENO], [], []) - if self.child_fd in r: - data = self.__interact_read(self.child_fd) - os.write(self.STDOUT_FILENO, data) - if self.STDIN_FILENO in r: - data = self.__interact_read(self.STDIN_FILENO) - self.__interact_writen(self.child_fd, data) - if escape_character in data: - break - -############################################################################## -# End of Spawn -############################################################################## - -def _which (filename): - """This takes a given filename; tries to find it in the - environment path; then checks if it is executable. - """ - - # Special case where filename already contains a path. - if os.path.dirname(filename) != '': - if os.access (filename, os.X_OK): - return filename - - if not os.environ.has_key('PATH') or os.environ['PATH'] == '': - p = os.defpath - else: - p = os.environ['PATH'] - - # Oddly enough this was the one line that made Pexpect - # incompatible with Python 1.5.2. - #pathlist = p.split (os.pathsep) - pathlist = string.split (p, os.pathsep) - - for path in pathlist: - f = os.path.join(path, filename) - if os.access(f, os.X_OK): - return f - return None - -def _split_command_line(command_line): - """This splits a command line into a list of arguments. - It splits arguments on spaces, but handles - embedded quotes, doublequotes, and escaped characters. - It's impossible to do this with a regular expression, so - I wrote a little state machine to parse the command line. - """ - arg_list = [] - arg = '' - state_quote = 0 - state_doublequote = 0 - state_esc = 0 - for c in command_line: - if c == '\\': # Escape the next character - state_esc = 1 - elif c == r"'": # Handle single quote - if state_esc: - state_esc = 0 - elif not state_quote: - state_quote = 1 - else: - state_quote = 0 - elif c == r'"': # Handle double quote - if state_esc: - state_esc = 0 - elif not state_doublequote: - state_doublequote = 1 - else: - state_doublequote = 0 - - # Add arg to arg_list unless in some other state. - elif c == ' 'and not state_quote and not state_doublequote and not state_esc: - arg_list.append(arg) - arg = '' - else: - arg = arg + c - if c != '\\'and state_esc: # escape mode lasts for one character. - state_esc = 0 - - # Handle last argument. - if arg != '': - arg_list.append(arg) - return arg_list - -import shlex -def _split_command_line(command_line): - return shlex.split(command_line) - - -#################### -# -# NOTES -# -#################### - -## def send_human(self, text, delay_min = 0, delay_max = 1): -## pass -## def spawn2(self, command, args): -## """return pid, fd_stdio, fd_stderr -## """ -## pass - - -# Reason for double fork: -# http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC15 -# Reason for ptys: -# http://www.erlenstar.demon.co.uk/unix/faq_4.html#SEC52 - -# Nonblocking on Win32? -# Reasearch this as a way to maybe make pipe work for Win32. -# http://groups.google.com/groups?q=setraw+tty&hl=en&selm=uvgpvisvk.fsf%40roundpoint.com&rnum=7 -# -# if istty: -# if os.name=='posix': -# import tty -# tty.setraw(sys.stdin.fileno()) -# elif os.name=='nt': -# import win32file, win32con -# hstdin = win32file._get_osfhandle(sys.stdin.fileno()) -# modes = (win32file.GetConsoleMode(hstdin) -# & ~(win32con.ENABLE_LINE_INPUT -# |win32con.ENABLE_ECHO_INPUT)) -# win32file.SetConsoleMode(hstdin, modes) - -# Basic documentation: -# Explain use of lists of patterns and return index. -# Explain exceptions for non-handled special cases like EOF - -# Test bad fork -# Test ENOENT. In other words, no more TTY devices. - -#GLOBAL_SIGCHLD_RECEIVED = 0 -#def childdied (signum, frame): -# print 'Signal handler called with signal', signum -# frame.f_globals['pexpect'].GLOBAL_SIGCHLD_RECEIVED = 1 -# print str(frame.f_globals['pexpect'].GLOBAL_SIGCHLD_RECEIVED) -# GLOBAL_SIGCHLD_RECEIVED = 1 - -### Weird bug. If you read too fast after doing a sendline() -# Sometimes you will read the data back that you just sent even if -# the child did not echo the data. This is particularly a problem if -# you send a password. - -# This was previously used to implement a look-ahead in reads. -# if the lookahead failed then Pexpect would "push-back" the data -# that was read. The idea was to allow read() to read blocks of data. -# What I do now is just read one character at a time and then try a -# match. This is not as efficient, but it works well enough for the -# output of most applications and it makes program logic much simpler. -##class PushbackReader: -## """This class is a wrapper around os.read. It adds the features of buffering -## to allow push-back of data and to provide a timeout on a read. -## """ -## def __init__(self, file_descriptor): -## self.fd = file_descriptor -## self.buffer = '' -## -## def read(self, n, timeout = None): -## """This does a read restricted by a timeout and -## it includes any cached data from previous calls. -## This is a non-blocking wrapper around os.read. -## it uses select.select to supply a timeout. -## Note that if this is called with timeout=None (the default) -## then this actually MAY block. -## """ -## # The read() call is a problem. -## # Some platforms return an empty string '' at EOF. -## # Whereas other platforms raise an Input/output exception. -## -## avail = len(self.buffer) -## if n > avail: -## result = self.buffer -## n = n-avail -## else: -## result = self.buffer[: n] -## self.buffer = self.buffer[n:] -## -## r, w, e = select.select([self.fd], [], [], timeout) -## if not r: -## self.flag_timeout = 1 -## raise TIMEOUT('Read exceeded time: %d'%timeout) -## -## if self.fd in r: -## try: -## s = os.read(self.fd, n) -## except OSError, e: -## self.flag_eof = 1 -## raise EOF('Read reached End Of File (EOF). Exception platform.') -## if s == '': -## self.flag_eof = 1 -## raise EOF('Read reached End Of File (EOF). Empty string platform.') -## return s -## -## self.flag_error = 1 -## raise ExceptionPexpect('PushbackReader.read() reached an unexpected state.'+ -## ' There is a logic error in the Pexpect source code.') -## -## def pushback(self, data): -## self.buffer = piece+self.buffer - - -#def _setwinsize(r, c): -# """This sets the windowsize of the tty for stdout. -# This does not change the physical window size. -# It changes the size reported to TTY-aware applications like -# vi or curses -- applications that respond to the SIGWINCH signal. -# This is used by __spawn to set the tty window size of the child. -# """ -# # Check for buggy platforms. Some Pythons on some platforms -# # (notably OSF1 Alpha and RedHat 7.1) truncate the value for -# # termios.TIOCSWINSZ. It is not clear why this happens. -# # These platforms don't seem to handle the signed int very well; -# # yet other platforms like OpenBSD have a large negative value for -# # TIOCSWINSZ and they don't truncate. -# # Newer versions of Linux have totally different values for TIOCSWINSZ. -# # -# # Note that this fix is a hack. -# TIOCSWINSZ = termios.TIOCSWINSZ -# if TIOCSWINSZ == 2148037735L: # L is not required in Python 2.2. -# TIOCSWINSZ = -2146929561 # Same number in binary, but with sign. -# -# # Assume ws_xpixel and ws_ypixel are zero. -# s = struct.pack("HHHH", r, c, 0, 0) -# fcntl.ioctl(sys.stdout.fileno(), TIOCSWINSZ, s) -# -#def _getwinsize(): -# s = struct.pack("HHHH", 0, 0, 0, 0) -# x = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, s) -# return struct.unpack("HHHH", x) - diff --git a/RepSys/rpmutil.py b/RepSys/rpmutil.py index 774f64f..a461fc2 100644 --- a/RepSys/rpmutil.py +++ b/RepSys/rpmutil.py @@ -14,7 +14,7 @@ import sys import os def get_spec(pkgdirurl, targetdir=".", submit=False): - svn = SVN(baseurl=pkgdirurl) + svn = SVN() tmpdir = tempfile.mktemp() try: geturl = "/".join([pkgdirurl, "current", "SPECS"]) @@ -47,7 +47,7 @@ def get_srpm(pkgdirurl, template = None, macros = [], verbose = 0): - svn = SVN(baseurl=pkgdirurl) + svn = SVN() tmpdir = tempfile.mktemp() topdir = "--define '_topdir %s'" % tmpdir builddir = "--define '_builddir %s/%s'" % (tmpdir, "BUILD") @@ -120,7 +120,7 @@ def get_srpm(pkgdirurl, shutil.rmtree(tmpdir) def patch_spec(pkgdirurl, patchfile, log=""): - svn = SVN(baseurl=pkgdirurl) + svn = SVN() tmpdir = tempfile.mktemp() try: geturl = "/".join([pkgdirurl, "current", "SPECS"]) @@ -142,7 +142,7 @@ def put_srpm(pkgdirurl, srpmfile, appendname=0, log=""): srpm = SRPM(srpmfile) if appendname: pkgdirurl = "/".join([pkgdirurl, srpm.name]) - svn = SVN(baseurl=pkgdirurl) + svn = SVN() tmpdir = tempfile.mktemp() try: if srpm.epoch: @@ -239,7 +239,7 @@ def put_srpm(pkgdirurl, srpmfile, appendname=0, log=""): (version, srpm.release)) def create_package(pkgdirurl, log="", verbose=0): - svn = SVN(baseurl=pkgdirurl) + svn = SVN() tmpdir = tempfile.mktemp() try: basename = RepSysTree.pkgname(pkgdirurl) @@ -280,7 +280,7 @@ revision: %s return log def mark_release(pkgdirurl, version, release, revision): - svn = SVN(baseurl=pkgdirurl) + svn = SVN() releasesurl = "/".join([pkgdirurl, "releases"]) versionurl = "/".join([releasesurl, version]) releaseurl = "/".join([versionurl, release]) @@ -302,7 +302,7 @@ def mark_release(pkgdirurl, version, release, revision): log=markreleaselog) def check_changed(pkgdirurl, all=0, show=0, verbose=0): - svn = SVN(baseurl=pkgdirurl) + svn = SVN() if all: baseurl = pkgdirurl packages = [] @@ -372,7 +372,7 @@ def checkout(pkgdirurl, path=None, revision=None, use_mirror=True): if (o_pkgdirurl != pkgdirurl) and use_mirror and mirror.enabled(): current = mirror.checkout_url(current) print "checking out from mirror", current - svn = SVN(baseurl=pkgdirurl) + svn = SVN() svn.checkout(current, path, rev=revision, show=1) def _getpkgtopdir(basedir=None): @@ -387,7 +387,7 @@ def _getpkgtopdir(basedir=None): return topdir def sync(dryrun=False): - svn = SVN(noauth=True) + svn = SVN() topdir = _getpkgtopdir() # run svn info because svn st does not complain when topdir is not an # working copy @@ -438,7 +438,7 @@ def sync(dryrun=False): svn.add(path, local=True) def commit(target=".", message=None): - svn = SVN(noauth=True) + svn = SVN() status = svn.status(target, quiet=True) if not status: print "nothing to commit" @@ -451,7 +451,8 @@ def commit(target=".", message=None): if mirrored: newurl = mirror.switchto_parent(svn, url, target) print "relocated to", newurl - # we can't use the svn object here because pexpect hides VISUAL + # we can't use the svn object here because svn --non-interactive option + # hides VISUAL mopt = "" if message is not None: mopt = "-m \"%s\"" % message @@ -461,7 +462,7 @@ def commit(target=".", message=None): "later" def switch(mirrorurl=None): - svn = SVN(noauth=True) + svn = SVN() topdir = _getpkgtopdir() info = svn.info2(topdir) wcurl = info.get("URL") @@ -490,7 +491,7 @@ def get_submit_info(path): if not os.path.isdir(os.path.join(path, ".svn")): raise Error, "subversion directory not found" - svn = SVN(noauth=True) + svn = SVN() # Now, extract the package name. info = svn.info2(path) diff --git a/RepSys/svn.py b/RepSys/svn.py index b91af0c..3244c3a 100644 --- a/RepSys/svn.py +++ b/RepSys/svn.py @@ -1,4 +1,4 @@ -from RepSys import Error, config, pexpect +from RepSys import Error, config from RepSys.util import execcmd, get_auth import sys import re @@ -18,67 +18,26 @@ class SVNLogEntry: return cmp(self.date, other.date) class SVN: - def __init__(self, username=None, password=None, noauth=0, baseurl=None): - self.noauth = noauth or ( - baseurl and ( - baseurl.startswith("file:") or - baseurl.startswith("svn+ssh:"))) - if not self.noauth: # argh - self.username, self.password = get_auth() - - def _execsvn(self, *args, **kwargs): - cmdstr = "svn "+" ".join(args) - if kwargs.get("local") or kwargs.get("noauth") or self.noauth: + if not kwargs.get("show"): + args = list(args) + args.append("--non-interactive") + svn_command = config.get("global", "svn-command", + "SVN_SSH='ssh -o \"BatchMode yes\"' svn") + cmdstr = svn_command + " " + " ".join(args) + try: return execcmd(cmdstr, **kwargs) - show = kwargs.get("show") - noerror = kwargs.get("noerror") - p = pexpect.spawn(cmdstr, timeout=1) - p.setmaxread(1024) - p.setecho(False) - outlist = [] - while True: - i = p.expect_exact([pexpect.EOF, pexpect.TIMEOUT, - "username:", "password:", - "(p)ermanently?", - "Authorization failed"]) - if i == 0: - if show and p.before: - print p.before, - outlist.append(p.before) - break - elif i == 1: - if show and p.before: - print p.before, - outlist.append(p.before) - elif i == 2: - p.sendline(self.username) - outlist = [] - elif i == 3: - p.sendline(self.password) - outlist = [] - elif i == 4: - p.sendline("p") - outlist = [] - elif i == 5: - if not noerror: - raise Error, "authorization failed" - else: - break - while p.isalive(): - try: - time.sleep(1) - except (pexpect.TIMEOUT, pexpect.EOF): - # Continue until the child dies - pass - status, output = p.exitstatus, "".join(outlist).strip() - if status != 0 and not kwargs.get("noerror"): - sys.stderr.write(cmdstr) - sys.stderr.write("\n") - sys.stderr.write(output) - sys.stderr.write("\n") - raise Error, "command failed: "+cmdstr - return status, output + except Error, e: + if "Permission denied" in e.message: + raise Error, ("%s\n" + "Seems ssh-agent or ForwardAgent are not setup, see " + "http://wiki.mandriva.com/en/Development/Docs/Contributor_Tricks#SSH_configuration" + " for more information." % e) + elif "authorization failed": + raise Error, ("%s\n" + "Note that repsys does not support any HTTP " + "authenticated access." % e) + raise def _execsvn_success(self, *args, **kwargs): status, output = self._execsvn(*args, **kwargs) |