From 1ef7e77c7e415b6bf79364e3aa1d8ee15be6b0db Mon Sep 17 00:00:00 2001 From: staticdev Date: Fri, 22 May 2020 22:29:50 -0300 Subject: [PATCH 1/5] Flake8 and fixes --- .darglint | 2 + .flake8 | 4 +- .pre-commit-config.yaml | 20 +- .travis.yml | 46 +-- noxfile.py | 17 +- poetry.lock | 364 +++++++++++++++++- pyproject.toml | 4 + runtests.py | 53 +++ src/django_pagination_bootstrap/middleware.py | 12 +- src/django_pagination_bootstrap/paginator.py | 64 ++- .../templatetags/__init__.py | 1 + .../templatetags/pagination_tags.py | 36 +- .../tests.py => tests/test_paginator.py | 66 ++-- 13 files changed, 532 insertions(+), 157 deletions(-) create mode 100644 .darglint create mode 100644 runtests.py rename src/django_pagination_bootstrap/tests.py => tests/test_paginator.py (83%) diff --git a/.darglint b/.darglint new file mode 100644 index 0000000..2b03755 --- /dev/null +++ b/.darglint @@ -0,0 +1,2 @@ +[darglint] +strictness = short diff --git a/.flake8 b/.flake8 index 891336c..acff101 100644 --- a/.flake8 +++ b/.flake8 @@ -1,6 +1,6 @@ [flake8] -select = ANN,B,B9,BLK,C,D,DAR,E,F,S,W -ignore = E203,E501,W503,C901,S308 +select = B,B9,C,E,F,N,S,W +ignore = E203,E501,W503,C901,B950 max-line-length = 80 max-complexity = 10 docstring-convention = google diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 25c5720..59a30bc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,16 +15,16 @@ repos: rev: 19.10b0 hooks: - id: black - # - repo: https://gitlab.com/pycqa/flake8 - # rev: 3.7.9 - # hooks: - # - id: flake8 - # additional_dependencies: - # - flake8-bandit==2.1.2 - # - flake8-bugbear==20.1.4 - # - flake8-docstrings==1.5.0 - # - pep8-naming==0.10.0 - # - darglint==1.2.3 + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.8.1 + hooks: + - id: flake8 + additional_dependencies: + - flake8-bandit==2.1.2 + - flake8-bugbear==20.1.4 + - flake8-docstrings==1.5.0 + - pep8-naming==0.10.0 + - darglint==1.3.0 - repo: https://github.com/asottile/reorder_python_imports rev: v2.3.0 hooks: diff --git a/.travis.yml b/.travis.yml index ae93a7b..1010bc2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,11 @@ language: python python: - - "2.7" - - "3.3" - - "3.4" - - "3.5" - "3.6" + - "3.7" + - "3.8" env: - - DJANGO="Django>=1.7.0,<1.8.0" - - DJANGO="Django>=1.8.0,<1.9.0" - - DJANGO="Django>=1.9.0,<1.10.0" - - DJANGO="Django>=1.10.0,<1.11.0" - - DJANGO="Django>=1.11.0,<2.0.0" - DJANGO="Django>=2.0.0,<2.1.0" - DJANGO="Django>=2.1.0,<2.2.0" @@ -25,38 +18,3 @@ script: after_success: - coveralls - -matrix: - exclude: - - python: "3.5" - env: DJANGO="Django>=1.7.0,<1.8.0" - - python: "3.6" - env: DJANGO="Django>=1.7.0,<1.8.0" - - - python: "3.6" - env: DJANGO="Django>=1.8.0,<1.9.0" - - - python: "3.3" - env: DJANGO="Django>=1.9.0,<1.10.0" - - python: "3.6" - env: DJANGO="Django>=1.9.0,<1.10.0" - - - python: "3.3" - env: DJANGO="Django>=1.10.0,<1.11.0" - - python: "3.6" - env: DJANGO="Django>=1.10.0,<1.11.0" - - - python: "3.3" - env: DJANGO="Django>=1.11.0,<2.0.0" - - - python: "2.7" - env: DJANGO="Django>=2.0.0,<2.1.0" - - python: "3.3" - env: DJANGO="Django>=2.0.0,<2.1.0" - - - python: "2.7" - env: DJANGO="Django>=2.1.0,<2.2.0" - - python: "3.3" - env: DJANGO="Django>=2.1.0,<2.2.0" - - python: "3.4" - env: DJANGO="Django>=2.1.0,<2.2.0" diff --git a/noxfile.py b/noxfile.py index 95edad9..2daa44d 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,6 +1,5 @@ """Nox sessions.""" import contextlib -import shutil import tempfile from pathlib import Path from typing import cast @@ -12,7 +11,7 @@ package = "django_pagination_bootstrap" python_versions = ["3.8", "3.7", "3.6"] -nox.options.sessions = "pre-commit", "safety", "mypy", "tests" +nox.options.sessions = "pre-commit", "safety", "mypy" # , "tests" locations = "src", "tests", "noxfile.py" @@ -141,8 +140,18 @@ def tests(session: Session) -> None: """Run the test suite.""" install_package(session) install(session, "coverage[toml]", "pytest") - session.run("coverage", "run", "-m", "pytest", *session.posargs) - session.run("coverage", "report") + session.run("coverage", "run", "--parallel", "-m", "pytest", *session.posargs) + session.notify("coverage") + + +@nox.session +def coverage(session: Session) -> None: + """Produce the coverage report.""" + args = session.posargs or ["report"] + install(session, "coverage[toml]") + if not session.posargs and any(Path().glob(".coverage.*")): + session.run("coverage", "combine") + session.run("coverage", *args) @nox.session(python=python_versions) diff --git a/poetry.lock b/poetry.lock index a60659d..426657d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -17,6 +17,15 @@ version = "3.2.7" [package.extras] tests = ["pytest (>=4.3.0,<4.4.0)", "pytest-asyncio (>=0.10.0,<0.11.0)"] +[[package]] +category = "dev" +description = "Atomic file writes." +marker = "sys_platform == \"win32\"" +name = "atomicwrites" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.4.0" + [[package]] category = "dev" description = "Classes Without Boilerplate" @@ -51,6 +60,14 @@ typed-ast = ">=1.4.0" [package.extras] d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] +[[package]] +category = "dev" +description = "Python package for providing Mozilla's CA Bundle." +name = "certifi" +optional = false +python-versions = "*" +version = "2020.4.5.1" + [[package]] category = "dev" description = "Validate configuration and produce human readable error messages." @@ -59,6 +76,14 @@ optional = false python-versions = ">=3.6.1" version = "3.1.0" +[[package]] +category = "dev" +description = "Universal encoding detector for Python 2 and 3" +name = "chardet" +optional = false +python-versions = "*" +version = "3.0.4" + [[package]] category = "dev" description = "Composable command line interface toolkit" @@ -67,6 +92,31 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "7.1.2" +[[package]] +category = "dev" +description = "Cross-platform colored terminal text." +marker = "sys_platform == \"win32\"" +name = "colorama" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.4.3" + +[[package]] +category = "dev" +description = "Code coverage measurement for Python" +name = "coverage" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +version = "5.1" + +[package.dependencies] +[package.dependencies.toml] +optional = true +version = "*" + +[package.extras] +toml = ["toml"] + [[package]] category = "dev" description = "Distribution utilities" @@ -92,6 +142,22 @@ sqlparse = ">=0.2.2" argon2 = ["argon2-cffi (>=16.1.0)"] bcrypt = ["bcrypt"] +[[package]] +category = "dev" +description = "A parser for Python dependency files" +name = "dparse" +optional = false +python-versions = ">=3.5" +version = "0.5.1" + +[package.dependencies] +packaging = "*" +pyyaml = "*" +toml = "*" + +[package.extras] +pipenv = ["pipenv"] + [[package]] category = "dev" description = "A platform independent file lock." @@ -111,6 +177,14 @@ version = "1.4.16" [package.extras] license = ["editdistance"] +[[package]] +category = "dev" +description = "Internationalized Domain Names in Applications (IDNA)" +name = "idna" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.9" + [[package]] category = "main" description = "Read metadata from Python packages" @@ -148,6 +222,38 @@ version = ">=0.4" [package.extras] docs = ["sphinx", "rst.linker", "jaraco.packaging"] +[[package]] +category = "dev" +description = "More routines for operating on iterables, beyond itertools" +name = "more-itertools" +optional = false +python-versions = ">=3.5" +version = "8.3.0" + +[[package]] +category = "dev" +description = "Optional static typing for Python" +name = "mypy" +optional = false +python-versions = ">=3.5" +version = "0.770" + +[package.dependencies] +mypy-extensions = ">=0.4.3,<0.5.0" +typed-ast = ">=1.4.0,<1.5.0" +typing-extensions = ">=3.7.4" + +[package.extras] +dmypy = ["psutil (>=4.0)"] + +[[package]] +category = "dev" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +name = "mypy-extensions" +optional = false +python-versions = "*" +version = "0.4.3" + [[package]] category = "dev" description = "Node.js virtual environment builder" @@ -156,6 +262,18 @@ optional = false python-versions = "*" version = "1.3.5" +[[package]] +category = "dev" +description = "Core utilities for Python packages" +name = "packaging" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "20.4" + +[package.dependencies] +pyparsing = ">=2.0.2" +six = "*" + [[package]] category = "dev" description = "Utility library for gitignore style pattern matching of file paths." @@ -164,6 +282,22 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "0.8.0" +[[package]] +category = "dev" +description = "plugin and hook calling mechanisms for python" +name = "pluggy" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.13.1" + +[package.dependencies] +[package.dependencies.importlib-metadata] +python = "<3.8" +version = ">=0.12" + +[package.extras] +dev = ["pre-commit", "tox"] + [[package]] category = "dev" description = "A framework for managing and maintaining multi-language pre-commit hooks." @@ -188,6 +322,48 @@ version = "*" python = "<3.7" version = "*" +[[package]] +category = "dev" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +name = "py" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.8.1" + +[[package]] +category = "dev" +description = "Python parsing module" +name = "pyparsing" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "2.4.7" + +[[package]] +category = "dev" +description = "pytest: simple powerful testing with Python" +name = "pytest" +optional = false +python-versions = ">=3.5" +version = "5.4.2" + +[package.dependencies] +atomicwrites = ">=1.0" +attrs = ">=17.4.0" +colorama = "*" +more-itertools = ">=4.0.0" +packaging = "*" +pluggy = ">=0.12,<1.0" +py = ">=1.5.0" +wcwidth = "*" + +[package.dependencies.importlib-metadata] +python = "<3.8" +version = ">=0.12" + +[package.extras] +checkqa-mypy = ["mypy (v0.761)"] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + [[package]] category = "main" description = "World timezone definitions, modern and historical" @@ -201,7 +377,7 @@ category = "dev" description = "YAML parser and emitter for Python" name = "pyyaml" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "*" version = "5.3.1" [[package]] @@ -212,6 +388,39 @@ optional = false python-versions = "*" version = "2020.5.14" +[[package]] +category = "dev" +description = "Python HTTP for Humans." +name = "requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.23.0" + +[package.dependencies] +certifi = ">=2017.4.17" +chardet = ">=3.0.2,<4" +idna = ">=2.5,<3" +urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" + +[package.extras] +security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] + +[[package]] +category = "dev" +description = "Checks installed dependencies for known vulnerabilities." +name = "safety" +optional = false +python-versions = ">=3.5" +version = "1.9.0" + +[package.dependencies] +Click = ">=6.0" +dparse = ">=0.5.1" +packaging = "*" +requests = "*" +setuptools = "*" + [[package]] category = "dev" description = "Python 2 and 3 compatibility utilities" @@ -244,6 +453,27 @@ optional = false python-versions = "*" version = "1.4.1" +[[package]] +category = "dev" +description = "Backported and Experimental Type Hints for Python 3.5+" +name = "typing-extensions" +optional = false +python-versions = "*" +version = "3.7.4.2" + +[[package]] +category = "dev" +description = "HTTP library with thread-safe connection pooling, file post, and more." +name = "urllib3" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +version = "1.25.9" + +[package.extras] +brotli = ["brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] + [[package]] category = "dev" description = "Virtual Python Environment builder" @@ -270,6 +500,14 @@ version = ">=1.0,<2" docs = ["sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)", "proselint (>=0.10.2)"] testing = ["pytest (>=4)", "coverage (>=5)", "coverage-enable-subprocess (>=1)", "pytest-xdist (>=1.31.0)", "pytest-mock (>=2)", "pytest-env (>=0.6.2)", "pytest-randomly (>=1)", "pytest-timeout", "packaging (>=20.0)", "xonsh (>=0.9.16)"] +[[package]] +category = "dev" +description = "Measures number of Terminal column cells of wide-character codes" +name = "wcwidth" +optional = false +python-versions = "*" +version = "0.1.9" + [[package]] category = "main" description = "Backport of pathlib-compatible object wrapper for zip files" @@ -284,7 +522,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["jaraco.itertools", "func-timeout"] [metadata] -content-hash = "14d689979d62ef5a9d78f91e97e6b34a10295d330ce3d36b74d2ebb26a9f3114" +content-hash = "a681af778117c7e6e80db0a73365a046b048da8d01e593da4f4f09cc62abf2c2" python-versions = "^3.6.1" [metadata.files] @@ -296,6 +534,10 @@ asgiref = [ {file = "asgiref-3.2.7-py2.py3-none-any.whl", hash = "sha256:9ca8b952a0a9afa61d30aa6d3d9b570bb3fd6bafcf7ec9e6bed43b936133db1c"}, {file = "asgiref-3.2.7.tar.gz", hash = "sha256:8036f90603c54e93521e5777b2b9a39ba1bad05773fcf2d208f0299d1df58ce5"}, ] +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-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, @@ -304,14 +546,59 @@ black = [ {file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"}, {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"}, ] +certifi = [ + {file = "certifi-2020.4.5.1-py2.py3-none-any.whl", hash = "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304"}, + {file = "certifi-2020.4.5.1.tar.gz", hash = "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"}, +] cfgv = [ {file = "cfgv-3.1.0-py2.py3-none-any.whl", hash = "sha256:1ccf53320421aeeb915275a196e23b3b8ae87dea8ac6698b1638001d4a486d53"}, {file = "cfgv-3.1.0.tar.gz", hash = "sha256:c8e8f552ffcc6194f4e18dd4f68d9aef0c0d58ae7e7be8c82bee3c5e9edfa513"}, ] +chardet = [ + {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, + {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, +] click = [ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, ] +colorama = [ + {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, + {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, +] +coverage = [ + {file = "coverage-5.1-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:0cb4be7e784dcdc050fc58ef05b71aa8e89b7e6636b99967fadbdba694cf2b65"}, + {file = "coverage-5.1-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:c317eaf5ff46a34305b202e73404f55f7389ef834b8dbf4da09b9b9b37f76dd2"}, + {file = "coverage-5.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b83835506dfc185a319031cf853fa4bb1b3974b1f913f5bb1a0f3d98bdcded04"}, + {file = "coverage-5.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5f2294dbf7875b991c381e3d5af2bcc3494d836affa52b809c91697449d0eda6"}, + {file = "coverage-5.1-cp27-cp27m-win32.whl", hash = "sha256:de807ae933cfb7f0c7d9d981a053772452217df2bf38e7e6267c9cbf9545a796"}, + {file = "coverage-5.1-cp27-cp27m-win_amd64.whl", hash = "sha256:bf9cb9a9fd8891e7efd2d44deb24b86d647394b9705b744ff6f8261e6f29a730"}, + {file = "coverage-5.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:acf3763ed01af8410fc36afea23707d4ea58ba7e86a8ee915dfb9ceff9ef69d0"}, + {file = "coverage-5.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:dec5202bfe6f672d4511086e125db035a52b00f1648d6407cc8e526912c0353a"}, + {file = "coverage-5.1-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:7a5bdad4edec57b5fb8dae7d3ee58622d626fd3a0be0dfceda162a7035885ecf"}, + {file = "coverage-5.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1601e480b9b99697a570cea7ef749e88123c04b92d84cedaa01e117436b4a0a9"}, + {file = "coverage-5.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:dbe8c6ae7534b5b024296464f387d57c13caa942f6d8e6e0346f27e509f0f768"}, + {file = "coverage-5.1-cp35-cp35m-win32.whl", hash = "sha256:a027ef0492ede1e03a8054e3c37b8def89a1e3c471482e9f046906ba4f2aafd2"}, + {file = "coverage-5.1-cp35-cp35m-win_amd64.whl", hash = "sha256:0e61d9803d5851849c24f78227939c701ced6704f337cad0a91e0972c51c1ee7"}, + {file = "coverage-5.1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:2d27a3f742c98e5c6b461ee6ef7287400a1956c11421eb574d843d9ec1f772f0"}, + {file = "coverage-5.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:66460ab1599d3cf894bb6baee8c684788819b71a5dc1e8fa2ecc152e5d752019"}, + {file = "coverage-5.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5c542d1e62eece33c306d66fe0a5c4f7f7b3c08fecc46ead86d7916684b36d6c"}, + {file = "coverage-5.1-cp36-cp36m-win32.whl", hash = "sha256:2742c7515b9eb368718cd091bad1a1b44135cc72468c731302b3d641895b83d1"}, + {file = "coverage-5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:dead2ddede4c7ba6cb3a721870f5141c97dc7d85a079edb4bd8d88c3ad5b20c7"}, + {file = "coverage-5.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:01333e1bd22c59713ba8a79f088b3955946e293114479bbfc2e37d522be03355"}, + {file = "coverage-5.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489"}, + {file = "coverage-5.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:adeb4c5b608574a3d647011af36f7586811a2c1197c861aedb548dd2453b41cd"}, + {file = "coverage-5.1-cp37-cp37m-win32.whl", hash = "sha256:782caea581a6e9ff75eccda79287daefd1d2631cc09d642b6ee2d6da21fc0a4e"}, + {file = "coverage-5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:00f1d23f4336efc3b311ed0d807feb45098fc86dee1ca13b3d6768cdab187c8a"}, + {file = "coverage-5.1-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:402e1744733df483b93abbf209283898e9f0d67470707e3c7516d84f48524f55"}, + {file = "coverage-5.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a3f3654d5734a3ece152636aad89f58afc9213c6520062db3978239db122f03c"}, + {file = "coverage-5.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6402bd2fdedabbdb63a316308142597534ea8e1895f4e7d8bf7476c5e8751fef"}, + {file = "coverage-5.1-cp38-cp38-win32.whl", hash = "sha256:8fa0cbc7ecad630e5b0f4f35b0f6ad419246b02bc750de7ac66db92667996d24"}, + {file = "coverage-5.1-cp38-cp38-win_amd64.whl", hash = "sha256:79a3cfd6346ce6c13145731d39db47b7a7b859c0272f02cdb89a3bdcbae233a0"}, + {file = "coverage-5.1-cp39-cp39-win32.whl", hash = "sha256:a82b92b04a23d3c8a581fc049228bafde988abacba397d57ce95fe95e0338ab4"}, + {file = "coverage-5.1-cp39-cp39-win_amd64.whl", hash = "sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e"}, + {file = "coverage-5.1.tar.gz", hash = "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052"}, +] distlib = [ {file = "distlib-0.3.0.zip", hash = "sha256:2e166e231a26b36d6dfe35a48c4464346620f8645ed0ace01ee31822b288de21"}, ] @@ -319,6 +606,10 @@ django = [ {file = "Django-3.0.6-py3-none-any.whl", hash = "sha256:051ba55d42daa3eeda3944a8e4df2bc96d4c62f94316dea217248a22563c3621"}, {file = "Django-3.0.6.tar.gz", hash = "sha256:9aaa6a09678e1b8f0d98a948c56482eac3e3dd2ddbfb8de70a868135ef3b5e01"}, ] +dparse = [ + {file = "dparse-0.5.1-py3-none-any.whl", hash = "sha256:e953a25e44ebb60a5c6efc2add4420c177f1d8404509da88da9729202f306994"}, + {file = "dparse-0.5.1.tar.gz", hash = "sha256:a1b5f169102e1c894f9a7d5ccf6f9402a836a5d24be80a986c7ce9eaed78f367"}, +] filelock = [ {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, @@ -327,6 +618,10 @@ identify = [ {file = "identify-1.4.16-py2.py3-none-any.whl", hash = "sha256:0f3c3aac62b51b86fea6ff52fe8ff9e06f57f10411502443809064d23e16f1c2"}, {file = "identify-1.4.16.tar.gz", hash = "sha256:f9ad3d41f01e98eb066b6e05c5b184fd1e925fadec48eb165b4e01c72a1ef3a7"}, ] +idna = [ + {file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"}, + {file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"}, +] importlib-metadata = [ {file = "importlib_metadata-1.6.0-py2.py3-none-any.whl", hash = "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f"}, {file = "importlib_metadata-1.6.0.tar.gz", hash = "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e"}, @@ -335,17 +630,61 @@ importlib-resources = [ {file = "importlib_resources-1.5.0-py2.py3-none-any.whl", hash = "sha256:85dc0b9b325ff78c8bef2e4ff42616094e16b98ebd5e3b50fe7e2f0bbcdcde49"}, {file = "importlib_resources-1.5.0.tar.gz", hash = "sha256:6f87df66833e1942667108628ec48900e02a4ab4ad850e25fbf07cb17cf734ca"}, ] +more-itertools = [ + {file = "more-itertools-8.3.0.tar.gz", hash = "sha256:558bb897a2232f5e4f8e2399089e35aecb746e1f9191b6584a151647e89267be"}, + {file = "more_itertools-8.3.0-py3-none-any.whl", hash = "sha256:7818f596b1e87be009031c7653d01acc46ed422e6656b394b0f765ce66ed4982"}, +] +mypy = [ + {file = "mypy-0.770-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:a34b577cdf6313bf24755f7a0e3f3c326d5c1f4fe7422d1d06498eb25ad0c600"}, + {file = "mypy-0.770-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:86c857510a9b7c3104cf4cde1568f4921762c8f9842e987bc03ed4f160925754"}, + {file = "mypy-0.770-cp35-cp35m-win_amd64.whl", hash = "sha256:a8ffcd53cb5dfc131850851cc09f1c44689c2812d0beb954d8138d4f5fc17f65"}, + {file = "mypy-0.770-cp36-cp36m-macosx_10_6_x86_64.whl", hash = "sha256:7687f6455ec3ed7649d1ae574136835a4272b65b3ddcf01ab8704ac65616c5ce"}, + {file = "mypy-0.770-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3beff56b453b6ef94ecb2996bea101a08f1f8a9771d3cbf4988a61e4d9973761"}, + {file = "mypy-0.770-cp36-cp36m-win_amd64.whl", hash = "sha256:15b948e1302682e3682f11f50208b726a246ab4e6c1b39f9264a8796bb416aa2"}, + {file = "mypy-0.770-cp37-cp37m-macosx_10_6_x86_64.whl", hash = "sha256:b90928f2d9eb2f33162405f32dde9f6dcead63a0971ca8a1b50eb4ca3e35ceb8"}, + {file = "mypy-0.770-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c56ffe22faa2e51054c5f7a3bc70a370939c2ed4de308c690e7949230c995913"}, + {file = "mypy-0.770-cp37-cp37m-win_amd64.whl", hash = "sha256:8dfb69fbf9f3aeed18afffb15e319ca7f8da9642336348ddd6cab2713ddcf8f9"}, + {file = "mypy-0.770-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:219a3116ecd015f8dca7b5d2c366c973509dfb9a8fc97ef044a36e3da66144a1"}, + {file = "mypy-0.770-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7ec45a70d40ede1ec7ad7f95b3c94c9cf4c186a32f6bacb1795b60abd2f9ef27"}, + {file = "mypy-0.770-cp38-cp38-win_amd64.whl", hash = "sha256:f91c7ae919bbc3f96cd5e5b2e786b2b108343d1d7972ea130f7de27fdd547cf3"}, + {file = "mypy-0.770-py3-none-any.whl", hash = "sha256:3b1fc683fb204c6b4403a1ef23f0b1fac8e4477091585e0c8c54cbdf7d7bb164"}, + {file = "mypy-0.770.tar.gz", hash = "sha256:8a627507ef9b307b46a1fea9513d5c98680ba09591253082b4c48697ba05a4ae"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] nodeenv = [ {file = "nodeenv-1.3.5-py2.py3-none-any.whl", hash = "sha256:5b2438f2e42af54ca968dd1b374d14a1194848955187b0e5e4be1f73813a5212"}, ] +packaging = [ + {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, + {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, +] pathspec = [ {file = "pathspec-0.8.0-py2.py3-none-any.whl", hash = "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0"}, {file = "pathspec-0.8.0.tar.gz", hash = "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"}, ] +pluggy = [ + {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, + {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, +] pre-commit = [ {file = "pre_commit-2.4.0-py2.py3-none-any.whl", hash = "sha256:5559e09afcac7808933951ffaf4ff9aac524f31efbc3f24d021540b6c579813c"}, {file = "pre_commit-2.4.0.tar.gz", hash = "sha256:703e2e34cbe0eedb0d319eff9f7b83e2022bb5a3ab5289a6a8841441076514d0"}, ] +py = [ + {file = "py-1.8.1-py2.py3-none-any.whl", hash = "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"}, + {file = "py-1.8.1.tar.gz", hash = "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa"}, +] +pyparsing = [ + {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, + {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, +] +pytest = [ + {file = "pytest-5.4.2-py3-none-any.whl", hash = "sha256:95c710d0a72d91c13fae35dce195633c929c3792f54125919847fdcdf7caa0d3"}, + {file = "pytest-5.4.2.tar.gz", hash = "sha256:eb2b5e935f6a019317e455b6da83dd8650ac9ffd2ee73a7b657a30873d67a698"}, +] pytz = [ {file = "pytz-2020.1-py2.py3-none-any.whl", hash = "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed"}, {file = "pytz-2020.1.tar.gz", hash = "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"}, @@ -386,6 +725,14 @@ regex = [ {file = "regex-2020.5.14-cp38-cp38-win_amd64.whl", hash = "sha256:7e61be8a2900897803c293247ef87366d5df86bf701083b6c43119c7c6c99108"}, {file = "regex-2020.5.14.tar.gz", hash = "sha256:ce450ffbfec93821ab1fea94779a8440e10cf63819be6e176eb1973a6017aff5"}, ] +requests = [ + {file = "requests-2.23.0-py2.py3-none-any.whl", hash = "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee"}, + {file = "requests-2.23.0.tar.gz", hash = "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"}, +] +safety = [ + {file = "safety-1.9.0-py2.py3-none-any.whl", hash = "sha256:86c1c4a031fe35bd624fce143fbe642a0234d29f7cbf7a9aa269f244a955b087"}, + {file = "safety-1.9.0.tar.gz", hash = "sha256:23bf20690d4400edc795836b0c983c2b4cbbb922233108ff925b7dd7750f00c9"}, +] six = [ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, @@ -421,10 +768,23 @@ typed-ast = [ {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, ] +typing-extensions = [ + {file = "typing_extensions-3.7.4.2-py2-none-any.whl", hash = "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"}, + {file = "typing_extensions-3.7.4.2-py3-none-any.whl", hash = "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5"}, + {file = "typing_extensions-3.7.4.2.tar.gz", hash = "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae"}, +] +urllib3 = [ + {file = "urllib3-1.25.9-py2.py3-none-any.whl", hash = "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"}, + {file = "urllib3-1.25.9.tar.gz", hash = "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527"}, +] virtualenv = [ {file = "virtualenv-20.0.21-py2.py3-none-any.whl", hash = "sha256:a730548b27366c5e6cbdf6f97406d861cccece2e22275e8e1a757aeff5e00c70"}, {file = "virtualenv-20.0.21.tar.gz", hash = "sha256:a116629d4e7f4d03433b8afa27f43deba09d48bc48f5ecefa4f015a178efb6cf"}, ] +wcwidth = [ + {file = "wcwidth-0.1.9-py2.py3-none-any.whl", hash = "sha256:cafe2186b3c009a04067022ce1dcd79cb38d8d65ee4f4791b8888d6599d1bbe1"}, + {file = "wcwidth-0.1.9.tar.gz", hash = "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1"}, +] zipp = [ {file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"}, {file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"}, diff --git a/pyproject.toml b/pyproject.toml index c568f66..642b09e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,10 @@ importlib_metadata = {version = "^1.6.0", python = "<3.8"} [tool.poetry.dev-dependencies] black = "^19.10b0" pre-commit = "^2.4.0" +pytest = "^5.4.2" +coverage = {extras = ["toml"], version = "^5.1"} +safety = "^1.9.0" +mypy = "^0.770" [build-system] requires = ["poetry>=0.12"] diff --git a/runtests.py b/runtests.py new file mode 100644 index 0000000..6ffdef4 --- /dev/null +++ b/runtests.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +"""Test run script.""" +import os +import sys + +import django +from django.conf import settings +from django.test.utils import get_runner + +APP_NAME = "django_pagination_bootstrap" + +# set TEMPLATE_CONTEXT_PROCESSORS or TEMPLATES, based on django version +# http://stackoverflow.com/a/16805125/4126114 +settings.configure( + DEBUG=True, + DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3"}}, + USE_TZ=True, + MIDDLEWARE=["pagination_bootstrap.middleware.PaginationMiddleware"], + SITE_ID=1, + INSTALLED_APPS=( + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.sites", + APP_NAME, + ), + TEMPLATES=[ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [os.path.join(os.path.dirname(__file__), "templates")], + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.template.context_processors.i18n", + "django.template.context_processors.media", + ], + "loaders": [ + "django.template.loaders.filesystem.Loader", + "django.template.loaders.app_directories.Loader", + ], + }, + } + ], +) + +django.setup() +TestRunner = get_runner(settings) +test_runner = TestRunner() +failures = test_runner.run_tests([APP_NAME]) +if failures: + sys.exit(failures) diff --git a/src/django_pagination_bootstrap/middleware.py b/src/django_pagination_bootstrap/middleware.py index a1b989b..1ec7196 100644 --- a/src/django_pagination_bootstrap/middleware.py +++ b/src/django_pagination_bootstrap/middleware.py @@ -1,9 +1,4 @@ -try: - from django.utils.deprecation import MiddlewareMixin - - object = MiddlewareMixin -except: - pass +"""django-pagination-bootstrap middleware.""" def get_page(self): @@ -17,8 +12,9 @@ def get_page(self): return 1 -class PaginationMiddleware(object): - """ +class PaginationMiddleware: + """Pagination Middleware class. + Inserts a variable representing the current page onto the request object if it exists in either **GET** or **POST** portions of the request. """ diff --git a/src/django_pagination_bootstrap/paginator.py b/src/django_pagination_bootstrap/paginator.py index 061c15f..c59a16e 100644 --- a/src/django_pagination_bootstrap/paginator.py +++ b/src/django_pagination_bootstrap/paginator.py @@ -1,3 +1,4 @@ +"""paginator classes.""" from django.core.paginator import EmptyPage from django.core.paginator import Page from django.core.paginator import PageNotAnInteger @@ -5,9 +6,9 @@ class InfinitePaginator(Paginator): - """ - Paginator designed for cases when it's not important to know how many total - pages. This is useful for any object_list that has no count() method or can + """Paginator designed for when it's not important to know how many total pages. + + This is useful for any object_list that has no count() method or can be used to improve performance for MySQL by removing counts. The orphans parameter has been removed for simplicity and there's a link @@ -21,6 +22,7 @@ def __init__( allow_empty_first_page=True, link_template="/page/%d/", ): + """Constructor.""" orphans = 0 # no orphans super(InfinitePaginator, self).__init__( object_list, per_page, orphans, allow_empty_first_page @@ -28,15 +30,13 @@ def __init__( try: # no count or num pages del self._num_pages, self._count - except: + except NameError: pass # bonus links self.link_template = link_template def validate_number(self, number): - """ - Validates the given 1-based page number. - """ + """Validates the given 1-based page number.""" try: number = int(number) except ValueError: @@ -62,25 +62,21 @@ def page(self, number): return InfinitePage(page_items, number, self) def _get_count(self): - """ - Returns the total number of objects, across all pages. - """ + """Returns the total number of objects, across all pages.""" raise NotImplementedError count = property(_get_count) def _get_num_pages(self): - """ - Returns the total number of pages. - """ + """Returns the total number of pages.""" raise NotImplementedError num_pages = property(_get_num_pages) def _get_page_range(self): - """ - Returns a 1-based range of pages for iterating through within - a template for loop. + """Returns a 1-based range of pages. + + It is used for iterating through within a template for loop. """ raise NotImplementedError @@ -92,21 +88,17 @@ def __repr__(self): return "" % self.number def has_next(self): - """ - Checks for one more item than last on this page. - """ + """Checks for one more item than last on this page.""" try: - next_item = self.paginator.object_list[ - self.number * self.paginator.per_page - ] + self.paginator.object_list[self.number * self.paginator.per_page] except IndexError: return False return True def end_index(self): - """ - Returns the 1-based index of the last object on this page, - relative to total objects found (hits). + """Returns the 1-based index of the last object on this page. + + This index is relative to total objects found (hits). """ return (self.number - 1) * self.paginator.per_page + len(self.object_list) @@ -123,8 +115,7 @@ def previous_link(self): class FinitePaginator(InfinitePaginator): - """ - Paginator for cases when the list of items is already finite. + """Paginator for cases when the list of items is already finite. A good example is a list generated from an API call. This is a subclass of InfinitePaginator because we have no idea how many items exist in the @@ -148,6 +139,7 @@ def __init__( allow_empty_first_page=True, link_template="/page/%d/", ): + """Constructor.""" super(FinitePaginator, self).__init__( object_list_plus, per_page, allow_empty_first_page, link_template ) @@ -164,9 +156,7 @@ def validate_number(self, number): return number def page(self, number): - """ - Returns a Page object for the given 1-based page number. - """ + """Returns a Page object for the given 1-based page number.""" number = self.validate_number(number) # remove the extra item(s) when creating the page page_items = self.object_list[: self.per_page] @@ -175,19 +165,17 @@ def page(self, number): class FinitePage(InfinitePage): def has_next(self): - """ - Checks for one more item than last on this page. - """ + """Checks for one more item than last on this page.""" try: - next_item = self.paginator.object_list[self.paginator.per_page] + self.paginator.object_list[self.paginator.per_page] except IndexError: return False return True def start_index(self): + """Returns the 1-based index of the first object on this page. + + This index is relative to total objects in the paginator. """ - Returns the 1-based index of the first object on this page, - relative to total objects in the paginator. - """ - ## TODO should this holler if you haven't defined the offset? + # TODO should this holler if you haven't defined the offset? return self.paginator.offset diff --git a/src/django_pagination_bootstrap/templatetags/__init__.py b/src/django_pagination_bootstrap/templatetags/__init__.py index e69de29..77c633c 100644 --- a/src/django_pagination_bootstrap/templatetags/__init__.py +++ b/src/django_pagination_bootstrap/templatetags/__init__.py @@ -0,0 +1 @@ +"""django-pagination-bootstrap templatetags.""" diff --git a/src/django_pagination_bootstrap/templatetags/pagination_tags.py b/src/django_pagination_bootstrap/templatetags/pagination_tags.py index 969c98a..1cc3577 100644 --- a/src/django_pagination_bootstrap/templatetags/pagination_tags.py +++ b/src/django_pagination_bootstrap/templatetags/pagination_tags.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- +"""django-bootstrap-pagination tags.""" from django import template from django.conf import settings from django.core.paginator import EmptyPage from django.core.paginator import InvalidPage -from django.core.paginator import PageNotAnInteger from django.core.paginator import Paginator from django.http import Http404 @@ -18,9 +18,7 @@ @register.tag(name="autopaginate") def do_autopaginate(parser, token): - """ - Splits the arguments to the autopaginate tag and formats them correctly. - """ + """Splits the arguments to the autopaginate tag and formats them correctly.""" split = token.split_contents() as_index = None context_var = None @@ -60,8 +58,7 @@ def do_autopaginate(parser, token): class AutoPaginateNode(template.Node): - """ - Emits the required objects to allow for Digg-style pagination. + """Emits the required objects to allow for Digg-style pagination. First, it looks in the current context for the variable specified, and using that object, it emits a simple ``Paginator`` and the current page object @@ -84,6 +81,7 @@ def __init__( orphans=DEFAULT_ORPHANS, context_var=None, ): + """Constructor.""" self.queryset_var = template.Variable(queryset_var) if isinstance(paginate_by, int): self.paginate_by = paginate_by @@ -145,30 +143,30 @@ def render(self, context): def paginate(context, window=DEFAULT_WINDOW, hashtag=""): - """ - Renders the ``pagination.html`` template, resulting in a - Digg-like display of the available pages, given the current page. If there - are too many pages to be displayed before and after the current page, then + """Renders the ``pagination.html`` template. + + The result is a Digg-like display of the available pages, given the current page. + If there are too many pages to be displayed before and after the current page, then elipses will be used to indicate the undisplayed gap between page numbers. Requires one argument, ``context``, which should be a dictionary-like data structure and must contain the following keys: ``paginator`` - A ``Paginator`` or ``QuerySetPaginator`` object. + A ``Paginator`` or ``QuerySetPaginator`` object. ``page_obj`` - This should be the result of calling the page method on the - aforementioned ``Paginator`` or ``QuerySetPaginator`` object, given - the current page. + This should be the result of calling the page method on the + aforementioned ``Paginator`` or ``QuerySetPaginator`` object, given + the current page. This same ``context`` dictionary-like data structure may also include: ``getvars`` - A dictionary of all of the **GET** parameters in the current request. - This is useful to maintain certain types of state, even when requesting - a different page. - """ + A dictionary of all of the **GET** parameters in the current request. + This is useful to maintain certain types of state, even when requesting + a different page. + """ try: paginator = context["paginator"] page_obj = context["page_obj"] @@ -261,7 +259,7 @@ def paginate(context, window=DEFAULT_WINDOW, hashtag=""): else: to_return["getvars"] = "" return to_return - except KeyError as AttributeError: + except KeyError: return {} diff --git a/src/django_pagination_bootstrap/tests.py b/tests/test_paginator.py similarity index 83% rename from src/django_pagination_bootstrap/tests.py rename to tests/test_paginator.py index c872911..f016aad 100644 --- a/src/django_pagination_bootstrap/tests.py +++ b/tests/test_paginator.py @@ -1,73 +1,75 @@ -try: - from StringIO import StringIO -except ImportError: - from io import StringIO +import io from django.core.handlers.wsgi import WSGIRequest from django.core.paginator import Paginator -from django.template import Template, Context +from django.http import HttpRequest as DjangoHttpRequest +from django.template import Context +from django.template import Template from django.test import TestCase -from .middleware import PaginationMiddleware - -from .paginator import InfinitePaginator, InfinitePage, FinitePaginator, FinitePage - -from .templatetags.pagination_tags import paginate +from django_pagination_bootstrap import middleware +from django_pagination_bootstrap import paginator +from django_pagination_bootstrap.templatetags import pagination_tags class TestPaginator(TestCase): def test_page_obj_one(self): p = Paginator(range(15), 2) - pg = paginate({"paginator": p, "page_obj": p.page(1)}) + pg = pagination_tags.paginate({"paginator": p, "page_obj": p.page(1)}) self.assertEqual(pg["pages"], [1, 2, 3, 4, 5, 6, 7, 8]) self.assertEqual(pg["records"]["first"], 1) self.assertEqual(pg["records"]["last"], 2) def test_page_obj_eight(self): p = Paginator(range(15), 2) - pg = paginate({"paginator": p, "page_obj": p.page(8)}) + pg = pagination_tags.paginate({"paginator": p, "page_obj": p.page(8)}) self.assertEqual(pg["pages"], [1, 2, 3, 4, 5, 6, 7, 8]) self.assertEqual(pg["records"]["first"], 15) self.assertEqual(pg["records"]["last"], 15) def test_pages_list(self): p = Paginator(range(17), 2) - pages = paginate({"paginator": p, "page_obj": p.page(1)})["pages"] + pages = pagination_tags.paginate({"paginator": p, "page_obj": p.page(1)})[ + "pages" + ] self.assertEqual(pages, [1, 2, 3, 4, 5, 6, 7, 8, 9]) def test_truncated_pages_list(self): p = Paginator(range(19), 2) - pages = paginate({"paginator": p, "page_obj": p.page(1)})["pages"] + pages = pagination_tags.paginate({"paginator": p, "page_obj": p.page(1)})[ + "pages" + ] self.assertEqual(pages, [1, 2, 3, 4, None, 7, 8, 9, 10]) def test_longer_truncated_pages_list(self): p = Paginator(range(21), 2) - pages = paginate({"paginator": p, "page_obj": p.page(1)})["pages"] + pages = pagination_tags.paginate({"paginator": p, "page_obj": p.page(1)})[ + "pages" + ] self.assertEqual(pages, [1, 2, 3, 4, None, 8, 9, 10, 11]) def test_orphaned_page_list(self): p = Paginator(range(5), 2, 1) - pages = paginate({"paginator": p, "page_obj": p.page(1)})["pages"] + pages = pagination_tags.paginate({"paginator": p, "page_obj": p.page(1)})[ + "pages" + ] self.assertEqual(pages, [1, 2]) def test_orphaned_page_obj_one(self): p = Paginator(range(5), 2, 1) - pg = paginate({"paginator": p, "page_obj": p.page(1)}) + pg = pagination_tags.paginate({"paginator": p, "page_obj": p.page(1)}) self.assertTrue(pg["pages"], [1, 2, 3, 4, None, 7, 8, 9, 10]) self.assertTrue(pg["records"]["first"], 1) self.assertTrue(pg["records"]["last"], 2) def test_orphaned_page_obj_ten(self): p = Paginator(range(21), 2, 1) - pg = paginate({"paginator": p, "page_obj": p.page(10)}) + pg = pagination_tags.paginate({"paginator": p, "page_obj": p.page(10)}) self.assertTrue(pg["pages"], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) self.assertTrue(pg["records"]["first"], 19) self.assertTrue(pg["records"]["last"], 21) -from django.http import HttpRequest as DjangoHttpRequest - - class TestHttpRequest(DjangoHttpRequest): page = 1 @@ -122,11 +124,11 @@ def test_render_range_by_var_as_name(self): class TestInfinitePaginator(TestCase): def test_paginate_range_by_two(self): - pg = InfinitePaginator(range(20), 2, link_template="/bacon/page/%d") + pg = paginator.InfinitePaginator(range(20), 2, link_template="/bacon/page/%d") self.assertEqual(pg.validate_number(2), 2) self.assertEqual(pg.orphans, 0) p3 = pg.page(3) - self.assertIsInstance(p3, InfinitePage) + self.assertIsInstance(p3, paginator.InfinitePage) self.assertEqual(p3.end_index(), 6) self.assertTrue(p3.has_next()) self.assertTrue(p3.has_previous()) @@ -139,11 +141,13 @@ def test_paginate_range_by_two(self): class TestFinitePaginator(TestCase): def test_paginate_range_by_two_offset_ten(self): - pg = FinitePaginator(range(20), 2, offset=10, link_template="/bacon/page/%d") + pg = paginator.FinitePaginator( + range(20), 2, offset=10, link_template="/bacon/page/%d" + ) self.assertEqual(pg.validate_number(2), 2) self.assertEqual(pg.orphans, 0) p3 = pg.page(3) - self.assertIsInstance(p3, FinitePage) + self.assertIsInstance(p3, paginator.FinitePage) self.assertEqual(p3.start_index(), 10) self.assertEqual(p3.end_index(), 6) self.assertTrue(p3.has_next()) @@ -152,11 +156,13 @@ def test_paginate_range_by_two_offset_ten(self): self.assertEqual(p3.previous_link(), "/bacon/page/2") def test_paginate_range_by_twenty_offset_ten(self): - pg = FinitePaginator(range(20), 20, offset=10, link_template="/bacon/page/%d") + pg = paginator.FinitePaginator( + range(20), 20, offset=10, link_template="/bacon/page/%d" + ) self.assertEqual(pg.validate_number(2), 2) self.assertEqual(pg.orphans, 0) p2 = pg.page(2) - self.assertIsInstance(p2, FinitePage) + self.assertIsInstance(p2, paginator.FinitePage) self.assertEqual(p2.start_index(), 10) self.assertEqual(p2.end_index(), 40) self.assertFalse(p2.has_next()) @@ -167,14 +173,14 @@ def test_paginate_range_by_twenty_offset_ten(self): class TestPaginationMiddleware(TestCase): def test_append_page_property(self): - middleware = PaginationMiddleware() + page_middleware = middleware.PaginationMiddleware() request = WSGIRequest( { "REQUEST_METHOD": "POST", "CONTENT_TYPE": "multipart", - "wsgi.input": StringIO(), + "wsgi.input": io.StringIO(), } ) - middleware.process_request(request) + page_middleware.process_request(request) self.assertTrue(hasattr(request, "page")) request.upload_handlers.append("asdf") From ea7564ec12e89190691823902b0827d726cb3dcf Mon Sep 17 00:00:00 2001 From: staticdev Date: Fri, 22 May 2020 22:30:11 -0300 Subject: [PATCH 2/5] Documentation --- README.rst | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/README.rst b/README.rst index 91f6741..6443fca 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ django-pagination-bootstrap .. badges-begin -|Travis| |Python Version| |PyPi| |Black| +|Travis| |Python Version| |PyPi| |Black| |pre-commit| .. |Travis| image:: https://api.travis-ci.org/staticdev/django-pagination-bootstrap.svg?branch=master :target: https://travis-ci.org/staticdev/django-pagination-bootstrap @@ -12,13 +12,15 @@ django-pagination-bootstrap .. |Python Version| image:: https://img.shields.io/pypi/pyversions/django-pagination-bootstrap :target: https://pypi.org/project/django-pagination-bootstrap :alt: Python Version - .. |PyPi| image:: https://badge.fury.io/py/django-pagination-bootstrap.svg :target: https://badge.fury.io/py/django-pagination-bootstrap - + :alt: PyPI .. |Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/psf/black :alt: Black +.. |pre-commit| image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white + :target: https://github.com/pre-commit/pre-commit + :alt: pre-commit Django-pagination-bootstrap is an app to easy add pagination in Django_, using `Bootstrap`_'s layout. @@ -44,7 +46,7 @@ We need to hook ``django-pagination-bootstrap`` into our project. INSTALLED_APPS = ( # other apps - "django-pagination_bootstrap", + "django_pagination_bootstrap", ) 2. Install the pagination middleware. Your settings file might look something like: @@ -56,22 +58,18 @@ We need to hook ``django-pagination-bootstrap`` into our project. "django_pagination_bootstrap.middleware.PaginationMiddleware", ) -3. If it's not already added in your setup, add the request context processor. Note that context processors are set by default implicitly, so to set them explicitly, you need to copy and paste this code into your under the value TEMPLATE_CONTEXT_PROCESSORS. +3. Guarantee you have ``django.template.context_processors.request`` on settings.py: .. code-block:: python TEMPLATES = [ { - "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [], - "APP_DIRS": True, + # ... "OPTIONS": { "context_processors": [ - "django.template.context_processors.debug", - "django.template.context_processors.request", - "django.contrib.auth.context_processors.auth", - "django.template.context_processors.i18n", - "django.template.context_processors.media", + # ... + "django.template.context_processors.request" + # ... ], }, }, @@ -111,7 +109,7 @@ That's it! You have now paginated object_list and given users of the site a way Side effects ============ -A django-paginator_ instance will be injected in the template context as ``paginator``. You can access it as usual:: +A django-paginator_ instance will be injected in the template context as ``paginator``. You can access it as usual: .. code-block:: python From 1bf811a6aab0385f0dc264e5310ee67b424fee90 Mon Sep 17 00:00:00 2001 From: staticdev Date: Fri, 22 May 2020 22:41:20 -0300 Subject: [PATCH 3/5] Fix travis --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1010bc2..34eadad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,9 +9,13 @@ env: - DJANGO="Django>=2.0.0,<2.1.0" - DJANGO="Django>=2.1.0,<2.2.0" +before_install: + - pip install poetry nox + install: - pip install -q $DJANGO - pip install coveralls + - poetry install script: - coverage run runtests.py From d35c9e4ff2e36216cc7a0db49bee1066d87754cf Mon Sep 17 00:00:00 2001 From: staticdev Date: Fri, 22 May 2020 22:45:38 -0300 Subject: [PATCH 4/5] Remove coveralls --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 34eadad..8a19d12 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,11 +14,7 @@ before_install: install: - pip install -q $DJANGO - - pip install coveralls - poetry install script: - coverage run runtests.py - -after_success: - - coveralls From 2796bc53b0f28920696c9d3b0da5c56ea147b913 Mon Sep 17 00:00:00 2001 From: staticdev Date: Fri, 22 May 2020 22:53:00 -0300 Subject: [PATCH 5/5] Coverage toml 2 --- pyproject.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 642b09e..4ee32f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,16 @@ coverage = {extras = ["toml"], version = "^5.1"} safety = "^1.9.0" mypy = "^0.770" +[tool.coverage.paths] +source = ["src", "*/site-packages"] + +[tool.coverage.run] +branch = true +source = ["django_pagination_bootstrap"] + +[tool.coverage.report] +show_missing = true + [build-system] requires = ["poetry>=0.12"] build-backend = "poetry.masonry.api"