From 82898b965560f459e9a8682d3991dc1c27548860 Mon Sep 17 00:00:00 2001 From: Kevin Conley Date: Mon, 3 Jan 2022 21:47:51 +0000 Subject: [PATCH 1/6] Make git ignore VS Code folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9647e71..39dd71b 100755 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.mpy .idea +.vscode __pycache__ _build *.pyc From 9035f7b1cdda43fed48a175f966247594745a82b Mon Sep 17 00:00:00 2001 From: Kevin Conley Date: Tue, 4 Jan 2022 06:33:01 +0000 Subject: [PATCH 2/6] Use protocols to fix importing in CPython --- adafruit_requests.py | 146 +++++++++++++++++++++++++++++++++---------- docs/conf.py | 2 +- requirements.txt | 1 + setup.py | 2 +- 4 files changed, 117 insertions(+), 34 deletions(-) diff --git a/adafruit_requests.py b/adafruit_requests.py index ee857b8..f7bfb94 100644 --- a/adafruit_requests.py +++ b/adafruit_requests.py @@ -37,35 +37,117 @@ __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Requests.git" import errno +import sys + +if sys.implementation.name == "circuitpython": + + def cast(_t, value): + """No-op shim for the typing.cast() function which is not available in CircuitPython.""" + return value + + +else: + from ssl import SSLContext + from types import ModuleType, TracebackType + from typing import Any, Dict, List, Optional, Tuple, Type, Union, cast + from typing_extensions import Protocol + + # Based on https://github.com/python/typeshed/blob/master/stdlib/_socket.pyi + class CommonSocketType(Protocol): + """Describes the common structure every socket type must have.""" + + def send(self, data: bytes, flags: int = ...) -> None: + """Send data to the socket. The meaning of the optional flags kwarg is + implementation-specific.""" + ... + + def settimeout(self, value: Optional[float]) -> None: + """Set a timeout on blocking socket operations.""" + ... + + def close(self) -> None: + """Close the socket.""" + ... + + class CommonCircuitPythonSocketType(CommonSocketType, Protocol): + """Describes the common structure every CircuitPython socket type must have.""" + + def connect( + self, + address: Tuple[str, int], + conntype: Optional[int] = ..., + ) -> None: + """Connect to a remote socket at the provided (host, port) address. The conntype + kwarg optionally may indicate SSL or not, depending on the underlying interface.""" + ... + + class LegacyCircuitPythonSocketType(CommonCircuitPythonSocketType, Protocol): + """Describes the structure a legacy CircuitPython socket type must have.""" + + def recv(self, bufsize: int = ...) -> bytes: + """Receive data from the socket. The return value is a bytes object representing + the data received. The maximum amount of data to be received at once is specified + by bufsize.""" + ... + + class SupportsRecvWithFlags(Protocol): + """Describes a type that posseses a socket recv() method supporting the flags kwarg.""" + + def recv(self, bufsize: int = ..., flags: int = ...) -> bytes: + """Receive data from the socket. The return value is a bytes object representing + the data received. The maximum amount of data to be received at once is specified + by bufsize. The meaning of the optional flags kwarg is implementation-specific.""" + ... + + class SupportsRecvInto(Protocol): + """Describes a type that possesses a socket recv_into() method.""" + + def recv_into( + self, buffer: bytearray, nbytes: int = ..., flags: int = ... + ) -> int: + """Receive up to nbytes bytes from the socket, storing the data into the provided + buffer. If nbytes is not specified (or 0), receive up to the size available in the + given buffer. The meaning of the optional flags kwarg is implementation-specific. + Returns the number of bytes received.""" + ... + + class CircuitPythonSocketType( + CommonCircuitPythonSocketType, + SupportsRecvInto, + SupportsRecvWithFlags, + Protocol, + ): # pylint: disable=too-many-ancestors + """Describes the structure every modern CircuitPython socket type must have.""" + + ... + + class StandardPythonSocketType( + CommonSocketType, SupportsRecvInto, SupportsRecvWithFlags, Protocol + ): + """Describes the structure every standard Python socket type must have.""" -try: - from typing import Union, TypeVar, Optional, Dict, Any, List, Type - import types - from types import TracebackType - import ssl - import adafruit_esp32spi.adafruit_esp32spi_socket as esp32_socket - import adafruit_wiznet5k.adafruit_wiznet5k_socket as wiznet_socket - import adafruit_fona.adafruit_fona_socket as cellular_socket - from adafruit_esp32spi.adafruit_esp32spi import ESP_SPIcontrol - from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K - from adafruit_fona.adafruit_fona import FONA - import socket as cpython_socket - - SocketType = TypeVar( - "SocketType", - esp32_socket.socket, - wiznet_socket.socket, - cellular_socket.socket, - cpython_socket.socket, - ) - SocketpoolModuleType = types.ModuleType - SSLContextType = ( - ssl.SSLContext - ) # Can use either CircuitPython or CPython ssl module - InterfaceType = TypeVar("InterfaceType", ESP_SPIcontrol, WIZNET5K, FONA) + def connect(self, address: Union[Tuple[Any, ...], str, bytes]) -> None: + """Connect to a remote socket at the provided address.""" + ... + + SocketType = Union[ + LegacyCircuitPythonSocketType, + CircuitPythonSocketType, + StandardPythonSocketType, + ] + + SocketpoolModuleType = ModuleType + + class InterfaceType(Protocol): + """Describes the structure every interface type must have.""" + + @property + def TLS_MODE(self) -> int: # pylint: disable=invalid-name + """Constant representing that a socket's connection mode is TLS.""" + ... + + SSLContextType = Union[SSLContext, "_FakeSSLContext"] -except ImportError: - pass # CircuitPython 6.0 does not have the bytearray.split method. # This function emulates buf.split(needle)[0], which is the functionality @@ -157,7 +239,7 @@ def _recv_into(self, buf: bytearray, size: int = 0) -> int: read_size = len(b) buf[:read_size] = b return read_size - return self.socket.recv_into(buf, size) + return cast("SupportsRecvInto", self.socket).recv_into(buf, size) @staticmethod def _find(buf: bytes, needle: bytes, start: int, end: int) -> int: @@ -440,7 +522,7 @@ def _free_sockets(self) -> None: def _get_socket( self, host: str, port: int, proto: str, *, timeout: float = 1 - ) -> SocketType: + ) -> CircuitPythonSocketType: # pylint: disable=too-many-branches key = (host, port, proto) if key in self._open_sockets: @@ -693,7 +775,7 @@ def delete(self, url: str, **kw) -> Response: class _FakeSSLSocket: - def __init__(self, socket: SocketType, tls_mode: int) -> None: + def __init__(self, socket: CircuitPythonSocketType, tls_mode: int) -> None: self._socket = socket self._mode = tls_mode self.settimeout = socket.settimeout @@ -701,7 +783,7 @@ def __init__(self, socket: SocketType, tls_mode: int) -> None: self.recv = socket.recv self.close = socket.close - def connect(self, address: Union[bytes, str]) -> None: + def connect(self, address: Tuple[str, int]) -> None: """connect wrapper to add non-standard mode parameter""" try: return self._socket.connect(address, self._mode) @@ -714,7 +796,7 @@ def __init__(self, iface: InterfaceType) -> None: self._iface = iface def wrap_socket( - self, socket: SocketType, server_hostname: Optional[str] = None + self, socket: CircuitPythonSocketType, server_hostname: Optional[str] = None ) -> _FakeSSLSocket: """Return the same socket""" # pylint: disable=unused-argument diff --git a/docs/conf.py b/docs/conf.py index 4a485bc..c0e0389 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -25,7 +25,7 @@ # Uncomment the below if you use native CircuitPython modules such as # digitalio, micropython and busio. List the modules you use. Without it, the # autodoc module docs will fail to generate with a warning. -autodoc_mock_imports = ["adafruit_esp32spi", "adafruit_wiznet5k", "adafruit_fona"] +# autodoc_mock_imports = ["digitalio", "busio"] intersphinx_mapping = { diff --git a/requirements.txt b/requirements.txt index 17a850d..aab6f10 100755 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ # SPDX-License-Identifier: Unlicense Adafruit-Blinka +typing-extensions diff --git a/setup.py b/setup.py index d92f376..a947d99 100755 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ # Author details author="Adafruit Industries", author_email="circuitpython@adafruit.com", - install_requires=["Adafruit-Blinka"], + install_requires=["Adafruit-Blinka", "typing-extensions"], # Choose your license license="MIT", # See https://pypi.python.org/pypi?%3Aaction=list_classifiers From 9741e141bdfbfa1ff430d79eeef9f67620a05ca0 Mon Sep 17 00:00:00 2001 From: Kevin Conley Date: Wed, 5 Jan 2022 05:39:30 +0000 Subject: [PATCH 3/6] Run tests in CI --- .github/workflows/build.yml | 130 +++++++++++++++++++----------------- .gitignore | 1 + tox.ini | 11 +++ 3 files changed, 79 insertions(+), 63 deletions(-) create mode 100644 tox.ini diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ca35544..4c2db77 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,66 +10,70 @@ jobs: test: runs-on: ubuntu-latest steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - name: Translate Repo Name For Build Tools filename_prefix - id: repo-name - run: | - echo ::set-output name=repo-name::$( - echo ${{ github.repository }} | - awk -F '\/' '{ print tolower($2) }' | - tr '_' '-' - ) - - name: Set up Python 3.7 - uses: actions/setup-python@v1 - with: - python-version: 3.7 - - name: Versions - run: | - python3 --version - - name: Checkout Current Repo - uses: actions/checkout@v1 - with: - submodules: true - - name: Checkout tools repo - uses: actions/checkout@v2 - with: - repository: adafruit/actions-ci-circuitpython-libs - path: actions-ci - - name: Install dependencies - # (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.) - run: | - source actions-ci/install.sh - - name: Pip install Sphinx, pre-commit - run: | - pip install --force-reinstall Sphinx sphinx-rtd-theme pre-commit - - name: Library version - run: git describe --dirty --always --tags - - name: Pre-commit hooks - run: | - pre-commit run --all-files - - name: Build assets - run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location . - - name: Archive bundles - uses: actions/upload-artifact@v2 - with: - name: bundles - path: ${{ github.workspace }}/bundles/ - - name: Build docs - working-directory: docs - run: sphinx-build -E -W -b html . _build/html - - name: Check For setup.py - id: need-pypi - run: | - echo ::set-output name=setup-py::$( find . -wholename './setup.py' ) - - name: Build Python package - if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') - run: | - pip install --upgrade setuptools wheel twine readme_renderer testresources - python setup.py sdist - python setup.py bdist_wheel --universal - twine check dist/* - - name: Setup problem matchers - uses: adafruit/circuitpython-action-library-ci-problem-matchers@v1 + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" + - name: Translate Repo Name For Build Tools filename_prefix + id: repo-name + run: | + echo ::set-output name=repo-name::$( + echo ${{ github.repository }} | + awk -F '\/' '{ print tolower($2) }' | + tr '_' '-' + ) + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Versions + run: | + python3 --version + - name: Checkout Current Repo + uses: actions/checkout@v1 + with: + submodules: true + - name: Checkout tools repo + uses: actions/checkout@v2 + with: + repository: adafruit/actions-ci-circuitpython-libs + path: actions-ci + - name: Install dependencies + # (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.) + run: | + source actions-ci/install.sh + - name: Pip install Sphinx, pre-commit + run: | + pip install --force-reinstall Sphinx sphinx-rtd-theme pre-commit + - name: Library version + run: git describe --dirty --always --tags + - name: Pre-commit hooks + run: | + pre-commit run --all-files + - name: Build assets + run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location . + - name: Archive bundles + uses: actions/upload-artifact@v2 + with: + name: bundles + path: ${{ github.workspace }}/bundles/ + - name: Build docs + working-directory: docs + run: sphinx-build -E -W -b html . _build/html + - name: Check For setup.py + id: need-pypi + run: | + echo ::set-output name=setup-py::$( find . -wholename './setup.py' ) + - name: Build Python package + if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') + run: | + pip install --upgrade setuptools wheel twine readme_renderer testresources + python setup.py sdist + python setup.py bdist_wheel --universal + twine check dist/* + - name: Test Python package + run: | + pip install tox==3.24.5 + tox + - name: Setup problem matchers + uses: adafruit/circuitpython-action-library-ci-problem-matchers@v1 diff --git a/.gitignore b/.gitignore index 39dd71b..bbe230d 100755 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ __pycache__ _build *.pyc .env +.tox bundles *.DS_Store .eggs diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..132d27e --- /dev/null +++ b/tox.ini @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2022 Kevin Conley +# +# SPDX-License-Identifier: MIT + +[tox] +envlist = py37 + +[testenv] +changedir = {toxinidir}/tests +deps = pytest==6.2.5 +commands = pytest From 91ce3262ac63c5530c51afcd55a4e0203e34c625 Mon Sep 17 00:00:00 2001 From: Kevin Conley Date: Thu, 6 Jan 2022 05:49:37 +0000 Subject: [PATCH 4/6] Revert whitespace changes to build.yml --- .github/workflows/build.yml | 134 ++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4c2db77..1e7f254 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,70 +10,70 @@ jobs: test: runs-on: ubuntu-latest steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - name: Translate Repo Name For Build Tools filename_prefix - id: repo-name - run: | - echo ::set-output name=repo-name::$( - echo ${{ github.repository }} | - awk -F '\/' '{ print tolower($2) }' | - tr '_' '-' - ) - - name: Set up Python 3.7 - uses: actions/setup-python@v1 - with: - python-version: 3.7 - - name: Versions - run: | - python3 --version - - name: Checkout Current Repo - uses: actions/checkout@v1 - with: - submodules: true - - name: Checkout tools repo - uses: actions/checkout@v2 - with: - repository: adafruit/actions-ci-circuitpython-libs - path: actions-ci - - name: Install dependencies - # (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.) - run: | - source actions-ci/install.sh - - name: Pip install Sphinx, pre-commit - run: | - pip install --force-reinstall Sphinx sphinx-rtd-theme pre-commit - - name: Library version - run: git describe --dirty --always --tags - - name: Pre-commit hooks - run: | - pre-commit run --all-files - - name: Build assets - run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location . - - name: Archive bundles - uses: actions/upload-artifact@v2 - with: - name: bundles - path: ${{ github.workspace }}/bundles/ - - name: Build docs - working-directory: docs - run: sphinx-build -E -W -b html . _build/html - - name: Check For setup.py - id: need-pypi - run: | - echo ::set-output name=setup-py::$( find . -wholename './setup.py' ) - - name: Build Python package - if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') - run: | - pip install --upgrade setuptools wheel twine readme_renderer testresources - python setup.py sdist - python setup.py bdist_wheel --universal - twine check dist/* - - name: Test Python package - run: | - pip install tox==3.24.5 - tox - - name: Setup problem matchers - uses: adafruit/circuitpython-action-library-ci-problem-matchers@v1 + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" + - name: Translate Repo Name For Build Tools filename_prefix + id: repo-name + run: | + echo ::set-output name=repo-name::$( + echo ${{ github.repository }} | + awk -F '\/' '{ print tolower($2) }' | + tr '_' '-' + ) + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Versions + run: | + python3 --version + - name: Checkout Current Repo + uses: actions/checkout@v1 + with: + submodules: true + - name: Checkout tools repo + uses: actions/checkout@v2 + with: + repository: adafruit/actions-ci-circuitpython-libs + path: actions-ci + - name: Install dependencies + # (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.) + run: | + source actions-ci/install.sh + - name: Pip install Sphinx, pre-commit + run: | + pip install --force-reinstall Sphinx sphinx-rtd-theme pre-commit + - name: Library version + run: git describe --dirty --always --tags + - name: Pre-commit hooks + run: | + pre-commit run --all-files + - name: Build assets + run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location . + - name: Archive bundles + uses: actions/upload-artifact@v2 + with: + name: bundles + path: ${{ github.workspace }}/bundles/ + - name: Build docs + working-directory: docs + run: sphinx-build -E -W -b html . _build/html + - name: Check For setup.py + id: need-pypi + run: | + echo ::set-output name=setup-py::$( find . -wholename './setup.py' ) + - name: Build Python package + if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') + run: | + pip install --upgrade setuptools wheel twine readme_renderer testresources + python setup.py sdist + python setup.py bdist_wheel --universal + twine check dist/* + - name: Test Python package + run: | + pip install tox==3.24.5 + tox + - name: Setup problem matchers + uses: adafruit/circuitpython-action-library-ci-problem-matchers@v1 From c63a0bcf96cc79978ac73a358bf287b17489eab5 Mon Sep 17 00:00:00 2001 From: Kevin Conley Date: Tue, 11 Jan 2022 05:12:08 +0000 Subject: [PATCH 5/6] Update repo to require Python >=3.8 --- .github/workflows/build.yml | 4 ++-- .github/workflows/release.yml | 2 +- .readthedocs.yaml | 2 +- setup.py | 4 ++-- tox.ini | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1e7f254..67ecf47 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,10 +22,10 @@ jobs: awk -F '\/' '{ print tolower($2) }' | tr '_' '-' ) - - name: Set up Python 3.7 + - name: Set up Python 3.8 uses: actions/setup-python@v1 with: - python-version: 3.7 + python-version: 3.8 - name: Versions run: | python3 --version diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6d0015a..1421ff9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,7 +69,7 @@ jobs: if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') uses: actions/setup-python@v1 with: - python-version: '3.x' + python-version: '3.8' - name: Install dependencies if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') run: | diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 1335112..a2f6d2d 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -9,7 +9,7 @@ version: 2 python: - version: "3.7" + version: "3.8" install: - requirements: docs/requirements.txt - requirements: requirements.txt diff --git a/setup.py b/setup.py index a947d99..29b3cc0 100755 --- a/setup.py +++ b/setup.py @@ -33,6 +33,7 @@ # Author details author="Adafruit Industries", author_email="circuitpython@adafruit.com", + python_requires=">=3.8", install_requires=["Adafruit-Blinka", "typing-extensions"], # Choose your license license="MIT", @@ -44,8 +45,7 @@ "Topic :: System :: Hardware", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.8", ], # What does your project relate to? keywords="adafruit blinka circuitpython micropython requests requests, networking", diff --git a/tox.ini b/tox.ini index 132d27e..ab2df5e 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,7 @@ # SPDX-License-Identifier: MIT [tox] -envlist = py37 +envlist = py38 [testenv] changedir = {toxinidir}/tests From c93388f85e9d41343b3d932989815aa1c4c30973 Mon Sep 17 00:00:00 2001 From: Kevin Conley Date: Tue, 11 Jan 2022 05:14:14 +0000 Subject: [PATCH 6/6] Remove dependency on typing-extensions --- adafruit_requests.py | 3 +-- requirements.txt | 1 - setup.py | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/adafruit_requests.py b/adafruit_requests.py index f7bfb94..6ec2542 100644 --- a/adafruit_requests.py +++ b/adafruit_requests.py @@ -49,8 +49,7 @@ def cast(_t, value): else: from ssl import SSLContext from types import ModuleType, TracebackType - from typing import Any, Dict, List, Optional, Tuple, Type, Union, cast - from typing_extensions import Protocol + from typing import Any, Dict, List, Optional, Protocol, Tuple, Type, Union, cast # Based on https://github.com/python/typeshed/blob/master/stdlib/_socket.pyi class CommonSocketType(Protocol): diff --git a/requirements.txt b/requirements.txt index aab6f10..17a850d 100755 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,3 @@ # SPDX-License-Identifier: Unlicense Adafruit-Blinka -typing-extensions diff --git a/setup.py b/setup.py index 29b3cc0..c18e1ce 100755 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ author="Adafruit Industries", author_email="circuitpython@adafruit.com", python_requires=">=3.8", - install_requires=["Adafruit-Blinka", "typing-extensions"], + install_requires=["Adafruit-Blinka"], # Choose your license license="MIT", # See https://pypi.python.org/pypi?%3Aaction=list_classifiers