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):
         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)
 
         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):
                 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
         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):
         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):
         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
         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):
         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.
 
         # 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):
             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 = []
         Partition = namedtuple('Partition', 'num start end type fs')
 
         partitions = []
@@ -177,7 +183,10 @@ class BundleVolume(object):
         return partitions
 
     def _shrink_partitions(self, image):
         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))
         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):
         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:
         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):
         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):
         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)
         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):
             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)):
         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):
             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)
         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):
         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',
 
         files = ['/etc/fstab',
                  '/boot/grub/grub.cfg',
@@ -343,6 +363,10 @@ class BundleVolume(object):
                     dest.write(line)
 
     def _create_filesystems(self, image, partitions):
                     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:
 
         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.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):
 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...")
     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:
             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)
                     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):
 
 
 def select_file(d, media):
-
+    """Select a media file"""
     if media == '/':
         return '/'
 
     if media == '/':
         return '/'
 
index 3524969..0e32167 100644 (file)
@@ -65,6 +65,7 @@ CONFIGURATION_TASKS = [
 
 
 class MetadataMonitor(object):
 
 
 class MetadataMonitor(object):
+    """Monitors image metadata chages"""
     def __init__(self, session, meta):
         self.session = session
         self.meta = meta
     def __init__(self, session, meta):
         self.session = session
         self.meta = meta
@@ -107,6 +108,7 @@ class MetadataMonitor(object):
 
 
 def upload_image(session):
 
 
 def upload_image(session):
+    """Upload the image to pithos+"""
     d = session["dialog"]
     image = session['image']
     meta = session['metadata']
     d = session["dialog"]
     image = session['image']
     meta = session['metadata']
@@ -187,6 +189,7 @@ def upload_image(session):
 
 
 def register_image(session):
 
 
 def register_image(session):
+    """Register image with cyclades"""
     d = session["dialog"]
 
     is_public = False
     d = session["dialog"]
 
     is_public = False
@@ -254,6 +257,7 @@ def register_image(session):
 
 
 def kamaki_menu(session):
 
 
 def kamaki_menu(session):
+    """Show kamaki related actions"""
     d = session['dialog']
     default_item = "Account"
 
     d = session['dialog']
     default_item = "Account"
 
@@ -316,6 +320,7 @@ def kamaki_menu(session):
 
 
 def add_property(session):
 
 
 def add_property(session):
+    """Add a new property to the image"""
     d = session['dialog']
 
     while 1:
     d = session['dialog']
 
     while 1:
@@ -350,6 +355,7 @@ def add_property(session):
 
 
 def modify_properties(session):
 
 
 def modify_properties(session):
+    """Modify an existing image property"""
     d = session['dialog']
 
     while 1:
     d = session['dialog']
 
     while 1:
@@ -392,6 +398,7 @@ def modify_properties(session):
 
 
 def delete_properties(session):
 
 
 def delete_properties(session):
+    """Delete an image property"""
     d = session['dialog']
 
     choices = []
     d = session['dialog']
 
     choices = []
@@ -414,6 +421,7 @@ def delete_properties(session):
 
 
 def exclude_tasks(session):
 
 
 def exclude_tasks(session):
+    """Exclude specific tasks from running during image deployment"""
     d = session['dialog']
 
     index = 0
     d = session['dialog']
 
     index = 0
@@ -478,6 +486,7 @@ def exclude_tasks(session):
 
 
 def sysprep(session):
 
 
 def sysprep(session):
+    """Perform various system preperation tasks on the image"""
     d = session['dialog']
     image = session['image']
 
     d = session['dialog']
     image = session['image']
 
@@ -568,6 +577,7 @@ def sysprep(session):
 
 
 def shrink(session):
 
 
 def shrink(session):
+    """Shrink the image"""
     d = session['dialog']
     image = session['image']
 
     d = session['dialog']
     image = session['image']
 
@@ -604,6 +614,7 @@ def shrink(session):
 
 
 def customization_menu(session):
 
 
 def customization_menu(session):
+    """Show image customization menu"""
     d = session['dialog']
 
     choices = [("Sysprep", "Run various image preparation tasks"),
     d = session['dialog']
 
     choices = [("Sysprep", "Run various image preparation tasks"),
@@ -635,6 +646,7 @@ def customization_menu(session):
 
 
 def main_menu(session):
 
 
 def main_menu(session):
+    """Show the main menu of the program"""
     d = session['dialog']
 
     update_background_title(session)
     d = session['dialog']
 
     update_background_title(session)
index f7add02..e18fb89 100644 (file)
@@ -42,6 +42,7 @@ WIDTH = 70
 
 
 def update_background_title(session):
 
 
 def update_background_title(session):
+    """Update the backgroud title of the dialog page"""
     d = session['dialog']
     disk = session['disk']
     image = session['image']
     d = session['dialog']
     disk = session['disk']
     image = session['image']
@@ -60,19 +61,23 @@ def update_background_title(session):
 
 
 def confirm_exit(d, msg=''):
 
 
 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):
     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):
     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):
     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:
     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):
 
 
 def extract_image(session):
+    """Dump the image to a local file"""
     d = session['dialog']
     dir = os.getcwd()
     while 1:
     d = session['dialog']
     dir = os.getcwd()
     while 1:
index e1719cc..5605cc7 100644 (file)
@@ -45,14 +45,22 @@ PAGE_WIDTH = 70
 
 
 class WizardExit(Exception):
 
 
 class WizardExit(Exception):
+    """Exception used to exit the wizard"""
     pass
 
 
 class WizardInvalidData(Exception):
     pass
 
 
 class WizardInvalidData(Exception):
+    """Exception triggered when the user provided data are invalid"""
     pass
 
 
 class Wizard:
     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 = []
     def __init__(self, session):
         self.session = session
         self.pages = []
@@ -60,9 +68,11 @@ class Wizard:
         self.d = session['dialog']
 
     def add_page(self, page):
         self.d = session['dialog']
 
     def add_page(self, page):
+        """Add a new page to the wizard"""
         self.pages.append(page)
 
     def run(self):
         self.pages.append(page)
 
     def run(self):
+        """Run the wizard"""
         idx = 0
         while True:
             try:
         idx = 0
         while True:
             try:
@@ -95,6 +105,7 @@ class Wizard:
 
 
 class WizardPage(object):
 
 
 class WizardPage(object):
+    """Represents a page in a wizard"""
     NEXT = 1
     PREV = -1
 
     NEXT = 1
     PREV = -1
 
@@ -106,11 +117,15 @@ class WizardPage(object):
         setattr(self, "display", display)
 
     def run(self, session, index, total):
         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):
         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
     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):
 
 
 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
     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 ''
 
         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
 
 
         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 = ""
     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):
         title="Registration Type", default="Private")
 
     def validate_account(token):
+        """Check if a token is valid"""
         d = session['dialog']
 
         if len(token) == 0:
         d = session['dialog']
 
         if len(token) == 0:
@@ -231,6 +249,7 @@ def wizard(session):
 
 
 def create_image(session):
 
 
 def create_image(session):
+    """Create an image using the information collected by the wizard"""
     d = session['dialog']
     image = session['image']
     wizard = session['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):
         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
 
         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):
         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):
         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):
         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)
         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):
 class MBR(object):
     """Represents a Master Boot Record."""
     class Partition(object):
+        """Represents a partition entry in MBR"""
         format = "<B3sB3sLL"
 
         def __init__(self, raw_part):
         format = "<B3sB3sLL"
 
         def __init__(self, raw_part):
+            """Create a Partition instance"""
             (
                 self.status,
                 self.start,
             (
                 self.status,
                 self.start,
@@ -55,6 +57,7 @@ class MBR(object):
             ) = struct.unpack(self.format, raw_part)
 
         def pack(self):
             ) = 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,
             return struct.pack(self.format,
                                self.status,
                                self.start,
@@ -112,6 +115,7 @@ class MBR(object):
     510     2               MBR signature
     """
     def __init__(self, block):
     510     2               MBR signature
     """
     def __init__(self, block):
+        """Create an MBR instance"""
         raw_part = {}
         (self.code_area,
          raw_part[0],
         raw_part = {}
         (self.code_area,
          raw_part[0],
@@ -126,11 +130,11 @@ class MBR(object):
 
     @staticmethod
     def size():
 
     @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):
         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(),
         return struct.pack(self.format,
                            self.code_area,
                            self.part[0].pack(),
@@ -174,6 +178,7 @@ class GPTPartitionTable(object):
         """
 
         def __init__(self, block):
         """
 
         def __init__(self, block):
+            """Create a GPTHeader instance"""
             (self.signature,
              self.revision,
              self.hdr_size,
             (self.signature,
              self.revision,
              self.hdr_size,
@@ -207,10 +212,11 @@ class GPTPartitionTable(object):
 
         @staticmethod
         def size():
 
         @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):
             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 + \
             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):
                    "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)
         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):
             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):
         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={}):
     """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
 
         self.device = device
         self.out = output
@@ -78,7 +78,7 @@ class Image(object):
         self.guestfs_enabled = False
 
     def enable(self):
         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",
 
         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):
         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
 
         if hasattr(self, "_os"):
             return self._os
 
@@ -135,7 +136,7 @@ class Image(object):
     os = property(_get_os)
 
     def destroy(self):
     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:
 
         # 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):
         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']
         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):
         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
         (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):
         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.
 
         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():
 
     @staticmethod
     def get_token():
+        """Get the saved token"""
         config = Config()
         return config.get('global', 'token')
 
     @staticmethod
     def save_token(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):
         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:
         config = Config()
         astakos = AstakosClient(config.get('astakos', 'url'), token)
         try:
@@ -69,6 +72,7 @@ class Kamaki(object):
         return account
 
     def __init__(self, account, output):
         return account
 
     def __init__(self, account, output):
+        """Create a Kamaki instance"""
         self.account = account
         self.out = output
 
         self.account = account
         self.out = output
 
index c0fa505..92dde0d 100644 (file)
@@ -38,6 +38,7 @@ import re
 
 
 def os_cls(distro, osfamily):
 
 
 def os_cls(distro, osfamily):
+    """Given the distro name and the osfamily, return the appropriate class"""
     module = None
     classname = None
     try:
     module = None
     classname = None
     try:
@@ -60,6 +61,7 @@ def add_prefix(target):
 
 
 def sysprep(enabled=True):
 
 
 def sysprep(enabled=True):
+    """Decorator for system preparation tasks"""
     def wrapper(func):
         func.sysprep = True
         func.enabled = enabled
     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):
 
 
 class Freebsd(Unix):
+    """OS class for FreeBSD Unix-like os"""
     pass
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
     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):
 
 
 class Hurd(Unix):
+    """OS class for GNU Hurd"""
     pass
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
     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):
 
 
 class Linux(Unix):
+    """OS class for Linux"""
     def __init__(self, rootdev, ghandler, output):
         super(Linux, self).__init__(rootdev, ghandler, output)
         self._uuid = dict()
     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):
 
 
 class Netbsd(Unix):
+    """OS class for NetBSD"""
     pass
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
     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):
 
 
 class Slackware(Linux):
+    """OS class for Slackware Linux"""
     @sysprep()
     @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.
         # 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):
 
 
 class Ubuntu(Linux):
+    """OS class for Ubuntu Linux variants"""
     def __init__(self, rootdev, ghandler, output):
         super(Ubuntu, self).__init__(rootdev, ghandler, output)
 
     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):
 
 
 class Unix(OSBase):
-
+    """OS class for Unix"""
     sensitive_userdata = [
         '.bash_history',
         '.gnupg',
     sensitive_userdata = [
         '.bash_history',
         '.gnupg',
index 7ba6767..f1e6921 100644 (file)
@@ -35,6 +35,7 @@ from image_creator.os_type import OSBase
 
 
 class Windows(OSBase):
 
 
 class Windows(OSBase):
+    """OS class for Windows"""
     pass
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
     pass
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
index 3992846..7beceac 100644 (file)
 
 
 class Output(object):
 
 
 class Output(object):
+    """A class for printing program output"""
     def error(self, msg, new_line=True):
     def error(self, msg, new_line=True):
+        """Print an error"""
         pass
 
     def warn(self, msg, new_line=True):
         pass
 
     def warn(self, msg, new_line=True):
+        """Print a warning"""
         pass
 
     def success(self, msg, new_line=True):
         pass
 
     def success(self, msg, new_line=True):
+        """Print msg after an action is completed"""
         pass
 
     def output(self, msg='', new_line=True):
         pass
 
     def output(self, msg='', new_line=True):
+        """Print normal program output"""
         pass
 
     def cleanup(self):
         pass
 
     def cleanup(self):
+        """Cleanup this output class"""
         pass
 
     def clear(self):
         pass
 
     def clear(self):
+        """Clear the screen"""
         pass
 
     def _get_progress(self):
         pass
 
     def _get_progress(self):
+        """Returns a new Progress object"""
         progress = self._Progress
         progress.output = self
         return progress
         progress = self._Progress
         progress.output = self
         return progress
@@ -59,21 +67,26 @@ class Output(object):
     Progress = property(_get_progress)
 
     class _Progress(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):
         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):
             pass
 
         def next(self):
+            """Move progress a step forward"""
             pass
 
         def success(self, result):
             pass
 
         def success(self, result):
+            """Print a msg after an action is completed successfully"""
             self.output.success(result)
 
     def progress_generator(self, message):
             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)
 
         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.
 
 # 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
 from image_creator.output import Output
 
 import sys
@@ -65,31 +67,41 @@ def clear(stream):
 
 
 class SilentOutput(Output):
 
 
 class SilentOutput(Output):
+    """Silent Output class. Only Errors are printed"""
     pass
 
 
 class SimpleOutput(Output):
     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):
     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):
         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):
         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):
         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):
         output(msg, new_line, lambda x: x, self.stream)
 
     def clear(self):
+        """Clear the screen"""
         clear(self.stream)
 
 
 class OutputWthProgress(SimpleOutput):
         clear(self.stream)
 
 
 class OutputWthProgress(SimpleOutput):
+    """Output class with progress."""
     class _Progress(Bar):
         MESSAGE_LENGTH = 30
 
     class _Progress(Bar):
         MESSAGE_LENGTH = 30
 
@@ -102,6 +114,7 @@ class OutputWthProgress(SimpleOutput):
         }
 
         def __init__(self, size, title, bar_type='default'):
         }
 
         def __init__(self, size, title, bar_type='default'):
+            """Create a Progress bar"""
             self.hide_cursor = False
             super(OutputWthProgress._Progress, self).__init__()
             self.title = title
             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):
             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)
 
             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=[]):
     """
 
     def __init__(self, outputs=[]):
+        """Add initial output instances"""
         self._outputs = outputs
 
     def add(self, output):
         self._outputs = outputs
 
     def add(self, output):
+        """Add another output instance"""
         self._outputs.append(output)
 
     def remove(self, output):
         self._outputs.append(output)
 
     def remove(self, output):
+        """Remove an output instance"""
         self._outputs.remove(output)
 
     def error(self, msg, new_line=True):
         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):
         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):
         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):
         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):
         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):
         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):
         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'):
 
         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):
             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):
             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):
             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)
 
             for progress in self._progresses:
                 progress.success(result)
 
index 672387d..2790b1c 100644 (file)
@@ -37,6 +37,7 @@ import fcntl
 
 
 class GaugeOutput(Output):
 
 
 class GaugeOutput(Output):
+    """Output class implemented using dialog's gauge widget"""
     def __init__(self, dialog, title, msg=''):
         self.d = dialog
         self.msg = msg
     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):
         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):
         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):
         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):
         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):
         self.d.gauge_stop()
 
     class _Progress(Output._Progress):
+        """Progress class for dialog's gauge widget"""
         template = {
             'default': '%(index)d/%(size)d',
             'percent': '',
         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):
             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())
             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):
                                        update_text=True)
 
         def next(self):
+            """Move progress bar one step forward"""
             self.goto(self.output.index + 1)
 
 
 class InfoBoxOutput(Output):
             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
     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):
         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
         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):
                        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):
         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):
         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)
 
         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):
 
 
 class FatalError(Exception):
+    """Fatal Error exception of snf-image-creator"""
     pass
 
 
 def get_command(command):
     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):
     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):
 
 
 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:
     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):
 
 
 def free_space(dirname):
+    """Compute the free space in a directory"""
     stat = os.statvfs(dirname)
     return stat.f_bavail * stat.f_frsize
 
 
 class MD5:
     stat = os.statvfs(dirname)
     return stat.f_bavail * stat.f_frsize
 
 
 class MD5:
+    """Represents MD5 computations"""
     def __init__(self, output):
     def __init__(self, output):
+        """Create an MD5 instance"""
         self.out = output
 
     def compute(self, filename, size):
         self.out = output
 
     def compute(self, filename, size):
+        """Compute the MD5 checksum of a file"""
         MB = 2 ** 20
         BLOCKSIZE = 4 * MB  # 4MB
 
         MB = 2 ** 20
         BLOCKSIZE = 4 * MB  # 4MB