diff options
Diffstat (limited to 'libbe/storage')
-rw-r--r-- | libbe/storage/__init__.py | 14 | ||||
-rw-r--r-- | libbe/storage/base.py | 6 | ||||
-rw-r--r-- | libbe/storage/http.py | 57 | ||||
-rw-r--r-- | libbe/storage/util/config.py | 45 | ||||
-rw-r--r-- | libbe/storage/util/mapfile.py | 28 | ||||
-rw-r--r-- | libbe/storage/util/properties.py | 51 | ||||
-rw-r--r-- | libbe/storage/util/settings_object.py | 95 | ||||
-rw-r--r-- | libbe/storage/vcs/__init__.py | 17 | ||||
-rw-r--r-- | libbe/storage/vcs/arch.py | 40 | ||||
-rw-r--r-- | libbe/storage/vcs/base.py | 266 | ||||
-rw-r--r-- | libbe/storage/vcs/bzr.py | 118 | ||||
-rw-r--r-- | libbe/storage/vcs/darcs.py | 104 | ||||
-rw-r--r-- | libbe/storage/vcs/git.py | 108 | ||||
-rw-r--r-- | libbe/storage/vcs/hg.py | 86 |
14 files changed, 570 insertions, 465 deletions
diff --git a/libbe/storage/__init__.py b/libbe/storage/__init__.py index c3bda4b..6bceac9 100644 --- a/libbe/storage/__init__.py +++ b/libbe/storage/__init__.py @@ -14,6 +14,20 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""Define the :class:`~libbe.storage.base.Storage` and +:class:`~libbe.storage.base.VersionedStorage` classes for storing BE +data. + +Also define assorted implementations for the Storage classes: + +* :mod:`libbe.storage.vcs` +* :mod:`libbe.storage.http` + +Also define an assortment of storage-related tools and utilities: + +* :mod:`libbe.storage.util` +""" + import base ConnectionError = base.ConnectionError diff --git a/libbe/storage/base.py b/libbe/storage/base.py index ad6b291..0ae9c53 100644 --- a/libbe/storage/base.py +++ b/libbe/storage/base.py @@ -519,10 +519,8 @@ class VersionedStorage (Storage): raise InvalidRevision(i) def changed(self, revision): - """ - Return a tuple of lists of ids - (new, modified, removed) - from the specified revision to the current situation. + """Return a tuple of lists of ids `(new, modified, removed)` from the + specified revision to the current situation. """ new = [] modified = [] diff --git a/libbe/storage/http.py b/libbe/storage/http.py index 5606383..7ec9f54 100644 --- a/libbe/storage/http.py +++ b/libbe/storage/http.py @@ -21,8 +21,13 @@ # A dictionary of response codes is available in # httplib.responses -""" -Access bug repository data over HTTP. +"""Define an HTTP-based :class:`~libbe.storage.base.VersionedStorage` +implementation. + +See Also +-------- +:mod:`libbe.command.serve` : the associated server + """ import sys @@ -50,6 +55,13 @@ HTTP_OK = 200 HTTP_FOUND = 302 HTTP_TEMP_REDIRECT = 307 HTTP_USER_ERROR = 418 +"""Status returned to indicate exceptions on the server side. + +A BE-specific extension to the HTTP/1.1 protocol (See `RFC 2616`_). + +.. _RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1 +""" + HTTP_VALID = [HTTP_OK, HTTP_FOUND, HTTP_TEMP_REDIRECT, HTTP_USER_ERROR] class InvalidURL (Exception): @@ -66,9 +78,18 @@ class InvalidURL (Exception): return self.msg def get_post_url(url, get=True, data_dict=None, headers=[]): - """ - get: use GET if True, otherwise use POST. - data_dict: dict of data to send. + """Execute a GET or POST transaction. + + Parameters + ---------- + url : str + The base URL (query portion added internally, if necessary). + get : bool + Use GET if True, otherwise use POST. + data_dict : dict + Data to send, either by URL query (if GET) or by POST (if POST). + headers : list + Extra HTTP headers to add to the request. """ if data_dict == None: data_dict = {} @@ -101,9 +122,10 @@ def get_post_url(url, get=True, data_dict=None, headers=[]): class HTTP (base.VersionedStorage): - """ - This class implements a Storage interface over HTTP, using GET to - retrieve information and POST to set information. + """:class:`~libbe.storage.base.VersionedStorage` implementation over + HTTP. + + Uses GET to retrieve information and POST to set information. """ name = 'HTTP' @@ -113,6 +135,10 @@ class HTTP (base.VersionedStorage): def parse_repo(self, repo): """Grab username and password (if any) from the repo URL. + + Examples + -------- + >>> s = HTTP('http://host.com/path/to/repo') >>> s.repo 'http://host.com/path/to/repo' @@ -249,15 +275,18 @@ class HTTP (base.VersionedStorage): return page.rstrip('\n') def revision_id(self, index=None): - """ - Return the name of the <index>th revision. The choice of - which branch to follow when crossing branches/merges is not - defined. Revision indices start at 1; ID 0 is the blank - repository. + """Return the name of the <index>th revision. + + The choice of which branch to follow when crossing + branches/merges is not defined. Revision indices start at 1; + ID 0 is the blank repository. Return None if index==None. - If the specified revision does not exist, raise InvalidRevision. + Raises + ------ + InvalidRevision + If the specified revision does not exist. """ if index == None: return None diff --git a/libbe/storage/util/config.py b/libbe/storage/util/config.py index 8526724..724d2d3 100644 --- a/libbe/storage/util/config.py +++ b/libbe/storage/util/config.py @@ -16,8 +16,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" -Create, save, and load the per-user config file at path(). +"""Create, save, and load the per-user config file at :func:`path`. """ import ConfigParser @@ -31,17 +30,29 @@ if libbe.TESTING == True: default_encoding = libbe.util.encoding.get_filesystem_encoding() +"""Default filesystem encoding. + +Initialized with :func:`libbe.util.encoding.get_filesystem_encoding`. +""" def path(): - """Return the path to the per-user config file""" + """Return the path to the per-user config file. + """ return os.path.expanduser("~/.bugs_everywhere") def set_val(name, value, section="DEFAULT", encoding=None): - """Set a value in the per-user config file + """Set a value in the per-user config file. - :param name: The name of the value to set - :param value: The new value to set (or None to delete the value) - :param section: The section to store the name/value in + Parameters + ---------- + name : str + The name of the value to set. + value : str or None + The new value to set (or None to delete the value). + section : str + The section to store the name/value in. + encoding : str + The config file's encoding, defaults to :data:`default_encoding`. """ if encoding == None: encoding = default_encoding @@ -60,12 +71,22 @@ def set_val(name, value, section="DEFAULT", encoding=None): f.close() def get_val(name, section="DEFAULT", default=None, encoding=None): - """ - Get a value from the per-user config file + """Get a value from the per-user config file + + Parameters + ---------- + name : str + The name of the value to set. + section : str + The section to store the name/value in. + default : + The value to return if `name` is not set. + encoding : str + The config file's encoding, defaults to :data:`default_encoding`. + + Examples + -------- - :param name: The name of the value to get - :section: The section that the name is in - :return: The value, or None >>> get_val("junk") is None True >>> set_val("junk", "random") diff --git a/libbe/storage/util/mapfile.py b/libbe/storage/util/mapfile.py index 0b8af23..55863d7 100644 --- a/libbe/storage/util/mapfile.py +++ b/libbe/storage/util/mapfile.py @@ -16,10 +16,10 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" -Provide a means of saving and loading dictionaries of parameters. The -saved "mapfiles" should be clear, flat-text files, and allow easy merging of -independent/conflicting changes. +"""Serializing and deserializing dictionaries of parameters. + +The serialized "mapfiles" should be clear, flat-text strings, and allow +easy merging of independent/conflicting changes. """ import errno @@ -49,6 +49,10 @@ class InvalidMapfileContents(Exception): def generate(map): """Generate a YAML mapfile content string. + + Examples + -------- + >>> generate({'q':'p'}) 'q: p\\n\\n' >>> generate({'q':u'Fran\u00e7ais'}) @@ -73,6 +77,10 @@ def generate(map): >>> generate({'q':'p\\n'}) Traceback (most recent call last): IllegalValue: Illegal value "p\\n" + + See Also + -------- + parse : inverse """ keys = map.keys() keys.sort() @@ -97,8 +105,11 @@ def generate(map): return '\n'.join(lines) def parse(contents): - """ - Parse a YAML mapfile string. + """Parse a YAML mapfile string. + + Examples + -------- + >>> parse('q: p\\n\\n')['q'] 'p' >>> parse('q: \\'p\\'\\n\\n')['q'] @@ -119,6 +130,11 @@ def parse(contents): Traceback (most recent call last): ... InvalidMapfileContents: Invalid YAML contents + + See Also + -------- + generate : inverse + """ c = yaml.load(contents) if type(c) == types.StringType: diff --git a/libbe/storage/util/properties.py b/libbe/storage/util/properties.py index 55bac85..b5681b1 100644 --- a/libbe/storage/util/properties.py +++ b/libbe/storage/util/properties.py @@ -16,16 +16,24 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" -This module provides a series of useful decorators for defining -various types of properties. For example usage, consider the -unittests at the end of the module. - -See - http://www.python.org/dev/peps/pep-0318/ -and - http://www.phyast.pitt.edu/~micheles/python/documentation.html -for more information on decorators. +"""Provides a series of useful decorators for defining various types +of properties. + +For example usage, consider the unittests at the end of the module. + +Notes +----- + +See `PEP 318` and Michele Simionato's `decorator documentation` for +more information on decorators. + +.. _PEP 318: http://www.python.org/dev/peps/pep-0318/ +.. _decorator documentation: http://www.phyast.pitt.edu/~micheles/python/documentation.html + +See Also +-------- +:mod:`libbe.storage.util.settings_object` : bundle properties into a convenient package + """ import copy @@ -336,12 +344,11 @@ def primed_property(primer, initVal=None, unprimeableVal=None): return decorator def change_hook_property(hook, mutable=False, default=None): - """ - Call the function hook(instance, old_value, new_value) whenever a - value different from the current value is set (instance is a a - reference to the class instance to which this property belongs). + """Call the function `hook` whenever a value different from the + current value is set. + This is useful for saving changes to disk, etc. This function is - called _after_ the new value has been stored, allowing you to + called *after* the new value has been stored, allowing you to change the stored value if you want. In the case of mutables, things are slightly trickier. Because @@ -350,11 +357,19 @@ def change_hook_property(hook, mutable=False, default=None): mutable value, and checking for changes whenever the property is set (obviously) or retrieved (to check for external changes). So long as you're conscientious about accessing the property after - making external modifications, mutability won't be a problem. + making external modifications, mutability won't be a problem:: + t.x.append(5) # external modification t.x # dummy access notices change and triggers hook - See testChangeHookMutableProperty for an example of the expected - behavior. + + See :class:`testChangeHookMutableProperty` for an example of the + expected behavior. + + Parameters + ---------- + hook : fn + `hook(instance, old_value, new_value)`, where `instance` is a + reference to the class instance to which this property belongs. """ def decorator(funcs): if hasattr(funcs, "__call__"): diff --git a/libbe/storage/util/settings_object.py b/libbe/storage/util/settings_object.py index 8434952..6e4da55 100644 --- a/libbe/storage/util/settings_object.py +++ b/libbe/storage/util/settings_object.py @@ -16,11 +16,12 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" -This module provides a base class implementing settings-dict based -property storage useful for BE objects with saved properties -(e.g. BugDir, Bug, Comment). For example usage, consider the -unittests at the end of the module. +"""Provides :class:`SavedSettingsObject` implementing settings-dict +based property storage. + +See Also +-------- +:mod:`libbe.storage.util.properties` : underlying property definitions """ import libbe @@ -33,9 +34,10 @@ if libbe.TESTING == True: import unittest class _Token (object): - """ - `Control' value class for properties. We want values that only - mean something to the settings_object module. + """`Control' value class for properties. + + We want values that only mean something to the `settings_object` + module. """ pass @@ -44,45 +46,58 @@ class UNPRIMED (_Token): pass class EMPTY (_Token): - """ - Property has been primed but has no user-set value, so use + """Property has been primed but has no user-set value, so use default/generator value. """ pass def prop_save_settings(self, old, new): - """ - The default action undertaken when a property changes. + """The default action undertaken when a property changes. """ if self.storage != None and self.storage.is_writeable(): self.save_settings() def prop_load_settings(self): - """ - The default action undertaken when an UNPRIMED property is - accessed. Attempt to run .load_settings(), which calls - ._setup_saved_settings() internally. If .storage is inaccessible, - don't do anything. + """The default action undertaken when an UNPRIMED property is + accessed. + + Attempt to run `.load_settings()`, which calls + `._setup_saved_settings()` internally. If `.storage` is + inaccessible, don't do anything. """ if self.storage != None and self.storage.is_readable(): self.load_settings() # Some name-mangling routines for pretty printing setting names def setting_name_to_attr_name(self, name): - """ - Convert keys to the .settings dict into their associated + """Convert keys to the `.settings` dict into their associated SavedSettingsObject attribute names. + + Examples + -------- + >>> print setting_name_to_attr_name(None,"User-id") user_id + + See Also + -------- + attr_name_to_setting_name : inverse """ return name.lower().replace('-', '_') def attr_name_to_setting_name(self, name): - """ - The inverse of setting_name_to_attr_name. + """Convert SavedSettingsObject attribute names to `.settings` dict + keys. + + Examples: + >>> print attr_name_to_setting_name(None, "user_id") User-id + + See Also + -------- + setting_name_to_attr_name : inverse """ return name.capitalize().replace('_', '-') @@ -96,8 +111,7 @@ def versioned_property(name, doc, settings_properties=[], required_saved_properties=[], require_save=False): - """ - Combine the common decorators in a single function. + """Combine the common decorators in a single function. Use zero or one (but not both) of default or generator, since a working default will keep the generator from functioning. Use the @@ -124,17 +138,20 @@ def versioned_property(name, doc, into our local scope. Don't mess with them. Set mutable=True if: - * default is a mutable - * your generator function may return mutables - * you set change_hook and might have mutable property values - See the docstrings in libbe.properties for details on how each of + + * default is a mutable + * your generator function may return mutables + * you set change_hook and might have mutable property values + + See the docstrings in `libbe.properties` for details on how each of these cases are handled. - The value stored in .settings[name] will be - * no value (or UNPRIMED) if the property has been neither set, - nor loaded as blank. - * EMPTY if the value has been loaded as blank. - * some value if the property has been either loaded or set. + The value stored in `.settings[name]` will be + + * no value (or UNPRIMED) if the property has been neither set, + nor loaded as blank. + * EMPTY if the value has been loaded as blank. + * some value if the property has been either loaded or set. """ settings_properties.append(name) if require_save == True: @@ -175,7 +192,19 @@ def versioned_property(name, doc, return decorator class SavedSettingsObject(object): - + """Setup a framework for lazy saving and loading of `.settings` + properties. + + This is useful for BE objects with saved properties + (e.g. :class:`~libbe.bugdir.BugDir`, :class:`~libbe.bug.Bug`, + :class:`~libbe.comment.Comment`). For example usage, consider the + unittests at the end of the module. + + See Also + -------- + versioned_property, prop_save_settings, prop_load_settings + setting_name_to_attr_name, attr_name_to_setting_name + """ # Keep a list of properties that may be stored in the .settings dict. #settings_properties = [] diff --git a/libbe/storage/vcs/__init__.py b/libbe/storage/vcs/__init__.py index 777c723..552d43e 100644 --- a/libbe/storage/vcs/__init__.py +++ b/libbe/storage/vcs/__init__.py @@ -14,6 +14,23 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""Define the Version Controlled System (VCS)-based +:class:`~libbe.storage.base.Storage` and +:class:`~libbe.storage.base.VersionedStorage` implementations. + +There is a base class (:class:`~libbe.storage.vcs.VCS`) translating +Storage language to VCS language, and a number of `VCS` implementations: + +* :class:`~libbe.storage.vcs.arch.Arch` +* :class:`~libbe.storage.vcs.bzr.Bzr` +* :class:`~libbe.storage.vcs.darcs.Darcs` +* :class:`~libbe.storage.vcs.git.Git` +* :class:`~libbe.storage.vcs.hg.Hg` + +The base `VCS` class also serves as a filesystem Storage backend (not +versioning) in the event that a user has no VCS installed. +""" + import base set_preferred_vcs = base.set_preferred_vcs diff --git a/libbe/storage/vcs/arch.py b/libbe/storage/vcs/arch.py index 38b1d02..3a50414 100644 --- a/libbe/storage/vcs/arch.py +++ b/libbe/storage/vcs/arch.py @@ -18,8 +18,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" -GNU Arch (tla) backend. +"""GNU Arch_ (tla) backend. + +.. _Arch: http://www.gnu.org/software/gnu-arch/ """ import codecs @@ -56,6 +57,8 @@ def new(): return Arch() class Arch(base.VCS): + """:class:`base.VCS` implementation for GNU Arch. + """ name = 'arch' client = client _archive_name = None @@ -90,10 +93,10 @@ class Arch(base.VCS): self._add_project_code(path) def _create_archive(self, path): - """ - Create a temporary Arch archive in the directory PATH. This - archive will be removed by - destroy->_vcs_destroy->_remove_archive + """Create a temporary Arch archive in the directory PATH. This + archive will be removed by:: + + destroy->_vcs_destroy->_remove_archive """ # http://regexps.srparish.net/tutorial-tla/new-archive.html#Creating_a_New_Archive assert self._archive_name == None @@ -109,8 +112,7 @@ class Arch(base.VCS): self._archive_dir, cwd=path) def _invoke_client(self, *args, **kwargs): - """ - Invoke the client on our archive. + """Invoke the client on our archive. """ assert self._archive_name != None command = args[0] @@ -164,16 +166,20 @@ class Arch(base.VCS): return '%s/%s' % (self._archive_name, self._project_name) def _adjust_naming_conventions(self, path): - """ - By default, Arch restricts source code filenames to - ^[_=a-zA-Z0-9].*$ - See - http://regexps.srparish.net/tutorial-tla/naming-conventions.html - Since our bug directory '.be' doesn't satisfy these conventions, - we need to adjust them. + """Adjust `Arch naming conventions`_ so ``.be`` is considered source + code. + + By default, Arch restricts source code filenames to:: + + ^[_=a-zA-Z0-9].*$ - The conventions are specified in - project-root/{arch}/=tagging-method + Since our bug directory ``.be`` doesn't satisfy these conventions, + we need to adjust them. The conventions are specified in:: + + project-root/{arch}/=tagging-method + + .. _Arch naming conventions: + http://regexps.srparish.net/tutorial-tla/naming-conventions.html """ tagpath = os.path.join(path, '{arch}', '=tagging-method') lines_out = [] diff --git a/libbe/storage/vcs/base.py b/libbe/storage/vcs/base.py index 337576e..d85c94d 100644 --- a/libbe/storage/vcs/base.py +++ b/libbe/storage/vcs/base.py @@ -19,10 +19,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" -Define the base VCS (Version Control System) class, which should be -subclassed by other Version Control System backends. The base class -implements a "do not version" VCS. +"""Define the base :class:`VCS` (Version Control System) class, which +should be subclassed by other Version Control System backends. The +base class implements a "do not version" VCS. """ import codecs @@ -50,11 +49,17 @@ if libbe.TESTING == True: import libbe.ui.util.user -# List VCS modules in order of preference. -# Don't list this module, it is implicitly last. VCS_ORDER = ['arch', 'bzr', 'darcs', 'git', 'hg'] +"""List VCS modules in order of preference. + +Don't list this module, it is implicitly last. +""" def set_preferred_vcs(name): + """Manipulate :data:`VCS_ORDER` to place `name` first. + + This is primarily indended for testing purposes. + """ global VCS_ORDER assert name in VCS_ORDER, \ 'unrecognized VCS %s not in\n %s' % (name, VCS_ORDER) @@ -62,7 +67,10 @@ def set_preferred_vcs(name): VCS_ORDER.insert(0, name) def _get_matching_vcs(matchfn): - """Return the first module for which matchfn(VCS_instance) is true""" + """Return the first module for which matchfn(VCS_instance) is True. + + Searches in :data:`VCS_ORDER`. + """ for submodname in VCS_ORDER: module = import_by_name('libbe.storage.vcs.%s' % submodname) vcs = module.new() @@ -71,17 +79,26 @@ def _get_matching_vcs(matchfn): return VCS() def vcs_by_name(vcs_name): - """Return the module for the VCS with the given name""" + """Return the module for the VCS with the given name. + + Searches in :data:`VCS_ORDER`. + """ if vcs_name == VCS.name: return new() return _get_matching_vcs(lambda vcs: vcs.name == vcs_name) def detect_vcs(dir): - """Return an VCS instance for the vcs being used in this directory""" + """Return an VCS instance for the vcs being used in this directory. + + Searches in :data:`VCS_ORDER`. + """ return _get_matching_vcs(lambda vcs: vcs._detect(dir)) def installed_vcs(): - """Return an instance of an installed VCS""" + """Return an instance of an installed VCS. + + Searches in :data:`VCS_ORDER`. + """ return _get_matching_vcs(lambda vcs: vcs.installed()) @@ -118,10 +135,17 @@ class NoSuchFile (InvalidID): class CachedPathID (object): - """ - Storage ID <-> path policy. - .../.be/BUGDIR/bugs/BUG/comments/COMMENT - ^-- root path + """Cache Storage ID <-> path policy. + + Paths generated following:: + + .../.be/BUGDIR/bugs/BUG/comments/COMMENT + ^-- root path + + See :mod:`libbe.util.id` for a discussion of ID formats. + + Examples + -------- >>> dir = Dir() >>> os.mkdir(os.path.join(dir.path, '.be')) @@ -183,10 +207,11 @@ class CachedPathID (object): self._root, self._spacer_dirs[0], 'id-cache') def init(self, verbose=True, cache=None): - """ - Create cache file for an existing .be directory. - File if multiple lines of the form: - UUID\tPATH + """Create cache file for an existing .be directory. + + The file contains multiple lines of the form:: + + UUID\tPATH """ if cache == None: self._cache = {} @@ -311,142 +336,13 @@ def new(): return VCS() class VCS (libbe.storage.base.VersionedStorage): - """ - This class implements a 'no-vcs' interface. + """Implement a 'no-VCS' interface. Support for other VCSs can be added by subclassing this class, and - overriding methods _vcs_*() with code appropriate for your VCS. + overriding methods `_vcs_*()` with code appropriate for your VCS. - The methods _u_*() are utility methods available to the _vcs_*() + The methods `_u_*()` are utility methods available to the `_vcs_*()` methods. - - Sink to existing root - ====================== - - Consider the following usage case: - You have a bug directory rooted in - /path/to/source - by which I mean the '.be' directory is at - /path/to/source/.be - However, you're of in some subdirectory like - /path/to/source/GUI/testing - and you want to comment on a bug. Setting sink_to_root=True when - you initialize your BugDir will cause it to search for the '.be' - file in the ancestors of the path you passed in as 'root'. - /path/to/source/GUI/testing/.be miss - /path/to/source/GUI/.be miss - /path/to/source/.be hit! - So it still roots itself appropriately without much work for you. - - File-system access - ================== - - BugDirs live completely in memory when .sync_with_disk is False. - This is the default configuration setup by BugDir(from_disk=False). - If .sync_with_disk == True (e.g. BugDir(from_disk=True)), then - any changes to the BugDir will be immediately written to disk. - - If you want to change .sync_with_disk, we suggest you use - .set_sync_with_disk(), which propogates the new setting through to - all bugs/comments/etc. that have been loaded into memory. If - you've been living in memory and want to move to - .sync_with_disk==True, but you're not sure if anything has been - changed in memory, a call to .save() immediately before the - .set_sync_with_disk(True) call is a safe move. - - Regardless of .sync_with_disk, a call to .save() will write out - all the contents that the BugDir instance has loaded into memory. - If sync_with_disk has been True over the course of all interesting - changes, this .save() call will be a waste of time. - - The BugDir will only load information from the file system when it - loads new settings/bugs/comments that it doesn't already have in - memory and .sync_with_disk == True. - - Allow storage initialization - ======================== - - This one is for testing purposes. Setting it to True allows the - BugDir to search for an installed Storage backend and initialize - it in the root directory. This is a convenience option for - supporting tests of versioning functionality - (e.g. RevisionedBugDir). - - Disable encoding manipulation - ============================= - - This one is for testing purposed. You might have non-ASCII - Unicode in your bugs, comments, files, etc. BugDir instances try - and support your preferred encoding scheme (e.g. "utf-8") when - dealing with stream and file input/output. For stream output, - this involves replacing sys.stdout and sys.stderr - (libbe.encode.set_IO_stream_encodings). However this messes up - doctest's output catching. In order to support doctest tests - using BugDirs, set manipulate_encodings=False, and stick to ASCII - in your tests. - - if root == None: - root = os.getcwd() - if sink_to_existing_root == True: - self.root = self._find_root(root) - else: - if not os.path.exists(root): - self.root = None - raise NoRootEntry(root) - self.root = root - # get a temporary storage until we've loaded settings - self.sync_with_disk = False - self.storage = self._guess_storage() - - if assert_new_BugDir == True: - if os.path.exists(self.get_path()): - raise AlreadyInitialized, self.get_path() - if storage == None: - storage = self._guess_storage(allow_storage_init) - self.storage = storage - self._setup_user_id(self.user_id) - - - # methods for getting the BugDir situated in the filesystem - - def _find_root(self, path): - ''' - Search for an existing bug database dir and it's ancestors and - return a BugDir rooted there. Only called by __init__, and - then only if sink_to_existing_root == True. - ''' - if not os.path.exists(path): - self.root = None - raise NoRootEntry(path) - versionfile=utility.search_parent_directories(path, - os.path.join(".be", "version")) - if versionfile != None: - beroot = os.path.dirname(versionfile) - root = os.path.dirname(beroot) - return root - else: - beroot = utility.search_parent_directories(path, ".be") - if beroot == None: - self.root = None - raise NoBugDir(path) - return beroot - - def _guess_storage(self, allow_storage_init=False): - ''' - Only called by __init__. - ''' - deepdir = self.get_path() - if not os.path.exists(deepdir): - deepdir = os.path.dirname(deepdir) - new_storage = storage.detect_storage(deepdir) - install = False - if new_storage.name == "None": - if allow_storage_init == True: - new_storage = storage.installed_storage() - new_storage.init(self.root) - return new_storage - -os.listdir(self.get_path("bugs")): """ name = 'None' client = 'false' # command-line tool for _u_invoke_client @@ -659,9 +555,28 @@ os.listdir(self.get_path("bugs")): return self._vcs_detect(path) def root(self): - """ - Set the root directory to the path's VCS root. This is the - default working directory for future invocations. + """Set the root directory to the path's VCS root. + + This is the default working directory for future invocations. + Consider the following usage case: + + You have a project rooted in:: + + /path/to/source/ + + by which I mean the VCS repository is in, for example:: + + /path/to/source/.bzr + + However, you're of in some subdirectory like:: + + /path/to/source/ui/testing + + and you want to comment on a bug. `root` will locate your VCS + root (``/path/to/source/``) and set the repo there. This + means that it doesn't matter where you are in your project + tree when you call "be COMMAND", it always acts as if you called + it from the VCS root. """ if self._detect(self.repo) == False: raise VCSUnableToRoot(self) @@ -678,6 +593,10 @@ os.listdir(self.get_path("bugs")): """ Begin versioning the tree based at self.repo. Also roots the vcs at path. + + See Also + -------- + root : called if the VCS has already been initialized. """ if not os.path.exists(self.repo) or not os.path.isdir(self.repo): raise VCSUnableToRoot(self) @@ -908,8 +827,7 @@ os.listdir(self.get_path("bugs")): return (new_id, mod_id, rem_id) def _u_any_in_string(self, list, string): - """ - Return True if any of the strings in list are in string. + """Return True if any of the strings in list are in string. Otherwise return False. """ for list_string in list: @@ -932,9 +850,8 @@ os.listdir(self.get_path("bugs")): return self._u_invoke(cl_args, **kwargs) def _u_search_parent_directories(self, path, filename): - """ - Find the file (or directory) named filename in path or in any - of path's parents. + """Find the file (or directory) named filename in path or in any of + path's parents. e.g. search_parent_directories("/a/b/c", ".be") @@ -952,8 +869,8 @@ os.listdir(self.get_path("bugs")): return ret def _u_find_id_from_manifest(self, id, manifest, revision=None): - """ - Search for the relative path to id using manifest, a list of all files. + """Search for the relative path to id using manifest, a list of all + files. Returns None if the id is not found. """ @@ -979,8 +896,8 @@ os.listdir(self.get_path("bugs")): raise InvalidID(id, revision=revision) def _u_find_id(self, id, revision): - """ - Search for the relative path to id as of revision. + """Search for the relative path to id as of revision. + Returns None if the id is not found. """ assert self._rooted == True @@ -1001,8 +918,10 @@ os.listdir(self.get_path("bugs")): return self._cached_path_id.id(path) def _u_rel_path(self, path, root=None): - """ - Return the relative path to path from root. + """Return the relative path to path from root. + + Examples: + >>> vcs = new() >>> vcs._u_rel_path("/a.b/c/.be", "/a.b/c") '.be' @@ -1028,8 +947,11 @@ os.listdir(self.get_path("bugs")): return relpath def _u_abspath(self, path, root=None): - """ - Return the absolute path from a path realtive to root. + """Return the absolute path from a path realtive to root. + + Examples + -------- + >>> vcs = new() >>> vcs._u_abspath(".be", "/a.b/c") '/a.b/c/.be' @@ -1040,9 +962,8 @@ os.listdir(self.get_path("bugs")): return os.path.abspath(os.path.join(root, path)) def _u_parse_commitfile(self, commitfile): - """ - Split the commitfile created in self.commit() back into - summary and header lines. + """Split the commitfile created in self.commit() back into summary and + header lines. """ f = codecs.open(commitfile, 'r', self.encoding) summary = f.readline() @@ -1059,8 +980,11 @@ os.listdir(self.get_path("bugs")): upgrade.upgrade(self.repo, version) def storage_version(self, revision=None, path=None): - """ - Requires disk access. + """Return the storage version of the on-disk files. + + See Also + -------- + :mod:`libbe.storage.util.upgrade` """ if path == None: path = os.path.join(self.repo, '.be', 'version') diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index 01d9948..5a62968 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -18,8 +18,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" -Bazaar (bzr) backend. +"""Bazaar_ (bzr) backend. + +.. _Bazaar: http://bazaar.canonical.com/ """ try: @@ -51,6 +52,8 @@ def new(): return Bzr() class Bzr(base.VCS): + """:class:`base.VCS` implementation for Bazaar. + """ name = 'bzr' client = None # bzrlib module @@ -64,12 +67,18 @@ class Bzr(base.VCS): return bzrlib.__version__ def version_cmp(self, *args): - """ - Compare the installed Bazaar version V_i with another version - V_o (given in *args). Returns - 1 if V_i > V_o, - 0 if V_i == V_o, and - -1 if V_i < V_o + """Compare the installed Bazaar version `V_i` with another version + `V_o` (given in `*args`). Returns + + === =============== + 1 if `V_i > V_o` + 0 if `V_i == V_o` + -1 if `V_i < V_o` + === =============== + + Examples + -------- + >>> b = Bzr(repo='.') >>> b._vcs_version = lambda : "2.3.1 (release)" >>> b.version_cmp(2,3,1) @@ -275,51 +284,54 @@ class Bzr(base.VCS): return cmd.outf.getvalue() def _parse_diff(self, diff_text): - """ - Example diff text: - - === modified file 'dir/changed' - --- dir/changed 2010-01-16 01:54:53 +0000 - +++ dir/changed 2010-01-16 01:54:54 +0000 - @@ -1,3 +1,3 @@ - hi - -there - +everyone and - joe - - === removed file 'dir/deleted' - --- dir/deleted 2010-01-16 01:54:53 +0000 - +++ dir/deleted 1970-01-01 00:00:00 +0000 - @@ -1,3 +0,0 @@ - -in - -the - -beginning - - === removed file 'dir/moved' - --- dir/moved 2010-01-16 01:54:53 +0000 - +++ dir/moved 1970-01-01 00:00:00 +0000 - @@ -1,4 +0,0 @@ - -the - -ants - -go - -marching - - === added file 'dir/moved2' - --- dir/moved2 1970-01-01 00:00:00 +0000 - +++ dir/moved2 2010-01-16 01:54:34 +0000 - @@ -0,0 +1,4 @@ - +the - +ants - +go - +marching - - === added file 'dir/new' - --- dir/new 1970-01-01 00:00:00 +0000 - +++ dir/new 2010-01-16 01:54:54 +0000 - @@ -0,0 +1,2 @@ - +hello - +world - + """_parse_diff(diff_text) -> (new,modified,removed) + + `new`, `modified`, and `removed` are lists of files. + + Example diff text:: + + === modified file 'dir/changed' + --- dir/changed 2010-01-16 01:54:53 +0000 + +++ dir/changed 2010-01-16 01:54:54 +0000 + @@ -1,3 +1,3 @@ + hi + -there + +everyone and + joe + + === removed file 'dir/deleted' + --- dir/deleted 2010-01-16 01:54:53 +0000 + +++ dir/deleted 1970-01-01 00:00:00 +0000 + @@ -1,3 +0,0 @@ + -in + -the + -beginning + + === removed file 'dir/moved' + --- dir/moved 2010-01-16 01:54:53 +0000 + +++ dir/moved 1970-01-01 00:00:00 +0000 + @@ -1,4 +0,0 @@ + -the + -ants + -go + -marching + + === added file 'dir/moved2' + --- dir/moved2 1970-01-01 00:00:00 +0000 + +++ dir/moved2 2010-01-16 01:54:34 +0000 + @@ -0,0 +1,4 @@ + +the + +ants + +go + +marching + + === added file 'dir/new' + --- dir/new 1970-01-01 00:00:00 +0000 + +++ dir/new 2010-01-16 01:54:54 +0000 + @@ -0,0 +1,2 @@ + +hello + +world + """ new = [] modified = [] diff --git a/libbe/storage/vcs/darcs.py b/libbe/storage/vcs/darcs.py index fd8b7d5..4a21888 100644 --- a/libbe/storage/vcs/darcs.py +++ b/libbe/storage/vcs/darcs.py @@ -15,8 +15,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" -Darcs backend. +"""Darcs_ backend. + +.. _Darcs: http://darcs.net/ """ import codecs @@ -44,6 +45,8 @@ def new(): return Darcs() class Darcs(base.VCS): + """:class:`base.VCS` implementation for Darcs. + """ name='darcs' client='darcs' @@ -57,12 +60,18 @@ class Darcs(base.VCS): return output.strip() def version_cmp(self, *args): - """ - Compare the installed darcs version V_i with another version - V_o (given in *args). Returns - 1 if V_i > V_o, - 0 if V_i == V_o, and - -1 if V_i < V_o + """Compare the installed Darcs version `V_i` with another version + `V_o` (given in `*args`). Returns + + === =============== + 1 if `V_i > V_o` + 0 if `V_i == V_o` + -1 if `V_i < V_o` + === =============== + + Examples + -------- + >>> d = Darcs(repo='.') >>> d._vcs_version = lambda : "2.3.1 (release)" >>> d.version_cmp(2,3,1) @@ -295,44 +304,47 @@ class Darcs(base.VCS): return output def _parse_diff(self, diff_text): - """ - Example diff text: - - Mon Jan 18 15:19:30 EST 2010 None <None@invalid.com> - * Final state - diff -rN --unified old-BEtestgQtDuD/.be/dir/bugs/modified new-BEtestgQtDuD/.be/dir/bugs/modified - --- old-BEtestgQtDuD/.be/dir/bugs/modified 2010-01-18 15:19:30.000000000 -0500 - +++ new-BEtestgQtDuD/.be/dir/bugs/modified 2010-01-18 15:19:30.000000000 -0500 - @@ -1 +1 @@ - -some value to be modified - \ No newline at end of file - +a new value - \ No newline at end of file - diff -rN --unified old-BEtestgQtDuD/.be/dir/bugs/moved new-BEtestgQtDuD/.be/dir/bugs/moved - --- old-BEtestgQtDuD/.be/dir/bugs/moved 2010-01-18 15:19:30.000000000 -0500 - +++ new-BEtestgQtDuD/.be/dir/bugs/moved 1969-12-31 19:00:00.000000000 -0500 - @@ -1 +0,0 @@ - -this entry will be moved - \ No newline at end of file - diff -rN --unified old-BEtestgQtDuD/.be/dir/bugs/moved2 new-BEtestgQtDuD/.be/dir/bugs/moved2 - --- old-BEtestgQtDuD/.be/dir/bugs/moved2 1969-12-31 19:00:00.000000000 -0500 - +++ new-BEtestgQtDuD/.be/dir/bugs/moved2 2010-01-18 15:19:30.000000000 -0500 - @@ -0,0 +1 @@ - +this entry will be moved - \ No newline at end of file - diff -rN --unified old-BEtestgQtDuD/.be/dir/bugs/new new-BEtestgQtDuD/.be/dir/bugs/new - --- old-BEtestgQtDuD/.be/dir/bugs/new 1969-12-31 19:00:00.000000000 -0500 - +++ new-BEtestgQtDuD/.be/dir/bugs/new 2010-01-18 15:19:30.000000000 -0500 - @@ -0,0 +1 @@ - +this entry is new - \ No newline at end of file - diff -rN --unified old-BEtestgQtDuD/.be/dir/bugs/removed new-BEtestgQtDuD/.be/dir/bugs/removed - --- old-BEtestgQtDuD/.be/dir/bugs/removed 2010-01-18 15:19:30.000000000 -0500 - +++ new-BEtestgQtDuD/.be/dir/bugs/removed 1969-12-31 19:00:00.000000000 -0500 - @@ -1 +0,0 @@ - -this entry will be deleted - \ No newline at end of file - + """_parse_diff(diff_text) -> (new,modified,removed) + + `new`, `modified`, and `removed` are lists of files. + + Example diff text:: + + Mon Jan 18 15:19:30 EST 2010 None <None@invalid.com> + * Final state + diff -rN --unified old-BEtestgQtDuD/.be/dir/bugs/modified new-BEtestgQtDuD/.be/dir/bugs/modified + --- old-BEtestgQtDuD/.be/dir/bugs/modified 2010-01-18 15:19:30.000000000 -0500 + +++ new-BEtestgQtDuD/.be/dir/bugs/modified 2010-01-18 15:19:30.000000000 -0500 + @@ -1 +1 @@ + -some value to be modified + \ No newline at end of file + +a new value + \ No newline at end of file + diff -rN --unified old-BEtestgQtDuD/.be/dir/bugs/moved new-BEtestgQtDuD/.be/dir/bugs/moved + --- old-BEtestgQtDuD/.be/dir/bugs/moved 2010-01-18 15:19:30.000000000 -0500 + +++ new-BEtestgQtDuD/.be/dir/bugs/moved 1969-12-31 19:00:00.000000000 -0500 + @@ -1 +0,0 @@ + -this entry will be moved + \ No newline at end of file + diff -rN --unified old-BEtestgQtDuD/.be/dir/bugs/moved2 new-BEtestgQtDuD/.be/dir/bugs/moved2 + --- old-BEtestgQtDuD/.be/dir/bugs/moved2 1969-12-31 19:00:00.000000000 -0500 + +++ new-BEtestgQtDuD/.be/dir/bugs/moved2 2010-01-18 15:19:30.000000000 -0500 + @@ -0,0 +1 @@ + +this entry will be moved + \ No newline at end of file + diff -rN --unified old-BEtestgQtDuD/.be/dir/bugs/new new-BEtestgQtDuD/.be/dir/bugs/new + --- old-BEtestgQtDuD/.be/dir/bugs/new 1969-12-31 19:00:00.000000000 -0500 + +++ new-BEtestgQtDuD/.be/dir/bugs/new 2010-01-18 15:19:30.000000000 -0500 + @@ -0,0 +1 @@ + +this entry is new + \ No newline at end of file + diff -rN --unified old-BEtestgQtDuD/.be/dir/bugs/removed new-BEtestgQtDuD/.be/dir/bugs/removed + --- old-BEtestgQtDuD/.be/dir/bugs/removed 2010-01-18 15:19:30.000000000 -0500 + +++ new-BEtestgQtDuD/.be/dir/bugs/removed 1969-12-31 19:00:00.000000000 -0500 + @@ -1 +0,0 @@ + -this entry will be deleted + \ No newline at end of file + """ new = [] modified = [] diff --git a/libbe/storage/vcs/git.py b/libbe/storage/vcs/git.py index c6638bc..4df9bc8 100644 --- a/libbe/storage/vcs/git.py +++ b/libbe/storage/vcs/git.py @@ -17,8 +17,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" -Git backend. +"""Git_ backend. + +.. _Git: http://git-scm.com/ """ import os @@ -40,6 +41,8 @@ def new(): return Git() class Git(base.VCS): + """:class:`base.VCS` implementation for Git. + """ name='git' client='git' @@ -179,55 +182,58 @@ class Git(base.VCS): return output def _parse_diff(self, diff_text): - """ - Example diff text: - - diff --git a/dir/changed b/dir/changed - index 6c3ea8c..2f2f7c7 100644 - --- a/dir/changed - +++ b/dir/changed - @@ -1,3 +1,3 @@ - hi - -there - +everyone and - joe - diff --git a/dir/deleted b/dir/deleted - deleted file mode 100644 - index 225ec04..0000000 - --- a/dir/deleted - +++ /dev/null - @@ -1,3 +0,0 @@ - -in - -the - -beginning - diff --git a/dir/moved b/dir/moved - deleted file mode 100644 - index 5ef102f..0000000 - --- a/dir/moved - +++ /dev/null - @@ -1,4 +0,0 @@ - -the - -ants - -go - -marching - diff --git a/dir/moved2 b/dir/moved2 - new file mode 100644 - index 0000000..5ef102f - --- /dev/null - +++ b/dir/moved2 - @@ -0,0 +1,4 @@ - +the - +ants - +go - +marching - diff --git a/dir/new b/dir/new - new file mode 100644 - index 0000000..94954ab - --- /dev/null - +++ b/dir/new - @@ -0,0 +1,2 @@ - +hello - +world + """_parse_diff(diff_text) -> (new,modified,removed) + + `new`, `modified`, and `removed` are lists of files. + + Example diff text:: + + diff --git a/dir/changed b/dir/changed + index 6c3ea8c..2f2f7c7 100644 + --- a/dir/changed + +++ b/dir/changed + @@ -1,3 +1,3 @@ + hi + -there + +everyone and + joe + diff --git a/dir/deleted b/dir/deleted + deleted file mode 100644 + index 225ec04..0000000 + --- a/dir/deleted + +++ /dev/null + @@ -1,3 +0,0 @@ + -in + -the + -beginning + diff --git a/dir/moved b/dir/moved + deleted file mode 100644 + index 5ef102f..0000000 + --- a/dir/moved + +++ /dev/null + @@ -1,4 +0,0 @@ + -the + -ants + -go + -marching + diff --git a/dir/moved2 b/dir/moved2 + new file mode 100644 + index 0000000..5ef102f + --- /dev/null + +++ b/dir/moved2 + @@ -0,0 +1,4 @@ + +the + +ants + +go + +marching + diff --git a/dir/new b/dir/new + new file mode 100644 + index 0000000..94954ab + --- /dev/null + +++ b/dir/new + @@ -0,0 +1,2 @@ + +hello + +world """ new = [] modified = [] diff --git a/libbe/storage/vcs/hg.py b/libbe/storage/vcs/hg.py index 97fc470..9378336 100644 --- a/libbe/storage/vcs/hg.py +++ b/libbe/storage/vcs/hg.py @@ -17,8 +17,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" -Mercurial (hg) backend. +"""Mercurial_ (hg) backend. + +.. _Mercurial: http://mercurial.selenic.com/ """ try: @@ -58,6 +59,8 @@ def new(): return Hg() class Hg(base.VCS): + """:class:`base.VCS` implementation for Mercurial. + """ name='hg' client=None # mercurial module @@ -177,45 +180,48 @@ class Hg(base.VCS): 'diff', '-r', revision, '--git') def _parse_diff(self, diff_text): - """ - Example diff text: + """_parse_diff(diff_text) -> (new,modified,removed) + + `new`, `modified`, and `removed` are lists of files. + + Example diff text:: - diff --git a/.be/dir/bugs/modified b/.be/dir/bugs/modified - --- a/.be/dir/bugs/modified - +++ b/.be/dir/bugs/modified - @@ -1,1 +1,1 @@ some value to be modified - -some value to be modified - \ No newline at end of file - +a new value - \ No newline at end of file - diff --git a/.be/dir/bugs/moved b/.be/dir/bugs/moved - deleted file mode 100644 - --- a/.be/dir/bugs/moved - +++ /dev/null - @@ -1,1 +0,0 @@ - -this entry will be moved - \ No newline at end of file - diff --git a/.be/dir/bugs/moved2 b/.be/dir/bugs/moved2 - new file mode 100644 - --- /dev/null - +++ b/.be/dir/bugs/moved2 - @@ -0,0 +1,1 @@ - +this entry will be moved - \ No newline at end of file - diff --git a/.be/dir/bugs/new b/.be/dir/bugs/new - new file mode 100644 - --- /dev/null - +++ b/.be/dir/bugs/new - @@ -0,0 +1,1 @@ - +this entry is new - \ No newline at end of file - diff --git a/.be/dir/bugs/removed b/.be/dir/bugs/removed - deleted file mode 100644 - --- a/.be/dir/bugs/removed - +++ /dev/null - @@ -1,1 +0,0 @@ - -this entry will be deleted - \ No newline at end of file + diff --git a/.be/dir/bugs/modified b/.be/dir/bugs/modified + --- a/.be/dir/bugs/modified + +++ b/.be/dir/bugs/modified + @@ -1,1 +1,1 @@ some value to be modified + -some value to be modified + \ No newline at end of file + +a new value + \ No newline at end of file + diff --git a/.be/dir/bugs/moved b/.be/dir/bugs/moved + deleted file mode 100644 + --- a/.be/dir/bugs/moved + +++ /dev/null + @@ -1,1 +0,0 @@ + -this entry will be moved + \ No newline at end of file + diff --git a/.be/dir/bugs/moved2 b/.be/dir/bugs/moved2 + new file mode 100644 + --- /dev/null + +++ b/.be/dir/bugs/moved2 + @@ -0,0 +1,1 @@ + +this entry will be moved + \ No newline at end of file + diff --git a/.be/dir/bugs/new b/.be/dir/bugs/new + new file mode 100644 + --- /dev/null + +++ b/.be/dir/bugs/new + @@ -0,0 +1,1 @@ + +this entry is new + \ No newline at end of file + diff --git a/.be/dir/bugs/removed b/.be/dir/bugs/removed + deleted file mode 100644 + --- a/.be/dir/bugs/removed + +++ /dev/null + @@ -1,1 +0,0 @@ + -this entry will be deleted + \ No newline at end of file """ new = [] modified = [] |