diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 957aaa51..573c684e 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -5,4 +5,4 @@ about: Pose an inquiry --- -- -**Important:** Before creating an issue, please consider posting your question to our [Gitter Lobby](https://gitter.im/python-uplink/Lobby) first, especially if you suspect that the underlying problem is not a bug nor a feature request. +**Important:** Before creating an issue, please consider posting your question to our [GitHub Discussions](https://github.com/prkumar/uplink/discussions) page first, especially if you suspect that the underlying problem is not a bug nor a feature request. diff --git a/.gitignore b/.gitignore index 3561cc15..af79fb2d 100644 --- a/.gitignore +++ b/.gitignore @@ -118,3 +118,6 @@ ENV/ # [2]: https://docs.pipenv.org/advanced/#pipfile-vs-setup-py # Pipfile.lock + +# macOS +.DS_Store diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 74955b82..4ab905d7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,14 +1,14 @@ repos: - repo: https://github.com/ambv/black - rev: 18.6b4 + rev: 21.12b0 hooks: - id: black - python-version: python3.6 + python-version: python3.8 files: ^((uplink|examples|tests)\/.+|setup|docs\/conf)\.py$ -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v1.3.0 +- repo: https://github.com/pycqa/flake8 + rev: '4.0.1' hooks: - id: flake8 name: flake8 - python-version: python3.6 + python-version: python3.8 files: ^((uplink|examples|tests)\/.+|setup|docs\/conf)\.py$ diff --git a/.travis.yml b/.travis.yml index 300a997c..1a6d1f62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,13 @@ language: python +dist: bionic # Ubuntu 18.04 python: + - '2.7' - '3.5' - '3.6' - '3.7' - '3.8' + - '3.9' + - '3.10.1' before_script: - pip install tox - if [[ $TRAVIS_PYTHON_VERSION == 3.6 ]]; then pip install flake8 flake8-bugbear; fi diff --git a/AUTHORS.rst b/AUTHORS.rst index de74a9f5..2a403eb5 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -16,3 +16,4 @@ Contributors - Sakorn Waungwiwatsin (`@SakornW `_) - Jacob Floyd (`@cognifloyd `_) - Guilherme Crocetti (`@gmcrocetti `_) +- Alexander Shadchin (`@shadchin `_) diff --git a/README.rst b/README.rst index 114bfe40..a8dbfd0c 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ Uplink ****** |PyPI Version| |Build Status| |Coverage Status| |Code Climate| |Documentation Status| -|Gitter| |Code Style| +|GitHub Discussions| |Gitter| |Code Style| - Builds Reusable Objects for Consuming Web APIs. - Works with **Requests**, **aiohttp**, and **Twisted**. @@ -169,9 +169,14 @@ For new users, a good place to start is this `quick tutorial`_. Community ========= -Join the conversation on `Gitter`_ to ask questions, provide feedback, +Use the `Discussions`_ tab on GitHub to join the conversation! Ask questions, provide feedback, and meet other users! +We're migrating our community from `Gitter`_ to GitHub `Discussions`_. Feel free to search our +Gitter lobby for past questions and answers. However, to help us transition, please start new +threads/posts in GitHub Discussions instead of Gitter. + +.. _Discussions: https://github.com/prkumar/uplink/discussions .. _Gitter: https://gitter.im/python-uplink/Lobby @@ -182,6 +187,8 @@ Want to report a bug, request a feature, or contribute code to Uplink? Checkout the `Contribution Guide`_ for where to start. Thank you for taking the time to improve an open source project :purple_heart: +.. |GitHub Discussions| image:: https://img.shields.io/github/discussions/prkumar/uplink.png + :target: https://github.com/prkumar/uplink/discussions .. |Build Status| image:: https://travis-ci.com/prkumar/uplink.svg?branch=master :target: https://travis-ci.com/prkumar/uplink .. |Code Climate| image:: https://api.codeclimate.com/v1/badges/d5c5666134763ff1d6c0/maintainability diff --git a/setup.py b/setup.py index 911a033c..9e48a8ab 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ def read(filename): # Twisted 19.7.0 dropped py3.4 support "twisted:python_version == '3.4'": "twisted<=19.2.1", "typing": ["typing>=3.6.4"], - "tests": ["pytest==4.6.5", "pytest-mock", "pytest-cov", "pytest-twisted"], + "tests": ["pytest", "pytest-mock", "pytest-cov", "pytest-twisted"], } metadata = { diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py index f79cf950..82eafa60 100644 --- a/tests/integration/__init__.py +++ b/tests/integration/__init__.py @@ -106,6 +106,10 @@ def headers(self): def data(self): return self._extras.get("data", None) + @property + def files(self): + return self._extras.get("files", None) + @property def json(self): return self._extras.get("json", None) diff --git a/tests/integration/test_form_url_encoded.py b/tests/integration/test_form_url_encoded.py new file mode 100644 index 00000000..073ef41e --- /dev/null +++ b/tests/integration/test_form_url_encoded.py @@ -0,0 +1,23 @@ +# Local imports +from uplink import Consumer, form_url_encoded, put, FieldMap + +# Constants +BASE_URL = "https://example.com/" + + +def test_without_converter(mock_response, mock_client): + class Calendar(Consumer): + @form_url_encoded + @put("/user/repos", args={"event_data": FieldMap}) + def add_event(self, **event_data): + pass + + mock_client.with_response(mock_response) + calendar = Calendar(base_url=BASE_URL, client=mock_client) + + # Run + calendar.add_event(name="Weekly Stand-up", public=True) + + # Assertions: should not convert if converter is None + request = mock_client.history[0] + assert request.data == {"name": "Weekly Stand-up", "public": True} diff --git a/tests/integration/test_multipart.py b/tests/integration/test_multipart.py new file mode 100644 index 00000000..3c208c72 --- /dev/null +++ b/tests/integration/test_multipart.py @@ -0,0 +1,24 @@ +# Local imports +from uplink import Consumer, PartMap, post, multipart + +# Constants +BASE_URL = "https://example.com/" + + +def test_without_converter(mock_response, mock_client): + class Calendar(Consumer): + @multipart + @post("/attachments", args={"files": PartMap}) + def upload_attachments(self, **files): + pass + + mock_client.with_response(mock_response) + calendar = Calendar(base_url=BASE_URL, client=mock_client) + file = object() + + # Run + calendar.upload_attachments(file=file) + + # Assertion: should not convert if converter is None + request = mock_client.history[0] + assert request.files == {"file": file} diff --git a/tests/unit/test_auth.py b/tests/unit/test_auth.py index 4ae4db7f..ab9b1fa5 100644 --- a/tests/unit/test_auth.py +++ b/tests/unit/test_auth.py @@ -17,7 +17,7 @@ def test_tuple(self): def test_callable(self): def func(): - None + pass output = auth.get_auth(func) assert output is func diff --git a/tox.ini b/tox.ini index 19b1c350..d19f6cba 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,9 @@ # Website (June 2016): http://tox.readthedocs.io/en/latest/ [testenv] -deps = pipenv +deps = + pipenv == 2018.11.26 ; python_version == '2.7' + pipenv ; python_version > '2.7' commands = pipenv install --skip-lock pipenv run py.test tests \ --cov-config .coveragerc \ diff --git a/uplink/arguments.py b/uplink/arguments.py index 9503c1c3..82375656 100644 --- a/uplink/arguments.py +++ b/uplink/arguments.py @@ -4,12 +4,12 @@ """ # Standard library imports import collections -from collections import abc import functools import inspect # Local imports from uplink import exceptions, hooks, interfaces, utils +from uplink.compat import abc from uplink.converters import keys __all__ = [ diff --git a/uplink/auth.py b/uplink/auth.py index 3334b72d..9ba53f29 100644 --- a/uplink/auth.py +++ b/uplink/auth.py @@ -1,13 +1,11 @@ """This module implements the auth layer.""" -# Standard library imports -from collections import abc - # Third-party imports from requests import auth # Local imports from uplink import utils +from uplink.compat import abc __all__ = [ "ApiTokenParam", diff --git a/uplink/clients/io/interfaces.py b/uplink/clients/io/interfaces.py index ff810824..d0e4d31b 100644 --- a/uplink/clients/io/interfaces.py +++ b/uplink/clients/io/interfaces.py @@ -1,6 +1,3 @@ -# Standard library imports -from collections import abc - # Local imports from uplink import compat @@ -69,7 +66,7 @@ def on_failure(self, exc_type, exc_val, exc_tb): raise NotImplementedError -class Executable(abc.Iterator): +class Executable(compat.abc.Iterator): """An abstraction for iterating over the execution of a request.""" def __next__(self): diff --git a/uplink/commands.py b/uplink/commands.py index 6805412d..40200391 100644 --- a/uplink/commands.py +++ b/uplink/commands.py @@ -1,5 +1,4 @@ # Standard library imports -from collections import abc import functools # Local imports @@ -12,6 +11,7 @@ returns, utils, ) +from uplink.compat import abc __all__ = ["get", "head", "put", "post", "patch", "delete"] diff --git a/uplink/compat.py b/uplink/compat.py index d0ac368b..6b6ac98d 100644 --- a/uplink/compat.py +++ b/uplink/compat.py @@ -1,6 +1,7 @@ # Third-party imports import six -__all__ = ["reraise"] +__all__ = ["abc", "reraise"] +abc = six.moves.collections_abc reraise = six.reraise diff --git a/uplink/converters/__init__.py b/uplink/converters/__init__.py index fdc2fcf5..ebc73b1c 100644 --- a/uplink/converters/__init__.py +++ b/uplink/converters/__init__.py @@ -1,8 +1,6 @@ -# Standard library imports -from collections import abc - # Local imports from uplink._extras import installer, plugin +from uplink.compat import abc from uplink.converters import keys from uplink.converters.interfaces import Factory, ConverterFactory, Converter from uplink.converters.register import ( diff --git a/uplink/converters/keys.py b/uplink/converters/keys.py index c9142372..2ca40d82 100644 --- a/uplink/converters/keys.py +++ b/uplink/converters/keys.py @@ -50,6 +50,8 @@ def __call__(self, converter_registry): def factory_wrapper(*args, **kwargs): converter = factory(*args, **kwargs) + if not converter: + return None return functools.partial(self.convert, converter) return factory_wrapper diff --git a/uplink/converters/typing_.py b/uplink/converters/typing_.py index 1c42ffd5..9f5f8f98 100644 --- a/uplink/converters/typing_.py +++ b/uplink/converters/typing_.py @@ -1,9 +1,9 @@ # Standard library imports import collections -from collections import abc import functools # Local imports +from uplink.compat import abc from uplink.converters import interfaces, register_default_converter_factory __all__ = ["TypingConverter", "ListConverter", "DictConverter"] diff --git a/uplink/decorators.py b/uplink/decorators.py index d0bfe6c0..bd32cf04 100644 --- a/uplink/decorators.py +++ b/uplink/decorators.py @@ -3,12 +3,12 @@ handling classes. """ # Standard library imports -from collections import abc import functools import inspect # Local imports from uplink import arguments, helpers, hooks, interfaces, utils +from uplink.compat import abc __all__ = [ "headers",