Browse Source

Improved documentation.

Jonas Borgström 12 years ago
parent
commit
f827b99ffe
11 changed files with 260 additions and 58 deletions
  1. 0 0
      LICENSE
  2. 19 14
      README.rst
  3. 64 37
      darc/archiver.py
  4. 2 1
      docs/Makefile
  5. 11 0
      docs/detailedusage.rst
  6. 2 1
      docs/generalusage.rst
  7. 3 2
      docs/index.rst
  8. 2 2
      docs/terminology.rst
  9. 6 0
      docs/update_usage.sh
  10. 147 0
      docs/usage.rst
  11. 4 1
      setup.py

+ 0 - 0
COPYING → LICENSE


+ 19 - 14
README → README.rst

@@ -1,14 +1,19 @@
-.. -*- restructuredtext -*-
-
-
 What is darc?
-=============
+-------------
 Darc is a Deduplicating ARChiver written in Python. The main goal of darc is
 to provide an efficient and secure way to backup data. The data deduplication
-technique used makes darc suitable for taking daily backups.
+technique used makes darc suitable for daily backups since only actual changes
+are stored.
+
+Easy to use
+~~~~~~~~~~~
+Initialze backup repository and create a backup archive::
+
+    $ darc init /usbdrive/my-backup.darc
+    $ darc create -v /usbdrive/my-backup.darc::documents ~/Documents
 
 Main features
--------------
+~~~~~~~~~~~~~
 Space efficient storage
   Variable block size deduplication is used to reduce the number of bytes 
   stored by detecting redundant data. Each file is split into a number of
@@ -24,28 +29,28 @@ Off-site backups
     darc is installed.
 
 What do I need?
-===============
+---------------
 Darc requires Python 3.2 or above to work. Besides Python darc also requires 
 msgpack-python and sufficiently recent OpenSSL (>= 1.0.0).
 
 How do I install it?
-====================
+--------------------
 ::
   $ python setup.py install
 
 Where are the docs?
-===================
-Go to http://pythonhosted.org/darc/ for a prebuilt version of the docs. You
+-------------------
+Go to https://pythonhosted.org/darc/ for a prebuilt version of the docs. You
 can also build them yourself form the docs folder.
 
 Where are the tests?
-====================
+--------------------
 The tests are in the darc/testsuite package. To run the test suite use the
 following command::
 
   $ python -m darc.testsuite.run
 
 Where can I get help?
-=====================
-
-Send wishes, comments, patches, etc. to jonas@borgstrom.se
+---------------------
+Send questions, comments, patches, etc. to jonas@borgstrom.se. Issues and
+pull requests can also be created on https://github.com/jborg/darc

+ 64 - 37
darc/archiver.py

@@ -46,6 +46,8 @@ class Archiver:
         return RepositoryServer().serve()
 
     def do_init(self, args):
+        """Initialize a new repository
+        """
         print('Initializing repository at "%s"' % args.repository.orig)
         repository = self.open_repository(args.repository, create=True)
         key = key_creator(repository, args)
@@ -57,12 +59,16 @@ class Archiver:
         return self.exit_code
 
     def do_change_passphrase(self, args):
+        """Change passphrase on repository key file
+        """
         repository = self.open_repository(Location(args.repository))
         manifest, key = Manifest.load(repository)
         key.change_passphrase()
         return self.exit_code
 
     def do_create(self, args):
+        """Create new archive
+        """
         t0 = datetime.now()
         repository = self.open_repository(args.archive)
         manifest, key = Manifest.load(repository)
@@ -150,6 +156,8 @@ class Archiver:
             self.print_error('Unknown file type: %s', path)
 
     def do_extract(self, args):
+        """Extract archive contents
+        """
         repository = self.open_repository(args.archive)
         manifest, key = Manifest.load(repository)
         archive = Archive(repository, key, manifest, args.archive.archive,
@@ -174,6 +182,8 @@ class Archiver:
         return self.exit_code
 
     def do_delete(self, args):
+        """Delete archive
+        """
         repository = self.open_repository(args.archive)
         manifest, key = Manifest.load(repository)
         cache = Cache(repository, key, manifest)
@@ -182,6 +192,8 @@ class Archiver:
         return self.exit_code
 
     def do_list(self, args):
+        """List archive or repository contents
+        """
         repository = self.open_repository(args.src)
         manifest, key = Manifest.load(repository)
         if args.src.archive:
@@ -214,6 +226,8 @@ class Archiver:
         return self.exit_code
 
     def do_verify(self, args):
+        """Verify archive consistency
+        """
         repository = self.open_repository(args.archive)
         manifest, key = Manifest.load(repository)
         archive = Archive(repository, key, manifest, args.archive.archive)
@@ -234,6 +248,8 @@ class Archiver:
         return self.exit_code
 
     def do_info(self, args):
+        """Show archive details such as disk space used
+        """
         repository = self.open_repository(args.archive)
         manifest, key = Manifest.load(repository)
         cache = Cache(repository, key, manifest)
@@ -249,6 +265,8 @@ class Archiver:
         return self.exit_code
 
     def do_prune(self, args):
+        """Prune repository archives according to specified rules
+        """
         repository = self.open_repository(args.repository)
         manifest, key = Manifest.load(repository)
         cache = Cache(repository, key, manifest)
@@ -294,7 +312,7 @@ class Archiver:
         common_parser = argparse.ArgumentParser(add_help=False)
         common_parser.add_argument('-v', '--verbose', dest='verbose', action='store_true',
                             default=False,
-                            help='Verbose output')
+                            help='verbose output')
 
         parser = argparse.ArgumentParser(description='Darc - Deduplicating Archiver')
         subparsers = parser.add_subparsers(title='Available subcommands')
@@ -302,104 +320,113 @@ class Archiver:
         subparser = subparsers.add_parser('serve', parents=[common_parser])
         subparser.set_defaults(func=self.do_serve)
 
-        subparser = subparsers.add_parser('init', parents=[common_parser])
+        subparser = subparsers.add_parser('init', parents=[common_parser],
+                                          description=self.do_init.__doc__)
         subparser.set_defaults(func=self.do_init)
         subparser.add_argument('repository',
                                type=location_validator(archive=False),
-                               help='Repository to create')
+                               help='repository to create')
         subparser.add_argument('--key-file', dest='keyfile',
                                action='store_true', default=False,
-                               help='Encrypt data using key file')
+                               help='enable key file based encryption')
         subparser.add_argument('--passphrase', dest='passphrase',
                                action='store_true', default=False,
-                               help='Encrypt data using passphrase derived keys')
+                               help='enable passphrase based encryption')
 
-        subparser = subparsers.add_parser('change-passphrase', parents=[common_parser])
+        subparser = subparsers.add_parser('change-passphrase', parents=[common_parser],
+                                          description=self.do_change_passphrase.__doc__)
         subparser.set_defaults(func=self.do_change_passphrase)
         subparser.add_argument('repository', type=location_validator(archive=False))
 
-        subparser = subparsers.add_parser('create', parents=[common_parser])
+        subparser = subparsers.add_parser('create', parents=[common_parser],
+                                          description=self.do_create.__doc__)
         subparser.set_defaults(func=self.do_create)
         subparser.add_argument('-s', '--stats', dest='stats',
                                action='store_true', default=False,
-                               help='Print statistics for the created archive')
+                               help='print statistics for the created archive')
         subparser.add_argument('-e', '--exclude', dest='excludes',
                                type=ExcludePattern, action='append',
-                               help='Exclude condition')
+                               metavar="PATTERN", help='exclude paths matching PATTERN')
         subparser.add_argument('-c', '--checkpoint-interval', dest='checkpoint_interval',
                                type=int, default=300, metavar='SECONDS',
-                               help='Write checkpointe ever SECONDS seconds (Default: 300)')
+                               help='write checkpointe ever SECONDS seconds (Default: 300)')
         subparser.add_argument('--do-not-cross-mountpoints', dest='dontcross',
                                action='store_true', default=False,
-                               help='Do not cross mount points')
+                               help='do not cross mount points')
         subparser.add_argument('--numeric-owner', dest='numeric_owner',
                                action='store_true', default=False,
-                               help='Only store numeric user and group identifiers')
+                               help='only store numeric user and group identifiers')
         subparser.add_argument('archive', metavar='ARCHIVE',
                                type=location_validator(archive=True),
-                               help='Archive to create')
+                               help='archive to create')
         subparser.add_argument('paths', metavar='PATH', nargs='+', type=str,
-                               help='Paths to archive')
+                               help='paths to archive')
 
-        subparser = subparsers.add_parser('extract', parents=[common_parser])
+        subparser = subparsers.add_parser('extract', parents=[common_parser],
+                                          description=self.do_extract.__doc__)
         subparser.set_defaults(func=self.do_extract)
         subparser.add_argument('-e', '--exclude', dest='excludes',
                                type=ExcludePattern, action='append',
-                               help='Exclude condition')
+                               metavar="PATTERN", help='exclude paths matching PATTERN')
         subparser.add_argument('--numeric-owner', dest='numeric_owner',
                                action='store_true', default=False,
-                               help='Only obey numeric user and group identifiers')
+                               help='only obey numeric user and group identifiers')
         subparser.add_argument('archive', metavar='ARCHIVE',
                                type=location_validator(archive=True),
-                               help='Archive to extract')
+                               help='archive to extract')
         subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
-                               help='Paths to extract')
+                               help='paths to extract')
 
-        subparser = subparsers.add_parser('delete', parents=[common_parser])
+        subparser = subparsers.add_parser('delete', parents=[common_parser],
+                                          description=self.do_delete.__doc__)
         subparser.set_defaults(func=self.do_delete)
         subparser.add_argument('archive', metavar='ARCHIVE',
                                type=location_validator(archive=True),
-                               help='Archive to delete')
+                               help='archive to delete')
 
-        subparser = subparsers.add_parser('list', parents=[common_parser])
+        subparser = subparsers.add_parser('list', parents=[common_parser],
+                                          description=self.do_list.__doc__)
         subparser.set_defaults(func=self.do_list)
         subparser.add_argument('src', metavar='SRC', type=location_validator(),
-                               help='Repository/Archive to list contents of')
+                               help='repository/Archive to list contents of')
 
-        subparser = subparsers.add_parser('verify', parents=[common_parser])
+        subparser = subparsers.add_parser('verify', parents=[common_parser],
+                                          description=self.do_verify.__doc__)
         subparser.set_defaults(func=self.do_verify)
         subparser.add_argument('-e', '--exclude', dest='excludes',
                                type=ExcludePattern, action='append',
-                               help='Include condition')
+                               metavar="PATTERN", help='exclude paths matching PATTERN')
         subparser.add_argument('archive', metavar='ARCHIVE',
                                type=location_validator(archive=True),
-                               help='Archive to verity integrity of')
+                               help='archive to verity integrity of')
         subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
-                               help='Paths to verify')
+                               help='paths to verify')
 
-        subparser = subparsers.add_parser('info', parents=[common_parser])
+        subparser = subparsers.add_parser('info', parents=[common_parser],
+                                          description=self.do_info.__doc__)
         subparser.set_defaults(func=self.do_info)
         subparser.add_argument('archive', metavar='ARCHIVE',
                                type=location_validator(archive=True),
-                               help='Archive to display information about')
+                               help='archive to display information about')
 
-        subparser = subparsers.add_parser('prune', parents=[common_parser])
+        subparser = subparsers.add_parser('prune', parents=[common_parser],
+                                          description=self.do_prune.__doc__)
         subparser.set_defaults(func=self.do_prune)
         subparser.add_argument('-H', '--hourly', dest='hourly', type=int, default=0,
-                               help='Number of hourly archives to keep')
+                               help='number of hourly archives to keep')
         subparser.add_argument('-d', '--daily', dest='daily', type=int, default=0,
-                               help='Number of daily archives to keep')
+                               help='number of daily archives to keep')
         subparser.add_argument('-w', '--weekly', dest='weekly', type=int, default=0,
-                               help='Number of daily archives to keep')
+                               help='number of daily archives to keep')
         subparser.add_argument('-m', '--monthly', dest='monthly', type=int, default=0,
-                               help='Number of monthly archives to keep')
+                               help='number of monthly archives to keep')
         subparser.add_argument('-y', '--yearly', dest='yearly', type=int, default=0,
-                               help='Number of yearly archives to keep')
+                               help='number of yearly archives to keep')
         subparser.add_argument('-p', '--prefix', dest='prefix', type=str,
-                               help='Only consider archive names starting with this prefix')
+                               help='only consider archive names starting with this prefix')
         subparser.add_argument('repository', metavar='REPOSITORY',
                                type=location_validator(archive=False),
-                               help='Repository to prune')
+                               help='repository to prune')
         args = parser.parse_args(args or ['-h'])
         self.verbose = args.verbose
         return args.func(args)

+ 2 - 1
docs/Makefile

@@ -37,6 +37,7 @@ clean:
 	-rm -rf $(BUILDDIR)/*
 
 html:
+	./update_usage.sh
 	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
 	@echo
 	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
@@ -138,4 +139,4 @@ gh-pages: html
 	rm -rf $$GH_PAGES_CLONE
 
 inotify:
-	while inotifywait -r .; do make html ; done
+	while inotifywait -r . --exclude usage.rst --exclude '_build/*' ; do make html ; done

+ 11 - 0
docs/detailedusage.rst

@@ -0,0 +1,11 @@
+.. include:: global.rst.inc
+.. _detailed_usage:
+
+Detailed Usage
+--------------
+
+|project_name| consists of a number of subcommands. Each subcommand accepts
+a number of arguments and options. The following sections will describe each
+subcommand in detail.
+
+.. include:: usage.rst

+ 2 - 1
docs/generalusage.rst

@@ -5,7 +5,8 @@ General Usage
 =============
 
 The following examples showcases how to use |project_name| to backup some
-important files from a users home directory.
+important files from a users home directory (for more detailed information
+about each subcommand see the :ref:`detailed_usage` section).
 
 Initialize a local :ref:`repository <repository_def>` to store backup
 :ref:`archives <archive_def>` in (See :ref:`encrypted_repos` and

+ 3 - 2
docs/index.rst

@@ -5,7 +5,7 @@ Darc
 |project_name| is a Deduplicating ARChiver written in Python.
 The main goal of |project_name| is to provide an efficient and secure way
 to backup data. The data deduplication technique used makes |project_name|
-suitable for taking daily backups.
+suitable for daily backups since only actual changes are stored.
 
 Main Features
 -------------
@@ -49,8 +49,9 @@ User's Guide
 
    installation
    generalusage
+   detailedusage
    faq
-   definitions
+   terminology
 
 Contribute
 ==========

+ 2 - 2
docs/definitions.rst → docs/terminology.rst

@@ -1,7 +1,7 @@
-.. _definitions:
+.. _terminology:
 .. include:: global.rst.inc
 
-Definitions
+Terminology
 ===========
 
 .. _deduplication_def:

+ 6 - 0
docs/update_usage.sh

@@ -0,0 +1,6 @@
+#!/usr/bin/bash
+echo -n > usage.rst 
+for cmd in init create extract delete prune verify change-passphrase; do
+  echo -e ".. _usage_darc_$cmd:\n\ndarc $cmd\n~~~~~~\n::\n" >> usage.rst
+  darc $cmd -h >> usage.rst
+done

+ 147 - 0
docs/usage.rst

@@ -0,0 +1,147 @@
+.. _usage_darc_init:
+
+darc init
+~~~~~~
+::
+
+usage: darc init [-h] [-v] [--key-file] [--passphrase] repository
+
+Initialize a new repository
+
+positional arguments:
+  repository     repository to create
+
+optional arguments:
+  -h, --help     show this help message and exit
+  -v, --verbose  verbose output
+  --key-file     enable key file based encryption
+  --passphrase   enable passphrase based encryption
+.. _usage_darc_create:
+
+darc create
+~~~~~~
+::
+
+usage: darc create [-h] [-v] [-s] [-e PATTERN] [-c SECONDS]
+                   [--do-not-cross-mountpoints] [--numeric-owner]
+                   ARCHIVE PATH [PATH ...]
+
+Create new archive
+
+positional arguments:
+  ARCHIVE               archive to create
+  PATH                  paths to archive
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -v, --verbose         verbose output
+  -s, --stats           print statistics for the created archive
+  -e PATTERN, --exclude PATTERN
+                        exclude paths matching PATTERN
+  -c SECONDS, --checkpoint-interval SECONDS
+                        write checkpointe ever SECONDS seconds (Default: 300)
+  --do-not-cross-mountpoints
+                        do not cross mount points
+  --numeric-owner       only store numeric user and group identifiers
+.. _usage_darc_extract:
+
+darc extract
+~~~~~~
+::
+
+usage: darc extract [-h] [-v] [-e PATTERN] [--numeric-owner]
+                    ARCHIVE [PATH [PATH ...]]
+
+Extract archive contents
+
+positional arguments:
+  ARCHIVE               archive to extract
+  PATH                  paths to extract
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -v, --verbose         verbose output
+  -e PATTERN, --exclude PATTERN
+                        exclude paths matching PATTERN
+  --numeric-owner       only obey numeric user and group identifiers
+.. _usage_darc_delete:
+
+darc delete
+~~~~~~
+::
+
+usage: darc delete [-h] [-v] ARCHIVE
+
+Delete archive
+
+positional arguments:
+  ARCHIVE        archive to delete
+
+optional arguments:
+  -h, --help     show this help message and exit
+  -v, --verbose  verbose output
+.. _usage_darc_prune:
+
+darc prune
+~~~~~~
+::
+
+usage: darc prune [-h] [-v] [-H HOURLY] [-d DAILY] [-w WEEKLY] [-m MONTHLY]
+                  [-y YEARLY] [-p PREFIX]
+                  REPOSITORY
+
+Prune repository archives according to specified rules
+
+positional arguments:
+  REPOSITORY            repository to prune
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -v, --verbose         verbose output
+  -H HOURLY, --hourly HOURLY
+                        number of hourly archives to keep
+  -d DAILY, --daily DAILY
+                        number of daily archives to keep
+  -w WEEKLY, --weekly WEEKLY
+                        number of daily archives to keep
+  -m MONTHLY, --monthly MONTHLY
+                        number of monthly archives to keep
+  -y YEARLY, --yearly YEARLY
+                        number of yearly archives to keep
+  -p PREFIX, --prefix PREFIX
+                        only consider archive names starting with this prefix
+.. _usage_darc_verify:
+
+darc verify
+~~~~~~
+::
+
+usage: darc verify [-h] [-v] [-e PATTERN] ARCHIVE [PATH [PATH ...]]
+
+Verify archive consistency
+
+positional arguments:
+  ARCHIVE               archive to verity integrity of
+  PATH                  paths to verify
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -v, --verbose         verbose output
+  -e PATTERN, --exclude PATTERN
+                        exclude paths matching PATTERN
+.. _usage_darc_change-passphrase:
+
+darc change-passphrase
+~~~~~~
+::
+
+usage: darc change-passphrase [-h] [-v] repository
+
+Change passphrase on repository key file
+
+positional arguments:
+  repository
+
+optional arguments:
+  -h, --help     show this help message and exit
+  -v, --verbose  verbose output

+ 4 - 1
setup.py

@@ -1,5 +1,4 @@
 # -*- encoding: utf-8 *-*
-#!/usr/bin/env python
 import os
 import sys
 from glob import glob
@@ -45,6 +44,9 @@ except ImportError:
     if not os.path.exists(chunker_source) or not os.path.exists(hashindex_source):
         raise ImportError('The GIT version of darc needs Cython. Install Cython or use a released version')
 
+with open('README.rst', 'r') as fd:
+    long_description = fd.read()
+
 setup(
     name='darc',
     version=darc.__release__,
@@ -52,6 +54,7 @@ setup(
     author_email='jonas@borgstrom.se',
     url='http://github.com/jborg/darc/',
     description='Deduplicating ARChiver written in Python',
+    long_description=long_description,
     license='BSD',
     platforms=['Linux', 'MacOS X'],
     classifiers=[