[tor-commits] [stem/master] Replace stem.prereq module

atagar at torproject.org atagar at torproject.org
Tue Feb 18 07:57:23 UTC 2020


commit 75403ace36d39bc003955e5aec91b53c66c2e472
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun Feb 16 17:19:52 2020 -0800

    Replace stem.prereq module
    
    Our prereq module provides dependency checks, mostly for python versioning
    (now moot) and the cryptography module.
    
    It is not only cleaner but less error prone to perform dependency checks down
    where their imports are performed rather than upfront. This way import changes
    can't fall out of sync with our upfront checks.
---
 run_tests.py                              |   9 +-
 stem/__init__.py                          |   1 -
 stem/client/__init__.py                   |   8 +-
 stem/client/datatype.py                   |   1 -
 stem/descriptor/__init__.py               |  36 +++---
 stem/descriptor/certificate.py            |   9 +-
 stem/descriptor/extrainfo_descriptor.py   |   1 -
 stem/descriptor/hidden_service.py         | 115 +++++++++----------
 stem/descriptor/microdescriptor.py        |   1 -
 stem/descriptor/remote.py                 |  13 ++-
 stem/descriptor/router_status_entry.py    |   1 -
 stem/descriptor/server_descriptor.py      |  27 +++--
 stem/exit_policy.py                       |   1 -
 stem/interpreter/__init__.py              |   1 -
 stem/interpreter/autocomplete.py          |   2 -
 stem/interpreter/help.py                  |   2 -
 stem/manual.py                            |  22 ++--
 stem/prereq.py                            | 179 ------------------------------
 stem/process.py                           |   1 -
 stem/response/events.py                   |   1 -
 stem/response/protocolinfo.py             |   1 -
 stem/socket.py                            |   1 -
 stem/util/__init__.py                     |  16 +--
 stem/util/conf.py                         |   2 -
 stem/util/log.py                          |   1 -
 stem/util/proc.py                         |   1 -
 stem/util/str_tools.py                    |   1 -
 stem/util/system.py                       |   3 +-
 stem/util/test_tools.py                   |   1 -
 stem/version.py                           |   1 -
 test/integ/installation.py                |   4 +-
 test/integ/process.py                     |   1 -
 test/integ/util/system.py                 |   4 +-
 test/integ/version.py                     |   1 -
 test/require.py                           |  20 +++-
 test/runner.py                            |   1 -
 test/settings.cfg                         |  24 ----
 test/task.py                              |   4 +-
 test/unit/descriptor/certificate.py       |   7 +-
 test/unit/descriptor/collector.py         |   2 +-
 test/unit/descriptor/hidden_service_v2.py |   1 -
 test/unit/descriptor/hidden_service_v3.py |  36 ++----
 test/unit/descriptor/remote.py            |  23 +---
 test/unit/descriptor/server_descriptor.py |  13 ---
 test/unit/directory/authority.py          |   1 -
 test/unit/manual.py                       |   1 -
 test/unit/tutorial.py                     |   1 -
 test/unit/tutorial_examples.py            |   1 -
 test/unit/util/system.py                  |   5 +-
 49 files changed, 172 insertions(+), 437 deletions(-)

diff --git a/run_tests.py b/run_tests.py
index 16fbe267..c9196e18 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -21,7 +21,6 @@ import time
 import traceback
 import unittest
 
-import stem.prereq
 import stem.util.conf
 import stem.util.log
 import stem.util.system
@@ -179,10 +178,10 @@ def _get_tests(modules, module_prefixes, exclude):
 def main():
   start_time = time.time()
 
-  try:
-    stem.prereq.check_requirements()
-  except ImportError as exc:
-    println('%s\n' % exc)
+  major_version, minor_version = sys.version_info[0:2]
+
+  if major_version < 3 or (major_version == 3 and minor_version < 6):
+    println('stem requires python version 3.6 or greater\n')
     sys.exit(1)
 
   signal.signal(signal.SIGABRT, log_traceback)
diff --git a/stem/__init__.py b/stem/__init__.py
index 3600e920..907156fe 100644
--- a/stem/__init__.py
+++ b/stem/__init__.py
@@ -522,7 +522,6 @@ __all__ = [
   'control',
   'directory',
   'exit_policy',
-  'prereq',
   'process',
   'socket',
   'version',
diff --git a/stem/client/__init__.py b/stem/client/__init__.py
index 2abeac88..7456726a 100644
--- a/stem/client/__init__.py
+++ b/stem/client/__init__.py
@@ -303,12 +303,12 @@ class Circuit(object):
   """
 
   def __init__(self, relay, circ_id, kdf):
-    if not stem.prereq.is_crypto_available():
+    try:
+      from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+      from cryptography.hazmat.backends import default_backend
+    except ImportError:
       raise ImportError('Circuit construction requires the cryptography module')
 
-    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
-    from cryptography.hazmat.backends import default_backend
-
     ctr = modes.CTR(ZERO * (algorithms.AES.block_size // 8))
 
     self.relay = relay
diff --git a/stem/client/datatype.py b/stem/client/datatype.py
index 1db46a28..4f7110e9 100644
--- a/stem/client/datatype.py
+++ b/stem/client/datatype.py
@@ -140,7 +140,6 @@ import collections
 import hashlib
 
 import stem.client.cell
-import stem.prereq
 import stem.util
 import stem.util.connection
 import stem.util.enum
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index 556970c3..ff273405 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -115,7 +115,6 @@ import re
 import string
 import tarfile
 
-import stem.prereq
 import stem.util
 import stem.util.enum
 import stem.util.str_tools
@@ -205,6 +204,13 @@ class _Compression(object):
       try:
         self._module = __import__(module)
         self.available = True
+
+        # Unfortunately the zstandard module uses the same namespace as another
+        # zstd module (https://pypi.org/project/zstd/), so we need to
+        # differentiate them.
+
+        if module == 'zstd' and not hasattr(self._module, 'ZstdDecompressor'):
+          raise ImportError()
       except ImportError:
         self._module = None
         self.available = False
@@ -1033,13 +1039,13 @@ class Descriptor(object):
     :raises: ValueError if unable to provide a validly signed digest
     """
 
-    if not stem.prereq.is_crypto_available():
+    try:
+      from cryptography.hazmat.backends import default_backend
+      from cryptography.hazmat.primitives.serialization import load_der_public_key
+      from cryptography.utils import int_to_bytes, int_from_bytes
+    except ImportError:
       raise ValueError('Generating the signed digest requires the cryptography module')
 
-    from cryptography.hazmat.backends import default_backend
-    from cryptography.hazmat.primitives.serialization import load_der_public_key
-    from cryptography.utils import int_to_bytes, int_from_bytes
-
     key = load_der_public_key(_bytes_for_block(signing_key), default_backend())
     modulus = key.public_numbers().n
     public_exponent = key.public_numbers().e
@@ -1334,13 +1340,13 @@ def create_signing_key(private_key = None):
   :raises: **ImportError** if the cryptography module is unavailable
   """
 
-  if not stem.prereq.is_crypto_available():
+  try:
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives import serialization
+    from cryptography.hazmat.primitives.asymmetric import rsa
+  except ImportError:
     raise ImportError('Signing requires the cryptography module')
 
-  from cryptography.hazmat.backends import default_backend
-  from cryptography.hazmat.primitives import serialization
-  from cryptography.hazmat.primitives.asymmetric import rsa
-
   if private_key is None:
     private_key = rsa.generate_private_key(
       public_exponent = 65537,
@@ -1381,12 +1387,12 @@ def _append_router_signature(content, private_key):
   :returns: **bytes** with the signed descriptor content
   """
 
-  if not stem.prereq.is_crypto_available():
+  try:
+    from cryptography.hazmat.primitives import hashes
+    from cryptography.hazmat.primitives.asymmetric import padding
+  except ImportError:
     raise ImportError('Signing requires the cryptography module')
 
-  from cryptography.hazmat.primitives import hashes
-  from cryptography.hazmat.primitives.asymmetric import padding
-
   signature = base64.b64encode(private_key.sign(content, padding.PKCS1v15(), hashes.SHA1()))
   return content + b'\n'.join([b'-----BEGIN SIGNATURE-----'] + stem.util.str_tools._split_by_length(signature, 64) + [b'-----END SIGNATURE-----\n'])
 
diff --git a/stem/descriptor/certificate.py b/stem/descriptor/certificate.py
index fed16bac..d7c0743d 100644
--- a/stem/descriptor/certificate.py
+++ b/stem/descriptor/certificate.py
@@ -59,7 +59,6 @@ import hashlib
 import re
 
 import stem.descriptor.hidden_service
-import stem.prereq
 import stem.util
 import stem.util.enum
 import stem.util.str_tools
@@ -372,7 +371,10 @@ class Ed25519CertificateV1(Ed25519Certificate):
 
     import stem.descriptor.server_descriptor
 
-    if not stem.prereq.is_crypto_available(ed25519 = True):
+    try:
+      from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
+      from cryptography.exceptions import InvalidSignature
+    except ImportError:
       raise ImportError('Certificate validation requires the cryptography module and ed25519 support')
 
     if isinstance(descriptor, stem.descriptor.server_descriptor.RelayDescriptor):
@@ -386,9 +388,6 @@ class Ed25519CertificateV1(Ed25519Certificate):
     else:
       raise TypeError('Certificate validation only supported for server and hidden service descriptors, not %s' % type(descriptor).__name__)
 
-    from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
-    from cryptography.exceptions import InvalidSignature
-
     try:
       key = Ed25519PublicKey.from_public_bytes(self.key)
       key.verify(signature, signed_content)
diff --git a/stem/descriptor/extrainfo_descriptor.py b/stem/descriptor/extrainfo_descriptor.py
index c9125efa..dcca9d14 100644
--- a/stem/descriptor/extrainfo_descriptor.py
+++ b/stem/descriptor/extrainfo_descriptor.py
@@ -71,7 +71,6 @@ import functools
 import hashlib
 import re
 
-import stem.prereq
 import stem.util.connection
 import stem.util.enum
 import stem.util.str_tools
diff --git a/stem/descriptor/hidden_service.py b/stem/descriptor/hidden_service.py
index 64daee16..980222a3 100644
--- a/stem/descriptor/hidden_service.py
+++ b/stem/descriptor/hidden_service.py
@@ -44,7 +44,6 @@ import time
 
 import stem.client.datatype
 import stem.descriptor.certificate
-import stem.prereq
 import stem.util
 import stem.util.connection
 import stem.util.str_tools
@@ -218,9 +217,7 @@ class IntroductionPointV3(collections.namedtuple('IntroductionPointV3', ['link_s
     :raises: **ValueError** if the address, port, or keys are malformed
     """
 
-    if not stem.prereq.is_crypto_available(ed25519 = True):
-      raise ImportError('Introduction point creation requires the cryptography module ed25519 support')
-    elif not stem.util.connection.is_valid_port(port):
+    if not stem.util.connection.is_valid_port(port):
       raise ValueError("'%s' is an invalid port" % port)
 
     if stem.util.connection.is_valid_ipv4_address(address):
@@ -250,12 +247,12 @@ class IntroductionPointV3(collections.namedtuple('IntroductionPointV3', ['link_s
     :raises: **ValueError** if the address, port, or keys are malformed
     """
 
-    if not stem.prereq.is_crypto_available(ed25519 = True):
+    try:
+      from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
+      from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
+    except ImportError:
       raise ImportError('Introduction point creation requires the cryptography module ed25519 support')
 
-    from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
-    from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
-
     if expiration is None:
       expiration = datetime.datetime.utcnow() + datetime.timedelta(hours = stem.descriptor.certificate.DEFAULT_EXPIRATION_HOURS)
 
@@ -354,7 +351,11 @@ class IntroductionPointV3(collections.namedtuple('IntroductionPointV3', ['link_s
   def _key_as(value, x25519 = False, ed25519 = False):
     if value is None or (not x25519 and not ed25519):
       return value
-    elif not stem.prereq.is_crypto_available():
+
+    try:
+      from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PublicKey
+      from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
+    except ImportError:
       raise ImportError('cryptography module unavailable')
 
     if x25519:
@@ -364,14 +365,9 @@ class IntroductionPointV3(collections.namedtuple('IntroductionPointV3', ['link_s
 
         raise EnvironmentError('OpenSSL x25519 unsupported')
 
-      from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PublicKey
       return X25519PublicKey.from_public_bytes(base64.b64decode(value))
 
     if ed25519:
-      if not stem.prereq.is_crypto_available(ed25519 = True):
-        raise EnvironmentError('cryptography ed25519 unsupported')
-
-      from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
       return Ed25519PublicKey.from_public_bytes(value)
 
   @staticmethod
@@ -719,13 +715,16 @@ class HiddenServiceDescriptorV2(HiddenServiceDescriptor):
 
       self._parse(entries, validate)
 
-      if not skip_crypto_validation and stem.prereq.is_crypto_available():
-        signed_digest = self._digest_for_signature(self.permanent_key, self.signature)
-        digest_content = self._content_range('rendezvous-service-descriptor ', '\nsignature\n')
-        content_digest = hashlib.sha1(digest_content).hexdigest().upper()
+      if not skip_crypto_validation:
+        try:
+          signed_digest = self._digest_for_signature(self.permanent_key, self.signature)
+          digest_content = self._content_range('rendezvous-service-descriptor ', '\nsignature\n')
+          content_digest = hashlib.sha1(digest_content).hexdigest().upper()
 
-        if signed_digest != content_digest:
-          raise ValueError('Decrypted digest does not match local digest (calculated: %s, local: %s)' % (signed_digest, content_digest))
+          if signed_digest != content_digest:
+            raise ValueError('Decrypted digest does not match local digest (calculated: %s, local: %s)' % (signed_digest, content_digest))
+        except ImportError:
+          pass  # cryptography module unavailable
     else:
       self._entries = entries
 
@@ -746,9 +745,6 @@ class HiddenServiceDescriptorV2(HiddenServiceDescriptor):
     if not content:
       return []
     elif authentication_cookie:
-      if not stem.prereq.is_crypto_available():
-        raise DecryptionFailure('Decrypting introduction-points requires the cryptography module')
-
       try:
         authentication_cookie = stem.util.str_tools._decode_b64(authentication_cookie)
       except TypeError as exc:
@@ -772,8 +768,11 @@ class HiddenServiceDescriptorV2(HiddenServiceDescriptor):
 
   @staticmethod
   def _decrypt_basic_auth(content, authentication_cookie):
-    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
-    from cryptography.hazmat.backends import default_backend
+    try:
+      from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+      from cryptography.hazmat.backends import default_backend
+    except ImportError:
+      raise DecryptionFailure('Decrypting introduction-points requires the cryptography module')
 
     try:
       client_blocks = int(binascii.hexlify(content[1:2]), 16)
@@ -816,8 +815,11 @@ class HiddenServiceDescriptorV2(HiddenServiceDescriptor):
 
   @staticmethod
   def _decrypt_stealth_auth(content, authentication_cookie):
-    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
-    from cryptography.hazmat.backends import default_backend
+    try:
+      from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+      from cryptography.hazmat.backends import default_backend
+    except ImportError:
+      raise DecryptionFailure('Decrypting introduction-points requires the cryptography module')
 
     # byte 1 = authentication type, 2-17 = input vector, 18 on = encrypted content
     iv, encrypted = content[1:17], content[17:]
@@ -965,14 +967,13 @@ class HiddenServiceDescriptorV3(HiddenServiceDescriptor):
       * **ImportError** if cryptography is unavailable
     """
 
-    if not stem.prereq.is_crypto_available(ed25519 = True):
+    try:
+      from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
+    except ImportError:
       raise ImportError('Hidden service descriptor creation requires cryptography version 2.6')
-    elif not stem.prereq._is_sha3_available():
-      raise ImportError('Hidden service descriptor creation requires python 3.6+ or the pysha3 module (https://pypi.org/project/pysha3/)')
-    elif blinding_nonce and len(blinding_nonce) != 32:
-      raise ValueError('Blinding nonce must be 32 bytes, but was %i' % len(blinding_nonce))
 
-    from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
+    if blinding_nonce and len(blinding_nonce) != 32:
+      raise ValueError('Blinding nonce must be 32 bytes, but was %i' % len(blinding_nonce))
 
     inner_layer = inner_layer if inner_layer else InnerLayer.create(exclude = exclude)
     identity_key = identity_key if identity_key else Ed25519PrivateKey.generate()
@@ -1038,8 +1039,11 @@ class HiddenServiceDescriptorV3(HiddenServiceDescriptor):
 
       self._parse(entries, validate)
 
-      if self.signing_cert and stem.prereq.is_crypto_available(ed25519 = True):
-        self.signing_cert.validate(self)
+      if self.signing_cert:
+        try:
+          self.signing_cert.validate(self)
+        except ImportError:
+          pass  # cryptography module unavailable
     else:
       self._entries = entries
 
@@ -1059,22 +1063,20 @@ class HiddenServiceDescriptorV3(HiddenServiceDescriptor):
       * **ValueError** if unable to decrypt or validation fails
     """
 
-    if not stem.prereq.is_crypto_available(ed25519 = True):
-      raise ImportError('Hidden service descriptor decryption requires cryptography version 2.6')
-    elif not stem.prereq._is_sha3_available():
-      raise ImportError('Hidden service descriptor decryption requires python 3.6+ or the pysha3 module (https://pypi.org/project/pysha3/)')
-
     if self._inner_layer is None:
-      blinded_key = self.signing_cert.signing_key() if self.signing_cert else None
+      try:
+        blinded_key = self.signing_cert.signing_key() if self.signing_cert else None
 
-      if not blinded_key:
-        raise ValueError('No signing key is present')
+        if not blinded_key:
+          raise ValueError('No signing key is present')
 
-      identity_public_key = HiddenServiceDescriptorV3.identity_key_from_address(onion_address)
-      subcredential = HiddenServiceDescriptorV3._subcredential(identity_public_key, blinded_key)
+        identity_public_key = HiddenServiceDescriptorV3.identity_key_from_address(onion_address)
+        subcredential = HiddenServiceDescriptorV3._subcredential(identity_public_key, blinded_key)
 
-      outer_layer = OuterLayer._decrypt(self.superencrypted, self.revision_counter, subcredential, blinded_key)
-      self._inner_layer = InnerLayer._decrypt(outer_layer, self.revision_counter, subcredential, blinded_key)
+        outer_layer = OuterLayer._decrypt(self.superencrypted, self.revision_counter, subcredential, blinded_key)
+        self._inner_layer = InnerLayer._decrypt(outer_layer, self.revision_counter, subcredential, blinded_key)
+      except ImportError:
+        raise ImportError('Hidden service descriptor decryption requires cryptography version 2.6')
 
     return self._inner_layer
 
@@ -1092,9 +1094,6 @@ class HiddenServiceDescriptorV3(HiddenServiceDescriptor):
     :raises: **ImportError** if sha3 unsupported
     """
 
-    if not stem.prereq._is_sha3_available():
-      raise ImportError('Hidden service address conversion requires python 3.6+ or the pysha3 module (https://pypi.org/project/pysha3/)')
-
     key = stem.util._pubkey_bytes(key)  # normalize key into bytes
 
     version = stem.client.datatype.Size.CHAR.pack(3)
@@ -1117,9 +1116,6 @@ class HiddenServiceDescriptorV3(HiddenServiceDescriptor):
       * **ValueError** if address malformed or checksum is invalid
     """
 
-    if not stem.prereq._is_sha3_available():
-      raise ImportError('Hidden service address conversion requires python 3.6+ or the pysha3 module (https://pypi.org/project/pysha3/)')
-
     if onion_address.endswith('.onion'):
       onion_address = onion_address[:-6]
 
@@ -1202,15 +1198,14 @@ class OuterLayer(Descriptor):
 
   @classmethod
   def content(cls, attr = None, exclude = (), validate = True, sign = False, inner_layer = None, revision_counter = None, authorized_clients = None, subcredential = None, blinded_key = None):
-    if not stem.prereq.is_crypto_available(ed25519 = True):
+    try:
+      from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
+      from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
+    except ImportError:
       raise ImportError('Hidden service layer creation requires cryptography version 2.6')
-    elif not stem.prereq._is_sha3_available():
-      raise ImportError('Hidden service layer creation requires python 3.6+ or the pysha3 module (https://pypi.org/project/pysha3/)')
-    elif authorized_clients and 'auth-client' in attr:
-      raise ValueError('Authorized clients cannot be specified through both attr and authorized_clients')
 
-    from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
-    from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
+    if authorized_clients and 'auth-client' in attr:
+      raise ValueError('Authorized clients cannot be specified through both attr and authorized_clients')
 
     inner_layer = inner_layer if inner_layer else InnerLayer.create()
     revision_counter = revision_counter if revision_counter else 1
diff --git a/stem/descriptor/microdescriptor.py b/stem/descriptor/microdescriptor.py
index 42d115d6..c62a3d0d 100644
--- a/stem/descriptor/microdescriptor.py
+++ b/stem/descriptor/microdescriptor.py
@@ -68,7 +68,6 @@ import functools
 import hashlib
 
 import stem.exit_policy
-import stem.prereq
 
 from stem.descriptor import (
   Descriptor,
diff --git a/stem/descriptor/remote.py b/stem/descriptor/remote.py
index 0f411a71..24eb7b9b 100644
--- a/stem/descriptor/remote.py
+++ b/stem/descriptor/remote.py
@@ -96,7 +96,6 @@ import stem.client
 import stem.descriptor
 import stem.descriptor.networkstatus
 import stem.directory
-import stem.prereq
 import stem.util.enum
 import stem.util.tor_tools
 
@@ -383,10 +382,10 @@ class Query(object):
     elif not isinstance(compression, list):
       compression = [compression]  # caller provided only a single option
 
-    if Compression.ZSTD in compression and not stem.prereq.is_zstd_available():
+    if Compression.ZSTD in compression and not Compression.ZSTD.available:
       compression.remove(Compression.ZSTD)
 
-    if Compression.LZMA in compression and not stem.prereq.is_lzma_available():
+    if Compression.LZMA in compression and not Compression.LZMA.available:
       compression.remove(Compression.LZMA)
 
     if not compression:
@@ -765,10 +764,14 @@ class DescriptorDownloader(object):
     # if we're performing validation then check that it's signed by the
     # authority key certificates
 
-    if consensus_query.validate and consensus_query.document_handler == stem.descriptor.DocumentHandler.DOCUMENT and stem.prereq.is_crypto_available():
+    if consensus_query.validate and consensus_query.document_handler == stem.descriptor.DocumentHandler.DOCUMENT:
       consensus = list(consensus_query.run())[0]
       key_certs = self.get_key_certificates(**query_args).run()
-      consensus.validate_signatures(key_certs)
+
+      try:
+        consensus.validate_signatures(key_certs)
+      except ImportError:
+        pass  # cryptography module unavailable
 
     return consensus_query
 
diff --git a/stem/descriptor/router_status_entry.py b/stem/descriptor/router_status_entry.py
index 0486d78d..c2d8dd07 100644
--- a/stem/descriptor/router_status_entry.py
+++ b/stem/descriptor/router_status_entry.py
@@ -25,7 +25,6 @@ import binascii
 import io
 
 import stem.exit_policy
-import stem.prereq
 import stem.util.str_tools
 
 from stem.descriptor import (
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index d38bac9d..955b8429 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -53,7 +53,6 @@ import re
 
 import stem.descriptor.extrainfo_descriptor
 import stem.exit_policy
-import stem.prereq
 import stem.util.connection
 import stem.util.enum
 import stem.util.str_tools
@@ -764,20 +763,26 @@ class RelayDescriptor(ServerDescriptor):
         if key_hash != self.fingerprint.lower():
           raise ValueError('Fingerprint does not match the hash of our signing key (fingerprint: %s, signing key hash: %s)' % (self.fingerprint.lower(), key_hash))
 
-      if not skip_crypto_validation and stem.prereq.is_crypto_available():
-        signed_digest = self._digest_for_signature(self.signing_key, self.signature)
+      if not skip_crypto_validation:
+        try:
+          signed_digest = self._digest_for_signature(self.signing_key, self.signature)
 
-        if signed_digest != self.digest():
-          raise ValueError('Decrypted digest does not match local digest (calculated: %s, local: %s)' % (signed_digest, self.digest()))
+          if signed_digest != self.digest():
+            raise ValueError('Decrypted digest does not match local digest (calculated: %s, local: %s)' % (signed_digest, self.digest()))
 
-        if self.onion_key_crosscert and stem.prereq.is_crypto_available():
-          onion_key_crosscert_digest = self._digest_for_signature(self.onion_key, self.onion_key_crosscert)
+          if self.onion_key_crosscert:
+            onion_key_crosscert_digest = self._digest_for_signature(self.onion_key, self.onion_key_crosscert)
 
-          if onion_key_crosscert_digest != self._onion_key_crosscert_digest():
-            raise ValueError('Decrypted onion-key-crosscert digest does not match local digest (calculated: %s, local: %s)' % (onion_key_crosscert_digest, self._onion_key_crosscert_digest()))
+            if onion_key_crosscert_digest != self._onion_key_crosscert_digest():
+              raise ValueError('Decrypted onion-key-crosscert digest does not match local digest (calculated: %s, local: %s)' % (onion_key_crosscert_digest, self._onion_key_crosscert_digest()))
+        except ImportError:
+          pass  # cryptography module unavailable
 
-      if stem.prereq.is_crypto_available(ed25519 = True) and self.certificate:
-        self.certificate.validate(self)
+      if self.certificate:
+        try:
+          self.certificate.validate(self)
+        except ImportError:
+          pass  # cryptography module unavailable
 
   @classmethod
   def content(cls, attr = None, exclude = (), sign = False, signing_key = None, exit_policy = None):
diff --git a/stem/exit_policy.py b/stem/exit_policy.py
index 0150e190..e2a64bc4 100644
--- a/stem/exit_policy.py
+++ b/stem/exit_policy.py
@@ -70,7 +70,6 @@ import re
 import socket
 import zlib
 
-import stem.prereq
 import stem.util
 import stem.util.connection
 import stem.util.enum
diff --git a/stem/interpreter/__init__.py b/stem/interpreter/__init__.py
index 67984261..07a5f573 100644
--- a/stem/interpreter/__init__.py
+++ b/stem/interpreter/__init__.py
@@ -11,7 +11,6 @@ import sys
 
 import stem
 import stem.connection
-import stem.prereq
 import stem.process
 import stem.util.conf
 import stem.util.system
diff --git a/stem/interpreter/autocomplete.py b/stem/interpreter/autocomplete.py
index 05585b48..9f5f2659 100644
--- a/stem/interpreter/autocomplete.py
+++ b/stem/interpreter/autocomplete.py
@@ -7,8 +7,6 @@ Tab completion for our interpreter prompt.
 
 import functools
 
-import stem.prereq
-
 from stem.interpreter import uses_settings
 
 
diff --git a/stem/interpreter/help.py b/stem/interpreter/help.py
index 5fde9246..1f242a8e 100644
--- a/stem/interpreter/help.py
+++ b/stem/interpreter/help.py
@@ -7,8 +7,6 @@ Provides our /help responses.
 
 import functools
 
-import stem.prereq
-
 from stem.interpreter import (
   STANDARD_OUTPUT,
   BOLD_OUTPUT,
diff --git a/stem/manual.py b/stem/manual.py
index 3f385ba0..44117f93 100644
--- a/stem/manual.py
+++ b/stem/manual.py
@@ -57,7 +57,6 @@ import tempfile
 import urllib.request
 
 import stem
-import stem.prereq
 import stem.util
 import stem.util.conf
 import stem.util.enum
@@ -134,11 +133,11 @@ def query(query, *param):
     * **sqlite3.OperationalError** if query fails
   """
 
-  if not stem.prereq.is_sqlite_available():
+  try:
+    import sqlite3
+  except ImportError:
     raise ImportError('Querying requires the sqlite3 module')
 
-  import sqlite3
-
   # The only reason to explicitly close the sqlite connection is to ensure
   # transactions are committed. Since we're only using read-only access this
   # doesn't matter, and can allow interpreter shutdown to do the needful.
@@ -385,13 +384,13 @@ class Manual(object):
         it or the schema is out of date
     """
 
-    if path is None:
-      path = CACHE_PATH
-
-    if not stem.prereq.is_sqlite_available():
+    try:
+      import sqlite3
+    except ImportError:
       raise ImportError('Reading a sqlite cache requires the sqlite3 module')
 
-    import sqlite3
+    if path is None:
+      path = CACHE_PATH
 
     if not os.path.exists(path):
       raise IOError("%s doesn't exist" % path)
@@ -517,10 +516,11 @@ class Manual(object):
       * **IOError** if unsuccessful
     """
 
-    if not stem.prereq.is_sqlite_available():
+    try:
+      import sqlite3
+    except ImportError:
       raise ImportError('Saving a sqlite cache requires the sqlite3 module')
 
-    import sqlite3
     tmp_path = path + '.new'
 
     if os.path.exists(tmp_path):
diff --git a/stem/prereq.py b/stem/prereq.py
deleted file mode 100644
index 74584165..00000000
--- a/stem/prereq.py
+++ /dev/null
@@ -1,179 +0,0 @@
-# Copyright 2012-2020, Damian Johnson and The Tor Project
-# See LICENSE for licensing information
-
-"""
-Checks for stem dependencies.
-
-Aside from Python itself Stem only has soft dependencies, which is to say
-module unavailability only impacts features that require it. For example,
-descriptor signature validation requires 'cryptography'. If unavailable
-stem will still read descriptors - just without signature checks.
-
-::
-
-  check_requirements - checks for minimum requirements for running stem
-  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
-"""
-
-import functools
-import hashlib
-import inspect
-import platform
-import sys
-
-# TODO: in stem 2.x consider replacing these functions with requirement
-# annotations (like our tests)
-
-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.org/project/cryptography/"
-ZSTD_UNAVAILABLE = 'ZSTD compression requires the zstandard module (https://pypi.org/project/zstandard/)'
-LZMA_UNAVAILABLE = 'LZMA compression requires the lzma module (https://docs.python.org/3/library/lzma.html)'
-ED25519_UNSUPPORTED = 'Unable to verify descriptor ed25519 certificate integrity. ed25519 is not supported by installed versions of OpenSSL and/or cryptography'
-
-
-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 < 3 or (major_version == 3 and minor_version < 6):
-    raise ImportError('stem requires python version 3.6 or greater')
-
-
-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(ed25519 = False):
-  """
-  Checks if the cryptography functions we use are available. This is used for
-  verifying relay descriptor signatures.
-
-  :param bool ed25519: check for `ed25519 support
-    <https://cryptography.io/en/latest/hazmat/primitives/asymmetric/ed25519/>`_,
-    which requires both cryptography version 2.6 and OpenSSL support
-
-  :returns: **True** if we can use the cryptography module and **False**
-    otherwise
-  """
-
-  from stem.util import log
-
-  try:
-    from cryptography.utils import int_from_bytes, int_to_bytes
-    from cryptography.hazmat.backends import default_backend
-    from cryptography.hazmat.backends.openssl.backend import 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()
-
-    if ed25519:
-      # The following import confirms cryptography support (ie. version 2.6+),
-      # whereas ed25519_supported() checks for OpenSSL bindings.
-
-      from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
-
-      if not hasattr(backend, 'ed25519_supported') or not backend.ed25519_supported():
-        log.log_once('stem.prereq._is_crypto_ed25519_supported', log.INFO, ED25519_UNSUPPORTED)
-        return False
-
-    return True
-  except ImportError:
-    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.org/project/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.org/project/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_sha3_available():
-  """
-  Check if hashlib has sha3 support. This requires Python 3.6+ *or* the `pysha3
-  module <https://github.com/tiran/pysha3>`_.
-  """
-
-  # If pysha3 is present then importing sha3 will monkey patch the methods we
-  # want onto hashlib.
-
-  if not hasattr(hashlib, 'sha3_256') or not hasattr(hashlib, 'shake_256'):
-    try:
-      import sha3
-    except ImportError:
-      pass
-
-  return hasattr(hashlib, 'sha3_256') and hasattr(hashlib, 'shake_256')
diff --git a/stem/process.py b/stem/process.py
index 913a9be7..a1d805ec 100644
--- a/stem/process.py
+++ b/stem/process.py
@@ -25,7 +25,6 @@ import subprocess
 import tempfile
 import threading
 
-import stem.prereq
 import stem.util.str_tools
 import stem.util.system
 import stem.version
diff --git a/stem/response/events.py b/stem/response/events.py
index 38708abc..b82457e6 100644
--- a/stem/response/events.py
+++ b/stem/response/events.py
@@ -7,7 +7,6 @@ import re
 import stem
 import stem.control
 import stem.descriptor.router_status_entry
-import stem.prereq
 import stem.response
 import stem.util
 import stem.version
diff --git a/stem/response/protocolinfo.py b/stem/response/protocolinfo.py
index 46f6ab4f..459fef5b 100644
--- a/stem/response/protocolinfo.py
+++ b/stem/response/protocolinfo.py
@@ -3,7 +3,6 @@
 
 import sys
 
-import stem.prereq
 import stem.response
 import stem.socket
 import stem.version
diff --git a/stem/socket.py b/stem/socket.py
index 45a1c53c..2ef42dd5 100644
--- a/stem/socket.py
+++ b/stem/socket.py
@@ -78,7 +78,6 @@ import ssl
 import threading
 import time
 
-import stem.prereq
 import stem.response
 import stem.util.str_tools
 
diff --git a/stem/util/__init__.py b/stem/util/__init__.py
index 2bcc8a07..cde49de7 100644
--- a/stem/util/__init__.py
+++ b/stem/util/__init__.py
@@ -7,8 +7,6 @@ Utility functions used by the stem library.
 
 import datetime
 
-import stem.prereq
-
 __all__ = [
   'conf',
   'connection',
@@ -88,14 +86,12 @@ def _pubkey_bytes(key):
   if isinstance(key, (bytes, str)):
     return key
 
-  if not stem.prereq.is_crypto_available():
-    raise ImportError('Key normalization requires the cryptography module')
-  elif not stem.prereq.is_crypto_available(ed25519 = True):
-    raise ImportError('Key normalization requires the cryptography ed25519 support')
-
-  from cryptography.hazmat.primitives import serialization
-  from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey
-  from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey
+  try:
+    from cryptography.hazmat.primitives import serialization
+    from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey
+    from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X25519PublicKey
+  except ImportError:
+    raise ImportError('Key normalization requires the cryptography module with ed25519 support')
 
   if isinstance(key, (X25519PrivateKey, Ed25519PrivateKey)):
     return key.public_key().public_bytes(
diff --git a/stem/util/conf.py b/stem/util/conf.py
index 1dbcc243..a06f1fd7 100644
--- a/stem/util/conf.py
+++ b/stem/util/conf.py
@@ -162,8 +162,6 @@ import inspect
 import os
 import threading
 
-import stem.prereq
-
 from stem.util import log
 
 CONFS = {}  # mapping of identifier to singleton instances of configs
diff --git a/stem/util/log.py b/stem/util/log.py
index 3093396f..94d055ff 100644
--- a/stem/util/log.py
+++ b/stem/util/log.py
@@ -50,7 +50,6 @@ them at your own risk.**
 
 import logging
 
-import stem.prereq
 import stem.util.enum
 import stem.util.str_tools
 
diff --git a/stem/util/proc.py b/stem/util/proc.py
index 0b4c0ab4..3589af13 100644
--- a/stem/util/proc.py
+++ b/stem/util/proc.py
@@ -51,7 +51,6 @@ import socket
 import sys
 import time
 
-import stem.prereq
 import stem.util.connection
 import stem.util.enum
 import stem.util.str_tools
diff --git a/stem/util/str_tools.py b/stem/util/str_tools.py
index bf582bea..8ce22dc9 100644
--- a/stem/util/str_tools.py
+++ b/stem/util/str_tools.py
@@ -23,7 +23,6 @@ import datetime
 import re
 import sys
 
-import stem.prereq
 import stem.util
 import stem.util.enum
 
diff --git a/stem/util/system.py b/stem/util/system.py
index b9a7ef8d..b3dee151 100644
--- a/stem/util/system.py
+++ b/stem/util/system.py
@@ -75,7 +75,6 @@ import tarfile
 import threading
 import time
 
-import stem.prereq
 import stem.util
 import stem.util.enum
 import stem.util.proc
@@ -483,7 +482,7 @@ def size_of(obj, exclude = None):
   :raises: **NotImplementedError** if using PyPy
   """
 
-  if stem.prereq.is_pypy():
+  if platform.python_implementation() == 'PyPy':
     raise NotImplementedError('PyPy does not implement sys.getsizeof()')
 
   if exclude is None:
diff --git a/stem/util/test_tools.py b/stem/util/test_tools.py
index 5888eb86..71165214 100644
--- a/stem/util/test_tools.py
+++ b/stem/util/test_tools.py
@@ -38,7 +38,6 @@ import time
 import traceback
 import unittest
 
-import stem.prereq
 import stem.util.conf
 import stem.util.enum
 import stem.util.system
diff --git a/stem/version.py b/stem/version.py
index 4f873f47..e67a93b5 100644
--- a/stem/version.py
+++ b/stem/version.py
@@ -58,7 +58,6 @@ import functools
 import os
 import re
 
-import stem.prereq
 import stem.util
 import stem.util.enum
 import stem.util.system
diff --git a/test/integ/installation.py b/test/integ/installation.py
index 2f41ef58..16d63538 100644
--- a/test/integ/installation.py
+++ b/test/integ/installation.py
@@ -4,6 +4,7 @@ Tests installation of our library.
 
 import glob
 import os
+import platform
 import shutil
 import sys
 import tarfile
@@ -11,7 +12,6 @@ import time
 import unittest
 
 import stem
-import stem.prereq
 import stem.util.system
 import stem.util.test_tools
 import test
@@ -75,7 +75,7 @@ class TestInstallation(unittest.TestCase):
         stem.util.system.call('%s setup.py install --prefix %s' % (PYTHON_EXE, BASE_INSTALL_PATH), timeout = 60, cwd = test.STEM_BASE)
         stem.util.system.call('%s setup.py clean --all' % PYTHON_EXE, timeout = 60, cwd = test.STEM_BASE)  # tidy up the build directory
 
-        if stem.prereq.is_pypy():
+        if platform.python_implementation() == 'PyPy':
           site_packages_paths = glob.glob('%s/site-packages' % BASE_INSTALL_PATH)
         else:
           site_packages_paths = glob.glob('%s/lib*/*/site-packages' % BASE_INSTALL_PATH)
diff --git a/test/integ/process.py b/test/integ/process.py
index 430ed484..347e3f11 100644
--- a/test/integ/process.py
+++ b/test/integ/process.py
@@ -15,7 +15,6 @@ import threading
 import time
 import unittest
 
-import stem.prereq
 import stem.process
 import stem.socket
 import stem.util.str_tools
diff --git a/test/integ/util/system.py b/test/integ/util/system.py
index 2a7ac07a..08a367c9 100644
--- a/test/integ/util/system.py
+++ b/test/integ/util/system.py
@@ -5,10 +5,10 @@ that we're running.
 
 import getpass
 import os
+import platform
 import tempfile
 import unittest
 
-import stem.prereq
 import stem.util.proc
 import stem.util.system
 import test.require
@@ -556,7 +556,7 @@ class TestSystem(unittest.TestCase):
     Exercises the get_process_name() and set_process_name() methods.
     """
 
-    if stem.prereq.is_pypy():
+    if platform.python_implementation() == 'PyPy':
       self.skipTest('(unimplemented for pypy)')
 
     initial_name = stem.util.system.get_process_name()
diff --git a/test/integ/version.py b/test/integ/version.py
index b28ee868..641629d4 100644
--- a/test/integ/version.py
+++ b/test/integ/version.py
@@ -5,7 +5,6 @@ running with.
 
 import unittest
 
-import stem.prereq
 import stem.version
 import test.require
 import test.runner
diff --git a/test/require.py b/test/require.py
index 12b3adf5..53d66f62 100644
--- a/test/require.py
+++ b/test/require.py
@@ -12,7 +12,6 @@ run.
   |- needs - skips the test unless a requirement is met
   |
   |- cryptography - skips test unless the cryptography module is present
-  |- ed25519_support - skips test unless cryptography has ed25519 support
   |- command - requires a command to be on the path
   |- proc - requires the platform to have recognized /proc contents
   |
@@ -27,6 +26,22 @@ import stem.version
 import test
 import test.runner
 
+try:
+  from cryptography.utils import int_from_bytes, int_to_bytes
+  from cryptography.hazmat.backends import default_backend
+  from cryptography.hazmat.backends.openssl.backend import 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
+  from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
+
+  if not hasattr(rsa.RSAPrivateKey, 'sign') or not hasattr(backend, 'ed25519_supported') or not backend.ed25519_supported():
+    raise ImportError()
+
+  CRYPTOGRAPHY_AVAILABLE = True
+except ImportError:
+  CRYPTOGRAPHY_AVAILABLE = False
+
 RAN_TESTS = []
 
 
@@ -94,8 +109,7 @@ def version(req_version):
   return needs(lambda: test.tor_version() >= req_version, 'requires %s' % req_version)
 
 
-cryptography = needs(stem.prereq.is_crypto_available, 'requires cryptography')
-ed25519_support = needs(lambda: stem.prereq.is_crypto_available(ed25519 = True), 'requires ed25519 support')
+cryptography = needs(lambda: CRYPTOGRAPHY_AVAILABLE, 'requires cryptography')
 proc = needs(stem.util.proc.is_available, 'proc unavailable')
 controller = needs(_can_access_controller, 'no connection')
 ptrace = needs(_can_ptrace, 'DisableDebuggerAttachment is set')
diff --git a/test/runner.py b/test/runner.py
index d6a0d561..a8079908 100644
--- a/test/runner.py
+++ b/test/runner.py
@@ -42,7 +42,6 @@ import time
 import uuid
 
 import stem.connection
-import stem.prereq
 import stem.process
 import stem.socket
 import stem.util.conf
diff --git a/test/settings.cfg b/test/settings.cfg
index d22bec42..d796993a 100644
--- a/test/settings.cfg
+++ b/test/settings.cfg
@@ -195,30 +195,6 @@ pycodestyle.ignore test/unit/util/connection.py => W291: _tor     tor        158
 pyflakes.ignore run_tests.py => 'unittest' imported but unused
 pyflakes.ignore stem/control.py => undefined name 'controller'
 pyflakes.ignore stem/manual.py => undefined name 'unichr'
-pyflakes.ignore stem/prereq.py => 'int_to_bytes' imported but unused
-pyflakes.ignore stem/prereq.py => 'int_from_bytes' imported but unused
-pyflakes.ignore stem/prereq.py => 'default_backend' imported but unused
-pyflakes.ignore stem/prereq.py => 'load_der_public_key' imported but unused
-pyflakes.ignore stem/prereq.py => 'modes' imported but unused
-pyflakes.ignore stem/prereq.py => 'Cipher' imported but unused
-pyflakes.ignore stem/prereq.py => 'algorithms' imported but unused
-pyflakes.ignore stem/prereq.py => 'unittest' imported but unused
-pyflakes.ignore stem/prereq.py => 'unittest.mock' imported but unused
-pyflakes.ignore stem/prereq.py => 'long_to_bytes' imported but unused
-pyflakes.ignore stem/prereq.py => 'encoding' imported but unused
-pyflakes.ignore stem/prereq.py => 'sha3' imported but unused
-pyflakes.ignore stem/prereq.py => 'signing' imported but unused
-pyflakes.ignore stem/prereq.py => 'sqlite3' imported but unused
-pyflakes.ignore stem/prereq.py => 'cryptography.utils.int_to_bytes' imported but unused
-pyflakes.ignore stem/prereq.py => 'cryptography.utils.int_from_bytes' imported but unused
-pyflakes.ignore stem/prereq.py => 'cryptography.hazmat.backends.default_backend' imported but unused
-pyflakes.ignore stem/prereq.py => 'cryptography.hazmat.backends.openssl.backend.backend' imported but unused
-pyflakes.ignore stem/prereq.py => 'cryptography.hazmat.primitives.serialization.load_der_public_key' imported but unused
-pyflakes.ignore stem/prereq.py => 'cryptography.hazmat.primitives.ciphers.modes' imported but unused
-pyflakes.ignore stem/prereq.py => 'cryptography.hazmat.primitives.ciphers.Cipher' imported but unused
-pyflakes.ignore stem/prereq.py => 'cryptography.hazmat.primitives.ciphers.algorithms' imported but unused
-pyflakes.ignore stem/prereq.py => 'cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey' imported but unused
-pyflakes.ignore stem/prereq.py => 'lzma' imported but unused
 pyflakes.ignore stem/client/datatype.py => redefinition of unused 'pop' from *
 pyflakes.ignore stem/descriptor/hidden_service_descriptor.py => 'stem.descriptor.hidden_service.*' imported but unused
 pyflakes.ignore stem/descriptor/hidden_service_descriptor.py => 'from stem.descriptor.hidden_service import *' used; unable to detect undefined names
diff --git a/test/task.py b/test/task.py
index 19026165..939e263c 100644
--- a/test/task.py
+++ b/test/task.py
@@ -34,12 +34,12 @@ import time
 import traceback
 
 import stem
-import stem.prereq
 import stem.util.conf
 import stem.util.system
 import stem.util.test_tools
 import stem.version
 import test
+import test.require
 import test.output
 
 from test.output import STATUS, ERROR, NO_NL, println
@@ -321,7 +321,7 @@ STEM_VERSION = Task('stem version', _check_stem_version)
 TOR_VERSION = Task('tor version', _check_tor_version)
 PYTHON_VERSION = Task('python version', _check_python_version)
 PLATFORM_VERSION = Task('operating system', _check_platform_version)
-CRYPTO_VERSION = ModuleVersion('cryptography version', 'cryptography', stem.prereq.is_crypto_available)
+CRYPTO_VERSION = ModuleVersion('cryptography version', 'cryptography', lambda: test.require.CRYPTOGRAPHY_AVAILABLE)
 PYFLAKES_VERSION = ModuleVersion('pyflakes version', 'pyflakes')
 PYCODESTYLE_VERSION = ModuleVersion('pycodestyle version', ['pycodestyle', 'pep8'])
 CLEAN_PYC = Task('checking for orphaned .pyc files', _clean_orphaned_pyc, (SRC_PATHS,), print_runtime = True)
diff --git a/test/unit/descriptor/certificate.py b/test/unit/descriptor/certificate.py
index 74ea0a6e..9e29556a 100644
--- a/test/unit/descriptor/certificate.py
+++ b/test/unit/descriptor/certificate.py
@@ -9,7 +9,6 @@ import unittest
 
 import stem.descriptor.certificate
 import stem.util.str_tools
-import stem.prereq
 import test.require
 
 from stem.client.datatype import Size, CertType
@@ -183,7 +182,7 @@ class TestEd25519Certificate(unittest.TestCase):
     exc_msg = 'Ed25519 HAS_SIGNING_KEY extension must be 32 bytes, but was 2.'
     self.assertRaisesWith(ValueError, exc_msg, Ed25519Certificate.from_base64, certificate(extension_data = [b'\x00\x02\x04\x07\11\12']))
 
-  @test.require.ed25519_support
+  @test.require.cryptography
   def test_validation_with_descriptor_key(self):
     """
     Validate a descriptor signature using the ed25519 master key within the
@@ -195,7 +194,7 @@ class TestEd25519Certificate(unittest.TestCase):
 
     desc.certificate.validate(desc)
 
-  @test.require.ed25519_support
+  @test.require.cryptography
   def test_validation_with_embedded_key(self):
     """
     Validate a descriptor signature using the signing key within the ed25519
@@ -208,7 +207,7 @@ class TestEd25519Certificate(unittest.TestCase):
     desc.ed25519_master_key = None
     desc.certificate.validate(desc)
 
-  @test.require.ed25519_support
+  @test.require.cryptography
   def test_validation_with_invalid_descriptor(self):
     """
     Validate a descriptor without a valid signature.
diff --git a/test/unit/descriptor/collector.py b/test/unit/descriptor/collector.py
index 99e19d7c..2960cf53 100644
--- a/test/unit/descriptor/collector.py
+++ b/test/unit/descriptor/collector.py
@@ -6,7 +6,7 @@ import datetime
 import io
 import unittest
 
-import stem.prereq
+import stem.descriptor.collector
 
 from unittest.mock import Mock, patch
 
diff --git a/test/unit/descriptor/hidden_service_v2.py b/test/unit/descriptor/hidden_service_v2.py
index 54553d03..7112648b 100644
--- a/test/unit/descriptor/hidden_service_v2.py
+++ b/test/unit/descriptor/hidden_service_v2.py
@@ -7,7 +7,6 @@ import functools
 import unittest
 
 import stem.descriptor
-import stem.prereq
 import test.require
 
 from stem.descriptor.hidden_service import (
diff --git a/test/unit/descriptor/hidden_service_v3.py b/test/unit/descriptor/hidden_service_v3.py
index 4004a94d..c24eb1ad 100644
--- a/test/unit/descriptor/hidden_service_v3.py
+++ b/test/unit/descriptor/hidden_service_v3.py
@@ -10,7 +10,6 @@ import unittest
 import stem.client.datatype
 import stem.descriptor
 import stem.descriptor.hidden_service
-import stem.prereq
 
 import test.require
 
@@ -30,7 +29,6 @@ from test.unit.descriptor import (
   base_expect_invalid_attr_for_text,
 )
 
-require_sha3 = test.require.needs(stem.prereq._is_sha3_available, 'requires sha3')
 require_x25519 = test.require.needs(lambda: stem.descriptor.hidden_service.X25519_AVAILABLE, 'requires openssl x5509')
 
 expect_invalid_attr = functools.partial(base_expect_invalid_attr, HiddenServiceDescriptorV3, 'version', 3)
@@ -89,8 +87,7 @@ class TestHiddenServiceDescriptorV3(unittest.TestCase):
     self.assertTrue('eaH8VdaTKS' in desc.superencrypted)
     self.assertEqual('aglChCQF+lbzKgyxJJTpYGVShV/GMDRJ4+cRGCp+a2y/yX/tLSh7hzqI7rVZrUoGj74Xr1CLMYO3fXYCS+DPDQ', desc.signature)
 
-  @require_sha3
-  @test.require.ed25519_support
+  @test.require.cryptography
   def test_decryption(self):
     """
     Decrypt our descriptor and validate its content.
@@ -152,7 +149,7 @@ class TestHiddenServiceDescriptorV3(unittest.TestCase):
     self.assertEqual(None, intro_point.legacy_key_raw)
     self.assertEqual(None, intro_point.legacy_key_cert)
 
-  @test.require.ed25519_support
+  @test.require.cryptography
   def test_required_fields(self):
     """
     Check that we require the mandatory fields.
@@ -171,7 +168,7 @@ class TestHiddenServiceDescriptorV3(unittest.TestCase):
       desc_text = HiddenServiceDescriptorV3.content(exclude = (line,))
       expect_invalid_attr_for_text(self, desc_text, line_to_attr[line], None)
 
-  @test.require.ed25519_support
+  @test.require.cryptography
   def test_invalid_version(self):
     """
     Checks that our version field expects a numeric value.
@@ -186,7 +183,7 @@ class TestHiddenServiceDescriptorV3(unittest.TestCase):
     for test_value in test_values:
       expect_invalid_attr(self, {'hs-descriptor': test_value}, 'version')
 
-  @test.require.ed25519_support
+  @test.require.cryptography
   def test_invalid_lifetime(self):
     """
     Checks that our lifetime field expects a numeric value.
@@ -201,7 +198,7 @@ class TestHiddenServiceDescriptorV3(unittest.TestCase):
     for test_value in test_values:
       expect_invalid_attr(self, {'descriptor-lifetime': test_value}, 'lifetime')
 
-  @test.require.ed25519_support
+  @test.require.cryptography
   def test_invalid_revision_counter(self):
     """
     Checks that our revision counter field expects a numeric value.
@@ -216,11 +213,9 @@ class TestHiddenServiceDescriptorV3(unittest.TestCase):
     for test_value in test_values:
       expect_invalid_attr(self, {'revision-counter': test_value}, 'revision_counter')
 
-  @require_sha3
   def test_address_from_identity_key(self):
     self.assertEqual(HS_ADDRESS, HiddenServiceDescriptorV3.address_from_identity_key(HS_PUBKEY))
 
-  @require_sha3
   def test_identity_key_from_address(self):
     self.assertEqual(HS_PUBKEY, HiddenServiceDescriptorV3.identity_key_from_address(HS_ADDRESS))
     self.assertRaisesWith(ValueError, "'boom.onion' isn't a valid hidden service v3 address", HiddenServiceDescriptorV3.identity_key_from_address, 'boom')
@@ -249,7 +244,7 @@ class TestHiddenServiceDescriptorV3(unittest.TestCase):
     self.assertEqual(INTRO_POINT_STR.rstrip(), intro_point.encode())
 
   @require_x25519
-  @test.require.ed25519_support
+  @test.require.cryptography
   def test_intro_point_crypto(self):
     """
     Retrieve IntroductionPointV3 cryptographic materials.
@@ -276,16 +271,7 @@ class TestHiddenServiceDescriptorV3(unittest.TestCase):
     self.assertEqual(None, intro_point.legacy_key_raw)
     self.assertEqual(None, intro_point.legacy_key())
 
-  @patch('stem.prereq.is_crypto_available', Mock(return_value = False))
-  def test_intro_point_crypto_without_prereq(self):
-    """
-    Fetch cryptographic materials when the module is unavailable.
-    """
-
-    intro_point = InnerLayer(INNER_LAYER_STR).introduction_points[0]
-    self.assertRaisesWith(ImportError, 'cryptography module unavailable', intro_point.onion_key)
-
-  @test.require.ed25519_support
+  @test.require.cryptography
   def test_intro_point_creation(self):
     """
     Create an introduction point, encode it, then re-parse.
@@ -301,7 +287,7 @@ class TestHiddenServiceDescriptorV3(unittest.TestCase):
     reparsed = IntroductionPointV3.parse(intro_point.encode())
     self.assertEqual(intro_point, reparsed)
 
-  @test.require.ed25519_support
+  @test.require.cryptography
   def test_inner_layer_creation(self):
     """
     Internal layer creation.
@@ -344,7 +330,7 @@ class TestHiddenServiceDescriptorV3(unittest.TestCase):
       IntroductionPointV3.create_for_address('1.1.1.1', 9001),
     ]).startswith(b'create2-formats 2\nintroduction-point AQAGAQEBASMp'))
 
-  @test.require.ed25519_support
+  @test.require.cryptography
   def test_outer_layer_creation(self):
     """
     Outer layer creation.
@@ -402,7 +388,7 @@ class TestHiddenServiceDescriptorV3(unittest.TestCase):
     self.assertEqual(1, len(inner_layer.introduction_points))
     self.assertEqual('1.1.1.1', inner_layer.introduction_points[0].link_specifiers[0].address)
 
-  @test.require.ed25519_support
+  @test.require.cryptography
   def test_descriptor_creation(self):
     """
     HiddenServiceDescriptorV3 creation.
@@ -451,7 +437,7 @@ class TestHiddenServiceDescriptorV3(unittest.TestCase):
     self.assertEqual(3, len(inner_layer.introduction_points))
     self.assertEqual('1.1.1.1', inner_layer.introduction_points[0].link_specifiers[0].address)
 
-  @test.require.ed25519_support
+  @test.require.cryptography
   def test_blinding(self):
     """
     Create a descriptor with key blinding.
diff --git a/test/unit/descriptor/remote.py b/test/unit/descriptor/remote.py
index 01e0c58e..70f88b52 100644
--- a/test/unit/descriptor/remote.py
+++ b/test/unit/descriptor/remote.py
@@ -11,7 +11,6 @@ import unittest
 import stem
 import stem.descriptor
 import stem.descriptor.remote
-import stem.prereq
 import stem.util.str_tools
 
 from unittest.mock import patch, Mock, MagicMock
@@ -168,24 +167,6 @@ class TestDescriptorDownloader(unittest.TestCase):
     self.assertEqual([stem.descriptor.Compression.GZIP], query.compression)
     self.assertEqual(TEST_RESOURCE, query.resource)
 
-  def test_zstd_support_check(self):
-    with patch('stem.prereq.is_zstd_available', Mock(return_value = True)):
-      query = stem.descriptor.remote.Query(TEST_RESOURCE, compression = Compression.ZSTD, start = False)
-      self.assertEqual([stem.descriptor.Compression.ZSTD], query.compression)
-
-    with patch('stem.prereq.is_zstd_available', Mock(return_value = False)):
-      query = stem.descriptor.remote.Query(TEST_RESOURCE, compression = Compression.ZSTD, start = False)
-      self.assertEqual([stem.descriptor.Compression.PLAINTEXT], query.compression)
-
-  def test_lzma_support_check(self):
-    with patch('stem.prereq.is_lzma_available', Mock(return_value = True)):
-      query = stem.descriptor.remote.Query(TEST_RESOURCE, compression = Compression.LZMA, start = False)
-      self.assertEqual([stem.descriptor.Compression.LZMA], query.compression)
-
-    with patch('stem.prereq.is_lzma_available', Mock(return_value = False)):
-      query = stem.descriptor.remote.Query(TEST_RESOURCE, compression = Compression.LZMA, start = False)
-      self.assertEqual([stem.descriptor.Compression.PLAINTEXT], query.compression)
-
   @patch('urllib.request.urlopen', _dirport_mock(read_resource('compressed_identity'), encoding = 'identity'))
   def test_compression_plaintext(self):
     """
@@ -222,7 +203,7 @@ class TestDescriptorDownloader(unittest.TestCase):
     Download a zstd compressed descriptor.
     """
 
-    if not stem.prereq.is_zstd_available():
+    if not Compression.ZSTD.available:
       self.skipTest('(requires zstd module)')
 
     descriptors = list(stem.descriptor.remote.get_server_descriptors(
@@ -240,7 +221,7 @@ class TestDescriptorDownloader(unittest.TestCase):
     Download a lzma compressed descriptor.
     """
 
-    if not stem.prereq.is_lzma_available():
+    if not Compression.LZMA.available:
       self.skipTest('(requires lzma module)')
 
     descriptors = list(stem.descriptor.remote.get_server_descriptors(
diff --git a/test/unit/descriptor/server_descriptor.py b/test/unit/descriptor/server_descriptor.py
index 99eb2d27..e1036ada 100644
--- a/test/unit/descriptor/server_descriptor.py
+++ b/test/unit/descriptor/server_descriptor.py
@@ -16,7 +16,6 @@ import stem.descriptor
 import stem.descriptor.router_status_entry
 import stem.descriptor.server_descriptor
 import stem.exit_policy
-import stem.prereq
 import stem.version
 import stem.util.str_tools
 import test.require
@@ -647,18 +646,6 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
 
     expect_invalid_attr(self, {'opt': 'protocols Link 1 2'}, 'circuit_protocols')
 
-  @patch('stem.prereq.is_crypto_available', Mock(return_value = False))
-  def test_published_leap_year(self):
-    """
-    Constructs with a published entry for a leap year, and when the date is
-    invalid.
-    """
-
-    expect_invalid_attr(self, {'published': '2011-02-29 04:03:19'}, 'published')
-
-    desc = RelayDescriptor.create({'published': '2012-02-29 04:03:19'})
-    self.assertEqual(datetime.datetime(2012, 2, 29, 4, 3, 19), desc.published)
-
   def test_published_no_time(self):
     """
     Constructs with a published entry without a time component.
diff --git a/test/unit/directory/authority.py b/test/unit/directory/authority.py
index 1574bea3..68f1a2af 100644
--- a/test/unit/directory/authority.py
+++ b/test/unit/directory/authority.py
@@ -7,7 +7,6 @@ import unittest
 
 import stem
 import stem.directory
-import stem.prereq
 
 from unittest.mock import patch, Mock
 
diff --git a/test/unit/manual.py b/test/unit/manual.py
index e1891ca4..006e0b3e 100644
--- a/test/unit/manual.py
+++ b/test/unit/manual.py
@@ -10,7 +10,6 @@ import tempfile
 import unittest
 import urllib.request
 
-import stem.prereq
 import stem.manual
 import stem.util.system
 import test.require
diff --git a/test/unit/tutorial.py b/test/unit/tutorial.py
index 91a70a32..35b2fe28 100644
--- a/test/unit/tutorial.py
+++ b/test/unit/tutorial.py
@@ -168,7 +168,6 @@ class TestTutorial(unittest.TestCase):
 
   @patch('sys.stdout', new_callable = io.StringIO)
   @patch('stem.descriptor.remote.DescriptorDownloader')
-  @patch('stem.prereq.is_crypto_available', Mock(return_value = False))
   def test_mirror_mirror_on_the_wall_5(self, downloader_mock, stdout_mock):
     def tutorial_example():
       from stem.descriptor.remote import DescriptorDownloader
diff --git a/test/unit/tutorial_examples.py b/test/unit/tutorial_examples.py
index d96ff71b..fccdba57 100644
--- a/test/unit/tutorial_examples.py
+++ b/test/unit/tutorial_examples.py
@@ -9,7 +9,6 @@ import unittest
 
 import stem.response
 import stem.descriptor.remote
-import stem.prereq
 
 from unittest.mock import Mock, patch
 
diff --git a/test/unit/util/system.py b/test/unit/util/system.py
index 32be337c..042c1bd1 100644
--- a/test/unit/util/system.py
+++ b/test/unit/util/system.py
@@ -8,12 +8,11 @@ system running the tests.
 import functools
 import ntpath
 import os
+import platform
 import posixpath
 import tempfile
 import unittest
 
-import stem.prereq
-
 from unittest.mock import Mock, patch
 
 from stem.util import system
@@ -152,7 +151,7 @@ class TestSystem(unittest.TestCase):
     Exercises the size_of function.
     """
 
-    if stem.prereq.is_pypy():
+    if platform.python_implementation() == 'PyPy':
       self.assertRaises(NotImplementedError, system.size_of, 'hello')
     else:
       self.assertTrue(system.size_of('') < system.size_of('hello') < system.size_of('hello world'))





More information about the tor-commits mailing list