summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Utils.py
blob: 247e4f16b6d7f88955d6170d6b76133a93376c93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
""" Miscellaneous useful utility functions, classes, etc., that are
used by both client and server.  Stuff that doesn't fit anywhere
else. """

import fcntl
from Bcfg2.Compat import any  # pylint: disable=W0622


class PackedDigitRange(object):
    """ Representation of a set of integer ranges. A range is
    described by a comma-delimited string of integers and ranges,
    e.g.::

        1,10-12,15-20

    Ranges are inclusive on both bounds, and may include 0.  Negative
    numbers are not supported."""

    def __init__(self, *ranges):
        """ May be instantiated in one of two ways::

            PackedDigitRange(<comma-delimited list of ranges>)

        Or::

            PackedDigitRange(<int_or_range>[, <int_or_range>[, ...]])

        E.g., both of the following are valid::

            PackedDigitRange("1-5,7, 10-12")
            PackedDigitRange("1-5", 7, "10-12")
        """
        self.ranges = []
        self.ints = []
        self.str = ",".join(str(r) for r in ranges)
        if len(ranges) == 1 and "," in ranges[0]:
            ranges = ranges[0].split(",")
        for item in ranges:
            item = str(item).strip()
            if item.endswith("-"):
                self.ranges.append((int(item[:-1]), None))
            elif '-' in str(item):
                self.ranges.append(tuple(int(x) for x in item.split('-')))
            else:
                self.ints.append(int(item))

    def includes(self, other):
        """ Return True if ``other`` is included in this range.
        Functionally equivalent to ``other in range``, which should be
        used instead. """
        return other in self

    def __contains__(self, other):
        other = int(other)
        if other in self.ints:
            return True
        return any((end is None and other >= start) or
                   (end is not None and other >= start and other <= end)
                   for start, end in self.ranges)

    def __repr__(self):
        return "%s:%s" % (self.__class__.__name__, str(self))

    def __str__(self):
        return "[%s]" % self.str

    def __len__(self):
        return sum(r[1] - r[0] + 1 for r in self.ranges) + len(self.ints)


def locked(fd):
    """ Acquire a lock on a file """
    try:
        fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except IOError:
        return True
    return False