diff --git a/.builder/actions/manylinux-ci.py b/.builder/actions/manylinux-ci.py index 19839b396..9a3ee87c7 100644 --- a/.builder/actions/manylinux-ci.py +++ b/.builder/actions/manylinux-ci.py @@ -8,8 +8,6 @@ 'cp35-cp35m', 'cp36-cp36m', 'cp37-cp37m', - 'cp27-cp27m', - 'cp27-cp27mu', ) diff --git a/.lgtm.yml b/.lgtm.yml new file mode 100644 index 000000000..d267832eb --- /dev/null +++ b/.lgtm.yml @@ -0,0 +1,5 @@ +extraction: + cpp: + index: + # not sure why cpp builds are using python 2, but this should stop it + build_command: "python3 setup.py build" diff --git a/README.md b/README.md index f9976cb97..a236d4897 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## AWS CRT Python -Python bindings for the AWS Common Runtime. +Python 3 bindings for the AWS Common Runtime. API documentation: https://awslabs.github.io/aws-crt-python/ @@ -8,11 +8,14 @@ API documentation: https://awslabs.github.io/aws-crt-python/ This library is licensed under the Apache 2.0 License. +## Minimum Requirements: +* Python 3.5+ + ## Installation To install from pip: ````bash -python -m pip install awscrt +python3 -m pip install awscrt ```` To install from Github: @@ -20,7 +23,7 @@ To install from Github: git clone https://github.com/awslabs/aws-crt-python.git cd aws-crt-python git submodule update --init -python -m pip install . +python3 -m pip install . ```` To use from your Python application, declare `awscrt` as a dependency in your `setup.py` file. diff --git a/awscrt/__init__.py b/awscrt/__init__.py index 1e7187f17..883bca413 100644 --- a/awscrt/__init__.py +++ b/awscrt/__init__.py @@ -1,7 +1,6 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0. -from sys import version_info from weakref import WeakSet __all__ = [ @@ -13,7 +12,7 @@ ] -class NativeResource(object): +class NativeResource: """ Base for classes that bind to a native type. _binding is a python capsule referencing the native object. @@ -33,12 +32,3 @@ class NativeResource(object): def __init__(self): if NativeResource._track_lifetime: NativeResource._living.add(self) - - -def isinstance_str(x): - """ - Python 2/3 compatible way to check isinstance(x, str). - """ - if version_info[0] == 2: - return isinstance(x, basestring) - return isinstance(x, str) diff --git a/awscrt/auth.py b/awscrt/auth.py index 8a68901cf..859eee54c 100644 --- a/awscrt/auth.py +++ b/awscrt/auth.py @@ -7,35 +7,13 @@ from __future__ import absolute_import import _awscrt -from awscrt import isinstance_str, NativeResource +from awscrt import NativeResource import awscrt.exceptions from awscrt.http import HttpRequest from awscrt.io import ClientBootstrap from concurrent.futures import Future import datetime from enum import IntEnum -import time - -try: - _utc = datetime.timezone.utc -except AttributeError: - # Python 2 lacks the datetime.timestamp() method. - # We can do the timestamp math ourselves, but only if datetime.tzinfo is set. - # Python 2 also lacks any predefined tzinfo classes (ex: datetime.timezone.utc), - # so we must define our own. - class _UTC(datetime.tzinfo): - ZERO = datetime.timedelta(0) - - def utcoffset(self, dt): - return _UTC.ZERO - - def tzname(self, dt): - return "UTC" - - def dst(self, dt): - return _UTC.ZERO - - _utc = _UTC() class AwsCredentials(NativeResource): @@ -57,11 +35,11 @@ class AwsCredentials(NativeResource): __slots__ = () def __init__(self, access_key_id, secret_access_key, session_token=None): - assert isinstance_str(access_key_id) - assert isinstance_str(secret_access_key) - assert isinstance_str(session_token) or session_token is None + assert isinstance(access_key_id, str) + assert isinstance(secret_access_key, str) + assert isinstance(session_token, str) or session_token is None - super(AwsCredentials, self).__init__() + super().__init__() self._binding = _awscrt.credentials_new(access_key_id, secret_access_key, session_token) @classmethod @@ -98,7 +76,7 @@ class AwsCredentialsProviderBase(NativeResource): __slots__ = () def __init__(self, binding=None): - super(AwsCredentialsProviderBase, self).__init__() + super().__init__() if binding is None: # TODO: create binding type that lets native code call into python subclass @@ -156,9 +134,9 @@ def new_static(cls, access_key_id, secret_access_key, session_token=None): Returns: AwsCredentialsProvider: """ - assert isinstance_str(access_key_id) - assert isinstance_str(secret_access_key) - assert isinstance_str(session_token) or session_token is None + assert isinstance(access_key_id, str) + assert isinstance(secret_access_key, str) + assert isinstance(session_token, str) or session_token is None binding = _awscrt.credentials_provider_new_static(access_key_id, secret_access_key, session_token) return cls(binding) @@ -345,28 +323,19 @@ def __init__(self, assert isinstance(algorithm, AwsSigningAlgorithm) assert isinstance(signature_type, AwsSignatureType) assert isinstance(credentials_provider, AwsCredentialsProviderBase) - assert isinstance_str(region) - assert isinstance_str(service) + assert isinstance(region, str) + assert isinstance(service, str) assert callable(should_sign_header) or should_sign_header is None assert signed_body_value is None or (isinstance(signed_body_value, str) and len(signed_body_value) > 0) assert isinstance(signed_body_header_type, AwsSignedBodyHeaderType) assert expiration_in_seconds is None or expiration_in_seconds > 0 - super(AwsSigningConfig, self).__init__() + super().__init__() if date is None: - date = datetime.datetime.now(_utc) + date = datetime.datetime.now(datetime.timezone.utc) - try: - timestamp = date.timestamp() - except AttributeError: - # Python 2 doesn't have datetime.timestamp() function. - # If it did we could just call it from binding code instead of calculating it here. - if date.tzinfo is None: - timestamp = time.mktime(date.timetuple()) - else: - epoch = datetime.datetime(1970, 1, 1, tzinfo=_utc) - timestamp = (date - epoch).total_seconds() + timestamp = date.timestamp() self._priv_should_sign_cb = should_sign_header diff --git a/awscrt/crypto.py b/awscrt/crypto.py index 2c3fae2cf..4c951df3b 100644 --- a/awscrt/crypto.py +++ b/awscrt/crypto.py @@ -4,7 +4,7 @@ import _awscrt -class Hash(object): +class Hash: def __init__(self, native_handle): """ @@ -33,7 +33,7 @@ def digest(self, truncate_to=0): return _awscrt.hash_digest(self._hash, truncate_to) -class HMAC(object): +class HMAC: def __init__(self, native_handle): """ don't call me, I'm private diff --git a/awscrt/http.py b/awscrt/http.py index 1d7638de4..b24a2a3d4 100644 --- a/awscrt/http.py +++ b/awscrt/http.py @@ -10,7 +10,7 @@ from __future__ import absolute_import import _awscrt from concurrent.futures import Future -from awscrt import NativeResource, isinstance_str +from awscrt import NativeResource import awscrt.exceptions from awscrt.io import ClientBootstrap, EventLoopGroup, DefaultHostResolver, InputStream, TlsConnectionOptions, SocketOptions from enum import IntEnum @@ -30,7 +30,7 @@ class HttpConnectionBase(NativeResource): __slots__ = ('_shutdown_future', '_version') def __init__(self): - super(HttpConnectionBase, self).__init__() + super().__init__() self._shutdown_future = Future() @@ -113,7 +113,7 @@ def new(cls, Otherwise, it will contain an exception. """ assert isinstance(bootstrap, ClientBootstrap) or bootstrap is None - assert isinstance_str(host_name) + assert isinstance(host_name, str) assert isinstance(port, int) assert isinstance(tls_connection_options, TlsConnectionOptions) or tls_connection_options is None assert isinstance(socket_options, SocketOptions) or socket_options is None @@ -226,7 +226,7 @@ class HttpStreamBase(NativeResource): __slots__ = ('_connection', '_completion_future', '_on_body_cb') def __init__(self, connection, on_body=None): - super(HttpStreamBase, self).__init__() + super().__init__() self._connection = connection self._completion_future = Future() self._on_body_cb = on_body @@ -268,7 +268,7 @@ def __init__(self, connection, request, on_response=None, on_body=None): assert callable(on_response) or on_response is None assert callable(on_body) or on_body is None - super(HttpClientStream, self).__init__(connection, on_body) + super().__init__(connection, on_body) self._on_response_cb = on_response self._response_status_code = None @@ -318,7 +318,7 @@ class HttpMessageBase(NativeResource): def __init__(self, binding, headers, body_stream=None): assert isinstance(headers, HttpHeaders) - super(HttpMessageBase, self).__init__() + super().__init__() self._binding = binding self._headers = headers @@ -364,7 +364,7 @@ def __init__(self, method='GET', path='/', headers=None, body_stream=None): headers = HttpHeaders() binding = _awscrt.http_message_new_request(headers) - super(HttpRequest, self).__init__(binding, headers, body_stream) + super().__init__(binding, headers, body_stream) self.method = method self.path = path @@ -414,7 +414,7 @@ class HttpHeaders(NativeResource): __slots__ = () def __init__(self, name_value_pairs=None): - super(HttpHeaders, self).__init__() + super().__init__() self._binding = _awscrt.http_headers_new() if name_value_pairs: self.add_pairs(name_value_pairs) @@ -435,8 +435,8 @@ def add(self, name, value): name (str): Name. value (str): Value. """ - assert isinstance_str(name) - assert isinstance_str(value) + assert isinstance(name, str) + assert isinstance(value, str) _awscrt.http_headers_add(self._binding, name, value) def add_pairs(self, name_value_pairs): @@ -456,8 +456,8 @@ def set(self, name, value): name (str): Name. value (str): Value. """ - assert isinstance_str(name) - assert isinstance_str(value) + assert isinstance(name, str) + assert isinstance(value, str) _awscrt.http_headers_set(self._binding, name, value) def get_values(self, name): @@ -470,7 +470,7 @@ def get_values(self, name): Returns: Iterator[Tuple[str, str]]: """ - assert isinstance_str(name) + assert isinstance(name, str) name = name.lower() for i in range(_awscrt.http_headers_count(self._binding)): name_i, value_i = _awscrt.http_headers_get_index(self._binding, i) @@ -489,7 +489,7 @@ def get(self, name, default=None): Returns: str: """ - assert isinstance_str(name) + assert isinstance(name, str) return _awscrt.http_headers_get(self._binding, name, default) def remove(self, name): @@ -500,7 +500,7 @@ def remove(self, name): Args: name (str): Header name. """ - assert isinstance_str(name) + assert isinstance(name, str) _awscrt.http_headers_remove(self._binding, name) def remove_value(self, name, value): @@ -512,8 +512,8 @@ def remove_value(self, name, value): name (str): Name. value (str): Value. """ - assert isinstance_str(name) - assert isinstance_str(value) + assert isinstance(name, str) + assert isinstance(value, str) _awscrt.http_headers_remove_value(self._binding, name, value) def clear(self): @@ -542,7 +542,7 @@ class HttpProxyAuthenticationType(IntEnum): """Username and password""" -class HttpProxyOptions(object): +class HttpProxyOptions: """ Proxy options for HTTP clients. diff --git a/awscrt/io.py b/awscrt/io.py index 322edc744..ba970a896 100644 --- a/awscrt/io.py +++ b/awscrt/io.py @@ -10,7 +10,7 @@ from __future__ import absolute_import import _awscrt -from awscrt import NativeResource, isinstance_str +from awscrt import NativeResource from enum import IntEnum import io import threading @@ -60,7 +60,7 @@ class EventLoopGroup(NativeResource): __slots__ = ('shutdown_event') def __init__(self, num_threads=0): - super(EventLoopGroup, self).__init__() + super().__init__() shutdown_event = threading.Event() @@ -88,7 +88,7 @@ class DefaultHostResolver(HostResolverBase): def __init__(self, event_loop_group, max_hosts=16): assert isinstance(event_loop_group, EventLoopGroup) - super(DefaultHostResolver, self).__init__() + super().__init__() self._binding = _awscrt.host_resolver_new_default(max_hosts, event_loop_group) @@ -110,7 +110,7 @@ def __init__(self, event_loop_group, host_resolver): assert isinstance(event_loop_group, EventLoopGroup) assert isinstance(host_resolver, HostResolverBase) - super(ClientBootstrap, self).__init__() + super().__init__() shutdown_event = threading.Event() @@ -145,7 +145,7 @@ class SocketType(IntEnum): `SocketDomain.Local` is not compatible with `DGram` """ -class SocketOptions(object): +class SocketOptions: """Socket options. Attributes: @@ -190,7 +190,7 @@ class TlsVersion(IntEnum): DEFAULT = 128 #: -class TlsContextOptions(object): +class TlsContextOptions: """Options to create a TLS context. The static `TlsContextOptions.create_X()` methods provide common TLS configurations. @@ -234,8 +234,8 @@ def create_client_with_mtls_from_path(cert_filepath, pk_filepath): TlsContextOptions: """ - assert isinstance_str(cert_filepath) - assert isinstance_str(pk_filepath) + assert isinstance(cert_filepath, str) + assert isinstance(pk_filepath, str) cert_buffer = _read_binary_file(cert_filepath) key_buffer = _read_binary_file(pk_filepath) @@ -282,8 +282,8 @@ def create_client_with_mtls_pkcs12(pkcs12_filepath, pkcs12_password): TlsContextOptions: """ - assert isinstance_str(pkcs12_filepath) - assert isinstance_str(pkcs12_password) + assert isinstance(pkcs12_filepath, str) + assert isinstance(pkcs12_password, str) opt = TlsContextOptions() opt.pkcs12_filepath = pkcs12_filepath @@ -307,8 +307,8 @@ def create_server_from_path(cert_filepath, pk_filepath): TlsContextOptions: """ - assert isinstance_str(cert_filepath) - assert isinstance_str(pk_filepath) + assert isinstance(cert_filepath, str) + assert isinstance(pk_filepath, str) cert_buffer = _read_binary_file(cert_filepath) key_buffer = _read_binary_file(pk_filepath) @@ -353,8 +353,8 @@ def create_server_pkcs12(pkcs12_filepath, pkcs12_password): TlsContextOptions: """ - assert isinstance_str(pkcs12_filepath) - assert isinstance_str(pkcs12_password) + assert isinstance(pkcs12_filepath, str) + assert isinstance(pkcs12_password, str) opt = TlsContextOptions() opt.pkcs12_filepath = pkcs12_filepath @@ -373,8 +373,8 @@ def override_default_trust_store_from_path(self, ca_dirpath=None, ca_filepath=No of trusted CA certificates. """ - assert isinstance_str(ca_dirpath) or ca_dirpath is None - assert isinstance_str(ca_filepath) or ca_filepath is None + assert isinstance(ca_dirpath, str) or ca_dirpath is None + assert isinstance(ca_filepath, str) or ca_filepath is None if ca_filepath: ca_buffer = _read_binary_file(ca_filepath) @@ -407,7 +407,7 @@ class ClientTlsContext(NativeResource): def __init__(self, options): assert isinstance(options, TlsContextOptions) - super(ClientTlsContext, self).__init__() + super().__init__() self._binding = _awscrt.client_tls_ctx_new( options.min_tls_ver.value, options.ca_dirpath, @@ -445,7 +445,7 @@ class TlsConnectionOptions(NativeResource): def __init__(self, tls_ctx): assert isinstance(tls_ctx, ClientTlsContext) - super(TlsConnectionOptions, self).__init__() + super().__init__() self.tls_ctx = tls_ctx self._binding = _awscrt.tls_connections_options_new_from_ctx(tls_ctx) @@ -478,7 +478,7 @@ def _alpn_list_to_str(alpn_list): None is returned if list is None or empty """ if alpn_list: - assert not isinstance_str(alpn_list) + assert not isinstance(alpn_list, str) return ';'.join(alpn_list) return None @@ -502,7 +502,7 @@ def __init__(self, stream): assert isinstance(stream, io.IOBase) assert not isinstance(stream, InputStream) - super(InputStream, self).__init__() + super().__init__() self._binding = _awscrt.input_stream_new(stream) @classmethod diff --git a/awscrt/mqtt.py b/awscrt/mqtt.py index 1d60684a0..198529ecc 100644 --- a/awscrt/mqtt.py +++ b/awscrt/mqtt.py @@ -96,7 +96,7 @@ class ConnectReturnCode(IntEnum): """ -class Will(object): +class Will: """A Will message is published by the server if a client is lost unexpectedly. The Will message is stored on the server when a client connects. @@ -142,7 +142,7 @@ def __init__(self, bootstrap, tls_ctx=None): assert isinstance(bootstrap, ClientBootstrap) assert tls_ctx is None or isinstance(tls_ctx, ClientTlsContext) - super(Client, self).__init__() + super().__init__() self.tls_ctx = tls_ctx self._binding = _awscrt.mqtt_client_new(bootstrap, tls_ctx) @@ -275,7 +275,7 @@ def __init__(self, if keep_alive_secs * 1000 <= ping_timeout_ms: raise ValueError("'keep_alive_secs' duration must be longer than 'ping_timeout_ms'") - super(Connection, self).__init__() + super().__init__() # init-only self.client = client @@ -641,7 +641,7 @@ def puback(packet_id, error_code): return future, packet_id -class WebsocketHandshakeTransformArgs(object): +class WebsocketHandshakeTransformArgs: """ Argument to a "websocket_handshake_transform" function. diff --git a/builder.json b/builder.json index b320e6c65..1515be45a 100644 --- a/builder.json +++ b/builder.json @@ -10,7 +10,9 @@ "openssl_include": "/opt/openssl/include", "openssl_lib": "/opt/openssl/lib" } - }, + } + }, + "hosts": { "manylinux": { "!build_steps": [ "manylinux-ci" diff --git a/codebuild/cd/manylinux-x64-build.yml b/codebuild/cd/manylinux-x64-build.yml index 11469541e..203a6e526 100644 --- a/codebuild/cd/manylinux-x64-build.yml +++ b/codebuild/cd/manylinux-x64-build.yml @@ -12,18 +12,12 @@ phases: build: commands: - echo Build started on `date` - - /opt/python/cp34-cp34m/bin/python setup.py sdist bdist_wheel - - auditwheel repair --plat manylinux1_x86_64 dist/awscrt-*cp34-cp34m-linux_x86_64.whl - /opt/python/cp35-cp35m/bin/python setup.py sdist bdist_wheel - auditwheel repair --plat manylinux1_x86_64 dist/awscrt-*cp35-cp35m-linux_x86_64.whl - /opt/python/cp36-cp36m/bin/python setup.py sdist bdist_wheel - auditwheel repair --plat manylinux1_x86_64 dist/awscrt-*cp36-cp36m-linux_x86_64.whl - /opt/python/cp37-cp37m/bin/python setup.py sdist bdist_wheel - auditwheel repair --plat manylinux1_x86_64 dist/awscrt-*cp37-cp37m-linux_x86_64.whl - - /opt/python/cp27-cp27m/bin/python setup.py sdist bdist_wheel - - auditwheel repair --plat manylinux1_x86_64 dist/awscrt-*cp27-cp27m-linux_x86_64.whl - - /opt/python/cp27-cp27mu/bin/python setup.py sdist bdist_wheel - - auditwheel repair --plat manylinux1_x86_64 dist/awscrt-*cp27-cp27mu-linux_x86_64.whl - cp -r wheelhouse ../dist - cp dist/*.tar.gz ../dist/ post_build: diff --git a/codebuild/cd/manylinux-x86-build.yml b/codebuild/cd/manylinux-x86-build.yml index 330711b11..c401c02f7 100644 --- a/codebuild/cd/manylinux-x86-build.yml +++ b/codebuild/cd/manylinux-x86-build.yml @@ -12,18 +12,12 @@ phases: build: commands: - echo Build started on `date` - - /opt/python/cp34-cp34m/bin/python setup.py sdist bdist_wheel - - auditwheel repair --plat manylinux1_i686 dist/awscrt-*cp34-cp34m-linux_i686.whl - /opt/python/cp35-cp35m/bin/python setup.py sdist bdist_wheel - auditwheel repair --plat manylinux1_i686 dist/awscrt-*cp35-cp35m-linux_i686.whl - /opt/python/cp36-cp36m/bin/python setup.py sdist bdist_wheel - auditwheel repair --plat manylinux1_i686 dist/awscrt-*cp36-cp36m-linux_i686.whl - /opt/python/cp37-cp37m/bin/python setup.py sdist bdist_wheel - auditwheel repair --plat manylinux1_i686 dist/awscrt-*cp37-cp37m-linux_i686.whl - - /opt/python/cp27-cp27m/bin/python setup.py sdist bdist_wheel - - auditwheel repair --plat manylinux1_i686 dist/awscrt-*cp27-cp27m-linux_i686.whl - - /opt/python/cp27-cp27mu/bin/python setup.py sdist bdist_wheel - - auditwheel repair --plat manylinux1_i686 dist/awscrt-*cp27-cp27mu-linux_i686.whl - cp -r wheelhouse ../dist post_build: commands: diff --git a/continuous-delivery/build-wheels-osx.sh b/continuous-delivery/build-wheels-osx.sh index fc4fb8875..0aaab1d17 100755 --- a/continuous-delivery/build-wheels-osx.sh +++ b/continuous-delivery/build-wheels-osx.sh @@ -6,7 +6,6 @@ set -e /Library/Frameworks/Python.framework/Versions/3.8/bin/python3 ./continuous-delivery/update-version.py export MACOSX_DEPLOYMENT_TARGET="10.9" -/Library/Frameworks/Python.framework/Versions/2.7/bin/python setup.py bdist_wheel /Library/Frameworks/Python.framework/Versions/3.5/bin/python3 setup.py bdist_wheel /Library/Frameworks/Python.framework/Versions/3.6/bin/python3 setup.py bdist_wheel /Library/Frameworks/Python.framework/Versions/3.7/bin/python3 setup.py sdist bdist_wheel diff --git a/continuous-delivery/build-wheels-win32.bat b/continuous-delivery/build-wheels-win32.bat index f6b4675e0..d5a88f62d 100644 --- a/continuous-delivery/build-wheels-win32.bat +++ b/continuous-delivery/build-wheels-win32.bat @@ -1,7 +1,6 @@ "C:\Program Files (x86)\Python38-32\python.exe" .\continuous-delivery\update-version.py || goto error -"C:\Python27 (x86)\python.exe" setup.py sdist bdist_wheel || goto error "C:\Program Files (x86)\Python35-32\python.exe" setup.py sdist bdist_wheel || goto error "C:\Program Files (x86)\Python36-32\python.exe" setup.py sdist bdist_wheel || goto error "C:\Program Files (x86)\Python37-32\python.exe" setup.py sdist bdist_wheel || goto error diff --git a/continuous-delivery/build-wheels-win64.bat b/continuous-delivery/build-wheels-win64.bat index 936ea1a9b..7371ca972 100644 --- a/continuous-delivery/build-wheels-win64.bat +++ b/continuous-delivery/build-wheels-win64.bat @@ -1,6 +1,5 @@ "C:\Program Files\Python38\python.exe" continuous-delivery\update-version.py || goto error -"C:\Python27\python.exe" setup.py sdist bdist_wheel || goto error "C:\Program Files\Python35\python.exe" setup.py sdist bdist_wheel || goto error "C:\Program Files\Python36\python.exe" setup.py sdist bdist_wheel || goto error "C:\Program Files\Python37\python.exe" setup.py sdist bdist_wheel || goto error diff --git a/continuous-delivery/sanity-check-test-pypi.bat b/continuous-delivery/sanity-check-test-pypi.bat index c6e6b35e6..34bd2e67b 100644 --- a/continuous-delivery/sanity-check-test-pypi.bat +++ b/continuous-delivery/sanity-check-test-pypi.bat @@ -4,9 +4,6 @@ set CURRENT_VERSION=%TAG_VERSION:v=% "C:\Program Files\Python37\python.exe" -m pip install --no-cache-dir -i https://testpypi.python.org/simple --user awscrt==%CURRENT_VERSION% || goto error "C:\Program Files\Python37\python.exe" continuous-delivery\test-pip-install.py || goto error -"C:\Python27\python.exe" -m pip install --no-cache-dir -i https://testpypi.python.org/simple --user awscrt==%CURRENT_VERSION% || goto error -"C:\Python27\python.exe" continuous-delivery\test-pip-install.py || goto error - goto :EOF :error diff --git a/continuous-delivery/sanity-check-test-pypi.sh b/continuous-delivery/sanity-check-test-pypi.sh index 29aa50d51..97c86ec82 100644 --- a/continuous-delivery/sanity-check-test-pypi.sh +++ b/continuous-delivery/sanity-check-test-pypi.sh @@ -3,7 +3,3 @@ set -ex CURRENT_TAG_VERSION=$(git describe --tags | cut -f2 -dv) python3 -m pip install --no-cache-dir -i https://testpypi.python.org/simple --user awscrt==$CURRENT_TAG_VERSION python3 continuous-delivery/test-pip-install.py - -python -m pip install --no-cache-dir -i https://testpypi.python.org/simple --user awscrt==$CURRENT_TAG_VERSION -python continuous-delivery/test-pip-install.py - diff --git a/elasticurl.py b/elasticurl.py index fe41dab94..1f2787120 100755 --- a/elasticurl.py +++ b/elasticurl.py @@ -4,12 +4,9 @@ import argparse import sys import os -from io import BytesIO, open # Python2's built-in open() doesn't return a stream +from io import BytesIO from awscrt import io, http -try: - from urllib.parse import urlparse -except ImportError: - from urlparse import urlparse +from urllib.parse import urlparse def print_header_list(headers): diff --git a/setup.py b/setup.py index b26c78015..808dd99cc 100644 --- a/setup.py +++ b/setup.py @@ -86,7 +86,7 @@ def check_cmake_installed(): raise Exception("'cmake' not found. cmake must be installed to build from source.") -class AwsLib(object): +class AwsLib: def __init__(self, name, extra_cmake_args=[]): self.name = name self.extra_cmake_args = extra_cmake_args @@ -182,7 +182,7 @@ def run(self): self.library_dirs.append(os.path.join(DEP_INSTALL_PATH, lib_dir)) # continue with normal build_ext.run() - setuptools.command.build_ext.build_ext.run(self) # python2 breaks if we use super().run() + super().run() def awscrt_ext(): @@ -242,15 +242,11 @@ def awscrt_ext(): url="https://github.com/awslabs/aws-crt-python", packages=['awscrt'], classifiers=[ - "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", ], - install_requires=[ - 'enum34;python_version<"3.4"', - 'futures;python_version<"3.2"', - ], + python_requires='>=3.5', ext_modules=[awscrt_ext()], cmdclass={'build_ext': awscrt_build_ext}, test_suite='test', diff --git a/source/auth_credentials.c b/source/auth_credentials.c index 767c8969c..05c2cde41 100644 --- a/source/auth_credentials.c +++ b/source/auth_credentials.c @@ -97,7 +97,7 @@ static PyObject *s_credentials_get_member_str(PyObject *args, enum credentials_m Py_RETURN_NONE; } - return PyString_FromAwsByteCursor(&cursor); + return PyUnicode_FromAwsByteCursor(&cursor); } PyObject *aws_py_credentials_access_key_id(PyObject *self, PyObject *args) { diff --git a/source/auth_signing_config.c b/source/auth_signing_config.c index 995d6e142..72fb2b04c 100644 --- a/source/auth_signing_config.c +++ b/source/auth_signing_config.c @@ -229,7 +229,7 @@ PyObject *aws_py_signing_config_get_region(PyObject *self, PyObject *args) { return NULL; } - return PyString_FromAwsByteCursor(&binding->native.region); + return PyUnicode_FromAwsByteCursor(&binding->native.region); } PyObject *aws_py_signing_config_get_service(PyObject *self, PyObject *args) { @@ -238,7 +238,7 @@ PyObject *aws_py_signing_config_get_service(PyObject *self, PyObject *args) { return NULL; } - return PyString_FromAwsByteCursor(&binding->native.service); + return PyUnicode_FromAwsByteCursor(&binding->native.service); } PyObject *aws_py_signing_config_get_date(PyObject *self, PyObject *args) { @@ -281,7 +281,7 @@ PyObject *aws_py_signing_config_get_signed_body_value(PyObject *self, PyObject * Py_RETURN_NONE; } - return PyString_FromAwsByteCursor(&binding->native.signed_body_value); + return PyUnicode_FromAwsByteCursor(&binding->native.signed_body_value); } PyObject *aws_py_signing_config_get_signed_body_header_type(PyObject *self, PyObject *args) { diff --git a/source/http_headers.c b/source/http_headers.c index 9a1e7f01b..187cd7c0e 100644 --- a/source/http_headers.c +++ b/source/http_headers.c @@ -106,8 +106,8 @@ PyObject *aws_py_http_headers_add_pairs(PyObject *self, PyObject *args) { goto done; } - struct aws_byte_cursor name = aws_byte_cursor_from_pystring(PyTuple_GET_ITEM(py_pair, 0)); - struct aws_byte_cursor value = aws_byte_cursor_from_pystring(PyTuple_GET_ITEM(py_pair, 1)); + 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; @@ -152,7 +152,7 @@ PyObject *aws_py_http_headers_get(PyObject *self, PyObject *args) { return py_default; } - return PyString_FromAwsByteCursor(&value); + return PyUnicode_FromAwsByteCursor(&value); } static PyObject *s_py_tuple_from_header(struct aws_http_header header) { @@ -160,12 +160,12 @@ static PyObject *s_py_tuple_from_header(struct aws_http_header header) { PyObject *py_value = NULL; PyObject *py_pair = NULL; - py_name = PyString_FromAwsByteCursor(&header.name); + py_name = PyUnicode_FromAwsByteCursor(&header.name); if (!py_name) { goto error; } - py_value = PyString_FromAwsByteCursor(&header.value); + py_value = PyUnicode_FromAwsByteCursor(&header.value); if (!py_value) { goto error; } diff --git a/source/http_message.c b/source/http_message.c index d938e6f54..687c45437 100644 --- a/source/http_message.c +++ b/source/http_message.c @@ -124,7 +124,7 @@ PyObject *aws_py_http_message_get_request_method(PyObject *self, PyObject *args) Py_RETURN_NONE; } - return PyString_FromAwsByteCursor(&method); + return PyUnicode_FromAwsByteCursor(&method); } PyObject *aws_py_http_message_set_request_method(PyObject *self, PyObject *args) { @@ -158,7 +158,7 @@ PyObject *aws_py_http_message_get_request_path(PyObject *self, PyObject *args) { if (aws_http_message_get_request_path(binding->native, &path)) { Py_RETURN_NONE; } - return PyString_FromAwsByteCursor(&path); + return PyUnicode_FromAwsByteCursor(&path); } PyObject *aws_py_http_message_set_request_path(PyObject *self, PyObject *args) { diff --git a/source/http_proxy.c b/source/http_proxy.c index 743affd57..4af156f6e 100644 --- a/source/http_proxy.c +++ b/source/http_proxy.c @@ -20,7 +20,7 @@ bool aws_py_http_proxy_options_init(struct aws_http_proxy_options *proxy_options PyObject *py_password = NULL; py_host_name = PyObject_GetAttrString(py_proxy_options, "host_name"); - proxy_options->host = aws_byte_cursor_from_pystring(py_host_name); + proxy_options->host = aws_byte_cursor_from_pyunicode(py_host_name); if (!proxy_options->host.ptr) { PyErr_SetString(PyExc_TypeError, "HttpProxyOptions.host_name is not a valid string"); goto done; @@ -48,7 +48,7 @@ bool aws_py_http_proxy_options_init(struct aws_http_proxy_options *proxy_options py_username = PyObject_GetAttrString(py_proxy_options, "auth_username"); if (py_username != Py_None) { - proxy_options->auth_username = aws_byte_cursor_from_pystring(py_username); + proxy_options->auth_username = aws_byte_cursor_from_pyunicode(py_username); if (!proxy_options->auth_username.ptr) { PyErr_SetString(PyExc_TypeError, "HttpProxyOptions.auth_username is not a valid string"); goto done; @@ -57,7 +57,7 @@ bool aws_py_http_proxy_options_init(struct aws_http_proxy_options *proxy_options py_password = PyObject_GetAttrString(py_proxy_options, "auth_password"); if (py_password != Py_None) { - proxy_options->auth_password = aws_byte_cursor_from_pystring(py_password); + proxy_options->auth_password = aws_byte_cursor_from_pyunicode(py_password); if (!proxy_options->auth_password.ptr) { PyErr_SetString(PyExc_TypeError, "HttpProxyOptions.auth_password is not a valid string"); goto done; diff --git a/source/http_stream.c b/source/http_stream.c index c902373aa..9d34550d5 100644 --- a/source/http_stream.c +++ b/source/http_stream.c @@ -166,8 +166,7 @@ static int s_on_incoming_body( return AWS_OP_ERR; /* Python has shut down. Nothing matters anymore, but don't crash */ } - PyObject *result = PyObject_CallMethod( - stream->self_proxy, "_on_body", "(" READABLE_BYTES_FORMAT_STR ")", (const char *)data->ptr, data_len); + PyObject *result = PyObject_CallMethod(stream->self_proxy, "_on_body", "(y#)", (const char *)data->ptr, data_len); if (!result) { aws_result = aws_py_raise_error(); goto done; @@ -200,12 +199,7 @@ static void s_on_stream_complete(struct aws_http_stream *native_stream, int erro } /* DECREF python self, we don't need to force it to stay alive any longer. */ - PyObject *self = PyWeakref_GetObject(stream->self_proxy); - Py_DECREF(self); - /* Hack note. This use to be a one liner: Py_DECREF(PyWeakref_GetObject(stream->self_proxy)); - * that would crash on Python 2, saying we were DECREF'ing Py_None. - * That should be impossible because we're forcing the python self to stay alive. - * I have no idea why splitting it into 2 lines fixes everything, but it does. */ + Py_DECREF(PyWeakref_GetObject(stream->self_proxy)); PyGILState_Release(state); /*************** GIL RELEASE ***************/ diff --git a/source/module.c b/source/module.c index 94c15ecee..aba3344c6 100644 --- a/source/module.c +++ b/source/module.c @@ -70,43 +70,39 @@ PyObject *aws_py_init_logging(PyObject *self, PyObject *args) { Py_RETURN_NONE; } -#if PY_MAJOR_VERSION == 3 -# define UNICODE_GET_BYTES_FN PyUnicode_DATA -# define UNICODE_GET_BYTE_LEN_FN PyUnicode_GET_LENGTH -#elif PY_MAJOR_VERSION == 2 -# define UNICODE_GET_BYTES_FN PyUnicode_AS_DATA -# define UNICODE_GET_BYTE_LEN_FN PyUnicode_GET_DATA_SIZE -#endif /* PY_MAJOR_VERSION */ - -struct aws_byte_cursor aws_byte_cursor_from_pystring(PyObject *str) { - if (PyBytes_CheckExact(str)) { - return aws_byte_cursor_from_array(PyBytes_AsString(str), PyBytes_Size(str)); - } - if (PyUnicode_CheckExact(str)) { - return aws_byte_cursor_from_array(UNICODE_GET_BYTES_FN(str), UNICODE_GET_BYTE_LEN_FN(str)); +struct aws_byte_cursor aws_byte_cursor_from_pyunicode(PyObject *str) { + Py_ssize_t len; + const char *ptr = PyUnicode_AsUTF8AndSize(str, &len); + if (ptr) { + return aws_byte_cursor_from_array(ptr, (size_t)len); } - return aws_byte_cursor_from_array(NULL, 0); } -PyObject *PyString_FromAwsString(const struct aws_string *aws_str) { - return PyString_FromStringAndSize(aws_string_c_str(aws_str), aws_str->len); +struct aws_byte_cursor aws_byte_cursor_from_pybytes(PyObject *py_bytes) { + char *ptr; + Py_ssize_t len; + if (PyBytes_AsStringAndSize(py_bytes, &ptr, &len) == -1) { + return aws_byte_cursor_from_array(NULL, 0); + } + + return aws_byte_cursor_from_array(ptr, (size_t)len); } -int PyIntEnum_Check(PyObject *int_enum_obj) { -#if PY_MAJOR_VERSION == 2 - return PyInt_Check(int_enum_obj); -#else - return PyLong_Check(int_enum_obj); -#endif +PyObject *PyUnicode_FromAwsByteCursor(const struct aws_byte_cursor *cursor) { + if (cursor->len > PY_SSIZE_T_MAX) { + PyErr_SetString(PyExc_OverflowError, "Cursor exceeds PY_SSIZE_T_MAX"); + return NULL; + } + return PyUnicode_FromStringAndSize((const char *)cursor->ptr, (Py_ssize_t)cursor->len); } -long PyIntEnum_AsLong(PyObject *int_enum_obj) { -#if PY_MAJOR_VERSION == 2 - return PyInt_AsLong(int_enum_obj); -#else - return PyLong_AsLong(int_enum_obj); -#endif +PyObject *PyUnicode_FromAwsString(const struct aws_string *aws_str) { + if (aws_str->len > PY_SSIZE_T_MAX) { + PyErr_SetString(PyExc_OverflowError, "String exceeds PY_SSIZE_T_MAX"); + return NULL; + } + return PyUnicode_FromStringAndSize(aws_string_c_str(aws_str), aws_str->len); } uint32_t PyObject_GetAttrAsUint32(PyObject *o, const char *class_name, const char *attr_name) { @@ -204,12 +200,12 @@ int PyObject_GetAttrAsIntEnum(PyObject *o, const char *class_name, const char *a return result; } - if (!PyIntEnum_Check(attr)) { + if (!PyLong_Check(attr)) { PyErr_Format(PyExc_TypeError, "%s.%s is not a valid enum", class_name, attr_name); goto done; } - result = PyIntEnum_AsLong(attr); + result = PyLong_AsLong(attr); done: Py_DECREF(attr); return result; @@ -246,11 +242,9 @@ static void s_error_map_init(void) { {PyExc_OverflowError, AWS_ERROR_OVERFLOW_DETECTED}, {PyExc_TypeError, AWS_ERROR_INVALID_ARGUMENT}, {PyExc_ValueError, AWS_ERROR_INVALID_ARGUMENT}, -#if PY_MAJOR_VERSION == 3 {PyExc_FileNotFoundError, AWS_ERROR_FILE_INVALID_PATH}, {PyExc_BlockingIOError, AWS_IO_READ_WOULD_BLOCK}, {PyExc_BrokenPipeError, AWS_IO_BROKEN_PIPE}, -#endif }; if (aws_hash_table_init( @@ -332,7 +326,7 @@ PyObject *aws_py_get_error_name(PyObject *self, PyObject *args) { } const char *name = aws_error_name(error_code); - return PyString_FromString(name); + return PyUnicode_FromString(name); } PyObject *aws_py_get_error_message(PyObject *self, PyObject *args) { @@ -343,7 +337,7 @@ PyObject *aws_py_get_error_message(PyObject *self, PyObject *args) { } const char *name = aws_error_str(error_code); - return PyString_FromString(name); + return PyUnicode_FromString(name); } PyObject *aws_py_memory_view_from_byte_buffer(struct aws_byte_buf *buf) { @@ -355,18 +349,7 @@ PyObject *aws_py_memory_view_from_byte_buffer(struct aws_byte_buf *buf) { Py_ssize_t mem_size = available; char *mem_start = (char *)(buf->buffer + buf->len); - -#if PY_MAJOR_VERSION == 3 return PyMemoryView_FromMemory(mem_start, mem_size, PyBUF_WRITE); -#else - Py_buffer py_buf; - int read_only = 0; - if (PyBuffer_FillInfo(&py_buf, NULL /*obj*/, mem_start, mem_size, read_only, PyBUF_WRITABLE)) { - return NULL; - } - - return PyMemoryView_FromBuffer(&py_buf); -#endif /* PY_MAJOR_VERSION */ } int aws_py_gilstate_ensure(PyGILState_STATE *out_state) { @@ -570,22 +553,6 @@ PyDoc_STRVAR(s_module_doc, "C extension for binding AWS implementations of MQTT, * Module Init ******************************************************************************/ -static void s_module_init(void) { - s_install_crash_handler(); - - aws_http_library_init(aws_py_get_allocator()); - aws_auth_library_init(aws_py_get_allocator()); - aws_mqtt_library_init(aws_py_get_allocator()); - - if (!PyEval_ThreadsInitialized()) { - PyEval_InitThreads(); - } - - s_error_map_init(); -} - -#if PY_MAJOR_VERSION == 3 - PyMODINIT_FUNC PyInit__awscrt(void) { static struct PyModuleDef s_module_def = { PyModuleDef_HEAD_INIT, @@ -604,20 +571,17 @@ PyMODINIT_FUNC PyInit__awscrt(void) { return NULL; } - s_module_init(); - return m; -} + s_install_crash_handler(); -#elif PY_MAJOR_VERSION == 2 + aws_http_library_init(aws_py_get_allocator()); + aws_auth_library_init(aws_py_get_allocator()); + aws_mqtt_library_init(aws_py_get_allocator()); -PyMODINIT_FUNC init_awscrt(void) { - if (!Py_InitModule3(s_module_name, s_module_methods, s_module_doc)) { - AWS_FATAL_ASSERT(0 && "Failed to initialize _awscrt"); + if (!PyEval_ThreadsInitialized()) { + PyEval_InitThreads(); } - s_module_init(); -} + s_error_map_init(); -#else -# error Unsupported Python version -#endif /* PY_MAJOR_VERSION */ + return m; +} diff --git a/source/module.h b/source/module.h index 757e83621..9a415b2ba 100644 --- a/source/module.h +++ b/source/module.h @@ -15,23 +15,12 @@ #include struct aws_byte_buf; +struct aws_byte_cursor; struct aws_string; -#if PY_MAJOR_VERSION >= 3 -# define PyString_FromStringAndSize PyUnicode_FromStringAndSize -# define PyString_FromString PyUnicode_FromString -# define READABLE_BYTES_FORMAT_STR "y#" -#else -# define READABLE_BYTES_FORMAT_STR "s#" -#endif /* PY_MAJOR_VERSION */ - /* AWS Specific Helpers */ -#define PyBool_FromAwsResult(result) PyBool_FromLong((result) == AWS_OP_SUCCESS) -#define PyString_FromAwsByteCursor(cursor) PyString_FromStringAndSize((const char *)(cursor)->ptr, (cursor)->len) -PyObject *PyString_FromAwsString(const struct aws_string *aws_str); - -int PyIntEnum_Check(PyObject *int_enum_obj); -long PyIntEnum_AsLong(PyObject *int_enum_obj); +PyObject *PyUnicode_FromAwsByteCursor(const struct aws_byte_cursor *cursor); +PyObject *PyUnicode_FromAwsString(const struct aws_string *aws_str); /* Return the named attribute, converted to the specified type. * If conversion cannot occur a python exception is set (check PyExc_Occurred()) */ @@ -40,9 +29,13 @@ uint16_t PyObject_GetAttrAsUint16(PyObject *o, const char *class_name, const cha bool PyObject_GetAttrAsBool(PyObject *o, const char *class_name, const char *attr_name); int PyObject_GetAttrAsIntEnum(PyObject *o, const char *class_name, const char *attr_name); -/* Create cursor from PyString, PyBytes, or PyUnicode. - * If conversion cannot occur, cursor->ptr will be NULL but no python exception is set */ -struct aws_byte_cursor aws_byte_cursor_from_pystring(PyObject *str); +/* Create cursor from PyUnicode. + * If conversion cannot occur, cursor->ptr will be NULL and a python exception is set */ +struct aws_byte_cursor aws_byte_cursor_from_pyunicode(PyObject *str); + +/* Create cursor from PyBytes. + * If conversion cannot occur, cursor->ptr will be NULL and a python exception is set */ +struct aws_byte_cursor aws_byte_cursor_from_pybytes(PyObject *py_bytes); /* Set current thread's error indicator based on aws_last_error() */ void PyErr_SetAwsLastError(void); diff --git a/source/mqtt_client_connection.c b/source/mqtt_client_connection.c index 5e043ce6d..74a1c74d0 100644 --- a/source/mqtt_client_connection.c +++ b/source/mqtt_client_connection.c @@ -290,9 +290,9 @@ bool s_set_will(struct aws_mqtt_client_connection *connection, PyObject *will) { PyObject *py_payload = NULL; py_topic = PyObject_GetAttrString(will, "topic"); - struct aws_byte_cursor topic = aws_byte_cursor_from_pystring(py_topic); + struct aws_byte_cursor topic = aws_byte_cursor_from_pyunicode(py_topic); if (!topic.ptr) { - PyErr_SetString(PyExc_TypeError, "Will.topic is invalid"); + PyErr_SetString(PyExc_TypeError, "Will.topic must be str type"); goto done; } @@ -302,9 +302,9 @@ bool s_set_will(struct aws_mqtt_client_connection *connection, PyObject *will) { } py_payload = PyObject_GetAttrString(will, "payload"); - struct aws_byte_cursor payload = aws_byte_cursor_from_pystring(py_payload); + struct aws_byte_cursor payload = aws_byte_cursor_from_pybytes(py_payload); if (!payload.ptr) { - PyErr_SetString(PyExc_TypeError, "Will.payload is invalid"); + PyErr_SetString(PyExc_TypeError, "Will.payload must be bytes type"); goto done; } @@ -813,7 +813,7 @@ static void s_subscribe_callback( PyObject *result = PyObject_CallFunction( callback, "(NN)", - PyString_FromAwsByteCursor(topic), + PyUnicode_FromAwsByteCursor(topic), PyBytes_FromStringAndSize((const char *)payload->ptr, (Py_ssize_t)payload->len)); if (result) { diff --git a/test/__init__.py b/test/__init__.py index 228b645db..c6f5c9961 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -67,7 +67,7 @@ def _printobj(prefix, obj): print(' gc.referrers():', len(referrers)) for r in referrers: if isinstance(r, types.FrameType): - print(' -', inspect.getframeinfo(r)) + _printobj(' -', inspect.getframeinfo(r)) else: _printobj(' -', r) diff --git a/test/test_auth.py b/test/test_auth.py index 09dd26ef9..b230270d5 100644 --- a/test/test_auth.py +++ b/test/test_auth.py @@ -15,7 +15,7 @@ EXAMPLE_SESSION_TOKEN = 'example_session_token' -class ScopedEnvironmentVariable(object): +class ScopedEnvironmentVariable: """ Set environment variable for lifetime of this object. """ @@ -238,7 +238,14 @@ def test_special_defaults(self): SIGV4TEST_SESSION_TOKEN = None SIGV4TEST_SERVICE = 'service' SIGV4TEST_REGION = 'us-east-1' -SIGV4TEST_DATE = datetime.datetime(year=2015, month=8, day=30, hour=12, minute=36, second=0, tzinfo=awscrt.auth._utc) +SIGV4TEST_DATE = datetime.datetime( + year=2015, + month=8, + day=30, + hour=12, + minute=36, + second=0, + tzinfo=datetime.timezone.utc) class TestSigner(NativeResourceTest): diff --git a/test/test_http_client.py b/test/test_http_client.py index 7b6ffaed0..f85d6ae2d 100644 --- a/test/test_http_client.py +++ b/test/test_http_client.py @@ -6,33 +6,20 @@ from awscrt.http import HttpClientConnection, HttpClientStream, HttpHeaders, HttpProxyOptions, HttpRequest, HttpVersion from awscrt.io import ClientBootstrap, ClientTlsContext, DefaultHostResolver, EventLoopGroup, TlsConnectionOptions, TlsContextOptions from concurrent.futures import Future -from io import BytesIO, open # Python2's built-in open() doesn't return a stream +from http.server import HTTPServer, SimpleHTTPRequestHandler +from io import BytesIO import os import ssl from test import NativeResourceTest import threading -try: - from urllib.parse import urlparse -except ImportError: - from urlparse import urlparse import unittest - -# Use a built-in Python HTTP server to test the awscrt's HTTP client -try: - from http.server import HTTPServer, SimpleHTTPRequestHandler -except ImportError: - # Simple HTTP server lives in a different places in Python3 vs Python2: - # http.server.HTTPServer == SocketServer.TCPServer - # http.server.SimpleHTTPRequestHandler == SimpleHTTPServer.SimpleHTTPRequestHandler - from SimpleHTTPServer import SimpleHTTPRequestHandler - import SocketServer - HTTPServer = SocketServer.TCPServer +from urllib.parse import urlparse PROXY_HOST = os.environ.get('proxyhost') PROXY_PORT = int(os.environ.get('proxyport', '0')) -class Response(object): +class Response: """Holds contents of incoming response""" def __init__(self): diff --git a/test/test_http_headers.py b/test/test_http_headers.py index 3146176ff..4aade884b 100644 --- a/test/test_http_headers.py +++ b/test/test_http_headers.py @@ -3,7 +3,6 @@ from awscrt.http import HttpHeaders, HttpRequest import awscrt.io -from io import open # Python2's built-in open() doesn't return a stream from test import NativeResourceTest import unittest @@ -50,6 +49,20 @@ def test_set(self): h.set('Host', 'example5.org') self.assertEqual(['example5.org'], list(h.get_values('Host'))) + def test_unicode(self): + # test adding unicode values in all the different ways + h = HttpHeaders([('a', 'แˆด')]) + self.assertEqual('แˆด', h.get('a')) + + h.set('b', '๐ฆ‰˜') + self.assertEqual('๐ฆ‰˜', h.get('b')) + + h.add('c', '๐Ÿ‘๐Ÿ‘„๐Ÿ‘') + self.assertEqual('๐Ÿ‘๐Ÿ‘„๐Ÿ‘', h.get('c')) + + h.add_pairs([('d', 'โ“คลฃแธŸโปโฝ')]) + self.assertEqual('โ“คลฃแธŸโปโฝ', h.get('d')) + def test_get_none(self): h = HttpHeaders() self.assertIsNone(h.get('Non-Existent')) @@ -141,7 +154,7 @@ def test_headers_live_after_message_del(self): headers.add('Cookie', 'a=1') self.assertEqual([('Cookie', 'a=1')], list(headers)) - def test_utf8(self): + def test_unicode(self): request = HttpRequest(path='/แˆด') self.assertEqual('/แˆด', request.path) diff --git a/test/test_mqtt.py b/test/test_mqtt.py index eb14af6be..870a80a25 100644 --- a/test/test_mqtt.py +++ b/test/test_mqtt.py @@ -50,10 +50,7 @@ def get(): # boto3 caches the HTTPS connection for the API calls, which appears to the unit test # framework as a leak, so ignore it, that's not what we're testing here - try: - warnings.simplefilter('ignore', ResourceWarning) - except NameError: # Python 2 has no ResourceWarning - pass + warnings.simplefilter('ignore', ResourceWarning) try: secrets = boto3.client('secretsmanager')