aboutsummaryrefslogtreecommitdiffstats
path: root/src/cryptography/hazmat/bindings/utils.py
diff options
context:
space:
mode:
authorDonald Stufft <donald@stufft.io>2014-11-07 19:17:08 -0500
committerDonald Stufft <donald@stufft.io>2014-11-13 07:56:31 -0500
commitc62a78c015cf7aeb0c05bce82ef14cd86fe0b0fc (patch)
tree55482d6f2e98ff65f2174294f64ad96b7be68717 /src/cryptography/hazmat/bindings/utils.py
parentd9f137db78d451ecb6ef7925b7dec0139ca59898 (diff)
downloadcryptography-c62a78c015cf7aeb0c05bce82ef14cd86fe0b0fc.tar.gz
cryptography-c62a78c015cf7aeb0c05bce82ef14cd86fe0b0fc.tar.bz2
cryptography-c62a78c015cf7aeb0c05bce82ef14cd86fe0b0fc.zip
Move the cryptography package into a src/ subdirectory
Due to differences in how py.test determines which module to ``import`` the test suite actually runs against the cryptography which is in the *current* directory instead of the cryptography which is installed. The problem essentially boils down to when there is a tests/__init__.py then py.test adds the current directory to the front of the sys.path, causing it to take precedence over the installed location. This means that running the tests relies on the implicit compile that CFFI does instead of testing against what people will actually be runnning, which is the module compiled by setup.py.
Diffstat (limited to 'src/cryptography/hazmat/bindings/utils.py')
-rw-r--r--src/cryptography/hazmat/bindings/utils.py144
1 files changed, 144 insertions, 0 deletions
diff --git a/src/cryptography/hazmat/bindings/utils.py b/src/cryptography/hazmat/bindings/utils.py
new file mode 100644
index 00000000..55b61292
--- /dev/null
+++ b/src/cryptography/hazmat/bindings/utils.py
@@ -0,0 +1,144 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import, division, print_function
+
+import binascii
+import sys
+import threading
+
+from cffi import FFI
+from cffi.verifier import Verifier
+
+
+class LazyLibrary(object):
+ def __init__(self, ffi):
+ self._ffi = ffi
+ self._lib = None
+ self._lock = threading.Lock()
+
+ def __getattr__(self, name):
+ if self._lib is None:
+ with self._lock:
+ if self._lib is None:
+ self._lib = self._ffi.verifier.load_library()
+
+ return getattr(self._lib, name)
+
+
+def load_library_for_binding(ffi, module_prefix, modules):
+ lib = ffi.verifier.load_library()
+
+ for name in modules:
+ module_name = module_prefix + name
+ module = sys.modules[module_name]
+ for condition, names in module.CONDITIONAL_NAMES.items():
+ if not getattr(lib, condition):
+ for name in names:
+ delattr(lib, name)
+
+ return lib
+
+
+def build_ffi_for_binding(module_prefix, modules, pre_include="",
+ post_include="", libraries=[], extra_compile_args=[],
+ extra_link_args=[]):
+ """
+ Modules listed in ``modules`` should have the following attributes:
+
+ * ``INCLUDES``: A string containing C includes.
+ * ``TYPES``: A string containing C declarations for types.
+ * ``FUNCTIONS``: A string containing C declarations for functions.
+ * ``MACROS``: A string containing C declarations for any macros.
+ * ``CUSTOMIZATIONS``: A string containing arbitrary top-level C code, this
+ can be used to do things like test for a define and provide an
+ alternate implementation based on that.
+ * ``CONDITIONAL_NAMES``: A dict mapping strings of condition names from the
+ library to a list of names which will not be present without the
+ condition.
+ """
+ types = []
+ includes = []
+ functions = []
+ macros = []
+ customizations = []
+ for name in modules:
+ module_name = module_prefix + name
+ __import__(module_name)
+ module = sys.modules[module_name]
+
+ types.append(module.TYPES)
+ macros.append(module.MACROS)
+ functions.append(module.FUNCTIONS)
+ includes.append(module.INCLUDES)
+ customizations.append(module.CUSTOMIZATIONS)
+
+ # We include functions here so that if we got any of their definitions
+ # wrong, the underlying C compiler will explode. In C you are allowed
+ # to re-declare a function if it has the same signature. That is:
+ # int foo(int);
+ # int foo(int);
+ # is legal, but the following will fail to compile:
+ # int foo(int);
+ # int foo(short);
+ verify_source = "\n".join(
+ [pre_include] +
+ includes +
+ [post_include] +
+ functions +
+ customizations
+ )
+ ffi = build_ffi(
+ cdef_source="\n".join(types + functions + macros),
+ verify_source=verify_source,
+ libraries=libraries,
+ extra_compile_args=extra_compile_args,
+ extra_link_args=extra_link_args,
+ )
+
+ return ffi
+
+
+def build_ffi(cdef_source, verify_source, libraries=[], extra_compile_args=[],
+ extra_link_args=[]):
+ ffi = FFI()
+ ffi.cdef(cdef_source)
+
+ ffi.verifier = Verifier(
+ ffi,
+ verify_source,
+ tmpdir='',
+ modulename=_create_modulename(cdef_source, verify_source, sys.version),
+ libraries=libraries,
+ ext_package="cryptography",
+ extra_compile_args=extra_compile_args,
+ extra_link_args=extra_link_args,
+ )
+ return ffi
+
+
+def _create_modulename(cdef_sources, source, sys_version):
+ """
+ cffi creates a modulename internally that incorporates the cffi version.
+ This will cause cryptography's wheels to break when the version of cffi
+ the user has does not match what was used when building the wheel. To
+ resolve this we build our own modulename that uses most of the same code
+ from cffi but elides the version key.
+ """
+ key = '\x00'.join([sys_version[:3], source, cdef_sources])
+ key = key.encode('utf-8')
+ k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff)
+ k1 = k1.lstrip('0x').rstrip('L')
+ k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff)
+ k2 = k2.lstrip('0').rstrip('L')
+ return '_Cryptography_cffi_{0}{1}'.format(k1, k2)