[py-svn] r51450 - py/branch/guido-svn-auth/py/path/svn

guido at codespeak.net guido at codespeak.net
Wed Feb 13 21:01:31 CET 2008


Author: guido
Date: Wed Feb 13 21:01:30 2008
New Revision: 51450

Added:
   py/branch/guido-svn-auth/py/path/svn/auth.txt
Modified:
   py/branch/guido-svn-auth/py/path/svn/wccommand.py
Log:
Added auth support to the remaining methods that may connect to the server,
updated text a bit and turned it into documentation/discussion doc.


Added: py/branch/guido-svn-auth/py/path/svn/auth.txt
==============================================================================
--- (empty file)
+++ py/branch/guido-svn-auth/py/path/svn/auth.txt	Wed Feb 13 21:01:30 2008
@@ -0,0 +1,76 @@
+SVN authentication support
+==========================
+
+This document describes authentication support to both py.path.svnwc and
+py.path.svnurl (yet in its implemention phase). This allows using the library
+in a completely automated situation, without having to provide svn credentials
+interactively.
+
+Current implementation
+----------------------
+
+The credentials are passed to the constructor of the path objects, and are used
+(transparently) for every action that accesses the server. Also, when provided,
+they are passed recursively to all child objects created by methods such as
+join(), ensure(), etc.
+
+To pass credentials to path objects, an SvnAuth class needs to be created to
+hold them. This is then passed to the constructor or methods, usually as
+the 'auth' keyword argument.
+
+It is configurable whether the credentials are stored on disk. Storing them is
+useful in certain situations (executive access to the repository will not
+require the credentials to be passed) but might not be desired in others - for
+instance if a webserver runs more than one application, you do not want to
+pollute the webserver's home directory (if it even has one). This behaviour can
+be controlled by passing a False value for the 'cache_auth' argument to
+SvnAuth.
+
+Code examples
+-------------
+
+So, tying this together, code using this feature would look something like::
+
+  >>> auth = py.path.SvnAuth('user', 'pass', cache_auth=False)
+  >>> wc = py.path.svnwc(url, auth=auth)
+
+Open issues
+-----------
+
+* How do we deal with externals properly?
+
+  It looks like the svn command-line client uses the credentials provided for
+  all externals, if possible, and either prompts for the password in
+  interactive mode, or barfs when --non-interactive is passed. I think it makes
+  sense to copy its behaviour here, pass the credentials to any child svn path
+  objects (as discussed above), and either let the command-line app ask for
+  creds or throw an exception when 'interactive' is set to False (see above).
+
+  Current idea: ignore this and let the client handle (so no passing auth
+  around to the children).
+
+* Functional testing
+
+  Functional testing is relatively hard, and will not work on all systems. It
+  looks like a setup using 'svnserve' is possible, but it will be slow and
+  provide relatively little advantages, apart from testing the integration.
+  Holger suggested that perhaps functional tests could be skipped in favour
+  of only unit tests.
+
+* Non-interactive sessions
+
+  I still think having a way to tell the system we don't want the session to
+  be interactive would be very useful... It is unacceptable for certain types
+  of applications to block on user input. Should we have 'interactive' as an
+  argument to the methods/constructor, with a default value of True?
+
+* Affected methods
+
+  - switch
+  - checkout
+  - update
+  - lock
+  - unlock
+  - diff (when using older revisions?)
+  - commit
+  - log

Modified: py/branch/guido-svn-auth/py/path/svn/wccommand.py
==============================================================================
--- py/branch/guido-svn-auth/py/path/svn/wccommand.py	(original)
+++ py/branch/guido-svn-auth/py/path/svn/wccommand.py	Wed Feb 13 21:01:30 2008
@@ -102,9 +102,14 @@
             raise
         return out
 
-    def switch(self, url): 
+    def switch(self, url, auth=None):
         """ switch to given URL. """
-        self._svn('switch', url)
+        args = []
+        if auth is not None:
+            args.append(str(auth))
+        elif self._auth is not None:
+            args.append(str(self._auth))
+        self._svn('switch', url, *args)
 
     def checkout(self, url=None, rev=None, auth=None):
         """ checkout from url to local wcpath. """
@@ -126,9 +131,14 @@
             args.append(str(self._auth))
         self._svn('co', url, *args)
 
-    def update(self, rev = 'HEAD'):
+    def update(self, rev = 'HEAD', auth=None):
         """ update working copy item to given revision. (None -> HEAD). """
-        self._svn('up -r %s' % rev)
+        args = []
+        if auth is not None:
+            args.append(str(auth))
+        elif self._auth is not None:
+            args.append(str(self._auth))
+        self._svn('up -r %s' % rev, *args)
 
     def write(self, content, mode='wb'):
         """ write content into local filesystem wc. """
@@ -204,16 +214,26 @@
 
     _rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(\S+)\s+(.*)')
 
-    def lock(self):
+    def lock(self, auth=None):
         """ set a lock (exclusive) on the resource """
-        out = self._svn('lock').strip()
+        args = []
+        if auth is not None:
+            args.append(str(auth))
+        elif self._auth is not None:
+            args.append(str(self._auth))
+        out = self._svn('lock', *args).strip()
         if not out:
             # warning or error, raise exception
             raise Exception(out[4:])
     
-    def unlock(self):
+    def unlock(self, auth=None):
         """ unset a previously set lock """
-        out = self._svn('unlock').strip()
+        args = []
+        if auth is not None:
+            args.append(str(auth))
+        elif self._auth is not None:
+            args.append(str(self._auth))
+        out = self._svn('unlock', *args).strip()
         if out.startswith('svn:'):
             # warning or error, raise exception
             raise Exception(out[4:])
@@ -335,14 +355,19 @@
                 continue
         return rootstatus
 
-    def diff(self, rev=None):
+    def diff(self, rev=None, auth=None):
         """ return a diff of the current path against revision rev (defaulting
             to the last one).
         """
         if rev is None:
             out = self._svn('diff')
         else:
-            out = self._svn('diff -r %d' % rev)
+            args = []
+            if auth is not None:
+                args.append(str(auth))
+            elif self._auth is not None:
+                args.append(str(self._auth))
+            out = self._svn('diff -r %d' % rev, *args)
         return out
 
     def blame(self):
@@ -528,7 +553,7 @@
             else:
                 return True 
 
-    def log(self, rev_start=None, rev_end=1, verbose=False):
+    def log(self, rev_start=None, rev_end=1, verbose=False, auth=None):
         """ return a list of LogEntry instances for this path.
 rev_start is the starting revision (defaulting to the first one).
 rev_end is the last revision (defaulting to HEAD).
@@ -544,11 +569,20 @@
         else:
             rev_opt = "-r %s:%s" % (rev_start, rev_end)
         verbose_opt = verbose and "-v" or ""
-        s = svncommon.fixlocale()
+        locale_env = svncommon.fixlocale()
         # some blather on stderr
-        stdin, stdout, stderr  = os.popen3(s + 'svn log --xml %s %s "%s"' % (
-                                           rev_opt, verbose_opt,
-                                           self.strpath))
+        auth_opt = ''
+        if auth is not None:
+            auth_opt = '%s' % (auth,)
+        elif self._auth is not None:
+            auth_opt = '%s' % (self._auth,)
+        auth_opt = ((auth is not None and str(auth)) or
+                    (self._auth is not None and str(self._auth)) or
+                    '')
+        stdin, stdout, stderr  = os.popen3(locale_env +
+                                           'svn log --xml %s %s %s "%s"' % (
+                                            rev_opt, verbose_opt, auth_opt,
+                                            self.strpath))
         from xml.dom import minidom
         from xml.parsers.expat import ExpatError
         try:



More information about the pytest-commit mailing list