Add missing docstrings
authorNikos Skalkotos <skalkoto@grnet.gr>
Thu, 28 Mar 2013 12:26:27 +0000 (14:26 +0200)
committerNikos Skalkotos <skalkoto@grnet.gr>
Thu, 28 Mar 2013 12:26:27 +0000 (14:26 +0200)
23 files changed:
image_creator/bundle_volume.py
image_creator/dialog_main.py
image_creator/dialog_menu.py
image_creator/dialog_util.py
image_creator/dialog_wizard.py
image_creator/disk.py
image_creator/gpt.py
image_creator/image.py
image_creator/kamaki_wrapper.py
image_creator/os_type/__init__.py
image_creator/os_type/freebsd.py
image_creator/os_type/hurd.py
image_creator/os_type/linux.py
image_creator/os_type/netbsd.py
image_creator/os_type/slackware.py
image_creator/os_type/ubuntu.py
image_creator/os_type/unix.py
image_creator/os_type/windows.py
image_creator/output/__init__.py
image_creator/output/cli.py
image_creator/output/composite.py
image_creator/output/dialog.py
image_creator/util.py

index aca14b3..c4cc83c 100644 (file)
@@ -92,6 +92,7 @@ class BundleVolume(object):
         self.disk = parted.Disk(device)
 
     def _read_fstable(self, f):
+        """Use this generator to iterate over the lines of and fstab file"""
 
         if not os.path.isfile(f):
             raise FatalError("Unable to open: `%s'. File is missing." % f)
@@ -106,6 +107,7 @@ class BundleVolume(object):
                 yield FileSystemTableEntry(*entry)
 
     def _get_root_partition(self):
+        """Return the fstab entry accosiated with the root filesystem"""
         for entry in self._read_fstable('/etc/fstab'):
             if entry.mpoint == '/':
                 return entry.dev
@@ -113,12 +115,14 @@ class BundleVolume(object):
         raise FatalError("Unable to find root device in /etc/fstab")
 
     def _is_mpoint(self, path):
+        """Check if a directory is currently a mount point"""
         for entry in self._read_fstable('/proc/mounts'):
             if entry.mpoint == path:
                 return True
         return False
 
     def _get_mount_options(self, device):
+        """Return the mount entry associated with a mounted device"""
         for entry in self._read_fstable('/proc/mounts'):
             if not entry.dev.startswith('/'):
                 continue
@@ -129,6 +133,7 @@ class BundleVolume(object):
         return None
 
     def _create_partition_table(self, image):
+        """Copy the partition table of the host system into the image"""
 
         # Copy the MBR and the space between the MBR and the first partition.
         # In msdos partition tables Grub Stage 1.5 is located there.
@@ -163,6 +168,7 @@ class BundleVolume(object):
             start = logical[i].geometry.end + 1
 
     def _get_partitions(self, disk):
+        """Returns a list with the partitions of the provided disk"""
         Partition = namedtuple('Partition', 'num start end type fs')
 
         partitions = []
@@ -177,7 +183,10 @@ class BundleVolume(object):
         return partitions
 
     def _shrink_partitions(self, image):
-
+        """Remove the last partition of the image if it is a swap partition and
+        shrink the partition before that. Make sure it can still host all the
+        files the corresponding host file system hosts
+        """
         new_end = self.disk.device.length
 
         image_disk = parted.Disk(parted.Device(image))
@@ -246,6 +255,7 @@ class BundleVolume(object):
         return (new_end, self._get_partitions(image_disk))
 
     def _map_partition(self, dev, num, start, end):
+        """Map a partition into a block device using the device mapper"""
         name = os.path.basename(dev) + "_" + uuid.uuid4().hex
         tablefd, table = tempfile.mkstemp()
         try:
@@ -258,13 +268,14 @@ class BundleVolume(object):
         return "/dev/mapper/%sp%d" % (name, num)
 
     def _unmap_partition(self, dev):
+        """Unmap a previously mapped partition"""
         if not os.path.exists(dev):
             return
 
         try_fail_repeat(dmsetup, 'remove', dev.split('/dev/mapper/')[1])
 
     def _mount(self, target, devs):
-
+        """Mount a list of filesystems in mountpoints relative to target"""
         devs.sort(key=lambda d: d[1])
         for dev, mpoint in devs:
             absmpoint = os.path.abspath(target + mpoint)
@@ -273,6 +284,8 @@ class BundleVolume(object):
             mount(dev, absmpoint)
 
     def _umount_all(self, target):
+        """Unmount all filesystems that are mounted under the directory target
+        """
         mpoints = []
         for entry in self._read_fstable('/proc/mounts'):
             if entry.mpoint.startswith(os.path.abspath(target)):
@@ -283,6 +296,10 @@ class BundleVolume(object):
             try_fail_repeat(umount, mpoint)
 
     def _to_exclude(self):
+        """Find which directories to exclude during the image copy. This is
+        accompliced by checking which directories serve as mount points for
+        virtual file systems
+        """
         excluded = ['/tmp', '/var/tmp']
         if self.tmp is not None:
             excluded.append(self.tmp)
@@ -318,6 +335,9 @@ class BundleVolume(object):
         return excluded
 
     def _replace_uuids(self, target, new_uuid):
+        """Replace UUID references in various files. This is needed after
+        copying system files of the host into a new filesystem
+        """
 
         files = ['/etc/fstab',
                  '/boot/grub/grub.cfg',
@@ -343,6 +363,10 @@ class BundleVolume(object):
                     dest.write(line)
 
     def _create_filesystems(self, image, partitions):
+        """Fill the image with data. Host file systems that are not currently
+        mounted are binary copied into the image. For mounted file systems, a
+        file system level copy is performed.
+        """
 
         filesystem = {}
         for p in self.disk.partitions:
index 205ec8c..d2c9182 100644 (file)
@@ -48,14 +48,14 @@ from image_creator.output.cli import SimpleOutput
 from image_creator.output.dialog import GaugeOutput
 from image_creator.output.composite import CompositeOutput
 from image_creator.disk import Disk
-from image_creator.dialog_wizard import wizard
+from image_creator.dialog_wizard import start_wizard
 from image_creator.dialog_menu import main_menu
 from image_creator.dialog_util import SMALL_WIDTH, WIDTH, confirm_exit, \
     Reset, update_background_title
 
 
 def create_image(d, media, out, tmp):
-
+    """Create an image out of `media'"""
     d.setBackgroundTitle('snf-image-creator')
 
     gauge = GaugeOutput(d, "Initialization", "Initializing...")
@@ -108,7 +108,7 @@ def create_image(d, media, out, tmp):
             code = d.yesno(msg, width=WIDTH, height=12, yes_label="Wizard",
                            no_label="Expert")
             if code == d.DIALOG_OK:
-                if wizard(session):
+                if start_wizard(session):
                     break
             elif code == d.DIALOG_CANCEL:
                 main_menu(session)
@@ -125,7 +125,7 @@ def create_image(d, media, out, tmp):
 
 
 def select_file(d, media):
-
+    """Select a media file"""
     if media == '/':
         return '/'
 
index 3524969..0e32167 100644 (file)
@@ -65,6 +65,7 @@ CONFIGURATION_TASKS = [
 
 
 class MetadataMonitor(object):
+    """Monitors image metadata chages"""
     def __init__(self, session, meta):
         self.session = session
         self.meta = meta
@@ -107,6 +108,7 @@ class MetadataMonitor(object):
 
 
 def upload_image(session):
+    """Upload the image to pithos+"""
     d = session["dialog"]
     image = session['image']
     meta = session['metadata']
@@ -187,6 +189,7 @@ def upload_image(session):
 
 
 def register_image(session):
+    """Register image with cyclades"""
     d = session["dialog"]
 
     is_public = False
@@ -254,6 +257,7 @@ def register_image(session):
 
 
 def kamaki_menu(session):
+    """Show kamaki related actions"""
     d = session['dialog']
     default_item = "Account"
 
@@ -316,6 +320,7 @@ def kamaki_menu(session):
 
 
 def add_property(session):
+    """Add a new property to the image"""
     d = session['dialog']
 
     while 1:
@@ -350,6 +355,7 @@ def add_property(session):
 
 
 def modify_properties(session):
+    """Modify an existing image property"""
     d = session['dialog']
 
     while 1:
@@ -392,6 +398,7 @@ def modify_properties(session):
 
 
 def delete_properties(session):
+    """Delete an image property"""
     d = session['dialog']
 
     choices = []
@@ -414,6 +421,7 @@ def delete_properties(session):
 
 
 def exclude_tasks(session):
+    """Exclude specific tasks from running during image deployment"""
     d = session['dialog']
 
     index = 0
@@ -478,6 +486,7 @@ def exclude_tasks(session):
 
 
 def sysprep(session):
+    """Perform various system preperation tasks on the image"""
     d = session['dialog']
     image = session['image']
 
@@ -568,6 +577,7 @@ def sysprep(session):
 
 
 def shrink(session):
+    """Shrink the image"""
     d = session['dialog']
     image = session['image']
 
@@ -604,6 +614,7 @@ def shrink(session):
 
 
 def customization_menu(session):
+    """Show image customization menu"""
     d = session['dialog']
 
     choices = [("Sysprep", "Run various image preparation tasks"),
@@ -635,6 +646,7 @@ def customization_menu(session):
 
 
 def main_menu(session):
+    """Show the main menu of the program"""
     d = session['dialog']
 
     update_background_title(session)
index f7add02..e18fb89 100644 (file)
@@ -42,6 +42,7 @@ WIDTH = 70
 
 
 def update_background_title(session):
+    """Update the backgroud title of the dialog page"""
     d = session['dialog']
     disk = session['disk']
     image = session['image']
@@ -60,19 +61,23 @@ def update_background_title(session):
 
 
 def confirm_exit(d, msg=''):
+    """Ask the user to confirm when exiting the program"""
     return not d.yesno("%s Do you want to exit?" % msg, width=SMALL_WIDTH)
 
 
 def confirm_reset(d):
+    """Ask the user to confirm a reset action"""
     return not d.yesno("Are you sure you want to reset everything?",
                        width=SMALL_WIDTH, defaultno=1)
 
 
 class Reset(Exception):
+    """Exception used to reset the program"""
     pass
 
 
 def extract_metadata_string(session):
+    """Convert image metadata to text"""
     metadata = ['%s=%s' % (k, v) for (k, v) in session['metadata'].items()]
 
     if 'task_metadata' in session:
@@ -82,6 +87,7 @@ def extract_metadata_string(session):
 
 
 def extract_image(session):
+    """Dump the image to a local file"""
     d = session['dialog']
     dir = os.getcwd()
     while 1:
index e1719cc..5605cc7 100644 (file)
@@ -45,14 +45,22 @@ PAGE_WIDTH = 70
 
 
 class WizardExit(Exception):
+    """Exception used to exit the wizard"""
     pass
 
 
 class WizardInvalidData(Exception):
+    """Exception triggered when the user provided data are invalid"""
     pass
 
 
 class Wizard:
+    """Represents a dialog-based wizard
+
+    The wizard is a collection of pages that have a "Next" and a "Back" button
+    on them. The pages are used to collect user data.
+    """
+
     def __init__(self, session):
         self.session = session
         self.pages = []
@@ -60,9 +68,11 @@ class Wizard:
         self.d = session['dialog']
 
     def add_page(self, page):
+        """Add a new page to the wizard"""
         self.pages.append(page)
 
     def run(self):
+        """Run the wizard"""
         idx = 0
         while True:
             try:
@@ -95,6 +105,7 @@ class Wizard:
 
 
 class WizardPage(object):
+    """Represents a page in a wizard"""
     NEXT = 1
     PREV = -1
 
@@ -106,11 +117,15 @@ class WizardPage(object):
         setattr(self, "display", display)
 
     def run(self, session, index, total):
+        """Display this wizard page
+
+        This function is used by the wizard program when accessing a page.
+        """
         raise NotImplementedError
 
 
 class WizardRadioListPage(WizardPage):
-
+    """Represent a Radio List in a wizard"""
     def __init__(self, name, printable, message, choices, **kargs):
         super(WizardRadioListPage, self).__init__(**kargs)
         self.name = name
@@ -145,12 +160,13 @@ class WizardRadioListPage(WizardPage):
 
 
 class WizardInputPage(WizardPage):
-
+    """Represents an input field in a wizard"""
     def __init__(self, name, printable, message, **kargs):
         super(WizardInputPage, self).__init__(**kargs)
         self.name = name
         self.printable = printable
         self.message = message
+        self.info = "%s: <none>" % self.printable
         self.title = kargs['title'] if 'title' in kargs else ''
         self.init = kargs['init'] if 'init' in kargs else ''
 
@@ -173,7 +189,8 @@ class WizardInputPage(WizardPage):
         return self.NEXT
 
 
-def wizard(session):
+def start_wizard(session):
+    """Run the image creation wizard"""
     init_token = Kamaki.get_token()
     if init_token is None:
         init_token = ""
@@ -196,6 +213,7 @@ def wizard(session):
         title="Registration Type", default="Private")
 
     def validate_account(token):
+        """Check if a token is valid"""
         d = session['dialog']
 
         if len(token) == 0:
@@ -231,6 +249,7 @@ def wizard(session):
 
 
 def create_image(session):
+    """Create an image using the information collected by the wizard"""
     d = session['dialog']
     image = session['image']
     wizard = session['wizard']
index 0645496..2d6135c 100644 (file)
@@ -76,6 +76,9 @@ class Disk(object):
         self._add_cleanup(shutil.rmtree, self.tmp)
 
     def _get_tmp_dir(self, default=None):
+        """Check tmp directory candidates and return the one with the most
+        available space.
+        """
         if default is not None:
             return default
 
@@ -92,15 +95,20 @@ class Disk(object):
         return TMP_CANDIDATES[max_idx]
 
     def _add_cleanup(self, job, *args):
+        """Add a new job in the cleanup list"""
         self._cleanup_jobs.append((job, args))
 
     def _losetup(self, fname):
+        """Setup a loop device and add it to the cleanup list. The loop device
+        will be detached when cleanup is called.
+        """
         loop = losetup('-f', '--show', fname)
         loop = loop.strip()  # remove the new-line char
         self._add_cleanup(try_fail_repeat, losetup, '-d', loop)
         return loop
 
     def _dir_to_disk(self):
+        """Create a disk out of a directory"""
         if self.source == '/':
             bundle = BundleVolume(self.out, self.meta)
             image = '%s/%s.diskdump' % (self.tmp, uuid.uuid4().hex)
index 98f7cc8..3be594e 100644 (file)
@@ -42,9 +42,11 @@ BLOCKSIZE = 512
 class MBR(object):
     """Represents a Master Boot Record."""
     class Partition(object):
+        """Represents a partition entry in MBR"""
         format = "<B3sB3sLL"
 
         def __init__(self, raw_part):
+            """Create a Partition instance"""
             (
                 self.status,
                 self.start,
@@ -55,6 +57,7 @@ class MBR(object):
             ) = struct.unpack(self.format, raw_part)
 
         def pack(self):
+            """Pack the partition values into a binary string"""
             return struct.pack(self.format,
                                self.status,
                                self.start,
@@ -112,6 +115,7 @@ class MBR(object):
     510     2               MBR signature
     """
     def __init__(self, block):
+        """Create an MBR instance"""
         raw_part = {}
         (self.code_area,
          raw_part[0],
@@ -126,11 +130,11 @@ class MBR(object):
 
     @staticmethod
     def size():
-        """Returns the size of a Master Boot Record."""
+        """Return the size of a Master Boot Record."""
         return struct.calcsize(MBR.format)
 
     def pack(self):
-        """Packs an MBR to a binary string."""
+        """Pack an MBR to a binary string."""
         return struct.pack(self.format,
                            self.code_area,
                            self.part[0].pack(),
@@ -174,6 +178,7 @@ class GPTPartitionTable(object):
         """
 
         def __init__(self, block):
+            """Create a GPTHeader instance"""
             (self.signature,
              self.revision,
              self.hdr_size,
@@ -207,10 +212,11 @@ class GPTPartitionTable(object):
 
         @staticmethod
         def size():
-            """Returns the size of a GPT Header."""
+            """Return the size of a GPT Header."""
             return struct.calcsize(GPTPartitionTable.GPTHeader.format)
 
         def __str__(self):
+            """Print a GPTHeader""" 
             return "Signature: %s\n" % self.signature + \
                    "Revision: %r\n" % self.revision + \
                    "Header Size: %d\n" % self.hdr_size + \
@@ -227,6 +233,7 @@ class GPTPartitionTable(object):
                    "CRC32 of partition array: %s\n" % self.part_crc32
 
     def __init__(self, disk):
+        """Create a GPTPartitionTable instance"""
         self.disk = disk
         with open(disk, "rb") as d:
             # MBR (Logical block address 0)
@@ -249,7 +256,7 @@ class GPTPartitionTable(object):
             self.secondary = self.GPTHeader(raw_header)
 
     def size(self):
-        """Returns the payload size of GPT partitioned device."""
+        """Return the payload size of GPT partitioned device."""
         return (self.primary.backup_lba + 1) * BLOCKSIZE
 
     def shrink(self, size, old_size):
index 3393efe..a5804a3 100644 (file)
@@ -44,7 +44,7 @@ class Image(object):
     """The instances of this class can create images out of block devices."""
 
     def __init__(self, device, output, bootable=True, meta={}):
-        """Create a new ImageCreator."""
+        """Create a new Image instance"""
 
         self.device = device
         self.out = output
@@ -78,7 +78,7 @@ class Image(object):
         self.guestfs_enabled = False
 
     def enable(self):
-        """Enable a newly created ImageCreator"""
+        """Enable a newly created Image instance"""
 
         self.out.output('Launching helper VM (may take a while) ...', False)
         # self.progressbar = self.out.Progress(100, "Launching helper VM",
@@ -110,6 +110,7 @@ class Image(object):
         self.out.success('found a(n) %s system' % self.distro)
 
     def _get_os(self):
+        """Return an OS class instance for this image"""
         if hasattr(self, "_os"):
             return self._os
 
@@ -135,7 +136,7 @@ class Image(object):
     os = property(_get_os)
 
     def destroy(self):
-        """Destroy this ImageCreator instance."""
+        """Destroy this Image instance."""
 
         # In new guestfs versions, there is a handy shutdown method for this
         try:
@@ -185,6 +186,7 @@ class Image(object):
         self.mounted = False
 
     def _last_partition(self):
+        """Return the last partition of the image disk"""
         if self.meta['PARTITION_TABLE'] not in 'msdos' 'gpt':
             msg = "Unsupported partition table: %s. Only msdos and gpt " \
                 "partition tables are supported" % self.meta['PARTITION_TABLE']
@@ -211,10 +213,10 @@ class Image(object):
         return last_partition
 
     def shrink(self):
-        """Shrink the disk.
+        """Shrink the image.
 
-        This is accomplished by shrinking the last filesystem in the
-        disk and then updating the partition table. The new disk size
+        This is accomplished by shrinking the last file system of the
+        image and then updating the partition table. The new disk size
         (in bytes) is returned.
 
         ATTENTION: make sure unmount is called before shrink
@@ -337,7 +339,7 @@ class Image(object):
         return self.size
 
     def dump(self, outfile):
-        """Dumps the content of device into a file.
+        """Dumps the content of the image into a file.
 
         This method will only dump the actual payload, found by reading the
         partition table. Empty space in the end of the device will be ignored.
index f5a40c7..654a933 100644 (file)
@@ -46,17 +46,20 @@ class Kamaki(object):
 
     @staticmethod
     def get_token():
+        """Get the saved token"""
         config = Config()
         return config.get('global', 'token')
 
     @staticmethod
     def save_token(token):
+        """Save this token to the configuration file"""
         config = Config()
         config.set('global', 'token', token)
         config.write()
 
     @staticmethod
     def get_account(token):
+        """Return the account corresponding to this token"""
         config = Config()
         astakos = AstakosClient(config.get('astakos', 'url'), token)
         try:
@@ -69,6 +72,7 @@ class Kamaki(object):
         return account
 
     def __init__(self, account, output):
+        """Create a Kamaki instance"""
         self.account = account
         self.out = output
 
index c0fa505..92dde0d 100644 (file)
@@ -38,6 +38,7 @@ import re
 
 
 def os_cls(distro, osfamily):
+    """Given the distro name and the osfamily, return the appropriate class"""
     module = None
     classname = None
     try:
@@ -60,6 +61,7 @@ def add_prefix(target):
 
 
 def sysprep(enabled=True):
+    """Decorator for system preparation tasks"""
     def wrapper(func):
         func.sysprep = True
         func.enabled = enabled
index 33ba887..5feb9fb 100644 (file)
@@ -35,6 +35,7 @@ from image_creator.os_type.unix import Unix
 
 
 class Freebsd(Unix):
+    """OS class for FreeBSD Unix-like os"""
     pass
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
index a294ca1..64c4a67 100644 (file)
@@ -35,6 +35,7 @@ from image_creator.os_type.unix import Unix
 
 
 class Hurd(Unix):
+    """OS class for GNU Hurd"""
     pass
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
index 0166129..1bee1f2 100644 (file)
@@ -38,6 +38,7 @@ import time
 
 
 class Linux(Unix):
+    """OS class for Linux"""
     def __init__(self, rootdev, ghandler, output):
         super(Linux, self).__init__(rootdev, ghandler, output)
         self._uuid = dict()
index bfb94d6..da5443f 100644 (file)
@@ -35,6 +35,7 @@ from image_creator.os_type.unix import Unix
 
 
 class Netbsd(Unix):
+    """OS class for NetBSD"""
     pass
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
index 5eb823b..5b0b571 100644 (file)
@@ -35,8 +35,14 @@ from image_creator.os_type.linux import Linux, sysprep
 
 
 class Slackware(Linux):
+    """OS class for Slackware Linux"""
     @sysprep()
-    def cleanup_log(self):
+    def cleanup_log(self, print_header=True):
+        """Empty all files under /var/log"""
+
+        if print_header:
+            self.out.output('Emptying all files under /var/log')
+
         # In slackware the metadata about installed packages are
         # stored in /var/log/packages. Clearing all /var/log files
         # will destroy the package management system.
index cc6e973..c0ed913 100644 (file)
@@ -35,6 +35,7 @@ from image_creator.os_type.linux import Linux
 
 
 class Ubuntu(Linux):
+    """OS class for Ubuntu Linux variants"""
     def __init__(self, rootdev, ghandler, output):
         super(Ubuntu, self).__init__(rootdev, ghandler, output)
 
index a1ef905..38dc0fb 100644 (file)
@@ -37,7 +37,7 @@ from image_creator.os_type import OSBase, sysprep
 
 
 class Unix(OSBase):
-
+    """OS class for Unix"""
     sensitive_userdata = [
         '.bash_history',
         '.gnupg',
index 7ba6767..f1e6921 100644 (file)
@@ -35,6 +35,7 @@ from image_creator.os_type import OSBase
 
 
 class Windows(OSBase):
+    """OS class for Windows"""
     pass
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
index 3992846..7beceac 100644 (file)
 
 
 class Output(object):
+    """A class for printing program output"""
     def error(self, msg, new_line=True):
+        """Print an error"""
         pass
 
     def warn(self, msg, new_line=True):
+        """Print a warning"""
         pass
 
     def success(self, msg, new_line=True):
+        """Print msg after an action is completed"""
         pass
 
     def output(self, msg='', new_line=True):
+        """Print normal program output"""
         pass
 
     def cleanup(self):
+        """Cleanup this output class"""
         pass
 
     def clear(self):
+        """Clear the screen"""
         pass
 
     def _get_progress(self):
+        """Returns a new Progress object"""
         progress = self._Progress
         progress.output = self
         return progress
@@ -59,21 +67,26 @@ class Output(object):
     Progress = property(_get_progress)
 
     class _Progress(object):
+        """Internal progress bar class"""
         def __init__(self, size, title, bar_type='default'):
             self.size = size
             self.bar_type = bar_type
             self.output.output("%s..." % title, False)
 
         def goto(self, dest):
+            """Move progress to a specific position"""
             pass
 
         def next(self):
+            """Move progress a step forward"""
             pass
 
         def success(self, result):
+            """Print a msg after an action is completed successfully"""
             self.output.success(result)
 
     def progress_generator(self, message):
+        """A python generator for the progress bar class"""
         def generator(n):
             progressbar = self.Progress(n, message)
 
index 6e2404c..f88fdbf 100644 (file)
@@ -31,6 +31,8 @@
 # interpreted as representing official policies, either expressed
 # or implied, of GRNET S.A.
 
+"""Normal Command-line interface output"""
+
 from image_creator.output import Output
 
 import sys
@@ -65,31 +67,41 @@ def clear(stream):
 
 
 class SilentOutput(Output):
+    """Silent Output class. Only Errors are printed"""
     pass
 
 
 class SimpleOutput(Output):
+    """Print messages but not progress bars. Progress bars are treated as
+    output messages. The user gets informed when the action begins and when it
+    ends, but no progress is shown in between."""
     def __init__(self, colored=True, stream=None):
         self.colored = colored
         self.stream = sys.stderr if stream is None else stream
 
     def error(self, msg, new_line=True):
+        """Print an error"""
         error(msg, new_line, self.colored, self.stream)
 
     def warn(self, msg, new_line=True):
+        """Print a warning"""
         warn(msg, new_line, self.colored, self.stream)
 
     def success(self, msg, new_line=True):
+        """Print msg after an action is completed"""
         success(msg, new_line, self.colored, self.stream)
 
     def output(self, msg='', new_line=True):
+        """Print msg as normal program output"""
         output(msg, new_line, lambda x: x, self.stream)
 
     def clear(self):
+        """Clear the screen"""
         clear(self.stream)
 
 
 class OutputWthProgress(SimpleOutput):
+    """Output class with progress."""
     class _Progress(Bar):
         MESSAGE_LENGTH = 30
 
@@ -102,6 +114,7 @@ class OutputWthProgress(SimpleOutput):
         }
 
         def __init__(self, size, title, bar_type='default'):
+            """Create a Progress bar"""
             self.hide_cursor = False
             super(OutputWthProgress._Progress, self).__init__()
             self.title = title
@@ -116,6 +129,7 @@ class OutputWthProgress(SimpleOutput):
             self.start()
 
         def success(self, result):
+            """Print result after progress has finished"""
             self.output.output("\r%s ...\033[K" % self.title, False)
             self.output.success(result)
 
index adb7c6a..a9502ec 100644 (file)
@@ -44,54 +44,68 @@ class CompositeOutput(Output):
     """
 
     def __init__(self, outputs=[]):
+        """Add initial output instances"""
         self._outputs = outputs
 
     def add(self, output):
+        """Add another output instance"""
         self._outputs.append(output)
 
     def remove(self, output):
+        """Remove an output instance"""
         self._outputs.remove(output)
 
     def error(self, msg, new_line=True):
+        """Call the error method of each of the output instances"""
         for out in self._outputs:
             out.error(msg, new_line)
 
     def warn(self, msg, new_line=True):
+        """Call the warn method of each of the output instances"""
         for out in self._outputs:
             out.warn(msg, new_line)
 
     def success(self, msg, new_line=True):
+        """Call the success method of each of the output instances"""
         for out in self._outputs:
             out.success(msg, new_line)
 
     def output(self, msg='', new_line=True):
+        """Call the output method of each of the output instances"""
         for out in self._outputs:
             out.output(msg, new_line)
 
     def cleanup(self):
+        """Call the cleanup method of each of the output instances"""
         for out in self._outputs:
             out.cleanup()
 
     def clear(self):
+        """Call the clear method of each of the output instances"""
         for out in self._outputs:
             out.clear()
 
     class _Progress(object):
+        """Class used to composite different Progress objects"""
 
         def __init__(self, size, title, bar_type='default'):
+            """Create a progress on each of the added output instances"""
             self._progresses = []
             for out in self.output._outputs:
                 self._progresses.append(out.Progress(size, title, bar_type))
 
         def goto(self, dest):
+            """Call the goto method of each of the progress instances"""
             for progress in self._progresses:
                 progress.goto(dest)
 
         def next(self):
+            """Call the next method of each of the progress instances"""
             for progress in self._progresses:
                 progress.next()
 
         def success(self, result):
+            """Call the success method of each of the progress instances"""
             for progress in self._progresses:
                 progress.success(result)
 
index 672387d..2790b1c 100644 (file)
@@ -37,6 +37,7 @@ import fcntl
 
 
 class GaugeOutput(Output):
+    """Output class implemented using dialog's gauge widget"""
     def __init__(self, dialog, title, msg=''):
         self.d = dialog
         self.msg = msg
@@ -55,26 +56,31 @@ class GaugeOutput(Output):
         fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)
 
     def output(self, msg='', new_line=True):
+        """Print msg as normal output"""
         self.msg = msg
         self.percent = 0
         self.d.gauge_update(self.percent, self.msg, update_text=True)
         time.sleep(0.4)
 
     def success(self, result, new_line=True):
+        """Print result after a successfull action"""
         self.percent = 100
         self.d.gauge_update(self.percent, "%s %s" % (self.msg, result),
                             update_text=True)
         time.sleep(0.4)
 
     def warn(self, msg, new_line=True):
+        """Print a warning"""
         self.d.gauge_update(self.percent, "%s Warning: %s" % (self.msg, msg),
                             update_text=True)
         time.sleep(0.4)
 
     def cleanup(self):
+        """Cleanup the GaugeOutput instance"""
         self.d.gauge_stop()
 
     class _Progress(Output._Progress):
+        """Progress class for dialog's gauge widget"""
         template = {
             'default': '%(index)d/%(size)d',
             'percent': '',
@@ -93,6 +99,7 @@ class GaugeOutput(Output):
             return self.template[self.bar_type] % self.output.__dict__
 
         def goto(self, dest):
+            """Move progress bar to a specific position"""
             self.output.index = dest
             self.output.percent = self.output.index * 100 // self.output.size
             msg = "%s %s" % (self.output.msg, self._postfix())
@@ -100,10 +107,12 @@ class GaugeOutput(Output):
                                        update_text=True)
 
         def next(self):
+            """Move progress bar one step forward"""
             self.goto(self.output.index + 1)
 
 
 class InfoBoxOutput(Output):
+    """Output class implemented using dialog's infobox widget"""
     def __init__(self, dialog, title, msg='', height=20, width=70):
         self.d = dialog
         self.title = title
@@ -113,6 +122,7 @@ class InfoBoxOutput(Output):
         self.d.infobox(self.msg, title=self.title)
 
     def output(self, msg='', new_line=True):
+        """Print msg as normal output"""
         nl = '\n' if new_line else ''
         self.msg += "%s%s" % (msg, nl)
         # If output is long, only output the last lines that fit in the box
@@ -123,12 +133,17 @@ class InfoBoxOutput(Output):
                        width=self.width)
 
     def success(self, result, new_line=True):
+        """Print result after an action is completed successfully"""
         self.output(result, new_line)
 
     def warn(self, msg, new_line=True):
+        """Print a warning message"""
         self.output("Warning: %s" % msg, new_line)
 
     def finalize(self):
+        """Finalize the output. After this is called, the InfoboxOutput
+        instance should be destroyed
+        """
         self.d.msgbox(self.msg, title=self.title, height=(self.height + 2),
                       width=self.width)
 
index b4d5000..9339fd8 100644 (file)
@@ -38,10 +38,12 @@ import os
 
 
 class FatalError(Exception):
+    """Fatal Error exception of snf-image-creator"""
     pass
 
 
 def get_command(command):
+    """Return a file system binary command"""
     def find_sbin_command(command, exception):
         search_paths = ['/usr/local/sbin', '/usr/sbin', '/sbin']
         for fullpath in map(lambda x: "%s/%s" % (x, command), search_paths):
@@ -56,7 +58,7 @@ def get_command(command):
 
 
 def try_fail_repeat(command, *args):
-
+    """Execute a command multiple times until it succeeds"""
     times = (0.1, 0.5, 1, 2)
     i = iter(times)
     while True:
@@ -74,15 +76,19 @@ def try_fail_repeat(command, *args):
 
 
 def free_space(dirname):
+    """Compute the free space in a directory"""
     stat = os.statvfs(dirname)
     return stat.f_bavail * stat.f_frsize
 
 
 class MD5:
+    """Represents MD5 computations"""
     def __init__(self, output):
+        """Create an MD5 instance"""
         self.out = output
 
     def compute(self, filename, size):
+        """Compute the MD5 checksum of a file"""
         MB = 2 ** 20
         BLOCKSIZE = 4 * MB  # 4MB