diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000..64425f1 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,22 @@ +environment: + matrix: + - PYTHON: C:\Python37-x64 + - PYTHON: C:\Python36-x64 + - PYTHON: C:\Python35-x64 + - PYTHON: C:\Python34-x64 + - PYTHON: C:\Python27-x64 + +init: + - SET PATH=%PYTHON%;%PATH% + +install: + - python -m pip install -r requirements-dev.txt + - python setup.py develop + +build: false + +test_script: + - python -m pytest + +cache: + - '%LOCALAPPDATA%\pip\Cache' diff --git a/.ci/test_servers.py b/.ci/test_servers.py new file mode 100644 index 0000000..84081b3 --- /dev/null +++ b/.ci/test_servers.py @@ -0,0 +1,60 @@ +"""We want to ensure that users can run Texrrow through UWSGI without issues. +This script is ran by Travis and AppVeyor to ensure the compatibility. + +This is a heavy test and incompatible test with pytest. +Thus we keep it separated from the rest of the tests.""" +import os +import signal +import sys +import time + +import mock +import requests +import requests.exceptions + +from texrrow import __main__ as main + +TEST_PORT = 12345 + + +def _knock_url_and_wait(url, timeout_per_request=1, global_timeout=10): + status = None + end_time = time.time() + global_timeout + while status is None: + try: + response = requests.head(url, timeout=timeout_per_request) + status = response.status_code + except requests.exceptions.ConnectionError: # noqa + if time.time() > end_time: + raise + assert status == 200 + + +def _run_and_test(to_run): + child_pid = os.fork() + if child_pid == 0: + to_run() + sys.exit(0) + try: + test_url = 'http://127.0.0.1:%d' % TEST_PORT + time.sleep(0.1) + pid, status = os.waitpid(child_pid, os.P_NOWAIT) + + # ensure the process did not exit + assert not os.WEXITSTATUS(pid) + + # try to ping uwsgi and get a HTTP 200 + _knock_url_and_wait(test_url) + finally: + os.kill(child_pid, signal.SIGTERM) + os.wait() + + +@mock.patch.object(main, 'PORT', new=TEST_PORT) +def test_start_servers(): + _run_and_test(main.start_uwsgi) + _run_and_test(main.start_flask) + + +if __name__ == '__main__': + test_start_servers() diff --git a/.travis.yml b/.travis.yml index 099515e..8e6e9c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,19 +5,19 @@ cache: - node_modules sudo: false python: + - "2.7" + - "3.4" - "3.5" - "3.6" + - "pypy" - "pypy3" -services: - - postgresql -env: - global: - - DATABASE_URL=postgres://postgres@localhost:5432/showcased install: - - pip install codecov pytest-cov pipenv + - pip install codecov pytest-cov uwsgi + - pip install -r requirements-dev.txt - nvm install 8 - npm i - npm run build-assets --production + - python setup.py develop matrix: include: - python: "3.7" @@ -27,17 +27,12 @@ matrix: sudo: required dist: xenial allow_failures: - - python: "pypy3" - python: "nightly" + - python: "pypy" + - python: "pypy3" fast_finish: true -before_script: # code coverage tool - - 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 script: - - pipenv install - pytest --cov --cov-report= -after_script: - - if [[ "$TRAVIS_PULL_REQUEST" == "false" && "$TRAVIS_PYTHON_VERSION" == "3.7" ]]; then ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT; fi + - python .ci/test_servers.py after_success: - codecov diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ee6ced2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 NyanKiyoshi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e907f41 --- /dev/null +++ b/Makefile @@ -0,0 +1,98 @@ +.PHONY: build + +MODULE:=texrrow + +all: ensure-pip dev style checks dists test + +ensure-pip: + pip install --user --upgrade pipenv pip + pip --version + pipenv --version + +ensure-pip-ci: + pip install --upgrade pipenv pip + pip --version + pipenv --version + +dev: + pipenv install --dev + pipenv run pip install -e . + +dev-ci: + pipenv install --dev --skip-lock + pipenv run pip install -e . + +dev-py2: + pipenv install --dev --two + pipenv run pip install -e . + +style: isort autopep8 yapf + +isort: + pipenv run isort -y + +autopep8: + pipenv run autopep8 --in-place --recursive setup.py $(MODULE) + +yapf: + pipenv run yapf --style .yapf --recursive -i $(MODULE) + +checks: flake8 pylint + +flake8: + pipenv run python setup.py flake8 + +pylint: + pipenv run pylint --rcfile=.pylintrc --output-format=colorized $(MODULE) + +sc: style checks +sct: style checks test + +build: dists + +test: + pipenv run pytest $(MODULE) + +test-verbose: + pipenv run pytest -s $(MODULE) + +test-coverage: + pipenv run py.test -v --cov $(MODULE) --cov-report term-missing + +dists: sdist bdist wheels + +sdist: + pipenv run python setup.py sdist + +bdist: + pipenv run python setup.py bdist + +wheels: + pipenv run python setup.py bdist_wheel + +pypi-publish: build + pipenv run twine upload --repository pypi dist/*.whl + +pypi-test-publish: build + pipenv run twine upload --repository testpypi dist/*.whl + +update: + pipenv update + +githook: style + +push: githook + @git push origin --tags + +clean: + pipenv --rm + +# aliases to gracefully handle typos on poor dev's terminal +check: checks +devel: dev +develop: dev +dist: dists +install: install-system +pypi: pypi-publish +styles: style +wheel: wheels diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 2604ad3..0000000 --- a/Pipfile +++ /dev/null @@ -1,21 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -flask = "*" -flask-wtf = "*" -flask-babel = "*" -flask-bootstrap-4-alpha = {ref = "fork", git = "https://github.com/NyanKiyoshi/flask-bootstrap-4-alpha"} -keyboard = "*" - -[dev-packages] -pytest = "*" -pipenv-to-requirements = "*" -coverage = "*" -blinker = "*" -flask-debugtoolbar = "*" - -[requires] -python_version = "3" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index c34f4b9..0000000 --- a/Pipfile.lock +++ /dev/null @@ -1,304 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "b2a1cc21d4c5ff61935d63f7917f0a5a3abdb68437ba1aa142fc612f31c6f40e" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "babel": { - "hashes": [ - "sha256:6778d85147d5d85345c14a26aada5e478ab04e39b078b0745ee6870c2b5cf669", - "sha256:8cba50f48c529ca3fa18cf81fa9403be176d374ac4d60738b839122dfaaa3d23" - ], - "version": "==2.6.0" - }, - "click": { - "hashes": [ - "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", - "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b" - ], - "version": "==6.7" - }, - "flask": { - "hashes": [ - "sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48", - "sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05" - ], - "index": "pypi", - "version": "==1.0.2" - }, - "flask-babel": { - "hashes": [ - "sha256:462a3c599b0ccf426ca1757cc612f1db383844efd346d14170da04c8c76dd521", - "sha256:c0d75710bd4b0fe866f9f2347de6e19208712f9cec006436b4c1c15d4cb0c939" - ], - "index": "pypi", - "version": "==0.11.2" - }, - "flask-bootstrap-4-alpha": { - "git": "https://github.com/NyanKiyoshi/flask-bootstrap-4-alpha", - "ref": "8c340bf2192327bba49cece1f0ffc7af82e5d40e" - }, - "flask-wtf": { - "hashes": [ - "sha256:5d14d55cfd35f613d99ee7cba0fc3fbbe63ba02f544d349158c14ca15561cc36", - "sha256:d9a9e366b32dcbb98ef17228e76be15702cd2600675668bca23f63a7947fd5ac" - ], - "index": "pypi", - "version": "==0.14.2" - }, - "itsdangerous": { - "hashes": [ - "sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519" - ], - "version": "==0.24" - }, - "jinja2": { - "hashes": [ - "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", - "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4" - ], - "version": "==2.10" - }, - "keyboard": { - "hashes": [ - "sha256:58912e4be21529fc5b57f89af7f90b0b5f5af4db08c74a8233821700eaabf189", - "sha256:d4375e4be666a91b71e782fdffa017cbf1a6de98f8c9fe8b66e9386944380c45" - ], - "index": "pypi", - "version": "==0.13.2" - }, - "markupsafe": { - "hashes": [ - "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665" - ], - "version": "==1.0" - }, - "pytz": { - "hashes": [ - "sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053", - "sha256:ffb9ef1de172603304d9d2819af6f5ece76f2e85ec10692a524dd876e72bf277" - ], - "version": "==2018.5" - }, - "werkzeug": { - "hashes": [ - "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c", - "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b" - ], - "version": "==0.14.1" - }, - "wtforms": { - "hashes": [ - "sha256:0cdbac3e7f6878086c334aa25dc5a33869a3954e9d1e015130d65a69309b3b61", - "sha256:e3ee092c827582c50877cdbd49e9ce6d2c5c1f6561f849b3b068c1b8029626f1" - ], - "version": "==2.2.1" - } - }, - "develop": { - "atomicwrites": { - "hashes": [ - "sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0", - "sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee" - ], - "markers": "python_version >= '2.7' and python_version != '3.3.*' and python_version != '3.1.*' and python_version != '3.0.*' and python_version != '3.2.*'", - "version": "==1.2.1" - }, - "attrs": { - "hashes": [ - "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69", - "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb" - ], - "version": "==18.2.0" - }, - "blinker": { - "hashes": [ - "sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6" - ], - "index": "pypi", - "version": "==1.4" - }, - "certifi": { - "hashes": [ - "sha256:376690d6f16d32f9d1fe8932551d80b23e9d393a8578c5633a2ed39a64861638", - "sha256:456048c7e371c089d0a77a5212fb37a2c2dce1e24146e3b7e0261736aaeaa22a" - ], - "version": "==2018.8.24" - }, - "click": { - "hashes": [ - "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", - "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b" - ], - "version": "==6.7" - }, - "coverage": { - "hashes": [ - "sha256:03481e81d558d30d230bc12999e3edffe392d244349a90f4ef9b88425fac74ba", - "sha256:0b136648de27201056c1869a6c0d4e23f464750fd9a9ba9750b8336a244429ed", - "sha256:10a46017fef60e16694a30627319f38a2b9b52e90182dddb6e37dcdab0f4bf95", - "sha256:198626739a79b09fa0a2f06e083ffd12eb55449b5f8bfdbeed1df4910b2ca640", - "sha256:23d341cdd4a0371820eb2b0bd6b88f5003a7438bbedb33688cd33b8eae59affd", - "sha256:28b2191e7283f4f3568962e373b47ef7f0392993bb6660d079c62bd50fe9d162", - "sha256:2a5b73210bad5279ddb558d9a2bfedc7f4bf6ad7f3c988641d83c40293deaec1", - "sha256:2eb564bbf7816a9d68dd3369a510be3327f1c618d2357fa6b1216994c2e3d508", - "sha256:337ded681dd2ef9ca04ef5d93cfc87e52e09db2594c296b4a0a3662cb1b41249", - "sha256:3a2184c6d797a125dca8367878d3b9a178b6fdd05fdc2d35d758c3006a1cd694", - "sha256:3c79a6f7b95751cdebcd9037e4d06f8d5a9b60e4ed0cd231342aa8ad7124882a", - "sha256:3d72c20bd105022d29b14a7d628462ebdc61de2f303322c0212a054352f3b287", - "sha256:3eb42bf89a6be7deb64116dd1cc4b08171734d721e7a7e57ad64cc4ef29ed2f1", - "sha256:4635a184d0bbe537aa185a34193898eee409332a8ccb27eea36f262566585000", - "sha256:56e448f051a201c5ebbaa86a5efd0ca90d327204d8b059ab25ad0f35fbfd79f1", - "sha256:5a13ea7911ff5e1796b6d5e4fbbf6952381a611209b736d48e675c2756f3f74e", - "sha256:69bf008a06b76619d3c3f3b1983f5145c75a305a0fea513aca094cae5c40a8f5", - "sha256:6bc583dc18d5979dc0f6cec26a8603129de0304d5ae1f17e57a12834e7235062", - "sha256:701cd6093d63e6b8ad7009d8a92425428bc4d6e7ab8d75efbb665c806c1d79ba", - "sha256:7608a3dd5d73cb06c531b8925e0ef8d3de31fed2544a7de6c63960a1e73ea4bc", - "sha256:76ecd006d1d8f739430ec50cc872889af1f9c1b6b8f48e29941814b09b0fd3cc", - "sha256:7aa36d2b844a3e4a4b356708d79fd2c260281a7390d678a10b91ca595ddc9e99", - "sha256:7d3f553904b0c5c016d1dad058a7554c7ac4c91a789fca496e7d8347ad040653", - "sha256:7e1fe19bd6dce69d9fd159d8e4a80a8f52101380d5d3a4d374b6d3eae0e5de9c", - "sha256:8c3cb8c35ec4d9506979b4cf90ee9918bc2e49f84189d9bf5c36c0c1119c6558", - "sha256:9d6dd10d49e01571bf6e147d3b505141ffc093a06756c60b053a859cb2128b1f", - "sha256:be6cfcd8053d13f5f5eeb284aa8a814220c3da1b0078fa859011c7fffd86dab9", - "sha256:c1bb572fab8208c400adaf06a8133ac0712179a334c09224fb11393e920abcdd", - "sha256:de4418dadaa1c01d497e539210cb6baa015965526ff5afc078c57ca69160108d", - "sha256:e05cb4d9aad6233d67e0541caa7e511fa4047ed7750ec2510d466e806e0255d6", - "sha256:f3f501f345f24383c0000395b26b726e46758b71393267aeae0bd36f8b3ade80" - ], - "index": "pypi", - "version": "==4.5.1" - }, - "flask": { - "hashes": [ - "sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48", - "sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05" - ], - "index": "pypi", - "version": "==1.0.2" - }, - "flask-debugtoolbar": { - "hashes": [ - "sha256:3d9657bc0c3633ace429e3ff451742bb59d1b7a7b95c9eb23a65ac9be2812959", - "sha256:ec810083123aae0632eb32ba11e1cb4cdace81e7ce6c5009dd06c5204afbce52" - ], - "index": "pypi", - "version": "==0.10.1" - }, - "itsdangerous": { - "hashes": [ - "sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519" - ], - "version": "==0.24" - }, - "jinja2": { - "hashes": [ - "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", - "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4" - ], - "version": "==2.10" - }, - "markupsafe": { - "hashes": [ - "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665" - ], - "version": "==1.0" - }, - "more-itertools": { - "hashes": [ - "sha256:c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092", - "sha256:c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e", - "sha256:fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d" - ], - "version": "==4.3.0" - }, - "pbr": { - "hashes": [ - "sha256:1b8be50d938c9bb75d0eaf7eda111eec1bf6dc88a62a6412e33bf077457e0f45", - "sha256:b486975c0cafb6beeb50ca0e17ba047647f229087bd74e37f4a7e2cac17d2caa" - ], - "version": "==4.2.0" - }, - "pipenv": { - "hashes": [ - "sha256:6ecf60c66187c6ca25a2dcd095036aba07539354a5011d94805fbfc5932582b0", - "sha256:bb6bd074f853d9bab675942226a785a64d4fc42b5847538755e9573f5b77f63a", - "sha256:d4b1aea904ae1488b7060137059642246baae709232f4536ad723ce216e4b540" - ], - "markers": "python_version != '3.3.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version >= '2.7' and python_version != '3.0.*'", - "version": "==2018.7.1" - }, - "pipenv-to-requirements": { - "hashes": [ - "sha256:1ddb3ef76062cc6f360d29771881ebb4911d1581591ec61a6334953834622d23" - ], - "index": "pypi", - "version": "==0.5.1" - }, - "pluggy": { - "hashes": [ - "sha256:6e3836e39f4d36ae72840833db137f7b7d35105079aee6ec4a62d9f80d594dd1", - "sha256:95eb8364a4708392bae89035f45341871286a333f749c3141c20573d2b3876e1" - ], - "markers": "python_version != '3.1.*' and python_version != '3.0.*' and python_version >= '2.7' and python_version != '3.2.*' and python_version != '3.3.*'", - "version": "==0.7.1" - }, - "py": { - "hashes": [ - "sha256:06a30435d058473046be836d3fc4f27167fd84c45b99704f2fb5509ef61f9af1", - "sha256:50402e9d1c9005d759426988a492e0edaadb7f4e68bcddfea586bc7432d009c6" - ], - "markers": "python_version >= '2.7' and python_version != '3.3.*' and python_version != '3.1.*' and python_version != '3.0.*' and python_version != '3.2.*'", - "version": "==1.6.0" - }, - "pytest": { - "hashes": [ - "sha256:453cbbbe5ce6db38717d282b758b917de84802af4288910c12442984bde7b823", - "sha256:a8a07f84e680482eb51e244370aaf2caa6301ef265f37c2bdefb3dd3b663f99d" - ], - "index": "pypi", - "version": "==3.8.0" - }, - "six": { - "hashes": [ - "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", - "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" - ], - "version": "==1.11.0" - }, - "virtualenv": { - "hashes": [ - "sha256:2ce32cd126117ce2c539f0134eb89de91a8413a29baac49cbab3eb50e2026669", - "sha256:ca07b4c0b54e14a91af9f34d0919790b016923d157afda5efdde55c96718f752" - ], - "markers": "python_version != '3.2.*' and python_version >= '2.7' and python_version != '3.1.*' and python_version != '3.0.*'", - "version": "==16.0.0" - }, - "virtualenv-clone": { - "hashes": [ - "sha256:4507071d81013fd03ea9930ec26bc8648b997927a11fa80e8ee81198b57e0ac7", - "sha256:b5cfe535d14dc68dfc1d1bb4ac1209ea28235b91156e2bba8e250d291c3fb4f8" - ], - "version": "==0.3.0" - }, - "werkzeug": { - "hashes": [ - "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c", - "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b" - ], - "version": "==0.14.1" - } - } -} diff --git a/Piplock b/Piplock deleted file mode 100644 index b31227c..0000000 --- a/Piplock +++ /dev/null @@ -1,29 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -flask = "*" -flask-sqlalchemy = "*" -flask-migrate = "*" -sqlalchemy-migrate = "*" -flask-wtf = "*" -flask-babel = "*" -wtforms-alchemy = "*" -"jinja2-highlight" = "*" -"psycopg2" = "*" -sqlalchemy-utils = "*" -python-slugify = "*" -flask-login = "*" -passlib = "*" - -[dev-packages] -pytest = "*" -pipenv-to-requirements = "*" -coverage = "*" -blinker = "*" -flask-debugtoolbar = "*" - -[requires] -python_version = "3" diff --git a/README.md b/README.md index 7029552..881dcf3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,29 @@ -# Texrrow +