Example #1
0
    def __init__(self,
                 path,
                 umask=None,
                 rndhex=None,
                 maxelts=16000,
                 schema=dict()):
        """Check and set schema. Build the queue directory structure.

        Arguments:
            path
                the queue toplevel directory
            umask
                the umask to use when creating files and directories
                (default: use the running process' umask)
            rndhex
                the hexadecimal digit to use in names
                (default: randomly chosen)
            maxelts
                the maximum number of elements that an intermediate
                directory can hold (default: 16,000)
            schema
                the schema defining how to interpret user supplied
                data (mandatory if elements are added or read)
        Raise:
            TypeError  - wrong input data types provided
            QueueError - problems with the queue schema definition
            OSError    - can't create directory structure
        """
        super(Queue, self).__init__(path, umask=umask, rndhex=rndhex)

        if type(maxelts) in VALID_INT_TYPES:
            self.maxelts = maxelts
        else:
            raise TypeError("'maxelts' should be int or long")
        # check schema
        self.type = {}
        self.mandatory = {}
        if schema:
            if not isinstance(schema, dict):
                raise QueueError("invalid schema: %r" % schema)
            for name in schema.keys():
                if not _FileRegexp.match(name):
                    raise QueueError("invalid schema name: %r" % name)
                if not isinstance(schema[name], str):
                    raise QueueError("invalid data type for schema " +
                                     "specification: %r" % type(schema[name]))
                match = re.match('(binary|string|table)([\\?\\*]{0,2})?$',
                                 schema[name])
                if not match:
                    raise QueueError("invalid schema data type: %r" %
                                     schema[name])
                self.type[name] = match.group(1)
                if not re.search('\\?', match.group(2)):
                    self.mandatory[name] = True
            if not self.mandatory:
                raise QueueError("invalid schema: no mandatory data")
        # create directories
        for directory in (TEMPORARY_DIRECTORY, OBSOLETE_DIRECTORY):
            _special_mkdir('%s/%s' % (self.path, directory), self.umask)
Example #2
0
    def _add(self, *queues):
        """Add lists of queues to existing ones. Copies of the object
        instances are used.

        Arguments:
            *queues - add([q1,..]/(q1,..)) or add(q1,..)

        Raise:
            QueueError - queue already in the set
            TypeError  - wrong queue object type provided
        """
        type_queue = False
        for queue in queues:
            if type(queue) in [list, tuple] and not type_queue:
                for _queue in queue:
                    if isinstance(_queue, QueueBase):
                        if _queue.id in [x.id for x in self.qset]:
                            raise QueueError("queue already in the set: %s" %
                                             _queue.path)
                        self.qset.append(_queue.copy())
                    else:
                        raise TypeError("QueueBase objects expected.")
                break
            elif isinstance(queue, QueueBase):
                type_queue = True
                self.qset.append(queue.copy())
            else:
                raise TypeError("expected QueueBase object(s) or list/tuple "
                                "of QueueBase objects")
Example #3
0
def _check_element(name):
    """Check the given string to make sure it represents a valid element name.

    Raise:
        QueueError - given element is invalid
    """
    if not _DirElemRegexp.match(name):
        raise QueueError("invalid element name: %s" % name)
Example #4
0
    def get(self, ename):
        """Get an element data from a locked element.

        Arguments:
            ename - name of an element

        Return:
            dictionary representing an element

        Raise:
            QueueError - schema is unknown; unexpected data type in
                         the schema specification; missing mandatory
                         file of the element

            OSError    - problems opening/closing file
            IOError    - file read error
        """
        if not self.type:
            raise QueueError("unknown schema")
        _check_element(ename)
        if not self._is_locked(ename):
            raise QueueError("cannot get %s: not locked" % ename)
        data = {}
        for dname in self.type.keys():
            path = '%s/%s/%s' % (self.path, ename, dname)
            try:
                os.lstat(path)
            except Exception:
                error = sys.exc_info()[1]
                if error.errno != errno.ENOENT:
                    raise OSError("cannot lstat(%s): %s" % (path, error))
                if dname in self.mandatory:
                    raise QueueError("missing data file: %s" % path)
                else:
                    continue
            if self.type[dname] == 'binary':
                data[dname] = _file_read(path, 0)
            elif self.type[dname] == 'string':
                data[dname] = _file_read(path, 1)
            elif self.type[dname] == 'table':
                data[dname] = _string2hash(_file_read(path, 1))
            else:
                raise QueueError("unexpected data type: %s" % self.type[dname])
        return data
Example #5
0
def _hash2string(data):
    """Transform a hash of strings into a string.

    Raise:
        QueueError - invalid type of a value in hash
                     (allowed string or unicode)

    Note:
        the keys are sorted so that identical hashes yield to identical strings
    """
    string = ''
    for key in sorted(data.keys()):
        val = data[key]
        if type(val) not in VALID_STR_TYPES:
            raise QueueError("invalid hash value type: %r" % val)
        key = _H2SRegexp.sub(lambda m: _Byte2Esc[m.group(1)], key)
        val = _H2SRegexp.sub(lambda m: _Byte2Esc[m.group(1)], val)
        string = '%s%s' % (string, '%s\x09%s\x0a' % (key, val))
    return string
Example #6
0
def _string2hash(given):
    """Transform a string into a hash of strings.

    Raise:
        QueueError - unexpected hash line

    Note:
        duplicate keys are not checked (the last one wins)
    """
    _hash = dict()
    if not given:
        return _hash
    for line in given.strip('\n').split('\x0a'):
        match = _KeyValRegexp.match(line)
        if not match:
            raise QueueError("unexpected hash line: %s" % line)
        key = _S2HRegexp.sub(lambda m: _Esc2Byte[str(m.group(1))],
                             match.group(1))
        val = _S2HRegexp.sub(lambda m: _Esc2Byte[str(m.group(1))],
                             match.group(2))
        _hash[key] = val
    return _hash
Example #7
0
    def add(self, data):
        """Add a new element to the queue and return its name.
        Arguments:

            data - element as a dictionary (should conform to the schema)

        Raise:

            QueueError - problem with schema definition or data
            OSError    - problem putting element on disk

        Note:

          the destination directory must _not_ be created beforehand as
          it would be seen as a valid (but empty) element directory by
          another process, we therefore use rename() from a temporary
          directory
        """
        if not self.type:
            raise QueueError("unknown schema")
        while True:
            temp = '%s/%s/%s' % (self.path, TEMPORARY_DIRECTORY,
                                 _name(self.rndhex))
            if _special_mkdir(temp, self.umask):
                break
        for name in data.keys():
            if name not in self.type:
                raise QueueError("unexpected data: %s" % name)
            if self.type[name] == 'binary':
                if type(data[name]) not in VALID_STR_TYPES:
                    raise QueueError("unexpected binary data in %s: %r" %
                                     (name, data[name]))
                _file_write('%s/%s' % (temp, name), 0, self.umask, data[name])
            elif self.type[name] == 'string':
                if type(data[name]) not in VALID_STR_TYPES:
                    raise QueueError("unexpected string data in %s: %r" %
                                     (name, data[name]))
                _file_write('%s/%s' % (temp, name), 1, self.umask, data[name])
            elif self.type[name] == 'table':
                if not isinstance(data[name], dict):
                    raise QueueError("unexpected table data in %s: %r" %
                                     (name, data[name]))
                _file_write('%s/%s' % (temp, name), 1, self.umask,
                            _hash2string(data[name]))
            else:
                raise QueueError("unexpected data type in %s: %r" %
                                 (name, self.type[name]))
        for name in self.mandatory.keys():
            if name not in data:
                raise QueueError("missing mandatory data: %s" % name)
        while True:
            name = '%s/%s' % (self._insertion_directory(), _name(self.rndhex))
            path = '%s/%s' % (self.path, name)
            try:
                os.rename(temp, path)
                return name
            except Exception:
                error = sys.exc_info()[1]
                if error.errno != errno.ENOTEMPTY and \
                        error.errno != errno.EEXIST:
                    raise OSError("cannot rename(%s, %s): %s" %
                                  (temp, path, error))
Example #8
0
    def remove(self, ename):
        """Remove locked element from the queue.

        Arguments:
            ename - name of an element

        Raise:
            QueueError - invalid element name; element not locked;
                         unexpected file in the element directory

            OSError    - can't rename/remove a file/directory

        Note:
            doesn't return anything explicitly (i.e. returns NoneType)
            or fails
        """
        _check_element(ename)
        if not self._is_locked(ename):
            raise QueueError("cannot remove %s: not locked" % ename)
        # move the element out of its intermediate directory
        path = '%s/%s' % (self.path, ename)
        while True:
            temp = '%s/%s/%s' % (self.path, OBSOLETE_DIRECTORY,
                                 _name(self.rndhex))
            try:
                os.rename(path, temp)
                break
            except Exception:
                error = sys.exc_info()[1]
                if error.errno != errno.ENOTEMPTY and \
                        error.errno != errno.EEXIST:
                    raise OSError("cannot rename(%s, %s): %s" %
                                  (ename, temp, error))
                    # RACE: the target directory was already present...
        # remove the data files
        for name in _directory_contents(temp):
            if name == LOCKED_DIRECTORY:
                continue
            if not _FileRegexp.match(name):
                raise QueueError("unexpected file in %s: %s" % (temp, name))
            path = '%s/%s' % (temp, name)
            try:
                os.unlink(path)
            except Exception:
                error = sys.exc_info()[1]
                raise OSError("cannot unlink(%s): %s" % (path, error))
        # remove the locked directory
        path = '%s/%s' % (temp, LOCKED_DIRECTORY)
        while True:
            try:
                os.rmdir(path)
            except Exception:
                error = sys.exc_info()[1]
                raise OSError("cannot rmdir(%s): %s" % (path, error))
            try:
                os.rmdir(temp)
                return
            except Exception:
                error = sys.exc_info()[1]
                if error.errno != errno.ENOTEMPTY and \
                        error.errno != errno.EEXIST:
                    raise OSError("cannot rmdir(%s): %s" % (temp, error))