From c6cc1f89600d9c8825f6fa6fdd255f00cc90ac6a Mon Sep 17 00:00:00 2001 From: Stuart Bradley Date: Tue, 25 Jan 2022 15:29:33 +0000 Subject: [PATCH 1/5] Move to Poetry --- .bumpversion.cfg | 9 - .coveragerc | 10 + .../action.yml | 30 + .github/workflows/continuous_integration.yml | 35 + .github/workflows/release.yml | 22 + .isort.cfg | 9 + .pre-commit-config.yaml | 24 + LICENSE.txt => LICENSE | 0 Pipfile | 25 - Pipfile.lock | 395 -------- README.md | 41 +- example/urls.py | 7 +- makefile | 46 - poetry.lock | 874 ++++++++++++++++++ pyproject.toml | 52 ++ pytest.ini | 2 - setup.cfg | 5 + setup.py | 43 - tests/settings.py | 9 +- tests/urls.py | 7 +- tox.ini | 20 - 21 files changed, 1073 insertions(+), 592 deletions(-) delete mode 100644 .bumpversion.cfg create mode 100644 .coveragerc create mode 100644 .github/actions/drf-react-template-framework-build/action.yml create mode 100644 .github/workflows/continuous_integration.yml create mode 100644 .github/workflows/release.yml create mode 100644 .isort.cfg create mode 100644 .pre-commit-config.yaml rename LICENSE.txt => LICENSE (100%) delete mode 100644 Pipfile delete mode 100644 Pipfile.lock delete mode 100644 makefile create mode 100644 poetry.lock create mode 100644 pyproject.toml delete mode 100644 pytest.ini delete mode 100644 setup.py delete mode 100644 tox.ini diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index f3191b8..0000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[bumpversion] -current_version = 1.0.0 -commit = False -tag = False - -[bumpversion:file:setup.py] - -[bumpversion:file:makefile] - diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..d83d598 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,10 @@ +[run] +branch = True +source = + drf_react_template +omit = + *example* + *tests* + +[report] +show_missing = True diff --git a/.github/actions/drf-react-template-framework-build/action.yml b/.github/actions/drf-react-template-framework-build/action.yml new file mode 100644 index 0000000..8461e5f --- /dev/null +++ b/.github/actions/drf-react-template-framework-build/action.yml @@ -0,0 +1,30 @@ +name: 'DRF React Template Framework Build' +description: 'DRF React Template Framework build steps' +inputs: + python-version: + description: 'Python version to use' + required: true + default: '3.10' +runs: + using: "composite" + steps: + - name: Set up Python ${{ inputs.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ inputs.python-version }} + - name: Install Poetry + uses: snok/install-poetry@v1.3 + with: + virtualenvs-create: true + virtualenvs-in-project: true + installer-parallel: true + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@v2 + with: + path: .venv + key: venv-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} + - name: Install dependencies + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' + run: poetry install --no-interaction --no-root + shell: bash diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml new file mode 100644 index 0000000..3af911f --- /dev/null +++ b/.github/workflows/continuous_integration.yml @@ -0,0 +1,35 @@ +name: "Continuous Integration" +on: [push] + +jobs: + lint: + runs-on: ubuntu-latest + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.10.0 + uses: actions/setup-python@v2 + with: + python-version: "3.10" + - name: Run pre-commit + uses: pre-commit/action@v2.0.0 + + test: + needs: lint + runs-on: ubuntu-latest + strategy: + matrix: + python: ["3.7", "3.8", "3.9", "3.10"] + fail-fast: false + + steps: + - name: Check out repository + uses: actions/checkout@v2 + - name: Build steps + uses: ./.github/actions/drf-react-template-framework-build + with: + python-version: ${{ matrix.python }} + - name: Test with pytest + run: | + poetry run pytest --cov diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..68b1771 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,22 @@ +name: Release + +on: + release: + types: + - created + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v2 + - name: Build steps + uses: ./.github/actions/drf-react-template-framework-build + with: + python-version: "3.10" + - name: Build and publish + uses: d1618033/gh-action-python-publish-using-poetry@0.1.1 + with: + pypi_username: '__token__' + pypi_password: ${{ secrets.PYPI_TOKEN }} diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..b6c007c --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,9 @@ +[settings] +; Taken from https://black.readthedocs.io/en/stable/compatible_configs.html#isort +multi_line_output = 3 +include_trailing_comma = True +force_grid_wrap = 0 +use_parentheses = True +ensure_newline_before_comments = True +line_length = 88 +known_third_party = django,factory,pytest,rest_framework,six diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..4a61b94 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,24 @@ +repos: +- repo: https://github.com/asottile/seed-isort-config + rev: v1.9.4 + hooks: + - id: seed-isort-config +- repo: https://github.com/PyCQA/isort + rev: 5.5.4 + hooks: + - id: isort +- repo: https://github.com/psf/black + rev: 21.12b0 + hooks: + - id: black + language_version: python3.10 +- repo: https://gitlab.com/pycqa/flake8 + rev: 3.8.4 + hooks: + - id: flake8 + +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: check-ast + - id: debug-statements diff --git a/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE diff --git a/Pipfile b/Pipfile deleted file mode 100644 index f78da7c..0000000 --- a/Pipfile +++ /dev/null @@ -1,25 +0,0 @@ -[[source]] -url = "https://pypi.python.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -"psycopg2" = "*" -django = "*" -djangorestframework = "*" -six = "*" - -[dev-packages] -pytest-mock = "*" -pytest-django = "*" -pytest = "*" -tox = "*" -factory-boy = "*" -xenon = "*" -"flake8" = "*" -twine = "*" -"bump2version" = "*" -pytest-cov = "*" - -[requires] -python_version = "3.6" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index fdc6567..0000000 --- a/Pipfile.lock +++ /dev/null @@ -1,395 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "23471cf5c00ed2d184d5405d44a1149f90c9308837efa620e8fccf6cb0d35ee9" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.6" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.python.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "django": { - "hashes": [ - "sha256:3eb25c99df1523446ec2dc1b00e25eb2ecbdf42c9d8b0b8b32a204a8db9011f8", - "sha256:69ff89fa3c3a8337015478a1a0744f52a9fef5d12c1efa01a01f99bcce9bf10c" - ], - "index": "pypi", - "version": "==2.0.6" - }, - "djangorestframework": { - "hashes": [ - "sha256:b6714c3e4b0f8d524f193c91ecf5f5450092c2145439ac2769711f7eba89a9d9", - "sha256:c375e4f95a3a64fccac412e36fb42ba36881e52313ec021ef410b40f67cddca4" - ], - "index": "pypi", - "version": "==3.8.2" - }, - "psycopg2": { - "hashes": [ - "sha256:027ae518d0e3b8fff41990e598bc7774c3d08a3a20e9ecc0b59fb2aaaf152f7f", - "sha256:092a80da1b052a181b6e6c765849c9b32d46c5dac3b81bf8c9b83e697f3cdbe8", - "sha256:0b9851e798bae024ed1a2a6377a8dab4b8a128a56ed406f572f9f06194e4b275", - "sha256:179c52eb870110a8c1b460c86d4f696d58510ea025602cd3f81453746fccb94f", - "sha256:19983b77ec1fc2a210092aa0333ee48811fd9fb5f194c6cd5b927ed409aea5f8", - "sha256:1d90379d01d0dc50ae9b40c863933d87ff82d51dd7d52cea5d1cb7019afd72cd", - "sha256:27467fd5af1dcc0a82d72927113b8f92da8f44b2efbdb8906bd76face95b596d", - "sha256:32702e3bd8bfe12b36226ba9846ed9e22336fc4bd710039d594b36bd432ae255", - "sha256:33f9e1032095e1436fa9ec424abcbd4c170da934fb70e391c5d78275d0307c75", - "sha256:36030ca7f4b4519ee4f52a74edc4ec73c75abfb6ea1d80ac7480953d1c0aa3c3", - "sha256:363fbbf4189722fc46779be1fad2597e2c40b3f577dc618f353a46391cf5d235", - "sha256:6f302c486132f8dd11f143e919e236ea4467d53bf18c451cac577e6988ecbd05", - "sha256:733166464598c239323142c071fa4c9b91c14359176e5ae7e202db6bcc1d2eb5", - "sha256:7cbc3b21ce2f681ca9ad2d8c0901090b23a30c955e980ebf1006d41f37068a95", - "sha256:888bba7841116e529f407f15c6d28fe3ef0760df8c45257442ec2f14f161c871", - "sha256:8966829cb0d21a08a3c5ac971a2eb67c3927ae27c247300a8476554cc0ce2ae8", - "sha256:8bf51191d60f6987482ef0cfe8511bbf4877a5aa7f313d7b488b53189cf26209", - "sha256:8eb94c0625c529215b53c08fb4e461546e2f3fc96a49c13d5474b5ad7aeab6cf", - "sha256:8ebba5314c609a05c6955e5773c7e0e57b8dd817e4f751f30de729be58fa5e78", - "sha256:932a4c101af007cb3132b1f8a9ffef23386acc53dad46536dc5ba43a3235ae02", - "sha256:ad75fe10bea19ad2188c5cb5fc4cdf53ee808d9b44578c94a3cd1e9fc2beb656", - "sha256:aeaba399254ca79c299d9fe6aa811d3c3eac61458dee10270de7f4e71c624998", - "sha256:b178e0923c93393e16646155794521e063ec17b7cc9f943f15b7d4b39776ea2c", - "sha256:b68e89bb086a9476fa85298caab43f92d0a6af135a5f433d1f6b6d82cafa7b55", - "sha256:d74cf9234ba76426add5e123449be08993a9b13ff434c6efa3a07caa305a619f", - "sha256:f3d3a88128f0c219bdc5b2d9ccd496517199660cea021c560a3252116df91cbd", - "sha256:fe6a7f87356116f5ea840c65b032af17deef0e1a5c34013a2962dd6f99b860dd" - ], - "index": "pypi", - "version": "==2.7.4" - }, - "pytz": { - "hashes": [ - "sha256:65ae0c8101309c45772196b21b74c46b2e5d11b6275c45d251b150d5da334555", - "sha256:c06425302f2cf668f1bba7a0a03f3c1d34d4ebeef2c72003da308b3947c7f749" - ], - "version": "==2018.4" - } - }, - "develop": { - "atomicwrites": { - "hashes": [ - "sha256:240831ea22da9ab882b551b31d4225591e5e447a68c5e188db5b89ca1d487585", - "sha256:a24da68318b08ac9c9c45029f4a10371ab5b20e4226738e150e6e7c571630ae6" - ], - "version": "==1.1.5" - }, - "attrs": { - "hashes": [ - "sha256:4b90b09eeeb9b88c35bc642cbac057e45a5fd85367b985bd2809c62b7b939265", - "sha256:e0d0eb91441a3b53dab4d9b743eafc1ac44476296a2053b6ca3af0b139faf87b" - ], - "version": "==18.1.0" - }, - "bump2version": { - "hashes": [ - "sha256:6df32d297f7a624d4a66f06c5dfcf29ec5fafdc53cf8d0c0ee973563a8f1794d", - "sha256:bc169d268e616dd4aa578ad7d0da99217d037d459b0eea14387d9e4f3cdabb02" - ], - "index": "pypi", - "version": "==0.5.8" - }, - "certifi": { - "hashes": [ - "sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7", - "sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0" - ], - "version": "==2018.4.16" - }, - "chardet": { - "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" - ], - "version": "==3.0.4" - }, - "colorama": { - "hashes": [ - "sha256:463f8483208e921368c9f306094eb6f725c6ca42b0f97e313cb5d5512459feda", - "sha256:48eb22f4f8461b1df5734a074b57042430fb06e1d61bd1e11b078c0fe6d7a1f1" - ], - "version": "==0.3.9" - }, - "coverage": { - "hashes": [ - "sha256:03481e81d558d30d230bc12999e3edffe392d244349a90f4ef9b88425fac74ba", - "sha256:0b136648de27201056c1869a6c0d4e23f464750fd9a9ba9750b8336a244429ed", - "sha256:104ab3934abaf5be871a583541e8829d6c19ce7bde2923b2751e0d3ca44db60a", - "sha256:15b111b6a0f46ee1a485414a52a7ad1d703bdf984e9ed3c288a4414d3871dcbd", - "sha256:198626739a79b09fa0a2f06e083ffd12eb55449b5f8bfdbeed1df4910b2ca640", - "sha256:1c383d2ef13ade2acc636556fd544dba6e14fa30755f26812f54300e401f98f2", - "sha256:28b2191e7283f4f3568962e373b47ef7f0392993bb6660d079c62bd50fe9d162", - "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:9e112fcbe0148a6fa4f0a02e8d58e94470fc6cb82a5481618fea901699bf34c4", - "sha256:ac4fef68da01116a5c117eba4dd46f2e06847a497de5ed1d64bb99a5fda1ef91", - "sha256:b8815995e050764c8610dbc82641807d196927c3dbed207f0a079833ffcf588d", - "sha256:be6cfcd8053d13f5f5eeb284aa8a814220c3da1b0078fa859011c7fffd86dab9", - "sha256:c1bb572fab8208c400adaf06a8133ac0712179a334c09224fb11393e920abcdd", - "sha256:de4418dadaa1c01d497e539210cb6baa015965526ff5afc078c57ca69160108d", - "sha256:e05cb4d9aad6233d67e0541caa7e511fa4047ed7750ec2510d466e806e0255d6", - "sha256:e4d96c07229f58cb686120f168276e434660e4358cc9cf3b0464210b04913e77", - "sha256:f3f501f345f24383c0000395b26b726e46758b71393267aeae0bd36f8b3ade80", - "sha256:f8a923a85cb099422ad5a2e345fe877bbc89a8a8b23235824a93488150e45f6e" - ], - "version": "==4.5.1" - }, - "factory-boy": { - "hashes": [ - "sha256:6f25cc4761ac109efd503f096e2ad99421b1159f01a29dbb917359dcd68e08ca", - "sha256:d552cb872b310ae78bd7429bf318e42e1e903b1a109e899a523293dfa762ea4f" - ], - "index": "pypi", - "version": "==2.11.1" - }, - "faker": { - "hashes": [ - "sha256:782a58cec0e083df8e3536b0a890fce9bdc1633782c140b64183dcc626fea53e", - "sha256:a77a1a2223a8e0d32618878350bbd2171040f32b526ba2cddfab8864704bb370" - ], - "version": "==0.8.15" - }, - "flake8": { - "hashes": [ - "sha256:7253265f7abd8b313e3892944044a365e3f4ac3fcdcfb4298f55ee9ddf188ba0", - "sha256:c7841163e2b576d435799169b78703ad6ac1bbb0f199994fc05f700b2a90ea37" - ], - "index": "pypi", - "version": "==3.5.0" - }, - "flake8-polyfill": { - "hashes": [ - "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9", - "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda" - ], - "version": "==1.0.2" - }, - "idna": { - "hashes": [ - "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f", - "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4" - ], - "version": "==2.6" - }, - "mando": { - "hashes": [ - "sha256:4ce09faec7e5192ffc3c57830e26acba0fd6cd11e1ee81af0d4df0657463bd1c", - "sha256:79feb19dc0f097daa64a1243db578e7674909b75f88ac2220f1c065c10a0d960" - ], - "version": "==0.6.4" - }, - "mccabe": { - "hashes": [ - "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", - "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" - ], - "version": "==0.6.1" - }, - "more-itertools": { - "hashes": [ - "sha256:2b6b9893337bfd9166bee6a62c2b0c9fe7735dcf85948b387ec8cba30e85d8e8", - "sha256:6703844a52d3588f951883005efcf555e49566a48afd4db4e965d69b883980d3", - "sha256:a18d870ef2ffca2b8463c0070ad17b5978056f403fb64e3f15fe62a52db21cc0" - ], - "version": "==4.2.0" - }, - "pkginfo": { - "hashes": [ - "sha256:5878d542a4b3f237e359926384f1dde4e099c9f5525d236b1840cf704fa8d474", - "sha256:a39076cb3eb34c333a0dd390b568e9e1e881c7bf2cc0aee12120636816f55aee" - ], - "version": "==1.4.2" - }, - "pluggy": { - "hashes": [ - "sha256:7f8ae7f5bdf75671a718d2daf0a64b7885f74510bcd98b1a0bb420eb9a9d0cff", - "sha256:d345c8fe681115900d6da8d048ba67c25df42973bda370783cd58826442dcd7c", - "sha256:e160a7fcf25762bb60efc7e171d4497ff1d8d2d75a3d0df7a21b76821ecbf5c5" - ], - "version": "==0.6.0" - }, - "py": { - "hashes": [ - "sha256:29c9fab495d7528e80ba1e343b958684f4ace687327e6f789a94bf3d1915f881", - "sha256:983f77f3331356039fdd792e9220b7b8ee1aa6bd2b25f567a963ff1de5a64f6a" - ], - "version": "==1.5.3" - }, - "pycodestyle": { - "hashes": [ - "sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766", - "sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9" - ], - "version": "==2.3.1" - }, - "pyflakes": { - "hashes": [ - "sha256:08bd6a50edf8cffa9fa09a463063c425ecaaf10d1eb0335a7e8b1401aef89e6f", - "sha256:8d616a382f243dbf19b54743f280b80198be0bca3a5396f1d2e1fca6223e8805" - ], - "version": "==1.6.0" - }, - "pytest": { - "hashes": [ - "sha256:26838b2bc58620e01675485491504c3aa7ee0faf335c37fcd5f8731ca4319591", - "sha256:32c49a69566aa7c333188149ad48b58ac11a426d5352ea3d8f6ce843f88199cb" - ], - "index": "pypi", - "version": "==3.6.1" - }, - "pytest-cov": { - "hashes": [ - "sha256:03aa752cf11db41d281ea1d807d954c4eda35cfa1b21d6971966cc041bbf6e2d", - "sha256:890fe5565400902b0c78b5357004aab1c814115894f4f21370e2433256a3eeec" - ], - "index": "pypi", - "version": "==2.5.1" - }, - "pytest-django": { - "hashes": [ - "sha256:534505e0261cc566279032d9d887f844235342806fd63a6925689670fa1b29d7", - "sha256:7501942093db2250a32a4e36826edfc542347bb9b26c78ed0649cdcfd49e5789" - ], - "index": "pypi", - "version": "==3.2.1" - }, - "pytest-mock": { - "hashes": [ - "sha256:53801e621223d34724926a5c98bd90e8e417ce35264365d39d6c896388dcc928", - "sha256:d89a8209d722b8307b5e351496830d5cc5e192336003a485443ae9adeb7dd4c0" - ], - "index": "pypi", - "version": "==1.10.0" - }, - "python-dateutil": { - "hashes": [ - "sha256:1adb80e7a782c12e52ef9a8182bebeb73f1d7e24e374397af06fb4956c8dc5c0", - "sha256:e27001de32f627c22380a688bcc43ce83504a7bc5da472209b4c70f02829f0b8" - ], - "version": "==2.7.3" - }, - "pyyaml": { - "hashes": [ - "sha256:0c507b7f74b3d2dd4d1322ec8a94794927305ab4cebbe89cc47fe5e81541e6e8", - "sha256:16b20e970597e051997d90dc2cddc713a2876c47e3d92d59ee198700c5427736", - "sha256:3262c96a1ca437e7e4763e2843746588a965426550f3797a79fca9c6199c431f", - "sha256:326420cbb492172dec84b0f65c80942de6cedb5233c413dd824483989c000608", - "sha256:4474f8ea030b5127225b8894d626bb66c01cda098d47a2b0d3429b6700af9fd8", - "sha256:592766c6303207a20efc445587778322d7f73b161bd994f227adaa341ba212ab", - "sha256:5ac82e411044fb129bae5cfbeb3ba626acb2af31a8d17d175004b70862a741a7", - "sha256:5f84523c076ad14ff5e6c037fe1c89a7f73a3e04cf0377cb4d017014976433f3", - "sha256:827dc04b8fa7d07c44de11fabbc888e627fa8293b695e0f99cb544fdfa1bf0d1", - "sha256:b4c423ab23291d3945ac61346feeb9a0dc4184999ede5e7c43e1ffb975130ae6", - "sha256:bc6bced57f826ca7cb5125a10b23fd0f2fff3b7c4701d64c439a300ce665fff8", - "sha256:c01b880ec30b5a6e6aa67b09a2fe3fb30473008c85cd6a67359a1b15ed6d83a4", - "sha256:ca233c64c6e40eaa6c66ef97058cdc80e8d0157a443655baa1b2966e812807ca", - "sha256:e863072cdf4c72eebf179342c94e6989c67185842d9997960b3e69290b2fa269" - ], - "version": "==3.12" - }, - "radon": { - "hashes": [ - "sha256:c7212cb3ac4947f74cb3189bc998c03488c68e2c63c2045e5c2b6bbfb554c785", - "sha256:d5b7fd86c8ce3f81a2307298aa5e00a0b9d3334c9aeb69a9edce654b302cf81d" - ], - "version": "==2.2.0" - }, - "requests": { - "hashes": [ - "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b", - "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e" - ], - "version": "==2.18.4" - }, - "requests-toolbelt": { - "hashes": [ - "sha256:42c9c170abc2cacb78b8ab23ac957945c7716249206f90874651971a4acff237", - "sha256:f6a531936c6fa4c6cfce1b9c10d5c4f498d16528d2a54a22ca00011205a187b5" - ], - "version": "==0.8.0" - }, - "six": { - "hashes": [ - "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", - "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" - ], - "version": "==1.11.0" - }, - "text-unidecode": { - "hashes": [ - "sha256:5a1375bb2ba7968740508ae38d92e1f889a0832913cb1c447d5e2046061a396d", - "sha256:801e38bd550b943563660a91de8d4b6fa5df60a542be9093f7abf819f86050cc" - ], - "version": "==1.2" - }, - "tox": { - "hashes": [ - "sha256:96efa09710a3daeeb845561ebbe1497641d9cef2ee0aea30db6969058b2bda2f", - "sha256:9ee7de958a43806402a38c0d2aa07fa8553f4d2c20a15b140e9f771c2afeade0" - ], - "index": "pypi", - "version": "==3.0.0" - }, - "tqdm": { - "hashes": [ - "sha256:224291ee0d8c52d91b037fd90806f48c79bcd9994d3b0abc9e44b946a908fccd", - "sha256:77b8424d41b31e68f437c6dd9cd567aebc9a860507cb42fbd880a5f822d966fe" - ], - "version": "==4.23.4" - }, - "twine": { - "hashes": [ - "sha256:08eb132bbaec40c6d25b358f546ec1dc96ebd2638a86eea68769d9e67fe2b129", - "sha256:2fd9a4d9ff0bcacf41fdc40c8cb0cfaef1f1859457c9653fd1b92237cc4e9f25" - ], - "index": "pypi", - "version": "==1.11.0" - }, - "urllib3": { - "hashes": [ - "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b", - "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f" - ], - "version": "==1.22" - }, - "virtualenv": { - "hashes": [ - "sha256:2ce32cd126117ce2c539f0134eb89de91a8413a29baac49cbab3eb50e2026669", - "sha256:ca07b4c0b54e14a91af9f34d0919790b016923d157afda5efdde55c96718f752" - ], - "version": "==16.0.0" - }, - "xenon": { - "hashes": [ - "sha256:03c6fa6820ca260a85931cec44bd60ae60194eec7eb22f53ba160b3c9578cdf1", - "sha256:ac86e17741c6b6c0cc7e38e48b2d9f1cb9918e78f7a12f892d798ba73d5c2c09" - ], - "index": "pypi", - "version": "==0.5.4" - } - } -} diff --git a/README.md b/README.md index 9f26b03..c8d4960 100644 --- a/README.md +++ b/README.md @@ -8,17 +8,11 @@ Documentation & Example Worflow: https://yoyowallet.github.io/yoflow/ ## Requirements -* Django ≥ 1.10 +* Django ≥ 3.2 * PostgreSQL ≥ 9.4 (JSON support required) ## Install -Install from PyPI: - -``` -pipenv install yoflow -``` - Add `yoflow` to your `INSTALLED_APPS`: ``` @@ -33,36 +27,3 @@ Apply yoflow database migrations: ``` python manage.py migrate ``` - -## Running Example - -``` -pipenv install -pipenv run make -pipenv run ./manage runserver -``` - -## Running Tests - -``` -pipenv install --dev -pipenv run make test -``` - -## Release - -### Bump Version - -This will automatically create a commit and tag for the new version - as per `.bumpversion.cfg`. - -``` -pipenv run make bump-patch # x.x.0 > x.x.1 -pipenv run make bump-minor # x.0.x > x.1.x -pipenv run make bump-major # 0.x.x > 1.x.x -``` - -To release to PyPI: - -``` -pipenv run make release -``` diff --git a/example/urls.py b/example/urls.py index f18f759..3b5dbd0 100644 --- a/example/urls.py +++ b/example/urls.py @@ -1,13 +1,12 @@ -from django.conf.urls import url +from django.urls import re_path from rest_framework import routers from example import views - router = routers.SimpleRouter() router.register(r'post', views.PostViewSet) urlpatterns = router.urls + [ - url(r'^post-view/(?P[0-9]+)', views.approved_function_view), - url(r'^post-cbv/(?P[0-9]+)', views.ApprovedClassBasedView.as_view()), + re_path(r'^post-view/(?P[0-9]+)', views.approved_function_view), + re_path(r'^post-cbv/(?P[0-9]+)', views.ApprovedClassBasedView.as_view()), ] diff --git a/makefile b/makefile deleted file mode 100644 index 9cd653e..0000000 --- a/makefile +++ /dev/null @@ -1,46 +0,0 @@ -build: clean database - -clean: - find . -type f -name "*.pyc" -delete - find . -type f -name "*,cover" -delete - -database: - psql -lqt | cut -d \| -f 1 | grep -wq yoflow || createdb yoflow - ./manage.py migrate - -static_analysis: pep8 xenon - -pep8: - @echo "Running flake8 over codebase" - flake8 --ignore=E501,W391,F999 --exclude=migrations yoflow/ - -xenon: - @echo "Running xenon over codebase" - xenon --max-absolute B --max-modules B --max-average A --exclude test_*.py yoflow/\ - -test: static_analysis - tox $(pytest_args) - -coverage: - py.test --cov=yoflow tests/ - -bundle: - python setup.py sdist - -release-test: - twine upload --repository-url https://test.pypi.org/legacy/ dist/yoflow-1.0.0.tar.gz - -release: - @echo -n "Are you sure you want to upload to PyPI? [y/N] " && read ans && [ $${ans:-N} == y ] - twine upload dist/* - -bump-major: - bump2version major - -bump-minor: - bump2version minor - -bump-patch: - bump2version patch - -.PHONY: bump-major bump-minor bump-patch bundle clean coverage database pep8 release release-test static_analysis test virtualenv xenon diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..3b43142 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,874 @@ +[[package]] +name = "asgiref" +version = "3.4.1" +description = "ASGI specs, helper code, and adapters" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"] + +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "21.4.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] + +[[package]] +name = "certifi" +version = "2021.10.8" +description = "Python package for providing Mozilla's CA Bundle." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "cfgv" +version = "3.0.0" +description = "Validate configuration and produce human readable error messages." +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "charset-normalizer" +version = "2.0.10" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "dev" +optional = false +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "coverage" +version = "6.2" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "distlib" +version = "0.3.4" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "django" +version = "3.2.11" +description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +asgiref = ">=3.3.2,<4" +pytz = "*" +sqlparse = ">=0.2.2" + +[package.extras] +argon2 = ["argon2-cffi (>=19.1.0)"] +bcrypt = ["bcrypt"] + +[[package]] +name = "djangorestframework" +version = "3.13.1" +description = "Web APIs for Django, made easy." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +django = ">=2.2" +pytz = "*" + +[[package]] +name = "factory-boy" +version = "3.2.1" +description = "A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +Faker = ">=0.7.0" + +[package.extras] +dev = ["coverage", "django", "flake8", "isort", "pillow", "sqlalchemy", "mongoengine", "wheel (>=0.32.0)", "tox", "zest.releaser"] +doc = ["sphinx", "sphinx-rtd-theme", "sphinxcontrib-spelling"] + +[[package]] +name = "faker" +version = "11.3.0" +description = "Faker is a Python package that generates fake data for you." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +python-dateutil = ">=2.4" +text-unidecode = "1.3" +typing-extensions = {version = ">=3.10.0.2", markers = "python_version < \"3.8\""} + +[[package]] +name = "filelock" +version = "3.4.1" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] +testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] + +[[package]] +name = "flake8" +version = "4.0.1" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +importlib-metadata = {version = "<4.3", markers = "python_version < \"3.8\""} +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.8.0,<2.9.0" +pyflakes = ">=2.4.0,<2.5.0" + +[[package]] +name = "future" +version = "0.18.2" +description = "Clean single-source support for Python 3 and 2" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "identify" +version = "1.6.2" +description = "File identification library for Python" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" + +[package.extras] +license = ["editdistance"] + +[[package]] +name = "idna" +version = "3.3" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "importlib-metadata" +version = "4.2.0" +description = "Read metadata from Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] + +[[package]] +name = "importlib-resources" +version = "5.4.0" +description = "Read resources from Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "mando" +version = "0.6.4" +description = "Create Python CLI apps with little to no effort at all!" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +six = "*" + +[package.extras] +restructuredText = ["rst2ansi"] + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "nodeenv" +version = "1.6.0" +description = "Node.js virtual environment builder" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "platformdirs" +version = "2.4.0" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "2.1.1" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +importlib-resources = {version = "*", markers = "python_version < \"3.7\""} +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +toml = "*" +virtualenv = ">=15.2" + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pycodestyle" +version = "2.8.0" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pyflakes" +version = "2.4.0" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pyparsing" +version = "3.0.7" +description = "Python parsing module" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pytest" +version = "6.2.5" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +toml = "*" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "3.0.0" +description = "Pytest plugin for measuring coverage." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] + +[[package]] +name = "pytest-django" +version = "4.5.2" +description = "A Django plugin for pytest." +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +pytest = ">=5.4.0" + +[package.extras] +docs = ["sphinx", "sphinx-rtd-theme"] +testing = ["django", "django-configurations (>=2.0)"] + +[[package]] +name = "pytest-mock" +version = "3.6.1" +description = "Thin-wrapper around the mock package for easier use with pytest" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "tox", "pytest-asyncio"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2021.3" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pyyaml" +version = "5.4.1" +description = "YAML parser and emitter for Python" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[[package]] +name = "radon" +version = "5.1.0" +description = "Code Metrics in Python" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +colorama = {version = ">=0.4.1", markers = "python_version > \"3.4\""} +future = "*" +mando = ">=0.6,<0.7" + +[[package]] +name = "requests" +version = "2.27.1" +description = "Python HTTP for Humans." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "sqlparse" +version = "0.4.2" +description = "A non-validating SQL parser." +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "text-unidecode" +version = "1.3" +description = "The most basic Text::Unidecode port" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "tomli" +version = "1.2.3" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "typing-extensions" +version = "4.0.1" +description = "Backported and Experimental Type Hints for Python 3.6+" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "urllib3" +version = "1.26.8" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +brotli = ["brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "virtualenv" +version = "20.13.0" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[package.dependencies] +distlib = ">=0.3.1,<1" +filelock = ">=3.2,<4" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""} +platformdirs = ">=2,<3" +six = ">=1.9.0,<2" + +[package.extras] +docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] + +[[package]] +name = "xenon" +version = "0.8.0" +description = "Monitor code metrics for Python on your CI server" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +PyYAML = ">=4.2b1,<6.0" +radon = ">=4,<6" +requests = ">=2.0,<3.0" + +[[package]] +name = "zipp" +version = "3.6.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.6" +content-hash = "328c2fbb8368ac6291eea5ad153598335b10550b6fcc1ea1fd6c153f08218781" + +[metadata.files] +asgiref = [ + {file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"}, + {file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"}, +] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [ + {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, + {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, +] +certifi = [ + {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, + {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, +] +cfgv = [ + {file = "cfgv-3.0.0-py2.py3-none-any.whl", hash = "sha256:f22b426ed59cd2ab2b54ff96608d846c33dfb8766a67f0b4a6ce130ce244414f"}, + {file = "cfgv-3.0.0.tar.gz", hash = "sha256:04b093b14ddf9fd4d17c53ebfd55582d27b76ed30050193c14e560770c5360eb"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.0.10.tar.gz", hash = "sha256:876d180e9d7432c5d1dfd4c5d26b72f099d503e8fcc0feb7532c9289be60fcbd"}, + {file = "charset_normalizer-2.0.10-py3-none-any.whl", hash = "sha256:cb957888737fc0bbcd78e3df769addb41fd1ff8cf950dc9e7ad7793f1bf44455"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +coverage = [ + {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"}, + {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"}, + {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"}, + {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"}, + {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"}, + {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"}, + {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"}, + {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"}, + {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"}, + {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"}, + {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"}, + {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"}, + {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"}, + {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"}, + {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"}, + {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"}, + {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"}, + {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"}, + {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"}, + {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"}, +] +distlib = [ + {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, + {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, +] +django = [ + {file = "Django-3.2.11-py3-none-any.whl", hash = "sha256:0a0a37f0b93aef30c4bf3a839c187e1175bcdeb7e177341da0cb7b8194416891"}, + {file = "Django-3.2.11.tar.gz", hash = "sha256:69c94abe5d6b1b088bf475e09b7b74403f943e34da107e798465d2045da27e75"}, +] +djangorestframework = [ + {file = "djangorestframework-3.13.1-py3-none-any.whl", hash = "sha256:24c4bf58ed7e85d1fe4ba250ab2da926d263cd57d64b03e8dcef0ac683f8b1aa"}, + {file = "djangorestframework-3.13.1.tar.gz", hash = "sha256:0c33407ce23acc68eca2a6e46424b008c9c02eceb8cf18581921d0092bc1f2ee"}, +] +factory-boy = [ + {file = "factory_boy-3.2.1-py2.py3-none-any.whl", hash = "sha256:eb02a7dd1b577ef606b75a253b9818e6f9eaf996d94449c9d5ebb124f90dc795"}, + {file = "factory_boy-3.2.1.tar.gz", hash = "sha256:a98d277b0c047c75eb6e4ab8508a7f81fb03d2cb21986f627913546ef7a2a55e"}, +] +faker = [ + {file = "Faker-11.3.0-py3-none-any.whl", hash = "sha256:61f97034cea252b8426d81810afab2f3c27b584f2b4313400a0cc83a9b013ded"}, + {file = "Faker-11.3.0.tar.gz", hash = "sha256:adbe567e64da6a1097feacab699000e1ad16e17a6592a8f0ae1ee0b7fbf19887"}, +] +filelock = [ + {file = "filelock-3.4.1-py3-none-any.whl", hash = "sha256:a4bc51381e01502a30e9f06dd4fa19a1712eab852b6fb0f84fd7cce0793d8ca3"}, + {file = "filelock-3.4.1.tar.gz", hash = "sha256:0f12f552b42b5bf60dba233710bf71337d35494fc8bdd4fd6d9f6d082ad45e06"}, +] +flake8 = [ + {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, + {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, +] +future = [ + {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, +] +identify = [ + {file = "identify-1.6.2-py2.py3-none-any.whl", hash = "sha256:8f9879b5b7cca553878d31548a419ec2f227d3328da92fe8202bc5e546d5cbc3"}, + {file = "identify-1.6.2.tar.gz", hash = "sha256:1c2014f6985ed02e62b2e6955578acf069cb2c54859e17853be474bfe7e13bed"}, +] +idna = [ + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, +] +importlib-metadata = [ + {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, + {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, +] +importlib-resources = [ + {file = "importlib_resources-5.4.0-py3-none-any.whl", hash = "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45"}, + {file = "importlib_resources-5.4.0.tar.gz", hash = "sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +mando = [ + {file = "mando-0.6.4-py2.py3-none-any.whl", hash = "sha256:4ce09faec7e5192ffc3c57830e26acba0fd6cd11e1ee81af0d4df0657463bd1c"}, + {file = "mando-0.6.4.tar.gz", hash = "sha256:79feb19dc0f097daa64a1243db578e7674909b75f88ac2220f1c065c10a0d960"}, +] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] +nodeenv = [ + {file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"}, + {file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +platformdirs = [ + {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, + {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, +] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +pre-commit = [ + {file = "pre_commit-2.1.1-py2.py3-none-any.whl", hash = "sha256:09ebe467f43ce24377f8c2f200fe3cd2570d328eb2ce0568c8e96ce19da45fa6"}, + {file = "pre_commit-2.1.1.tar.gz", hash = "sha256:f8d555e31e2051892c7f7b3ad9f620bd2c09271d87e9eedb2ad831737d6211eb"}, +] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] +pycodestyle = [ + {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, + {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, +] +pyflakes = [ + {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, + {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, +] +pyparsing = [ + {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, + {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, +] +pytest = [ + {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, + {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, +] +pytest-cov = [ + {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, + {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, +] +pytest-django = [ + {file = "pytest-django-4.5.2.tar.gz", hash = "sha256:d9076f759bb7c36939dbdd5ae6633c18edfc2902d1a69fdbefd2426b970ce6c2"}, + {file = "pytest_django-4.5.2-py3-none-any.whl", hash = "sha256:c60834861933773109334fe5a53e83d1ef4828f2203a1d6a0fa9972f4f75ab3e"}, +] +pytest-mock = [ + {file = "pytest-mock-3.6.1.tar.gz", hash = "sha256:40217a058c52a63f1042f0784f62009e976ba824c418cced42e88d5f40ab0e62"}, + {file = "pytest_mock-3.6.1-py3-none-any.whl", hash = "sha256:30c2f2cc9759e76eee674b81ea28c9f0b94f8f0445a1b87762cadf774f0df7e3"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] +pytz = [ + {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"}, + {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"}, +] +pyyaml = [ + {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, + {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, + {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, + {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, + {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, + {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, + {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, + {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, + {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, +] +radon = [ + {file = "radon-5.1.0-py2.py3-none-any.whl", hash = "sha256:fa74e018197f1fcb54578af0f675d8b8e2342bd8e0b72bef8197bc4c9e645f36"}, + {file = "radon-5.1.0.tar.gz", hash = "sha256:cb1d8752e5f862fb9e20d82b5f758cbc4fb1237c92c9a66450ea0ea7bf29aeee"}, +] +requests = [ + {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, + {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +sqlparse = [ + {file = "sqlparse-0.4.2-py3-none-any.whl", hash = "sha256:48719e356bb8b42991bdbb1e8b83223757b93789c00910a616a071910ca4a64d"}, + {file = "sqlparse-0.4.2.tar.gz", hash = "sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae"}, +] +text-unidecode = [ + {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, + {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] +tomli = [ + {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, + {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, +] +typing-extensions = [ + {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"}, + {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"}, +] +urllib3 = [ + {file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"}, + {file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"}, +] +virtualenv = [ + {file = "virtualenv-20.13.0-py2.py3-none-any.whl", hash = "sha256:339f16c4a86b44240ba7223d0f93a7887c3ca04b5f9c8129da7958447d079b09"}, + {file = "virtualenv-20.13.0.tar.gz", hash = "sha256:d8458cf8d59d0ea495ad9b34c2599487f8a7772d796f9910858376d1600dd2dd"}, +] +xenon = [ + {file = "xenon-0.8.0-py2.py3-none-any.whl", hash = "sha256:4c3d7157d9ae058364e130c831702e4a65a1f729d4b4def912418ed09772c851"}, + {file = "xenon-0.8.0.tar.gz", hash = "sha256:cd5cad0930673d0e52609712c63fe4721a8f4c4342dc338bd7ea5fa0666b8515"}, +] +zipp = [ + {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, + {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..c01e717 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,52 @@ +[tool.poetry] +name = "yoflow" +version = "1.0.1" +description = "Django workflows" +authors = ["Yoyo "] +include = [ + "LICENSE", +] +packages = [ + { include="yoflow" }, +] + +[tool.poetry.dependencies] +python = "^3.6" + +django = ">=3.2,<4.0.0" +djangorestframework = "*" +six = "*" + +[tool.poetry.dev-dependencies] +pytest-mock = "*" +pytest-django = "*" +pytest = "*" +factory-boy = "*" +xenon = "*" +flake8 = "*" +pytest-cov = "*" +pre-commit = "*" + +[tool.black] +include = '\.pyi?$' +skip-string-normalization = true +exclude = ''' +/( + \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist +)/ +''' + +[tool.pytest.ini_options] +DJANGO_SETTINGS_MODULE='tests.settings' + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 2a8c655..0000000 --- a/pytest.ini +++ /dev/null @@ -1,2 +0,0 @@ -[pytest] -DJANGO_SETTINGS_MODULE = tests.settings diff --git a/setup.cfg b/setup.cfg index b88034e..c87b6fb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,7 @@ [metadata] description-file = README.md + +[flake8] +# Recommend matching the black line length (default 88), +# rather than using the flake8 default of 79: +max-line-length = 88 diff --git a/setup.py b/setup.py deleted file mode 100644 index 021d6ba..0000000 --- a/setup.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from setuptools import setup - -with open('README.md') as f: - long_description = f.read() - -setup( - name='yoflow', - version='1.0.0', - author='Gary Evans', - author_email='dev@yoyowallet.com', - description='Django workflows', - long_description=long_description, - long_description_content_type="text/markdown", - platforms=['Any'], - keywords=['django', 'yoflow', 'workflow'], - url='http://github.com/yoyowallet/yoflow', - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Natural Language :: English', - 'Operating System :: OS Independent', - 'Framework :: Django', - 'Framework :: Django :: 1.10', - 'Framework :: Django :: 1.11', - 'Framework :: Django :: 2.0', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - ], - install_requires=[ - 'Django>=1.10' - ], - packages=[ - 'yoflow' - ], - setup_requires=[ - 'setuptools>=38.6.0' - ], - include_package_data=True, - zip_safe=False, -) diff --git a/tests/settings.py b/tests/settings.py index 81aaede..e7084c7 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -12,6 +12,8 @@ import os +from rest_framework.exceptions import APIException + # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -93,7 +95,8 @@ AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + 'NAME': 'django.contrib.auth.password_validation.' + 'UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', @@ -116,8 +119,6 @@ USE_I18N = True -USE_L10N = True - USE_TZ = True @@ -131,5 +132,5 @@ YOFLOW_STATE_MAX_LENGTH = 256 YOFLOW_OBJECT_ID_MAX_LENGTH = 256 YOFLOW_DEFAULT_STATE_FIELD = 'state' -from rest_framework.exceptions import APIException + YOFLOW_TYPE_ERROR = APIException diff --git a/tests/urls.py b/tests/urls.py index 8738909..b9a7d8b 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,8 +1,7 @@ from django.contrib import admin -from django.conf.urls import include, url - +from django.urls import include, re_path urlpatterns = [ - url('^admin/', admin.site.urls), - url('^blog/', include('example.urls')), + re_path('^admin/', admin.site.urls), + re_path('^blog/', include('example.urls')), ] diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 2d37f7a..0000000 --- a/tox.ini +++ /dev/null @@ -1,20 +0,0 @@ -[tox] -envlist = - py{27,36}-django{110,111} - py{36}-django{2} -basepython = - py27: python2.7 - py36: python3.6 - -[testenv] -deps = - factory-boy==2.10.0 - pytest==3.4.0 - pytest-django==3.1.2 - pytest-mock==1.9.0 - djangorestframework==3.8.2 - psycopg2==2.7.3.2 - django110: Django>=1.10,<1.11 - django111: Django>=1.11,<1.12 - django2: Django>=2.0,<2.1 -commands = pytest {posargs} From 561670c6ed00103f2893d7a8d3b4a1ef6f708dd9 Mon Sep 17 00:00:00 2001 From: Stuart Bradley Date: Tue, 25 Jan 2022 15:34:30 +0000 Subject: [PATCH 2/5] Pre-commit run --- example/admin.py | 4 ++-- example/flows.py | 9 +++++-- example/migrations/0001_initial.py | 20 ++++++++++++---- example/models.py | 1 + example/serializers.py | 1 + example/views.py | 13 ++++++---- tests/conftest.py | 3 +-- tests/factories.py | 1 + tests/test_decorators.py | 7 ++---- tests/test_flow.py | 7 +++--- tests/test_transition.py | 29 ++++++++++++----------- tests/test_views.py | 7 ++---- yoflow/admin.py | 35 ++++++++++++++++++++++----- yoflow/decorators.py | 2 ++ yoflow/flow.py | 19 +++++++++++---- yoflow/forms.py | 7 ++++-- yoflow/migrations/0001_initial.py | 38 +++++++++++++++++++++++++----- yoflow/models.py | 16 +++++++++---- yoflow/transition.py | 7 ++++-- 19 files changed, 159 insertions(+), 67 deletions(-) diff --git a/example/admin.py b/example/admin.py index d54cd1a..8d78bdf 100644 --- a/example/admin.py +++ b/example/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin -from yoflow.admin import FlowAdmin -from example import models, flows +from example import flows, models +from yoflow.admin import FlowAdmin @admin.register(models.Post) diff --git a/example/flows.py b/example/flows.py index faf2a4b..d3685a1 100644 --- a/example/flows.py +++ b/example/flows.py @@ -1,7 +1,7 @@ from django.core.mail import send_mail -from yoflow import flow from example import models +from yoflow import flow class PostFlow(flow.Flow): @@ -21,7 +21,12 @@ def draft_to_approved(obj, meta): @staticmethod def on_approved(obj, meta): - send_mail('Approved!', '{} was approved'.format(obj), 'from@example.com', ['to@example.com']) + send_mail( + 'Approved!', + '{} was approved'.format(obj), + 'from@example.com', + ['to@example.com'], + ) @staticmethod def all(obj, meta): diff --git a/example/migrations/0001_initial.py b/example/migrations/0001_initial.py index 87c28f3..2c5d820 100644 --- a/example/migrations/0001_initial.py +++ b/example/migrations/0001_initial.py @@ -9,17 +9,29 @@ class Migration(migrations.Migration): initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( name='Post', fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ( + 'id', + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name='ID', + ), + ), ('name', models.CharField(max_length=256)), ('content', models.TextField()), - ('state', models.IntegerField(choices=[(1, 'draft'), (2, 'approved')], default=1)), + ( + 'state', + models.IntegerField( + choices=[(1, 'draft'), (2, 'approved')], default=1 + ), + ), ], options={ 'abstract': False, diff --git a/example/models.py b/example/models.py index 66202d8..30902e5 100644 --- a/example/models.py +++ b/example/models.py @@ -1,4 +1,5 @@ from django.db import models + from yoflow.models import FlowModel diff --git a/example/serializers.py b/example/serializers.py index 82ec3e1..8ec67dc 100644 --- a/example/serializers.py +++ b/example/serializers.py @@ -1,4 +1,5 @@ from rest_framework import serializers + from example import models diff --git a/example/views.py b/example/views.py index 723552b..e5e79aa 100644 --- a/example/views.py +++ b/example/views.py @@ -1,10 +1,11 @@ from django.http import HttpResponse from django.views import View -from rest_framework import viewsets, status +from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.response import Response -from yoflow.decorators import transition + from example import flows, models, serializers +from yoflow.decorators import transition # django function based view example @@ -12,7 +13,9 @@ def approved_function_view(request, pk): obj = models.Post.objects.get(pk=pk) flow = flows.PostFlow() flow.check_permissions(obj=obj, to_state=models.Post.APPROVED) - flow.process(obj=obj, to_state=models.Post.APPROVED, request=request, meta=request.POST) + flow.process( + obj=obj, to_state=models.Post.APPROVED, request=request, meta=request.POST + ) return HttpResponse(status=status.HTTP_204_NO_CONTENT) @@ -42,4 +45,6 @@ def approved(self, request, pk=None): @action(methods=['get'], detail=True) def history(self, request, pk=None): qs = self.get_object().yoflow_history.all() - return Response(qs.values('created_at', 'new_state', 'previous_state', 'meta', 'user')) + return Response( + qs.values('created_at', 'new_state', 'previous_state', 'meta', 'user') + ) diff --git a/tests/conftest.py b/tests/conftest.py index 533a089..3df5d0a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,5 @@ import pytest -from yoflow.flow import Flow from example import flows from tests import factories @@ -14,7 +13,7 @@ def flow(): def user_client(client, django_user_model, user): user.set_password(user) user.save() - response = client.login(username=user.username, password=user.username) + client.login(username=user.username, password=user.username) yield client diff --git a/tests/factories.py b/tests/factories.py index e6c88da..66fc11d 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -7,6 +7,7 @@ class DraftPostFactory(factory.django.DjangoModelFactory): class Meta: model = models.Post + name = factory.fuzzy.FuzzyText() content = factory.fuzzy.FuzzyText() state = models.Post.DRAFT diff --git a/tests/test_decorators.py b/tests/test_decorators.py index f87d6ec..d70dd25 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -1,11 +1,8 @@ -import json import pytest from yoflow.decorators import transition from yoflow.flow import Flow -from example import models - class TestView(object): flow = Flow @@ -21,7 +18,7 @@ def view(self): def test_validate(mocker): mocked_process = mocker.patch.object(Flow, 'process') - mocked_validate = mocker.patch.object(Flow, 'validate') + mocker.patch.object(Flow, 'validate') mocked_validate = mocker.patch.object(Flow, 'check_permissions') TestView().view() assert mocked_validate.call_count == 1 @@ -36,5 +33,5 @@ def test_validate_exception(mocker): with pytest.raises(Exception): TestView().view() - + assert mocked_process.call_count == 0 diff --git a/tests/test_flow.py b/tests/test_flow.py index 8315d4b..cdd9500 100644 --- a/tests/test_flow.py +++ b/tests/test_flow.py @@ -1,12 +1,9 @@ -import json import pytest - from django.core.exceptions import PermissionDenied from django.test import override_settings -from django.urls import reverse -from yoflow.flow import Flow from example import models +from yoflow.flow import Flow def test_state_field_default(): @@ -22,8 +19,10 @@ def test_state_field_settings(): def test_state_field_class(): field = 'test' + class Test(Flow): state_field = field + assert Test().field == field diff --git a/tests/test_transition.py b/tests/test_transition.py index db8b6c5..fd5748d 100644 --- a/tests/test_transition.py +++ b/tests/test_transition.py @@ -1,14 +1,10 @@ -import json import pytest - from django.conf import settings from django.contrib.auth.models import AnonymousUser, User from django.test import override_settings -from django.test.client import RequestFactory -from rest_framework.exceptions import APIException -from yoflow.transition import Transition from example import models +from yoflow.transition import Transition @pytest.fixture @@ -31,7 +27,9 @@ def test_get_user_fallback(rf): @pytest.mark.django_db def test_get_user(rf): request = rf.request() - test_user = User.objects.create_user(username='test', email='test@example.com', password='top_secret') + test_user = User.objects.create_user( + username='test', email='test@example.com', password='top_secret' + ) request.user = test_user user = Transition.get_user(request=request) assert user == test_user @@ -49,20 +47,23 @@ def test_create_history(transition, rf): def test_create_history_meta_valid_json_object(transition, rf): request = rf.request() request.user = AnonymousUser() - data = { 'test': True } + data = {'test': True} transition.create_history(to_state=models.Post.APPROVED, request=request, meta=data) assert transition.obj.yoflow_history.count() == 1 assert transition.obj.yoflow_history.first().meta == data @pytest.mark.django_db -@pytest.mark.parametrize("data", [ - 'test', - 1, - [1, 2], - True, - False, -]) +@pytest.mark.parametrize( + "data", + [ + 'test', + 1, + [1, 2], + True, + False, + ], +) def test_create_history_meta_invalid_json_objects(transition, rf, data): with override_settings(): del settings.YOFLOW_TYPE_ERROR diff --git a/tests/test_views.py b/tests/test_views.py index 01e281c..936d60b 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -1,12 +1,9 @@ import json -import pytest +import pytest from django.conf import settings from django.test import override_settings from rest_framework import status -from rest_framework.exceptions import APIException - -from example import views urls = [ ('/blog/post/{}/approved/'), @@ -40,7 +37,7 @@ def test_approved_bad_meta(admin_client, draft_post): del settings.YOFLOW_TYPE_ERROR meta = 'bad' with pytest.raises(TypeError): - response = admin_client.post( + admin_client.post( '/blog/post/{}/approved/'.format(draft_post.id), json.dumps(meta), content_type='application/json', diff --git a/yoflow/admin.py b/yoflow/admin.py index c38ed51..b3315e0 100644 --- a/yoflow/admin.py +++ b/yoflow/admin.py @@ -10,28 +10,49 @@ class FlowMetaAdmin(admin.ModelAdmin): list_display = ('pk', 'content_type', 'previous_state', 'new_state') list_filter = ('content_type', 'created_at') - readonly_fields = ('id', 'user', 'created_at', 'previous_state', 'new_state', 'meta', 'link') + readonly_fields = ( + 'id', + 'user', + 'created_at', + 'previous_state', + 'new_state', + 'meta', + 'link', + ) exclude = ('content_type', 'object_id') def link(self, obj): app_label = obj.content_object._meta.app_label model_name = obj.content_object._meta.model_name - url = reverse('admin:{}_{}_change'.format(app_label, model_name), args=(obj.object_id,)) + url = reverse( + 'admin:{}_{}_change'.format(app_label, model_name), args=(obj.object_id,) + ) return format_html('{}'.format(url, obj.content_object)) class FlowInline(GenericTabularInline): model = models.Flow extra = 0 - exclude = ('content_type', 'meta',) - readonly_fields = ('link', 'user', 'created_at', 'previous_state', 'new_state',) + exclude = ( + 'content_type', + 'meta', + ) + readonly_fields = ( + 'link', + 'user', + 'created_at', + 'previous_state', + 'new_state', + ) ordering = ('-pk',) can_delete = False def link(self, obj): app_label = obj._meta.app_label model_name = obj._meta.model_name - url = reverse('admin:{}_{}_change'.format(app_label, model_name), args=(obj.pk,)) + url = reverse( + 'admin:{}_{}_change'.format(app_label, model_name), args=(obj.pk,) + ) return format_html('{}'.format(url, obj.pk)) def has_add_permission(self, request): @@ -54,4 +75,6 @@ def add_view(self, request, form_url='', extra_context=None): def change_view(self, request, object_id, form_url='', extra_context=None): self.inlines = [FlowInline] - return super(FlowAdmin, self).change_view(request, object_id, form_url, extra_context) + return super(FlowAdmin, self).change_view( + request, object_id, form_url, extra_context + ) diff --git a/yoflow/decorators.py b/yoflow/decorators.py index e2aef43..853592b 100644 --- a/yoflow/decorators.py +++ b/yoflow/decorators.py @@ -17,5 +17,7 @@ def wrapper(self, *args, **kwargs): # process flow logic flow.process(obj=obj, to_state=to_state, meta=meta, request=self.request) return result + return wrapper + return decorator diff --git a/yoflow/flow.py b/yoflow/flow.py index 4f3c759..28340dd 100644 --- a/yoflow/flow.py +++ b/yoflow/flow.py @@ -6,7 +6,6 @@ class Flow(object): - @property def field(self): if hasattr(self, 'state_field'): @@ -24,7 +23,9 @@ def check_permissions(self, obj, to_state): from_state = getattr(obj, self.field) valid_states = self.transitions[from_state] if to_state not in valid_states: - raise PermissionDenied('{} not in allowed states'.format(self.states[to_state])) + raise PermissionDenied( + '{} not in allowed states'.format(self.states[to_state]) + ) def process_state_to_state(self, from_state, to_state, obj, meta): state_to_state = '{}_to_{}'.format(from_state, to_state) @@ -39,7 +40,8 @@ def process_all(self, obj, meta): def validate(self, view): """ - Override to run preprocessing validation checks - e.g. raise exception if POST body invalid + Override to run preprocessing validation checks - + e.g. raise exception if POST body invalid :return: meta json """ pass @@ -47,9 +49,16 @@ def validate(self, view): @transaction.atomic def process(self, obj, to_state, request, meta=None): from_state = getattr(obj, self.field) - transition = Transition(obj=obj, state_field=self.field, from_state=from_state, states=self.states) + transition = Transition( + obj=obj, state_field=self.field, from_state=from_state, states=self.states + ) transition.transition(to_state=to_state, meta=meta, request=request) # process custom state update logic - self.process_state_to_state(from_state=self.states[from_state], to_state=self.states[to_state], obj=obj, meta=meta) + self.process_state_to_state( + from_state=self.states[from_state], + to_state=self.states[to_state], + obj=obj, + meta=meta, + ) self.process_on_state(to_state=self.states[to_state], obj=obj, meta=meta) self.process_all(obj=obj, meta=meta) diff --git a/yoflow/forms.py b/yoflow/forms.py index 6b6b027..2986dbf 100644 --- a/yoflow/forms.py +++ b/yoflow/forms.py @@ -3,14 +3,17 @@ class FlowForm(forms.ModelForm): - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # limit available choices based on current state state_field = self.flow.field current_state = getattr(self.instance, state_field) available_states = self.flow.transitions.get(current_state, []) - states = tuple((k, v) for k, v in self.flow.states.items() if k in available_states or k is current_state) + states = tuple( + (k, v) + for k, v in self.flow.states.items() + if k in available_states or k is current_state + ) if len(states) == 1: self.fields[state_field].disabled = True else: diff --git a/yoflow/migrations/0001_initial.py b/yoflow/migrations/0001_initial.py index f4491ba..6caa016 100644 --- a/yoflow/migrations/0001_initial.py +++ b/yoflow/migrations/0001_initial.py @@ -2,10 +2,10 @@ # Generated by Django 1.11 on 2018-04-19 07:35 from __future__ import unicode_literals -from django.conf import settings import django.contrib.postgres.fields.jsonb -from django.db import migrations, models import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): @@ -21,14 +21,40 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Flow', fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ( + 'id', + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name='ID', + ), + ), ('created_at', models.DateTimeField(auto_now_add=True)), ('previous_state', models.CharField(max_length=256, null=True)), ('new_state', models.CharField(max_length=256)), - ('meta', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), + ( + 'meta', + django.contrib.postgres.fields.jsonb.JSONField( + blank=True, null=True + ), + ), ('object_id', models.CharField(max_length=256)), - ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), - ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + 'content_type', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to='contenttypes.ContentType', + ), + ), + ( + 'user', + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], ), ] diff --git a/yoflow/models.py b/yoflow/models.py index 0a84faa..a494d10 100644 --- a/yoflow/models.py +++ b/yoflow/models.py @@ -8,13 +8,21 @@ @python_2_unicode_compatible class Flow(models.Model): - user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE) + user = models.ForeignKey( + settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE + ) created_at = models.DateTimeField(auto_now_add=True) - previous_state = models.CharField(max_length=getattr(settings, 'YOFLOW_STATE_MAX_LENGTH', 256), null=True) - new_state = models.CharField(max_length=getattr(settings, 'YOFLOW_STATE_MAX_LENGTH', 256)) + previous_state = models.CharField( + max_length=getattr(settings, 'YOFLOW_STATE_MAX_LENGTH', 256), null=True + ) + new_state = models.CharField( + max_length=getattr(settings, 'YOFLOW_STATE_MAX_LENGTH', 256) + ) meta = JSONField(null=True, blank=True) content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) - object_id = models.CharField(max_length=getattr(settings, 'YOFLOW_OBJECT_ID_MAX_LENGTH', 256)) + object_id = models.CharField( + max_length=getattr(settings, 'YOFLOW_OBJECT_ID_MAX_LENGTH', 256) + ) content_object = GenericForeignKey() def __str__(self): diff --git a/yoflow/transition.py b/yoflow/transition.py index 4e362e0..08a78f8 100644 --- a/yoflow/transition.py +++ b/yoflow/transition.py @@ -3,7 +3,6 @@ class Transition(object): - def __init__(self, obj, states, from_state, state_field='state'): self.obj = obj self.from_state = from_state @@ -13,7 +12,11 @@ def __init__(self, obj, states, from_state, state_field='state'): @staticmethod def get_user(request): user = request.user - is_anonymous = user.is_anonymous if isinstance(user.is_anonymous, bool) else user.is_anonymous() + is_anonymous = ( + user.is_anonymous + if isinstance(user.is_anonymous, bool) + else user.is_anonymous() + ) return None if is_anonymous else user def create_history(self, to_state, meta, request): From 7f36bab10fa612b1e7d83ccdf43776ebc9b418d7 Mon Sep 17 00:00:00 2001 From: Stuart Bradley Date: Tue, 25 Jan 2022 15:38:41 +0000 Subject: [PATCH 3/5] Add dep --- poetry.lock | 23 ++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 3b43142..174af04 100644 --- a/poetry.lock +++ b/poetry.lock @@ -326,6 +326,14 @@ pyyaml = ">=5.1" toml = "*" virtualenv = ">=15.2" +[[package]] +name = "psycopg2" +version = "2.9.3" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +category = "main" +optional = false +python-versions = ">=3.6" + [[package]] name = "py" version = "1.11.0" @@ -594,7 +602,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "328c2fbb8368ac6291eea5ad153598335b10550b6fcc1ea1fd6c153f08218781" +content-hash = "f67ce5651cb28297edc951c19e4f1c3209780bf16a14699c3485d83a6177738c" [metadata.files] asgiref = [ @@ -753,6 +761,19 @@ pre-commit = [ {file = "pre_commit-2.1.1-py2.py3-none-any.whl", hash = "sha256:09ebe467f43ce24377f8c2f200fe3cd2570d328eb2ce0568c8e96ce19da45fa6"}, {file = "pre_commit-2.1.1.tar.gz", hash = "sha256:f8d555e31e2051892c7f7b3ad9f620bd2c09271d87e9eedb2ad831737d6211eb"}, ] +psycopg2 = [ + {file = "psycopg2-2.9.3-cp310-cp310-win32.whl", hash = "sha256:083707a696e5e1c330af2508d8fab36f9700b26621ccbcb538abe22e15485362"}, + {file = "psycopg2-2.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:d3ca6421b942f60c008f81a3541e8faf6865a28d5a9b48544b0ee4f40cac7fca"}, + {file = "psycopg2-2.9.3-cp36-cp36m-win32.whl", hash = "sha256:9572e08b50aed176ef6d66f15a21d823bb6f6d23152d35e8451d7d2d18fdac56"}, + {file = "psycopg2-2.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:a81e3866f99382dfe8c15a151f1ca5fde5815fde879348fe5a9884a7c092a305"}, + {file = "psycopg2-2.9.3-cp37-cp37m-win32.whl", hash = "sha256:cb10d44e6694d763fa1078a26f7f6137d69f555a78ec85dc2ef716c37447e4b2"}, + {file = "psycopg2-2.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:4295093a6ae3434d33ec6baab4ca5512a5082cc43c0505293087b8a46d108461"}, + {file = "psycopg2-2.9.3-cp38-cp38-win32.whl", hash = "sha256:34b33e0162cfcaad151f249c2649fd1030010c16f4bbc40a604c1cb77173dcf7"}, + {file = "psycopg2-2.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:0762c27d018edbcb2d34d51596e4346c983bd27c330218c56c4dc25ef7e819bf"}, + {file = "psycopg2-2.9.3-cp39-cp39-win32.whl", hash = "sha256:8cf3878353cc04b053822896bc4922b194792df9df2f1ad8da01fb3043602126"}, + {file = "psycopg2-2.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:06f32425949bd5fe8f625c49f17ebb9784e1e4fe928b7cce72edc36fb68e4c0c"}, + {file = "psycopg2-2.9.3.tar.gz", hash = "sha256:8e841d1bf3434da985cc5ef13e6f75c8981ced601fd70cc6bf33351b91562981"}, +] py = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, diff --git a/pyproject.toml b/pyproject.toml index c01e717..fdaaed5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ python = "^3.6" django = ">=3.2,<4.0.0" djangorestframework = "*" six = "*" +psycopg2 = "*" [tool.poetry.dev-dependencies] pytest-mock = "*" From f4b81084ecb0e0332e3fc55254b54833ec837633 Mon Sep 17 00:00:00 2001 From: Stuart Bradley Date: Tue, 25 Jan 2022 15:44:46 +0000 Subject: [PATCH 4/5] Add postgres to workflow. --- .github/workflows/continuous_integration.yml | 12 +++++++++++- tests/settings.py | 4 ++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 3af911f..051b3a3 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -22,7 +22,17 @@ jobs: matrix: python: ["3.7", "3.8", "3.9", "3.10"] fail-fast: false - + services: + postgres: + image: postgres:12.5 + env: + POSTGRES_DB: yoflow + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - 5432:5432 + # needed because the postgres container does not provide a healthcheck + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - name: Check out repository uses: actions/checkout@v2 diff --git a/tests/settings.py b/tests/settings.py index e7084c7..427c1ba 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -83,8 +83,8 @@ 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'yoflow', 'USER': 'postgres', - 'PASSWORD': '', - 'HOST': '127.0.0.1', + 'PASSWORD': 'postgres', + 'HOST': 'localhost', 'POST': '5432', } } From f73d6301acea3b8d82fb87fdea8ee26a745d9c08 Mon Sep 17 00:00:00 2001 From: Stuart Bradley Date: Tue, 25 Jan 2022 15:48:58 +0000 Subject: [PATCH 5/5] Updated readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index c8d4960..0abe9d5 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,12 @@ Documentation & Example Worflow: https://yoyowallet.github.io/yoflow/ ## Install +Install via github: +``` +pip install https://github.com/yoyowallet/yoflow.git +``` +(Or similar) + Add `yoflow` to your `INSTALLED_APPS`: ```