diff --git a/setup.py b/setup.py index 36a8cdc..0b106b2 100644 --- a/setup.py +++ b/setup.py @@ -14,10 +14,11 @@ import shutil import subprocess import sys import sysconfig +from wheel.bdist_wheel import bdist_wheel def is_64bit(): - return sys.maxsize > 2**32 + return sys.maxsize > 2 ** 32 def is_32bit(): @@ -152,7 +153,6 @@ AWS_LIBS.append(AwsLib('aws-c-auth')) AWS_LIBS.append(AwsLib('aws-c-mqtt')) AWS_LIBS.append(AwsLib('aws-c-s3')) - PROJECT_DIR = os.path.dirname(os.path.realpath(__file__)) VERSION_RE = re.compile(r""".*__version__ = ["'](.*?)['"]""", re.S) @@ -283,11 +283,23 @@ class awscrt_build_ext(setuptools.command.build_ext.build_ext): super().run() +class bdist_wheel_abi3(bdist_wheel): + def get_tag(self): + python, abi, plat = super().get_tag() + if python.startswith("cp") and sys.version_info >= (3, 11): + # on CPython, our wheels are abi3 and compatible back to 3.11 + return "cp311", "abi3", plat + + return python, abi, plat + + def awscrt_ext(): # fetch the CFLAGS/LDFLAGS from env extra_compile_args = os.environ.get('CFLAGS', '').split() extra_link_args = os.environ.get('LDFLAGS', '').split() extra_objects = [] + define_macros = [] + py_limited_api = False libraries = [x.libname for x in AWS_LIBS] @@ -350,6 +362,10 @@ def awscrt_ext(): if not is_macos_universal2(): extra_link_args += ['-Wl,-fatal_warnings'] + if sys.version_info >= (3, 11): + define_macros.append(('Py_LIMITED_API', '0x030B0000')) + py_limited_api = True + return setuptools.Extension( '_awscrt', language='c', @@ -357,7 +373,9 @@ def awscrt_ext(): sources=glob.glob('source/*.c'), extra_compile_args=extra_compile_args, extra_link_args=extra_link_args, - extra_objects=extra_objects + extra_objects=extra_objects, + define_macros=define_macros, + py_limited_api=py_limited_api, ) @@ -392,6 +410,6 @@ setuptools.setup( ], python_requires='>=3.7', ext_modules=[awscrt_ext()], - cmdclass={'build_ext': awscrt_build_ext}, + cmdclass={'build_ext': awscrt_build_ext, "bdist_wheel": bdist_wheel_abi3}, test_suite='test', ) diff --git a/source/auth_credentials.c b/source/auth_credentials.c index 80bca19..66b1afa 100644 --- a/source/auth_credentials.c +++ b/source/auth_credentials.c @@ -513,7 +513,7 @@ PyObject *aws_py_credentials_provider_new_chain(PyObject *self, PyObject *args) if (!providers_pyseq) { goto done; } - size_t provider_count = (size_t)PySequence_Fast_GET_SIZE(providers_pyseq); + size_t provider_count = (size_t)PySequence_Size(providers_pyseq); if (provider_count == 0) { PyErr_SetString(PyExc_ValueError, "Must supply at least one AwsCredentialsProvider."); goto done; @@ -526,8 +526,9 @@ PyObject *aws_py_credentials_provider_new_chain(PyObject *self, PyObject *args) } for (size_t i = 0; i < provider_count; ++i) { - PyObject *provider_py = PySequence_Fast_GET_ITEM(providers_pyseq, i); + PyObject *provider_py = PySequence_GetItem(providers_pyseq, i); /* new reference */ providers_carray[i] = aws_py_get_credentials_provider(provider_py); + Py_XDECREF(provider_py); if (!providers_carray[i]) { goto done; } @@ -724,7 +725,7 @@ PyObject *aws_py_credentials_provider_new_cognito(PyObject *self, PyObject *args goto done; } - logins_count = (size_t)PySequence_Fast_GET_SIZE(logins_pyseq); + logins_count = (size_t)PySequence_Size(logins_pyseq); if (logins_count > 0) { logins_carray = @@ -735,7 +736,7 @@ PyObject *aws_py_credentials_provider_new_cognito(PyObject *self, PyObject *args } for (size_t i = 0; i < logins_count; ++i) { - PyObject *login_tuple_py = PySequence_Fast_GET_ITEM(logins_pyseq, i); + PyObject *login_tuple_py = PySequence_GetItem(logins_pyseq, i); /* New reference */ struct aws_cognito_identity_provider_token_pair *login_entry = &logins_carray[i]; AWS_ZERO_STRUCT(*login_entry); @@ -750,8 +751,10 @@ PyObject *aws_py_credentials_provider_new_cognito(PyObject *self, PyObject *args PyExc_TypeError, "cognito credentials provider: logins[%zu] is invalid, should be type (str, str)", i); + Py_XDECREF(login_tuple_py); goto done; } + Py_XDECREF(login_tuple_py); } } } diff --git a/source/event_stream_headers.c b/source/event_stream_headers.c index f8ae4ef..cfb1ac3 100644 --- a/source/event_stream_headers.c +++ b/source/event_stream_headers.c @@ -166,19 +166,20 @@ bool aws_py_event_stream_native_headers_init(struct aws_array_list *native_heade bool success = false; PyObject *sequence_py = NULL; - sequence_py = PySequence_Fast(headers_py, "Expected sequence of Headers"); /* New reference */ + sequence_py = PySequence_Fast(headers_py, "Expected sequence of Headers"); if (!sequence_py) { goto done; } - const Py_ssize_t count = PySequence_Fast_GET_SIZE(sequence_py); + const Py_ssize_t count = PySequence_Size(sequence_py); for (Py_ssize_t i = 0; i < count; ++i) { - /* Borrowed reference, don't need to decref */ - PyObject *header_py = PySequence_Fast_GET_ITEM(sequence_py, i); + PyObject *header_py = PySequence_GetItem(sequence_py, i); /* New Reference */ if (!s_add_native_header(native_headers, header_py)) { + Py_XDECREF(header_py); goto done; } + Py_XDECREF(header_py); } success = true; @@ -270,7 +271,7 @@ PyObject *aws_py_event_stream_python_headers_create( goto error; } - PyList_SET_ITEM(list_py, i, tuple_py); /* steals reference to tuple */ + PyList_SetItem(list_py, i, tuple_py); /* steals reference to tuple */ } return list_py; diff --git a/source/http_headers.c b/source/http_headers.c index 187cd7c..66974f9 100644 --- a/source/http_headers.c +++ b/source/http_headers.c @@ -85,6 +85,29 @@ PyObject *aws_py_http_headers_add(PyObject *self, PyObject *args) { Py_RETURN_NONE; } +static bool s_py_http_headers_add_pair(PyObject *py_pair, struct aws_http_headers *headers) { + + const char *type_errmsg = "List of (name,value) pairs expected."; + if (!PyTuple_Check(py_pair) || PyTuple_Size(py_pair) != 2) { + PyErr_SetString(PyExc_TypeError, type_errmsg); + return false; + } + + struct aws_byte_cursor name = aws_byte_cursor_from_pyunicode(PyTuple_GetItem(py_pair, 0) /* Borrowed reference */); + struct aws_byte_cursor value = aws_byte_cursor_from_pyunicode(PyTuple_GetItem(py_pair, 1) /* Borrowed reference */); + if (!name.ptr || !value.ptr) { + PyErr_SetString(PyExc_TypeError, type_errmsg); + return false; + } + + if (aws_http_headers_add(headers, name, value)) { + PyErr_SetAwsLastError(); + return false; + } + + return true; +} + PyObject *aws_py_http_headers_add_pairs(PyObject *self, PyObject *args) { PyObject *py_pairs; S_HEADERS_METHOD_START("O", &py_pairs); @@ -96,25 +119,12 @@ PyObject *aws_py_http_headers_add_pairs(PyObject *self, PyObject *args) { return NULL; } - const Py_ssize_t count = PySequence_Fast_GET_SIZE(py_sequence); + const Py_ssize_t count = PySequence_Size(py_pairs); for (Py_ssize_t i = 0; i < count; ++i) { - /* XYZ_GET_ITEM() calls returns borrowed references */ - PyObject *py_pair = PySequence_Fast_GET_ITEM(py_sequence, i); - - if (!PyTuple_Check(py_pair) || PyTuple_GET_SIZE(py_pair) != 2) { - PyErr_SetString(PyExc_TypeError, type_errmsg); - goto done; - } - - struct aws_byte_cursor name = aws_byte_cursor_from_pyunicode(PyTuple_GET_ITEM(py_pair, 0)); - struct aws_byte_cursor value = aws_byte_cursor_from_pyunicode(PyTuple_GET_ITEM(py_pair, 1)); - if (!name.ptr || !value.ptr) { - PyErr_SetString(PyExc_TypeError, type_errmsg); - goto done; - } - - if (aws_http_headers_add(headers, name, value)) { - PyErr_SetAwsLastError(); + PyObject *py_pair = PySequence_GetItem(py_sequence, i); /* New Reference */ + bool success = s_py_http_headers_add_pair(py_pair, headers); + Py_DECREF(py_pair); + if (!success) { goto done; } } @@ -175,8 +185,8 @@ static PyObject *s_py_tuple_from_header(struct aws_http_header header) { goto error; } - PyTuple_SET_ITEM(py_pair, 0, py_name); - PyTuple_SET_ITEM(py_pair, 1, py_value); + PyTuple_SetItem(py_pair, 0, py_name); /* Steals a reference */ + PyTuple_SetItem(py_pair, 1, py_value); /* Steals a reference */ return py_pair; error: diff --git a/source/http_stream.c b/source/http_stream.c index 18ed406..bf70218 100644 --- a/source/http_stream.c +++ b/source/http_stream.c @@ -121,7 +121,7 @@ static int s_on_incoming_header_block_done( goto done; } - PyList_SET_ITEM(header_list, i, tuple); /* steals reference to tuple */ + PyList_SetItem(header_list, i, tuple); /* steals reference to tuple */ } /* TODO: handle informational and trailing headers */ diff --git a/source/module.c b/source/module.c index ca8a0d9..f120daa 100644 --- a/source/module.c +++ b/source/module.c @@ -531,20 +531,12 @@ void *aws_py_get_binding(PyObject *obj, const char *capsule_name, const char *cl PyObject *py_binding = PyObject_GetAttrString(obj, "_binding"); /* new reference */ if (!py_binding) { - return PyErr_Format( - PyExc_TypeError, - "Expected valid '%s', received '%s' (no '_binding' attribute)", - class_name, - Py_TYPE(obj)->tp_name); + return PyErr_Format(PyExc_TypeError, "Expected valid '%s' (no '_binding' attribute)", class_name); } void *binding = NULL; if (!PyCapsule_CheckExact(py_binding)) { - PyErr_Format( - PyExc_TypeError, - "Expected valid '%s', received '%s' ('_binding' attribute is not a capsule)", - class_name, - Py_TYPE(obj)->tp_name); + PyErr_Format(PyExc_TypeError, "Expected valid '%s' ('_binding' attribute is not a capsule)", class_name); goto done; } @@ -552,9 +544,8 @@ void *aws_py_get_binding(PyObject *obj, const char *capsule_name, const char *cl if (!binding) { PyErr_Format( PyExc_TypeError, - "Expected valid '%s', received '%s' ('_binding' attribute does not contain '%s')", + "Expected valid '%s' ('_binding' attribute does not contain '%s')", class_name, - Py_TYPE(obj)->tp_name, capsule_name); goto done; } diff --git a/source/mqtt5_client.c b/source/mqtt5_client.c index e07eb91..c43c90c 100644 --- a/source/mqtt5_client.c +++ b/source/mqtt5_client.c @@ -200,7 +200,7 @@ static PyObject *s_aws_set_user_properties_to_PyObject( Py_XDECREF(user_properties_list); return NULL; } - PyList_SET_ITEM(user_properties_list, i, tuple); /* Steals reference to tuple */ + PyList_SetItem(user_properties_list, i, tuple); /* Steals reference to tuple */ } return user_properties_list; } @@ -237,8 +237,10 @@ static void s_on_publish_received(const struct aws_mqtt5_packet_publish_view *pu } for (size_t i = 0; i < subscription_identifier_count; ++i) { - PyList_SET_ITEM( - subscription_identifier_list, i, PyLong_FromLongLong(publish_packet->subscription_identifiers[i])); + PyList_SetItem( + subscription_identifier_list, + i, + PyLong_FromLongLong(publish_packet->subscription_identifiers[i])); /* Steals a reference */ } user_properties_list = s_aws_set_user_properties_to_PyObject(publish_packet->user_properties, user_property_count); @@ -1629,7 +1631,7 @@ static void s_on_subscribe_complete_fn( } for (size_t i = 0; i < reason_codes_count; ++i) { - PyList_SET_ITEM(reason_codes_list, i, PyLong_FromLong(suback->reason_codes[i])); + PyList_SetItem(reason_codes_list, i, PyLong_FromLong(suback->reason_codes[i])); /* Steals a reference */ } } @@ -1860,7 +1862,7 @@ static void s_on_unsubscribe_complete_fn( } for (size_t i = 0; i < reason_codes_count; ++i) { - PyList_SET_ITEM(reason_codes_list, i, PyLong_FromLong(unsuback->reason_codes[i])); + PyList_SetItem(reason_codes_list, i, PyLong_FromLong(unsuback->reason_codes[i])); /* Steals a reference */ } } @@ -2030,23 +2032,35 @@ PyObject *aws_py_mqtt5_client_get_stats(PyObject *self, PyObject *args) { goto done; } - PyTuple_SET_ITEM(result, 0, PyLong_FromUnsignedLongLong((unsigned long long)stats.incomplete_operation_count)); - if (PyTuple_GET_ITEM(result, 0) == NULL) { + PyTuple_SetItem( + result, + 0, + PyLong_FromUnsignedLongLong((unsigned long long)stats.incomplete_operation_count)); /* Steals a reference */ + if (PyTuple_GetItem(result, 0) == NULL) { /* Borrowed reference */ goto done; } - PyTuple_SET_ITEM(result, 1, PyLong_FromUnsignedLongLong((unsigned long long)stats.incomplete_operation_size)); - if (PyTuple_GET_ITEM(result, 1) == NULL) { + PyTuple_SetItem( + result, + 1, + PyLong_FromUnsignedLongLong((unsigned long long)stats.incomplete_operation_size)); /* Steals a reference */ + if (PyTuple_GetItem(result, 1) == NULL) { /* Borrowed reference */ goto done; } - PyTuple_SET_ITEM(result, 2, PyLong_FromUnsignedLongLong((unsigned long long)stats.unacked_operation_count)); - if (PyTuple_GET_ITEM(result, 2) == NULL) { + PyTuple_SetItem( + result, + 2, + PyLong_FromUnsignedLongLong((unsigned long long)stats.unacked_operation_count)); /* Steals a reference */ + if (PyTuple_GetItem(result, 2) == NULL) { /* Borrowed reference */ goto done; } - PyTuple_SET_ITEM(result, 3, PyLong_FromUnsignedLongLong((unsigned long long)stats.unacked_operation_size)); - if (PyTuple_GET_ITEM(result, 3) == NULL) { + PyTuple_SetItem( + result, + 3, + PyLong_FromUnsignedLongLong((unsigned long long)stats.unacked_operation_size)); /* Steals a reference */ + if (PyTuple_GetItem(result, 3) == NULL) { /* Borrowed reference */ goto done; } diff --git a/source/mqtt_client_connection.c b/source/mqtt_client_connection.c index bd06d29..96e7ec6 100644 --- a/source/mqtt_client_connection.c +++ b/source/mqtt_client_connection.c @@ -1063,7 +1063,7 @@ static void s_suback_multi_callback( goto done_prepping_args; } - PyList_SET_ITEM(topic_qos_list, i, tuple); /* Steals reference to tuple */ + PyList_SetItem(topic_qos_list, i, tuple); /* Steals reference to tuple */ } done_prepping_args:; @@ -1202,23 +1202,35 @@ PyObject *aws_py_mqtt_client_connection_get_stats(PyObject *self, PyObject *args goto done; } - PyTuple_SET_ITEM(result, 0, PyLong_FromUnsignedLongLong((unsigned long long)stats.incomplete_operation_count)); - if (PyTuple_GET_ITEM(result, 0) == NULL) { + PyTuple_SetItem( + result, + 0, + PyLong_FromUnsignedLongLong((unsigned long long)stats.incomplete_operation_count)); /* Steals a reference */ + if (PyTuple_GetItem(result, 0) == NULL) { /* Borrowed reference */ goto done; } - PyTuple_SET_ITEM(result, 1, PyLong_FromUnsignedLongLong((unsigned long long)stats.incomplete_operation_size)); - if (PyTuple_GET_ITEM(result, 1) == NULL) { + PyTuple_SetItem( + result, + 1, + PyLong_FromUnsignedLongLong((unsigned long long)stats.incomplete_operation_size)); /* Steals a reference */ + if (PyTuple_GetItem(result, 1) == NULL) { /* Borrowed reference */ goto done; } - PyTuple_SET_ITEM(result, 2, PyLong_FromUnsignedLongLong((unsigned long long)stats.unacked_operation_count)); - if (PyTuple_GET_ITEM(result, 2) == NULL) { + PyTuple_SetItem( + result, + 2, + PyLong_FromUnsignedLongLong((unsigned long long)stats.unacked_operation_count)); /* Steals a reference */ + if (PyTuple_GetItem(result, 2) == NULL) { /* Borrowed reference */ goto done; } - PyTuple_SET_ITEM(result, 3, PyLong_FromUnsignedLongLong((unsigned long long)stats.unacked_operation_size)); - if (PyTuple_GET_ITEM(result, 3) == NULL) { + PyTuple_SetItem( + result, + 3, + PyLong_FromUnsignedLongLong((unsigned long long)stats.unacked_operation_size)); /* Steals a reference */ + if (PyTuple_GetItem(result, 3) == NULL) { /* Borrowed reference */ goto done; } diff --git a/source/s3_meta_request.c b/source/s3_meta_request.c index 88a7c19..f7ffa56 100644 --- a/source/s3_meta_request.c +++ b/source/s3_meta_request.c @@ -69,7 +69,7 @@ static PyObject *s_get_py_headers(const struct aws_http_headers *headers) { if (!tuple) { goto error; } - PyList_SET_ITEM(header_list, i, tuple); /* steals reference to tuple */ + PyList_SetItem(header_list, i, tuple); /* steals reference to tuple */ } return header_list; error: diff --git a/source/websocket.c b/source/websocket.c index 2b056ee..95f9d8e 100644 --- a/source/websocket.c +++ b/source/websocket.c @@ -211,13 +211,13 @@ static void s_websocket_on_connection_setup( PyObject *name_py = PyUnicode_FromAwsByteCursor(&header_i->name); AWS_FATAL_ASSERT(name_py && "header name wrangling failed"); - PyTuple_SET_ITEM(tuple_py, 0, name_py); + PyTuple_SetItem(tuple_py, 0, name_py); /* Steals a reference */ PyObject *value_py = PyUnicode_FromAwsByteCursor(&header_i->value); AWS_FATAL_ASSERT(value_py && "header value wrangling failed"); - PyTuple_SET_ITEM(tuple_py, 1, value_py); + PyTuple_SetItem(tuple_py, 1, value_py); /* Steals a reference */ - PyList_SET_ITEM(headers_py, i, tuple_py); + PyList_SetItem(headers_py, i, tuple_py); /* Steals a reference */ } } @@ -580,13 +580,13 @@ PyObject *aws_py_websocket_create_handshake_request(PyObject *self, PyObject *ar if (!request_binding_py) { goto cleanup; } - PyTuple_SET_ITEM(tuple_py, 0, request_binding_py); /* steals reference to request_binding_py */ + PyTuple_SetItem(tuple_py, 0, request_binding_py); /* steals reference to request_binding_py */ PyObject *headers_binding_py = aws_py_http_headers_new_from_native(aws_http_message_get_headers(request)); if (!headers_binding_py) { goto cleanup; } - PyTuple_SET_ITEM(tuple_py, 1, headers_binding_py); /* steals reference to headers_binding_py */ + PyTuple_SetItem(tuple_py, 1, headers_binding_py); /* steals reference to headers_binding_py */ /* Success! */ success = true; diff --git a/test/test_http_client.py b/test/test_http_client.py index dd2631a..14a6d75 100644 --- a/test/test_http_client.py +++ b/test/test_http_client.py @@ -57,10 +57,9 @@ class TestClient(NativeResourceTest): self.server = HTTPServer((self.hostname, 0), TestRequestHandler) if secure: - self.server.socket = ssl.wrap_socket(self.server.socket, - keyfile="test/resources/unittest.key", - certfile='test/resources/unittest.crt', - server_side=True) + context = ssl.SSLContext() + context.load_cert_chain(certfile='test/resources/unittest.crt', keyfile="test/resources/unittest.key") + self.server.socket = context.wrap_socket(self.server.socket, server_side=True) self.port = self.server.server_address[1] # put requests are stored in this dict