File: prereq.py

package info (click to toggle)
python-stem 1.7.1-1.1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, sid
  • size: 5,768 kB
  • sloc: python: 29,441; java: 312; makefile: 125; sh: 17
file content (258 lines) | stat: -rw-r--r-- 7,322 bytes parent folder | download | duplicates (3)
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# Copyright 2012-2018, Damian Johnson and The Tor Project
# See LICENSE for licensing information

"""
Checks for stem dependencies. We require python 2.6 or greater (including the
3.x series), but note we'll be bumping our requirements to python 2.7 in stem
2.0. Other requirements for complete functionality are...

* cryptography module

  * validating descriptor signature integrity

::

  check_requirements - checks for minimum requirements for running stem
  is_python_3 - checks if python 3.0 or later is available
  is_sqlite_available - checks if the sqlite3 module is available
  is_crypto_available - checks if the cryptography module is available
  is_zstd_available - checks if the zstd module is available
  is_lzma_available - checks if the lzma module is available
  is_mock_available - checks if the mock module is available
"""

import functools
import inspect
import platform
import sys

CRYPTO_UNAVAILABLE = "Unable to import the cryptography module. Because of this we'll be unable to verify descriptor signature integrity. You can get cryptography from: https://pypi.python.org/pypi/cryptography"
ZSTD_UNAVAILABLE = 'ZSTD compression requires the zstandard module (https://pypi.python.org/pypi/zstandard)'
LZMA_UNAVAILABLE = 'LZMA compression requires the lzma module (https://docs.python.org/3/library/lzma.html)'
PYNACL_UNAVAILABLE = "Unable to import the pynacl module. Because of this we'll be unable to verify descriptor ed25519 certificate integrity. You can get pynacl from https://pypi.python.org/pypi/PyNaCl/"


def check_requirements():
  """
  Checks that we meet the minimum requirements to run stem. If we don't then
  this raises an ImportError with the issue.

  :raises: **ImportError** with the problem if we don't meet stem's
    requirements
  """

  major_version, minor_version = sys.version_info[0:2]

  if major_version < 2 or (major_version == 2 and minor_version < 6):
    raise ImportError('stem requires python version 2.6 or greater')


def _is_python_26():
  """
  Checks if we're running python 2.6. This isn't for users as it'll be removed
  in stem 2.0 (when python 2.6 support goes away).

  :returns: **True** if we're running python 2.6, **False** otherwise
  """

  major_version, minor_version = sys.version_info[0:2]

  return major_version == 2 and minor_version == 6


def is_python_27():
  """
  Checks if we're running python 2.7 or above (including the 3.x series).

  .. deprecated:: 1.5.0
     Function lacks much utility and will be eventually removed.

  :returns: **True** if we meet this requirement and **False** otherwise
  """

  major_version, minor_version = sys.version_info[0:2]

  return major_version > 2 or (major_version == 2 and minor_version >= 7)


def is_python_3():
  """
  Checks if we're in the 3.0 - 3.x range.

  :returns: **True** if we meet this requirement and **False** otherwise
  """

  return sys.version_info[0] == 3


def is_pypy():
  """
  Checks if we're running PyPy.

  .. versionadded:: 1.7.0

  :returns: **True** if running pypy, **False** otherwise
  """

  return platform.python_implementation() == 'PyPy'


def is_sqlite_available():
  """
  Checks if the sqlite3 module is available. Usually this is built in, but some
  platforms such as FreeBSD and Gentoo exclude it by default.

  .. versionadded:: 1.6.0

  :returns: **True** if we can use the sqlite3 module and **False** otherwise
  """

  try:
    import sqlite3
    return True
  except ImportError:
    return False


def is_crypto_available():
  """
  Checks if the cryptography functions we use are available. This is used for
  verifying relay descriptor signatures.

  :returns: **True** if we can use the cryptography module and **False**
    otherwise
  """

  try:
    from cryptography.utils import int_from_bytes, int_to_bytes
    from cryptography.hazmat.backends import default_backend
    from cryptography.hazmat.primitives.asymmetric import rsa
    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
    from cryptography.hazmat.primitives.serialization import load_der_public_key

    if not hasattr(rsa.RSAPrivateKey, 'sign'):
      raise ImportError()

    return True
  except ImportError:
    from stem.util import log
    log.log_once('stem.prereq.is_crypto_available', log.INFO, CRYPTO_UNAVAILABLE)
    return False


def is_zstd_available():
  """
  Checks if the `zstd module <https://pypi.python.org/pypi/zstandard>`_ is
  available.

  .. versionadded:: 1.7.0

  :returns: **True** if we can use the zstd module and **False** otherwise
  """

  try:
    # Unfortunately the zstandard module uses the same namespace as another
    # zstd module (https://pypi.python.org/pypi/zstd), so we need to
    # differentiate them.

    import zstd
    return hasattr(zstd, 'ZstdDecompressor')
  except ImportError:
    from stem.util import log
    log.log_once('stem.prereq.is_zstd_available', log.INFO, ZSTD_UNAVAILABLE)
    return False


def is_lzma_available():
  """
  Checks if the `lzma module <https://docs.python.org/3/library/lzma.html>`_ is
  available. This was added as a builtin in Python 3.3.

  .. versionadded:: 1.7.0

  :returns: **True** if we can use the lzma module and **False** otherwise
  """

  try:
    import lzma
    return True
  except ImportError:
    from stem.util import log
    log.log_once('stem.prereq.is_lzma_available', log.INFO, LZMA_UNAVAILABLE)
    return False


def is_mock_available():
  """
  Checks if the mock module is available. In python 3.3 and up it is a builtin
  unittest module, but before this it needed to be `installed separately
  <https://pypi.python.org/pypi/mock/>`_. Imports should be as follows....

  ::

    try:
      # added in python 3.3
      from unittest.mock import Mock
    except ImportError:
      from mock import Mock

  :returns: **True** if the mock module is available and **False** otherwise
  """

  try:
    # checks for python 3.3 version
    import unittest.mock
    return True
  except ImportError:
    pass

  try:
    import mock

    # check for mock's patch.dict() which was introduced in version 0.7.0

    if not hasattr(mock.patch, 'dict'):
      raise ImportError()

    # check for mock's new_callable argument for patch() which was introduced in version 0.8.0

    if 'new_callable' not in inspect.getargspec(mock.patch).args:
      raise ImportError()

    return True
  except ImportError:
    return False


def _is_lru_cache_available():
  """
  Functools added lru_cache to the standard library in Python 3.2. Prior to
  this using a bundled implementation. We're also using this with Python 3.5
  due to a buggy implementation. (:trac:`26412`)
  """

  major_version, minor_version = sys.version_info[0:2]

  if major_version == 3 and minor_version == 5:
    return False
  else:
    return hasattr(functools, 'lru_cache')


def _is_pynacl_available():
  """
  Checks if the pynacl functions we use are available. This is used for
  verifying ed25519 certificates in relay descriptor signatures.

  :returns: **True** if we can use pynacl and **False** otherwise
  """

  from stem.util import log

  try:
    from nacl import encoding
    from nacl import signing
    return True
  except ImportError:
    log.log_once('stem.prereq._is_pynacl_available', log.INFO, PYNACL_UNAVAILABLE)
    return False