diff --git a/doc/conf.py b/doc/conf.py index eb8f8005e..77beed706 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # pyOpenSSL documentation build configuration file, created by # sphinx-quickstart on Sat Jul 16 07:12:22 2011. @@ -16,7 +15,6 @@ import re import sys - HERE = os.path.abspath(os.path.dirname(__file__)) diff --git a/pyproject.toml b/pyproject.toml index 3d0f0e683..168848b26 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,3 +2,9 @@ line-length = 79 target-version = ["py37"] +[tool.ruff] +select = ['E', 'F', 'I', 'W', 'UP', 'RUF'] +line-length = 79 + +[tool.ruff.isort] +known-first-party = ["OpenSSL", "tests"] diff --git a/setup.py b/setup.py index 3f27e12dd..f52b3f2eb 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # # Copyright (C) Jean-Paul Calderone 2008-2015, All rights reserved # @@ -13,7 +12,6 @@ from setuptools import find_packages, setup - HERE = os.path.abspath(os.path.dirname(__file__)) META_PATH = os.path.join("src", "OpenSSL", "version.py") @@ -23,9 +21,7 @@ def read_file(*parts): Build an absolute path from *parts* and return the contents of the resulting file. Assume UTF-8 encoding. """ - with open( - os.path.join(HERE, *parts), "r", encoding="utf-8", newline=None - ) as f: + with open(os.path.join(HERE, *parts), encoding="utf-8", newline=None) as f: return f.read() @@ -37,11 +33,11 @@ def find_meta(meta): Extract __*meta*__ from META_FILE. """ meta_match = re.search( - r"^__{meta}__ = ['\"]([^'\"]*)['\"]".format(meta=meta), META_FILE, re.M + rf"^__{meta}__ = ['\"]([^'\"]*)['\"]", META_FILE, re.M ) if meta_match: return meta_match.group(1) - raise RuntimeError("Unable to find __{meta}__ string.".format(meta=meta)) + raise RuntimeError(f"Unable to find __{meta}__ string.") URI = find_meta("uri") diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py index a0d0b6acb..ae07442f5 100644 --- a/src/OpenSSL/SSL.py +++ b/src/OpenSSL/SSL.py @@ -1,5 +1,6 @@ import os import socket +import typing from errno import errorcode from functools import partial, wraps from itertools import chain, count @@ -8,18 +9,32 @@ from OpenSSL._util import ( UNSPECIFIED as _UNSPECIFIED, +) +from OpenSSL._util import ( exception_from_error_queue as _exception_from_error_queue, +) +from OpenSSL._util import ( ffi as _ffi, +) +from OpenSSL._util import ( lib as _lib, +) +from OpenSSL._util import ( make_assert as _make_assert, +) +from OpenSSL._util import ( no_zero_allocator as _no_zero_allocator, +) +from OpenSSL._util import ( path_bytes as _path_bytes, +) +from OpenSSL._util import ( text_to_bytes_and_warn as _text_to_bytes_and_warn, ) from OpenSSL.crypto import ( FILETYPE_PEM, - PKey, X509, + PKey, X509Name, X509Store, _PassphraseHelper, @@ -803,7 +818,7 @@ class Context: not be used. """ - _methods = { + _methods: typing.ClassVar[typing.Dict] = { SSLv23_METHOD: (_lib.TLS_method, None), TLSv1_METHOD: (_lib.TLS_method, TLS1_VERSION), TLSv1_1_METHOD: (_lib.TLS_method, TLS1_1_VERSION), @@ -1373,8 +1388,8 @@ def set_client_ca_list(self, certificate_authorities): for ca_name in certificate_authorities: if not isinstance(ca_name, X509Name): raise TypeError( - "client CAs must be X509Name objects, not %s " - "objects" % (type(ca_name).__name__,) + "client CAs must be X509Name objects, not {} " + "objects".format(type(ca_name).__name__) ) copy = _lib.X509_NAME_dup(ca_name._name) _openssl_assert(copy != _ffi.NULL) @@ -1777,8 +1792,7 @@ def __getattr__(self, name): """ if self._socket is None: raise AttributeError( - "'%s' object has no attribute '%s'" - % (self.__class__.__name__, name) + f"'{self.__class__.__name__}' object has no attribute '{name}'" ) else: return getattr(self._socket, name) diff --git a/src/OpenSSL/__init__.py b/src/OpenSSL/__init__.py index 0af3acdb8..2442ee6f0 100644 --- a/src/OpenSSL/__init__.py +++ b/src/OpenSSL/__init__.py @@ -17,7 +17,6 @@ __version__, ) - __all__ = [ "SSL", "crypto", diff --git a/src/OpenSSL/crypto.py b/src/OpenSSL/crypto.py index f7a97f4c5..5fddc4c30 100644 --- a/src/OpenSSL/crypto.py +++ b/src/OpenSSL/crypto.py @@ -1,6 +1,7 @@ import calendar import datetime import functools +import typing from base64 import b16encode from functools import partial from os import PathLike @@ -22,23 +23,36 @@ from cryptography.hazmat.primitives.asymmetric import ( dsa, ec, - ed25519, ed448, + ed25519, rsa, ) from OpenSSL._util import ( UNSPECIFIED as _UNSPECIFIED, +) +from OpenSSL._util import ( byte_string as _byte_string, +) +from OpenSSL._util import ( exception_from_error_queue as _exception_from_error_queue, +) +from OpenSSL._util import ( ffi as _ffi, +) +from OpenSSL._util import ( lib as _lib, +) +from OpenSSL._util import ( make_assert as _make_assert, +) +from OpenSSL._util import ( path_bytes as _path_bytes, +) +from OpenSSL._util import ( text_to_bytes_and_warn as _text_to_bytes_and_warn, ) - __all__ = [ "FILETYPE_PEM", "FILETYPE_ASN1", @@ -111,7 +125,7 @@ def _untested_error(where: str) -> NoReturn: encountered isn't one that's exercised by the test suite so future behavior of pyOpenSSL is now somewhat less predictable. """ - raise RuntimeError("Unknown %s failure" % (where,)) + raise RuntimeError(f"Unknown {where} failure") def _new_mem_buf(buffer: Optional[bytes] = None) -> Any: @@ -448,7 +462,7 @@ def __ne__(self, other: Any) -> bool: circumstance. """ if isinstance(other, _EllipticCurve): - return super(_EllipticCurve, self).__ne__(other) + return super().__ne__(other) return NotImplemented @classmethod @@ -518,7 +532,7 @@ def __init__(self, lib: Any, nid: int, name: str) -> None: self.name = name def __repr__(self) -> str: - return "" % (self.name,) + return f"" def _to_EC_KEY(self) -> Any: """ @@ -602,14 +616,15 @@ def __init__(self, name: "X509Name") -> None: def __setattr__(self, name: str, value: Any) -> None: if name.startswith("_"): - return super(X509Name, self).__setattr__(name, value) + return super().__setattr__(name, value) # Note: we really do not want str subclasses here, so we do not use # isinstance. if type(name) is not str: # noqa: E721 raise TypeError( - "attribute name must be string, not '%.200s'" - % (type(value).__name__,) + "attribute name must be string, not '{:.200}'".format( + type(value).__name__ + ) ) nid = _lib.OBJ_txt2nid(_byte_string(name)) @@ -701,7 +716,7 @@ def __repr__(self) -> str: ) _openssl_assert(format_result != _ffi.NULL) - return "" % ( + return "".format( _ffi.string(result_buffer).decode("utf-8"), ) @@ -839,7 +854,7 @@ def _nid(self) -> Any: _lib.X509_EXTENSION_get_object(self._extension) ) - _prefixes = { + _prefixes: typing.ClassVar[typing.Dict[int, str]] = { _lib.GEN_EMAIL: "email", _lib.GEN_DNS: "DNS", _lib.GEN_URI: "URI", @@ -1814,7 +1829,7 @@ class X509StoreContextError(Exception): def __init__( self, message: str, errors: List[Any], certificate: X509 ) -> None: - super(X509StoreContextError, self).__init__(message) + super().__init__(message) self.errors = errors self.certificate = certificate @@ -2166,7 +2181,7 @@ class Revoked: # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches # OCSP_crl_reason_str. We use the latter, just like the command line # program. - _crl_reasons = [ + _crl_reasons: typing.ClassVar[typing.List[bytes]] = [ b"unspecified", b"keyCompromise", b"CACompromise", @@ -2667,7 +2682,7 @@ def set_friendlyname(self, name: Optional[bytes]) -> None: self._friendlyname = None elif not isinstance(name, bytes): raise TypeError( - "name must be a byte string or None (not %r)" % (name,) + f"name must be a byte string or None (not {name!r})" ) self._friendlyname = name diff --git a/src/OpenSSL/debug.py b/src/OpenSSL/debug.py index e39b128a7..e0ed3f81d 100644 --- a/src/OpenSSL/debug.py +++ b/src/OpenSSL/debug.py @@ -1,17 +1,13 @@ -from __future__ import print_function - import ssl import sys import cffi - import cryptography import OpenSSL.SSL from . import version - _env_info = """\ pyOpenSSL: {pyopenssl} cryptography: {cryptography} diff --git a/src/OpenSSL/version.py b/src/OpenSSL/version.py index f7d871dc9..e5cfb04fc 100644 --- a/src/OpenSSL/version.py +++ b/src/OpenSSL/version.py @@ -25,4 +25,4 @@ __author__ = "The pyOpenSSL developers" __email__ = "cryptography-dev@python.org" __license__ = "Apache License, Version 2.0" -__copyright__ = "Copyright 2001-2023 {0}".format(__author__) +__copyright__ = f"Copyright 2001-2023 {__author__}" diff --git a/tests/conftest.py b/tests/conftest.py index 5bae6b8f7..573b4b3d6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,9 +7,10 @@ def pytest_report_header(config): - import OpenSSL.SSL import cryptography + import OpenSSL.SSL + return "OpenSSL: {openssl}\ncryptography: {cryptography}".format( openssl=OpenSSL.SSL.SSLeay_version(OpenSSL.SSL.SSLEAY_VERSION), cryptography=cryptography.__version__, diff --git a/tests/memdbg.py b/tests/memdbg.py index 0dd8c318e..5eed1ba8b 100644 --- a/tests/memdbg.py +++ b/tests/memdbg.py @@ -3,7 +3,6 @@ from cffi import api as _api - sys.modules["ssl"] = None sys.modules["_hashlib"] = None @@ -22,7 +21,7 @@ char **backtrace_symbols(void *const *buffer, int size); void backtrace_symbols_fd(void *const *buffer, int size, int fd); """ -) # noqa +) _api = _ffi.verify( """ #include @@ -79,7 +78,7 @@ def free(p): if p != _ffi.NULL: C.free(p) del heap[p] - log("free(0x%x)" % (int(_ffi.cast("int", p)),)) + log("free(0x{:x})".format(int(_ffi.cast("int", p)))) if _api.CRYPTO_set_mem_functions(malloc, realloc, free): diff --git a/tests/test_crypto.py b/tests/test_crypto.py index cb2140c3d..7a5d36e82 100644 --- a/tests/test_crypto.py +++ b/tests/test_crypto.py @@ -10,26 +10,25 @@ from datetime import datetime, timedelta, timezone from subprocess import PIPE, Popen -from cryptography import x509 -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import ec, ed25519, ed448, rsa - import flaky - import pytest +from cryptography import x509 +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ec, ed448, ed25519, rsa -from OpenSSL._util import ffi as _ffi, lib as _lib +from OpenSSL._util import ffi as _ffi +from OpenSSL._util import lib as _lib from OpenSSL.crypto import ( CRL, - Error, FILETYPE_ASN1, FILETYPE_PEM, FILETYPE_TEXT, - PKey, - Revoked, TYPE_DSA, TYPE_RSA, X509, + Error, + PKey, + Revoked, X509Extension, X509Name, X509Req, @@ -57,9 +56,9 @@ from OpenSSL.crypto import PKCS12, NetscapeSPKI from .util import ( - EqualityTestsMixin, NON_ASCII, WARNING_TYPE_EXPECTED, + EqualityTestsMixin, is_consistent_type, ) @@ -2581,7 +2580,7 @@ def test_export_without_bytes(self): with pytest.warns(DeprecationWarning) as w: warnings.simplefilter("always") dumped_p12 = p12.export(passphrase=b"randomtext".decode("ascii")) - msg = "{0} for passphrase is no longer accepted, use bytes".format( + msg = "{} for passphrase is no longer accepted, use bytes".format( WARNING_TYPE_EXPECTED ) assert msg == str(w[-1].message) @@ -2607,7 +2606,7 @@ def _runopenssl(pem, *args): Run the command line openssl tool with the given arguments and write the given PEM to its stdin. Not safe for quotes. """ - proc = Popen([b"openssl"] + list(args), stdin=PIPE, stdout=PIPE) + proc = Popen([b"openssl", *list(args)], stdin=PIPE, stdout=PIPE) proc.stdin.write(pem) proc.stdin.close() output = proc.stdout.read() @@ -3908,7 +3907,7 @@ def intermediate_ca_file(self, tmpdir): @staticmethod def _create_ca_file(base_path, hash_directory, cacert): - ca_hash = "{:08x}.0".format(cacert.subject_name_hash()) + ca_hash = f"{cacert.subject_name_hash():08x}.0" cafile = base_path.join(hash_directory, ca_hash) cafile.write_binary( dump_certificate(FILETYPE_PEM, cacert), ensure=True @@ -4041,14 +4040,14 @@ def test_sign_verify_with_text(self): with pytest.warns(DeprecationWarning) as w: warnings.simplefilter("always") sig = sign(priv_key, content, digest) - assert "{0} for data is no longer accepted, use bytes".format( + assert "{} for data is no longer accepted, use bytes".format( WARNING_TYPE_EXPECTED ) == str(w[-1].message) with pytest.warns(DeprecationWarning) as w: warnings.simplefilter("always") verify(cert, sig, content, digest) - assert "{0} for data is no longer accepted, use bytes".format( + assert "{} for data is no longer accepted, use bytes".format( WARNING_TYPE_EXPECTED ) == str(w[-1].message) @@ -4132,7 +4131,7 @@ def test_repr(self): """ curves = get_elliptic_curves() curve = next(iter(curves)) - assert "" % (curve.name,) == repr(curve) + assert f"" == repr(curve) def test_to_EC_KEY(self): """ diff --git a/tests/test_ssl.py b/tests/test_ssl.py index 369f6a6d7..a23e16210 100644 --- a/tests/test_ssl.py +++ b/tests/test_ssl.py @@ -27,41 +27,46 @@ AF_INET6, MSG_PEEK, SHUT_RDWR, - error, socket, ) from sys import getfilesystemencoding, platform from typing import Union from weakref import ref +import flaky +import pytest from cryptography import x509 -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.x509.oid import NameOID - -import flaky - from pretend import raiser -import pytest - from OpenSSL import SSL +from OpenSSL._util import ffi as _ffi +from OpenSSL._util import lib as _lib +from OpenSSL.crypto import ( + FILETYPE_PEM, + TYPE_RSA, + X509, + PKey, + X509Extension, + X509Store, + dump_certificate, + dump_privatekey, + get_elliptic_curves, + load_certificate, + load_privatekey, +) from OpenSSL.SSL import ( - Connection, - Context, DTLS_METHOD, - Error, MODE_RELEASE_BUFFERS, NO_OVERLAPPING_PROTOCOLS, - OPENSSL_VERSION_NUMBER, OP_COOKIE_EXCHANGE, OP_NO_COMPRESSION, OP_NO_QUERY_MTU, - OP_NO_SSLv2, - OP_NO_SSLv3, OP_NO_TICKET, OP_SINGLE_DH_USE, + OPENSSL_VERSION_NUMBER, RECEIVED_SHUTDOWN, SENT_SHUTDOWN, SESS_CACHE_BOTH, @@ -72,11 +77,6 @@ SESS_CACHE_NO_INTERNAL_STORE, SESS_CACHE_OFF, SESS_CACHE_SERVER, - SSLEAY_BUILT_ON, - SSLEAY_CFLAGS, - SSLEAY_DIR, - SSLEAY_PLATFORM, - SSLEAY_VERSION, SSL_CB_ACCEPT_EXIT, SSL_CB_ACCEPT_LOOP, SSL_CB_ALERT, @@ -93,45 +93,41 @@ SSL_ST_ACCEPT, SSL_ST_CONNECT, SSL_ST_MASK, - SSLeay_version, - SSLv23_METHOD, - Session, - SysCallError, + SSLEAY_BUILT_ON, + SSLEAY_CFLAGS, + SSLEAY_DIR, + SSLEAY_PLATFORM, + SSLEAY_VERSION, TLS1_1_VERSION, TLS1_2_VERSION, TLS1_3_VERSION, TLS_METHOD, - TLSv1_1_METHOD, - TLSv1_2_METHOD, - TLSv1_METHOD, VERIFY_CLIENT_ONCE, VERIFY_FAIL_IF_NO_PEER_CERT, VERIFY_NONE, VERIFY_PEER, + Connection, + Context, + Error, + OP_NO_SSLv2, + OP_NO_SSLv3, + Session, + SSLeay_version, + SSLv23_METHOD, + SysCallError, + TLSv1_1_METHOD, + TLSv1_2_METHOD, + TLSv1_METHOD, WantReadError, WantWriteError, ZeroReturnError, _make_requires, ) -from OpenSSL._util import ffi as _ffi, lib as _lib -from OpenSSL.crypto import ( - FILETYPE_PEM, - PKey, - TYPE_RSA, - X509, - X509Extension, - X509Store, - dump_certificate, - dump_privatekey, - get_elliptic_curves, - load_certificate, - load_privatekey, -) try: from OpenSSL.SSL import ( - SSL_ST_INIT, SSL_ST_BEFORE, + SSL_ST_INIT, SSL_ST_OK, SSL_ST_RENEGOTIATE, ) @@ -153,7 +149,6 @@ ) from .util import NON_ASCII, WARNING_TYPE_EXPECTED, is_consistent_type - # openssl dhparam 2048 -out dh-2048.pem dhparam = """\ -----BEGIN DH PARAMETERS----- @@ -170,7 +165,7 @@ def socket_any_family(): try: return socket(AF_INET) - except error as e: + except OSError as e: if e.errno == EAFNOSUPPORT: return socket(AF_INET6) raise @@ -641,7 +636,7 @@ def _use_privatekey_file_test(self, pemfile, filetype): key = PKey() key.generate_key(TYPE_RSA, 1024) - with open(pemfile, "wt") as pem: + with open(pemfile, "w") as pem: pem.write(dump_privatekey(FILETYPE_PEM, key).decode("ascii")) ctx = Context(SSLv23_METHOD) @@ -1723,7 +1718,7 @@ def test_set_tlsext_use_srtp_not_bytes(self): """ context = Context(SSLv23_METHOD) with pytest.raises(TypeError): - context.set_tlsext_use_srtp(str("SRTP_AES128_CM_SHA1_80")) + context.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80") def test_set_tlsext_use_srtp_invalid_profile(self): """ @@ -1782,7 +1777,7 @@ def replacement(connection): # pragma: no cover if callback is not None: referrers = get_referrers(callback) if len(referrers) > 1: # pragma: nocover - pytest.fail("Some references remain: %r" % (referrers,)) + pytest.fail(f"Some references remain: {referrers!r}") def test_no_servername(self): """ @@ -2370,7 +2365,7 @@ def test_connect_refused(self): # 2.6: https://github.com/pytest-dev/pytest/issues/988 try: clientSSL.connect((loopback_address(client), 1)) - except error as e: + except OSError as e: exc = e assert exc.args[0] == ECONNREFUSED @@ -2708,7 +2703,7 @@ def callback(conn, cert, errnum, depth, ok): # pragma: no cover if callback is not None: # pragma: nocover referrers = get_referrers(callback) if len(referrers) > 1: - pytest.fail("Some references remain: %r" % (referrers,)) + pytest.fail(f"Some references remain: {referrers!r}") def test_get_session_unconnected(self): """ @@ -2850,7 +2845,7 @@ def test_wantWriteError(self): for i in range(1024 * 1024 * 64): try: client_socket.send(msg) - except error as e: + except OSError as e: if e.errno == EWOULDBLOCK: break raise # pragma: no cover @@ -3121,7 +3116,7 @@ def test_text(self): server, client = loopback() with pytest.warns(DeprecationWarning) as w: count = server.send(b"xy".decode("ascii")) - assert "{0} for buf is no longer accepted, use bytes".format( + assert "{} for buf is no longer accepted, use bytes".format( WARNING_TYPE_EXPECTED ) == str(w[-1].message) assert count == 2 @@ -3328,7 +3323,7 @@ def test_text(self): server, client = loopback() with pytest.warns(DeprecationWarning) as w: server.sendall(b"x".decode("ascii")) - assert "{0} for buf is no longer accepted, use bytes".format( + assert "{} for buf is no longer accepted, use bytes".format( WARNING_TYPE_EXPECTED ) == str(w[-1].message) assert client.recv(1) == b"x"