Skip to content

Commit

Permalink
Fixed pyca#3747 -- cache extensions on x.509 objects (pyca#3769)
Browse files Browse the repository at this point in the history
* Fixed pyca#3747 -- cache extensions on x.509 objects

* be kind to cpython, save a dict lookup

* flake8

* changelog
  • Loading branch information
alex authored and reaperhulk committed Jul 9, 2017
1 parent ae487bb commit 0c9aed9
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 4 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ Changelog
and
:meth:`~cryptography.hazmat.primitives.asymmetric.dh.DHParameters.parameter_bytes`
.
* The ``extensions`` attribute on :class:`~cryptography.x509.Certificate`,
:class:`~cryptography.x509.CertificateSigningRequest`,
:class:`~cryptography.x509.CertificateRevocationList`, and
:class:`~cryptography.x509.RevokedCertificate` now caches the computed
``Extensions`` object. There should be no performance change, just a
performance improvement for programs accessing the ``extensions`` attribute
multiple times.

1.9 - 2017-05-29
~~~~~~~~~~~~~~~~
Expand Down
8 changes: 4 additions & 4 deletions src/cryptography/hazmat/backends/openssl/x509.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def signature_algorithm_oid(self):
oid = _obj2txt(self._backend, alg[0].algorithm)
return x509.ObjectIdentifier(oid)

@property
@utils.cached_property
def extensions(self):
if self._backend._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER:
return _CERTIFICATE_EXTENSION_PARSER.parse(
Expand Down Expand Up @@ -200,7 +200,7 @@ def revocation_date(self):
)
)

@property
@utils.cached_property
def extensions(self):
return _REVOKED_CERTIFICATE_EXTENSION_PARSER.parse(
self._backend, self._x509_revoked
Expand Down Expand Up @@ -334,7 +334,7 @@ def __len__(self):
else:
return self._backend._lib.sk_X509_REVOKED_num(revoked)

@property
@utils.cached_property
def extensions(self):
return _CRL_EXTENSION_PARSER.parse(self._backend, self._x509_crl)

Expand Down Expand Up @@ -391,7 +391,7 @@ def signature_algorithm_oid(self):
oid = _obj2txt(self._backend, alg[0].algorithm)
return x509.ObjectIdentifier(oid)

@property
@utils.cached_property
def extensions(self):
x509_exts = self._backend._lib.X509_REQ_get_extensions(self._x509_req)
return _CSR_EXTENSION_PARSER.parse(self._backend, x509_exts)
Expand Down
14 changes: 14 additions & 0 deletions src/cryptography/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,17 @@ def deprecated(value, module_name, message, warning_class):
if not isinstance(module, _ModuleWithDeprecations):
sys.modules[module_name] = _ModuleWithDeprecations(module)
return _DeprecatedValue(value, message, warning_class)


def cached_property(func):
cached_name = "_cached_{0}".format(func)
sentinel = object()

def inner(instance):
cache = getattr(instance, cached_name, sentinel)
if cache is not sentinel:
return cache
result = func(instance)
setattr(instance, cached_name, result)
return result
return property(inner)
47 changes: 47 additions & 0 deletions tests/test_cryptography_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,55 @@

from __future__ import absolute_import, division, print_function

import pytest

from cryptography import utils


def test_int_from_bytes_bytearray():
assert utils.int_from_bytes(bytearray(b"\x02\x10"), "big") == 528


class TestCachedProperty(object):
def test_simple(self):
accesses = []

class T(object):
@utils.cached_property
def t(self):
accesses.append(None)
return 14

assert T.t
t = T()
assert t.t == 14
assert len(accesses) == 1
assert t.t == 14
assert len(accesses) == 1

t = T()
assert t.t == 14
assert len(accesses) == 2
assert t.t == 14
assert len(accesses) == 2

def test_set(self):
accesses = []

class T(object):
@utils.cached_property
def t(self):
accesses.append(None)
return 14

t = T()
with pytest.raises(AttributeError):
t.t = None
assert len(accesses) == 0
assert t.t == 14
assert len(accesses) == 1
with pytest.raises(AttributeError):
t.t = None
assert len(accesses) == 1
assert t.t == 14
assert len(accesses) == 1

0 comments on commit 0c9aed9

Please sign in to comment.