diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 0000000000000000000000000000000000000000..62b61057e8914c1b737cff0b15dae5c4750f8795 --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,33 @@ +engines: + duplication: + enabled: true + config: + languages: + python: + + fixme: + enabled: true + markdownlint: + enabled: true + pep8: + enabled: true +exclude_paths: + - tests/** + - setup.py +ratings: + paths: + - flask_minijson.py +checks: + argument-count: + config: + threshold: 15 + method-complexity: + config: + threshold: 50 + method-count: + config: + threshold: 85 + file-lines: + enabled: true + config: + threshold: 700 diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000000000000000000000000000000000000..c9f2f50eae8000b5249e50b5a7d183e792bdf430 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,8 @@ +[run] +branch=1 +source= + rapid_minijson + +[report] +exclude_lines= + pragma: no cover diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..f999d70488e33bd38883bbee160632136629a394 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,53 @@ +language: python +stages: + - name: test + +cache: pip +before_script: + - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter + - chmod +x ./cc-test-reporter + - ./cc-test-reporter before-build + - pip install -r requirements.txt + - pip install -U pytest-xdist pytest-cov pytest pytest-forked pluggy py + - python setup.py install +jobs: + include: + - stage: test + python: "3.5" + script: + - pytest -n 8 --cov=flask_minijson + after_script: + - coverage xml + - ./cc-test-reporter after-build -t coverage.py --exit-code $TRAVIS_TEST_RESULT + - stage: test + python: "3.6" + script: + - pytest -n 8 --cov=flask_minijson + after_script: + - coverage xml + - ./cc-test-reporter after-build -t coverage.py --exit-code $TRAVIS_TEST_RESULT + - stage: test + python: "3.7" + script: + - pytest -n 8 --cov=flask_minijson + after_script: + - coverage xml + - ./cc-test-reporter after-build -t coverage.py --exit-code $TRAVIS_TEST_RESULT + - stage: test + python: "3.8" + script: + - pytest -n 8 --cov=flask_minijson + after_script: + - coverage xml + - ./cc-test-reporter after-build -t coverage.py --exit-code $TRAVIS_TEST_RESULT + - stage: test + python: "3.9" + script: + - pytest -n 8 --cov=flask_minijson + after_script: + - coverage xml + - ./cc-test-reporter after-build -t coverage.py --exit-code $TRAVIS_TEST_RESULT + - stage: test + python: "pypy3.5" + script: + - pytest -n 8 diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000000000000000000000000000000000000..f9bd1455b374de796e12d240c1211dee9829d97e --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +include requirements.txt diff --git a/README.md b/README.md index 370b2b4bead26024d3f7bc5b58b73e055c110ced..82ef322952cb29e793fc90a6b6ae0342cb726570 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,12 @@ # flask-minijson An extension for Flask to allow clients to submit data using the application/minijson codec + +flask-json is required to be initialized before `FlaskMiniJSON`, in such a way: + +```python +from flask_minijson import FlaskMiniJSON + +app = Flask(__name__) +FlaskJSON(app) +FlaskMiniJSON(app) +``` diff --git a/flask_minijson.py b/flask_minijson.py new file mode 100644 index 0000000000000000000000000000000000000000..5bdc93d411fb64d9d070ae4c9ddef97a8c45b2ae --- /dev/null +++ b/flask_minijson.py @@ -0,0 +1,35 @@ +import typing as tp + +import minijson +from flask import Flask +from flask_json import JsonRequest + + +class MiniJSONRequest(JsonRequest): + def get_json(self, force=False, silent=False, cache=True): + """ + Return JSON data, if content type is application/minijson it will be loaded + via minijson, else it will be loaded the normal way. + """ + if self.headers['Content-Type'] == 'application/minijson': + return minijson.loads(self.get_data()) + else: + return super().get_json(force, silent, cache) + + +class FlaskMiniJSON(object): + """Flask-MiniJSON extension class.""" + def __init__(self, app: tp.Optional[Flask] = None): + self._app = app + self._error_handler_func = None + self._decoder_error_func = None + if app is not None: + self.init_app(app) + + def init_app(self, app: Flask): + if 'json' not in app.extensions: + raise RuntimeError('flask-json must be initialized before MiniJSON!') + app.extensions['minijson'] = self + + self._app = app + app.request_class = MiniJSONRequest diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..cf312031d81c1289f8b2108817f52e80ccd9e770 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +flask +flask-json +minijson diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000000000000000000000000000000000000..349e4946142e2aa8a630af798abb34c4c331120a --- /dev/null +++ b/setup.cfg @@ -0,0 +1,41 @@ +# coding: utf-8 +[metadata] +version = 2.5a1 +name = flask-minijson +long_description = file: README.md +long_description_content_type = text/markdown; charset=UTF-8 +author = Piotr Maślanka +license_files = + LICENSE +author_email = piotr.maslanka@dronehub.ai +description = A Flask extension to allow client's to submit data using the MiniJSON codec +url = https://github.com/Dronehub/flask-minijson +project_urls = + Code = https://github.com/Dronehub/flask-minijson + Issue tracker = https://github.com/Dronehub/flask-minijson/issues +classifier = + Development Status :: 4 - Beta + Programming Language :: Python + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: Implementation :: CPython + Programming Language :: Python :: Implementation :: PyPy + Operating System :: OS Independent + License :: OSI Approved :: MIT License + Topic :: Software Development :: Libraries + +[pycodestyle] +max-line-length = 100 + +[pep8] +max-line-length = 100 + +[bdist_wheel] +universal = 0 + +[options] +python_requires = !=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.* +py_modules = flask_minijson diff --git a/setup.py b/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..606849326a4002007fd42060b51e69a19c18675c --- /dev/null +++ b/setup.py @@ -0,0 +1,3 @@ +from setuptools import setup + +setup() diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/app.py b/tests/app.py new file mode 100644 index 0000000000000000000000000000000000000000..4ca3ceb9aead517a68de1bce2b37f843b54ddfce --- /dev/null +++ b/tests/app.py @@ -0,0 +1,19 @@ +from flask import Flask, request +from flask_json import FlaskJSON, as_json +from flask_minijson import FlaskMiniJSON + +app = Flask(__name__) +FlaskJSON(app) +FlaskMiniJSON(app) + +app.config['DEBUG'] = True +app.config['TESTING'] = True + + +@app.route('/v1', methods=['POST']) +@as_json +def example_point(): + if request.get_json() == {'1': '2'}: + return {'status': 'ok'}, + else: + return {'status': 'fail'} diff --git a/tests/test_minijson.py b/tests/test_minijson.py new file mode 100644 index 0000000000000000000000000000000000000000..bdbfe4ffdc8f759585a63b5fabce0ec5104d0813 --- /dev/null +++ b/tests/test_minijson.py @@ -0,0 +1,17 @@ +import unittest + +import minijson + +from tests.app import app + + +class TestMiniJSON(unittest.TestCase): + def setUp(self): + self.client = app.test_client() + + def test_minijson(self): + data = minijson.dumps({'1': '2'}) + resp = self.client.post('/v1', data=data, headers={'Content-Type': 'application/minijson'}) + self.assertEqual(resp.get_json(), {'status': 'ok'}) + resp = self.client.post('/v1', json={'1': '3'}) + self.assertEqual(resp.get_json(), {'status': 'fail'})