From f3e912e4a9a532bb7a0e528ff74c55ce0484fc01 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 5 Feb 2010 20:06:02 -0500 Subject: Created Sphinx framework with sphinx-quickstart and added .txt extensiosns --- doc/Makefile | 89 ++++++++ doc/README.dev | 74 ------- doc/conf.py | 194 ++++++++++++++++++ doc/distributed_bugtracking | 46 ----- doc/distributed_bugtracking.txt | 46 +++++ doc/hacking.txt | 74 +++++++ doc/index.txt | 20 ++ doc/spam | 34 ---- doc/spam.txt | 34 ++++ doc/tutorial | 442 ---------------------------------------- doc/tutorial-email | 1 - doc/tutorial-email.txt | 1 + doc/tutorial-html | 3 - doc/tutorial-html.txt | 3 + doc/tutorial.txt | 442 ++++++++++++++++++++++++++++++++++++++++ 15 files changed, 903 insertions(+), 600 deletions(-) create mode 100644 doc/Makefile delete mode 100644 doc/README.dev create mode 100644 doc/conf.py delete mode 100644 doc/distributed_bugtracking create mode 100644 doc/distributed_bugtracking.txt create mode 100644 doc/hacking.txt create mode 100644 doc/index.txt delete mode 100644 doc/spam create mode 100644 doc/spam.txt delete mode 100644 doc/tutorial delete mode 120000 doc/tutorial-email create mode 120000 doc/tutorial-email.txt delete mode 100644 doc/tutorial-html create mode 100644 doc/tutorial-html.txt create mode 100644 doc/tutorial.txt diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..df8818c --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,89 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = .build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/bugs-everywhere.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/bugs-everywhere.qhc" + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ + "run these through (pdf)latex." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/README.dev b/doc/README.dev deleted file mode 100644 index 67be177..0000000 --- a/doc/README.dev +++ /dev/null @@ -1,74 +0,0 @@ -Extending BE -============ - -Adding commands ---------------- - -To write a plugin, you simply create a new file in the -``libbe/commands/`` directory. Take a look at one of the simpler -plugins (e.g. ``remove.py``) for an example of how that looks, and to -start getting a feel for the libbe interface. - -See ``libbe/commands/base.py`` for the definition of the important -classes ``Option``, ``Argument``, ``Command``, ``InputOutput``, -``StorageCallbacks``, and ``UserInterface`` classes. You'll be -subclassing ``Command`` for your command, but all those classes will -be important. - - -Command completion -~~~~~~~~~~~~~~~~~~ - -BE implements a general framework to make it easy to support command -completion for arbitrary plugins. In order to support this system, -any of your completable ``Argument()`` instances (in your command's -``.options`` or ``.args``) should be initialized with some valid -completion_callback function. Some common cases are defined in -``libbe.command.util``. If you need more flexibility, see -``libbe.command.list``'s ``--sort`` option for an example of -extensions via ``libbe.command.util.Completer``, or write a custom -completion function from scratch. - - -Adding user interfaces ----------------------- - -Take a look at ``libbe/ui/command_line.py`` for an example. Basically -you'll need to setup a ``UserInterface`` instance for running commands. -More details to come after I write an HTML UI... - - -Testing -======= - -Run any tests in your module with:: - - be$ python test.py - -for example: - - be$ python test.py libbe.command.merge - -For a definition of "any tests", see ``test.py``'s -``add_module_tests()`` function. - -Note that you will need to run ``make`` before testing a clean BE -branch to auto-generate required files like ``libbe/_version.py``. - - -Profiling -========= - -Find out which 20 calls take the most cumulative time (time of -execution + childrens' times):: - - $ python -m cProfile -o profile be [command] [args] - $ python -c "import pstats; p=pstats.Stats('profile'); p.sort_stats('cumulative').print_stats(20)" - -It's often useful to toss:: - - import sys, traceback - print >> sys.stderr, '-'*60, '\n', '\n'.join(traceback.format_stack()[-10:]) - -into expensive functions (e.g. ``libbe.util.subproc.invoke()``) if -you're not sure why they're being called. diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..68b279f --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,194 @@ +# -*- coding: utf-8 -*- +# +# bugs-everywhere documentation build configuration file, created by +# sphinx-quickstart on Fri Feb 5 20:02:21 2010. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.append(os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['.templates'] + +# The suffix of source filenames. +source_suffix = '.txt' + +# The encoding of source files. +#source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'bugs-everywhere' +copyright = u'2010, W. Trevor King' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '432' +# The full version, including alpha/beta/rc tags. +release = '432' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['.build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['.static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_use_modindex = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = '' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'bugs-everywheredoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'bugs-everywhere.tex', u'bugs-everywhere Documentation', + u'W. Trevor King', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_use_modindex = True diff --git a/doc/distributed_bugtracking b/doc/distributed_bugtracking deleted file mode 100644 index 1164c14..0000000 --- a/doc/distributed_bugtracking +++ /dev/null @@ -1,46 +0,0 @@ -Usage Cases -=========== - -Case 1: Tracking the status of bugs in remote repo branches ------------------------------------------------------------ - -See discussion in - #bea86499-824e-4e77-b085-2d581fa9ccab/12c986be-d19a-4b8b-b1b5-68248ff4d331# -Here, it doesn't matter whether the remote repository is a branch of -the local repository, or a completely separate project -(e.g. upstream, ...). So long as the remote project provides access -via some REPO format, you can use - be --repo REPO ... -to run your query, or - be diff REPO -to see the changes between the local and remote repositories. - - -Case 2: Importing bugs from other repositories ----------------------------------------------- - -Case 2.1: If the remote repository is a branch of the local repository - VCS merge REPO -Case 2.2: If the remote repository is not a branch of the local repository - Hypothetical command: - be import REPO ID - - -Notes -===== - -Providing public repositories ------------------------------ - -e.g. for non-dev users. These are just branches that expose a public -interface (HTML, email, ...). Merge and query like any other -development branch. - - -Managing permissions --------------------- - -Many bugtrackers implement some sort of permissions system, and they -are certainly required for a central system with diverse user roles. -However DVCSs also support the 'pull my changes' workflow, where -permissions are irrelevant. diff --git a/doc/distributed_bugtracking.txt b/doc/distributed_bugtracking.txt new file mode 100644 index 0000000..1164c14 --- /dev/null +++ b/doc/distributed_bugtracking.txt @@ -0,0 +1,46 @@ +Usage Cases +=========== + +Case 1: Tracking the status of bugs in remote repo branches +----------------------------------------------------------- + +See discussion in + #bea86499-824e-4e77-b085-2d581fa9ccab/12c986be-d19a-4b8b-b1b5-68248ff4d331# +Here, it doesn't matter whether the remote repository is a branch of +the local repository, or a completely separate project +(e.g. upstream, ...). So long as the remote project provides access +via some REPO format, you can use + be --repo REPO ... +to run your query, or + be diff REPO +to see the changes between the local and remote repositories. + + +Case 2: Importing bugs from other repositories +---------------------------------------------- + +Case 2.1: If the remote repository is a branch of the local repository + VCS merge REPO +Case 2.2: If the remote repository is not a branch of the local repository + Hypothetical command: + be import REPO ID + + +Notes +===== + +Providing public repositories +----------------------------- + +e.g. for non-dev users. These are just branches that expose a public +interface (HTML, email, ...). Merge and query like any other +development branch. + + +Managing permissions +-------------------- + +Many bugtrackers implement some sort of permissions system, and they +are certainly required for a central system with diverse user roles. +However DVCSs also support the 'pull my changes' workflow, where +permissions are irrelevant. diff --git a/doc/hacking.txt b/doc/hacking.txt new file mode 100644 index 0000000..67be177 --- /dev/null +++ b/doc/hacking.txt @@ -0,0 +1,74 @@ +Extending BE +============ + +Adding commands +--------------- + +To write a plugin, you simply create a new file in the +``libbe/commands/`` directory. Take a look at one of the simpler +plugins (e.g. ``remove.py``) for an example of how that looks, and to +start getting a feel for the libbe interface. + +See ``libbe/commands/base.py`` for the definition of the important +classes ``Option``, ``Argument``, ``Command``, ``InputOutput``, +``StorageCallbacks``, and ``UserInterface`` classes. You'll be +subclassing ``Command`` for your command, but all those classes will +be important. + + +Command completion +~~~~~~~~~~~~~~~~~~ + +BE implements a general framework to make it easy to support command +completion for arbitrary plugins. In order to support this system, +any of your completable ``Argument()`` instances (in your command's +``.options`` or ``.args``) should be initialized with some valid +completion_callback function. Some common cases are defined in +``libbe.command.util``. If you need more flexibility, see +``libbe.command.list``'s ``--sort`` option for an example of +extensions via ``libbe.command.util.Completer``, or write a custom +completion function from scratch. + + +Adding user interfaces +---------------------- + +Take a look at ``libbe/ui/command_line.py`` for an example. Basically +you'll need to setup a ``UserInterface`` instance for running commands. +More details to come after I write an HTML UI... + + +Testing +======= + +Run any tests in your module with:: + + be$ python test.py + +for example: + + be$ python test.py libbe.command.merge + +For a definition of "any tests", see ``test.py``'s +``add_module_tests()`` function. + +Note that you will need to run ``make`` before testing a clean BE +branch to auto-generate required files like ``libbe/_version.py``. + + +Profiling +========= + +Find out which 20 calls take the most cumulative time (time of +execution + childrens' times):: + + $ python -m cProfile -o profile be [command] [args] + $ python -c "import pstats; p=pstats.Stats('profile'); p.sort_stats('cumulative').print_stats(20)" + +It's often useful to toss:: + + import sys, traceback + print >> sys.stderr, '-'*60, '\n', '\n'.join(traceback.format_stack()[-10:]) + +into expensive functions (e.g. ``libbe.util.subproc.invoke()``) if +you're not sure why they're being called. diff --git a/doc/index.txt b/doc/index.txt new file mode 100644 index 0000000..0aca57c --- /dev/null +++ b/doc/index.txt @@ -0,0 +1,20 @@ +.. bugs-everywhere documentation master file, created by + sphinx-quickstart on Fri Feb 5 20:02:21 2010. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to bugs-everywhere's documentation! +=========================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/doc/spam b/doc/spam deleted file mode 100644 index 4d74580..0000000 --- a/doc/spam +++ /dev/null @@ -1,34 +0,0 @@ -Removing spam commits from the history -====================================== - -arch bzr darcs git hg none - -In the case that some spam or inappropriate comment makes its way -through you interface, you can remove the offending commit XYZ with: - - If the offending commit is the last commit: - - arch: - bzr: bzr uncommit && bzr revert - darcs: darcs obliterate --last=1 - git: git reset --hard HEAD^ - hg: hg rollback && hg revert - - If the offending commit is not the last commit: - - arch: - bzr: bzr rebase -r ..-1 --onto before:XYZ . - (requires bzr-rebase plugin, note, you have to increment XYZ by - hand for , because bzr does not support "after:XYZ".) - darcs: darcs obliterate --matches 'name XYZ' - git: git rebase --onto XYZ~1 XYZ - hg: -not-supported- - (From http://hgbook.red-bean.com/read/finding-and-fixing-mistakes.html#id394667 - "Mercurial also does not provide a way to make a file or - changeset completely disappear from history, because there is no - way to enforce its disappearance") - -Note that all of these _change_the_repo_history_, so only do this on -your interface-specific repo before it interacts with any other repo. -Otherwise, you'll have to survive by cherry-picking only the good -commits. diff --git a/doc/spam.txt b/doc/spam.txt new file mode 100644 index 0000000..4d74580 --- /dev/null +++ b/doc/spam.txt @@ -0,0 +1,34 @@ +Removing spam commits from the history +====================================== + +arch bzr darcs git hg none + +In the case that some spam or inappropriate comment makes its way +through you interface, you can remove the offending commit XYZ with: + + If the offending commit is the last commit: + + arch: + bzr: bzr uncommit && bzr revert + darcs: darcs obliterate --last=1 + git: git reset --hard HEAD^ + hg: hg rollback && hg revert + + If the offending commit is not the last commit: + + arch: + bzr: bzr rebase -r ..-1 --onto before:XYZ . + (requires bzr-rebase plugin, note, you have to increment XYZ by + hand for , because bzr does not support "after:XYZ".) + darcs: darcs obliterate --matches 'name XYZ' + git: git rebase --onto XYZ~1 XYZ + hg: -not-supported- + (From http://hgbook.red-bean.com/read/finding-and-fixing-mistakes.html#id394667 + "Mercurial also does not provide a way to make a file or + changeset completely disappear from history, because there is no + way to enforce its disappearance") + +Note that all of these _change_the_repo_history_, so only do this on +your interface-specific repo before it interacts with any other repo. +Otherwise, you'll have to survive by cherry-picking only the good +commits. diff --git a/doc/tutorial b/doc/tutorial deleted file mode 100644 index 5a91bcd..0000000 --- a/doc/tutorial +++ /dev/null @@ -1,442 +0,0 @@ -Tutorial -======== - -Introduction ------------- - -Bugs Everywhere (BE) is a bugtracker built on distributed revision -control. The idea is to package the bug information with the source -code, so that developers working on the code can make appropriate -changes to the bug repository as they go. For example, by marking a -bug as "fixed" and applying the fixing changes in the same commit. -This makes it easy to see what's been going on in a particular branch -and helps keep the bug repository in sync with the code. - -However, there are some differences compared to centralized -bugtrackers. Because bugs and comments can be created by several -users in parallel, they have globally unique IDs_ rather than numbers. -There is also a developer-friendly command-line_ interface to -compliment the user-friendly web_ and email_ interfaces. This -tutorial will focus on the command-line interface as the most -powerful, and leave the web and email interfaces to other documents. - -.. _command-line: `Command-line interface`_ -.. _web: ../doc/tutorial-html -.. _email: ../doc/tutorial-email - -Installation ------------- - -If your distribution packages BE, it will be easiest to use their package. -For example, most Debian-based distributions support:: - - $ apt-get install bugs-everywhere - -Bugs ----- - -If you have any problems with BE, you can look for matching bugs:: - - $ be --repo http://bugseverywhere.org/bugs list - -If your bug isn't listed, please open a new bug:: - - $ be --repo http://bugseverywhere.org/bugs new 'bug' - Created bug with ID bea/abc - $ be --repo http://bugseverywhere.org/bugs comment bea/def - - - -Command-line interface ----------------------- - -Help -~~~~ - -All of the following information elaborates on the command help text, -which is stored in the code itself, and therefore more likely to be up -to date. You can get a list of commands and topics with:: - - $ be help - -Or see specific help on ``COMMAND`` with - - $ be help COMMAND - -for example:: - - $ be help init - -will give help on the ``init`` command. - -Initialization -~~~~~~~~~~~~~~ - -You're happily coding in your Arch_ / Bazaar_ / Darcs_ / Git_ / -Mercurial_ versioned project and you discover a bug. "Hmm, I'll need -a simple way to track these things", you think. This is where BE -comes in. One of the benefits of distributed versioning systems is -the ease of repository creation, and BE follows this trend. Just -type:: - - $ be init - Using for revision control. - BE repository initialized. - -in your project's root directory. This will create a ``.be`` -directory containing the bug repository and notify your VCS so it will -be versioned starting with your next commit. See:: - - $ be help init - -for specific details about where the ``.be`` directory will end up -if you call it from a directory besides your project's root. - -.. _Arch: http://www.gnu.org/software/gnu-arch/ -.. _Bazaar: http://bazaar.canonical.com/ -.. _Darcs: http://darcs.net/ -.. _Git: http://git-scm.com/ -.. _Mercurial: http://mercurial.selenic.com/ - -Inside the ``.be`` directory (among other things) there will be a long -UUID_ directory. This is your bug directory. The idea is that you -could keep several bug directories in the same repository, using one -to track bugs, another to track roadmap issues, etc. See IDs_ for -details. For BE itself, the bug directory is -``bea86499-824e-4e77-b085-2d581fa9ccab``, which is why all the bug and -comment IDs in this tutorial will start with ``bea/``. - -.. _UUID: http://en.wikipedia.org/wiki/Universally_Unique_Identifier - - -Creating bugs -~~~~~~~~~~~~~ - -Create new bugs with:: - - $ be new - -For example:: - - $ be new 'Missing demuxalizer functionality' - Created bug with ID bea/28f - -If you are entering a bug reported by another person, take advantage -of the ``--reporter`` option to give them credit:: - - $ be new --reporter 'John Doe ' 'Missing whatsit...' - Created bug with ID bea/81a - -See ``be help new`` for more details. - -While the bug summary should include the appropriate keywords, it -should also be brief. You should probably add a comment immediately -giving a more elaborate explanation of the problem so that the -developer understands what you want and when the bug can be considered -fixed. - -Commenting on bugs -~~~~~~~~~~~~~~~~~~ - -Bugs are like little mailing lists, and you can comment on the bug -itself or previous comments, attach files, etc. For example - - $ be comment abc/28f "Thoughts about demuxalizers..." - Created comment with ID abc/28f/97a - $ be comment abc/def/012 "Oops, I forgot to mention..." - Created comment with ID abc/28f/e88 - -Usually comments will be long enough that you'll want to compose them -in a text editor, not on the command line itself. Running ``be -comment`` without providing a ``COMMENT`` argument will try to spawn -an editor automatically (using your environment's ``VISUAL`` or -``EDITOR``, see `Guide to Unix, Environmental Variables`_). - -.. _Guide to Unix, Environmental Variables: - http://en.wikibooks.org/wiki/Guide_to_Unix/Environment_Variables - -You can also pipe the comment body in on stdin, which is especially -useful for binary attachments, etc.:: - - $ cat screenshot.png | be comment --content-type image/png bea/28f - Created comment with ID bea/28f/35d - -It's polite to insert binary attachments under comments that explain -the content and why you're attaching it, so the above should have been - - $ be comment bea/28f "Whosit dissapears when you mouse-over whatsit." - Created comment with ID bea/28f/41d - $ cat screenshot.png | be comment --content-type image/png bea/28f/41d - Created comment with ID bea/28f/35d - -For more details, see ``be help comment``. - -Showing bugs -~~~~~~~~~~~~ - -Ok, you understand how to enter bugs, but how do you get that -information back out? If you know the ID of the item you're -interested in (e.g. bug bea/28f), try:: - - $ be show bea/28f - ID : 28fb711c-5124-4128-88fe-a88a995fc519 - Short name : bea/28f - Severity : minor - Status : open - Assigned : - Reporter : - Creator : ... - Created : ... - Missing demuxalizer functionality - --------- Comment --------- - Name: bea/28f/97a - From: ... - Date: ... - - Thoughts about demuxalizers... - --------- Comment --------- - Name: bea/28f/e88 - From: ... - Date: ... - - Thoughts about demuxalizers... - --------- Comment --------- - Name: bea/28f/41d - From: ... - Date: ... - - Whosit dissapears when you mouse-over whatsit. - --------- Comment --------- - Name: bea/28f/35d - From: ... - Date: ... - - Content type image/png not printable. Try XML output instead - -You can also get a single comment body, which is useful for extracting -binary attachments:: - - $ be show --only-raw-body bea/28f/35d > screenshot.png - -There is also an XML output format, which can be useful for emailing -entries around, scripting BE, etc. - - $ be show --xml bea/35d - - - ... - -Listing bugs -~~~~~~~~~~~~ - -If you *don't* know which bug you're interested in, you can query -the whole bug directory:: - - $ be list - bea/28f:om: Missing demuxalizer functionality - bea/81a:om: Missing whatsit... - -There are a whole slew of options for filtering the list of bugs. See -``be help list`` for details. - -Showing changes -~~~~~~~~~~~~~~~ - -Often you will want to see what's going on in another dev's branch or -remind yourself what you've been working on recently. All VCSs have -some sort of ``diff`` command that shows what's changed since revision -``XYZ``. BE has it's own command that formats the bug-repository -portion of those changes in an easy-to-understand summary format. To -compare your working tree with the last commit:: - - $ be diff - New bugs: - bea/01c:om: Need command output abstraction for flexible UIs - Modified bugs: - bea/343:om: Attach tests to bugs - Changed bug settings: - creator: None -> W. Trevor King - -Compare with a previous revision ``480``:: - - $ be diff 480 - ... - -Compare your BE branch with the trunk:: - - $ be diff --repo http://bugseverywhere.org/bugs/ - -Manipulating bugs -~~~~~~~~~~~~~~~~~ - -There are several commands that allow to to set bug properties. They -are all fairly straightforward, so we will merely point them out here, -and refer you to ``be help COMMAND`` for more details. - -* ``assign``, Assign an individual or group to fix a bug -* ``depend``, Add/remove bug dependencies -* ``due``, Set bug due dates -* ``status``, Change a bug's status level -* ``severity``, Change a bug's severity level -* ``tag``, Tag a bug, or search bugs for tags -* ``target``, Assorted bug target manipulations and queries - -You can also remove bugs you feel are no longer useful with -``be remove``, and merge duplicate bugs with ``be merge``. - -Subscriptions -~~~~~~~~~~~~~ - -Since BE bugs act as mini mailing lists, we provide ``be subscribe`` -as a way to manage change notification. You can subscribe to all -the changes with:: - - $ be subscribe --types all DIR - -Subscribe only to bug creaton on bugseverywhere.org with:: - - $ be subscribe --server bugseverywhere.org --types new DIR - -Subscribe to get all the details about bug ``bea/28f``:: - - $ be subscribe --types new bea/28f - -To unsubscribe, simply repeat the subscription command adding the -``--unsubscribe`` option, but be aware that it may take some time for -these changes to propogate between distributed repositories. If you -don't feel confident in your ability to filter email, it's best to -only subscribe to the repository for which you have direct write -access. - -Managing bug directories -~~~~~~~~~~~~~~~~~~~~~~~~ - -``be set`` lets you configure a bug directory. You can set - -* ``active_status`` - The allowed active bug states and their descriptions. -* ``inactive_status`` - The allowed inactive bug states and their descriptions. -* ``severities`` - The allowed bug severities and their descriptions. -* ``target`` - The current project development target (bug UUID). -* ``extra_strings`` - Space for an array of extra strings. You usually won't bother with - this directly. - -For example, to set the current target to '1.2.3':: - - $ be set target $(be target --resolve '1.2.3') - -Import XML -~~~~~~~~~~ - -For serializing bug information (e.g. to email to a mailing list), use:: - - $ be show --xml bea/28f > bug.xml - -This information can be imported into (another) bug directory via - - $ be import-xml bug.xml - -Also distributed with BE are some utilities to convert mailboxes -into BE-XML (``be-mail-to-xml``) and convert BE-XML into mbox_ -format for reading in your mail client. - -.. _mbox: http://en.wikipedia.org/wiki/Mbox - -Export HTML -~~~~~~~~~~~ - -To create a static dump of your bug directory, use:: - - $ be html - -This is a fairly flexible command, see ``be help html`` for details. -It works pretty well as the browsable part of a public interface using -the email_ interface for interactive access. - -BE over HTTP -~~~~~~~~~~~~ - -Besides using BE to work directly with local VCS-based repositories, -you can use:: - - $ be serve - -To serve a repository over HTTP. For example:: - - $ be serve > server.log 2>&1 & - $ be --repo http://localhost:8000 list - -Of course, be careful about serving over insecure networks, since -malicous users could fill your disk with endless bugs, etc. You can -dissable write access by using the ``--read-only`` option, which would -make serving on a public network safer. - -Driving the VCS through BE -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Since BE uses internal storage drivers for its various backends, it -seemed useful to provide a uniform interface to some of the common -functionality. These commands are not intended to replace the usually -much more powerful native VCS commands, but to provide an easy means -of simple VCS-agnostic scripting for BE user interfaces, etc. - -Currently, we only expose ``be commit``, which commits all currently -pending changes. - - -IDs ---- - -Format -~~~~~~ - -BE IDs are formatted:: - - [/[/]] - -where each ``<..>`` is a UUID. For example:: - - bea86499-824e-4e77-b085-2d581fa9ccab/3438b72c-6244-4f1d-8722-8c8d41484e35 - -refers to bug ``3438b72c-6244-4f1d-8722-8c8d41484e35`` which is -located in bug directory ``bea86499-824e-4e77-b085-2d581fa9ccab``. -This is a bit of a mouthful, so you can truncate each UUID so long as -it remains unique. For example:: - - bea/343 - -If there were two bugs ``3438...`` and ``343a...`` in ``bea``, you'd -have to use:: - - bea/3438 - -BE will only truncate each UUID down to three characters to slightly -future-proof the short user ids. However, if you want to save keystrokes -and you *know* there is only one bug directory, feel free to truncate -all the way to zero characters:: - - /3438 - -Cross references -~~~~~~~~~~~~~~~~ - -To refer to other bug-directories/bugs/comments from bug comments, simply -enclose the ID in pound signs (``#``). BE will automatically expand the -truncations to the full UUIDs before storing the comment, and the reference -will be appropriately truncated (and hyperlinked, if possible) when the -comment is displayed. - -Scope -~~~~~ - -Although bug and comment IDs always appear in compound references, -UUIDs at each level are globally unique. For example, comment -``bea/343/ba96f1c0-ba48-4df8-aaf0-4e3a3144fc46`` will *only* appear -under ``bea/343``. The prefix (``bea/343``) allows BE to reduce -caching global comment-lookup tables and enables easy error messages -("I couldn't find ``bea/343/ba9`` because I don't know where the -``bea`` bug directory is located"). diff --git a/doc/tutorial-email b/doc/tutorial-email deleted file mode 120000 index b25875f..0000000 --- a/doc/tutorial-email +++ /dev/null @@ -1 +0,0 @@ -../interfaces/email/interactive/README \ No newline at end of file diff --git a/doc/tutorial-email.txt b/doc/tutorial-email.txt new file mode 120000 index 0000000..b25875f --- /dev/null +++ b/doc/tutorial-email.txt @@ -0,0 +1 @@ +../interfaces/email/interactive/README \ No newline at end of file diff --git a/doc/tutorial-html b/doc/tutorial-html deleted file mode 100644 index 0822ebd..0000000 --- a/doc/tutorial-html +++ /dev/null @@ -1,3 +0,0 @@ -There's an interactive HTML interface in the works -(http://bitbucket.org/sjl/cherryflavoredbugseverywhere/), but it's not -ready for use as a public interface yet. diff --git a/doc/tutorial-html.txt b/doc/tutorial-html.txt new file mode 100644 index 0000000..0822ebd --- /dev/null +++ b/doc/tutorial-html.txt @@ -0,0 +1,3 @@ +There's an interactive HTML interface in the works +(http://bitbucket.org/sjl/cherryflavoredbugseverywhere/), but it's not +ready for use as a public interface yet. diff --git a/doc/tutorial.txt b/doc/tutorial.txt new file mode 100644 index 0000000..5a91bcd --- /dev/null +++ b/doc/tutorial.txt @@ -0,0 +1,442 @@ +Tutorial +======== + +Introduction +------------ + +Bugs Everywhere (BE) is a bugtracker built on distributed revision +control. The idea is to package the bug information with the source +code, so that developers working on the code can make appropriate +changes to the bug repository as they go. For example, by marking a +bug as "fixed" and applying the fixing changes in the same commit. +This makes it easy to see what's been going on in a particular branch +and helps keep the bug repository in sync with the code. + +However, there are some differences compared to centralized +bugtrackers. Because bugs and comments can be created by several +users in parallel, they have globally unique IDs_ rather than numbers. +There is also a developer-friendly command-line_ interface to +compliment the user-friendly web_ and email_ interfaces. This +tutorial will focus on the command-line interface as the most +powerful, and leave the web and email interfaces to other documents. + +.. _command-line: `Command-line interface`_ +.. _web: ../doc/tutorial-html +.. _email: ../doc/tutorial-email + +Installation +------------ + +If your distribution packages BE, it will be easiest to use their package. +For example, most Debian-based distributions support:: + + $ apt-get install bugs-everywhere + +Bugs +---- + +If you have any problems with BE, you can look for matching bugs:: + + $ be --repo http://bugseverywhere.org/bugs list + +If your bug isn't listed, please open a new bug:: + + $ be --repo http://bugseverywhere.org/bugs new 'bug' + Created bug with ID bea/abc + $ be --repo http://bugseverywhere.org/bugs comment bea/def + + + +Command-line interface +---------------------- + +Help +~~~~ + +All of the following information elaborates on the command help text, +which is stored in the code itself, and therefore more likely to be up +to date. You can get a list of commands and topics with:: + + $ be help + +Or see specific help on ``COMMAND`` with + + $ be help COMMAND + +for example:: + + $ be help init + +will give help on the ``init`` command. + +Initialization +~~~~~~~~~~~~~~ + +You're happily coding in your Arch_ / Bazaar_ / Darcs_ / Git_ / +Mercurial_ versioned project and you discover a bug. "Hmm, I'll need +a simple way to track these things", you think. This is where BE +comes in. One of the benefits of distributed versioning systems is +the ease of repository creation, and BE follows this trend. Just +type:: + + $ be init + Using for revision control. + BE repository initialized. + +in your project's root directory. This will create a ``.be`` +directory containing the bug repository and notify your VCS so it will +be versioned starting with your next commit. See:: + + $ be help init + +for specific details about where the ``.be`` directory will end up +if you call it from a directory besides your project's root. + +.. _Arch: http://www.gnu.org/software/gnu-arch/ +.. _Bazaar: http://bazaar.canonical.com/ +.. _Darcs: http://darcs.net/ +.. _Git: http://git-scm.com/ +.. _Mercurial: http://mercurial.selenic.com/ + +Inside the ``.be`` directory (among other things) there will be a long +UUID_ directory. This is your bug directory. The idea is that you +could keep several bug directories in the same repository, using one +to track bugs, another to track roadmap issues, etc. See IDs_ for +details. For BE itself, the bug directory is +``bea86499-824e-4e77-b085-2d581fa9ccab``, which is why all the bug and +comment IDs in this tutorial will start with ``bea/``. + +.. _UUID: http://en.wikipedia.org/wiki/Universally_Unique_Identifier + + +Creating bugs +~~~~~~~~~~~~~ + +Create new bugs with:: + + $ be new + +For example:: + + $ be new 'Missing demuxalizer functionality' + Created bug with ID bea/28f + +If you are entering a bug reported by another person, take advantage +of the ``--reporter`` option to give them credit:: + + $ be new --reporter 'John Doe ' 'Missing whatsit...' + Created bug with ID bea/81a + +See ``be help new`` for more details. + +While the bug summary should include the appropriate keywords, it +should also be brief. You should probably add a comment immediately +giving a more elaborate explanation of the problem so that the +developer understands what you want and when the bug can be considered +fixed. + +Commenting on bugs +~~~~~~~~~~~~~~~~~~ + +Bugs are like little mailing lists, and you can comment on the bug +itself or previous comments, attach files, etc. For example + + $ be comment abc/28f "Thoughts about demuxalizers..." + Created comment with ID abc/28f/97a + $ be comment abc/def/012 "Oops, I forgot to mention..." + Created comment with ID abc/28f/e88 + +Usually comments will be long enough that you'll want to compose them +in a text editor, not on the command line itself. Running ``be +comment`` without providing a ``COMMENT`` argument will try to spawn +an editor automatically (using your environment's ``VISUAL`` or +``EDITOR``, see `Guide to Unix, Environmental Variables`_). + +.. _Guide to Unix, Environmental Variables: + http://en.wikibooks.org/wiki/Guide_to_Unix/Environment_Variables + +You can also pipe the comment body in on stdin, which is especially +useful for binary attachments, etc.:: + + $ cat screenshot.png | be comment --content-type image/png bea/28f + Created comment with ID bea/28f/35d + +It's polite to insert binary attachments under comments that explain +the content and why you're attaching it, so the above should have been + + $ be comment bea/28f "Whosit dissapears when you mouse-over whatsit." + Created comment with ID bea/28f/41d + $ cat screenshot.png | be comment --content-type image/png bea/28f/41d + Created comment with ID bea/28f/35d + +For more details, see ``be help comment``. + +Showing bugs +~~~~~~~~~~~~ + +Ok, you understand how to enter bugs, but how do you get that +information back out? If you know the ID of the item you're +interested in (e.g. bug bea/28f), try:: + + $ be show bea/28f + ID : 28fb711c-5124-4128-88fe-a88a995fc519 + Short name : bea/28f + Severity : minor + Status : open + Assigned : + Reporter : + Creator : ... + Created : ... + Missing demuxalizer functionality + --------- Comment --------- + Name: bea/28f/97a + From: ... + Date: ... + + Thoughts about demuxalizers... + --------- Comment --------- + Name: bea/28f/e88 + From: ... + Date: ... + + Thoughts about demuxalizers... + --------- Comment --------- + Name: bea/28f/41d + From: ... + Date: ... + + Whosit dissapears when you mouse-over whatsit. + --------- Comment --------- + Name: bea/28f/35d + From: ... + Date: ... + + Content type image/png not printable. Try XML output instead + +You can also get a single comment body, which is useful for extracting +binary attachments:: + + $ be show --only-raw-body bea/28f/35d > screenshot.png + +There is also an XML output format, which can be useful for emailing +entries around, scripting BE, etc. + + $ be show --xml bea/35d + + + ... + +Listing bugs +~~~~~~~~~~~~ + +If you *don't* know which bug you're interested in, you can query +the whole bug directory:: + + $ be list + bea/28f:om: Missing demuxalizer functionality + bea/81a:om: Missing whatsit... + +There are a whole slew of options for filtering the list of bugs. See +``be help list`` for details. + +Showing changes +~~~~~~~~~~~~~~~ + +Often you will want to see what's going on in another dev's branch or +remind yourself what you've been working on recently. All VCSs have +some sort of ``diff`` command that shows what's changed since revision +``XYZ``. BE has it's own command that formats the bug-repository +portion of those changes in an easy-to-understand summary format. To +compare your working tree with the last commit:: + + $ be diff + New bugs: + bea/01c:om: Need command output abstraction for flexible UIs + Modified bugs: + bea/343:om: Attach tests to bugs + Changed bug settings: + creator: None -> W. Trevor King + +Compare with a previous revision ``480``:: + + $ be diff 480 + ... + +Compare your BE branch with the trunk:: + + $ be diff --repo http://bugseverywhere.org/bugs/ + +Manipulating bugs +~~~~~~~~~~~~~~~~~ + +There are several commands that allow to to set bug properties. They +are all fairly straightforward, so we will merely point them out here, +and refer you to ``be help COMMAND`` for more details. + +* ``assign``, Assign an individual or group to fix a bug +* ``depend``, Add/remove bug dependencies +* ``due``, Set bug due dates +* ``status``, Change a bug's status level +* ``severity``, Change a bug's severity level +* ``tag``, Tag a bug, or search bugs for tags +* ``target``, Assorted bug target manipulations and queries + +You can also remove bugs you feel are no longer useful with +``be remove``, and merge duplicate bugs with ``be merge``. + +Subscriptions +~~~~~~~~~~~~~ + +Since BE bugs act as mini mailing lists, we provide ``be subscribe`` +as a way to manage change notification. You can subscribe to all +the changes with:: + + $ be subscribe --types all DIR + +Subscribe only to bug creaton on bugseverywhere.org with:: + + $ be subscribe --server bugseverywhere.org --types new DIR + +Subscribe to get all the details about bug ``bea/28f``:: + + $ be subscribe --types new bea/28f + +To unsubscribe, simply repeat the subscription command adding the +``--unsubscribe`` option, but be aware that it may take some time for +these changes to propogate between distributed repositories. If you +don't feel confident in your ability to filter email, it's best to +only subscribe to the repository for which you have direct write +access. + +Managing bug directories +~~~~~~~~~~~~~~~~~~~~~~~~ + +``be set`` lets you configure a bug directory. You can set + +* ``active_status`` + The allowed active bug states and their descriptions. +* ``inactive_status`` + The allowed inactive bug states and their descriptions. +* ``severities`` + The allowed bug severities and their descriptions. +* ``target`` + The current project development target (bug UUID). +* ``extra_strings`` + Space for an array of extra strings. You usually won't bother with + this directly. + +For example, to set the current target to '1.2.3':: + + $ be set target $(be target --resolve '1.2.3') + +Import XML +~~~~~~~~~~ + +For serializing bug information (e.g. to email to a mailing list), use:: + + $ be show --xml bea/28f > bug.xml + +This information can be imported into (another) bug directory via + + $ be import-xml bug.xml + +Also distributed with BE are some utilities to convert mailboxes +into BE-XML (``be-mail-to-xml``) and convert BE-XML into mbox_ +format for reading in your mail client. + +.. _mbox: http://en.wikipedia.org/wiki/Mbox + +Export HTML +~~~~~~~~~~~ + +To create a static dump of your bug directory, use:: + + $ be html + +This is a fairly flexible command, see ``be help html`` for details. +It works pretty well as the browsable part of a public interface using +the email_ interface for interactive access. + +BE over HTTP +~~~~~~~~~~~~ + +Besides using BE to work directly with local VCS-based repositories, +you can use:: + + $ be serve + +To serve a repository over HTTP. For example:: + + $ be serve > server.log 2>&1 & + $ be --repo http://localhost:8000 list + +Of course, be careful about serving over insecure networks, since +malicous users could fill your disk with endless bugs, etc. You can +dissable write access by using the ``--read-only`` option, which would +make serving on a public network safer. + +Driving the VCS through BE +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Since BE uses internal storage drivers for its various backends, it +seemed useful to provide a uniform interface to some of the common +functionality. These commands are not intended to replace the usually +much more powerful native VCS commands, but to provide an easy means +of simple VCS-agnostic scripting for BE user interfaces, etc. + +Currently, we only expose ``be commit``, which commits all currently +pending changes. + + +IDs +--- + +Format +~~~~~~ + +BE IDs are formatted:: + + [/[/]] + +where each ``<..>`` is a UUID. For example:: + + bea86499-824e-4e77-b085-2d581fa9ccab/3438b72c-6244-4f1d-8722-8c8d41484e35 + +refers to bug ``3438b72c-6244-4f1d-8722-8c8d41484e35`` which is +located in bug directory ``bea86499-824e-4e77-b085-2d581fa9ccab``. +This is a bit of a mouthful, so you can truncate each UUID so long as +it remains unique. For example:: + + bea/343 + +If there were two bugs ``3438...`` and ``343a...`` in ``bea``, you'd +have to use:: + + bea/3438 + +BE will only truncate each UUID down to three characters to slightly +future-proof the short user ids. However, if you want to save keystrokes +and you *know* there is only one bug directory, feel free to truncate +all the way to zero characters:: + + /3438 + +Cross references +~~~~~~~~~~~~~~~~ + +To refer to other bug-directories/bugs/comments from bug comments, simply +enclose the ID in pound signs (``#``). BE will automatically expand the +truncations to the full UUIDs before storing the comment, and the reference +will be appropriately truncated (and hyperlinked, if possible) when the +comment is displayed. + +Scope +~~~~~ + +Although bug and comment IDs always appear in compound references, +UUIDs at each level are globally unique. For example, comment +``bea/343/ba96f1c0-ba48-4df8-aaf0-4e3a3144fc46`` will *only* appear +under ``bea/343``. The prefix (``bea/343``) allows BE to reduce +caching global comment-lookup tables and enables easy error messages +("I couldn't find ``bea/343/ba9`` because I don't know where the +``bea`` bug directory is located"). -- cgit From 52999e64546aa1ca5aba14e16311d5487dbb6ac1 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 5 Feb 2010 20:08:22 -0500 Subject: Moved manpage source doc/src to doc/man --- Makefile | 2 +- doc/man/be.1.sgml | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/man/module.mk | 44 ++++++++++++++++++ doc/src/be.1.sgml | 134 ------------------------------------------------------ doc/src/module.mk | 44 ------------------ setup.py | 2 +- 6 files changed, 180 insertions(+), 180 deletions(-) create mode 100644 doc/man/be.1.sgml create mode 100644 doc/man/module.mk delete mode 100644 doc/src/be.1.sgml delete mode 100644 doc/src/module.mk diff --git a/Makefile b/Makefile index 568e42c..5e2c4f4 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ SHELL = /bin/bash PATH = /usr/bin:/bin # Directories with semantic meaning -DOC_DIR := doc/src +DOC_DIR := doc/man # Variables that will be extended by module include files GENERATED_FILES := libbe/_version.py build diff --git a/doc/man/be.1.sgml b/doc/man/be.1.sgml new file mode 100644 index 0000000..a2df21f --- /dev/null +++ b/doc/man/be.1.sgml @@ -0,0 +1,134 @@ + manpage.1'. You may view + the manual page with: `docbook-to-man manpage.sgml | nroff -man | + less'. A typical entry in a Makefile or Makefile.am is: + +be.1: be.1.sgml + docbook-to-man $< > $@ + + The docbook-to-man binary is found in the docbook-to-man package. + Please remember that if you create the nroff version in one of the + debian/rules file targets (such as build), you will need to include + docbook-to-man in your Build-Depends control field. + + --> + + Ben"> + Finney"> + + 2009-06-25"> + + 1"> + ben+debian@benfinney.id.au"> + + BUGS-EVERYWHERE"> + + + BE"> + + + Debian"> + GNU"> + GPL"> +]> + + + +
+ &dhemail; +
+ + &dhfirstname; + &dhsurname; + + + 2009 + &dhusername; + + &dhdate; +
+ + &uccmdname; + + &dhsection; + + + &cmdname; + + distributed bug tracker + + + + &cmdname; + command + command_options ... + command_args ... + + + &cmdname; help + + + &cmdname; help + command + + + + DESCRIPTION + + This manual page documents briefly the + &cmdname; command, part of the + &pkgfullname; package. + + &cmdname; allows commandline interaction + with the &pkgfullname; database in a project tree. + + + + COMMANDS + + + help + + + Print help for be and a list of all available commands. + + + + + + + AUTHOR + + This manual page was written by &dhusername; <&dhemail;> for + the &debian; system (but may be used by others). Permission is + granted to copy, distribute and/or modify this document under + the terms of the &gnu; General Public License, Version 2 or any + later version published by the Free Software Foundation. + + + On Debian systems, the complete text of the GNU General Public + License can be found in /usr/share/common-licenses/GPL. + + + +
+ + diff --git a/doc/man/module.mk b/doc/man/module.mk new file mode 100644 index 0000000..1eb6955 --- /dev/null +++ b/doc/man/module.mk @@ -0,0 +1,44 @@ +# :vim: filetype=make : -*- makefile; coding: utf-8; -*- + +# doc/module.mk +# Part of Bugs Everywhere, a distributed bug tracking system. +# +# Copyright (C) 2008-2010 Chris Ball +# Gianluca Montecchi +# W. Trevor King +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# Makefile module for documentation + +MODULE_DIR := doc/man + +MANPAGES = be.1 +manpage_files = $(patsubst %,${MODULE_DIR}/%,${MANPAGES}) + +GENERATED_FILES += ${manpage_files} + + +.PHONY: doc +doc: man + +build: doc + + +.PHONY: man +man: ${manpage_files} + +%.1: %.1.sgml + docbook-to-man $< > $@ diff --git a/doc/src/be.1.sgml b/doc/src/be.1.sgml deleted file mode 100644 index a2df21f..0000000 --- a/doc/src/be.1.sgml +++ /dev/null @@ -1,134 +0,0 @@ - manpage.1'. You may view - the manual page with: `docbook-to-man manpage.sgml | nroff -man | - less'. A typical entry in a Makefile or Makefile.am is: - -be.1: be.1.sgml - docbook-to-man $< > $@ - - The docbook-to-man binary is found in the docbook-to-man package. - Please remember that if you create the nroff version in one of the - debian/rules file targets (such as build), you will need to include - docbook-to-man in your Build-Depends control field. - - --> - - Ben"> - Finney"> - - 2009-06-25"> - - 1"> - ben+debian@benfinney.id.au"> - - BUGS-EVERYWHERE"> - - - BE"> - - - Debian"> - GNU"> - GPL"> -]> - - - -
- &dhemail; -
- - &dhfirstname; - &dhsurname; - - - 2009 - &dhusername; - - &dhdate; -
- - &uccmdname; - - &dhsection; - - - &cmdname; - - distributed bug tracker - - - - &cmdname; - command - command_options ... - command_args ... - - - &cmdname; help - - - &cmdname; help - command - - - - DESCRIPTION - - This manual page documents briefly the - &cmdname; command, part of the - &pkgfullname; package. - - &cmdname; allows commandline interaction - with the &pkgfullname; database in a project tree. - - - - COMMANDS - - - help - - - Print help for be and a list of all available commands. - - - - - - - AUTHOR - - This manual page was written by &dhusername; <&dhemail;> for - the &debian; system (but may be used by others). Permission is - granted to copy, distribute and/or modify this document under - the terms of the &gnu; General Public License, Version 2 or any - later version published by the Free Software Foundation. - - - On Debian systems, the complete text of the GNU General Public - License can be found in /usr/share/common-licenses/GPL. - - - -
- - diff --git a/doc/src/module.mk b/doc/src/module.mk deleted file mode 100644 index ea4eacc..0000000 --- a/doc/src/module.mk +++ /dev/null @@ -1,44 +0,0 @@ -# :vim: filetype=make : -*- makefile; coding: utf-8; -*- - -# doc/module.mk -# Part of Bugs Everywhere, a distributed bug tracking system. -# -# Copyright (C) 2008-2010 Chris Ball -# Gianluca Montecchi -# W. Trevor King -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -# Makefile module for documentation - -MODULE_DIR := doc/src - -MANPAGES = be.1 -manpage_files = $(patsubst %,${MODULE_DIR}/%,${MANPAGES}) - -GENERATED_FILES += ${manpage_files} - - -.PHONY: doc -doc: man - -build: doc - - -.PHONY: man -man: ${manpage_files} - -%.1: %.1.sgml - docbook-to-man $< > $@ diff --git a/setup.py b/setup.py index 2bb724c..ab0a608 100755 --- a/setup.py +++ b/setup.py @@ -21,6 +21,6 @@ setup( 'libbe.util'], scripts=['be'], data_files=[ - ('share/man/man1', ['doc/src/be.1']), + ('share/man/man1', ['doc/man/be.1']), ] ) -- cgit From 4109434843ecdf2e73a010fcbb37cc3d404ef8ef Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Fri, 5 Feb 2010 20:09:58 -0500 Subject: Sphynx now gets version string automatically. --- doc/conf.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 68b279f..b2dab25 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -13,6 +13,8 @@ import sys, os +import libbe.version + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -45,9 +47,9 @@ copyright = u'2010, W. Trevor King' # built documents. # # The short X.Y version. -version = '432' +version = libbe.version.version() # The full version, including alpha/beta/rc tags. -release = '432' +release = libbe.version.version() # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -- cgit From da8309e67c669b1cca5d39c8e7da34c9b431bef6 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 6 Feb 2010 09:47:20 -0500 Subject: Added page titles to the documentation & adjusted section levels. --- README | 22 ++++---- doc/distributed_bugtracking.txt | 4 ++ doc/email.txt | 1 + doc/hacking.txt | 11 ++-- doc/html.txt | 7 +++ doc/index.txt | 84 +++++++++++++++++++++++++++--- doc/spam.txt | 5 ++ doc/tutorial-email.txt | 1 - doc/tutorial-html.txt | 3 -- doc/tutorial.txt | 101 ++++++++++-------------------------- interfaces/email/interactive/README | 4 ++ 11 files changed, 142 insertions(+), 101 deletions(-) create mode 120000 doc/email.txt create mode 100644 doc/html.txt delete mode 120000 doc/tutorial-email.txt delete mode 100644 doc/tutorial-html.txt diff --git a/README b/README index b39fca1..cba2738 100644 --- a/README +++ b/README @@ -10,6 +10,7 @@ The idea is to package the bug information with the source code, so that bugs can be marked "fixed" in the branches that fix them. So, instead of numbers, bugs have globally unique ids. + Getting BE ========== @@ -38,8 +39,9 @@ Getting started To get started, you must set the bugtracker root. Typically, you will want to set the bug root to your project root, so that Bugs Everywhere works in any -part of your project tree. -$ be init -r $PROJECT_ROOT +part of your project tree.:: + + $ be init -r $PROJECT_ROOT To create bugs, use ``be new $DESCRIPTION``. To comment on bugs, you can can use ``be comment $BUG_ID``. To close a bug, use @@ -47,18 +49,16 @@ can can use ``be comment $BUG_ID``. To close a bug, use commands, see ``be help``. You can also look at the usage examples in ``test_usage.sh``. + Documentation ============= -If ``be help`` isn't scratching your itch, there's also - -* doc/tutorial (a gentle introduction to BE) -* doc/distributed_bugtracking (notes on distributed workflows) -* doc/spam (notes on removing spam entries from VCSs) -* doc/README.dev (a guide to hacking BE) - -The documentation is marked up in reStructuredText_, so you can use -the docutils_ to convert it to other formats if you desire. +If ``be help`` isn't scratching your itch, the full documentation is +available in the doc directory as reStructuredText_ . You can build +the full documentation with Sphinx_ , convert single files with +docutils_ , or browse through the doc directory by hand. +doc/index.txt is a good place to start. .. _reStructuredText: http://docutils.sourceforge.net/docs/user/rst/quickref.html +.. _Sphinx: http://sphinx.pocoo.org/ .. _docutils: http://docutils.sourceforge.net/ diff --git a/doc/distributed_bugtracking.txt b/doc/distributed_bugtracking.txt index 1164c14..f3d574c 100644 --- a/doc/distributed_bugtracking.txt +++ b/doc/distributed_bugtracking.txt @@ -1,3 +1,7 @@ +*********************** +Distributed Bugtracking +*********************** + Usage Cases =========== diff --git a/doc/email.txt b/doc/email.txt new file mode 120000 index 0000000..b25875f --- /dev/null +++ b/doc/email.txt @@ -0,0 +1 @@ +../interfaces/email/interactive/README \ No newline at end of file diff --git a/doc/hacking.txt b/doc/hacking.txt index 67be177..5b075f9 100644 --- a/doc/hacking.txt +++ b/doc/hacking.txt @@ -1,8 +1,9 @@ -Extending BE -============ +********** +Hacking BE +********** Adding commands ---------------- +=============== To write a plugin, you simply create a new file in the ``libbe/commands/`` directory. Take a look at one of the simpler @@ -17,7 +18,7 @@ be important. Command completion -~~~~~~~~~~~~~~~~~~ +------------------ BE implements a general framework to make it easy to support command completion for arbitrary plugins. In order to support this system, @@ -31,7 +32,7 @@ completion function from scratch. Adding user interfaces ----------------------- +====================== Take a look at ``libbe/ui/command_line.py`` for an example. Basically you'll need to setup a ``UserInterface`` instance for running commands. diff --git a/doc/html.txt b/doc/html.txt new file mode 100644 index 0000000..9e7f114 --- /dev/null +++ b/doc/html.txt @@ -0,0 +1,7 @@ +************** +HTML Interface +************** + +There's an interactive HTML interface in the works +(http://bitbucket.org/sjl/cherryflavoredbugseverywhere/), but it's not +ready for use as a public interface yet. diff --git a/doc/index.txt b/doc/index.txt index 0aca57c..55ee543 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -1,16 +1,35 @@ -.. bugs-everywhere documentation master file, created by - sphinx-quickstart on Fri Feb 5 20:02:21 2010. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. +Welcome to the bugs-everywhere documentation! +============================================= -Welcome to bugs-everywhere's documentation! -=========================================== +Bugs Everywhere (BE) is a bugtracker built on distributed revision +control. It works with Arch_ , Bazaar_ , Darcs_ , Git_ , and +Mercurial_ at the moment, but is easily extensible. It can also +function with no RCS at all. + +.. _Arch: http://www.gnu.org/software/gnu-arch/ +.. _Bazaar: http://bazaar.canonical.com/ +.. _Darcs: http://darcs.net/ +.. _Git: http://git-scm.com/ +.. _Mercurial: http://mercurial.selenic.com/ + +The idea is to package the bug information with the source code, so +that bugs can be marked "fixed" in the branches that fix them. So, +instead of numbers, bugs have globally unique ids. Contents: .. toctree:: :maxdepth: 2 + tutorial.txt + ids.txt + email.txt + html.txt + distributed_bugtracking.txt + hacking.txt + spam.txt + + Indices and tables ================== @@ -18,3 +37,56 @@ Indices and tables * :ref:`modindex` * :ref:`search` +Getting BE +========== + +BE is available as a bzr repository:: + + $ bzr branch http://bzr.bugseverywhere.org/be + +See the homepage_ for details. If you do branch the bzr repo, you'll +need to run:: + + $ make + +to build some auto-generated files (e.g. ``libbe/_version.py``), and:: + + $ make install + +to install BE. By default BE will install into your home directory, +but you can tweak the ``PREFIX`` variable in ``Makefile`` to install +to another location. + +.. _homepage: http://bugseverywhere.org/ + + +Getting started +=============== + +To get started, you must set the bugtracker root. Typically, you will +want to set the bug root to your project root, so that Bugs Everywhere +works in any part of your project tree.:: + + $ be init -r $PROJECT_ROOT + +To create bugs, use ``be new $DESCRIPTION``. To comment on bugs, you +can can use ``be comment $BUG_ID``. To close a bug, use +``be close $BUG_ID`` or ``be status $BUG_ID fixed``. For more +commands, see ``be help``. You can also look at the usage examples in +``test_usage.sh``. + +Documentation +============= + +If ``be help`` isn't scratching your itch, there's also + +* doc/tutorial (a gentle introduction to BE) +* doc/distributed_bugtracking (notes on distributed workflows) +* doc/spam (notes on removing spam entries from VCSs) +* doc/README.dev (a guide to hacking BE) + +The documentation is marked up in reStructuredText_, so you can use +the docutils_ to convert it to other formats if you desire. + +.. _reStructuredText: http://docutils.sourceforge.net/docs/user/rst/quickref.html +.. _docutils: http://docutils.sourceforge.net/ diff --git a/doc/spam.txt b/doc/spam.txt index 4d74580..c0a2ba3 100644 --- a/doc/spam.txt +++ b/doc/spam.txt @@ -1,3 +1,8 @@ +***************** +Dealing with spam +***************** + + Removing spam commits from the history ====================================== diff --git a/doc/tutorial-email.txt b/doc/tutorial-email.txt deleted file mode 120000 index b25875f..0000000 --- a/doc/tutorial-email.txt +++ /dev/null @@ -1 +0,0 @@ -../interfaces/email/interactive/README \ No newline at end of file diff --git a/doc/tutorial-html.txt b/doc/tutorial-html.txt deleted file mode 100644 index 0822ebd..0000000 --- a/doc/tutorial-html.txt +++ /dev/null @@ -1,3 +0,0 @@ -There's an interactive HTML interface in the works -(http://bitbucket.org/sjl/cherryflavoredbugseverywhere/), but it's not -ready for use as a public interface yet. diff --git a/doc/tutorial.txt b/doc/tutorial.txt index 5a91bcd..3dd7ff3 100644 --- a/doc/tutorial.txt +++ b/doc/tutorial.txt @@ -1,8 +1,9 @@ +******** Tutorial -======== +******** Introduction ------------- +============ Bugs Everywhere (BE) is a bugtracker built on distributed revision control. The idea is to package the bug information with the source @@ -21,11 +22,12 @@ tutorial will focus on the command-line interface as the most powerful, and leave the web and email interfaces to other documents. .. _command-line: `Command-line interface`_ -.. _web: ../doc/tutorial-html -.. _email: ../doc/tutorial-email +.. _web: tutorial-html.txt +.. _email: tutorial-email.txt +.. _IDs: ids.txt Installation ------------- +============ If your distribution packages BE, it will be easiest to use their package. For example, most Debian-based distributions support:: @@ -33,7 +35,7 @@ For example, most Debian-based distributions support:: $ apt-get install bugs-everywhere Bugs ----- +==== If you have any problems with BE, you can look for matching bugs:: @@ -48,10 +50,10 @@ If your bug isn't listed, please open a new bug:: Command-line interface ----------------------- +====================== Help -~~~~ +---- All of the following information elaborates on the command help text, which is stored in the code itself, and therefore more likely to be up @@ -70,7 +72,7 @@ for example:: will give help on the ``init`` command. Initialization -~~~~~~~~~~~~~~ +-------------- You're happily coding in your Arch_ / Bazaar_ / Darcs_ / Git_ / Mercurial_ versioned project and you discover a bug. "Hmm, I'll need @@ -110,7 +112,7 @@ comment IDs in this tutorial will start with ``bea/``. Creating bugs -~~~~~~~~~~~~~ +------------- Create new bugs with:: @@ -136,7 +138,7 @@ developer understands what you want and when the bug can be considered fixed. Commenting on bugs -~~~~~~~~~~~~~~~~~~ +------------------ Bugs are like little mailing lists, and you can comment on the bug itself or previous comments, attach files, etc. For example @@ -172,7 +174,7 @@ the content and why you're attaching it, so the above should have been For more details, see ``be help comment``. Showing bugs -~~~~~~~~~~~~ +------------ Ok, you understand how to enter bugs, but how do you get that information back out? If you know the ID of the item you're @@ -227,7 +229,7 @@ entries around, scripting BE, etc. ... Listing bugs -~~~~~~~~~~~~ +------------ If you *don't* know which bug you're interested in, you can query the whole bug directory:: @@ -240,7 +242,7 @@ There are a whole slew of options for filtering the list of bugs. See ``be help list`` for details. Showing changes -~~~~~~~~~~~~~~~ +--------------- Often you will want to see what's going on in another dev's branch or remind yourself what you've been working on recently. All VCSs have @@ -267,7 +269,7 @@ Compare your BE branch with the trunk:: $ be diff --repo http://bugseverywhere.org/bugs/ Manipulating bugs -~~~~~~~~~~~~~~~~~ +----------------- There are several commands that allow to to set bug properties. They are all fairly straightforward, so we will merely point them out here, @@ -285,7 +287,7 @@ You can also remove bugs you feel are no longer useful with ``be remove``, and merge duplicate bugs with ``be merge``. Subscriptions -~~~~~~~~~~~~~ +------------- Since BE bugs act as mini mailing lists, we provide ``be subscribe`` as a way to manage change notification. You can subscribe to all @@ -309,7 +311,7 @@ only subscribe to the repository for which you have direct write access. Managing bug directories -~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------ ``be set`` lets you configure a bug directory. You can set @@ -330,7 +332,7 @@ For example, to set the current target to '1.2.3':: $ be set target $(be target --resolve '1.2.3') Import XML -~~~~~~~~~~ +---------- For serializing bug information (e.g. to email to a mailing list), use:: @@ -347,7 +349,7 @@ format for reading in your mail client. .. _mbox: http://en.wikipedia.org/wiki/Mbox Export HTML -~~~~~~~~~~~ +----------- To create a static dump of your bug directory, use:: @@ -358,7 +360,7 @@ It works pretty well as the browsable part of a public interface using the email_ interface for interactive access. BE over HTTP -~~~~~~~~~~~~ +------------ Besides using BE to work directly with local VCS-based repositories, you can use:: @@ -376,7 +378,7 @@ dissable write access by using the ``--read-only`` option, which would make serving on a public network safer. Driving the VCS through BE -~~~~~~~~~~~~~~~~~~~~~~~~~~ +-------------------------- Since BE uses internal storage drivers for its various backends, it seemed useful to provide a uniform interface to some of the common @@ -384,59 +386,8 @@ functionality. These commands are not intended to replace the usually much more powerful native VCS commands, but to provide an easy means of simple VCS-agnostic scripting for BE user interfaces, etc. -Currently, we only expose ``be commit``, which commits all currently -pending changes. - - -IDs ---- - -Format +Commit ~~~~~~ -BE IDs are formatted:: - - [/[/]] - -where each ``<..>`` is a UUID. For example:: - - bea86499-824e-4e77-b085-2d581fa9ccab/3438b72c-6244-4f1d-8722-8c8d41484e35 - -refers to bug ``3438b72c-6244-4f1d-8722-8c8d41484e35`` which is -located in bug directory ``bea86499-824e-4e77-b085-2d581fa9ccab``. -This is a bit of a mouthful, so you can truncate each UUID so long as -it remains unique. For example:: - - bea/343 - -If there were two bugs ``3438...`` and ``343a...`` in ``bea``, you'd -have to use:: - - bea/3438 - -BE will only truncate each UUID down to three characters to slightly -future-proof the short user ids. However, if you want to save keystrokes -and you *know* there is only one bug directory, feel free to truncate -all the way to zero characters:: - - /3438 - -Cross references -~~~~~~~~~~~~~~~~ - -To refer to other bug-directories/bugs/comments from bug comments, simply -enclose the ID in pound signs (``#``). BE will automatically expand the -truncations to the full UUIDs before storing the comment, and the reference -will be appropriately truncated (and hyperlinked, if possible) when the -comment is displayed. - -Scope -~~~~~ - -Although bug and comment IDs always appear in compound references, -UUIDs at each level are globally unique. For example, comment -``bea/343/ba96f1c0-ba48-4df8-aaf0-4e3a3144fc46`` will *only* appear -under ``bea/343``. The prefix (``bea/343``) allows BE to reduce -caching global comment-lookup tables and enables easy error messages -("I couldn't find ``bea/343/ba9`` because I don't know where the -``bea`` bug directory is located"). +Currently, we only expose ``be commit``, which commits all currently +pending changes. diff --git a/interfaces/email/interactive/README b/interfaces/email/interactive/README index f7d57ad..48bccdd 100644 --- a/interfaces/email/interactive/README +++ b/interfaces/email/interactive/README @@ -1,3 +1,7 @@ +*************** +Email Interface +*************** + Overview ======== -- cgit From 23b523c3ddd527012f0ae18dc2b3da2fadcfdae2 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 6 Feb 2010 11:06:32 -0500 Subject: Broke out install.txt + reStructuredText markup fixes --- README | 4 +- doc/distributed_bugtracking.txt | 29 ++++++++------ doc/index.txt | 63 +++---------------------------- doc/install.txt | 55 +++++++++++++++++++++++++++ doc/spam.txt | 83 ++++++++++++++++++++++++++--------------- 5 files changed, 132 insertions(+), 102 deletions(-) create mode 100644 doc/install.txt diff --git a/README b/README index cba2738..c0bae43 100644 --- a/README +++ b/README @@ -1,9 +1,9 @@ Bugs Everywhere =============== -This is Bugs Everywhere (BE), a bugtracker built on distributed revision +This is Bugs Everywhere (BE), a bugtracker built on distributed version control. It works with Arch, Bazaar, Darcs, Git, and Mercurial at the -moment, but is easily extensible. It can also function with no RCS at +moment, but is easily extensible. It can also function with no VCS at all. The idea is to package the bug information with the source code, so that diff --git a/doc/distributed_bugtracking.txt b/doc/distributed_bugtracking.txt index f3d574c..5ca42a6 100644 --- a/doc/distributed_bugtracking.txt +++ b/doc/distributed_bugtracking.txt @@ -8,26 +8,33 @@ Usage Cases Case 1: Tracking the status of bugs in remote repo branches ----------------------------------------------------------- -See discussion in - #bea86499-824e-4e77-b085-2d581fa9ccab/12c986be-d19a-4b8b-b1b5-68248ff4d331# +See the discussion in +#bea86499-824e-4e77-b085-2d581fa9ccab/12c986be-d19a-4b8b-b1b5-68248ff4d331#. Here, it doesn't matter whether the remote repository is a branch of the local repository, or a completely separate project (e.g. upstream, ...). So long as the remote project provides access -via some REPO format, you can use - be --repo REPO ... -to run your query, or - be diff REPO +via some REPO format, you can use:: + + $ be --repo REPO ... + +to run your query, or:: + + $ be diff REPO + to see the changes between the local and remote repositories. Case 2: Importing bugs from other repositories ---------------------------------------------- -Case 2.1: If the remote repository is a branch of the local repository - VCS merge REPO +Case 2.1: If the remote repository is a branch of the local repository:: + + $ merge + Case 2.2: If the remote repository is not a branch of the local repository - Hypothetical command: - be import REPO ID +(Hypothetical command):: + + $ be import Notes @@ -46,5 +53,5 @@ Managing permissions Many bugtrackers implement some sort of permissions system, and they are certainly required for a central system with diverse user roles. -However DVCSs also support the 'pull my changes' workflow, where +However DVCSs also support the "pull my changes" workflow, where permissions are irrelevant. diff --git a/doc/index.txt b/doc/index.txt index 55ee543..f1c1542 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -1,10 +1,10 @@ Welcome to the bugs-everywhere documentation! ============================================= -Bugs Everywhere (BE) is a bugtracker built on distributed revision +Bugs Everywhere (BE) is a bugtracker built on distributed version control. It works with Arch_ , Bazaar_ , Darcs_ , Git_ , and Mercurial_ at the moment, but is easily extensible. It can also -function with no RCS at all. +function with no VCS at all. .. _Arch: http://www.gnu.org/software/gnu-arch/ .. _Bazaar: http://bazaar.canonical.com/ @@ -13,14 +13,15 @@ function with no RCS at all. .. _Mercurial: http://mercurial.selenic.com/ The idea is to package the bug information with the source code, so -that bugs can be marked "fixed" in the branches that fix them. So, -instead of numbers, bugs have globally unique ids. +that bugs can be marked "fixed" in the branches that fix them. + Contents: .. toctree:: :maxdepth: 2 + install.txt tutorial.txt ids.txt email.txt @@ -36,57 +37,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - -Getting BE -========== - -BE is available as a bzr repository:: - - $ bzr branch http://bzr.bugseverywhere.org/be - -See the homepage_ for details. If you do branch the bzr repo, you'll -need to run:: - - $ make - -to build some auto-generated files (e.g. ``libbe/_version.py``), and:: - - $ make install - -to install BE. By default BE will install into your home directory, -but you can tweak the ``PREFIX`` variable in ``Makefile`` to install -to another location. - -.. _homepage: http://bugseverywhere.org/ - - -Getting started -=============== - -To get started, you must set the bugtracker root. Typically, you will -want to set the bug root to your project root, so that Bugs Everywhere -works in any part of your project tree.:: - - $ be init -r $PROJECT_ROOT - -To create bugs, use ``be new $DESCRIPTION``. To comment on bugs, you -can can use ``be comment $BUG_ID``. To close a bug, use -``be close $BUG_ID`` or ``be status $BUG_ID fixed``. For more -commands, see ``be help``. You can also look at the usage examples in -``test_usage.sh``. - -Documentation -============= - -If ``be help`` isn't scratching your itch, there's also - -* doc/tutorial (a gentle introduction to BE) -* doc/distributed_bugtracking (notes on distributed workflows) -* doc/spam (notes on removing spam entries from VCSs) -* doc/README.dev (a guide to hacking BE) - -The documentation is marked up in reStructuredText_, so you can use -the docutils_ to convert it to other formats if you desire. - -.. _reStructuredText: http://docutils.sourceforge.net/docs/user/rst/quickref.html -.. _docutils: http://docutils.sourceforge.net/ diff --git a/doc/install.txt b/doc/install.txt new file mode 100644 index 0000000..b1d153e --- /dev/null +++ b/doc/install.txt @@ -0,0 +1,55 @@ +************* +Installing BE +************* + +Bazaar repository +================= + +BE is available as a bzr repository:: + + $ bzr branch http://bzr.bugseverywhere.org/be + +See the homepage_ for details. If you do branch the bzr repo, you'll +need to run:: + + $ make + +to build some auto-generated files (e.g. ``libbe/_version.py``), and:: + + $ make install + +to install BE. By default BE will install into your home directory, +but you can tweak the ``PREFIX`` variable in ``Makefile`` to install +to another location. + +.. _homepage: http://bugseverywhere.org/ + + +Release tarballs +================ + +For those not interested in the cutting edge, or those who don't want +to worry about installing Bazaar, we'll post release tarballs somewhere +(once we actually make a release). After you've downloaded the release +tarball, unpack it with:: + + $ tar -xzvf be-.tar.gz + +And install it with::: + + $ cd be- + $ make install + + +Distribution packages +===================== + +Some distributions (Debian_ , Ubuntu_ , others?) package BE. If +you're running one of those distributions, you can install the package +with your regular package manager. For Debian, Ubuntu, and related +distros, that's:: + + $ apt-get install bugs-everywhere + +.. _Debian: http://packages.debian.org/sid/bugs-everywhere +.. _Ubuntu: http://packages.ubuntu.com/lucid/bugs-everywhere diff --git a/doc/spam.txt b/doc/spam.txt index c0a2ba3..39e7a86 100644 --- a/doc/spam.txt +++ b/doc/spam.txt @@ -2,38 +2,59 @@ Dealing with spam ***************** - -Removing spam commits from the history -====================================== - -arch bzr darcs git hg none - In the case that some spam or inappropriate comment makes its way -through you interface, you can remove the offending commit XYZ with: - - If the offending commit is the last commit: - - arch: - bzr: bzr uncommit && bzr revert - darcs: darcs obliterate --last=1 - git: git reset --hard HEAD^ - hg: hg rollback && hg revert - - If the offending commit is not the last commit: - - arch: - bzr: bzr rebase -r ..-1 --onto before:XYZ . - (requires bzr-rebase plugin, note, you have to increment XYZ by - hand for , because bzr does not support "after:XYZ".) - darcs: darcs obliterate --matches 'name XYZ' - git: git rebase --onto XYZ~1 XYZ - hg: -not-supported- - (From http://hgbook.red-bean.com/read/finding-and-fixing-mistakes.html#id394667 - "Mercurial also does not provide a way to make a file or - changeset completely disappear from history, because there is no - way to enforce its disappearance") - -Note that all of these _change_the_repo_history_, so only do this on +through you interface, you can (sometimes) remove the offending commit +``XYZ``. + + +If the offending commit is the last commit +========================================== + ++-------+----------------------------+ +| arch | | ++-------+----------------------------+ +| bzr | bzr uncommit && bzr revert | ++-------+----------------------------+ +| darcs | darcs obliterate --last=1 | ++-------+----------------------------+ +| git | git reset --hard HEAD^ | ++-------+----------------------------+ +| hg | hg rollback && hg revert | ++-------+----------------------------+ + +If the offending commit is not the last commit +============================================== + ++----------+-----------------------------------------------+ +| arch | | ++----------+-----------------------------------------------+ +| bzr [#]_ | bzr rebase -r ..-1 --onto before:XYZ . | ++----------+-----------------------------------------------+ +| darcs | darcs obliterate --matches 'name XYZ' | ++----------+-----------------------------------------------+ +| git | git rebase --onto XYZ~1 XYZ | ++----------+-----------------------------------------------+ +| hg [#]_ | | ++----------+-----------------------------------------------+ + +.. [#] Requires the ```bzr-rebase`` plugin`_. Note, you have to + increment ``XYZ`` by hand for ````, because ``bzr`` does not + support ``after:XYZ``. + +.. [#] From `Mercurial: The Definitive Guide`: + + "Mercurial also does not provide a way to make a file or + changeset completely disappear from history, because there is no + way to enforce its disappearance" + +.. _bzr-rebase plugin: http://wiki.bazaar.canonical.com/Rebase +.. _Mercurial: The Definitive Guide: + http://hgbook.red-bean.com/read/finding-and-fixing-mistakes.html#id394667 + +Warnings about changing history +=============================== + +Note that all of these *change the repo history* , so only do this on your interface-specific repo before it interacts with any other repo. Otherwise, you'll have to survive by cherry-picking only the good commits. -- cgit From 668be5b01d9ad47f4f9714bfe8deee377341eb35 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 6 Feb 2010 13:09:24 -0500 Subject: Added libbe.bug to the Sphinx documentation --- doc/conf.py | 5 +++-- doc/ids.txt | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/index.txt | 2 +- doc/libbe.bug.txt | 7 +++++++ doc/libbe.txt | 12 ++++++++++++ libbe/bug.py | 12 ++++++++---- 6 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 doc/ids.txt create mode 100644 doc/libbe.bug.txt create mode 100644 doc/libbe.txt diff --git a/doc/conf.py b/doc/conf.py index b2dab25..ea03b08 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -13,12 +13,13 @@ import sys, os -import libbe.version - # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.append(os.path.abspath('.')) +sys.path.insert(0, os.path.abspath('..')) + +import libbe.version # -- General configuration ----------------------------------------------------- diff --git a/doc/ids.txt b/doc/ids.txt new file mode 100644 index 0000000..ba1837b --- /dev/null +++ b/doc/ids.txt @@ -0,0 +1,53 @@ +********** +Object IDs +********** + +Format +====== + +BE IDs are formatted:: + + [/[/]] + +where each ``<..>`` is a UUID. For example:: + + bea86499-824e-4e77-b085-2d581fa9ccab/3438b72c-6244-4f1d-8722-8c8d41484e35 + +refers to bug ``3438b72c-6244-4f1d-8722-8c8d41484e35`` which is +located in bug directory ``bea86499-824e-4e77-b085-2d581fa9ccab``. +This is a bit of a mouthful, so you can truncate each UUID so long as +it remains unique. For example:: + + bea/343 + +If there were two bugs ``3438...`` and ``343a...`` in ``bea``, you'd +have to use:: + + bea/3438 + +BE will only truncate each UUID down to three characters to slightly +future-proof the short user ids. However, if you want to save keystrokes +and you *know* there is only one bug directory, feel free to truncate +all the way to zero characters:: + + /3438 + +Cross references +================ + +To refer to other bug-directories/bugs/comments from bug comments, simply +enclose the ID in pound signs (``#``). BE will automatically expand the +truncations to the full UUIDs before storing the comment, and the reference +will be appropriately truncated (and hyperlinked, if possible) when the +comment is displayed. + +Scope +===== + +Although bug and comment IDs always appear in compound references, +UUIDs at each level are globally unique. For example, comment +``bea/343/ba96f1c0-ba48-4df8-aaf0-4e3a3144fc46`` will *only* appear +under ``bea/343``. The prefix (``bea/343``) allows BE to reduce +caching global comment-lookup tables and enables easy error messages +("I couldn't find ``bea/343/ba9`` because I don't know where the +``bea`` bug directory is located"). diff --git a/doc/index.txt b/doc/index.txt index f1c1542..9c964fa 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -29,7 +29,7 @@ Contents: distributed_bugtracking.txt hacking.txt spam.txt - + libbe.txt Indices and tables ================== diff --git a/doc/libbe.bug.txt b/doc/libbe.bug.txt new file mode 100644 index 0000000..41427c5 --- /dev/null +++ b/doc/libbe.bug.txt @@ -0,0 +1,7 @@ +**************** +:mod:`libbe.bug` +**************** + +.. automodule:: libbe.bug + :members: + :undoc-members: diff --git a/doc/libbe.txt b/doc/libbe.txt new file mode 100644 index 0000000..dd04df1 --- /dev/null +++ b/doc/libbe.txt @@ -0,0 +1,12 @@ +************ +:mod:`libbe` +************ + +.. automodule:: libbe + :members: + :undoc-members: + +.. toctree:: + :maxdepth: 2 + + libbe.bug.txt diff --git a/libbe/bug.py b/libbe/bug.py index 0b40921..a4b569f 100644 --- a/libbe/bug.py +++ b/libbe/bug.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. -""" -Define the Bug class for representing bugs. +"""Define the Bug class for representing bugs. """ import copy @@ -122,7 +121,9 @@ load_status(active_status_def, inactive_status_def) class Bug (settings_object.SavedSettingsObject): - """ + """A bug (or issue) is a place to store attributes and attach + comments. In mailing-list terms, a bug is analogous to a thread. + >>> b = Bug() >>> print b.status open @@ -132,6 +133,7 @@ class Bug (settings_object.SavedSettingsObject): There are two formats for time, int and string. Setting either one will adjust the other appropriately. The string form is the one stored in the bug's settings file on disk. + >>> print type(b.time) >>> print type(b.time_string) @@ -333,7 +335,7 @@ class Bug (settings_object.SavedSettingsObject): return istring + sep.join(lines).rstrip('\n') def from_xml(self, xml_string, verbose=True): - """ + u""" Note: If a bug uuid is given, set .alt_id to it's value. >>> bugA = Bug(uuid="0123", summary="Need to test Bug.from_xml()") >>> bugA.date = "Thu, 01 Jan 1970 00:00:00 +0000" @@ -410,6 +412,7 @@ class Bug (settings_object.SavedSettingsObject): Add a comment too the current bug, under the parent specified by comment.in_reply_to. Note: If a bug uuid is given, set .alt_id to it's value. + >>> bugA = Bug(uuid='0123', summary='Need to test Bug.add_comment()') >>> bugA.creator = 'Jack' >>> commA = bugA.comment_root.new_reply(body='comment A') @@ -506,6 +509,7 @@ class Bug (settings_object.SavedSettingsObject): """ Merge info from other into this bug. Overrides any attributes in self that are listed in other.explicit_attrs. + >>> bugA = Bug(uuid='0123', summary='Need to test Bug.merge()') >>> bugA.date = 'Thu, 01 Jan 1970 00:00:00 +0000' >>> bugA.creator = 'Frank' -- cgit From 4c71190ebafd7ce198a5c0047588c4b2f7e5ef58 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 6 Feb 2010 13:44:05 -0500 Subject: Added bugdir and comment modules to Sphinx docs --- doc/libbe.bugdir.txt | 7 +++++++ doc/libbe.comment.txt | 7 +++++++ doc/libbe.txt | 2 ++ libbe/bug.py | 17 +++++++++++------ libbe/bugdir.py | 12 +++++++----- libbe/comment.py | 37 +++++++++++++++++++++---------------- 6 files changed, 55 insertions(+), 27 deletions(-) create mode 100644 doc/libbe.bugdir.txt create mode 100644 doc/libbe.comment.txt diff --git a/doc/libbe.bugdir.txt b/doc/libbe.bugdir.txt new file mode 100644 index 0000000..2464ea6 --- /dev/null +++ b/doc/libbe.bugdir.txt @@ -0,0 +1,7 @@ +******************* +:mod:`libbe.bugdir` +******************* + +.. automodule:: libbe.bugdir + :members: + :undoc-members: diff --git a/doc/libbe.comment.txt b/doc/libbe.comment.txt new file mode 100644 index 0000000..6e76f98 --- /dev/null +++ b/doc/libbe.comment.txt @@ -0,0 +1,7 @@ +******************** +:mod:`libbe.comment` +******************** + +.. automodule:: libbe.comment + :members: + :undoc-members: diff --git a/doc/libbe.txt b/doc/libbe.txt index dd04df1..5d29434 100644 --- a/doc/libbe.txt +++ b/doc/libbe.txt @@ -9,4 +9,6 @@ .. toctree:: :maxdepth: 2 + libbe.bugdir.txt libbe.bug.txt + libbe.comment.txt diff --git a/libbe/bug.py b/libbe/bug.py index a4b569f..dd68518 100644 --- a/libbe/bug.py +++ b/libbe/bug.py @@ -16,7 +16,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -"""Define the Bug class for representing bugs. +"""Define the :class:`Bug` class for representing bugs. """ import copy @@ -122,7 +122,9 @@ load_status(active_status_def, inactive_status_def) class Bug (settings_object.SavedSettingsObject): """A bug (or issue) is a place to store attributes and attach - comments. In mailing-list terms, a bug is analogous to a thread. + :class:`libbe.comment.Comment`\s. In mailing-list terms, a bug is + analogous to a thread. Bugs are normally stored in + :class:`libbe.bugdir.BugDir`\s. >>> b = Bug() >>> print b.status @@ -716,6 +718,7 @@ def cmp_severity(bug_1, bug_2): """ Compare the severity levels of two bugs, with more severe bugs comparing as less. + >>> bugA = Bug() >>> bugB = Bug() >>> bugA.severity = bugB.severity = "wishlist" @@ -734,8 +737,9 @@ def cmp_severity(bug_1, bug_2): def cmp_status(bug_1, bug_2): """ - Compare the status levels of two bugs, with more 'open' bugs + Compare the status levels of two bugs, with more "open" bugs comparing as less. + >>> bugA = Bug() >>> bugB = Bug() >>> bugA.status = bugB.status = "open" @@ -755,9 +759,10 @@ def cmp_status(bug_1, bug_2): def cmp_attr(bug_1, bug_2, attr, invert=False): """ - Compare a general attribute between two bugs using the conventional - comparison rule for that attribute type. If invert == True, sort - *against* that convention. + Compare a general attribute between two bugs using the + conventional comparison rule for that attribute type. If + ``invert==True``, sort *against* that convention. + >>> attr="severity" >>> bugA = Bug() >>> bugB = Bug() diff --git a/libbe/bugdir.py b/libbe/bugdir.py index 3c3afa0..fa8edb9 100644 --- a/libbe/bugdir.py +++ b/libbe/bugdir.py @@ -19,8 +19,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" -Define the BugDir class for representing bug comments. +"""Define the :class:`BugDir` class for storing a collection of bugs. """ import copy @@ -89,8 +88,10 @@ class DiskAccessRequired (Exception): class BugDir (list, settings_object.SavedSettingsObject): - """ - TODO: simple bugdir manipulation examples... + """A BugDir is a container for :class:`libbe.bug.Bug`\s, with some + additional attributes. + + See :class:`SimpleBugDir` for some bugdir manipulation exampes. """ settings_properties = [] @@ -344,7 +345,8 @@ class RevisionedBugDir (BugDir): if libbe.TESTING == True: class SimpleBugDir (BugDir): """ - For testing. Set memory=True for a memory-only bugdir. + For testing. Set ``memory=True`` for a memory-only bugdir. + >>> bugdir = SimpleBugDir() >>> uuids = list(bugdir.uuids()) >>> uuids.sort() diff --git a/libbe/comment.py b/libbe/comment.py index dd245de..003bf71 100644 --- a/libbe/comment.py +++ b/libbe/comment.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. -""" -Define the Comment class for representing bug comments. +"""Define the :class:`Comment` class for representing bug comments. """ import base64 @@ -101,7 +100,10 @@ def save_comments(bug): class Comment (Tree, settings_object.SavedSettingsObject): - """ + """Comments are a notes that attach to :class:`libbe.bug.Bug`\s in + threaded trees. In mailing-list terms, a comment is analogous to + a single part of an email. + >>> c = Comment() >>> c.uuid != None True @@ -194,19 +196,19 @@ class Comment (Tree, settings_object.SavedSettingsObject): def __init__(self, bug=None, uuid=None, from_storage=False, in_reply_to=None, body=None, content_type=None): """ - Set from_storage=True to load an old comment. - Set from_storage=False to create a new comment. + Set ``from_storage=True`` to load an old comment. + Set ``from_storage=False`` to create a new comment. - The uuid option is required when from_storage==True. + The ``uuid`` option is required when ``from_storage==True``. The in_reply_to, body, and content_type options are only used - if from_storage==False (the default). When - from_storage==True, they are loaded from the bug database. - content_type decides if the body should be run through - libbe.util.id.short_to_long_text() before saving. See - ._set_comment_body() for details. + if ``from_storage==False`` (the default). When + ``from_storage==True``, they are loaded from the bug database. + ``content_type`` decides if the body should be run through + :func:`util.id.short_to_long_text` before saving. See + :meth:`_set_comment_body` for details. - in_reply_to should be the uuid string of the parent comment. + ``in_reply_to`` should be the uuid string of the parent comment. """ Tree.__init__(self) settings_object.SavedSettingsObject.__init__(self) @@ -267,6 +269,7 @@ class Comment (Tree, settings_object.SavedSettingsObject): def safe_in_reply_to(self): """ Return self.in_reply_to, except... + * if no comment matches that id, in which case return None. * if that id matches another comments .alt_id, in which case return the matching comments .uuid. @@ -337,7 +340,7 @@ class Comment (Tree, settings_object.SavedSettingsObject): return istring + sep.join(lines).rstrip('\n') def from_xml(self, xml_string, verbose=True): - """ + u""" Note: If alt-id is not given, translates any fields to fields. >>> commA = Comment(bug=None, body="Some\\ninsightful\\nremarks\\n") @@ -412,6 +415,7 @@ class Comment (Tree, settings_object.SavedSettingsObject): """ Merge info from other into this comment. Overrides any attributes in self that are listed in other.explicit_attrs. + >>> commA = Comment(bug=None, body='Some insightful remarks') >>> commA.uuid = '0123' >>> commA.date = 'Thu, 01 Jan 1970 00:00:00 +0000' @@ -621,7 +625,7 @@ class Comment (Tree, settings_object.SavedSettingsObject): """ Save any loaded contents to storage. - However, if self.storage.is_writeable() == True, then any + However, if ``self.storage.is_writeable() == True``, then any changes are automatically written to storage as soon as they happen, so calling this method will just waste time (unless something else has been messing with your stored files). @@ -666,8 +670,8 @@ class Comment (Tree, settings_object.SavedSettingsObject): return reply def comment_from_uuid(self, uuid, match_alt_id=True): - """ - Use a uuid to look up a comment. + """Use a uuid to look up a comment. + >>> a = Comment(bug=None, uuid="a") >>> b = a.new_reply() >>> b.uuid = "b" @@ -708,6 +712,7 @@ def cmp_attr(comment_1, comment_2, attr, invert=False): Compare a general attribute between two comments using the conventional comparison rule for that attribute type. If invert == True, sort *against* that convention. + >>> attr="author" >>> commentA = Comment() >>> commentB = Comment() -- cgit From 413626d3b77e9bf89389a272ed489da29f3d9877 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 6 Feb 2010 16:53:57 -0500 Subject: Use numpydoc and generate-libbe-txt.py to autogenerate API documentation --- README | 11 ++++++++-- doc/conf.py | 3 ++- doc/doc.txt | 23 +++++++++++++++++++++ doc/generate-libbe-txt.py | 52 +++++++++++++++++++++++++++++++++++++++++++++++ doc/index.txt | 9 ++++---- doc/libbe.bug.txt | 7 ------- doc/libbe.bugdir.txt | 7 ------- doc/libbe.comment.txt | 7 ------- doc/libbe.txt | 14 ------------- libbe/bug.py | 4 ++-- libbe/bugdir.py | 2 +- libbe/command/assign.py | 2 +- libbe/command/serve.py | 3 --- libbe/comment.py | 2 +- 14 files changed, 96 insertions(+), 50 deletions(-) create mode 100644 doc/doc.txt create mode 100644 doc/generate-libbe-txt.py delete mode 100644 doc/libbe.bug.txt delete mode 100644 doc/libbe.bugdir.txt delete mode 100644 doc/libbe.comment.txt delete mode 100644 doc/libbe.txt diff --git a/README b/README index c0bae43..ef597bb 100644 --- a/README +++ b/README @@ -57,8 +57,15 @@ If ``be help`` isn't scratching your itch, the full documentation is available in the doc directory as reStructuredText_ . You can build the full documentation with Sphinx_ , convert single files with docutils_ , or browse through the doc directory by hand. -doc/index.txt is a good place to start. +doc/index.txt is a good place to start. If you do use Sphinx, you'll +need to install numpydoc_ for automatically generating API +documentation. See the ``NumPy/SciPy documentation guide``_ for an +introduction to the syntax. -.. _reStructuredText: http://docutils.sourceforge.net/docs/user/rst/quickref.html +.. _reStructuredText: + http://docutils.sourceforge.net/docs/user/rst/quickref.html .. _Sphinx: http://sphinx.pocoo.org/ .. _docutils: http://docutils.sourceforge.net/ +.. _numpydoc: http://pypi.python.org/pypi/numpydoc +.. _NumPy/SciPy documentation guide: + http://projects.scipy.org/numpy/wiki/CodingStyleGuidelines diff --git a/doc/conf.py b/doc/conf.py index ea03b08..1090a28 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -25,7 +25,8 @@ import libbe.version # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage'] +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage', + 'numpydoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['.templates'] diff --git a/doc/doc.txt b/doc/doc.txt new file mode 100644 index 0000000..e1b7a3a --- /dev/null +++ b/doc/doc.txt @@ -0,0 +1,23 @@ +**************************** +Producing this documentation +**************************** + +This documentation is written in reStructuredText_, and produced +using Sphinx_ and the numpydoc_ extension. The documentation source +should be fairly readable without processing, but you can compile the +documentation, you'll need to install Sphinx and numpydoc:: + + $ easy_install Sphinx + $ easy_install numpydoc + +.. _Sphinx: http://sphinx.pocoo.org/ +.. _numpydoc: http://pypi.python.org/pypi/numpydoc + +See the reStructuredText quick reference and the `NumPy/SciPy +documentation guide`_ for an introduction to the documentation +syntax. + +.. _reStructuredText: + http://docutils.sourceforge.net/docs/user/rst/quickref.html +.. _NumPy/SciPy documentation guide: + http://projects.scipy.org/numpy/wiki/CodingStyleGuidelines diff --git a/doc/generate-libbe-txt.py b/doc/generate-libbe-txt.py new file mode 100644 index 0000000..ec874fa --- /dev/null +++ b/doc/generate-libbe-txt.py @@ -0,0 +1,52 @@ +#!/usr/bin/python +# +# Copyright + +"""Auto-generate reStructuredText of the libbe module tree for Sphinx. +""" + +import sys +import os, os.path + +sys.path.insert(0, os.path.abspath('..')) +from test import python_tree + +def title(modname): + t = ':mod:`%s`' % modname + delim = '*'*len(t) + return '\n'.join([delim, t, delim, '', '']) + +def automodule(modname): + return '\n'.join([ + '.. automodule:: %s' % modname, + ' :members:', + ' :undoc-members:', + '', '']) + +def toctree(children): + if len(children) == 0: + return '' + return '\n'.join([ + '.. toctree::', + ' :maxdepth: 2', + '', + ] + [ + ' %s.txt' % c for c in children + ] + ['', '']) + +def make_module_txt(modname, children): + filename = os.path.join('libbe', '%s.txt' % modname) + if not os.path.exists('libbe'): + os.mkdir('libbe') + if os.path.exists(filename): + return None # don't overwrite potentially hand-written files. + f = file(filename, 'w') + f.write(title(modname)) + f.write(automodule(modname)) + f.write(toctree(children)) + f.close() + +if __name__ == '__main__': + pt = python_tree(root_path='../libbe', root_modname='libbe') + for node in pt.traverse(): + make_module_txt(node.modname, [c.modname for c in node]) diff --git a/doc/index.txt b/doc/index.txt index 9c964fa..6765a68 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -2,9 +2,9 @@ Welcome to the bugs-everywhere documentation! ============================================= Bugs Everywhere (BE) is a bugtracker built on distributed version -control. It works with Arch_ , Bazaar_ , Darcs_ , Git_ , and -Mercurial_ at the moment, but is easily extensible. It can also -function with no VCS at all. +control. It works with Arch_, Bazaar_, Darcs_, Git_, and Mercurial_ +at the moment, but is easily extensible. It can also function with no +VCS at all. .. _Arch: http://www.gnu.org/software/gnu-arch/ .. _Bazaar: http://bazaar.canonical.com/ @@ -29,7 +29,8 @@ Contents: distributed_bugtracking.txt hacking.txt spam.txt - libbe.txt + libbe/libbe.txt + doc.txt Indices and tables ================== diff --git a/doc/libbe.bug.txt b/doc/libbe.bug.txt deleted file mode 100644 index 41427c5..0000000 --- a/doc/libbe.bug.txt +++ /dev/null @@ -1,7 +0,0 @@ -**************** -:mod:`libbe.bug` -**************** - -.. automodule:: libbe.bug - :members: - :undoc-members: diff --git a/doc/libbe.bugdir.txt b/doc/libbe.bugdir.txt deleted file mode 100644 index 2464ea6..0000000 --- a/doc/libbe.bugdir.txt +++ /dev/null @@ -1,7 +0,0 @@ -******************* -:mod:`libbe.bugdir` -******************* - -.. automodule:: libbe.bugdir - :members: - :undoc-members: diff --git a/doc/libbe.comment.txt b/doc/libbe.comment.txt deleted file mode 100644 index 6e76f98..0000000 --- a/doc/libbe.comment.txt +++ /dev/null @@ -1,7 +0,0 @@ -******************** -:mod:`libbe.comment` -******************** - -.. automodule:: libbe.comment - :members: - :undoc-members: diff --git a/doc/libbe.txt b/doc/libbe.txt deleted file mode 100644 index 5d29434..0000000 --- a/doc/libbe.txt +++ /dev/null @@ -1,14 +0,0 @@ -************ -:mod:`libbe` -************ - -.. automodule:: libbe - :members: - :undoc-members: - -.. toctree:: - :maxdepth: 2 - - libbe.bugdir.txt - libbe.bug.txt - libbe.comment.txt diff --git a/libbe/bug.py b/libbe/bug.py index dd68518..8bf32dd 100644 --- a/libbe/bug.py +++ b/libbe/bug.py @@ -122,9 +122,9 @@ load_status(active_status_def, inactive_status_def) class Bug (settings_object.SavedSettingsObject): """A bug (or issue) is a place to store attributes and attach - :class:`libbe.comment.Comment`\s. In mailing-list terms, a bug is + :class:`~libbe.comment.Comment`\s. In mailing-list terms, a bug is analogous to a thread. Bugs are normally stored in - :class:`libbe.bugdir.BugDir`\s. + :class:`~libbe.bugdir.BugDir`\s. >>> b = Bug() >>> print b.status diff --git a/libbe/bugdir.py b/libbe/bugdir.py index fa8edb9..9328b06 100644 --- a/libbe/bugdir.py +++ b/libbe/bugdir.py @@ -88,7 +88,7 @@ class DiskAccessRequired (Exception): class BugDir (list, settings_object.SavedSettingsObject): - """A BugDir is a container for :class:`libbe.bug.Bug`\s, with some + """A BugDir is a container for :class:`~libbe.bug.Bug`\s, with some additional attributes. See :class:`SimpleBugDir` for some bugdir manipulation exampes. diff --git a/libbe/command/assign.py b/libbe/command/assign.py index 2430d23..6abf05e 100644 --- a/libbe/command/assign.py +++ b/libbe/command/assign.py @@ -24,7 +24,7 @@ import libbe.command.util class Assign (libbe.command.Command): - """Assign an individual or group to fix a bug + u"""Assign an individual or group to fix a bug >>> import sys >>> import libbe.bugdir diff --git a/libbe/command/serve.py b/libbe/command/serve.py index 43e07cc..7a98a47 100644 --- a/libbe/command/serve.py +++ b/libbe/command/serve.py @@ -293,9 +293,6 @@ class AuthenticationApp (WSGI_Object): def authenticate(self, environ): """Handle user-authentication sent in the 'Authorization' header. - Basic HTTP/1.0 Authentication - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - This function implements ``Basic`` authentication as described in HTTP/1.0 specification [1]_ . Do not use this module unless you are using SSL, as it transmits unencrypted passwords. diff --git a/libbe/comment.py b/libbe/comment.py index 003bf71..d8632a4 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -100,7 +100,7 @@ def save_comments(bug): class Comment (Tree, settings_object.SavedSettingsObject): - """Comments are a notes that attach to :class:`libbe.bug.Bug`\s in + """Comments are a notes that attach to :class:`~libbe.bug.Bug`\s in threaded trees. In mailing-list terms, a comment is analogous to a single part of an email. -- cgit From 8ccb71280c24480eb661fc668c31ff06bc7a3148 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 7 Feb 2010 13:37:37 -0500 Subject: Use email.utils.formataddr/parseaddr to generate/parse user IDs --- libbe/ui/util/user.py | 89 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 29 deletions(-) diff --git a/libbe/ui/util/user.py b/libbe/ui/util/user.py index 98f16a6..460a1dd 100644 --- a/libbe/ui/util/user.py +++ b/libbe/ui/util/user.py @@ -14,13 +14,21 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" -Tools for getting, setting, creating, and parsing the user's id. For -example, - 'John Doe ' -Note that the Arch VCS backend *enforces* ids with this format. +"""Tools for getting, setting, creating, and parsing the user's ID. + +IDs will look like 'John Doe '. Note that the +:mod:`libbe.storage.vcs.arch ` *enforces* IDs with +this format. + +Do not confuse the user IDs discussed in this module, which refer to +humans, with the "user IDs" discussed in :mod:`libbe.util.id`, which +are human-readable tags refering to objects. """ +try: + from email.utils import formataddr, parseaddr +except ImportErrror: # adjust to old python < 2.5 + from email.Utils import formataddr, parseaddr import os import re from socket import gethostname @@ -29,6 +37,8 @@ import libbe import libbe.storage.util.config def get_fallback_username(): + """Return a username extracted from environmental variables. + """ name = None for env in ["LOGNAME", "USERNAME"]: if os.environ.has_key(env): @@ -38,52 +48,69 @@ def get_fallback_username(): return name def get_fallback_email(): + """Return an email address extracted from environmental variables. + """ hostname = gethostname() name = get_fallback_username() return "%s@%s" % (name, hostname) def create_user_id(name, email=None): - """ + """Create a user ID string from given `name` and `email` strings. + + Examples + -------- + >>> create_user_id("John Doe", "jdoe@example.com") 'John Doe ' >>> create_user_id("John Doe") 'John Doe' + + See Also + -------- + parse_user_id : inverse """ assert len(name) > 0 if email == None or len(email) == 0: return name else: - return "%s <%s>" % (name, email) + return formataddr((name, email)) def parse_user_id(value): - """ + """Parse a user ID string into `name` and `email` strings. + + Examples + -------- + >>> parse_user_id("John Doe ") ('John Doe', 'jdoe@example.com') >>> parse_user_id("John Doe") ('John Doe', None) - >>> try: - ... parse_user_id("John Doe ") - ... except AssertionError: - ... print "Invalid match" - Invalid match + >>> parse_user_id("John Doe ") + ('John Doe', 'jdoe@example.com') + + See Also + -------- + create_user_id : inverse """ - emailexp = re.compile("(.*) <([^>]*)>(.*)") - match = emailexp.search(value) - if match == None: - email = None - name = value - else: - assert len(match.groups()) == 3 - assert match.groups()[2] == "", match.groups() - email = match.groups()[1] - name = match.groups()[0] - assert name != None - assert len(name) > 0 - return (name, email) + if '<' not in value: + return (value, None) + return parseaddr(value) def get_user_id(storage=None): - """ - Sometimes the storage will also keep track of the user id (e.g. most VCSs). + """Return a user ID, checking a list of possible sources. + + The source order is: + + 1. Global BE configuration. + 2. `storage.get_user_id`, if that function is defined. + 3. :func:`get_fallback_username` and :func:`get_fallback_email`. + + Notes + ----- + Sometimes the storage will keep track of the user ID (e.g. most + VCSs, see :meth:`libbe.storage.vcs.base.VCS.get_user_id`). If so, + we prefer that ID to the fallback, since the user has likely + configured it directly. """ user = libbe.storage.util.config.get_val('user') if user != None: @@ -98,6 +125,10 @@ def get_user_id(storage=None): return user def set_user_id(user_id): - """ + """Set the user ID in a user's BE configuration. + + See Also + -------- + libbe.storage.util.config.set_val """ user = libbe.storage.util.config.set_val('user', user_id) -- cgit From 977eff5af10b50ba6e6edb6abc4f40804c418b12 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 7 Feb 2010 17:53:53 -0500 Subject: Fixed docstrings so only Sphinx errors are "autosummary" and "missing attribute" --- doc/generate-libbe-txt.py | 2 +- doc/ids.txt | 53 ------ doc/index.txt | 1 - doc/tutorial.txt | 2 +- libbe/__init__.py | 38 ++++- libbe/bugdir.py | 47 ++---- libbe/command/serve.py | 143 ++++++++++------ libbe/diff.py | 32 +++- libbe/storage/__init__.py | 14 ++ libbe/storage/base.py | 6 +- libbe/storage/http.py | 57 +++++-- libbe/storage/util/config.py | 45 +++-- libbe/storage/util/mapfile.py | 28 +++- libbe/storage/util/properties.py | 51 ++++-- libbe/storage/util/settings_object.py | 95 +++++++---- libbe/storage/vcs/__init__.py | 17 ++ libbe/storage/vcs/arch.py | 40 +++-- libbe/storage/vcs/base.py | 266 +++++++++++------------------ libbe/storage/vcs/bzr.py | 118 +++++++------ libbe/storage/vcs/darcs.py | 104 ++++++------ libbe/storage/vcs/git.py | 108 ++++++------ libbe/storage/vcs/hg.py | 86 +++++----- libbe/util/id.py | 303 +++++++++++++++++++++++++++++----- libbe/util/tree.py | 127 ++++++++++---- libbe/util/utility.py | 138 +++++++++++++--- 25 files changed, 1212 insertions(+), 709 deletions(-) delete mode 100644 doc/ids.txt diff --git a/doc/generate-libbe-txt.py b/doc/generate-libbe-txt.py index ec874fa..35eb5c4 100644 --- a/doc/generate-libbe-txt.py +++ b/doc/generate-libbe-txt.py @@ -31,7 +31,7 @@ def toctree(children): ' :maxdepth: 2', '', ] + [ - ' %s.txt' % c for c in children + ' %s.txt' % c for c in sorted(children) ] + ['', '']) def make_module_txt(modname, children): diff --git a/doc/ids.txt b/doc/ids.txt deleted file mode 100644 index ba1837b..0000000 --- a/doc/ids.txt +++ /dev/null @@ -1,53 +0,0 @@ -********** -Object IDs -********** - -Format -====== - -BE IDs are formatted:: - - [/[/]] - -where each ``<..>`` is a UUID. For example:: - - bea86499-824e-4e77-b085-2d581fa9ccab/3438b72c-6244-4f1d-8722-8c8d41484e35 - -refers to bug ``3438b72c-6244-4f1d-8722-8c8d41484e35`` which is -located in bug directory ``bea86499-824e-4e77-b085-2d581fa9ccab``. -This is a bit of a mouthful, so you can truncate each UUID so long as -it remains unique. For example:: - - bea/343 - -If there were two bugs ``3438...`` and ``343a...`` in ``bea``, you'd -have to use:: - - bea/3438 - -BE will only truncate each UUID down to three characters to slightly -future-proof the short user ids. However, if you want to save keystrokes -and you *know* there is only one bug directory, feel free to truncate -all the way to zero characters:: - - /3438 - -Cross references -================ - -To refer to other bug-directories/bugs/comments from bug comments, simply -enclose the ID in pound signs (``#``). BE will automatically expand the -truncations to the full UUIDs before storing the comment, and the reference -will be appropriately truncated (and hyperlinked, if possible) when the -comment is displayed. - -Scope -===== - -Although bug and comment IDs always appear in compound references, -UUIDs at each level are globally unique. For example, comment -``bea/343/ba96f1c0-ba48-4df8-aaf0-4e3a3144fc46`` will *only* appear -under ``bea/343``. The prefix (``bea/343``) allows BE to reduce -caching global comment-lookup tables and enables easy error messages -("I couldn't find ``bea/343/ba9`` because I don't know where the -``bea`` bug directory is located"). diff --git a/doc/index.txt b/doc/index.txt index 6765a68..30b0318 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -23,7 +23,6 @@ Contents: install.txt tutorial.txt - ids.txt email.txt html.txt distributed_bugtracking.txt diff --git a/doc/tutorial.txt b/doc/tutorial.txt index 3dd7ff3..7932c9c 100644 --- a/doc/tutorial.txt +++ b/doc/tutorial.txt @@ -24,7 +24,7 @@ powerful, and leave the web and email interfaces to other documents. .. _command-line: `Command-line interface`_ .. _web: tutorial-html.txt .. _email: tutorial-email.txt -.. _IDs: ids.txt +.. _IDs: libbe/libbe.util.id.txt Installation ============ diff --git a/libbe/__init__.py b/libbe/__init__.py index 23acfef..d32716f 100644 --- a/libbe/__init__.py +++ b/libbe/__init__.py @@ -15,7 +15,39 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# To reduce module load time, test suite generation is turned of by -# default. If you _do_ want to generate the test suites, set -# TESTING=True before loading any libbe or becommands submodules. +"""The libbe module does all the legwork for bugs-everywhere_ (BE). + +.. _bugs-everywhere: http://bugseverywhere.org + +To facilitate faster loading, submodules are not imported by default. +The available submodules are: + +* :mod:`libbe.bugdir` +* :mod:`libbe.bug` +* :mod:`libbe.comment` +* :mod:`libbe.command` +* :mod:`libbe.diff` +* :mod:`libbe.error` +* :mod:`libbe.storage` +* :mod:`libbe.ui` +* :mod:`libbe.util` +* :mod:`libbe.version` +* :mod:`libbe._version` +""" + TESTING = False +"""Flag controlling test-suite generation. + +To reduce module load time, test suite generation is turned of by +default. If you *do* want to generate the test suites, set +``TESTING=True`` before loading any :mod:`libbe` submodules. + +Examples +-------- + +>>> import libbe +>>> libbe.TESTING = True +>>> import libbe.bugdir +>>> 'SimpleBugDir' in dir(libbe.bugdir) +True +""" diff --git a/libbe/bugdir.py b/libbe/bugdir.py index 9328b06..65136fe 100644 --- a/libbe/bugdir.py +++ b/libbe/bugdir.py @@ -48,31 +48,6 @@ if libbe.TESTING == True: import libbe.storage.base -class NoBugDir(Exception): - def __init__(self, path): - msg = "The directory \"%s\" has no bug directory." % path - Exception.__init__(self, msg) - self.path = path - -class NoRootEntry(Exception): - def __init__(self, path): - self.path = path - Exception.__init__(self, "Specified root does not exist: %s" % path) - -class AlreadyInitialized(Exception): - def __init__(self, path): - self.path = path - Exception.__init__(self, - "Specified root is already initialized: %s" % path) - -class MultipleBugMatches(ValueError): - def __init__(self, shortname, matches): - msg = ("More than one bug matches %s. " - "Please be more specific.\n%s" % (shortname, matches)) - ValueError.__init__(self, msg) - self.shortname = shortname - self.matches = matches - class NoBugMatches(libbe.util.id.NoIDMatches): def __init__(self, *args, **kwargs): libbe.util.id.NoIDMatches.__init__(self, *args, **kwargs) @@ -81,17 +56,27 @@ class NoBugMatches(libbe.util.id.NoIDMatches): return 'No bug matches %s' % self.id return self.msg -class DiskAccessRequired (Exception): - def __init__(self, goal): - msg = "Cannot %s without accessing the disk" % goal - Exception.__init__(self, msg) - class BugDir (list, settings_object.SavedSettingsObject): """A BugDir is a container for :class:`~libbe.bug.Bug`\s, with some additional attributes. - See :class:`SimpleBugDir` for some bugdir manipulation exampes. + Parameters + ---------- + storage : :class:`~libbe.storage.base.Storage` + Storage instance containing the bug directory. If + `from_storage` is `False`, `storage` may be `None`. + uuid : str, optional + Set the bugdir UUID (see :mod:`libbe.util.id`). + Useful if you are loading one of several bugdirs + stored in a single Storage instance. + from_storage : bool, optional + If `True`, attempt to load from storage. Otherwise, + setup in memory, saving to `storage` if it is not `None`. + + See Also + -------- + :class:`SimpleBugDir` for some bugdir manipulation exampes. """ settings_properties = [] diff --git a/libbe/command/serve.py b/libbe/command/serve.py index 7a98a47..7237343 100644 --- a/libbe/command/serve.py +++ b/libbe/command/serve.py @@ -14,6 +14,13 @@ # 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:`Serve` serving BE Storage over HTTP. + +See Also +-------- +:mod:`libbe.storage.http` : the associated client +""" + import hashlib import logging import os.path @@ -156,9 +163,10 @@ class Users (dict): class WSGI_Object (object): """Utility class for WGSI clients and middleware. + For details on WGSI, see `PEP 333`_ - .. PEP 333: http://www.python.org/dev/peps/pep-0333/ + .. _PEP 333: http://www.python.org/dev/peps/pep-0333/ """ def __init__(self, logger=None, log_level=logging.INFO, log_format=None): self.logger = logger @@ -223,6 +231,7 @@ class WSGI_Object (object): class ExceptionApp (WSGI_Object): """Some servers (e.g. cherrypy) eat app-raised exceptions. + Work around that by logging tracebacks by hand. """ def __init__(self, app, *args, **kwargs): @@ -242,7 +251,9 @@ class ExceptionApp (WSGI_Object): raise class UppercaseHeaderApp (WSGI_Object): - """From PEP 333, `The start_response() Callable`_ : + """WSGI middleware that uppercases incoming HTTP headers. + + From PEP 333, `The start_response() Callable`_ : A reminder for server/gateway authors: HTTP header names are case-insensitive, so be sure @@ -291,7 +302,7 @@ class AuthenticationApp (WSGI_Object): e.code, e.msg, e.headers) def authenticate(self, environ): - """Handle user-authentication sent in the 'Authorization' header. + """Handle user-authentication sent in the "Authorization" header. This function implements ``Basic`` authentication as described in HTTP/1.0 specification [1]_ . Do not use this module unless you @@ -299,6 +310,9 @@ class AuthenticationApp (WSGI_Object): .. [1] http://www.w3.org/Protocols/HTTP/1.0/draft-ietf-http-spec.html#BasicAA + Examples + -------- + >>> users = Users() >>> users.add_user(User('Aladdin', 'Big Al', password='open sesame')) >>> app = AuthenticationApp(app=None, realm='Dummy Realm', users=users) @@ -306,6 +320,9 @@ class AuthenticationApp (WSGI_Object): 'Aladdin' >>> app.authenticate({'HTTP_AUTHORIZATION':'Basic AAAAAAAAAAAAAAAAAAAAAAAAAA=='}) + Notes + ----- + Code based on authkit/authenticate/basic.py (c) 2005 Clark C. Evans. Released under the MIT License: @@ -339,8 +356,7 @@ class AuthenticationApp (WSGI_Object): return False class WSGI_AppObject (WSGI_Object): - """Utility class for WGSI clients and middleware with - useful utilities for handling data (POST, QUERY) and + """Useful WSGI utilities for handling data (POST, QUERY) and returning responses. """ def __init__(self, *args, **kwargs): @@ -469,10 +485,12 @@ class AdminApp (WSGI_AppObject): return self.ok_response(environ, start_response, None) class ServerApp (WSGI_AppObject): - """RESTful_ WSGI request handler for serving the + """WSGI server for a BE Storage instance over HTTP. + + RESTful_ WSGI request handler for serving the libbe.storage.http.HTTP backend with GET, POST, and HEAD commands. - For more information on authentication and REST, see John Calcote's - `Open Sourcery article`_ + For more information on authentication and REST, see John + Calcote's `Open Sourcery article`_ .. _RESTful: http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm .. _Open Sourcery article: http://jcalcote.wordpress.com/2009/08/10/restful-authentication/ @@ -480,6 +498,9 @@ class ServerApp (WSGI_AppObject): This serves files from a connected storage instance, usually a VCS-based repository located on the local machine. + Notes + ----- + The GET and HEAD requests are identical except that the HEAD request omits the actual content of the file. """ @@ -505,10 +526,12 @@ class ServerApp (WSGI_AppObject): ] def __call__(self, environ, start_response): - """The main WSGI application. Dispatch the current request to - the functions from above and store the regular expression - captures in the WSGI environment as `be-server.url_args` so - that the functions from above can access the url placeholders. + """The main WSGI application. + + Dispatch the current request to the functions from above and + store the regular expression captures in the WSGI environment + as `be-server.url_args` so that the functions from above can + access the url placeholders. URL dispatcher from Armin Ronacher's "Getting Started with WSGI" http://lucumr.pocoo.org/2007/5/21/getting-started-with-wsgi @@ -678,7 +701,8 @@ class ServerApp (WSGI_AppObject): class Serve (libbe.command.Command): - """Serve a Storage backend for the HTTP storage client + """:class:`~libbe.command.base.Command` wrapper around + :class:`ServerApp`. """ name = 'serve' @@ -1041,33 +1065,45 @@ def get_cert_filenames(server_name, autogenerate=True, logger=None): return (pkey_file, cert_file) def createKeyPair(type, bits): - """ - Create a public/private key pair. + """Create a public/private key pair. + + Returns the public/private key pair in a PKey object. - Arguments: type - Key type, must be one of TYPE_RSA and TYPE_DSA - bits - Number of bits to use in the key - Returns: The public/private key pair in a PKey object + Parameters + ---------- + type : TYPE_RSA or TYPE_DSA + Key type. + bits : int + Number of bits to use in the key. """ pkey = OpenSSL.crypto.PKey() pkey.generate_key(type, bits) return pkey def createCertRequest(pkey, digest="md5", **name): - """ - Create a certificate request. - - Arguments: pkey - The key to associate with the request - digest - Digestion method to use for signing, default is md5 - **name - The name of the subject of the request, possible - arguments are: - C - Country name - ST - State or province name - L - Locality name - O - Organization name - OU - Organizational unit name - CN - Common name - emailAddress - E-mail address - Returns: The certificate request in an X509Req object + """Create a certificate request. + + Returns the certificate request in an X509Req object. + + Parameters + ---------- + pkey : PKey + The key to associate with the request. + digest : "md5" or ? + Digestion method to use for signing, default is "md5", + `**name` : + The name of the subject of the request, possible. + Arguments are: + + ============ ======================== + C Country name + ST State or province name + L Locality name + O Organization name + OU Organizational unit name + CN Common name + emailAddress E-mail address + ============ ======================== """ req = OpenSSL.crypto.X509Req() subj = req.get_subject() @@ -1080,19 +1116,28 @@ def createCertRequest(pkey, digest="md5", **name): return req def createCertificate(req, (issuerCert, issuerKey), serial, (notBefore, notAfter), digest="md5"): - """ - Generate a certificate given a certificate request. - - Arguments: req - Certificate reqeust to use - issuerCert - The certificate of the issuer - issuerKey - The private key of the issuer - serial - Serial number for the certificate - notBefore - Timestamp (relative to now) when the certificate - starts being valid - notAfter - Timestamp (relative to now) when the certificate - stops being valid - digest - Digest method to use for signing, default is md5 - Returns: The signed certificate in an X509 object + """Generate a certificate given a certificate request. + + Returns the signed certificate in an X509 object. + + Parameters + ---------- + req : + Certificate reqeust to use + issuerCert : + The certificate of the issuer + issuerKey : + The private key of the issuer + serial : + Serial number for the certificate + notBefore : + Timestamp (relative to now) when the certificate + starts being valid + notAfter : + Timestamp (relative to now) when the certificate + stops being valid + digest : + Digest method to use for signing, default is md5 """ cert = OpenSSL.crypto.X509() cert.set_serial_number(serial) @@ -1105,9 +1150,9 @@ def createCertificate(req, (issuerCert, issuerKey), serial, (notBefore, notAfter return cert def make_certs(server_name, logger=None) : - """ - Generate private key and certification files. - mk_certs(server_name) -> (pkey_filename, cert_filename) + """Generate private key and certification files. + + `mk_certs(server_name) -> (pkey_filename, cert_filename)` """ if OpenSSL == None: raise libbe.command.UserError, \ diff --git a/libbe/diff.py b/libbe/diff.py index 35e2151..dc13b61 100644 --- a/libbe/diff.py +++ b/libbe/diff.py @@ -16,7 +16,8 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -"""Compare two bug trees.""" +"""Tools for comparing two :class:`libbe.bug.BugDir`\s. +""" import difflib import types @@ -30,8 +31,7 @@ from libbe.util.utility import time_to_str class SubscriptionType (libbe.util.tree.Tree): - """ - Trees of subscription types to allow users to select exactly what + """Trees of subscription types to allow users to select exactly what notifications they want to subscribe to. """ def __init__(self, type_name, *args, **kwargs): @@ -80,7 +80,11 @@ def type_from_name(name, type_root, default=None, default_ok=False): raise InvalidType(name, type_root) class Subscription (object): - """ + """A user subscription. + + Examples + -------- + >>> subscriptions = [Subscription('XYZ', 'all'), ... Subscription('DIR', 'new'), ... Subscription('ABC', BUG_TYPE_ALL),] @@ -112,7 +116,11 @@ class Subscription (object): return '' % (self.id, self.type) def subscriptions_from_string(string=None, subscription_sep=',', id_sep=':'): - """ + """Provide a simple way for non-Python interfaces to read in subscriptions. + + Examples + -------- + >>> subscriptions_from_string(None) [] >>> subscriptions_from_string('DIR:new,DIR:rem,ABC:all,XYZ:all') @@ -135,8 +143,11 @@ def subscriptions_from_string(string=None, subscription_sep=',', id_sep=':'): return subscriptions class DiffTree (libbe.util.tree.Tree): - """ - A tree holding difference data for easy report generation. + """A tree holding difference data for easy report generation. + + Examples + -------- + >>> bugdir = DiffTree('bugdir') >>> bdsettings = DiffTree('settings', data='target: None -> 1.0') >>> bugdir.append(bdsettings) @@ -251,8 +262,11 @@ class DiffTree (libbe.util.tree.Tree): return data_part class Diff (object): - """ - Difference tree generator for BugDirs. + """Difference tree generator for BugDirs. + + Examples + -------- + >>> import copy >>> bd = libbe.bugdir.SimpleBugDir(memory=True) >>> bd_new = copy.deepcopy(bd) 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 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 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 - * 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 + * 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 = [] diff --git a/libbe/util/id.py b/libbe/util/id.py index 81f5396..76079e7 100644 --- a/libbe/util/id.py +++ b/libbe/util/id.py @@ -15,8 +15,57 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" -Handle ID creation and parsing. +"""Handle ID creation and parsing. + +Format +====== + +BE IDs are formatted:: + + [/[/]] + +where each ``<..>`` is a UUID. For example:: + + bea86499-824e-4e77-b085-2d581fa9ccab/3438b72c-6244-4f1d-8722-8c8d41484e35 + +refers to bug ``3438b72c-6244-4f1d-8722-8c8d41484e35`` which is +located in bug directory ``bea86499-824e-4e77-b085-2d581fa9ccab``. +This is a bit of a mouthful, so you can truncate each UUID so long as +it remains unique. For example:: + + bea/343 + +If there were two bugs ``3438...`` and ``343a...`` in ``bea``, you'd +have to use:: + + bea/3438 + +BE will only truncate each UUID down to three characters to slightly +future-proof the short user ids. However, if you want to save keystrokes +and you *know* there is only one bug directory, feel free to truncate +all the way to zero characters:: + + /3438 + +Cross references +================ + +To refer to other bug-directories/bugs/comments from bug comments, simply +enclose the ID in pound signs (``#``). BE will automatically expand the +truncations to the full UUIDs before storing the comment, and the reference +will be appropriately truncated (and hyperlinked, if possible) when the +comment is displayed. + +Scope +===== + +Although bug and comment IDs always appear in compound references, +UUIDs at each level are globally unique. For example, comment +``bea/343/ba96f1c0-ba48-4df8-aaf0-4e3a3144fc46`` will *only* appear +under ``bea/343``. The prefix (``bea/343``) allows BE to reduce +caching global comment-lookup tables and enables easy error messages +("I couldn't find ``bea/343/ba9`` because I don't know where the +``bea`` bug directory is located"). """ import os.path @@ -64,9 +113,21 @@ except ImportError: HIERARCHY = ['bugdir', 'bug', 'comment'] - +"""Keep track of the object type hierarchy. +""" class MultipleIDMatches (ValueError): + """Multiple IDs match the given user ID. + + Parameters + ---------- + id : str + The not-specific-enough truncated UUID. + common : str + The initial characters common to all matching UUIDs. + matches : list of str + The list of possibly matching UUIDs. + """ def __init__(self, id, common, matches): msg = ('More than one id matches %s. ' 'Please be more specific (%s*).\n%s' % (id, common, matches)) @@ -76,6 +137,17 @@ class MultipleIDMatches (ValueError): self.matches = matches class NoIDMatches (KeyError): + """No IDs match the given user ID. + + Parameters + ---------- + id : str + The not-matching, possibly truncated UUID. + possible_ids : list of str + The list of potential UUIDs at that level. + msg : str, optional + A helpful message explaining what went wrong. + """ def __init__(self, id, possible_ids, msg=None): KeyError.__init__(self, id) self.id = id @@ -87,6 +159,15 @@ class NoIDMatches (KeyError): return self.msg class InvalidIDStructure (KeyError): + """A purported ID does not have the appropriate syntax. + + Parameters + ---------- + id : str + The purported ID. + msg : str, optional + A helpful message explaining what went wrong. + """ def __init__(self, id, msg=None): KeyError.__init__(self, id) self.id = id @@ -97,6 +178,12 @@ class InvalidIDStructure (KeyError): return self.msg def _assemble(args, check_length=False): + """Join a bunch of level UUIDs into a single ID. + + See Also + -------- + _split : inverse + """ args = list(args) for i,arg in enumerate(args): if arg == None: @@ -104,22 +191,47 @@ def _assemble(args, check_length=False): id = '/'.join(args) if check_length == True: assert len(args) > 0, args - if len(args) > 3: - raise InvalidIDStructure(id, '%d > 3 levels in "%s"' % (len(args), id)) + if len(args) > len(HIERARCHY): + raise InvalidIDStructure( + id, '%d > %d levels in "%s"' % (len(args), len(HIERARCHY), id)) return id def _split(id, check_length=False): + """Split an ID into a list of level UUIDs. + + See Also + -------- + _assemble : inverse + """ args = id.split('/') for i,arg in enumerate(args): if arg == '': args[i] = None if check_length == True: assert len(args) > 0, args - if len(args) > 3: - raise InvalidIDStructure(id, '%d > 3 levels in "%s"' % (len(args), id)) + if len(args) > len(HIERARCHY): + raise InvalidIDStructure( + id, '%d > %d levels in "%s"' % (len(args), len(HIERARCHY), id)) return args def _truncate(uuid, other_uuids, min_length=3): + """Truncate a UUID to the shortest length >= `min_length` such that it + is *not* a truncated form of a UUID in `other_uuids`. + + Parameters + ---------- + uuid : str + The UUID to truncate. + other_uuids : list of str + The other UUIDs which the truncation *might* (but doesn't) refer + to. + min_length : int + Avoid rapidly outdated truncations, even if they are unique now. + + See Also + -------- + _expand : inverse + """ chars = min_length for id in other_uuids: if id == uuid: @@ -129,6 +241,29 @@ def _truncate(uuid, other_uuids, min_length=3): return uuid[:chars] def _expand(truncated_id, common, other_ids): + """Expand a truncated UUID. + + Parameters + ---------- + truncated_id : str + The ID to expand. + common : str + The common portion `truncated_id` shares with the UUIDs in + `other_ids`. Not used by ``_expand``, but passed on to the + matching exceptions if they occur. + other_uuids : list of str + The other UUIDs which the truncation *might* (but doesn't) refer + to. + + Raises + ------ + NoIDMatches + MultipleIDMatches + + See Also + -------- + _expand : inverse + """ other_ids = list(other_ids) if len(other_ids) == 0: raise NoIDMatches(truncated_id, other_ids) @@ -151,7 +286,18 @@ def _expand(truncated_id, common, other_ids): class ID (object): - """ + """Store an object ID and produce various representations. + + Parameters + ---------- + object : :class:`~libbe.bugdir.BugDir` or :class:`~libbe.bug.Bug` or :class:`~libbe.comment.Comment` + The object that the ID applies to. + type : 'bugdir' or 'bug' or 'comment' + The type of the object. + + Notes + ----- + IDs have several formats specialized for different uses. In storage, all objects are represented by their uuid alone, @@ -166,41 +312,39 @@ class ID (object): them while retaining local uniqueness (with regards to the other objects currently in storage). We also prepend truncated parent ids for two reasons: - (1) so that a user can locate the repository containing the - referenced object. It would be hard to find bug 'XYZ' if - that's all you knew. Much easier with 'ABC/XYZ', where ABC - is the bugdir. Each project can publish a list of bugdir-id - - to - location mappings, e.g. + + 1. So that a user can locate the repository containing the + referenced object. It would be hard to find bug ``XYZ`` if + that's all you knew. Much easier with ``ABC/XYZ``, where + ``ABC`` is the bugdir. Each project can publish a list of + bugdir-id-to-location mappings, e.g.:: + ABC...(full uuid)...DEF https://server.com/projectX/be/ - which is easier than publishing all-object-ids-to-location - mappings. - (2) because it's easier to generate and parse truncated ids if - you don't have to fetch all the ids in the storage - repository, but can restrict yourself to a specific branch. - You can generate ids of this sort with the .user() method, - although in order to preform the truncation, your object (and its - parents must define a .sibling_uuids() method. + which is easier than publishing all-object-ids-to-location + mappings. + + 2. Because it's easier to generate and parse truncated ids if you + don't have to fetch all the ids in the storage repository but + can restrict yourself to a specific branch. + + You can generate ids of this sort with the :meth:`user` method, + although in order to preform the truncation, your object (and its + parents must define a `sibling_uuids` method. While users can use the convenient short user ids in the short term, the truncation will inevitably lead to name collision. To avoid that, we provide a non-truncated form of the short user ids - via the .long_user() method. These long user ids should be + via the :meth:`long_user` method. These long user ids should be converted to short user ids by intelligent user interfaces. - Related tools: - * get uuids back out of the user ids: - parse_user() - * convert a single short user id to a long user id: - short_to_long_user() - * convert a single long user id to a short user id: - long_to_short_user() - * scan text for user ids & convert to long user ids: - short_to_long_text() - * scan text for long user ids & convert to short user ids: - long_to_short_text() - - Supported types: 'bugdir', 'bug', 'comment' + See Also + -------- + parse_user : get uuids back out of the user ids. + short_to_long_user : convert a single short user id to a long user id. + long_to_short_user : convert a single long user id to a short user id. + short_to_long_text : scan text for user ids & convert to long user ids. + long_to_short_text : scan text for long user ids & convert to short user ids. """ def __init__(self, object, type): self._object = object @@ -236,9 +380,17 @@ class ID (object): return _assemble(ids, check_length=True) def child_uuids(child_storage_ids): - """ - Extract uuid children from other children generated by the - ID.storage() method. + """Extract uuid children from other children generated by + :meth:`ID.storage`. + + This is useful for separating data belonging to a particular + object directly from entries for its child objects. Since the + :class:`~libbe.storage.base.Storage` backend doesn't distinguish + between the two. + + Examples + -------- + >>> list(child_uuids(['abc123/values', '123abc', '123def'])) ['123abc', '123def'] """ @@ -248,6 +400,15 @@ def child_uuids(child_storage_ids): yield fields[0] def long_to_short_user(bugdirs, id): + """Convert a long user ID to a short user ID (see :class:`ID`). + The list of bugdirs allows uniqueness-maintaining truncation of + the bugdir portion of the ID. + + See Also + -------- + short_to_long_user : inverse + long_to_short_text : conversion on a block of text + """ ids = _split(id, check_length=True) matching_bugdirs = [bd for bd in bugdirs if bd.uuid == ids[0]] if len(matching_bugdirs) == 0: @@ -267,6 +428,15 @@ def long_to_short_user(bugdirs, id): return _assemble(ids) def short_to_long_user(bugdirs, id): + """Convert a short user ID to a long user ID (see :class:`ID`). The + list of bugdirs allows uniqueness-checking during expansion of the + bugdir portion of the ID. + + See Also + -------- + long_to_short_user : inverse + short_to_long_text : conversion on a block of text + """ ids = _split(id, check_length=True) ids[0] = _expand(ids[0], common=None, other_ids=[bd.uuid for bd in bugdirs]) @@ -284,8 +454,19 @@ def short_to_long_user(bugdirs, id): REGEXP = '#([-a-f0-9]*)(/[-a-g0-9]*)?(/[-a-g0-9]*)?#' +"""Regular expression for matching IDs (both short and long) in text. +""" class IDreplacer (object): + """Helper class for ID replacement in text. + + Reassembles the match elements from :data:`REGEXP` matching + into the original ID, for easier replacement. + + See Also + -------- + short_to_long_text, long_to_short_text + """ def __init__(self, bugdirs, replace_fn, wrap=True): self.bugdirs = bugdirs self.replace_fn = replace_fn @@ -302,13 +483,36 @@ class IDreplacer (object): return replacement def short_to_long_text(bugdirs, text): + """Convert short user IDs to long user IDs in text (see :class:`ID`). + The list of bugdirs allows uniqueness-checking during expansion of + the bugdir portion of the ID. + + See Also + -------- + short_to_long_user : conversion on a single ID + long_to_short_text : inverse + """ return re.sub(REGEXP, IDreplacer(bugdirs, short_to_long_user), text) def long_to_short_text(bugdirs, text): + """Convert long user IDs to short user IDs in text (see :class:`ID`). + The list of bugdirs allows uniqueness-maintaining truncation of + the bugdir portion of the ID. + + See Also + -------- + long_to_short_user : conversion on a single ID + short_to_long_text : inverse + """ return re.sub(REGEXP, IDreplacer(bugdirs, long_to_short_user), text) def residual(base, fragment): - """ + """Split the short ID `fragment` into a portion corresponding + to `base`, and a portion inside `base`. + + Examples + -------- + >>> residual('ABC/DEF/', '//GHI') ('//', 'GHI') >>> residual('ABC/DEF/', '/D/GHI') @@ -326,7 +530,15 @@ def residual(base, fragment): return ('/'.join(root_ids), '/'.join(residual_ids)) def _parse_user(id): - """ + """Parse a user ID (see :class:`ID`), returning a dict of parsed + information. + + The returned dict will contain a value for "type" (from + :data:`HIERARCHY`) and values for the levels that are defined. + + Examples + -------- + >>> _parse_user('ABC/DEF/GHI') == \\ ... {'bugdir':'ABC', 'bug':'DEF', 'comment':'GHI', 'type':'comment'} True @@ -361,6 +573,17 @@ def _parse_user(id): return ret def parse_user(bugdir, id): + """Parse a user ID (see :class:`ID`), returning a dict of parsed + information. + + The returned dict will contain a value for "type" (from + :data:`HIERARCHY`) and values for the levels that are defined. + + Notes + ----- + This function tries to expand IDs before parsing, so it can handle + both short and long IDs successfully. + """ long_id = short_to_long_user([bugdir], id) return _parse_user(long_id) diff --git a/libbe/util/tree.py b/libbe/util/tree.py index 04ce4b3..812b0bd 100644 --- a/libbe/util/tree.py +++ b/libbe/util/tree.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. -""" -Define a traversable tree structure. +"""Define :class:`Tree`, a traversable tree structure. """ import libbe @@ -25,12 +24,19 @@ if libbe.TESTING == True: import doctest class Tree(list): - """ - Construct + """A traversable tree structure. + + Examples + -------- + + Construct:: + +-b---d-g a-+ +-e +-c-+-f-h-i + with + >>> i = Tree(); i.n = "i" >>> h = Tree([i]); h.n = "h" >>> f = Tree([h]); f.n = "f" @@ -43,16 +49,31 @@ class Tree(list): >>> a.append(c) >>> a.append(b) + Get the longest branch length with + >>> a.branch_len() 5 + + Sort the tree recursively. Here we sort longest branch length + first. + >>> a.sort(key=lambda node : -node.branch_len()) >>> "".join([node.n for node in a.traverse()]) 'acfhiebdg' + + And here we sort shortest branch length first. + >>> a.sort(key=lambda node : node.branch_len()) >>> "".join([node.n for node in a.traverse()]) 'abdgcefhi' + + We can also do breadth-first traverses. + >>> "".join([node.n for node in a.traverse(depth_first=False)]) 'abcdefghi' + + Serialize the tree with depth marking branches. + >>> for depth,node in a.thread(): ... print "%*s" % (2*depth+1, node.n) a @@ -64,6 +85,10 @@ class Tree(list): f h i + + Flattening the thread disables depth increases except at + branch splits. + >>> for depth,node in a.thread(flatten=True): ... print "%*s" % (2*depth+1, node.n) a @@ -75,6 +100,9 @@ class Tree(list): f h i + + We can also check if a node is contained in a tree. + >>> a.has_descendant(g) True >>> c.has_descendant(g) @@ -94,17 +122,22 @@ class Tree(list): return self.__cmp__(other) != 0 def branch_len(self): - """ - Exhaustive search every time == SLOW. + """Return the largest number of nodes from root to leaf (inclusive). - Use only on small trees, or reimplement by overriding - child-addition methods to allow accurate caching. + For the tree:: - For the tree +-b---d-g a-+ +-e +-c-+-f-h-i + this method returns 5. + + Notes + ----- + Exhaustive search every time == *slow*. + + Use only on small trees, or reimplement by overriding + child-addition methods to allow accurate caching. """ if len(self) == 0: return 1 @@ -112,18 +145,30 @@ class Tree(list): return 1 + max([child.branch_len() for child in self]) def sort(self, *args, **kwargs): - """ - This method can be slow, e.g. on a branch_len() sort, since a - node at depth N from the root has it's branch_len() method - called N times. + """Sort the tree recursively. + + This method extends :meth:`list.sort` to Trees. + + Notes + ----- + This method can be slow, e.g. on a :meth:`branch_len` sort, + since a node at depth `N` from the root has it's + :meth:`branch_len` method called `N` times. """ list.sort(self, *args, **kwargs) for child in self: child.sort(*args, **kwargs) def traverse(self, depth_first=True): - """ - Note: you might want to sort() your tree first. + """Generate all the nodes in a tree, starting with the root node. + + Parameters + ---------- + depth_first : bool + Depth first by default, but you can set `depth_first` to + `False` for breadth first ordering. Siblings are returned + in the order they are stored, so you might want to + :meth:`sort` your tree first. """ if depth_first == True: yield self @@ -139,25 +184,31 @@ class Tree(list): queue.extend(node) def thread(self, flatten=False): - """ - When flatten==False, the depth of any node is one greater than - the depth of its parent. That way the inheritance is - explicit, but you can end up with highly indented threads. - - When flatten==True, the depth of any node is only greater than - the depth of its parent when there is a branch, and the node - is not the last child. This can lead to ancestry ambiguity, - but keeps the total indentation down. E.g. + """Generate a (depth, node) tuple for every node in the tree. + + When `flatten` is `False`, the depth of any node is one + greater than the depth of its parent. That way the + inheritance is explicit, but you can end up with highly + indented threads. + + When `flatten` is `True`, the depth of any node is only + greater than the depth of its parent when there is a branch, + and the node is not the last child. This can lead to ancestry + ambiguity, but keeps the total indentation down. For example:: + +-b +-b-c a-+-c and a-+ +-d-e-f +-d-e-f - would both produce (after sorting by branch_len()) - (0, a) - (1, b) - (1, c) - (0, d) - (0, e) - (0, f) + + would both produce (after sorting by :meth:`branch_len`):: + + (0, a) + (1, b) + (1, c) + (0, d) + (0, e) + (0, f) + """ stack = [] # ancestry of the current node if flatten == True: @@ -182,6 +233,20 @@ class Tree(list): stack.append(node) def has_descendant(self, descendant, depth_first=True, match_self=False): + """Check if a node is contained in a tree. + + Parameters + ---------- + descendant : Tree + The potential descendant. + depth_first : bool + The search order. Set this if you feel depth/breadth would + be a faster search. + match_self : bool + Set to `True` for:: + + x.has_descendant(x, match_self=True) -> True + """ if descendant == self: return match_self for d in self.traverse(depth_first): diff --git a/libbe/util/utility.py b/libbe/util/utility.py index d42a4f9..92ca0d5 100644 --- a/libbe/util/utility.py +++ b/libbe/util/utility.py @@ -33,11 +33,16 @@ if libbe.TESTING == True: import doctest class InvalidXML(ValueError): - """ - Invalid XML while parsing for a *.from_xml() method. - type - string identifying *, e.g. "bug", "comment", ... - element - ElementTree.Element instance which caused the error - error - string describing the error + """Invalid XML while parsing for a `*.from_xml()` method. + + Parameters + ---------- + type : str + String identifying `*`, e.g. "bug", "comment", ... + element : :class:`ElementTree.Element` + ElementTree.Element instance which caused the error. + error : str + Error description. """ def __init__(self, type, element, error): msg = 'Invalid %s xml: %s\n %s\n' \ @@ -50,16 +55,18 @@ class InvalidXML(ValueError): def search_parent_directories(path, filename): """ 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") - will return the path to the first existing file from - /a/b/c/.be - /a/b/.be - /a/.be - /.be - or None if none of those files exist. + of path's parents. For example:: + + search_parent_directories("/a/b/c", ".be") + + will return the path to the first existing file from:: + + /a/b/c/.be + /a/b/.be + /a/.be + /.be + + or `None` if none of those files exist. """ path = os.path.realpath(path) assert os.path.exists(path) @@ -74,7 +81,11 @@ def search_parent_directories(path, filename): path = os.path.dirname(path) class Dir (object): - "A temporary directory for testing use" + """A temporary directory for testing use. + + Make sure you run :meth:`cleanup` after you're done using the + directory. + """ def __init__(self): self.path = tempfile.mkdtemp(prefix="BEtest") self.removed = False @@ -86,18 +97,47 @@ class Dir (object): return self.path RFC_2822_TIME_FMT = "%a, %d %b %Y %H:%M:%S +0000" +"""RFC 2822 [#]_ format string for :func:`time.strftime` and +:func:`time.strptime`. +.. [#] See `RFC 2822`_, sections 3.3 and A.1.1. +.. _RFC 2822: http://www.faqs.org/rfcs/rfc2822.html +""" def time_to_str(time_val): - """Convert a time value into an RFC 2822-formatted string. This format - lacks sub-second data. + """Convert a time number into an RFC 2822-formatted string. + + Parameters + ---------- + time_val : float + Float seconds since the Epoc, see :func:`time.time`. + Note that while `time_val` may contain sub-second data, + the output string will not. + + Examples + -------- + >>> time_to_str(0) 'Thu, 01 Jan 1970 00:00:00 +0000' + + See Also + -------- + str_to_time : inverse + handy_time : localtime string """ return time.strftime(RFC_2822_TIME_FMT, time.gmtime(time_val)) def str_to_time(str_time): """Convert an RFC 2822-fomatted string into a time value. + + Parameters + ---------- + str_time : str + An RFC 2822-formatted string. + + Examples + -------- + >>> str_to_time("Thu, 01 Jan 1970 00:00:00 +0000") 0 >>> q = time.time() @@ -105,6 +145,10 @@ def str_to_time(str_time): True >>> str_to_time("Thu, 01 Jan 1970 00:00:00 -1000") 36000 + + See Also + -------- + time_to_str : inverse """ timezone_str = str_time[-5:] if timezone_str != "+0000": @@ -116,10 +160,29 @@ def str_to_time(str_time): return time_val + timesign*timezone def handy_time(time_val): + """Convert a time number into a useful localtime. + + Where :func:`time_to_str` returns GMT +0000, `handy_time` returns + a string in local time. This may be more accessible for the user. + + Parameters + ---------- + time_val : float + Float seconds since the Epoc, see :func:`time.time`. + """ return time.strftime("%a, %d %b %Y %H:%M", time.localtime(time_val)) def time_to_gmtime(str_time): """Convert an RFC 2822-fomatted string to a GMT string. + + Parameters + ---------- + str_time : str + An RFC 2822-formatted string. + + Examples + -------- + >>> time_to_gmtime("Thu, 01 Jan 1970 00:00:00 -1000") 'Thu, 01 Jan 1970 10:00:00 +0000' """ @@ -127,8 +190,23 @@ def time_to_gmtime(str_time): return time_to_str(time_val) def iterable_full_of_strings(value, alternative=None): - """ - Require an iterable full of strings. + """Require an iterable full of strings. + + This is useful, for example, in validating `*.extra_strings`. + See :attr:`libbe.bugdir.BugDir.extra_strings` + + Parameters + ---------- + value : list or None + The potential list of strings. + alternative + Allow a default (e.g. `None`), such that:: + + iterable_full_of_strings(value=x, alternative=x) -> True + + Examples + -------- + >>> iterable_full_of_strings([]) True >>> iterable_full_of_strings(["abc", "def", u"hij"]) @@ -140,21 +218,31 @@ def iterable_full_of_strings(value, alternative=None): """ if value == alternative: return True - elif not hasattr(value, "__iter__"): + elif not hasattr(value, '__iter__'): return False for x in value: if type(x) not in types.StringTypes: return False return True -def underlined(instring): - """Produces a version of a string that is underlined with '=' +def underlined(string, char='='): + """Produces a version of a string that is underlined. + + Parameters + ---------- + string : str + The string to underline + char : str + The character to use for the underlining. + + Examples + -------- >>> underlined("Underlined String") 'Underlined String\\n=================' """ - - return "%s\n%s" % (instring, "="*len(instring)) + assert len(char) == 0, char + return '%s\n%s' % (string, char*len(string)) if libbe.TESTING == True: suite = doctest.DocTestSuite() -- cgit From 960565a8cc80f98d0a8bfa77029fbc78692ea1a1 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Mon, 8 Feb 2010 17:02:56 -0500 Subject: Consolidated Makefile and doc/man/module.mk. Incorperated doc/Makefile. Now make sphinx builds the Sphinx HTML documentation (in doc/.build/html), and make clean cleans up everything. Having a separate module.mk was just making things confusing, so I took it out ;). --- Makefile | 53 ++++++++++++++++++++++++++++++----------------------- doc/Makefile | 27 ++++++++++++++++----------- doc/man/module.mk | 44 -------------------------------------------- 3 files changed, 46 insertions(+), 78 deletions(-) delete mode 100644 doc/man/module.mk diff --git a/Makefile b/Makefile index 5e2c4f4..e9e1748 100644 --- a/Makefile +++ b/Makefile @@ -24,51 +24,58 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. SHELL = /bin/bash -PATH = /usr/bin:/bin - -# Directories with semantic meaning -DOC_DIR := doc/man - -# Variables that will be extended by module include files -GENERATED_FILES := libbe/_version.py build -CODE_MODULES := -CODE_PROGRAMS := - -# List of modules (directories) that comprise our 'make' project -MODULES += ${DOC_DIR} - RM = rm +#PATH = /usr/bin:/bin # must include sphinx-build for 'sphinx' target. #PREFIX = /usr/local PREFIX = ${HOME} INSTALL_OPTIONS = "--prefix=${PREFIX}" +# Directories with semantic meaning +DOC_DIR := doc +MAN_DIR := ${DOC_DIR}/man + +MANPAGES = be.1 +GENERATED_FILES := build libbe/_version.py + +MANPAGE_FILES = $(patsubst %,${MAN_DIR}/%,${MANPAGES}) +GENERATED_FILES += ${MANPAGE_FILES} + .PHONY: all all: build -# Include the make data for each module -include $(patsubst %,%/module.mk,${MODULES}) - .PHONY: build build: libbe/_version.py python setup.py build +.PHONY: doc +doc: sphinx man + .PHONY: install -install: doc build +install: build doc python setup.py install ${INSTALL_OPTIONS} -#cp -v interfaces/xml/* ${PREFIX}/bin -#cp -v interfaces/email/catmutt ${PREFIX}/bin - +test: build + python test.py + .PHONY: clean clean: $(RM) -rf ${GENERATED_FILES} + $(MAKE) -C ${DOC_DIR} clean -test : build - python test.py - + .PHONY: libbe/_version.py libbe/_version.py: bzr version-info --format python > $@ + +.PHONY: man +man: ${MANPAGE_FILES} + +%.1: %.1.sgml + docbook-to-man $< > $@ + +.PHONY: sphinx +sphinx: + $(MAKE) -C ${DOC_DIR} html diff --git a/doc/Makefile b/doc/Makefile index df8818c..6d7bbc5 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -26,37 +26,38 @@ help: @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " libbe to autogenerate files for all libbe modules" clean: - -rm -rf $(BUILDDIR)/* + -rm -rf $(BUILDDIR) libbe -html: +html: libbe $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." -dirhtml: +dirhtml: libbe $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." -pickle: +pickle: libbe $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." -json: +json: libbe $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." -htmlhelp: +htmlhelp: libbe $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." -qthelp: +qthelp: libbe $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ @@ -65,25 +66,29 @@ qthelp: @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/bugs-everywhere.qhc" -latex: +latex: libbe $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." -changes: +changes: libbe $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." -linkcheck: +linkcheck: libbe $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." -doctest: +doctest: libbe $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." + +.PHONY : libbe +libbe : + python generate-libbe-txt.py diff --git a/doc/man/module.mk b/doc/man/module.mk deleted file mode 100644 index 1eb6955..0000000 --- a/doc/man/module.mk +++ /dev/null @@ -1,44 +0,0 @@ -# :vim: filetype=make : -*- makefile; coding: utf-8; -*- - -# doc/module.mk -# Part of Bugs Everywhere, a distributed bug tracking system. -# -# Copyright (C) 2008-2010 Chris Ball -# Gianluca Montecchi -# W. Trevor King -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -# Makefile module for documentation - -MODULE_DIR := doc/man - -MANPAGES = be.1 -manpage_files = $(patsubst %,${MODULE_DIR}/%,${MANPAGES}) - -GENERATED_FILES += ${manpage_files} - - -.PHONY: doc -doc: man - -build: doc - - -.PHONY: man -man: ${manpage_files} - -%.1: %.1.sgml - docbook-to-man $< > $@ -- cgit