From defc6247d72ec6507df5d408ca82a34388edec0d Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 6 May 2010 07:25:12 -0400 Subject: [PATCH] Added Sphinx documentation framework --- doc/.sconsrc-sphinx | 1 + doc/SConstruct | 288 ++++++++++++++++++++++++++++++++++ doc/USAGE | 246 ----------------------------- doc/conf.py | 197 +++++++++++++++++++++++ doc/config.txt | 44 ++++++ doc/doc.txt | 28 ++++ doc/generate-hooke-txt.py | 320 ++++++++++++++++++++++++++++++++++++++ doc/{GUI => gui.txt} | 0 doc/hacking.txt | 34 ++++ doc/index.txt | 78 ++++++++++ doc/install.txt | 77 +++++++++ doc/testing.txt | 25 +++ doc/tutorial.txt | 294 ++++++++++++++++++++++++++++++++++ hooke/__init__.py | 75 +++++++++ 14 files changed, 1461 insertions(+), 246 deletions(-) create mode 100644 doc/.sconsrc-sphinx create mode 100644 doc/SConstruct delete mode 100644 doc/USAGE create mode 100644 doc/conf.py create mode 100644 doc/config.txt create mode 100644 doc/doc.txt create mode 100644 doc/generate-hooke-txt.py rename doc/{GUI => gui.txt} (100%) create mode 100644 doc/hacking.txt create mode 100644 doc/index.txt create mode 100644 doc/install.txt create mode 100644 doc/testing.txt create mode 100644 doc/tutorial.txt diff --git a/doc/.sconsrc-sphinx b/doc/.sconsrc-sphinx new file mode 100644 index 0000000..bb1926a --- /dev/null +++ b/doc/.sconsrc-sphinx @@ -0,0 +1 @@ +genrst = 'python generate-hooke-txt.py' diff --git a/doc/SConstruct b/doc/SConstruct new file mode 100644 index 0000000..fb1abe5 --- /dev/null +++ b/doc/SConstruct @@ -0,0 +1,288 @@ +""" +This is a generic SCons script for running Sphinx (http://sphinx.pocoo.org). + +Type 'scons -h' for help. This prints the available build targets on your +system, and the configuration options you can set. + +If you set the 'cache' option, the option settings are cached into a file +called '.sconsrc-sphinx' in the current directory. When running +subsequently, this file is reread. A file with this name is also read from +your home directory, if it exists, so you can put global settings there. + +The script looks into your 'conf.py' file for information about the +project. This is used in various places (e.g., to print the introductory +message, and create package files). + +Here's some examples. To build HTML docs: + + scons html + +To create a package containing HTML and PDF docs, remembering the 'install' +setting: + + scons install=html,pdf cache=True package + +To clean up everything: + + scons -c all +""" + +# Script info. +__author__ = "Glenn Hutchings" +__email__ = "zondo42@googlemail.com" +__url__ = "http://bitbucket.org/zondo/sphinx-scons" +__license__ = "BSD" +__version__ = "0.4" + +import sys, os + +# Build targets. +targets = ( + ("html", "make standalone HTML files"), + ("dirhtml", "make HTML files named index.html in directories"), + ("pickle", "make pickle files"), + ("json", "make JSON files"), + ("htmlhelp", "make HTML files and a HTML help project"), + ("qthelp", "make HTML files and a qthelp project"), + ("devhelp", "make HTML files and a GNOME DevHelp project"), + ("epub", "make HTML files and an EPUB file for bookreaders"), + ("latex", "make LaTeX sources"), + ("text", "make text file for each RST file"), + ("pdf", "make PDF file from LaTeX sources"), + ("ps", "make PostScript file from LaTeX sources"), + ("dvi", "make DVI file from LaTeX sources"), + ("changes", "make an overview over all changed/added/deprecated items"), + ("linkcheck", "check all external links for integrity"), + ("doctest", "run all doctests embedded in the documentation if enabled"), + ("source", "run a command to generate the reStructuredText source"), +) + +# LaTeX builders. +latex_builders = {"pdf": "PDF", "ps": "PostScript", "dvi": "DVI"} + +# List of target names. +targetnames = [name for name, desc in targets] + +# Configuration cache filename. +cachefile = ".sconsrc-sphinx" + +# User cache file. +homedir = os.path.expanduser('~') +usercache = os.path.join(homedir, cachefile) + +# Configuration options. +config = Variables([usercache, cachefile], ARGUMENTS) + +config.AddVariables( + EnumVariable("default", "default build target", "html", targetnames), + PathVariable("config", "sphinx configuration file", "conf.py"), + PathVariable("srcdir", "source directory", ".", + PathVariable.PathIsDir), + PathVariable("builddir", "build directory", "build", + PathVariable.PathIsDirCreate), + PathVariable("doctrees", "place to put doctrees", None, + PathVariable.PathAccept), + EnumVariable("paper", "LaTeX paper size", None, + ["a4", "letter"], ignorecase = False), + ("tags", "comma-separated list of 'only' tags", None), + ("builder", "program to run to build things", "sphinx-build"), + ("opts", "extra builder options to use", None), + ListVariable("install", "targets to install", ["html"], targetnames), + PathVariable("instdir", "installation directory", "/usr/local/doc", + PathVariable.PathAccept), + EnumVariable("pkgtype", "package type to build with 'scons package'", + "zip", ["zip", "targz", "tarbz2"], ignorecase = False), + BoolVariable("cache", "whether to cache settings in %s" % cachefile, False), + BoolVariable("debug", "debugging flag", False), + ("genrst", "Command to regenerate reStructuredText source", None), +) + +# Create a new environment, inheriting PATH to find builder program. Also +# force LaTeX instead of TeX, since the .tex file won't exist at the right +# time to check which one to use. +env = Environment(ENV = {"PATH" : os.environ["PATH"]}, + TEX = "latex", PDFTEX = "pdflatex", + tools = ['default', 'packaging'], + variables = config) + +# Get configuration values from environment. +sphinxconf = env["config"] +builder = env["builder"] +default = env["default"] + +srcdir = env["srcdir"] +builddir = env["builddir"] +doctrees = env.get("doctrees", os.path.join(builddir, "doctrees")) + +cache = env["cache"] +debug = env["debug"] + +options = env.get("opts", None) +paper = env.get("paper", None) +tags = env.get("tags", None) +genrst = env.get("genrst", None) + +instdir = env["instdir"] +install = env["install"] +pkgtype = env["pkgtype"] + +# Dump internals if debugging. +if debug: + print "Environment:" + print env.Dump() + +# Get parameters from Sphinx config file. +sphinxparams = {} +execfile(sphinxconf, sphinxparams) + +project = sphinxparams["project"] +release = sphinxparams["release"] +copyright = sphinxparams["copyright"] + +try: + texfilename = sphinxparams["latex_documents"][0][1] +except KeyError: + texfilename = None + +name2tag = lambda name: name.replace(" ", "-").strip("()") +project_tag = name2tag(project) +release_tag = name2tag(release) +package_tag = project_tag.lower() + "-" + release_tag.lower() + +# Build project description string. +description = "%(project)s, release %(release)s, " \ + "copyright %(copyright)s" % locals() + +Help(description + "\n\n") +help_format = " %-10s %s\n" + +# Print banner if required. +if not any(map(GetOption, ("silent", "clean", "help"))): + print + print "This is", description + print + +# Build sphinx command-line options. +opts = [] + +if tags: + opts.extend(["-t %s" % tag for tag in tags.split(",")]) + +if paper: + opts.append("-D latex_paper_size=%s" % paper) + +if options: + opts.append(options) + +options = " ".join(opts) + +# Build Sphinx command template. +sphinxcmd = """ +%(builder)s -b %(name)s -d %(doctrees)s %(options)s %(srcdir)s %(targetdir)s +""".strip() + +# Set up LaTeX input builder if required. +if texfilename: + latexdir = Dir("latex", builddir) + texinput = File(texfilename, latexdir) + env.SideEffect(texinput, "latex") + env.NoClean(texinput) + +# Add build targets. +Help("Build targets:\n\n") + +if genrst != None: + source = env.Command('source', [], genrst) + env.AlwaysBuild(source) + env.Depends(srcdir, source) +else: + Alias('source', srcdir) + +for name, desc in targets: + target = Dir(name, builddir) + targetdir = str(target) + + if name == 'source': + pass + elif name not in latex_builders: + # Standard Sphinx target. + env.Command(name, sphinxconf, + sphinxcmd % locals(), chdir = True) + env.AlwaysBuild(name) + env.Alias(target, name) + elif texinput: + # Target built from LaTeX sources. + try: + buildfunc = getattr(env, latex_builders[name]) + except AttributeError: + continue + + filename = project_tag + "." + name + outfile = File(filename, latexdir) + + buildfunc(outfile, texinput) + + # Copy built file to separate directory. + target = File(filename, target) + env.Command(target, outfile, Move(target, outfile), chdir = True) + + env.Alias(name, target) + else: + continue + + env.Clean(name, [target]) + env.Clean('all', target) + + if name == default: desc += " (default)" + Help(help_format % (name, desc)) + +Clean('all', doctrees) +Default(default) + +# Add installation targets and collect package sources. +Help("\nOther targets:\n\n") + +Help(help_format % ("install", "install documentation")) +projectdir = os.path.join(instdir, project_tag) +sources = [] + +for name in install: + source = Dir(name, builddir) + sources.append(source) + + inst = env.Install(projectdir, source) + env.Alias('install', inst) + + for node in env.Glob(os.path.join(str(source), '*')): + filename = str(node).replace(builddir + os.path.sep, "") + dirname = os.path.dirname(filename) + dest = os.path.join(projectdir, dirname) + inst = env.Install(dest, node) + env.Alias('install', inst) + +# Add uninstall target. +env.Command('uninstall', None, Delete(projectdir), chdir = True) +Help(help_format % ("uninstall", "uninstall documentation")) + +## Add package builder. +#packageroot = "-".join([project_tag, release_tag]) +#archive, package = env.Package(NAME = project_tag, VERSION = release, +# PACKAGEROOT = packageroot, +# PACKAGETYPE = pkgtype, +# source = sources) +# +#env.AlwaysBuild(archive) +#env.AddPostAction(archive, Delete(packageroot)) +#Help(help_format % ("package", "build documentation package")) +# +#env.Clean('all', archive) + +# Add config settings to help. +Help("\nConfiguration variables:") +for line in config.GenerateHelpText(env).split("\n"): + Help("\n " + line) + +# Save local configuration if required. +if cache: + config.Update(env) + config.Save(cachefile, env) diff --git a/doc/USAGE b/doc/USAGE deleted file mode 100644 index eed629a..0000000 --- a/doc/USAGE +++ /dev/null @@ -1,246 +0,0 @@ -Starting Hooke -============== - -Open a terminal, go to the directory Hooke is installed and type - python bin/hooke -(You may need to give the full path for Python on Windows systems). -If everything is OK, Hooke displays a nice splashscreen and starts. - -Once Hooke is launched from the terminal window, you see a text like -the following: - - Starting Hooke. - Imported plugin fit - Imported plugin procplots - Imported plugin flatfilts - Imported plugin generalclamp - Imported plugin generalvclamp - Imported plugin massanalysis - Imported plugin macro - Imported driver picoforce - Imported driver hemingclamp - Imported driver csvdriver - Imported driver tutorialdriver - - Warning: Invalid work directory. - This is Hooke, version 0.8.0 Seinei - (c) Massimo Sandal, 2006. - Released under the GNU General Public License Version 2. - Hooke is Free software. - ---- - hooke: - -Hooke tells you that plugins and drivers have been loaded, and now -you’re ready to go. You’re now at the Hooke command line. In the -meantime, a splashscreen and a window with a dummy force curve should -appear . At the command line, type "help" or "?" to obtain a list of -available commands. - -hooke: ? -Documented commands (type help ): -======================================== -addtolist debug exit genlist ls notelog previous set -cd derivplot export getlist n p printlist size -contact dir flatfilt jump next plateau pwd subtplot -current distance force loadlist note plot savelist wlc -Undocumented commands: -====================== -help - -hooke: - - -Begin your analysis -=================== - -Create a playlist ------------------ - -To start analyzing your curves, you first have to build a playlist. The -playlist is just an index of the force curve files you want to -analyze. Imagine it as a music playlist (that’s why it is called a -playlist), but made of data files instead of MP3s (or FLACs :p). - -Suppose you have 100 PicoForce curve files in your curves directory, -starting from mycurve.000 and ending in mycurve.100 and you want to -analyze them all. - -You then can cd to the directory - - hooke: cd c:\curves - -Type pwd to check the directory is correct - - hooke: pwd - c:\curves - hooke: - -You can list the files in the directory using ls or dir (they’re synonims) - - hooke: ls - [’mycurve.000’, ’mycurve.001’, ...] - -Now you are ready to generate the playlist. The command to use is genlist - - hooke: genlist mycurve.* - -You can also generate a playlist containing all what you find in the -directory by typing: - - hooke: genlist c:\curves - -If you want to select what curves to see, based on the filename, you -can use wildcards. - - For example: - - hooke: genlist mycurve.05* - -will take only curves from mycurve.050 to mycurve.059. - -Saving and loading playlists ----------------------------- - -Note that by using genlist you just generate the playlist in the local -session. To save your playlist to a file, thus avoiding to regenerate -it, type: - - hooke: savelist mylist - -The list will be saved, in this example, in the file mylist.hkp. Hooke -will add the extension .hkp to the playlist if you forget to. The .hkp -file is an XML file you can read and edit with any text editor -(i.e. Wordpad), if needed. If you want to load it, just issue loadlist -mylist.hkp or loadlist mylist, Hooke will handle the missing .hkp -extension. This will load the saved playlist, as if you just generated -it. - -Generating the playlist, you should see the plot of the first curve -appearing. If, generating the playlist, you are including by chance a -non-force curve file that Hooke cannot open, it should be (more or -less) silently ignored. If it gives some error, or it does not plot -anything, try to navigate forward, and see if the next curve is -plotted; it is possible you spotted a corrupted file. Navigate the -playlist - -Navigating playlists --------------------- - -Now you can navigate through your playlist using the command next and -previous or, more easily, their aliases n and p. You don’t need to -type n every time to run along a list of curves. If you press Return -to an empty prompt, Hooke will repeat the last command you issued -explicitly. - -You can also navigate through the command history by using the up and -down arrows. - -When arriving to the last curve of your playlist, pressing n will just -come back to the first. Analogously, pressing p when at the first -curve will jump to the last. - -You can also jump to a given curve, this way: - - hooke: jump c:\curves\mycurve.123 - -but be careful to tell Hooke the full path to that curve, otherwise it -will not find it. - - -Taking notes -============ - -You can take notes about the curves you are looking at. Just type note -followed by the text you want to append to that curve. Hooke will save -the text in your current playlis and in an external log file. The -output will look like this: - - Notes taken at Sun Sep 17 20:42:07 2006 - /home/cyclopia/work/tris/20060620a.041 | This is a note - /home/cyclopia/work/tris/20060620a.207 | This is another note - /home/cyclopia/work/tris/20060620a.286 | This is a third one - -The first time you type note in a session, Hooke will ask you for a -filename of the log. - -Usually curves you annotated are useful later. You can copy the curves -you annotated in a different directory by using the copylog command, - - hooke: copylog c:\nicecurves - -which will copy all curves you have annotated in the c:\nicecurves -directory. Take care that the directory already exists before doing -that. - - -Exporting curves -================ - -You can export Hooke curves as images and as text columns. To export -as images, issue the export command followed by the -filename. Supported formats are PNG (raster) and EPS (Encapsulated -Postscript, vector). The export format is determined by the filename -extension, so export foo.png and export foo.eps will save a PNG and -EPS file respectively. - -To export as text, use the txt command, followed by the filename. The -output is a text file containing columns (first two are X and Y of -extension, last two are X and Y of retraction). - - -Interacting with the plot -========================= - -Measuring distances and forces ------------------------------- - -You can easily zoom in the plot by dragging a rectangle on it with the -left mouse button. To zoom out, click the right mouse -button. Sometimes by zooming in and out too much, you can lose the -picture (this is probably a small bug in Matplotlib). Just type plot -at the command line and the curve will be refreshed. - -You can measure distances and forces directly in the plot. Just issue -the command distance. You will be asked to click two points: do -it. When you click a point, a blue dot should appear. When you click -the second point, the distance (in nanometers) will apper on the -command line. force works in the same way. You can use delta if you -prefer, which gives meaningful values for every kind of graph (not -only force curves). If you want to know the coordinates of a single -point, use point. - -Hooke automatically adjusts the position of the clicked point at the -nearest point in the graph, so you will be always measuring distances -and forces between points in the graph. - -The commands force and distance are present in the generalvclamp.py -plugin. - -Worm like chain fitting ------------------------ - -You can measure by hand the parameters relative to a force peak using -a worm-like chain fitting with the wlc command. The command by default -automatically finds the contact point, asks for two points delimiting -the portion to fit, and performs a two-variable fit, with contour -length and persistence length as output, with relative errors. If -desired, one can use the noauto option to manually click the contact -point, and/or the pl=[number] options to impose a specific persistence -length (in nanometers). Please see the help of the wlc command from -the Hooke command line for details. - -Variables ---------- - -You can set environment variables to influence the behaviour of -Hooke. The command to use is set. - -You can alter permanently the behaviour of Hooke by setting these -variables in the file conf/hooke.conf. This is a very simple XML file, -just change the values of the variables with an ASCII text editor (not -Word or a word processor - on Windows, Wordpad should work). Be -careful with the correct XML syntax (which you should grasp very -easily looking at the default configuration file) otherwise Hooke will -crash on the next startup. - -See VariableList for help on individual variables. diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..dd29ec5 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,197 @@ +# -*- coding: utf-8 -*- +# +# sphinx-scons documentation build configuration file, created by +# sphinx-quickstart on Thu Mar 12 10:37:15 2009. +# +# 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('.')) +sys.path.insert(0, os.path.abspath('..')) + +import hooke + +# -- 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 = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.txt' #'.rst' + +# The encoding of source files. +#source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Hooke' +copyright = u'2006-2010, Massimo Sandal et al.' + +# 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 = hooke.version(2) +# The full version, including alpha/beta/rc tags. +release = hooke.version(5) + +# 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 = project + " documentation" + +# 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 = 'sphinx-sconsdoc' + + +# -- 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', 'hooke.tex', ur'Hooke Documentation', + ur'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/config.txt b/doc/config.txt new file mode 100644 index 0000000..6f0359b --- /dev/null +++ b/doc/config.txt @@ -0,0 +1,44 @@ +***************** +Configuring Hooke +***************** + +Hooke initializes a number of variables by reading configuration +files. + +Syntax +------ + +Hooke uses Python's configparser_ library to read the config files. +The format is similar to MS Windows' INI (initialization) format:: + + [My Section] + foo = bar + baz = 5 + +.. _configparser: http://docs.python.org/library/configparser.html + +Examples +-------- + +Run:: + + $ hooke --example-config-file + +To print a well commented example config file to stdout. + + +Finding configuration files +--------------------------- + +The default search path follows the `Filesystem Hierarchy +Standard`_, and so will probably need adjustment for non-*nix systems. +The default path list is + +* /usr/share/hooke/hooke.cfg +* /etc/hooke/hooke.cfg +* ~/.hooke.cfg + +but alternatives can be specified from the command line launching +Hooke. + +_ ..Filesystem Hierarchy Standard: http://www.pathname.com/fhs/ diff --git a/doc/doc.txt b/doc/doc.txt new file mode 100644 index 0000000..319fe4d --- /dev/null +++ b/doc/doc.txt @@ -0,0 +1,28 @@ +**************************** +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 to compile the +documentation, change to the ``doc/`` directory and run:: + + $ scons + +For which you'll need to install Sphinx, numpydoc, and SCons_:: + + $ easy_install Sphinx + $ easy_install numpydoc + +.. _Sphinx: http://sphinx.pocoo.org/ +.. _numpydoc: http://pypi.python.org/pypi/numpydoc +.. _SCons: http://www.scons.org/ + +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-hooke-txt.py b/doc/generate-hooke-txt.py new file mode 100644 index 0000000..0f7dd5c --- /dev/null +++ b/doc/generate-hooke-txt.py @@ -0,0 +1,320 @@ +#!/usr/bin/python +# +# COPYRIGHT + +"""Auto-generate reStructuredText of the hooke module tree for Sphinx. + +This script is adapted from one written for `Bugs Everywhere`_. + +.. _Bugs Everywhere: http://bugseverywhere.org/ +""" + +import sys +import os, os.path + + +sys.path.insert(0, os.path.abspath('..')) + + +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 sorted(children) + ] + ['', '']) + +def make_module_txt(modname, children): + filename = os.path.join('hooke', '%s.txt' % modname) + if not os.path.exists('hooke'): + os.mkdir('hooke') + 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() + + + +class Tree(list): + """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" + >>> e = Tree(); e.n = "e" + >>> c = Tree([f,e]); c.n = "c" + >>> g = Tree(); g.n = "g" + >>> d = Tree([g]); d.n = "d" + >>> b = Tree([d]); b.n = "b" + >>> a = Tree(); a.n = "a" + >>> 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 + b + d + g + c + e + 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 + b + d + g + c + e + f + h + i + + We can also check if a node is contained in a tree. + + >>> a.has_descendant(g) + True + >>> c.has_descendant(g) + False + >>> a.has_descendant(a) + False + >>> a.has_descendant(a, match_self=True) + True + """ + def __cmp__(self, other): + return cmp(id(self), id(other)) + + def __eq__(self, other): + return self.__cmp__(other) == 0 + + def __ne__(self, other): + return self.__cmp__(other) != 0 + + def branch_len(self): + """Return the largest number of nodes from root to leaf (inclusive). + + 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 + else: + return 1 + max([child.branch_len() for child in self]) + + def sort(self, *args, **kwargs): + """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): + """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 + for child in self: + for descendant in child.traverse(): + yield descendant + else: # breadth first, Wikipedia algorithm + # http://en.wikipedia.org/wiki/Breadth-first_search + queue = [self] + while len(queue) > 0: + node = queue.pop(0) + yield node + queue.extend(node) + + def thread(self, flatten=False): + """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 :meth:`branch_len`):: + + (0, a) + (1, b) + (1, c) + (0, d) + (0, e) + (0, f) + + """ + stack = [] # ancestry of the current node + if flatten == True: + depthDict = {} + + for node in self.traverse(depth_first=True): + while len(stack) > 0 \ + and id(node) not in [id(c) for c in stack[-1]]: + stack.pop(-1) + if flatten == False: + depth = len(stack) + else: + if len(stack) == 0: + depth = 0 + else: + parent = stack[-1] + depth = depthDict[id(parent)] + if len(parent) > 1 and node != parent[-1]: + depth += 1 + depthDict[id(node)] = depth + yield (depth,node) + 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): + if descendant == d: + return True + return False + + +def python_tree(root_path='hooke', root_modname='hooke'): + tree = Tree() + tree.path = root_path + tree.parent = None + stack = [tree] + while len(stack) > 0: + f = stack.pop(0) + if f.path.endswith('.py'): + f.name = os.path.basename(f.path)[:-len('.py')] + elif os.path.isdir(f.path) \ + and os.path.exists(os.path.join(f.path, '__init__.py')): + f.name = os.path.basename(f.path) + f.is_module = True + for child in os.listdir(f.path): + if child == '__init__.py': + continue + c = Tree() + c.path = os.path.join(f.path, child) + c.parent = f + stack.append(c) + else: + continue + if f.parent == None: + f.modname = root_modname + else: + f.modname = f.parent.modname + '.' + f.name + f.parent.append(f) + return tree + +if __name__ == '__main__': + pt = python_tree(root_path='../hooke', root_modname='hooke') + for node in pt.traverse(): + make_module_txt(node.modname, [c.modname for c in node]) diff --git a/doc/GUI b/doc/gui.txt similarity index 100% rename from doc/GUI rename to doc/gui.txt diff --git a/doc/hacking.txt b/doc/hacking.txt new file mode 100644 index 0000000..86e969b --- /dev/null +++ b/doc/hacking.txt @@ -0,0 +1,34 @@ +************* +Hacking Hooke +************* + + +Architecture +============ + +To clean up the internals, were going to go crazy on the +object-oriented front, and try to keep the core functionality free of +any dependencies other than the `Python Standard Library`_ and `Numpy`_ +/ `Scipy`_. + +.. _Python Standard Library: http://docs.python.org/library/ +.. _Numpy: http://numpy.scipy.org/ +.. _Scipy: http://www.scipy.org/ + +To make a responsive user interface in parallel with data processing +and possible GUIs, we'll use Python's multiprocessing_ and queue_ +modules. These modules are both new in Python 2.6, but 2.6 usage is +becoming fairly widespread. Certainly more widespread than any +alternative queue module that I know of. Since we're leveraging the +power of the standard library, we use configparser_ for the config +files. + +.. _multiprocessing: http://docs.python.org/dev/library/multiprocessing.html +.. _queue: http://docs.python.org/library/queue.html +.. _configparser: http://docs.python.org/library/configparser.html + +On the testing side, the need to stick to the standard library relaxes +(developers can install extra packages), so we can use nose_. See +the `Testing`_ section for more information. + +.. _Testing: testing.txt diff --git a/doc/index.txt b/doc/index.txt new file mode 100644 index 0000000..cb6b97b --- /dev/null +++ b/doc/index.txt @@ -0,0 +1,78 @@ +Welcome to the Hooke documentation! +=================================== + +Hooke_ is software for the (semiautomatic) analysis and filtering of +force curves. Force curves are the output of an analytical technique +called `force spectroscopy`_. Force spectroscopy experiments usually +require the analysis of thousands of force curves at a time. As of +today, there is no standard, free software for the analysis of force +curves. Hooke aims to solve that. + +.. _Hooke: http://code.google.com/p/hooke/ +.. _force spectroscopy: http://en.wikipedia.org/wiki/Force_spectroscopy + +Features: + +* View, annotate, and measure force curves +* Worm-like chain and freely-jointed chain fits of force peaks +* Automatic convolution-based filtering of empty curves +* Automatic fit and measurement of multiple force peaks +* Handles force-clamp force experiments (experimental) +* Extensible by users by mean of plugins and drivers + +Contents: + +.. toctree:: + :maxdepth: 2 + + install.txt + tutorial.txt + hacking.txt + hooke/hooke.txt + doc.txt + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + +Publications +============ + +Hooke has been published [1]. Please cite Hooke if you use it. Let us +know, and we'll link to your paper! + + +[1] Sandal M, Benedetti F, Brucale M, Gomez-Casado A, Samorì B., + "Hooke: an open software platform for force spectroscopy." + Bioinformatics, 2009. + doi: `10.1093/bioinformatics/btp180 `_ + + +Troubleshooting +=============== + +If you have troubles in using Hooke: + +1. Search the `issue tracker`_. +2. Look at the `trouble shooting wiki page`_. +3. Search the `discussion group`_. +3. Ask a question in the discussion group. +4. File a bug on the issue tracker. + +.. _issue tracker: http://code.google.com/p/hooke/issues/list +.. _trouble shooting wiki page: + http://code.google.com/p/hooke/wiki/TroubleShooting +.. _discussion group: http://groups.google.com/group/hookesoftware + + +Disclaimer +========== + +Remember that Hooke is still experimental software! It has been mostly +done to fit the needs of its authors, and there is no guarantee it +will do what you need. However, you can write us/help us improve it so +that it does. We aim to make of Hooke a little, universal tool that +can help your research. diff --git a/doc/install.txt b/doc/install.txt new file mode 100644 index 0000000..a8b754c --- /dev/null +++ b/doc/install.txt @@ -0,0 +1,77 @@ +**************** +Installing Hooke +**************** + +Dependencies +============ + +Hooke is routinely run successfully on Windows and Linux based +systems. It is possible to run it happily on Mac OS X too (though +install can be a bit trickier). Technically, Hooke should run wherever +the Python_ programming language is installed. + +You'll need the following Python modules: + +* Numpy_ +* Scipy_ +* Matplotlib_ (for generating plots) +* wxPython_ (for the GUI) + +.. Numpy: http://numpy.scipy.org/ +.. Scipy: http://www.scipy.org/ +.. Python: http://www.python.org/ +.. Matplotlib: http://matplotlib.sourceforge.net/ +.. wxPython: http://www.wxpython.org/ + +Getting the source +================== + +Hooke_ is available as an Subversion_ repository:: + + $ svn checkout http://hooke.googlecode.com/svn/trunk/ hooke + +There is also a `GUI fork`_ (Rolf Schmidt):: + + $ svn checkout http://hooke.googlecode.com/svn/trunk/ hooke + +And a `fork`_ versioned in Git_ (W. Trevor King):: + + $ git clone http://www.physics.drexel.edu/~wking/code/git/hooke.git/ hooke + +There are also periodic bundled releases. See the homepage for each +fork for details. For example, get the most recent snapshot of +Trevor's fork in zip form with:: + + $ wget -o hooke.zip http://www.physics.drexel.edu/~wking/code/git/git.php?p=hooke.git&dl=zip&h=HEAD + $ unzip hooke.zip + +.. _Hooke: http://code.google.com/p/hooke/ +.. _GUI fork: http://code.google.com/p/hooke/wiki/HookeGUI +.. _fork: http://www.physics.drexel.edu/~wking/code/git/git.php?p=hooke.git +.. _Subversion: http://subversion.tigris.org/ +.. _Git: http://git-scm.com/ + + +Installation +============ + +Run:: + + $ python setup.py install + +to install Hooke. Run:: + + $ python setup.py install --help + +to see a list of installation options you may want to configure. + +Running Hooke from the source directory +======================================= + +If you like, you can avoid installation by running Hooke directly from +it's source directory:: + + $ wget -o hooke.zip http://www.physics.drexel.edu/~wking/code/git/git.php?p=hooke.git&dl=zip&h=HEAD + $ unzip hooke.zip + $ cd hooke + $ python bin/hooke diff --git a/doc/testing.txt b/doc/testing.txt new file mode 100644 index 0000000..1f5ded4 --- /dev/null +++ b/doc/testing.txt @@ -0,0 +1,25 @@ +************* +Testing Hooke +************* + +Hooke's test framework is build using doctest_, unittest_, and nose_. +``nosetests`` (from the ``nose`` package) scans through the source +tree, searching out the various tests and running them. If you aren't +familiar with ``nose``, there is excellent documentation on its home +page. We use ``nose`` because its auto-discovery allows us to avoid +collecting all of our assorted tests into ``unittest.TestSuite``\s and +running them by hand. + +To run the test suite from the Hooke installation directory, just use:: + + nosetests --with-doctest --doctest-tests + +.. _doctest: http://docs.python.org/library/doctest.html +.. _unittest: http://docs.python.org/library/unittest.html +.. _nose: http://somethingaboutorange.com/mrl/projects/nose/0.11.3/ + + +Adding tests to modules +----------------------- + +Just go crazy with doctests and unittests; ``nose`` will find them. diff --git a/doc/tutorial.txt b/doc/tutorial.txt new file mode 100644 index 0000000..1158aad --- /dev/null +++ b/doc/tutorial.txt @@ -0,0 +1,294 @@ +******** +Tutorial +******** + +`A short video showing Hooke in action`_! (courtesy of Fabrizio +Benedetti, EPFL, Lausanne) + +.. _A short video showing Hooke in action: + https://documents.epfl.ch/users/f/fb/fbenedet/www/hooke_short_demostration.ogv + +Introduction +============ + +This tutorial will focus on the command-line interface as the most +powerful, and leave the GUI interface to another document. + +.. _command-line: `Command-line interface`_ +.. _GUI: gui.txt + +Installation +============ + +See the Installation_ page for details on downloading and installing +Hooke. + +.. _Installation: install.txt + + +Command-line interface +====================== + +Running the hooke shell +----------------------- + +Hooke has a set of commands that depend on the loaded plugins_. +To access these commands, you'll need to run the Hooke shell.:: + + $ hooke + +If you are running hooke from the source directory (see +Installation_), the equivalent command is:: + + $ python bin/hooke + +You may need to give the full path for Python on Windows systems. + +As Hooke launches, you should see something like the following in your +terminal:: + + Starting Hooke. + Imported plugin fit + Imported plugin procplots + Imported plugin flatfilts + Imported plugin generalclamp + Imported plugin generalvclamp + Imported plugin massanalysis + Imported plugin macro + Imported driver picoforce + Imported driver hemingclamp + Imported driver csvdriver + Imported driver tutorialdriver + + Warning: Invalid work directory. + This is Hooke, version 0.8.0 Seinei + (c) Massimo Sandal, 2006. Released under the GNU General Public License Version 2 + Hooke is Free software. + ---- + hooke> + +The final line, ``hooke>``, is the Hooke prompt. It allows you to +enter commans to interact with the interpreter. + +Help +---- + +All commands have help text explaining their purpose and usage. The +text is stored in the code itself, and therefore more likely to be up +to date than this tutorial. You can get a list of commands and topics +with:: + + hooke> help + +Or see specific help on ``TOPIC`` with:: + + hooke> help TOPIC + +for example:: + + hooke> help current + +will give help on the ``current`` command. + +Creating a playlist +------------------- + +To start analyzing your curves, you first have to build a playlist. The +playlist is just an index of the force curve files you want to +analyze. Imagine it as a music playlist (that’s why it is called a +playlist), but with data files instead of audio files. + +Suppose you have 100 PicoForce curve files in your curves directory, +starting from ``mycurve.000`` and ending in ``mycurve.100`` and you +want to analyze them all. + +You then can ``cd`` (change directory) to the directory:: + + hooke> cd c:\curves + +Type ``pwd`` (print working directory) to check the directory is correct.:: + + hooke> pwd + c:\curves + +You can list the files in the directory using ``ls`` or ``dir`` (they’re +synonyms).:: + + hooke> ls + [’mycurve.000’, ’mycurve.001’, ... + ] + +Now you are ready to generate the playlist. The command to use is +``genlist``.:: + + hooke> genlist mycurve.* + +You can also generate a playlist containing all what you find in the +directory by typing: + + hooke> genlist c:\curves + +If you want to select what curves to see, based on the filename, you +can use wildcards. For example:: + + hooke> genlist mycurve.05* + +will take only curves from mycurve.050 to mycurve.059. + +Note that by using ``genlist`` you just generate the playlist in the +local session. To save your playlist to a file for future reuse, +type:: + + hooke> savelist mylist + +In this example, the list will be saved in the file ``mylist.hkp``. +Hooke will add the extension ``.hkp`` (Hooke playlist) to the playlist +if you forget to. The ``.hkp`` file is an XML file you can read and +edit with any text editor (i.e. Wordpad), if needed. If you want to +load it, simply issue ``loadlist mylist.hkp`` or ``loadlist mylist``, +Hooke will add ``.hkp`` if necessary. + +If, generating the playlist, you are including by chance a non-force +curve file that Hooke cannot open, Hooke will print an error and +continue on. + +Navigating the playlist +----------------------- + +Now you can navigate through your playlist using the commands ``next`` +and ``previous`` or, their aliases ``n`` and ``p``. You don’t need to +type ``n`` every time to run along a list of curves. If you press +Return to an empty prompt, Hooke will repeat the last command you +issued explicitly. You can also navigate through the command history +by using the up and down arrows. From the last curve of your +playlist, ``n`` will wrap around to the first curve. Analogously, +issuing ``p`` at the first curve will jump to the last. + +You can also jump to a given curve:: + + hooke> jump c:\curves\mycurve.012 + +where the path can be either an absolute path, or a path relative to +the directory holding the playlist file. + +Taking notes +------------ + +You can take notes about the curves you are looking at. Just type +``note`` followed by the text you want to append to that curve. Hooke +will save the text in your current playlist and in an external log +file. The output will look like this: + +Notes taken at Sun Sep 17 20:42:07 2006 +/home/cyclopia/work/tris/20060620a.041 | This is a note +/home/cyclopia/work/tris/20060620a.207 | This is another note +/home/cyclopia/work/tris/20060620a.286 | This is a third one + +The log file name can be configured_, but it defaults to hooke.log. + +.. _configured: config.txt + +Usually curves you annotated are useful later. You can copy the curves +you annotated to a different directory by using the ``copylog`` +command. + + hooke> copylog c:\nicecurves + +will copy all curves you have annotated to the c:\nicecurves +directory. Make sure that the directory already exists before doing +that. TODO: replace with:: + + hooke> copylist --log c:\curves\nice.hkp + +Exporting curves +---------------- + +You can export Hooke curves as images and as text columns. To export +as images, issue the ``export`` command followed by the filename. +Supported formats are PNG (raster) and EPS (Encapsulated Postscript, +vectorial). The export format is determined by the filename +extension, so ``export foo.png`` and ``export foo.eps`` will save +PNG and EPS files respectively. + +To export as text, use the ``txt`` command, followed by the +filename. The output is a text file containing columns (first two are +X and Y of extension, second two are X and Y of retraction). + +TODO: multiple cycles? Solution: blank lines for "breaks", add option +to extract specific sections using Python's slice notation. + + +Interacting with the plot (no plots in command line mode...) +------------------------- + +Measuring distances and forces +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can easily zoom in the plot by dragging a rectangle on it with the +left mouse button. To zoom out, click the right mouse +button. Sometimes by zooming in and out too much, you can lose the +picture (this is probably a small bug in Matplotlib). Just type +``plot`` at the command line and the curve will be refreshed. + +You can measure distances and forces directly in the plot. Just issue +the command ``distance``. You will be asked to click two points. +When you click a point, a blue dot should appear. When you click the +second point, the distances (in nanometers and piconewtons) will +appear on the command line. You can use ``delta`` if you prefer, +which gives meaningful values for every kind of graph (not only force +curves). If you want to know the coordinates of a single point, use +``point``. + +Hooke automatically adjusts the position of the clicked point to the +nearest point in the graph, so you will be always measuring distances +and forces between points in the graph. + +The commands ``force`` and ``distance`` are present in the +``generalvclamp`` plugin. + +Worm like chain and freely jointed chain fitting +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can measure by hand the parameters relative to a force peak using +a worm-like chain fitting with the ``fit`` command. The command by +default automatically finds the contact point, asks for two points +delimiting the portion to fit, and performs a two-variable fit, with +contour length, persistence length, and their relative errors as +output. If desired, one can use the ``noauto`` option to manually +click the contact point, and/or the ``pl=NUMBER`` options to impose a +specific persistence or Kuhn length (in nanometers). You can choose +which model to use with ``set fit_function wlc`` or ``set fit_function +fjc``. See the help of the ``fit`` command from the Hooke +command line for details. + +Multiple curve fitting and measuring +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can cycle through all your current playlist obtaining WLC fit, FJC +fit, rupture force and slope (loading rate) information from each +curve using the ``multifit`` command. The collected data can be saved +in a text file for further analysis in your favourite spreadsheet or +statistical program. If you want to check your parameters on the +current curve before fitting all the files in your playlist, use +``multifit justone``. See the ``multifit`` help for more options. + +Fast curve reviewing and saving +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When automatic routines are not good enough to filter your data, use +``review`` command to cycle through your playlist presenting ten +curves in the same graph. You can then enter the numbers of the +interesting curves and automatically save a copy of them into another +directory. + +Configuring Hooke +----------------- + +You can set environment variables to influence the behaviour of +Hooke. The command to use is ``set``. + +You can alter permanently the behaviour of Hooke by setting these +variables in a Hooke configuration file. See the `Configuring Hooke`_ +section for details. + +.. _Configuring Hooke: config.txt diff --git a/hooke/__init__.py b/hooke/__init__.py index e69de29..694887e 100644 --- a/hooke/__init__.py +++ b/hooke/__init__.py @@ -0,0 +1,75 @@ +"""The hooke module does all the legwork for Hooke_ (BE). + +.. _hooke: http://code.google.com/p/hooke/ + +To facilitate faster loading, submodules are not imported by default. +The available submodules are: + +* :mod:`hooke.config` +* :mod:`hooke.compat` +""" + +__version__ = (0, 9, 0, 'devel', None, 'Kenzo') +"""Version tuple:: + + (major, minor, release, type, patch, name) + +Where + + * type: Python uses alpha, beta, candidate, and final. Whatever + so long as the alphabetic sort gets them in the right order. + * patch: either manually incremented for each release, the packaging + date string, YYYYMMDD, date of last commit, whatever. + +See `Greg Noel's post on scons-devel`_ for a good explaination of why this +versioning scheme is a good idea. + +.. _Greg Noel's post on scons-devel + http://thread.gmane.org/gmane.comp.programming.tools.scons.devel/8740 +""" + +def version(depth=-1, version_tuple=None): + """Return a nicely formatted version string.:: + + major.minor.release.type[.patch] (name) + + Examples + -------- + + Since I seem to be unable to override __version__ in a Doctest, + we'll pass the version tuple in as an argument. You can ignore + ``version_tuple``. + + >>> v = (1, 2, 3, 'devel', '20100501', 'Kenzo') + + If depth -1, a full version string is returned + + >>> version(depth=-1, version_tuple=v) + '1.2.3.devel.20100501 (Kenzo)' + + Otherwise, only the first depth fields are used. + + >>> version(depth=3, version_tuple=v) + '1.2.3' + >>> version(depth=4, version_tuple=v) + '1.2.3.devel' + + Here's an example dropping the patch. + + >>> v = (1, 2, 3, 'devel', None, 'Kenzo') + >>> version(depth=-1, version_tuple=v) + '1.2.3.devel (Kenzo)' + """ + if version_tuple == None: + version_tuple = __version__ + patch_index = 4 + if version_tuple[patch_index] == None: # No patch field, drop that entry + version_tuple = version_tuple[0:patch_index] \ + + version_tuple[patch_index+1:] + if depth >= patch_index: + depth -= 1 + fields = version_tuple[0:depth] + string = '.'.join([str(x) for x in fields]) + if depth == -1 or depth == len(version_tuple): + string += ' (%s)' % version_tuple[-1] + return string -- 2.26.2