PyObject * AerospikeScan_Results(AerospikeScan * self, PyObject * args, PyObject * kwds)
{
	PyObject * py_policy = NULL;
	as_policy_scan scan_policy;
	as_policy_scan * scan_policy_p = NULL;

	static char * kwlist[] = {"policy", NULL};

	if ( PyArg_ParseTupleAndKeywords(args, kwds, "|O:results", kwlist, &py_policy) == false ) {
		return NULL;
	}

	as_error err;
	as_error_init(&err);

	if (!self || !self->client->as) {
		as_error_update(&err, AEROSPIKE_ERR_PARAM, "Invalid aerospike object");
		goto CLEANUP;
	}
	if (!self->client->is_conn_16) {
		as_error_update(&err, AEROSPIKE_ERR_CLUSTER, "No connection to aerospike cluster");
		goto CLEANUP;
	}

	// Convert python policy object to as_policy_scan
	pyobject_to_policy_scan(&err, py_policy, &scan_policy, &scan_policy_p,
			&self->client->as->config.policies.scan);
	if ( err.code != AEROSPIKE_OK ) {
		as_error_update(&err, err.code, NULL);
		goto CLEANUP;
	}

	PyObject * py_results = NULL;
	py_results = PyList_New(0);

	PyThreadState * _save = PyEval_SaveThread();

	aerospike_scan_foreach(self->client->as, &err, scan_policy_p, &self->scan, each_result, py_results);

	PyEval_RestoreThread(_save);


CLEANUP:

	if ( err.code != AEROSPIKE_OK ) {
		PyObject * py_err = NULL;
		error_to_pyobject(&err, &py_err);
		PyObject *exception_type = raise_exception(&err);
		PyErr_SetObject(exception_type, py_err);
		Py_DECREF(py_err);
		return NULL;
	}

	return py_results;
}
PyObject * AerospikeScan_Foreach(AerospikeScan * self, PyObject * args, PyObject * kwds)
{
	// Python Function Arguments
	PyObject * py_callback = NULL;
	PyObject * py_policy = NULL;

	// Python Function Keyword Arguments
	static char * kwlist[] = {"callback", "policy", NULL};

	// Python Function Argument Parsing
	if ( PyArg_ParseTupleAndKeywords(args, kwds, "O|O:foreach", kwlist, &py_callback, &py_policy) == false ) {
		return NULL;
	}

	// Aerospike Client Arguments
	as_error err;
	as_policy_scan policy;
	as_policy_scan * policy_p = NULL;

	// Initialize error
	as_error_init(&err);

	// Convert python policy object to as_policy_exists
	pyobject_to_policy_scan(&err, py_policy, &policy, &policy_p);
	if ( err.code != AEROSPIKE_OK ) {
		goto CLEANUP;
	}

	// Create and initialize callback user-data
	LocalData data;
	data.callback = py_callback;
	as_error_init(&data.error);
	
	// We are spawning multiple threads
	PyThreadState * _save = PyEval_SaveThread();

	// Invoke operation
	aerospike_scan_foreach(self->client->as, &err, policy_p, &self->scan, each_result, &data);

	// We are done using multiple threads
	PyEval_RestoreThread(_save);
	
CLEANUP:

	if ( err.code != AEROSPIKE_OK ) {
		PyObject * py_err = NULL;
		error_to_pyobject(&err, &py_err);
		PyErr_SetObject(PyExc_Exception, py_err);
		return NULL;
	}
	
	Py_INCREF(Py_None);
	return Py_None;
}
/**
 * Scans a set in the Aerospike DB and applies UDF on it.
 *
 * @param self                  The c client's aerospike object.
 * @param namespace_p           The namespace to scan.
 * @param set_p                 The set to scan.
 * @param module_p              The name of UDF module containing the
 *                              function to execute.
 * @param function_p            The name of the function to be applied
 *                              to the record.
 * @param py_args               An array of arguments for the UDF.
 * @py_policy                   The optional policy.
 * @py_options                  The optional scan options to set.
 */
static
PyObject * AerospikeClient_ScanApply_Invoke(
		AerospikeClient * self,
		char* namespace_p, PyObject * py_set, PyObject * py_module, PyObject * py_function,
		PyObject * py_args, PyObject * py_policy, PyObject * py_options, bool block)
{
	as_list* arglist = NULL;
	as_policy_scan scan_policy;
	as_policy_scan* scan_policy_p = NULL;
	as_policy_info info_policy;
	as_policy_info* info_policy_p = NULL;
	as_error err;
	as_scan scan;
	uint64_t scan_id = 0;
	bool is_scan_init = false;

	PyObject *py_ustr1 = NULL;
	PyObject *py_ustr2 = NULL;
	PyObject *py_ustr3 = NULL;

	as_static_pool static_pool;
	memset(&static_pool, 0, sizeof(static_pool));

	// Initialize error
	as_error_init(&err);

	if (!self || !self->as) {
		as_error_update(&err, AEROSPIKE_ERR_PARAM, "Invalid aerospike object");
		goto CLEANUP;
	}

	if (!self->is_conn_16) {
		as_error_update(&err, AEROSPIKE_ERR_CLUSTER, "No connection to aerospike cluster");
		goto CLEANUP;
	}

    self->is_client_put_serializer = false;

	if (!(namespace_p) || !(py_set) || !(py_module) || !(py_function)) {
		as_error_update(&err, AEROSPIKE_ERR_PARAM, "Parameter should not be null");
		goto CLEANUP;
	}

	if (!PyList_Check(py_args)) {
		as_error_update(&err, AEROSPIKE_ERR_PARAM, "Arguments should be a list");
		goto CLEANUP;
	}

	char *set_p = NULL;
	if (PyUnicode_Check(py_set)) {
		py_ustr1 = PyUnicode_AsUTF8String(py_set);
		set_p = PyString_AsString(py_ustr1);
	} else if (PyString_Check(py_set)) {
		set_p = PyString_AsString(py_set);
	} else if( Py_None != py_set ) {
		// Scan whole namespace if set is 'None' else error
		as_error_update(&err, AEROSPIKE_ERR_PARAM, "Set name should be string");
		goto CLEANUP;
	}

	as_scan_init(&scan, namespace_p, set_p);
	is_scan_init = true;

	if (py_policy) {
		pyobject_to_policy_scan(&err, py_policy, &scan_policy, &scan_policy_p,
				&self->as->config.policies.scan);

		if (err.code != AEROSPIKE_OK) {
			goto CLEANUP;
		}
	}

	if (py_options && PyDict_Check(py_options)) {
		set_scan_options(&err, &scan, py_options);
	}

	if (err.code != AEROSPIKE_OK) {
		goto CLEANUP;
	}

	char *module_p = NULL;
	if (PyUnicode_Check(py_module)) {
		py_ustr2 = PyUnicode_AsUTF8String(py_module);
		module_p = PyString_AsString(py_ustr2);
	} else if (PyString_Check(py_module)) {
		module_p = PyString_AsString(py_module);
	} else {
		as_error_update(&err, AEROSPIKE_ERR_PARAM, "Module name should be string");
		goto CLEANUP;
	}

	char *function_p = NULL;
	if (PyUnicode_Check(py_function)) {
		py_ustr3 = PyUnicode_AsUTF8String(py_function);
		function_p = PyString_AsString(py_ustr3);
	} else if (PyString_Check(py_function)) {
		function_p = PyString_AsString(py_function);
	} else {
		as_error_update(&err, AEROSPIKE_ERR_PARAM, "Function name should be string");
		goto CLEANUP;
	}

	pyobject_to_list(self, &err, py_args, &arglist, &static_pool,
			SERIALIZER_PYTHON);
	if (err.code != AEROSPIKE_OK) {
		goto CLEANUP;
	}

	if (!as_scan_apply_each(&scan, module_p, function_p, arglist)) {
		as_error_update(&err, AEROSPIKE_ERR_PARAM, "Unable to apply UDF on the scan");
		goto CLEANUP;
	}

    Py_BEGIN_ALLOW_THREADS
	aerospike_scan_background(self->as, &err, scan_policy_p, &scan, &scan_id);
    Py_END_ALLOW_THREADS
	arglist = NULL;
	if(err.code == AEROSPIKE_OK) {
		if(block) {
			if (py_policy) {
				pyobject_to_policy_info(&err, py_policy, &info_policy, &info_policy_p,
						&self->as->config.policies.info);
				if (err.code != AEROSPIKE_OK) {
					goto CLEANUP;
				}
			}
            Py_BEGIN_ALLOW_THREADS
			aerospike_scan_wait(self->as, &err, info_policy_p, scan_id, 0);
            Py_END_ALLOW_THREADS
			if(err.code != AEROSPIKE_OK) {
				as_error_update(&err, AEROSPIKE_ERR_PARAM, "Unable to perform scan_wait on the scan");
			}
		}
	} else {
		goto CLEANUP;