Repository: ColdHeat/pybluemonday (language: python)

Issue:
Potential issues in the Windows wheels
See https://github.com/CTFd/CTFd/issues/1855


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7939648..6b15bb1 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,8 +1,11 @@
 name: Build wheels
 
 on:
- release:
-   types: [created, edited]
+  release:
+    types: [created, edited]
+  push:
+  pull_request:
+    types: [opened, reopened, edited, synchronize]
 
 jobs:
   build_wheels:
@@ -23,7 +26,6 @@ jobs:
       - name: Install dependencies
         run: |
           python -m pip install --upgrade -r development.txt
-          python -m pip install cibuildwheel==1.6.4
 
       - name: Install Visual C++ for Python 2.7
         if: runner.os == 'Windows'
@@ -31,21 +33,31 @@ jobs:
           choco install vcpython27 -f -y
           choco install golang -f -y
           choco install mingw -f -y
+          go get -d
+
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v1
+        if: runner.os == 'Linux'
 
       - name: Build Linux wheels and sdist
         if: runner.os == 'Linux'
         env:
-          CIBW_BEFORE_ALL: 'pwd && ls && chmod +x ./scripts/setup-linux.sh && ./scripts/setup-linux.sh'
           CIBW_BUILD: cp3?-*
           CIBW_SKIP: "cp35-* *-win32 *-manylinux_i686 *-manylinux_aarch64 *-manylinux_ppc64le *-manylinux_s390x"
         run: |
           python setup.py sdist
           python3 -m cibuildwheel --output-dir wheelhouse
 
+      - name: Build ARM64 wheel
+        if: runner.os == 'Linux'
+        env:
+          CIBW_ARCHS_LINUX: aarch64
+        run: |
+          python3 -m cibuildwheel --output-dir wheelhouse
+
       - name: Build macOS wheels
         if: runner.os == 'macOS'
         env:
-          CIBW_BEFORE_ALL: 'pwd && ls && chmod +x ./scripts/setup-macos.sh && ./scripts/setup-macos.sh'
           CIBW_BUILD: cp3?-*
           CIBW_SKIP: "cp35-* *-win32 *-manylinux_i686 *-manylinux_aarch64 *-manylinux_ppc64le *-manylinux_s390x"
         run: |
@@ -57,7 +69,6 @@ jobs:
           CIBW_BUILD: cp3?-*
           CIBW_SKIP: "cp35-* *-win32 *-manylinux_i686 *-manylinux_aarch64 *-manylinux_ppc64le *-manylinux_s390x"
         run: |
-          go version
           python -m cibuildwheel --output-dir wheelhouse
 
       - name: Publish sdist
@@ -79,6 +90,7 @@ jobs:
         env:
           TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
           TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+        if: ${{ github.event_name == 'release' && env.TWINE_USERNAME != null }}
         run: |
           twine check ./wheelhouse/*.whl
           twine upload --skip-existing ./wheelhouse/*
diff --git a/Makefile b/Makefile
index 30bb125..284af64 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ so:
 	go build -buildmode=c-shared -o bluemonday.so .
 
 ffi:
-	python build_ffi.py
+	python3 build_ffi.py
 
 clean:
 	rm -f bluemonday.so
diff --git a/bluemonday.c b/bluemonday.c
deleted file mode 100644
index 85f4042..0000000
--- a/bluemonday.c
+++ /dev/null
@@ -1,1128 +0,0 @@
-#define _CFFI_
-
-/* We try to define Py_LIMITED_API before including Python.h.
-
-   Mess: we can only define it if Py_DEBUG, Py_TRACE_REFS and
-   Py_REF_DEBUG are not defined.  This is a best-effort approximation:
-   we can learn about Py_DEBUG from pyconfig.h, but it is unclear if
-   the same works for the other two macros.  Py_DEBUG implies them,
-   but not the other way around.
-
-   The implementation is messy (issue #350): on Windows, with _MSC_VER,
-   we have to define Py_LIMITED_API even before including pyconfig.h.
-   In that case, we guess what pyconfig.h will do to the macros above,
-   and check our guess after the #include.
-
-   Note that on Windows, with CPython 3.x, you need >= 3.5 and virtualenv
-   version >= 16.0.0.  With older versions of either, you don't get a
-   copy of PYTHON3.DLL in the virtualenv.  We can't check the version of
-   CPython *before* we even include pyconfig.h.  ffi.set_source() puts
-   a ``#define _CFFI_NO_LIMITED_API'' at the start of this file if it is
-   running on Windows < 3.5, as an attempt at fixing it, but that's
-   arguably wrong because it may not be the target version of Python.
-   Still better than nothing I guess.  As another workaround, you can
-   remove the definition of Py_LIMITED_API here.
-
-   See also 'py_limited_api' in cffi/setuptools_ext.py.
-*/
-#if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API)
-#  ifdef _MSC_VER
-#    if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API)
-#      define Py_LIMITED_API
-#    endif
-#    include <pyconfig.h>
-     /* sanity-check: Py_LIMITED_API will cause crashes if any of these
-        are also defined.  Normally, the Python file PC/pyconfig.h does not
-        cause any of these to be defined, with the exception that _DEBUG
-        causes Py_DEBUG.  Double-check that. */
-#    ifdef Py_LIMITED_API
-#      if defined(Py_DEBUG)
-#        error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set"
-#      endif
-#      if defined(Py_TRACE_REFS)
-#        error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set"
-#      endif
-#      if defined(Py_REF_DEBUG)
-#        error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set"
-#      endif
-#    endif
-#  else
-#    include <pyconfig.h>
-#    if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API)
-#      define Py_LIMITED_API
-#    endif
-#  endif
-#endif
-
-#include <Python.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-#include <stddef.h>
-
-/* This part is from file 'cffi/parse_c_type.h'.  It is copied at the
-   beginning of C sources generated by CFFI's ffi.set_source(). */
-
-typedef void *_cffi_opcode_t;
-
-#define _CFFI_OP(opcode, arg)   (_cffi_opcode_t)(opcode | (((uintptr_t)(arg)) << 8))
-#define _CFFI_GETOP(cffi_opcode)    ((unsigned char)(uintptr_t)cffi_opcode)
-#define _CFFI_GETARG(cffi_opcode)   (((intptr_t)cffi_opcode) >> 8)
-
-#define _CFFI_OP_PRIMITIVE       1
-#define _CFFI_OP_POINTER         3
-#define _CFFI_OP_ARRAY           5
-#define _CFFI_OP_OPEN_ARRAY      7
-#define _CFFI_OP_STRUCT_UNION    9
-#define _CFFI_OP_ENUM           11
-#define _CFFI_OP_FUNCTION       13
-#define _CFFI_OP_FUNCTION_END   15
-#define _CFFI_OP_NOOP           17
-#define _CFFI_OP_BITFIELD       19
-#define _CFFI_OP_TYPENAME       21
-#define _CFFI_OP_CPYTHON_BLTN_V 23   // varargs
-#define _CFFI_OP_CPYTHON_BLTN_N 25   // noargs
-#define _CFFI_OP_CPYTHON_BLTN_O 27   // O  (i.e. a single arg)
-#define _CFFI_OP_CONSTANT       29
-#define _CFFI_OP_CONSTANT_INT   31
-#define _CFFI_OP_GLOBAL_VAR     33
-#define _CFFI_OP_DLOPEN_FUNC    35
-#define _CFFI_OP_DLOPEN_CONST   37
-#define _CFFI_OP_GLOBAL_VAR_F   39
-#define _CFFI_OP_EXTERN_PYTHON  41
-
-#define _CFFI_PRIM_VOID          0
-#define _CFFI_PRIM_BOOL          1
-#define _CFFI_PRIM_CHAR          2
-#define _CFFI_PRIM_SCHAR         3
-#define _CFFI_PRIM_UCHAR         4
-#define _CFFI_PRIM_SHORT         5
-#define _CFFI_PRIM_USHORT        6
-#define _CFFI_PRIM_INT           7
-#define _CFFI_PRIM_UINT          8
-#define _CFFI_PRIM_LONG          9
-#define _CFFI_PRIM_ULONG        10
-#define _CFFI_PRIM_LONGLONG     11
-#define _CFFI_PRIM_ULONGLONG    12
-#define _CFFI_PRIM_FLOAT        13
-#define _CFFI_PRIM_DOUBLE       14
-#define _CFFI_PRIM_LONGDOUBLE   15
-
-#define _CFFI_PRIM_WCHAR        16
-#define _CFFI_PRIM_INT8         17
-#define _CFFI_PRIM_UINT8        18
-#define _CFFI_PRIM_INT16        19
-#define _CFFI_PRIM_UINT16       20
-#define _CFFI_PRIM_INT32        21
-#define _CFFI_PRIM_UINT32       22
-#define _CFFI_PRIM_INT64        23
-#define _CFFI_PRIM_UINT64       24
-#define _CFFI_PRIM_INTPTR       25
-#define _CFFI_PRIM_UINTPTR      26
-#define _CFFI_PRIM_PTRDIFF      27
-#define _CFFI_PRIM_SIZE         28
-#define _CFFI_PRIM_SSIZE        29
-#define _CFFI_PRIM_INT_LEAST8   30
-#define _CFFI_PRIM_UINT_LEAST8  31
-#define _CFFI_PRIM_INT_LEAST16  32
-#define _CFFI_PRIM_UINT_LEAST16 33
-#define _CFFI_PRIM_INT_LEAST32  34
-#define _CFFI_PRIM_UINT_LEAST32 35
-#define _CFFI_PRIM_INT_LEAST64  36
-#define _CFFI_PRIM_UINT_LEAST64 37
-#define _CFFI_PRIM_INT_FAST8    38
-#define _CFFI_PRIM_UINT_FAST8   39
-#define _CFFI_PRIM_INT_FAST16   40
-#define _CFFI_PRIM_UINT_FAST16  41
-#define _CFFI_PRIM_INT_FAST32   42
-#define _CFFI_PRIM_UINT_FAST32  43
-#define _CFFI_PRIM_INT_FAST64   44
-#define _CFFI_PRIM_UINT_FAST64  45
-#define _CFFI_PRIM_INTMAX       46
-#define _CFFI_PRIM_UINTMAX      47
-#define _CFFI_PRIM_FLOATCOMPLEX 48
-#define _CFFI_PRIM_DOUBLECOMPLEX 49
-#define _CFFI_PRIM_CHAR16       50
-#define _CFFI_PRIM_CHAR32       51
-
-#define _CFFI__NUM_PRIM         52
-#define _CFFI__UNKNOWN_PRIM           (-1)
-#define _CFFI__UNKNOWN_FLOAT_PRIM     (-2)
-#define _CFFI__UNKNOWN_LONG_DOUBLE    (-3)
-
-#define _CFFI__IO_FILE_STRUCT         (-1)
-
-
-struct _cffi_global_s {
-    const char *name;
-    void *address;
-    _cffi_opcode_t type_op;
-    void *size_or_direct_fn;  // OP_GLOBAL_VAR: size, or 0 if unknown
-                              // OP_CPYTHON_BLTN_*: addr of direct function
-};
-
-struct _cffi_getconst_s {
-    unsigned long long value;
-    const struct _cffi_type_context_s *ctx;
-    int gindex;
-};
-
-struct _cffi_struct_union_s {
-    const char *name;
-    int type_index;          // -> _cffi_types, on a OP_STRUCT_UNION
-    int flags;               // _CFFI_F_* flags below
-    size_t size;
-    int alignment;
-    int first_field_index;   // -> _cffi_fields array
-    int num_fields;
-};
-#define _CFFI_F_UNION         0x01   // is a union, not a struct
-#define _CFFI_F_CHECK_FIELDS  0x02   // complain if fields are not in the
-                                     // "standard layout" or if some are missing
-#define _CFFI_F_PACKED        0x04   // for CHECK_FIELDS, assume a packed struct
-#define _CFFI_F_EXTERNAL      0x08   // in some other ffi.include()
-#define _CFFI_F_OPAQUE        0x10   // opaque
-
-struct _cffi_field_s {
-    const char *name;
-    size_t field_offset;
-    size_t field_size;
-    _cffi_opcode_t field_type_op;
-};
-
-struct _cffi_enum_s {
-    const char *name;
-    int type_index;          // -> _cffi_types, on a OP_ENUM
-    int type_prim;           // _CFFI_PRIM_xxx
-    const char *enumerators; // comma-delimited string
-};
-
-struct _cffi_typename_s {
-    const char *name;
-    int type_index;   /* if opaque, points to a possibly artificial
-                         OP_STRUCT which is itself opaque */
-};
-
-struct _cffi_type_context_s {
-    _cffi_opcode_t *types;
-    const struct _cffi_global_s *globals;
-    const struct _cffi_field_s *fields;
-    const struct _cffi_struct_union_s *struct_unions;
-    const struct _cffi_enum_s *enums;
-    const struct _cffi_typename_s *typenames;
-    int num_globals;
-    int num_struct_unions;
-    int num_enums;
-    int num_typenames;
-    const char *const *includes;
-    int num_types;
-    int flags;      /* future extension */
-};
-
-struct _cffi_parse_info_s {
-    const struct _cffi_type_context_s *ctx;
-    _cffi_opcode_t *output;
-    unsigned int output_size;
-    size_t error_location;
-    const char *error_message;
-};
-
-struct _cffi_externpy_s {
-    const char *name;
-    size_t size_of_result;
-    void *reserved1, *reserved2;
-};
-
-#ifdef _CFFI_INTERNAL
-static int parse_c_type(struct _cffi_parse_info_s *info, const char *input);
-static int search_in_globals(const struct _cffi_type_context_s *ctx,
-                             const char *search, size_t search_len);
-static int search_in_struct_unions(const struct _cffi_type_context_s *ctx,
-                                   const char *search, size_t search_len);
-#endif
-
-/* this block of #ifs should be kept exactly identical between
-   c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py
-   and cffi/_cffi_include.h */
-#if defined(_MSC_VER)
-# include <malloc.h>   /* for alloca() */
-# if _MSC_VER < 1600   /* MSVC < 2010 */
-   typedef __int8 int8_t;
-   typedef __int16 int16_t;
-   typedef __int32 int32_t;
-   typedef __int64 int64_t;
-   typedef unsigned __int8 uint8_t;
-   typedef unsigned __int16 uint16_t;
-   typedef unsigned __int32 uint32_t;
-   typedef unsigned __int64 uint64_t;
-   typedef __int8 int_least8_t;
-   typedef __int16 int_least16_t;
-   typedef __int32 int_least32_t;
-   typedef __int64 int_least64_t;
-   typedef unsigned __int8 uint_least8_t;
-   typedef unsigned __int16 uint_least16_t;
-   typedef unsigned __int32 uint_least32_t;
-   typedef unsigned __int64 uint_least64_t;
-   typedef __int8 int_fast8_t;
-   typedef __int16 int_fast16_t;
-   typedef __int32 int_fast32_t;
-   typedef __int64 int_fast64_t;
-   typedef unsigned __int8 uint_fast8_t;
-   typedef unsigned __int16 uint_fast16_t;
-   typedef unsigned __int32 uint_fast32_t;
-   typedef unsigned __int64 uint_fast64_t;
-   typedef __int64 intmax_t;
-   typedef unsigned __int64 uintmax_t;
-# else
-#  include <stdint.h>
-# endif
-# if _MSC_VER < 1800   /* MSVC < 2013 */
-#  ifndef __cplusplus
-    typedef unsigned char _Bool;
-#  endif
-# endif
-#else
-# include <stdint.h>
-# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
-#  include <alloca.h>
-# endif
-#endif
-
-#ifdef __GNUC__
-# define _CFFI_UNUSED_FN  __attribute__((unused))
-#else
-# define _CFFI_UNUSED_FN  /* nothing */
-#endif
-
-#ifdef __cplusplus
-# ifndef _Bool
-   typedef bool _Bool;   /* semi-hackish: C++ has no _Bool; bool is builtin */
-# endif
-#endif
-
-/**********  CPython-specific section  **********/
-#ifndef PYPY_VERSION
-
-
-#if PY_MAJOR_VERSION >= 3
-# define PyInt_FromLong PyLong_FromLong
-#endif
-
-#define _cffi_from_c_double PyFloat_FromDouble
-#define _cffi_from_c_float PyFloat_FromDouble
-#define _cffi_from_c_long PyInt_FromLong
-#define _cffi_from_c_ulong PyLong_FromUnsignedLong
-#define _cffi_from_c_longlong PyLong_FromLongLong
-#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong
-#define _cffi_from_c__Bool PyBool_FromLong
-
-#define _cffi_to_c_double PyFloat_AsDouble
-#define _cffi_to_c_float PyFloat_AsDouble
-
-#define _cffi_from_c_int(x, type)                                        \
-    (((type)-1) > 0 ? /* unsigned */                                     \
-        (sizeof(type) < sizeof(long) ?                                   \
-            PyInt_FromLong((long)x) :                                    \
-         sizeof(type) == sizeof(long) ?                                  \
-            PyLong_FromUnsignedLong((unsigned long)x) :                  \
-            PyLong_FromUnsignedLongLong((unsigned long long)x)) :        \
-        (sizeof(type) <= sizeof(long) ?                                  \
-            PyInt_FromLong((long)x) :                                    \
-            PyLong_FromLongLong((long long)x)))
-
-#define _cffi_to_c_int(o, type)                                          \
-    ((type)(                                                             \
-     sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o)        \
-                                         : (type)_cffi_to_c_i8(o)) :     \
-     sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o)       \
-                                         : (type)_cffi_to_c_i16(o)) :    \
-     sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o)       \
-                                         : (type)_cffi_to_c_i32(o)) :    \
-     sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o)       \
-                                         : (type)_cffi_to_c_i64(o)) :    \
-     (Py_FatalError("unsupported size for type " #type), (type)0)))
-
-#define _cffi_to_c_i8                                                    \
-                 ((int(*)(PyObject *))_cffi_exports[1])
-#define _cffi_to_c_u8                                                    \
-                 ((int(*)(PyObject *))_cffi_exports[2])
-#define _cffi_to_c_i16                                                   \
-                 ((int(*)(PyObject *))_cffi_exports[3])
-#define _cffi_to_c_u16                                                   \
-                 ((int(*)(PyObject *))_cffi_exports[4])
-#define _cffi_to_c_i32                                                   \
-                 ((int(*)(PyObject *))_cffi_exports[5])
-#define _cffi_to_c_u32                                                   \
-                 ((unsigned int(*)(PyObject *))_cffi_exports[6])
-#define _cffi_to_c_i64                                                   \
-                 ((long long(*)(PyObject *))_cffi_exports[7])
-#define _cffi_to_c_u64                                                   \
-                 ((unsigned long long(*)(PyObject *))_cffi_exports[8])
-#define _cffi_to_c_char                                                  \
-                 ((int(*)(PyObject *))_cffi_exports[9])
-#define _cffi_from_c_pointer                                             \
-    ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[10])
-#define _cffi_to_c_pointer                                               \
-    ((char *(*)(PyObject *, struct _cffi_ctypedescr *))_cffi_exports[11])
-#define _cffi_get_struct_layout                                          \
-    not used any more
-#define _cffi_restore_errno                                              \
-    ((void(*)(void))_cffi_exports[13])
-#define _cffi_save_errno                                                 \
-    ((void(*)(void))_cffi_exports[14])
-#define _cffi_from_c_char                                                \
-    ((PyObject *(*)(char))_cffi_exports[15])
-#define _cffi_from_c_deref                                               \
-    ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[16])
-#define _cffi_to_c                                                       \
-    ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[17])
-#define _cffi_from_c_struct                                              \
-    ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[18])
-#define _cffi_to_c_wchar_t                                               \
-    ((_cffi_wchar_t(*)(PyObject *))_cffi_exports[19])
-#define _cffi_from_c_wchar_t                                             \
-    ((PyObject *(*)(_cffi_wchar_t))_cffi_exports[20])
-#define _cffi_to_c_long_double                                           \
-    ((long double(*)(PyObject *))_cffi_exports[21])
-#define _cffi_to_c__Bool                                                 \
-    ((_Bool(*)(PyObject *))_cffi_exports[22])
-#define _cffi_prepare_pointer_call_argument                              \
-    ((Py_ssize_t(*)(struct _cffi_ctypedescr *,                           \
-                    PyObject *, char **))_cffi_exports[23])
-#define _cffi_convert_array_from_object                                  \
-    ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[24])
-#define _CFFI_CPIDX  25
-#define _cffi_call_python                                                \
-    ((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[_CFFI_CPIDX])
-#define _cffi_to_c_wchar3216_t                                           \
-    ((int(*)(PyObject *))_cffi_exports[26])
-#define _cffi_from_c_wchar3216_t                                         \
-    ((PyObject *(*)(int))_cffi_exports[27])
-#define _CFFI_NUM_EXPORTS 28
-
-struct _cffi_ctypedescr;
-
-static void *_cffi_exports[_CFFI_NUM_EXPORTS];
-
-#define _cffi_type(index)   (                           \
-    assert((((uintptr_t)_cffi_types[index]) & 1) == 0), \
-    (struct _cffi_ctypedescr *)_cffi_types[index])
-
-static PyObject *_cffi_init(const char *module_name, Py_ssize_t version,
-                            const struct _cffi_type_context_s *ctx)
-{
-    PyObject *module, *o_arg, *new_module;
-    void *raw[] = {
-        (void *)module_name,
-        (void *)version,
-        (void *)_cffi_exports,
-        (void *)ctx,
-    };
-
-    module = PyImport_ImportModule("_cffi_backend");
-    if (module == NULL)
-        goto failure;
-
-    o_arg = PyLong_FromVoidPtr((void *)raw);
-    if (o_arg == NULL)
-        goto failure;
-
-    new_module = PyObject_CallMethod(
-        module, (char *)"_init_cffi_1_0_external_module", (char *)"O", o_arg);
-
-    Py_DECREF(o_arg);
-    Py_DECREF(module);
-    return new_module;
-
-  failure:
-    Py_XDECREF(module);
-    return NULL;
-}
-
-
-#ifdef HAVE_WCHAR_H
-typedef wchar_t _cffi_wchar_t;
-#else
-typedef uint16_t _cffi_wchar_t;   /* same random pick as _cffi_backend.c */
-#endif
-
-_CFFI_UNUSED_FN static uint16_t _cffi_to_c_char16_t(PyObject *o)
-{
-    if (sizeof(_cffi_wchar_t) == 2)
-        return (uint16_t)_cffi_to_c_wchar_t(o);
-    else
-        return (uint16_t)_cffi_to_c_wchar3216_t(o);
-}
-
-_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char16_t(uint16_t x)
-{
-    if (sizeof(_cffi_wchar_t) == 2)
-        return _cffi_from_c_wchar_t((_cffi_wchar_t)x);
-    else
-        return _cffi_from_c_wchar3216_t((int)x);
-}
-
-_CFFI_UNUSED_FN static int _cffi_to_c_char32_t(PyObject *o)
-{
-    if (sizeof(_cffi_wchar_t) == 4)
-        return (int)_cffi_to_c_wchar_t(o);
-    else
-        return (int)_cffi_to_c_wchar3216_t(o);
-}
-
-_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char32_t(unsigned int x)
-{
-    if (sizeof(_cffi_wchar_t) == 4)
-        return _cffi_from_c_wchar_t((_cffi_wchar_t)x);
-    else
-        return _cffi_from_c_wchar3216_t((int)x);
-}
-
-union _cffi_union_alignment_u {
-    unsigned char m_char;
-    unsigned short m_short;
-    unsigned int m_int;
-    unsigned long m_long;
-    unsigned long long m_longlong;
-    float m_float;
-    double m_double;
-    long double m_longdouble;
-};
-
-struct _cffi_freeme_s {
-    struct _cffi_freeme_s *next;
-    union _cffi_union_alignment_u alignment;
-};
-
-_CFFI_UNUSED_FN static int
-_cffi_convert_array_argument(struct _cffi_ctypedescr *ctptr, PyObject *arg,
-                             char **output_data, Py_ssize_t datasize,
-                             struct _cffi_freeme_s **freeme)
-{
-    char *p;
-    if (datasize < 0)
-        return -1;
-
-    p = *output_data;
-    if (p == NULL) {
-        struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc(
-            offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize);
-        if (fp == NULL)
-            return -1;
-        fp->next = *freeme;
-        *freeme = fp;
-        p = *output_data = (char *)&fp->alignment;
-    }
-    memset((void *)p, 0, (size_t)datasize);
-    return _cffi_convert_array_from_object(p, ctptr, arg);
-}
-
-_CFFI_UNUSED_FN static void
-_cffi_free_array_arguments(struct _cffi_freeme_s *freeme)
-{
-    do {
-        void *p = (void *)freeme;
-        freeme = freeme->next;
-        PyObject_Free(p);
-    } while (freeme != NULL);
-}
-
-/**********  end CPython-specific section  **********/
-#else
-_CFFI_UNUSED_FN
-static void (*_cffi_call_python_org)(struct _cffi_externpy_s *, char *);
-# define _cffi_call_python  _cffi_call_python_org
-#endif
-
-
-#define _cffi_array_len(array)   (sizeof(array) / sizeof((array)[0]))
-
-#define _cffi_prim_int(size, sign)                                      \
-    ((size) == 1 ? ((sign) ? _CFFI_PRIM_INT8  : _CFFI_PRIM_UINT8)  :    \
-     (size) == 2 ? ((sign) ? _CFFI_PRIM_INT16 : _CFFI_PRIM_UINT16) :    \
-     (size) == 4 ? ((sign) ? _CFFI_PRIM_INT32 : _CFFI_PRIM_UINT32) :    \
-     (size) == 8 ? ((sign) ? _CFFI_PRIM_INT64 : _CFFI_PRIM_UINT64) :    \
-     _CFFI__UNKNOWN_PRIM)
-
-#define _cffi_prim_float(size)                                          \
-    ((size) == sizeof(float) ? _CFFI_PRIM_FLOAT :                       \
-     (size) == sizeof(double) ? _CFFI_PRIM_DOUBLE :                     \
-     (size) == sizeof(long double) ? _CFFI__UNKNOWN_LONG_DOUBLE :       \
-     _CFFI__UNKNOWN_FLOAT_PRIM)
-
-#define _cffi_check_int(got, got_nonpos, expected)      \
-    ((got_nonpos) == (expected <= 0) &&                 \
-     (got) == (unsigned long long)expected)
-
-#ifdef MS_WIN32
-# define _cffi_stdcall  __stdcall
-#else
-# define _cffi_stdcall  /* nothing */
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-/************************************************************/
-
- //passed to the real C compiler
-        #include "bluemonday.h"
-    
-
-/************************************************************/
-
-static void *_cffi_types[] = {
-/*  0 */ _CFFI_OP(_CFFI_OP_FUNCTION, 2), // char *()(unsigned long, char *)
-/*  1 */ _CFFI_OP(_CFFI_OP_PRIMITIVE, 10), // unsigned long
-/*  2 */ _CFFI_OP(_CFFI_OP_POINTER, 35), // char *
-/*  3 */ _CFFI_OP(_CFFI_OP_FUNCTION_END, 0),
-/*  4 */ _CFFI_OP(_CFFI_OP_FUNCTION, 1), // unsigned long()(void)
-/*  5 */ _CFFI_OP(_CFFI_OP_FUNCTION_END, 0),
-/*  6 */ _CFFI_OP(_CFFI_OP_FUNCTION, 36), // void()(char *)
-/*  7 */ _CFFI_OP(_CFFI_OP_NOOP, 2),
-/*  8 */ _CFFI_OP(_CFFI_OP_FUNCTION_END, 0),
-/*  9 */ _CFFI_OP(_CFFI_OP_FUNCTION, 36), // void()(unsigned long)
-/* 10 */ _CFFI_OP(_CFFI_OP_PRIMITIVE, 10),
-/* 11 */ _CFFI_OP(_CFFI_OP_FUNCTION_END, 0),
-/* 12 */ _CFFI_OP(_CFFI_OP_FUNCTION, 36), // void()(unsigned long, char *)
-/* 13 */ _CFFI_OP(_CFFI_OP_PRIMITIVE, 10),
-/* 14 */ _CFFI_OP(_CFFI_OP_NOOP, 2),
-/* 15 */ _CFFI_OP(_CFFI_OP_FUNCTION_END, 0),
-/* 16 */ _CFFI_OP(_CFFI_OP_FUNCTION, 36), // void()(unsigned long, char *, char *)
-/* 17 */ _CFFI_OP(_CFFI_OP_PRIMITIVE, 10),
-/* 18 */ _CFFI_OP(_CFFI_OP_NOOP, 2),
-/* 19 */ _CFFI_OP(_CFFI_OP_NOOP, 2),
-/* 20 */ _CFFI_OP(_CFFI_OP_FUNCTION_END, 0),
-/* 21 */ _CFFI_OP(_CFFI_OP_FUNCTION, 36), // void()(unsigned long, char *, char *, char *, char *, char *, char *)
-/* 22 */ _CFFI_OP(_CFFI_OP_PRIMITIVE, 10),
-/* 23 */ _CFFI_OP(_CFFI_OP_NOOP, 2),
-/* 24 */ _CFFI_OP(_CFFI_OP_NOOP, 2),
-/* 25 */ _CFFI_OP(_CFFI_OP_NOOP, 2),
-/* 26 */ _CFFI_OP(_CFFI_OP_NOOP, 2),
-/* 27 */ _CFFI_OP(_CFFI_OP_NOOP, 2),
-/* 28 */ _CFFI_OP(_CFFI_OP_NOOP, 2),
-/* 29 */ _CFFI_OP(_CFFI_OP_FUNCTION_END, 0),
-/* 30 */ _CFFI_OP(_CFFI_OP_FUNCTION, 36), // void()(unsigned long, char *, unsigned int)
-/* 31 */ _CFFI_OP(_CFFI_OP_PRIMITIVE, 10),
-/* 32 */ _CFFI_OP(_CFFI_OP_NOOP, 2),
-/* 33 */ _CFFI_OP(_CFFI_OP_PRIMITIVE, 8), // unsigned int
-/* 34 */ _CFFI_OP(_CFFI_OP_FUNCTION_END, 0),
-/* 35 */ _CFFI_OP(_CFFI_OP_PRIMITIVE, 2), // char
-/* 36 */ _CFFI_OP(_CFFI_OP_PRIMITIVE, 0), // void
-};
-
-static void _cffi_d_CallAttrBuilderPolicyFunction(unsigned long x0, char * x1, char * x2, char * x3, char * x4, char * x5, char * x6)
-{
-  CallAttrBuilderPolicyFunction(x0, x1, x2, x3, x4, x5, x6);
-}
-#ifndef PYPY_VERSION
-static PyObject *
-_cffi_f_CallAttrBuilderPolicyFunction(PyObject *self, PyObject *args)
-{
-  unsigned long x0;
-  char * x1;
-  char * x2;
-  char * x3;
-  char * x4;
-  char * x5;
-  char * x6;
-  Py_ssize_t datasize;
-  struct _cffi_freeme_s *large_args_free = NULL;
-  PyObject *arg0;
-  PyObject *arg1;
-  PyObject *arg2;
-  PyObject *arg3;
-  PyObject *arg4;
-  PyObject *arg5;
-  PyObject *arg6;
-
-  if (!PyArg_UnpackTuple(args, "CallAttrBuilderPolicyFunction", 7, 7, &arg0, &arg1, &arg2, &arg3, &arg4, &arg5, &arg6))
-    return NULL;
-
-  x0 = _cffi_to_c_int(arg0, unsigned long);
-  if (x0 == (unsigned long)-1 && PyErr_Occurred())
-    return NULL;
-
-  datasize = _cffi_prepare_pointer_call_argument(
-      _cffi_type(2), arg1, (char **)&x1);
-  if (datasize != 0) {
-    x1 = ((size_t)datasize) <= 640 ? (char *)alloca((size_t)datasize) : NULL;
-    if (_cffi_convert_array_argument(_cffi_type(2), arg1, (char **)&x1,
-            datasize, &large_args_free) < 0)
-      return NULL;
-  }
-
-  datasize = _cffi_prepare_pointer_call_argument(
-      _cffi_type(2), arg2, (char **)&x2);
-  if (datasize != 0) {
-    x2 = ((size_t)datasize) <= 640 ? (char *)alloca((size_t)datasize) : NULL;
-    if (_cffi_convert_array_argument(_cffi_type(2), arg2, (char **)&x2,
-            datasize, &large_args_free) < 0)
-      return NULL;
-  }
-
-  datasize = _cffi_prepare_pointer_call_argument(
-      _cffi_type(2), arg3, (char **)&x3);
-  if (datasize != 0) {
-    x3 = ((size_t)datasize) <= 640 ? (char *)alloca((size_t)datasize) : NULL;
-    if (_cffi_convert_array_argument(_cffi_type(2), arg3, (char **)&x3,
-            datasize, &large_args_free) < 0)
-      return NULL;
-  }
-
-  datasize = _cffi_prepare_pointer_call_argument(
-      _cffi_type(2), arg4, (char **)&x4);
-  if (datasize != 0) {
-    x4 = ((size_t)datasize) <= 640 ? (char *)alloca((size_t)datasize) : NULL;
-    if (_cffi_convert_array_argument(_cffi_type(2), arg4, (char **)&x4,
-            datasize, &large_args_free) < 0)
-      return NULL;
-  }
-
-  datasize = _cffi_prepare_pointer_call_argument(
-      _cffi_type(2), arg5, (char **)&x5);
-  if (datasize != 0) {
-    x5 = ((size_t)datasize) <= 640 ? (char *)alloca((size_t)datasize) : NULL;
-    if (_cffi_convert_array_argument(_cffi_type(2), arg5, (char **)&x5,
-            datasize, &large_args_free) < 0)
-      return NULL;
-  }
-
-  datasize = _cffi_prepare_pointer_call_argument(
-      _cffi_type(2), arg6, (char **)&x6);
-  if (datasize != 0) {
-    x6 = ((size_t)datasize) <= 640 ? (char *)alloca((size_t)datasize) : NULL;
-    if (_cffi_convert_array_argument(_cffi_type(2), arg6, (char **)&x6,
-            datasize, &large_args_free) < 0)
-      return NULL;
-  }
-
-  Py_BEGIN_ALLOW_THREADS
-  _cffi_restore_errno();
-  { CallAttrBuilderPolicyFunction(x0, x1, x2, x3, x4, x5, x6); }
-  _cffi_save_errno();
-  Py_END_ALLOW_THREADS
-
-  (void)self; /* unused */
-  if (large_args_free != NULL) _cffi_free_array_arguments(large_args_free);
-  Py_INCREF(Py_None);
-  return Py_None;
-}
-#else
-#  define _cffi_f_CallAttrBuilderPolicyFunction _cffi_d_CallAttrBuilderPolicyFunction
-#endif
-
-static void _cffi_d_CallPolicyFunction(unsigned long x0, char * x1)
-{
-  CallPolicyFunction(x0, x1);
-}
-#ifndef PYPY_VERSION
-static PyObject *
-_cffi_f_CallPolicyFunction(PyObject *self, PyObject *args)
-{
-  unsigned long x0;
-  char * x1;
-  Py_ssize_t datasize;
-  struct _cffi_freeme_s *large_args_free = NULL;
-  PyObject *arg0;
-  PyObject *arg1;
-
-  if (!PyArg_UnpackTuple(args, "CallPolicyFunction", 2, 2, &arg0, &arg1))
-    return NULL;
-
-  x0 = _cffi_to_c_int(arg0, unsigned long);
-  if (x0 == (unsigned long)-1 && PyErr_Occurred())
-    return NULL;
-
-  datasize = _cffi_prepare_pointer_call_argument(
-      _cffi_type(2), arg1, (char **)&x1);
-  if (datasize != 0) {
-    x1 = ((size_t)datasize) <= 640 ? (char *)alloca((size_t)datasize) : NULL;
-    if (_cffi_convert_array_argument(_cffi_type(2), arg1, (char **)&x1,
-            datasize, &large_args_free) < 0)
-      return NULL;
-  }
-
-  Py_BEGIN_ALLOW_THREADS
-  _cffi_restore_errno();
-  { CallPolicyFunction(x0, x1); }
-  _cffi_save_errno();
-  Py_END_ALLOW_THREADS
-
-  (void)self; /* unused */
-  if (large_args_free != NULL) _cffi_free_array_arguments(large_args_free);
-  Py_INCREF(Py_None);
-  return Py_None;
-}
-#else
-#  define _cffi_f_CallPolicyFunction _cffi_d_CallPolicyFunction
-#endif
-
-static void _cffi_d_CallPolicyFunctionWithBool(unsigned long x0, char * x1, unsigned int x2)
-{
-  CallPolicyFunctionWithBool(x0, x1, x2);
-}
-#ifndef PYPY_VERSION
-static PyObject *
-_cffi_f_CallPolicyFunctionWithBool(PyObject *self, PyObject *args)
-{
-  unsigned long x0;
-  char * x1;
-  unsigned int x2;
-  Py_ssize_t datasize;
-  struct _cffi_freeme_s *large_args_free = NULL;
-  PyObject *arg0;
-  PyObject *arg1;
-  PyObject *arg2;
-
-  if (!PyArg_UnpackTuple(args, "CallPolicyFunctionWithBool", 3, 3, &arg0, &arg1, &arg2))
-    return NULL;
-
-  x0 = _cffi_to_c_int(arg0, unsigned long);
-  if (x0 == (unsigned long)-1 && PyErr_Occurred())
-    return NULL;
-
-  datasize = _cffi_prepare_pointer_call_argument(
-      _cffi_type(2), arg1, (char **)&x1);
-  if (datasize != 0) {
-    x1 = ((size_t)datasize) <= 640 ? (char *)alloca((size_t)datasize) : NULL;
-    if (_cffi_convert_array_argument(_cffi_type(2), arg1, (char **)&x1,
-            datasize, &large_args_free) < 0)
-      return NULL;
-  }
-
-  x2 = _cffi_to_c_int(arg2, unsigned int);
-  if (x2 == (unsigned int)-1 && PyErr_Occurred())
-    return NULL;
-
-  Py_BEGIN_ALLOW_THREADS
-  _cffi_restore_errno();
-  { CallPolicyFunctionWithBool(x0, x1, x2); }
-  _cffi_save_errno();
-  Py_END_ALLOW_THREADS
-
-  (void)self; /* unused */
-  if (large_args_free != NULL) _cffi_free_array_arguments(large_args_free);
-  Py_INCREF(Py_None);
-  return Py_None;
-}
-#else
-#  define _cffi_f_CallPolicyFunctionWithBool _cffi_d_CallPolicyFunctionWithBool
-#endif
-
-static void _cffi_d_CallPolicyFunctionWithString(unsigned long x0, char * x1, char * x2)
-{
-  CallPolicyFunctionWithString(x0, x1, x2);
-}
-#ifndef PYPY_VERSION
-static PyObject *
-_cffi_f_CallPolicyFunctionWithString(PyObject *self, PyObject *args)
-{
-  unsigned long x0;
-  char * x1;
-  char * x2;
-  Py_ssize_t datasize;
-  struct _cffi_freeme_s *large_args_free = NULL;
-  PyObject *arg0;
-  PyObject *arg1;
-  PyObject *arg2;
-
-  if (!PyArg_UnpackTuple(args, "CallPolicyFunctionWithString", 3, 3, &arg0, &arg1, &arg2))
-    return NULL;
-
-  x0 = _cffi_to_c_int(arg0, unsigned long);
-  if (x0 == (unsigned long)-1 && PyErr_Occurred())
-    return NULL;
-
-  datasize = _cffi_prepare_pointer_call_argument(
-      _cffi_type(2), arg1, (char **)&x1);
-  if (datasize != 0) {
-    x1 = ((size_t)datasize) <= 640 ? (char *)alloca((size_t)datasize) : NULL;
-    if (_cffi_convert_array_argument(_cffi_type(2), arg1, (char **)&x1,
-            datasize, &large_args_free) < 0)
-      return NULL;
-  }
-
-  datasize = _cffi_prepare_pointer_call_argument(
-      _cffi_type(2), arg2, (char **)&x2);
-  if (datasize != 0) {
-    x2 = ((size_t)datasize) <= 640 ? (char *)alloca((size_t)datasize) : NULL;
-    if (_cffi_convert_array_argument(_cffi_type(2), arg2, (char **)&x2,
-            datasize, &large_args_free) < 0)
-      return NULL;
-  }
-
-  Py_BEGIN_ALLOW_THREADS
-  _cffi_restore_errno();
-  { CallPolicyFunctionWithString(x0, x1, x2); }
-  _cffi_save_errno();
-  Py_END_ALLOW_THREADS
-
-  (void)self; /* unused */
-  if (large_args_free != NULL) _cffi_free_array_arguments(large_args_free);
-  Py_INCREF(Py_None);
-  return Py_None;
-}
-#else
-#  define _cffi_f_CallPolicyFunctionWithString _cffi_d_CallPolicyFunctionWithString
-#endif
-
-static void _cffi_d_DestroyPolicy(unsigned long x0)
-{
-  DestroyPolicy(x0);
-}
-#ifndef PYPY_VERSION
-static PyObject *
-_cffi_f_DestroyPolicy(PyObject *self, PyObject *arg0)
-{
-  unsigned long x0;
-
-  x0 = _cffi_to_c_int(arg0, unsigned long);
-  if (x0 == (unsigned long)-1 && PyErr_Occurred())
-    return NULL;
-
-  Py_BEGIN_ALLOW_THREADS
-  _cffi_restore_errno();
-  { DestroyPolicy(x0); }
-  _cffi_save_errno();
-  Py_END_ALLOW_THREADS
-
-  (void)self; /* unused */
-  Py_INCREF(Py_None);
-  return Py_None;
-}
-#else
-#  define _cffi_f_DestroyPolicy _cffi_d_DestroyPolicy
-#endif
-
-static void _cffi_d_FreeCString(char * x0)
-{
-  FreeCString(x0);
-}
-#ifndef PYPY_VERSION
-static PyObject *
-_cffi_f_FreeCString(PyObject *self, PyObject *arg0)
-{
-  char * x0;
-  Py_ssize_t datasize;
-  struct _cffi_freeme_s *large_args_free = NULL;
-
-  datasize = _cffi_prepare_pointer_call_argument(
-      _cffi_type(2), arg0, (char **)&x0);
-  if (datasize != 0) {
-    x0 = ((size_t)datasize) <= 640 ? (char *)alloca((size_t)datasize) : NULL;
-    if (_cffi_convert_array_argument(_cffi_type(2), arg0, (char **)&x0,
-            datasize, &large_args_free) < 0)
-      return NULL;
-  }
-
-  Py_BEGIN_ALLOW_THREADS
-  _cffi_restore_errno();
-  { FreeCString(x0); }
-  _cffi_save_errno();
-  Py_END_ALLOW_THREADS
-
-  (void)self; /* unused */
-  if (large_args_free != NULL) _cffi_free_array_arguments(large_args_free);
-  Py_INCREF(Py_None);
-  return Py_None;
-}
-#else
-#  define _cffi_f_FreeCString _cffi_d_FreeCString
-#endif
-
-static unsigned long _cffi_d_NewPolicy(void)
-{
-  return NewPolicy();
-}
-#ifndef PYPY_VERSION
-static PyObject *
-_cffi_f_NewPolicy(PyObject *self, PyObject *noarg)
-{
-  unsigned long result;
-  PyObject *pyresult;
-
-  Py_BEGIN_ALLOW_THREADS
-  _cffi_restore_errno();
-  { result = NewPolicy(); }
-  _cffi_save_errno();
-  Py_END_ALLOW_THREADS
-
-  (void)self; /* unused */
-  (void)noarg; /* unused */
-  pyresult = _cffi_from_c_int(result, unsigned long);
-  return pyresult;
-}
-#else
-#  define _cffi_f_NewPolicy _cffi_d_NewPolicy
-#endif
-
-static char * _cffi_d_SanitizeWithPolicy(unsigned long x0, char * x1)
-{
-  return SanitizeWithPolicy(x0, x1);
-}
-#ifndef PYPY_VERSION
-static PyObject *
-_cffi_f_SanitizeWithPolicy(PyObject *self, PyObject *args)
-{
-  unsigned long x0;
-  char * x1;
-  Py_ssize_t datasize;
-  struct _cffi_freeme_s *large_args_free = NULL;
-  char * result;
-  PyObject *pyresult;
-  PyObject *arg0;
-  PyObject *arg1;
-
-  if (!PyArg_UnpackTuple(args, "SanitizeWithPolicy", 2, 2, &arg0, &arg1))
-    return NULL;
-
-  x0 = _cffi_to_c_int(arg0, unsigned long);
-  if (x0 == (unsigned long)-1 && PyErr_Occurred())
-    return NULL;
-
-  datasize = _cffi_prepare_pointer_call_argument(
-      _cffi_type(2), arg1, (char **)&x1);
-  if (datasize != 0) {
-    x1 = ((size_t)datasize) <= 640 ? (char *)alloca((size_t)datasize) : NULL;
-    if (_cffi_convert_array_argument(_cffi_type(2), arg1, (char **)&x1,
-            datasize, &large_args_free) < 0)
-      return NULL;
-  }
-
-  Py_BEGIN_ALLOW_THREADS
-  _cffi_restore_errno();
-  { result = SanitizeWithPolicy(x0, x1); }
-  _cffi_save_errno();
-  Py_END_ALLOW_THREADS
-
-  (void)self; /* unused */
-  pyresult = _cffi_from_c_pointer((char *)result, _cffi_type(2));
-  if (large_args_free != NULL) _cffi_free_array_arguments(large_args_free);
-  return pyresult;
-}
-#else
-#  define _cffi_f_SanitizeWithPolicy _cffi_d_SanitizeWithPolicy
-#endif
-
-static unsigned long _cffi_d_StrictPolicy(void)
-{
-  return StrictPolicy();
-}
-#ifndef PYPY_VERSION
-static PyObject *
-_cffi_f_StrictPolicy(PyObject *self, PyObject *noarg)
-{
-  unsigned long result;
-  PyObject *pyresult;
-
-  Py_BEGIN_ALLOW_THREADS
-  _cffi_restore_errno();
-  { result = StrictPolicy(); }
-  _cffi_save_errno();
-  Py_END_ALLOW_THREADS
-
-  (void)self; /* unused */
-  (void)noarg; /* unused */
-  pyresult = _cffi_from_c_int(result, unsigned long);
-  return pyresult;
-}
-#else
-#  define _cffi_f_StrictPolicy _cffi_d_StrictPolicy
-#endif
-
-static unsigned long _cffi_d_UGCPolicy(void)
-{
-  return UGCPolicy();
-}
-#ifndef PYPY_VERSION
-static PyObject *
-_cffi_f_UGCPolicy(PyObject *self, PyObject *noarg)
-{
-  unsigned long result;
-  PyObject *pyresult;
-
-  Py_BEGIN_ALLOW_THREADS
-  _cffi_restore_errno();
-  { result = UGCPolicy(); }
-  _cffi_save_errno();
-  Py_END_ALLOW_THREADS
-
-  (void)self; /* unused */
-  (void)noarg; /* unused */
-  pyresult = _cffi_from_c_int(result, unsigned long);
-  return pyresult;
-}
-#else
-#  define _cffi_f_UGCPolicy _cffi_d_UGCPolicy
-#endif
-
-static const struct _cffi_global_s _cffi_globals[] = {
-  { "CallAttrBuilderPolicyFunction", (void *)_cffi_f_CallAttrBuilderPolicyFunction, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_V, 21), (void *)_cffi_d_CallAttrBuilderPolicyFunction },
-  { "CallPolicyFunction", (void *)_cffi_f_CallPolicyFunction, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_V, 12), (void *)_cffi_d_CallPolicyFunction },
-  { "CallPolicyFunctionWithBool", (void *)_cffi_f_CallPolicyFunctionWithBool, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_V, 30), (void *)_cffi_d_CallPolicyFunctionWithBool },
-  { "CallPolicyFunctionWithString", (void *)_cffi_f_CallPolicyFunctionWithString, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_V, 16), (void *)_cffi_d_CallPolicyFunctionWithString },
-  { "DestroyPolicy", (void *)_cffi_f_DestroyPolicy, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_O, 9), (void *)_cffi_d_DestroyPolicy },
-  { "FreeCString", (void *)_cffi_f_FreeCString, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_O, 6), (void *)_cffi_d_FreeCString },
-  { "NewPolicy", (void *)_cffi_f_NewPolicy, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_N, 4), (void *)_cffi_d_NewPolicy },
-  { "SanitizeWithPolicy", (void *)_cffi_f_SanitizeWithPolicy, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_V, 0), (void *)_cffi_d_SanitizeWithPolicy },
-  { "StrictPolicy", (void *)_cffi_f_StrictPolicy, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_N, 4), (void *)_cffi_d_StrictPolicy },
-  { "UGCPolicy", (void *)_cffi_f_UGCPolicy, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_N, 4), (void *)_cffi_d_UGCPolicy },
-};
-
-static const struct _cffi_type_context_s _cffi_type_context = {
-  _cffi_types,
-  _cffi_globals,
-  NULL,  /* no fields */
-  NULL,  /* no struct_unions */
-  NULL,  /* no enums */
-  NULL,  /* no typenames */
-  10,  /* num_globals */
-  0,  /* num_struct_unions */
-  0,  /* num_enums */
-  0,  /* num_typenames */
-  NULL,  /* no includes */
-  37,  /* num_types */
-  0,  /* flags */
-};
-
-#ifdef __GNUC__
-#  pragma GCC visibility push(default)  /* for -fvisibility= */
-#endif
-
-#ifdef PYPY_VERSION
-PyMODINIT_FUNC
-_cffi_pypyinit_bluemonday(const void *p[])
-{
-    p[0] = (const void *)0x2601;
-    p[1] = &_cffi_type_context;
-#if PY_MAJOR_VERSION >= 3
-    return NULL;
-#endif
-}
-#  ifdef _MSC_VER
-     PyMODINIT_FUNC
-#  if PY_MAJOR_VERSION >= 3
-     PyInit_bluemonday(void) { return NULL; }
-#  else
-     initbluemonday(void) { }
-#  endif
-#  endif
-#elif PY_MAJOR_VERSION >= 3
-PyMODINIT_FUNC
-PyInit_bluemonday(void)
-{
-  return _cffi_init("bluemonday", 0x2601, &_cffi_type_context);
-}
-#else
-PyMODINIT_FUNC
-initbluemonday(void)
-{
-  _cffi_init("bluemonday", 0x2601, &_cffi_type_context);
-}
-#endif
-
-#ifdef __GNUC__
-#  pragma GCC visibility pop
-#endif
diff --git a/bluemonday.go b/bluemonday.go
index 1fdcd2f..a0910c3 100644
--- a/bluemonday.go
+++ b/bluemonday.go
@@ -14,16 +14,16 @@ import (
 	"github.com/microcosm-cc/bluemonday"
 )
 
-var POLICIES = map[uint64]*bluemonday.Policy{}
+var POLICIES = map[uint32]*bluemonday.Policy{}
 
-func GetPolicyId() uint64 {
-	policyId := rand.Uint64()
+func GetPolicyId() uint32 {
+	policyId := rand.Uint32()
 
 	for {
 		if POLICIES[policyId] == nil {
 			break
 		} else {
-			policyId = rand.Uint64()
+			policyId = rand.Uint32()
 		}
 	}
 	return policyId
@@ -55,13 +55,13 @@ func UGCPolicy() C.ulong {
 
 //export DestroyPolicy
 func DestroyPolicy(policyId C.ulong) {
-	goPolicyId := uint64(policyId)
+	goPolicyId := uint32(policyId)
 	delete(POLICIES, goPolicyId)
 }
 
 //export CallAttrBuilderPolicyFunction
 func CallAttrBuilderPolicyFunction(policyId C.ulong, policyMethod *C.char, policyArgument *C.char, valueFunction *C.char, valueFilter *C.char, selectorFunction *C.char, selectorValue *C.char) {
-	goPolicyId := uint64(policyId)
+	goPolicyId := uint32(policyId)
 	goPolicyMethod := C.GoString(policyMethod)
 	goPolicyArgument := C.GoString(policyArgument)
 	goValueFunction := C.GoString(valueFunction)
@@ -125,7 +125,7 @@ func CallAttrBuilderPolicyFunction(policyId C.ulong, policyMethod *C.char, polic
 
 //export CallPolicyFunction
 func CallPolicyFunction(policyId C.ulong, method *C.char) {
-	goPolicyId := uint64(policyId)
+	goPolicyId := uint32(policyId)
 	goMethod := C.GoString(method)
 
 	policy := POLICIES[goPolicyId]
@@ -135,7 +135,7 @@ func CallPolicyFunction(policyId C.ulong, method *C.char) {
 
 //export CallPolicyFunctionWithString
 func CallPolicyFunctionWithString(policyId C.ulong, method *C.char, argument *C.char) {
-	goPolicyId := uint64(policyId)
+	goPolicyId := uint32(policyId)
 	goMethod := C.GoString(method)
 	goArgument := C.GoString(argument)
 
@@ -147,7 +147,7 @@ func CallPolicyFunctionWithString(policyId C.ulong, method *C.char, argument *C.
 
 //export CallPolicyFunctionWithBool
 func CallPolicyFunctionWithBool(policyId C.ulong, method *C.char, argument C.uint) {
-	goPolicyId := uint64(policyId)
+	goPolicyId := uint32(policyId)
 	goMethod := C.GoString(method)
 	goArgument := int(argument) != 0
 
@@ -159,7 +159,7 @@ func CallPolicyFunctionWithBool(policyId C.ulong, method *C.char, argument C.uin
 
 //export SanitizeWithPolicy
 func SanitizeWithPolicy(policyId C.ulong, document *C.char) *C.char {
-	goPolicyId := uint64(policyId)
+	goPolicyId := uint32(policyId)
 	goDocument := C.GoString(document)
 
 	policy := POLICIES[goPolicyId]
diff --git a/bluemonday.h b/bluemonday.h
deleted file mode 100644
index 39498db..0000000
--- a/bluemonday.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/* Code generated by cmd/cgo; DO NOT EDIT. */
-
-/* package _/Users/kchung/Repositories/pybluemonday */
-
-
-#line 1 "cgo-builtin-export-prolog"
-
-#include <stddef.h> /* for ptrdiff_t below */
-
-#ifndef GO_CGO_EXPORT_PROLOGUE_H
-#define GO_CGO_EXPORT_PROLOGUE_H
-
-#ifndef GO_CGO_GOSTRING_TYPEDEF
-typedef struct { const char *p; ptrdiff_t n; } _GoString_;
-#endif
-
-#endif
-
-/* Start of preamble from import "C" comments.  */
-
-
-#line 3 "bluemonday.go"
-
- #include <stdlib.h>
-
-#line 1 "cgo-generated-wrapper"
-
-
-/* End of preamble from import "C" comments.  */
-
-
-/* Start of boilerplate cgo prologue.  */
-#line 1 "cgo-gcc-export-header-prolog"
-
-#ifndef GO_CGO_PROLOGUE_H
-#define GO_CGO_PROLOGUE_H
-
-typedef signed char GoInt8;
-typedef unsigned char GoUint8;
-typedef short GoInt16;
-typedef unsigned short GoUint16;
-typedef int GoInt32;
-typedef unsigned int GoUint32;
-typedef long long GoInt64;
-typedef unsigned long long GoUint64;
-typedef GoInt64 GoInt;
-typedef GoUint64 GoUint;
-typedef __SIZE_TYPE__ GoUintptr;
-typedef float GoFloat32;
-typedef double GoFloat64;
-typedef float _Complex GoComplex64;
-typedef double _Complex GoComplex128;
-
-/*
-  static assertion to make sure the file is being used on architecture
-  at least with matching size of GoInt.
-*/
-typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
-
-#ifndef GO_CGO_GOSTRING_TYPEDEF
-typedef _GoString_ GoString;
-#endif
-typedef void *GoMap;
-typedef void *GoChan;
-typedef struct { void *t; void *v; } GoInterface;
-typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
-
-#endif
-
-/* End of boilerplate cgo prologue.  */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-extern long unsigned int NewPolicy();
-
-extern long unsigned int StrictPolicy();
-
-extern long unsigned int UGCPolicy();
-
-extern void DestroyPolicy(long unsigned int p0);
-
-extern void CallAttrBuilderPolicyFunction(long unsigned int p0, char* p1, char* p2, char* p3, char* p4, char* p5, char* p6);
-
-extern void CallPolicyFunction(long unsigned int p0, char* p1);
-
-extern void CallPolicyFunctionWithString(long unsigned int p0, char* p1, char* p2);
-
-extern void CallPolicyFunctionWithBool(long unsigned int p0, char* p1, unsigned int p2);
-
-extern char* SanitizeWithPolicy(long unsigned int p0, char* p1);
-
-extern void FreeCString(char* p0);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/development.txt b/development.txt
index 0f49ae4..4f840c3 100644
--- a/development.txt
+++ b/development.txt
@@ -1,8 +1,9 @@
 setuptools-golang==2.3.0
 wheel==0.36.1
-cffi==1.14.3
+cffi==1.14.5
 black==20.8b1
 isort==5.6.4
 twine==3.1.1
 pytest==6.1.2
 psutil==5.7.3
+cibuildwheel==1.10.0
diff --git a/scripts/setup-arm64.sh b/scripts/setup-arm64.sh
new file mode 100755
index 0000000..b9bacf3
--- /dev/null
+++ b/scripts/setup-arm64.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+if [ ! -f "/usr/bin/go" ]; then
+    echo "Downloading arm64 Golang"
+    curl -vvv -L -O https://golang.org/dl/go1.15.5.linux-arm64.tar.gz
+    tar -xf go1.15.5.linux-arm64.tar.gz
+fi
\ No newline at end of file
diff --git a/scripts/setup-arm6vl.sh b/scripts/setup-arm6vl.sh
new file mode 100755
index 0000000..18f8746
--- /dev/null
+++ b/scripts/setup-arm6vl.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+if [ ! -f "/usr/bin/go" ]; then
+    echo "Downloading arm6vl Golang"
+    curl -L -O https://golang.org/dl/go1.15.5.linux-armv6l.tar.gz
+    tar -xf go1.15.5.linux-armv6l.tar.gz
+fi
\ No newline at end of file
diff --git a/scripts/setup-linux.sh b/scripts/setup-linux.sh
old mode 100644
new mode 100755
index 0a0a825..5e99cdb
--- a/scripts/setup-linux.sh
+++ b/scripts/setup-linux.sh
@@ -1,7 +1,6 @@
 #!/bin/bash
 if [ ! -f "/usr/bin/go" ]; then
-    curl -vvv -L -O https://golang.org/dl/go1.15.5.linux-amd64.tar.gz
+    echo "Downloading linux-amd64 Golang"
+    curl -L -O https://golang.org/dl/go1.15.5.linux-amd64.tar.gz
     tar -xf go1.15.5.linux-amd64.tar.gz
-    mv go /usr/local
-    ln -s /usr/local/go/bin/go /usr/bin/go
 fi
diff --git a/scripts/setup-macos.sh b/scripts/setup-macos.sh
old mode 100644
new mode 100755
index a2f738e..c5ba5a3
--- a/scripts/setup-macos.sh
+++ b/scripts/setup-macos.sh
@@ -1,7 +1,6 @@
 #!/bin/bash
 if [ ! -f "/usr/local/bin/go" ]; then
-    curl -vvv -L -O https://golang.org/dl/go1.15.5.darwin-amd64.tar.gz
+    echo "Downloading darwin-amd64 Golang"
+    curl -L -O https://golang.org/dl/go1.15.5.darwin-amd64.tar.gz
     tar -xf go1.15.5.darwin-amd64.tar.gz
-    mv go /tmp
-    ln -s /tmp/go/bin/go /usr/local/bin/go
 fi
diff --git a/setup.py b/setup.py
index 187c644..96fae91 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,42 @@
+import os
+import platform
 import setuptools
+import subprocess
 import re
 
+# Print out our uname for debugging purposes
+uname = platform.uname()
+print(uname)
+
+# Install OSX Golang if needed
+if uname.system == "Darwin":
+    os.system("./scripts/setup-macos.sh")
+
+# Install Linux Golang if needed
+elif uname.system == "Linux":
+    if uname.machine == "aarch64":
+        os.system("./scripts/setup-arm64.sh")
+    elif uname.machine in ("armv7l", "armv6l"):
+        os.system("./scripts/setup-arm6vl.sh")
+    else:
+        os.system("./scripts/setup-linux.sh")
+
+# Add in our downloaded Go compiler to PATH
+old_path = os.environ["PATH"]
+new_path = os.path.join(os.getcwd(), "go", "bin")
+env = {"PATH": f"{old_path}:{new_path}"}
+env = dict(os.environ, **env)
+os.environ["PATH"] = f"{old_path}:{new_path}"
+
+# Clean out any existing files
+subprocess.call(["make", "clean"], env=env)
+
+# Build the Go shared module for whatever OS we're on
+subprocess.call(["make", "so"], env=env)
+
+# Build the CFFI headers
+subprocess.call(["make", "ffi"], env=env)
+
 with open("pybluemonday/__init__.py", "r", encoding="utf8") as f:
     version = re.search(r'__version__ = "(.*?)"', f.read()).group(1)
 
@@ -26,6 +62,6 @@
     # I'm not sure what this value is supposed to be
     build_golang={"root": "github.com/ColdHeat/pybluemonday"},
     ext_modules=[setuptools.Extension("pybluemonday/bluemonday", ["bluemonday.go"])],
-    setup_requires=["setuptools-golang==2.3.0", "cffi==1.14.3"],
-    install_requires=["cffi==1.14.3"],
+    setup_requires=["setuptools-golang==2.3.0", "cffi~=1.1"],
+    install_requires=["cffi~=1.1"],
 )


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index d356391..48e5153 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -22,8 +22,12 @@ jobs:
           architecture: x64
       - name: Install dependencies
         run: |
-            python -m pip install -r development.txt
-            python -m pip install .
+          python -m pip install --upgrade -r development.txt
+      - name: Build wheel
+        run: |
+          go get -d
+          python setup.py bdist_wheel
+          pip install dist/pybluemonday*.whl
       - name: Test
         # For some reason .so's being built aren't working and I have no idea why.
         # Let python use the wheel that we installed above by deleting the module in the folder