Parse bridge blocking info from SQL database.
[tor-bridgedb.git] / bridgedb / parse / versions.py
blob832969ef4445ccdf177c6a72946fb0b7621ba489
1 # -*- coding: utf-8 ; test-case-name: bridgedb.test.test_parse_versions ; -*-
3 # This file is part of BridgeDB, a Tor bridge distribution system.
5 # :authors: Isis Lovecruft 0xA3ADB67A2CDB8B35 <isis@torproject.org>
6 # please also see AUTHORS file
7 # :copyright: (c) 2014-2017 Isis Lovecruft
8 # (c) 2007-2017, The Tor Project, Inc.
9 # (c) 2007-2017, all entities within the AUTHORS file
10 # :license: see included LICENSE for information
12 """Parsers for Tor version number strings.
14 .. py:module:: bridgedb.parse.versions
15 :synopsis: Parsers for Tor version number strings.
17 bridgedb.parse.versions
18 =======================
21 Version - Holds, parses, and does comparison operations for package
22 version numbers.
24 """
26 import logging
28 import stem.version
30 from twisted import version as _txversion
32 # The twisted.python.util.Version class was moved in Twisted==14.0.0 to
33 # twisted.python.versions.Version:
34 if _txversion.major >= 14:
35 from twisted.python.versions import Version as _Version
36 else:
37 from twisted.python.util import Version as _Version
40 class InvalidVersionStringFormat(ValueError):
41 """Raised when a version string is not in a parseable format."""
44 class Version(_Version):
45 """Holds, parses, and does comparison operations for version numbers.
47 :attr str package: The package name, if available.
48 :attr int major: The major version number.
49 :attr int minor: The minor version number.
50 :attr int micro: The micro version number.
51 :attr str prerelease: The **prerelease** specifier isn't always present,
52 though when it is, it's usually separated from the main
53 ``major.minor.micro`` part of the version string with a ``-``, ``+``,
54 or ``#`` character. Sometimes the **prerelease** is another number,
55 although often it can be a word specifying the release state,
56 i.e. ``+alpha``, ``-rc2``, etc.
57 """
59 def __init__(self, version, package=None):
60 """Create a version object.
62 Comparisons may be computed between instances of :class:`Version`s.
64 >>> from bridgedb.parse.versions import Version
65 >>> v1 = Version("0.2.3.25", package="tor")
66 >>> v1.base()
67 '0.2.3.25'
68 >>> v1.package
69 'tor'
70 >>> v2 = Version("0.2.5.1-alpha", package="tor")
71 >>> v2
72 Version(package=tor, major=0, minor=2, micro=5, prerelease=1-alpha)
73 >>> v1 == v2
74 False
75 >>> v2 > v1
76 True
78 :param str version: A Tor version string specifier, i.e. one taken
79 from either the ``client-versions`` or ``server-versions`` lines
80 within a Tor ``cached-consensus`` file.
81 :param str package: The package or program which we are creating a
82 version number for.
83 """
84 if version.find('.') == -1:
85 raise InvalidVersionStringFormat(
86 "Invalid delimiters in version string: %r" % version)
88 package = package if package is not None else str()
89 major, minor, micro = [int() for _ in range(3)]
90 prerelease = str()
91 components = version.split('.')
92 if len(components) > 0:
93 try:
94 prerelease = str(components.pop())
95 micro = int(components.pop())
96 minor = int(components.pop())
97 major = int(components.pop())
98 except IndexError:
99 pass
100 super(Version, self).__init__(package, major, minor, micro, prerelease)
102 def base(self):
103 """Get the base version number (with prerelease).
105 :rtype: string
106 :returns: A version number, without the package/program name, and with
107 the prefix (if available). For example: '0.2.5.1-alpha'.
109 pre = self.getPrefixedPrerelease()
110 return '%s.%s.%s%s' % (self.major, self.minor, self.micro, pre)
112 def getPrefixedPrerelease(self, separator='.'):
113 """Get the prerelease string, prefixed by the separator ``prefix``.
115 :param string separator: The separator to use between the rest of the
116 version string and the :attr:`prerelease` string.
117 :rtype: string
118 :returns: The separator plus the ``prefix``, i.e. '.1-alpha'.
120 pre = ''
121 if self.prerelease is not None:
122 pre = separator + self.prerelease
123 return pre
125 def __repr__(self):
126 prerelease = self.getPrefixedPrerelease('')
127 return '%s(package=%s, major=%s, minor=%s, micro=%s, prerelease=%s)' \
128 % (str(self.__class__.__name__),
129 str(self.package),
130 str(self.major),
131 str(self.minor),
132 str(self.micro),
133 str(prerelease))
136 def parseVersionsList(versions_list):
137 """Turn the given version strings into stem objects.
139 :param list versions_list: A list of tuples. Each tuple contains a minimum
140 and maximum version number as strings.
141 :rtype: list
142 :returns: A list of tuples. Each tuple contains a minimum and maximum
143 version number as :class:`stem.version.Version` objects.
145 parsed = []
146 for v1, v2 in versions_list:
147 # We're dealing with an already-parsed version list.
148 if isinstance(v1, stem.version.Version):
149 return versions_list
150 try:
151 parsed.append(tuple([stem.version.Version(v1),
152 stem.version.Version(v2)]))
153 except ValueError:
154 logging.error("Couldn't parse BLACKLISTED_TOR_VERSIONS; "
155 "probably because of badly formatted "
156 "configuration file. Ignoring config option.")
157 return []
158 return parsed