Changeset - 4ebd9fb9901b
[Not reviewed]
0 2 0
Lance Edgar - 9 years ago 2015-04-27 15:30:52
ledgar@sacfoodcoop.com
Add `PathNotFound` exception, normalize to it within `locking_copy_test()`.

Hopefully this improves the retry situation on a certain system I know...
2 files changed with 46 insertions and 2 deletions:
0 comments (0 inline, 0 general)
rattail/exceptions.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2014 Lance Edgar
 
#  Copyright © 2010-2015 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
#  Rattail is free software: you can redistribute it and/or modify it under the
 
#  terms of the GNU Affero General Public License as published by the Free
 
#  Software Foundation, either version 3 of the License, or (at your option)
 
#  any later version.
 
#
 
#  Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
 
#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 
#  FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for
 
#  more details.
 
@@ -52,24 +52,50 @@ class SenderNotFound(ConfigurationError):
 

	
 

	
 
class RecipientsNotFound(ConfigurationError):
 
    
 
    def __init__(self, key):
 
        self.key = key
 

	
 
    def __unicode__(self):
 
        return ("No email recipients (To: addresses) found in config.  Please set '{0}.to' "
 
                "(or 'default.to') in the [rattail.mail] section.".format(self.key))
 

	
 

	
 
class FileOperationError(RattailError):
 
    """
 
    Generic exception for file operation failures.
 
    """
 

	
 

	
 
class PathNotFound(FileOperationError):
 
    """
 
    Raised when "path not found" errors are encountered within the
 
    :func:`rattail.files.locking_copy_test()` function.  The purpose of this is
 
    to normalize these errors to a single type, since the file monitor retry
 
    mechanism will fail if two distinct exceptions are encountered during its
 
    processing attempts.
 
    """
 
    def __init__(self, original_error):
 
        self.original_error = original_error
 

	
 
    def __str__(self):
 
        return unicode(self).encode('utf_8')
 

	
 
    def __unicode__(self):
 
        return '{0}: {1}'.format(
 
            self.original_error.__class__.__name__,
 
            self.original_error)
 

	
 

	
 
class BatchError(RattailError):
 
    """
 
    Base class for all batch-related errors.
 
    """
 

	
 

	
 
class BatchTypeNotFound(BatchError):
 

	
 
    def __init__(self, name):
 
        self.name = name
 

	
 
    def __str__(self):
rattail/files.py
Show inline comments
 
@@ -30,24 +30,26 @@ from __future__ import unicode_literals
 

	
 
import io
 
import os
 
import os.path
 
import shutil
 
import lockfile
 
import tempfile
 
import errno
 
from datetime import datetime
 

	
 
import pkg_resources
 

	
 
from rattail.exceptions import PathNotFound
 

	
 

	
 
def count_lines(path, encoding=None):
 
    """
 
    Counts the number of lines in a text file.  Some attempt is made to ensure
 
    cross-platform compatibility.
 

	
 
    :param path: Path to the file.
 
    :type path: string
 

	
 
    :param encoding: Optional encoding to be used when opening the file for
 
      reading.  This is passed directly to :func:`python:io.open()`.
 

	
 
@@ -126,34 +128,50 @@ def locking_copy_test(src, destdir):
 
    :ref:`filemon-profile-watchlocks`.
 

	
 
    :param src: Path to the source file.
 
    :type src: string
 

	
 
    :param destdir: Path to the destination directory.
 
    :type destdir: string
 
    """
 
    fn = os.path.basename(src)
 
    dst = os.path.join(destdir, fn)
 
    lockdir = '{0}.lock'.format(dst)
 

	
 
    # Note that we normalize two separate errors to PathNotFound below.  This
 
    # is because the file monitor retry mechanism will fail if two distinct
 
    # error types are encountered.  Since this function needs to be somewhat
 
    # robust in the face of network problems, raising a common error type will
 
    # give us a better chance at being retried (if so configured).
 

	
 
    # Attempt to create the lock dir.  We ignore "file exists" error here since
 
    # it's possible this function may be called mutliple times as part of a
 
    # filemon retry strategy.
 
    try:
 
        os.mkdir(lockdir)
 
    except OSError as error:
 
        if error.errno == errno.ENOENT:
 
            raise PathNotFound(error)
 
        if error.errno != errno.EEXIST:
 
            raise
 

	
 
    shutil.copy(src, dst)
 
    # Attempt to copy the file.
 
    try:
 
        shutil.copy(src, dst)
 
    except IOError as error:
 
        if error.errno == errno.ENOENT:
 
            raise PathNotFound(error)
 
        raise
 

	
 
    # Attempt to remove the lock dir.
 
    os.rmdir(lockdir)
 

	
 

	
 
def overwriting_move(src, dst):
 
    """
 
    Convenience function which is equivalent to ``shutil.move()``, except it
 
    will cause the destination file to be overwritten if it exists.
 
    """
 

	
 
    if os.path.isdir(dst):
 
        dst = os.path.join(dst, os.path.basename(src))
 
    if os.path.exists(dst):
0 comments (0 inline, 0 general)