[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