diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c407e7a..6186cb2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,5 +33,5 @@ jobs: - name: Run tests run: | PYTHON_VERSION=${{ matrix.python-version }} - TOX_ENV="py${PYTHON_VERSION//./}" + TOX_ENV=`tox --listenvs | grep "py${PYTHON_VERSION//./}-" | tr '\n' ','` tox -e $TOX_ENV diff --git a/docs/requirements.txt b/docs/requirements.txt index 4a1dd95..3a5c45a 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ -flask>=2.0.0 +flask>=2.2.0 sphinx==7.1.2 sphinx-notfound-page sphinx-rtd-theme==1.3.0rc1 diff --git a/docs/source/api.rst b/docs/source/api.rst index 9385024..19dc004 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -27,6 +27,12 @@ Decorators .. automodule:: flask_utils.decorators :members: +Utilities +--------- + +.. automodule:: flask_utils.utils + :members: + Private API ---------------------- diff --git a/flask_utils/__init__.py b/flask_utils/__init__.py index 4ecf9f6..146405d 100644 --- a/flask_utils/__init__.py +++ b/flask_utils/__init__.py @@ -1,5 +1,5 @@ # Increment versions here according to SemVer -__version__ = "0.3.0" +__version__ = "0.4.0" from flask_utils.errors import ConflictError from flask_utils.errors import ForbiddenError @@ -15,6 +15,7 @@ from flask_utils.decorators import validate_params +from flask_utils.utils import is_it_true from flask_utils.errors import register_error_handlers @@ -32,4 +33,5 @@ "UnprocessableEntityError", "ServiceUnavailableError", "validate_params", + "is_it_true", ] diff --git a/flask_utils/utils.py b/flask_utils/utils.py new file mode 100644 index 0000000..f8d3120 --- /dev/null +++ b/flask_utils/utils.py @@ -0,0 +1,36 @@ +def is_it_true(value: str) -> bool: + """ + This function checks if a string value is true. + Useful for flask's request.form.get() method and request.args.get() method + + :param value: String value to check if it is true + :return: True if value is true, False otherwise + + :Example: + + .. code-block:: python + + from flask_utils import is_it_true + + @app.route('/example', methods=['GET']) + def example(): + is_ordered = request.args.get('is_ordered', type=is_it_true, default=False) + + This allows your API to accept these kind of requests: + + .. code-block:: python + + import requests + + response = requests.get('http://localhost:5000/example?is_ordered=true') + print(response.json()) # True + + response = requests.get('http://localhost:5000/example?is_ordered=1') + print(response.json()) # True + + response = requests.get('http://localhost:5000/example?is_ordered=yes') + print(response.json()) # True + + .. versionadded:: 0.4.0 + """ + return value.lower() in ("true", "1", "yes") diff --git a/requirements.txt b/requirements.txt index 37f779e..0e98085 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -flask>=2.0.0 +flask>=2.2.0 diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..bbad630 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,76 @@ +import pytest +from flask import jsonify +from flask import request + +from flask_utils import is_it_true + + +class TestIsItTrueFunction: + def test_string_true(self): + assert is_it_true("true") is True + + def test_string_true_uppercase(self): + assert is_it_true("TRUE") is True + + def test_string_true_yes(self): + assert is_it_true("yes") is True + + def test_string_true_yes_uppercase(self): + assert is_it_true("YES") is True + + def test_string_true_one(self): + assert is_it_true("1") is True + + def test_string_false(self): + assert is_it_true("false") is False + + def test_string_false_no(self): + assert is_it_true("no") is False + + def test_string_false_zero(self): + assert is_it_true("0") is False + + +class TestIsItTrueInFlask: + @pytest.fixture(autouse=True) + def setup(self, flask_client): + @flask_client.route("/example", methods=["GET"]) + def example(): + is_ordered = request.args.get("is_ordered", type=is_it_true, default=False) + return jsonify(is_ordered) + + def test_is_ordered_true(self, client): + response = client.get("example?is_ordered=true") + assert response.json is True + + def test_is_ordered_true_uppercase(self, client): + response = client.get("example?is_ordered=TRUE") + assert response.json is True + + def test_is_ordered_true_yes(self, client): + response = client.get("example?is_ordered=yes") + assert response.json is True + + def test_is_ordered_true_yes_uppercase(self, client): + response = client.get("example?is_ordered=YES") + assert response.json is True + + def test_is_ordered_true_one(self, client): + response = client.get("example?is_ordered=1") + assert response.json is True + + def test_is_ordered_false(self, client): + response = client.get("example?is_ordered=false") + assert response.json is False + + def test_is_ordered_false_no(self, client): + response = client.get("example?is_ordered=no") + assert response.json is False + + def test_is_ordered_false_zero(self, client): + response = client.get("example?is_ordered=0") + assert response.json is False + + def test_is_ordered_default(self, client): + response = client.get("example") + assert response.json is False diff --git a/tox.ini b/tox.ini index f36d49e..c47dc9b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,9 @@ [tox] -envlist = py{38,39,310,311,312}-flask{20,21,22,23,30,latest} +envlist = py{38,39,310,311,312}-flask{22,23,30,latest} [testenv] deps = pytest - flask20: Flask==2.* - flask21: Flask==2.1.* flask22: Flask==2.2.* flask23: Flask==2.3.* flask30: Flask==3.*