From d94d773932fdde0511c278e6f12f4d8850a1b812 Mon Sep 17 00:00:00 2001 From: Lari Liuhamo Date: Sat, 28 Oct 2023 21:52:10 +0300 Subject: [PATCH] [PATCH] Ruff and Tox rule updates --- .github/workflows/unit_tests.yml | 46 ++-- poetry.lock | 142 ++++++------ pyproject.toml | 228 ++++++++++--------- {iplib3 => src/iplib3}/__init__.py | 2 +- {iplib3 => src/iplib3}/address.py | 102 ++++----- {iplib3 => src/iplib3}/constants/__init__.py | 2 +- {iplib3 => src/iplib3}/constants/address.py | 2 +- {iplib3 => src/iplib3}/constants/ipv4.py | 2 +- {iplib3 => src/iplib3}/constants/ipv6.py | 2 +- {iplib3 => src/iplib3}/constants/port.py | 2 +- {iplib3 => src/iplib3}/constants/subnet.py | 9 +- {iplib3 => src/iplib3}/subnet.py | 52 +++-- {iplib3 => src/iplib3}/validators.py | 39 ++-- tests/__init__.py | 0 tests/test_address.py | 65 +++--- tests/test_cases_address.py | 20 +- tests/test_cases_subnet.py | 2 + tests/test_cases_validators.py | 2 + tests/test_subnet.py | 35 +-- tests/test_validators.py | 44 ++-- 20 files changed, 400 insertions(+), 398 deletions(-) rename {iplib3 => src/iplib3}/__init__.py (73%) rename {iplib3 => src/iplib3}/address.py (80%) rename {iplib3 => src/iplib3}/constants/__init__.py (80%) rename {iplib3 => src/iplib3}/constants/address.py (71%) rename {iplib3 => src/iplib3}/constants/ipv4.py (92%) rename {iplib3 => src/iplib3}/constants/ipv6.py (94%) rename {iplib3 => src/iplib3}/constants/port.py (82%) rename {iplib3 => src/iplib3}/constants/subnet.py (83%) rename {iplib3 => src/iplib3}/subnet.py (74%) rename {iplib3 => src/iplib3}/validators.py (90%) delete mode 100644 tests/__init__.py diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 2442394..cb824e5 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -7,12 +7,7 @@ name: Unit tests -on: - push: - branches: - - main - pull_request: - workflow_dispatch: +on: [ push, pull_request ] jobs: test: @@ -20,29 +15,21 @@ jobs: strategy: matrix: os: [macos-latest, ubuntu-latest, windows-latest] - tox-env: [ - py39, - py310, - py311, - pypy3 + python-version: [ + '3.9', + '3.10', + '3.11', + # 'pypy-3.10' ] - include: - - tox-env: py39 - python-version: '3.9' - - tox-env: py310 - python-version: '3.10' - - tox-env: py311 - python-version: '3.11' - - tox-env: pypy3 - python-version: pypy-3.9 - - exclude: - - os: windows-latest - tox-env: pypy3 - - os: macos-latest - tox-env: pypy3 + + # exclude: + # - os: windows-latest + # python-version: 'pypy-3.10' + # - os: macos-latest + # python-version: 'pypy-3.10' + env: - TOXENV: ${{ matrix.tox-env }} + PYTHON_VERSION: ${{ matrix.python-version }} steps: @@ -67,16 +54,17 @@ jobs: poetry run tox env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PLATFORM: ${{ matrix.os }} - name: Upload Results to CodeCov if: success() uses: codecov/codecov-action@v3 with: - env_vars: TOXENV + env_vars: PYTHON_VERSION fail_ci_if_error: false files: ./tests/reports/coverage-html/index.html,./tests/reports/coverage.xml flags: unittests - name: ${{ matrix.os }}-${{ matrix.tox-env }} + name: "${{ matrix.os }} - Python ${{ matrix.python-version }}" token: ${{ secrets.CODECOV_TOKEN }} diff --git a/poetry.lock b/poetry.lock index 92cde49..64d5a6b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,13 +2,13 @@ [[package]] name = "cachetools" -version = "5.3.1" +version = "5.3.2" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" files = [ - {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, - {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, + {file = "cachetools-5.3.2-py3-none-any.whl", hash = "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1"}, + {file = "cachetools-5.3.2.tar.gz", hash = "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2"}, ] [[package]] @@ -35,63 +35,63 @@ files = [ [[package]] name = "coverage" -version = "7.3.1" +version = "7.3.2" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cd0f7429ecfd1ff597389907045ff209c8fdb5b013d38cfa7c60728cb484b6e3"}, - {file = "coverage-7.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:966f10df9b2b2115da87f50f6a248e313c72a668248be1b9060ce935c871f276"}, - {file = "coverage-7.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0575c37e207bb9b98b6cf72fdaaa18ac909fb3d153083400c2d48e2e6d28bd8e"}, - {file = "coverage-7.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:245c5a99254e83875c7fed8b8b2536f040997a9b76ac4c1da5bff398c06e860f"}, - {file = "coverage-7.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c96dd7798d83b960afc6c1feb9e5af537fc4908852ef025600374ff1a017392"}, - {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:de30c1aa80f30af0f6b2058a91505ea6e36d6535d437520067f525f7df123887"}, - {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:50dd1e2dd13dbbd856ffef69196781edff26c800a74f070d3b3e3389cab2600d"}, - {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9c0c19f70d30219113b18fe07e372b244fb2a773d4afde29d5a2f7930765136"}, - {file = "coverage-7.3.1-cp310-cp310-win32.whl", hash = "sha256:770f143980cc16eb601ccfd571846e89a5fe4c03b4193f2e485268f224ab602f"}, - {file = "coverage-7.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:cdd088c00c39a27cfa5329349cc763a48761fdc785879220d54eb785c8a38520"}, - {file = "coverage-7.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:74bb470399dc1989b535cb41f5ca7ab2af561e40def22d7e188e0a445e7639e3"}, - {file = "coverage-7.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:025ded371f1ca280c035d91b43252adbb04d2aea4c7105252d3cbc227f03b375"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6191b3a6ad3e09b6cfd75b45c6aeeffe7e3b0ad46b268345d159b8df8d835f9"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb0b188f30e41ddd659a529e385470aa6782f3b412f860ce22b2491c89b8593"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c8f0df9dfd8ff745bccff75867d63ef336e57cc22b2908ee725cc552689ec8"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7eb3cd48d54b9bd0e73026dedce44773214064be93611deab0b6a43158c3d5a0"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ac3c5b7e75acac31e490b7851595212ed951889918d398b7afa12736c85e13ce"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b4ee7080878077af0afa7238df1b967f00dc10763f6e1b66f5cced4abebb0a3"}, - {file = "coverage-7.3.1-cp311-cp311-win32.whl", hash = "sha256:229c0dd2ccf956bf5aeede7e3131ca48b65beacde2029f0361b54bf93d36f45a"}, - {file = "coverage-7.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:c6f55d38818ca9596dc9019eae19a47410d5322408140d9a0076001a3dcb938c"}, - {file = "coverage-7.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5289490dd1c3bb86de4730a92261ae66ea8d44b79ed3cc26464f4c2cde581fbc"}, - {file = "coverage-7.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca833941ec701fda15414be400c3259479bfde7ae6d806b69e63b3dc423b1832"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd694e19c031733e446c8024dedd12a00cda87e1c10bd7b8539a87963685e969"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aab8e9464c00da5cb9c536150b7fbcd8850d376d1151741dd0d16dfe1ba4fd26"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d38444efffd5b056fcc026c1e8d862191881143c3aa80bb11fcf9dca9ae204"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8a07b692129b8a14ad7a37941a3029c291254feb7a4237f245cfae2de78de037"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2829c65c8faaf55b868ed7af3c7477b76b1c6ebeee99a28f59a2cb5907a45760"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f111a7d85658ea52ffad7084088277135ec5f368457275fc57f11cebb15607f"}, - {file = "coverage-7.3.1-cp312-cp312-win32.whl", hash = "sha256:c397c70cd20f6df7d2a52283857af622d5f23300c4ca8e5bd8c7a543825baa5a"}, - {file = "coverage-7.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:5ae4c6da8b3d123500f9525b50bf0168023313963e0e2e814badf9000dd6ef92"}, - {file = "coverage-7.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca70466ca3a17460e8fc9cea7123c8cbef5ada4be3140a1ef8f7b63f2f37108f"}, - {file = "coverage-7.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f2781fd3cabc28278dc982a352f50c81c09a1a500cc2086dc4249853ea96b981"}, - {file = "coverage-7.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6407424621f40205bbe6325686417e5e552f6b2dba3535dd1f90afc88a61d465"}, - {file = "coverage-7.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04312b036580ec505f2b77cbbdfb15137d5efdfade09156961f5277149f5e344"}, - {file = "coverage-7.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9ad38204887349853d7c313f53a7b1c210ce138c73859e925bc4e5d8fc18e7"}, - {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:53669b79f3d599da95a0afbef039ac0fadbb236532feb042c534fbb81b1a4e40"}, - {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:614f1f98b84eb256e4f35e726bfe5ca82349f8dfa576faabf8a49ca09e630086"}, - {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f1a317fdf5c122ad642db8a97964733ab7c3cf6009e1a8ae8821089993f175ff"}, - {file = "coverage-7.3.1-cp38-cp38-win32.whl", hash = "sha256:defbbb51121189722420a208957e26e49809feafca6afeef325df66c39c4fdb3"}, - {file = "coverage-7.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:f4f456590eefb6e1b3c9ea6328c1e9fa0f1006e7481179d749b3376fc793478e"}, - {file = "coverage-7.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f12d8b11a54f32688b165fd1a788c408f927b0960984b899be7e4c190ae758f1"}, - {file = "coverage-7.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f09195dda68d94a53123883de75bb97b0e35f5f6f9f3aa5bf6e496da718f0cb6"}, - {file = "coverage-7.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6601a60318f9c3945be6ea0f2a80571f4299b6801716f8a6e4846892737ebe4"}, - {file = "coverage-7.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07d156269718670d00a3b06db2288b48527fc5f36859425ff7cec07c6b367745"}, - {file = "coverage-7.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:636a8ac0b044cfeccae76a36f3b18264edcc810a76a49884b96dd744613ec0b7"}, - {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5d991e13ad2ed3aced177f524e4d670f304c8233edad3210e02c465351f785a0"}, - {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:586649ada7cf139445da386ab6f8ef00e6172f11a939fc3b2b7e7c9082052fa0"}, - {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4aba512a15a3e1e4fdbfed2f5392ec221434a614cc68100ca99dcad7af29f3f8"}, - {file = "coverage-7.3.1-cp39-cp39-win32.whl", hash = "sha256:6bc6f3f4692d806831c136c5acad5ccedd0262aa44c087c46b7101c77e139140"}, - {file = "coverage-7.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:553d7094cb27db58ea91332e8b5681bac107e7242c23f7629ab1316ee73c4981"}, - {file = "coverage-7.3.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:220eb51f5fb38dfdb7e5d54284ca4d0cd70ddac047d750111a68ab1798945194"}, - {file = "coverage-7.3.1.tar.gz", hash = "sha256:6cb7fe1581deb67b782c153136541e20901aa312ceedaf1467dcb35255787952"}, + {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"}, + {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"}, + {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"}, + {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"}, + {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, + {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, + {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, + {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, + {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, + {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, + {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, + {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, + {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"}, + {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"}, + {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"}, + {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"}, + {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"}, + {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"}, + {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"}, + {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"}, + {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, + {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, ] [package.dependencies] @@ -127,19 +127,19 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.12.4" +version = "3.13.0" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.12.4-py3-none-any.whl", hash = "sha256:08c21d87ded6e2b9da6728c3dff51baf1dcecf973b768ef35bcbc3447edb9ad4"}, - {file = "filelock-3.12.4.tar.gz", hash = "sha256:2e6f249f1f3654291606e046b09f1fd5eac39b360664c27f5aad072012f8bcbd"}, + {file = "filelock-3.13.0-py3-none-any.whl", hash = "sha256:a552f4fde758f4eab33191e9548f671970f8b06d436d31388c9aa1e5861a710f"}, + {file = "filelock-3.13.0.tar.gz", hash = "sha256:63c6052c82a1a24c873a549fbd39a26982e8f35a3016da231ead11a5be9dad44"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-timeout (>=2.1)"] -typing = ["typing-extensions (>=4.7.1)"] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] [[package]] name = "iniconfig" @@ -211,24 +211,24 @@ files = [ [[package]] name = "packaging" -version = "23.1" +version = "23.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] name = "platformdirs" -version = "3.10.0" +version = "3.11.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, - {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, + {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, + {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, ] [package.extras] @@ -403,13 +403,13 @@ files = [ [[package]] name = "virtualenv" -version = "20.24.5" +version = "20.24.6" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.24.5-py3-none-any.whl", hash = "sha256:b80039f280f4919c77b30f1c23294ae357c4c8701042086e3fc005963e4e537b"}, - {file = "virtualenv-20.24.5.tar.gz", hash = "sha256:e8361967f6da6fbdf1426483bfe9fca8287c242ac0bc30429905721cefbff752"}, + {file = "virtualenv-20.24.6-py3-none-any.whl", hash = "sha256:520d056652454c5098a00c0f073611ccbea4c79089331f60bf9d7ba247bb7381"}, + {file = "virtualenv-20.24.6.tar.gz", hash = "sha256:02ece4f56fbf939dbbc33c0715159951d6bf14aaf5457b092e4548e1382455af"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index 32e323f..c79d582 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,74 +1,78 @@ [build-system] -requires = ['poetry-core>=1.2.0', 'wheel',] -build-backend = 'poetry.core.masonry.api' +requires = ["poetry-core>=1.2.0", "wheel",] +build-backend = "poetry.core.masonry.api" [tool.coverage.run] branch = true relative_files = true omit = [ - '.tox/*', - 'tests/*', + ".tox/*", + "tests/*", ] [tool.coverage.report] exclude_lines = [ - 'pragma: not covered', - '@overload', - 'if TYPE_CHECKING:', + "pragma: not covered", + "@overload", + "if TYPE_CHECKING:", ] [tool.poetry] -name = 'iplib3' -version = '0.2.4' +name = "iplib3" +version = "0.2.4" description = "A modern, object-oriented approach to IP addresses." +packages = [ + { include = "iplib3", from = "src" }, +] + authors = ["Lari Liuhamo ",] maintainers = ["Lari Liuhamo ",] -include = ['CHANGELOG.md', 'LICENSE', 'py.typed',] -license = 'MIT' -readme = 'README.md' +include = ["CHANGELOG.md", "LICENSE", "py.typed",] +license = "MIT" +readme = "README.md" -homepage = 'https://pypi.org/project/iplib3/' -repository = 'https://github.com/Diapolo10/iplib3' -documentation = 'https://github.com/Diapolo10/iplib3/tree/main/docs' +homepage = "https://pypi.org/project/iplib3/" +repository = "https://github.com/Diapolo10/iplib3" +documentation = "https://github.com/Diapolo10/iplib3/tree/main/docs" keywords = [ - 'network', - 'networking', - 'ip', - 'ipaddress', - 'address', - 'python3', - 'pathlib', + "network", + "networking", + "ip", + "ipaddress", + "address", + "python3", + "pathlib", ] classifiers = [ - 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'Intended Audience :: Information Technology', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - 'Programming Language :: Python :: Implementation :: CPython', - 'Programming Language :: Python :: Implementation :: PyPy', - 'Topic :: Internet', - 'Topic :: Software Development :: Libraries', - 'Typing :: Typed', + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Internet", + "Topic :: Software Development :: Libraries", + "Typing :: Typed", ] [tool.poetry.dependencies] -python = '^3.9.0' +python = "^3.9.0" [tool.poetry.group.dev.dependencies] -mypy = '^1.5.1' +mypy = "^1.6.1" [tool.poetry.group.linters] @@ -76,7 +80,7 @@ optional = true [tool.poetry.group.linters.dependencies] -ruff = '>=0.0.290,<0.1.3' +ruff = "^0.1.3" [tool.poetry.group.tests] @@ -84,16 +88,16 @@ optional = true [tool.poetry.group.tests.dependencies] -pytest = '^7.4.2' -pytest-cov = '^4.1.0' -tox = '^4.11.3' -tox-gh-actions = '^3.1.3' +pytest = "^7.4.3" +pytest-cov = "^4.1.0" +tox = "^4.11.3" +tox-gh-actions = "^3.1.3" [tool.poetry.urls] -"Source code" = 'https://github.com/Diapolo10/iplib3' -"Tracker" = 'https://github.com/Diapolo10/iplib3/issues' -"Changelog" = 'https://github.com/Diapolo10/iplib3/blob/main/CHANGELOG.md' +"Source code" = "https://github.com/Diapolo10/iplib3" +"Tracker" = "https://github.com/Diapolo10/iplib3/issues" +"Changelog" = "https://github.com/Diapolo10/iplib3/blob/main/CHANGELOG.md" [tool.pytest.ini_options] @@ -107,66 +111,92 @@ addopts = """ --ignore=docs/ """ testpaths = [ - 'tests', + "tests", ] [tool.ruff] select = [ - 'A', # Builtins - 'ANN', # Annotations - 'ARG', # Unused arguments - 'B', # Bugbear - 'BLE', # Blind except - 'C4', # Comprehensions - 'C90', # mccabe - 'COM', # Commas - 'DTZ', # Datetimes - 'ERA', # Commented-out code - 'EXE', # Executable - 'G', # Logging format - 'I', # Isort - 'ICN', # Import conventions - 'INP', # Disallow PEP-420 (Implicit namespace packages) - 'INT', # gettext - 'ISC', # Implicit str concat - 'N', # PEP-8 Naming - 'NPY', # Numpy - 'PGH', # Pygrep hooks - 'PIE', # Unnecessary code - 'PL', # Pylint - 'PT', # Pytest - 'PTH', # Use Pathlib - 'PYI', # Stub files - 'RET', # Return - 'RUF', # Ruff - 'RSE', # Raise - 'S', # Bandit - 'SIM', # Code simplification - 'SLF', # Private member access - 'T20', # Print - 'TCH', # Type checking - 'TID', # Tidy imports - 'UP', # Pyupgrade - 'W', # Warnings - 'YTT', # sys.version + "A", # Builtins + "ANN", # Annotations + "ARG", # Unused arguments + "B", # Bugbear + "BLE", # Blind except + "C4", # Comprehensions + "C90", # mccabe + "COM", # Commas + "D1", # Undocumented public elements + "D2", # Docstring conventions + "D3", # Triple double quotes + "D4", # Docstring text format + "DTZ", # Datetimes + "EM", # Error messages + "ERA", # Commented-out code + "EXE", # Executable + "F", # Pyflakes + "FA", # __future__ annotations + "FLY", # F-strings + # "FURB", # Refurb + "G", # Logging format + "I", # Isort + "ICN", # Import conventions + "INP", # Disallow PEP-420 (Implicit namespace packages) + "INT", # gettext + "ISC", # Implicit str concat + # "LOG", # Logging + "N", # PEP-8 Naming + "NPY", # Numpy + "PERF", # Unnecessary performance costs + "PGH", # Pygrep hooks + "PIE", # Unnecessary code + "PL", # Pylint + "PT", # Pytest + "PTH", # Use Pathlib + "PYI", # Stub files + "Q", # Quotes + "RET", # Return + "RUF", # Ruff + "RSE", # Raise + "S", # Bandit + "SIM", # Code simplification + "SLF", # Private member access + "SLOT", # __slots__ + "T10", # Debugger + "T20", # Print + "TCH", # Type checking + "TID", # Tidy imports + "TRY", # Exception handling + "UP", # Pyupgrade + "W", # Warnings + "YTT", # sys.version ] ignore = [ - 'PLR0913', # Too many arguments + "D203", # One blank line before class docstring + "D212", # Multi-line summary first line + "PLR0913", # Too many arguments + "Q000", # Single quotes found but double quotes preferred ] ignore-init-module-imports = true line-length = 120 +# preview = true +show-fixes = true +src = ["src",] +target-version = "py39" + + +[tool.ruff.flake8-quotes] +docstring-quotes = "double" +multiline-quotes = "double" [tool.ruff.mccabe] -# Unlike Flake8, default to a complexity level of 10. max-complexity = 10 [tool.ruff.per-file-ignores] # https://beta.ruff.rs/docs/rules/ -'__init__.py' = ['F401','F403','F405',] -'tests/*' = ['ANN', 'ARG', 'INP001', 'S101',] +"__init__.py" = ["F401", "F403", "F405",] +"tests/*" = ["ANN", "ARG", "INP001", "S101",] [tool.ruff.pylint] @@ -183,7 +213,7 @@ ban-relative-imports = "all" [tool.tox] legacy_tox_ini = """ [tox] -envlist = py39, py310, py311, pypy3 +envlist = py39, py310, py311, py312, pypy3 isolated_build = True [gh-actions] @@ -191,22 +221,14 @@ python = 3.9: py39 3.10: py310 3.11: py311 - pypy-3.9: pypy3 - -[gh-actions:env] -PLATFORM = - ubuntu-latest: linux - macos-latest: macos - windows-latest: windows + 3.12: py312 + pypy-3.10: pypy3 [testenv] passenv = GITHUB_* -deps = - coverage - coveralls[toml] - pytest - pytest-cov - wheel +allowlist_externals = poetry + commands = - coverage run -m pytest + poetry run pytest + poetry run coverage report """ diff --git a/iplib3/__init__.py b/src/iplib3/__init__.py similarity index 73% rename from iplib3/__init__.py rename to src/iplib3/__init__.py index 6977448..7d5702d 100644 --- a/iplib3/__init__.py +++ b/src/iplib3/__init__.py @@ -1,4 +1,4 @@ -"""A pathlib-equivalent library for IP addresses""" +"""A pathlib-equivalent library for IP addresses.""" from iplib3.address import * from iplib3.subnet import * diff --git a/iplib3/address.py b/src/iplib3/address.py similarity index 80% rename from iplib3/address.py rename to src/iplib3/address.py index 7723f09..7d5572a 100644 --- a/iplib3/address.py +++ b/src/iplib3/address.py @@ -1,4 +1,4 @@ -"""iplib3's functionality specific to addresses""" +"""iplib3's functionality specific to addresses.""" from __future__ import annotations @@ -36,7 +36,7 @@ class PureAddress: - """Bare-bones, independent base class for IP addresses""" + """Bare-bones, independent base class for IP addresses.""" __slots__ = ('_num', '_port') @@ -63,22 +63,19 @@ def __str__(self: PureAddress) -> str: @property def num(self: PureAddress) -> int: """ - Negative numbers aren't valid, - they are treated as zero. + Negative numbers aren't valid, they are treated as zero. TODO: Consider whether negative numbers should raise an exception """ - return max(0, self._num) @property def port(self: PureAddress) -> int | None: """ - Returns the port in the address, or None if no port is specified + Return the port in the address, or None if no port is specified. TODO: Consider whether invalid port numbers should raise an exception """ - if self._port is None: return None @@ -87,52 +84,40 @@ def port(self: PureAddress) -> int | None: @port.setter def port(self: PureAddress, value: int | None) -> None: """ - Sets a new port value. Value must be a valid integer and within the range of valid ports. + Set a new port value. Value must be a valid integer and within the range of valid ports. TODO: Find a way to avoid mutability """ - if value is None: pass # OK elif not isinstance(value, int): - raise TypeError(f"Port '{value}' is not a valid integer") + msg = f"Port '{value}' is not a valid integer" + raise TypeError(msg) elif not PORT_NUMBER_MIN_VALUE <= value <= PORT_NUMBER_MAX_VALUE: + msg = f"Port number '{value}' not in valid range ({PORT_NUMBER_MIN_VALUE}-{PORT_NUMBER_MAX_VALUE})" raise ValueError( - f"Port number '{value}' not in valid range " - f"({PORT_NUMBER_MIN_VALUE}-{PORT_NUMBER_MAX_VALUE})", + msg, ) self._port = value @property def as_hex(self: PureAddress) -> str: - """ - Returns a hexadecimal representation of the address - """ - + """Return a hexadecimal representation of the address.""" return f"0x{self.num:0X}" def num_to_ipv4(self: PureAddress) -> str: - """ - A wrapper method for the otherwise equivalent static method - """ - + """Wrap method for the otherwise equivalent static method.""" return self._num_to_ipv4(self.num) def num_to_ipv6(self: PureAddress, shorten: bool = True, remove_zeroes: bool = False) -> str: - """ - A wrapper method for the otherwise equivalent static method - """ - + """Wrap method for the otherwise equivalent static method.""" return self._num_to_ipv6(self.num, shorten, remove_zeroes) @staticmethod def _num_to_ipv4(num: int) -> str: - """ - Generates an IPv4 string from an integer - """ - + """Generate an IPv4 string from an integer.""" segments = [] for _ in range(IPV4_MAX_SEGMENT_COUNT): num, segment = divmod(num, IPV4_MAX_SEGMENT_VALUE+1) @@ -141,11 +126,7 @@ def _num_to_ipv4(num: int) -> str: @staticmethod def _num_to_ipv6(num: int, shorten: bool = True, remove_zeroes: bool = False) -> str: - """ - Generates an IPv6 string from an integer, - with optional zero removal and shortening. - """ - + """Generate an IPv6 string from an integer, with optional zero removal and shortening.""" segment_min_length = (IPV6_SEGMENT_BIT_COUNT // IPV6_NUMBER_BIT_COUNT) * (not shorten) zero_segment = f'{0:0{segment_min_length}}' @@ -173,12 +154,12 @@ def _num_to_ipv6(num: int, shorten: bool = True, remove_zeroes: bool = False) -> class IPAddress(PureAddress): - """More flexible PureAddress subclass""" + """More flexible PureAddress subclass.""" __slots__ = ('_ipv4', '_ipv6', '_submask') def __new__(cls: type[IPAddress], address: int | str | None = None, port_num: int | None = None, **kwargs) -> IPAddress: # noqa: ANN003,PYI034 - + """Create PureAddress.""" if isinstance(address, str): # Only IPv4-addresses have '.', ':' is used in both IPv4 and IPv6 cls = IPv4 if '.' in address else IPv6 @@ -189,13 +170,14 @@ def __new__(cls: type[IPAddress], address: int | str | None = None, port_num: in return self def __init__(self: IPAddress, address: int | None = IPV4_LOCALHOST, port_num: int | None = None) -> None: + """Init PureAddres.""" super().__init__(num=address, port=port_num) self._ipv4: IPv4 | None = None self._ipv6: IPv6 | None = None self._submask: SubnetMask | None = None def __eq__(self: IPAddress, other: object) -> bool: - + """Compare equality.""" # To accommodate strings if str(self) == str(other): return True @@ -203,7 +185,7 @@ def __eq__(self: IPAddress, other: object) -> bool: return super().__eq__(other) def __str__(self: IPAddress) -> str: - + """Str variant.""" if IPV4_MIN_VALUE <= self.num <= IPV4_MAX_VALUE: if self._ipv4 is None: self._ipv4 = self.as_ipv4 @@ -214,28 +196,27 @@ def __str__(self: IPAddress) -> str: self._ipv6 = self.as_ipv6 return str(self._ipv6) - raise ValueError(f"No valid address representation exists for {self.num}") + msg = f'No valid address representation exists for {self.num}' + raise ValueError(msg) @property def as_ipv4(self: IPAddress) -> IPv4: - """Creates and returns an IPv4 version of the address, if possible""" - + """Creates and returns an IPv4 version of the address, if possible.""" return IPv4(self.num_to_ipv4(), port_num=self.port) # type: ignore # noqa: PGH003 @property def as_ipv6(self: IPAddress) -> IPv6: - """Creates and returns an IPv6-version of the address""" - + """Creates and returns an IPv6-version of the address.""" return IPv6(self.num_to_ipv6(), port_num=self.port) # type: ignore # noqa: PGH003 class IPv4(IPAddress): - """An IPAddress subclass specific to IPv4""" + """An IPAddress subclass specific to IPv4.""" __slots__ = ('_address',) def __init__(self: IPv4, address: str | None = None, port_num: int | None = None) -> None: - + """Init IPv4.""" new_address = self._num_to_ipv4(IPV4_LOCALHOST) if address is None else address _address, *_port = new_address.split(':') @@ -250,7 +231,7 @@ def __init__(self: IPv4, address: str | None = None, port_num: int | None = None super().__init__(address=self._ipv4_to_num(), port_num=port_num) def __str__(self: IPv4) -> str: - + """Str variant.""" if self.port is not None: return f"{self._address}:{self.port}" @@ -258,12 +239,10 @@ def __str__(self: IPv4) -> str: def _ipv4_to_num(self: IPv4) -> int: """ - Takes a valid IPv4 address and turns - it into an equivalent integer value. + Take a valid IPv4 address and turns it into an equivalent integer value. Raises ValueError on invalid IPv4 format. """ - segments = [int(segment) for segment in self._address.split('.')][::-1] total = 0 @@ -274,12 +253,12 @@ def _ipv4_to_num(self: IPv4) -> int: class IPv6(IPAddress): - """An IPAddress subclass specific to IPv6""" + """An IPAddress subclass specific to IPv6.""" __slots__ = ('_address',) def __init__(self: IPv6, address: str | None = None, port_num: int | None = None) -> None: - + """Init IPv6.""" new_address = self._num_to_ipv6(IPV6_LOCALHOST) if address is None else address _address, *_port = new_address.split(']:') @@ -296,7 +275,7 @@ def __init__(self: IPv6, address: str | None = None, port_num: int | None = None super().__init__(address=self._ipv6_to_num(), port_num=port_num) def __str__(self: IPv6) -> str: - + """Str variant.""" if self.port is not None: return f"[{self._address}]:{self.port}" @@ -304,12 +283,10 @@ def __str__(self: IPv6) -> str: def _ipv6_to_num(self: IPv6) -> int: """ - Takes a valid IPv6 address and turns - it into an equivalent integer value. + Take a valid IPv6 address and turns it into an equivalent integer value. Raises ValueError on invalid IPv6 format. """ - halves = self._address.split('::') segments = [] @@ -335,7 +312,8 @@ def _ipv6_to_num(self: IPv6) -> int: segments.extend(halves[0].split(':')) else: - raise ValueError("Invalid IPv6 address format; only one zero-skip allowed") + msg = 'Invalid IPv6 address format; only one zero-skip allowed' + raise ValueError(msg) try: processed_segments: list[int] = [ @@ -343,26 +321,28 @@ def _ipv6_to_num(self: IPv6) -> int: for segment in segments[::-1] ] except ValueError as err: - raise ValueError("Invalid IPv6 address format; address contains invalid characters") from err + msg = 'Invalid IPv6 address format; address contains invalid characters' + raise ValueError(msg) from err segment_count = len(processed_segments) if segment_count > IPV6_MAX_SEGMENT_COUNT: + msg = f'Invalid IPv6 address format; too many segments ({segment_count} > {IPV6_MAX_SEGMENT_COUNT})' raise ValueError( - f"Invalid IPv6 address format; too many segments " - f"({segment_count} > {IPV6_MAX_SEGMENT_COUNT})", + msg, ) highest = max(processed_segments) if highest > IPV6_MAX_SEGMENT_VALUE: + msg = f'Invalid IPv6 address format; segment max value passed ({highest} > {IPV6_MAX_SEGMENT_VALUE})' raise ValueError( - f"Invalid IPv6 address format; segment max value " - f"passed ({highest} > {IPV6_MAX_SEGMENT_VALUE})", + msg, ) lowest = min(processed_segments) if lowest < IPV6_MIN_SEGMENT_VALUE: + msg = f'Invalid IPv6 address format; segment min value passed ({lowest} < 0)' raise ValueError( - f"Invalid IPv6 address format; segment min value passed ({lowest} < 0)", + msg, ) total = 0 diff --git a/iplib3/constants/__init__.py b/src/iplib3/constants/__init__.py similarity index 80% rename from iplib3/constants/__init__.py rename to src/iplib3/constants/__init__.py index ee717fc..de3bb34 100644 --- a/iplib3/constants/__init__.py +++ b/src/iplib3/constants/__init__.py @@ -1,4 +1,4 @@ -"""Various constant values used by iplib3""" +"""Various constant values used by iplib3.""" from iplib3.constants.address import * from iplib3.constants.ipv4 import * diff --git a/iplib3/constants/address.py b/src/iplib3/constants/address.py similarity index 71% rename from iplib3/constants/address.py rename to src/iplib3/constants/address.py index 6fba0d6..fd3fe5c 100644 --- a/iplib3/constants/address.py +++ b/src/iplib3/constants/address.py @@ -1,4 +1,4 @@ -"""Special addresses in hex values""" +"""Special addresses in hex values.""" IPV4_LOCALHOST = 0x7F_00_00_01 # 127.0.0.1, or 2130706433 IPV6_LOCALHOST = 0x1 # ::1 diff --git a/iplib3/constants/ipv4.py b/src/iplib3/constants/ipv4.py similarity index 92% rename from iplib3/constants/ipv4.py rename to src/iplib3/constants/ipv4.py index bf58a54..2e0b8b4 100644 --- a/iplib3/constants/ipv4.py +++ b/src/iplib3/constants/ipv4.py @@ -1,4 +1,4 @@ -"""IPv4 constants""" +"""IPv4 constants.""" # IPv4 constants IPV4_SEGMENT_BIT_COUNT = 8 diff --git a/iplib3/constants/ipv6.py b/src/iplib3/constants/ipv6.py similarity index 94% rename from iplib3/constants/ipv6.py rename to src/iplib3/constants/ipv6.py index d6218ec..f627385 100644 --- a/iplib3/constants/ipv6.py +++ b/src/iplib3/constants/ipv6.py @@ -1,4 +1,4 @@ -"""IPv6 constants""" +"""IPv6 constants.""" # IPv6 constants IPV6_NUMBER_BIT_COUNT = 4 diff --git a/iplib3/constants/port.py b/src/iplib3/constants/port.py similarity index 82% rename from iplib3/constants/port.py rename to src/iplib3/constants/port.py index ab43426..2376142 100644 --- a/iplib3/constants/port.py +++ b/src/iplib3/constants/port.py @@ -1,4 +1,4 @@ -"""Port number constants""" +"""Port number constants.""" # Port number constants (agnostic between IPV4 and IPV6) PORT_NUMBER_MIN_VALUE = 0 diff --git a/iplib3/constants/subnet.py b/src/iplib3/constants/subnet.py similarity index 83% rename from iplib3/constants/subnet.py rename to src/iplib3/constants/subnet.py index 7e1ddba..d2d7796 100644 --- a/iplib3/constants/subnet.py +++ b/src/iplib3/constants/subnet.py @@ -1,4 +1,4 @@ -"""Subnet constants""" +"""Subnet constants.""" from __future__ import annotations @@ -11,11 +11,13 @@ IPV4_VALID_SUBNET_SEGMENTS = (0, 128, 192, 224, 240, 248, 252, 254, 255) IPV4_MIN_SUBNET_VALUE = 0 # Usually 1 is a better choice, 0 is technically valid though IPV4_MAX_SUBNET_VALUE = IPV4_SEGMENT_BIT_COUNT * IPV4_MAX_SEGMENT_COUNT - 1 # == 31 -IPV6_MIN_SUBNET_VALUE = 0 # Unlike in IPV4, this should *always* be valid +IPV6_MIN_SUBNET_VALUE = 0 # Unlike in IPv4, this should *always* be valid IPV6_MAX_SUBNET_VALUE = IPV6_SEGMENT_BIT_COUNT * IPV6_MAX_SEGMENT_COUNT - 1 # == 127 class SubnetType(str, Enum): + """Subnet type.""" + IPV4 = 'ipv4' IPV6 = 'ipv6' @@ -25,4 +27,5 @@ def _missing_(cls: type[SubnetType], value: object) -> SubnetType: if isinstance(value, str) and member.value == value.lower(): return member - raise ValueError("Invalid subnet type") + msg = 'Invalid subnet type' + raise ValueError(msg) diff --git a/iplib3/subnet.py b/src/iplib3/subnet.py similarity index 74% rename from iplib3/subnet.py rename to src/iplib3/subnet.py index 936ebaa..9c29604 100644 --- a/iplib3/subnet.py +++ b/src/iplib3/subnet.py @@ -1,4 +1,4 @@ -"""iplib3's functionality specific to subnetting""" +"""iplib3's functionality specific to subnetting.""" from __future__ import annotations @@ -18,22 +18,24 @@ class PureSubnetMask: - """ - Platform and version-independent base class for subnets - """ + """Platform and version-independent base class for subnets.""" + __slots__ = ('_prefix_length',) def __init__(self: PureSubnetMask) -> None: + """Create PureSubnetMask.""" self._prefix_length: int | None = 0 def __str__(self: PureSubnetMask) -> str: + """Str conversion.""" return str(self.prefix_length) def __repr__(self: PureSubnetMask) -> str: + """Str representation.""" return f"iplib3.{self.__class__.__name__}('{self}')" def __eq__(self: PureSubnetMask, other: object) -> bool: - + """Compare equality.""" # To accommodate strings and integers if self.prefix_length == other or str(self) == other: return True @@ -44,17 +46,16 @@ def __eq__(self: PureSubnetMask, other: object) -> bool: return False def __ne__(self: PureSubnetMask, other: object) -> bool: + """Compare inequality.""" return not self == other @property def prefix_length(self: PureSubnetMask) -> int | None: """ - Negative numbers aren't valid, - they are treated as zero. + Negative numbers aren't valid, they are treated as zero. TODO: Consider whether negative numbers should raise an exception """ - if self._prefix_length is None: return None @@ -62,12 +63,12 @@ def prefix_length(self: PureSubnetMask) -> int | None: class SubnetMask(PureSubnetMask): - """Subnet mask for defining subnets""" + """Subnet mask for defining subnets.""" __slots__ = ('_subnet_type',) def __init__(self: SubnetMask, subnet_mask: int | str | None = None, subnet_type: SubnetType = SubnetType.IPV6) -> None: - + """Create SubnetMask.""" subnet_type = SubnetType(subnet_type) super().__init__() @@ -79,6 +80,7 @@ def __init__(self: SubnetMask, subnet_mask: int | str | None = None, subnet_type self._subnet_type = subnet_type def __repr__(self: SubnetMask) -> str: + """Str representation.""" if self._subnet_type == SubnetType.IPV4 and self._prefix_length is not None: return ( f"iplib3.{self.__class__.__name__}" @@ -105,9 +107,9 @@ def _subnet_to_num(subnet_mask: int | str | None, subnet_type: SubnetType = Subn return None if not isinstance(subnet_mask, (int, str)): + msg = f"Invalid type for subnet value: '{subnet_mask.__class__.__name__}'\nExpected int, string, or None" raise TypeError( - f"Invalid type for subnet value: '{subnet_mask.__class__.__name__}'\n" - f"Expected int, string, or None", + msg, ) if subnet_type == SubnetType.IPV4: @@ -116,12 +118,13 @@ def _subnet_to_num(subnet_mask: int | str | None, subnet_type: SubnetType = Subn if subnet_type == SubnetType.IPV6: if isinstance(subnet_mask, str): if '.' in subnet_mask: - raise ValueError("IPv6-subnets don't use a string representation") + msg = "IPv6-subnets don't use a string representation" + raise ValueError(msg) subnet_mask = int(subnet_mask) if not IPV6_MIN_SUBNET_VALUE <= subnet_mask <= IPV6_MAX_SUBNET_VALUE: + msg = f"Subnet '{subnet_mask}' not in valid range ({IPV6_MIN_SUBNET_VALUE}-{IPV6_MAX_SUBNET_VALUE})" raise ValueError( - f"Subnet '{subnet_mask}' not in valid range " - f"({IPV6_MIN_SUBNET_VALUE}-{IPV6_MAX_SUBNET_VALUE})", + msg, ) return int(subnet_mask) @@ -132,29 +135,32 @@ def _ipv4_subnet_to_num(subnet_mask: int | str) -> int: if '.' in subnet_mask: segments = tuple(int(s) for s in reversed(subnet_mask.split('.'))) if len(segments) != IPV4_MIN_SEGMENT_COUNT: + msg = f"Subnet value not valid; '{subnet_mask}' is not a valid string representation" raise ValueError( - f"Subnet value not valid; '{subnet_mask}' is not a valid string representation", + msg, ) segment_sum = sum(s<<(8*idx) for idx, s in enumerate(segments)) subnet_bits = f'{segment_sum:b}'.rstrip('0') if '0' in subnet_bits: - raise ValueError(f"'{subnet_mask}' is an invalid subnet mask") + msg = f"'{subnet_mask}' is an invalid subnet mask" + raise ValueError(msg) subnet_mask = len(subnet_bits) try: subnet_mask = int(subnet_mask) except ValueError as err: + msg = f"Subnet value not valid; '{subnet_mask}' is neither a valid string representation nor an integer" raise ValueError( - f"Subnet value not valid; '{subnet_mask}' is neither a valid string representation nor an integer", + msg, ) from err if not IPV4_MIN_SUBNET_VALUE <= subnet_mask <= IPV4_MAX_SUBNET_VALUE: + msg = f"Subnet '{subnet_mask}' not in valid range ({IPV4_MIN_SUBNET_VALUE}-{IPV4_MAX_SUBNET_VALUE})" raise ValueError( - f"Subnet '{subnet_mask}' not in valid range " - f"({IPV4_MIN_SUBNET_VALUE}-{IPV4_MAX_SUBNET_VALUE})", + msg, ) return subnet_mask @@ -165,10 +171,12 @@ def _prefix_to_subnet_mask(prefix_length: int, subnet_type: SubnetType) -> str: subnet_type = SubnetType(subnet_type) if subnet_type == SubnetType.IPV6: - raise ValueError("IPv6 does not support string representations of subnet masks") + msg = 'IPv6 does not support string representations of subnet masks' + raise ValueError(msg) if not IPV4_MIN_SUBNET_VALUE <= prefix_length <= IPV4_MAX_SUBNET_VALUE: - raise ValueError(f"Invalid subnet value for IPv4: '{prefix_length}'") + msg = f"Invalid subnet value for IPv4: '{prefix_length}'" + raise ValueError(msg) segments = [0, 0, 0, 0] diff --git a/iplib3/validators.py b/src/iplib3/validators.py similarity index 90% rename from iplib3/validators.py rename to src/iplib3/validators.py index f349cb9..ec33329 100644 --- a/iplib3/validators.py +++ b/src/iplib3/validators.py @@ -1,4 +1,4 @@ -"""iplib3's functionality specific to validating things""" +"""iplib3's functionality specific to validating things.""" from __future__ import annotations @@ -36,7 +36,7 @@ def port_validator(port_num: int | None) -> bool: """ - Validates an address port + Validate an address port. None means "no port", and is treated as a valid port value. Otherwise the port must be an integer between the minimum and maximum port values, inclusive. @@ -45,7 +45,6 @@ def port_validator(port_num: int | None) -> bool: The function should not raise an exception, wrong types will simply return False. """ - if port_num is None: return True @@ -57,12 +56,11 @@ def port_validator(port_num: int | None) -> bool: def ip_validator(address: str | int, strict: bool = True) -> bool: """ - Validates an IP address of any kind, returning a boolean + Validate an IP address of any kind, returning a boolean. Under strict mode ensures that the numerical values don't exceed legal bounds, otherwise focuses on form. """ - if ipv4_validator(address, strict): return True return ipv6_validator(address, strict) @@ -70,12 +68,11 @@ def ip_validator(address: str | int, strict: bool = True) -> bool: def ipv4_validator(address: str | int, strict: bool = True) -> bool: """ - Validates an IPv4 address, returning a boolean + Validate an IPv4 address, returning a boolean. Under strict mode ensures that the numerical values don't exceed legal bounds, otherwise focuses on form. """ - valid = False if isinstance(address, str) and '.' in address: @@ -93,12 +90,11 @@ def ipv4_validator(address: str | int, strict: bool = True) -> bool: def ipv6_validator(address: str | int, strict: bool = True) -> bool: """ - Validates an IPv6 address, returning a boolean + Validate an IPv6 address, returning a boolean. Under strict mode ensures that the numerical values don't exceed legal bounds, otherwise focuses on form. """ - valid = False if isinstance(address, str): @@ -118,12 +114,11 @@ def ipv6_validator(address: str | int, strict: bool = True) -> bool: def subnet_validator(subnet: str | int, protocol: SubnetType = SubnetType.IPV4) -> bool: """ - Validates a given subnet mask, defaulting to IPv4 protocol + Validate a given subnet mask, defaulting to IPv4 protocol. Strings will be validated as IPv4 regardless of the protocol toggle as IPv6 subnets have no valid string representation. """ - protocol = SubnetType(protocol) valid = False @@ -139,7 +134,7 @@ def subnet_validator(subnet: str | int, protocol: SubnetType = SubnetType.IPV4) def _ipv4_subnet_validator(subnet: str | int) -> bool: """ - Validates an IPv4-compliant subnet mask + Validate an IPv4-compliant subnet mask. The function uses the IPv4-standard to validate a subnet, including all values. @@ -148,7 +143,6 @@ def _ipv4_subnet_validator(subnet: str | int) -> bool: *will raise a TypeError* with the name of the used type. """ - if isinstance(subnet, str): segments = tuple(int(s) for s in reversed(subnet.split('.'))) if len(segments) != IPV4_MIN_SEGMENT_COUNT: @@ -165,15 +159,15 @@ def _ipv4_subnet_validator(subnet: str | int) -> bool: if isinstance(subnet, int): return IPV4_MIN_SUBNET_VALUE <= subnet <= IPV4_MAX_SUBNET_VALUE + msg = f"IPv4 subnet cannot be of type '{subnet.__class__.__name__}'; only strings and integers supported" raise TypeError( - f"IPv4 subnet cannot be of type '{subnet.__class__.__name__}';" - f" only strings and integers supported", + msg, ) def _ipv6_subnet_validator(subnet: int) -> bool: # IPv6 subnets have no string representation """ - Validates an IPv6-compliant subnet mask + Validate an IPv6-compliant subnet mask. The IPv6-standard has no string representation for subnests, so @@ -182,24 +176,23 @@ def _ipv6_subnet_validator(subnet: int) -> bool: # IPv6 subnets have no string Non-integer types will raise a ValueError with the name of the used type. """ - if isinstance(subnet, int): return ( IPV6_MIN_SUBNET_VALUE <= subnet <= IPV6_MAX_SUBNET_VALUE and subnet % IPV6_NUMBER_BIT_COUNT == 0 ) - raise TypeError(f"IPv6 subnet cannot be of type '{subnet.__class__.__name__}', it must be an integer") + msg = f"IPv6 subnet cannot be of type '{subnet.__class__.__name__}', it must be an integer" + raise TypeError(msg) def _port_stripper(address: str, protocol: SubnetType = SubnetType.IPV4, strict: bool = True) -> tuple[str, int | None, bool]: """ - Extracts the port number from IP addresses, if any + Extract the port number from IP addresses, if any. Returns a tuple with the portless address, the port (or None), and validation information as a boolean. """ - protocol = SubnetType(protocol) valid = True @@ -232,8 +225,7 @@ def _port_stripper(address: str, protocol: SubnetType = SubnetType.IPV4, strict: def _ipv4_address_validator(address: str, strict: bool = True) -> bool: - """Validates the address part of an IPv4 address""" - + """Validate the address part of an IPv4 address.""" valid = True try: @@ -257,8 +249,7 @@ def _ipv4_address_validator(address: str, strict: bool = True) -> bool: def _ipv6_address_validator(address: str, strict: bool = True) -> bool: - """Validates the address part of an IPv6 address""" - + """Validate the address part of an IPv6 address.""" address = address.strip() valid = True segments: list[str] = [] diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_address.py b/tests/test_address.py index e1316f1..a2bb06e 100644 --- a/tests/test_address.py +++ b/tests/test_address.py @@ -1,11 +1,6 @@ -"""Unit tests for iplib3.address""" +"""Unit tests for iplib3.address.""" import pytest - -from iplib3 import IPAddress -from iplib3.address import IPv6, PureAddress -from iplib3.constants import IPV6_MAX_VALUE -from iplib3.constants.port import PORT_NUMBER_MIN_VALUE from tests.test_cases_address import ( TEST_CASES_IPADDRESS, TEST_CASES_IPADDRESS_AS_IPV4, @@ -33,13 +28,17 @@ TEST_CASES_PURE_ADDRESS_PORT_SETTER_ERROR, ) +from iplib3 import IPAddress +from iplib3.address import IPv6, PureAddress +from iplib3.constants import IPV6_MAX_VALUE + @pytest.mark.parametrize( "pure_address", TEST_CASES_PURE_ADDRESS, ) def test_pure_address(pure_address): - """Test the PureAddress base class""" + """Test the PureAddress base class.""" assert pure_address @@ -48,7 +47,7 @@ def test_pure_address(pure_address): TEST_CASES_PURE_ADDRESS_EQUALITY, ) def test_pure_address_equality(address, input_address, excepted_output): - """Test PureAddress equality""" + """Test PureAddress equality.""" output = address == input_address assert output is excepted_output @@ -58,7 +57,7 @@ def test_pure_address_equality(address, input_address, excepted_output): TEST_CASES_PURE_ADDRESS_INEQUALITY, ) def test_pure_address_inequality(first_address, second_address): - """Test PureAddress inequality""" + """Test PureAddress inequality.""" assert first_address != second_address @@ -67,7 +66,7 @@ def test_pure_address_inequality(first_address, second_address): TEST_CASES_PURE_ADDRESS_NUM, ) def test_pure_address_num(address, excepted_output): - """Test PureAddress num property""" + """Test PureAddress num property.""" assert address.num == excepted_output @@ -76,13 +75,12 @@ def test_pure_address_num(address, excepted_output): TEST_CASES_PURE_ADDRESS_PORT, ) def test_pure_address_port(address, excepted_output): - """Test PureAddress port property""" + """Test PureAddress port property.""" assert address.port == excepted_output def test_pure_address_port_setter(): - """Test PureAddress port setter""" - + """Test PureAddress port setter.""" address = PureAddress() assert address.port is None @@ -98,6 +96,7 @@ def test_pure_address_port_setter(): TEST_CASES_PURE_ADDRESS_PORT_SETTER_ERROR, ) def test_pure_address_port_setter_error(value, error, match_message): + """Test pure address port errors.""" address = PureAddress() with pytest.raises(error, match=match_message): address.port = value @@ -108,7 +107,7 @@ def test_pure_address_port_setter_error(value, error, match_message): TEST_CASES_PURE_ADDRESS_AS_HEX, ) def test_pure_address_as_hex(pure_address, excepted_output): - """Test PureAddress hex output""" + """Test PureAddress hex output.""" assert pure_address.as_hex == excepted_output @@ -117,7 +116,7 @@ def test_pure_address_as_hex(pure_address, excepted_output): TEST_CASES_PURE_ADDRESS_NUM_TO_IPV4, ) def test_pure_address_num_to_ipv4(pure_address, excepted_output): - """Test PureAddress num to IPv4 string conversion""" + """Test PureAddress num to IPv4 string conversion.""" assert pure_address.num_to_ipv4() == excepted_output @@ -126,7 +125,7 @@ def test_pure_address_num_to_ipv4(pure_address, excepted_output): TEST_CASES_PURE_ADDRESS_NUM_TO_IPV6, ) def test_pure_address_num_to_ipv6(pure_address, excepted_output): - """Test PureAddress num to IPv6 string conversion""" + """Test PureAddress num to IPv6 string conversion.""" assert pure_address.num_to_ipv6() == excepted_output @@ -135,7 +134,7 @@ def test_pure_address_num_to_ipv6(pure_address, excepted_output): TEST_CASES_PURE_ADDRESS_NUM_TO_IPV6_NO_SHORTENING, ) def test_pure_address_num_to_ipv6_no_shortening(pure_address, excepted_output): - """Test PureAddress num to IPv6 string conversion without shortening""" + """Test PureAddress num to IPv6 string conversion without shortening.""" assert pure_address.num_to_ipv6(shorten=False) == excepted_output @@ -144,15 +143,12 @@ def test_pure_address_num_to_ipv6_no_shortening(pure_address, excepted_output): TEST_CASES_PURE_ADDRESS_NUM_TO_IPV6_REMOVE_ZEROS, ) def test_pure_address_num_to_ipv6_remove_zeroes(pure_address, excepted_output): - """Test PureAddress num to IPv6 string conversion with empty segment removal""" + """Test PureAddress num to IPv6 string conversion with empty segment removal.""" assert pure_address.num_to_ipv6(remove_zeroes=True) == excepted_output def test_pure_address_num_to_ipv6_remove_zeroes_no_shortening(): - """ - Test PureAddress num to IPv6 string conversion without - shortening but segment removal applied - """ + """Test PureAddress num to IPv6 string conversion without shortening but segment removal applied.""" assert PureAddress(0xBADC_0FFE_E0DD_F00D).num_to_ipv6(shorten=False, remove_zeroes=True) == '::BADC:0FFE:E0DD:F00D' @@ -161,7 +157,7 @@ def test_pure_address_num_to_ipv6_remove_zeroes_no_shortening(): TEST_CASES_IPADDRESS, ) def test_ipaddress(ip_address, excepted_instance): - """Test the IPAddress class""" + """Test the IPAddress class.""" assert isinstance(ip_address, excepted_instance) @@ -170,7 +166,7 @@ def test_ipaddress(ip_address, excepted_instance): TEST_CASES_IPADDRESS_EQUALITY, ) def test_ipaddress_equality(ip_address, excepted_output): - """Test IPAddress equality""" + """Test IPAddress equality.""" assert ip_address == excepted_output @@ -179,11 +175,12 @@ def test_ipaddress_equality(ip_address, excepted_output): TEST_CASES_IPADDRESS_STRING, ) def test_ipaddress_string(ip_address, excepted_output): - """Test IPAddress string representation""" + """Test IPAddress string representation.""" assert str(ip_address) == excepted_output def test_ipaddress_string_error(): + """Test string form errors.""" with pytest.raises(ValueError, match="No valid address representation exists"): str(IPAddress(IPV6_MAX_VALUE + 1)) @@ -193,6 +190,7 @@ def test_ipaddress_string_error(): TEST_CASES_IPADDRESS_REPR, ) def test_ipaddress_repr(ip_address, excepted_output): + """Test IP address representation.""" assert repr(ip_address) == excepted_output @@ -201,7 +199,7 @@ def test_ipaddress_repr(ip_address, excepted_output): TEST_CASES_IPADDRESS_AS_IPV4, ) def test_ipaddress_as_ipv4(ip_address, excepted_instance): - """Test the IPAddress IPv4 constructor""" + """Test the IPAddress IPv4 constructor.""" assert isinstance(ip_address.as_ipv4, excepted_instance) @@ -210,7 +208,7 @@ def test_ipaddress_as_ipv4(ip_address, excepted_instance): TEST_CASES_IPADDRESS_AS_IPV6, ) def test_ipaddress_as_ipv6(ip_address, excepted_instance): - """Test the IPAddress IPv6 constructor""" + """Test the IPAddress IPv6 constructor.""" assert isinstance(ip_address.as_ipv6, excepted_instance) @@ -218,7 +216,7 @@ def test_ipaddress_as_ipv6(ip_address, excepted_instance): "input_ipv4", TEST_CASES_IPV4, ) def test_ipv4(input_ipv4): - """Test the IPv4 class""" + """Test the IPv4 class.""" assert input_ipv4 @@ -227,7 +225,7 @@ def test_ipv4(input_ipv4): TEST_CASES_IPV4_STRING, ) def test_ipv4_string(input_ipv4, excepted_output): - """Test IPv4 string representation""" + """Test IPv4 string representation.""" assert str(input_ipv4) == excepted_output @@ -236,7 +234,7 @@ def test_ipv4_string(input_ipv4, excepted_output): TEST_CASES_IPV4_IPV4_TO_NUM, ) def test_ipv4_ipv4_to_num(input_ipv4, excepted_output): - """Test IPv4 to num conversion""" + """Test IPv4 to num conversion.""" assert input_ipv4._ipv4_to_num() == excepted_output # noqa: SLF001 @@ -244,7 +242,7 @@ def test_ipv4_ipv4_to_num(input_ipv4, excepted_output): "input_ipv6", TEST_CASES_IPV6, ) def test_ipv6(input_ipv6): - """Test the IPv6 class""" + """Test the IPv6 class.""" assert input_ipv6 @@ -253,7 +251,7 @@ def test_ipv6(input_ipv6): TEST_CASES_IPV6_STRING, ) def test_ipv6_string(input_ipv6, excepted_output): - """Test IPv6 string representation""" + """Test IPv6 string representation.""" assert str(input_ipv6) == excepted_output @@ -262,7 +260,7 @@ def test_ipv6_string(input_ipv6, excepted_output): TEST_CASES_IPV6_IPV6_TO_NUM, ) def test_ipv6_ipv6_to_num(input_ipv6, excepted_output): - """Test IPv6 to num conversion""" + """Test IPv6 to num conversion.""" assert IPv6(input_ipv6)._ipv6_to_num() == excepted_output # noqa: SLF001 @@ -271,5 +269,6 @@ def test_ipv6_ipv6_to_num(input_ipv6, excepted_output): TEST_CASES_IPV6_IPV6_TO_NUM_ERRORS, ) def test_ipv6_ipv6_to_num_errors(input_ipv6, error, match_message): + """Test errors converting IPv6 into number.""" with pytest.raises(error, match=match_message): IPv6(input_ipv6)._ipv6_to_num() # noqa: SLF001 diff --git a/tests/test_cases_address.py b/tests/test_cases_address.py index d8203ba..b9c1f6f 100644 --- a/tests/test_cases_address.py +++ b/tests/test_cases_address.py @@ -1,3 +1,5 @@ +"""Address test cases.""" + from iplib3 import IPAddress, IPv4, IPv6 from iplib3.address import PureAddress from iplib3.constants import ( @@ -10,19 +12,19 @@ PURE_ADDRESS_MASK = [ '127.0.0.1', '0:0:0:0:0:0:0:1', - ':'.join(['0', '0', '0', '0', 'DEAD', 'C0DE', '1057', 'BE17']), - ':'.join(['0', '0', '0', '0', 'BADC', 'FFE', 'E0DD', 'F00D']), + '0:0:0:0:DEAD:C0DE:1057:BE17', + '0:0:0:0:BADC:FFE:E0DD:F00D', '0000:0000:0000:0000:0000:0000:0000:0001', ':'.join(['0000'] * 4 + ['DEAD', 'C0DE', '1057', 'BE17']), ':'.join(['0000'] * 4 + ['BADC', '0FFE', 'E0DD', 'F00D']), '::1', - '::' + ':'.join(['DEAD', 'C0DE', '1057', 'BE17']), - '::' + ':'.join(['BADC', 'FFE', 'E0DD', 'F00D']), + '::' + 'DEAD:C0DE:1057:BE17', + '::' + 'BADC:FFE:E0DD:F00D', ] IP_ADDRESS_MASK = [ ':'.join(['0'] * 5 + ['DEAD', 'DEAD', 'BEEF']), - '::' + ':'.join(['DEAD', 'BEEF']), + '::' + 'DEAD:BEEF', '127.0.0.1', ] @@ -30,14 +32,14 @@ '127.0.0.1', '127.0.0.1:80', '127.0.0.1:8080', - '.'.join(['192', '168', '0', '1']), + '192.168.0.1', ] IPV6_MASK = [ - ':'.join(['2606', '4700', '4700', '', '1111']), - f"[{':'.join(['2606', '4700', '4700', '', '1111'])}]:80", + '2606:4700:4700::1111', + "[2606:4700:4700::1111]:80", '0:0:0:0:0:0:0:1', - f"[{':'.join(['2606', '4700', '4700', '', '1111'])}]:8080", + "[2606:4700:4700::1111]:8080", '70::', ] diff --git a/tests/test_cases_subnet.py b/tests/test_cases_subnet.py index b62b94e..93b0ec3 100644 --- a/tests/test_cases_subnet.py +++ b/tests/test_cases_subnet.py @@ -1,3 +1,5 @@ +"""Subnet test cases.""" + from iplib3.constants import ( IPV4_MAX_SUBNET_VALUE, IPV4_MIN_SUBNET_VALUE, diff --git a/tests/test_cases_validators.py b/tests/test_cases_validators.py index ca922c1..19a5fdd 100644 --- a/tests/test_cases_validators.py +++ b/tests/test_cases_validators.py @@ -1,3 +1,5 @@ +"""Validator test cases.""" + from iplib3.constants import ( IPV4_MAX_SUBNET_VALUE, IPV4_MAX_VALUE, diff --git a/tests/test_subnet.py b/tests/test_subnet.py index d3607e9..f577d7c 100644 --- a/tests/test_subnet.py +++ b/tests/test_subnet.py @@ -1,11 +1,6 @@ -"""Unit tests for iplib3.subnet""" +"""Unit tests for iplib3.subnet.""" import pytest - -from iplib3.subnet import ( - PureSubnetMask, - SubnetMask, -) from tests.test_cases_subnet import ( TEST_CASES_PURE_SUBNET_MASK_EQUALITY, TEST_CASES_PURE_SUBNET_MASK_INEQUALITY, @@ -20,9 +15,14 @@ TEST_CASES_SUBNET_MASK_SUBNET_TYPE, ) +from iplib3.subnet import ( + PureSubnetMask, + SubnetMask, +) + def test_pure_subnet_mask(): - """Test the PureSubnetMask base class""" + """Test the PureSubnetMask base class.""" _ = PureSubnetMask() @@ -31,7 +31,7 @@ def test_pure_subnet_mask(): TEST_CASES_PURE_SUBNET_MASK_PREFIX_LENGTH, ) def test_pure_subnet_mask_prefix_length(subnet, prefix_length): - """Test PureSubnetMask prefix length""" + """Test PureSubnetMask prefix length.""" subnet._prefix_length = prefix_length # noqa: SLF001 assert subnet._prefix_length == prefix_length # noqa: SLF001 @@ -41,7 +41,7 @@ def test_pure_subnet_mask_prefix_length(subnet, prefix_length): TEST_CASES_PURE_SUBNET_MASK_STRING, ) def test_pure_subnet_mask_string(subnet, excepted_output, representation): - """Test PureSubnetMask string representation""" + """Test PureSubnetMask string representation.""" if representation == 'str': assert str(subnet) == excepted_output elif representation == 'repr': @@ -53,7 +53,7 @@ def test_pure_subnet_mask_string(subnet, excepted_output, representation): TEST_CASES_PURE_SUBNET_MASK_EQUALITY, ) def test_pure_subnet_mask_equality(subnet, excepted_output): - """Test PureSubnetMask equality""" + """Test PureSubnetMask equality.""" assert subnet == excepted_output @@ -62,7 +62,7 @@ def test_pure_subnet_mask_equality(subnet, excepted_output): TEST_CASES_PURE_SUBNET_MASK_INEQUALITY, ) def test_pure_subnet_mask_inequality(subnet, excepted_output): - """Test PureSubnetMask inequality""" + """Test PureSubnetMask inequality.""" subnet._prefix_length = None # noqa: SLF001 assert subnet != excepted_output @@ -72,7 +72,7 @@ def test_pure_subnet_mask_inequality(subnet, excepted_output): TEST_CASES_SUBNET_MASK_SUBNET_TYPE, ) def test_subnet_mask_subnet_type(subnet, excepted_output): - """Test SubnetMask subnet type""" + """Test SubnetMask subnet type.""" assert subnet._subnet_type == excepted_output # noqa: SLF001 @@ -81,6 +81,7 @@ def test_subnet_mask_subnet_type(subnet, excepted_output): TEST_CASES_SUBNET_MASK_SUBNET_LENGTH, ) def test_subnet_mask_subnet_length(subnet, error, error_message): + """Test SubnetMask subnet length.""" with pytest.raises(error, match=error_message): SubnetMask._ipv4_subnet_to_num(subnet) # noqa: SLF001 @@ -90,7 +91,7 @@ def test_subnet_mask_subnet_length(subnet, error, error_message): TEST_CASES_SUBNET_MASK_STRING, ) def test_subnet_mask_string(subnet, excepted_output): - """Test SubnetMask string representation""" + """Test SubnetMask string representation.""" assert repr(subnet) == excepted_output @@ -99,7 +100,7 @@ def test_subnet_mask_string(subnet, excepted_output): TEST_CASES_SUBNET_MASK_SUBNET_TO_NUM, ) def test_subnet_mask_subnet_to_num(subnet_mask, subnet_type, excepted_output): - """Test SubnetMask subnet to number converter""" + """Test SubnetMask subnet to number converter.""" assert SubnetMask._subnet_to_num(subnet_mask=subnet_mask, subnet_type=subnet_type) == excepted_output # noqa: SLF001 @@ -108,7 +109,7 @@ def test_subnet_mask_subnet_to_num(subnet_mask, subnet_type, excepted_output): TEST_CASES_SUBNET_MASK_SUBNET_TO_NUM_ERRORS, ) def test_subnet_mask_subnet_to_num_errors(subnet_mask, subnet_type, error, match_message): - """Test SubnetMask subnet to number converter errors""" + """Test SubnetMask subnet to number converter errors.""" with pytest.raises(error, match=match_message): SubnetMask._subnet_to_num(subnet_mask=subnet_mask, subnet_type=subnet_type) # noqa: SLF001 @@ -118,7 +119,7 @@ def test_subnet_mask_subnet_to_num_errors(subnet_mask, subnet_type, error, match TEST_CASES_SUBNET_MASK_PREFIX_TO_SUBNET_MASK, ) def test_subnet_mask_prefix_to_subnet_mask(prefix_length, subnet_type, excepted_output): - """Test SubnetMask number to mask converter""" + """Test SubnetMask number to mask converter.""" assert SubnetMask._prefix_to_subnet_mask(prefix_length=prefix_length, subnet_type=subnet_type) == excepted_output # noqa: SLF001 @@ -127,6 +128,6 @@ def test_subnet_mask_prefix_to_subnet_mask(prefix_length, subnet_type, excepted_ TEST_CASES_SUBNET_MASK_PREFIX_TO_SUBNET_MASK_ERRORS, ) def test_subnet_mask_prefix_to_subnet_mask_errors(prefix_length, subnet_type, error, match_message): - """Test SubnetMask number to mask converter""" + """Test SubnetMask number to mask converter.""" with pytest.raises(error, match=match_message): SubnetMask._prefix_to_subnet_mask(prefix_length=prefix_length, subnet_type=subnet_type) # noqa: SLF001 diff --git a/tests/test_validators.py b/tests/test_validators.py index bb51046..4042f25 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -1,17 +1,6 @@ -"""Unit tests for iplib3.validators""" +"""Unit tests for iplib3.validators.""" import pytest - -from iplib3.validators import ( - _ipv4_subnet_validator, - _ipv6_subnet_validator, - _port_stripper, - ip_validator, - ipv4_validator, - ipv6_validator, - port_validator, - subnet_validator, -) from tests.test_cases_validators import ( TEST_CASES_IP_VALIDATOR, TEST_CASES_IPV4_SUBNET_VALIDATOR, @@ -26,13 +15,24 @@ TEST_CASES_SUBNET_VALIDATOR, ) +from iplib3.validators import ( + _ipv4_subnet_validator, + _ipv6_subnet_validator, + _port_stripper, + ip_validator, + ipv4_validator, + ipv6_validator, + port_validator, + subnet_validator, +) + @pytest.mark.parametrize( ("port_num", "excepted_output"), TEST_CASES_PORT_VALIDATOR, ) def test_port_validator(port_num, excepted_output): - """Test the port validator with None""" + """Test the port validator with None.""" assert port_validator(port_num=port_num) is excepted_output @@ -41,7 +41,7 @@ def test_port_validator(port_num, excepted_output): TEST_CASES_IP_VALIDATOR, ) def test_ip_validator(address, excepted_output): - """Test the generic IP address validator""" + """Test the generic IP address validator.""" assert ip_validator(address=address) is excepted_output @@ -50,6 +50,7 @@ def test_ip_validator(address, excepted_output): TEST_CASES_IPV4_VALIDATOR, ) def test_ipv4_validator(address, strict, excepted_output): + """Test IPv4 validator.""" assert ipv4_validator(address=address, strict=strict) is excepted_output @@ -58,6 +59,7 @@ def test_ipv4_validator(address, strict, excepted_output): TEST_CASES_IPV6_VALIDATOR, ) def test_ipv6_validator(address, strict, excepted_output): + """Test IPv6 validator.""" assert ipv6_validator(address=address, strict=strict) is excepted_output @@ -66,6 +68,7 @@ def test_ipv6_validator(address, strict, excepted_output): TEST_CASES_SUBNET_VALIDATOR, ) def test_subnet_validator(subnet, protocol, excepted_output): + """Test subnet validator.""" assert subnet_validator(subnet=subnet, protocol=protocol) is excepted_output @@ -74,6 +77,7 @@ def test_subnet_validator(subnet, protocol, excepted_output): TEST_CASES_IPV4_SUBNET_VALIDATOR, ) def test_ipv4_subnet_validator(subnet, excepted_output): + """Test IPv4 subnet validator.""" assert _ipv4_subnet_validator(subnet=subnet) is excepted_output @@ -82,7 +86,7 @@ def test_ipv4_subnet_validator(subnet, excepted_output): TEST_CASES_IPV4_SUBNET_VALIDATOR_ERRORS, ) def test_ipv4_subnet_validator_errors(subnet, error): - """Test the IPv4 subnet validator using invalid types""" + """Test the IPv4 subnet validator using invalid types.""" with pytest.raises(error): _ipv4_subnet_validator(subnet=subnet) @@ -92,6 +96,7 @@ def test_ipv4_subnet_validator_errors(subnet, error): TEST_CASES_IPV6_SUBNET_VALIDATOR, ) def test_ipv6_subnet_validator(subnet, excepted_output): + """Test IPv6 subnet validator.""" assert _ipv6_subnet_validator(subnet=subnet) == excepted_output @@ -100,7 +105,7 @@ def test_ipv6_subnet_validator(subnet, excepted_output): TEST_CASES_IPV6_SUBNET_VALIDATOR_ERRORS, ) def test_ipv6_subnet_validator_errors(subnet, error): - """Test the IPv6 subnet validator using invalid types""" + """Test the IPv6 subnet validator using invalid types.""" with pytest.raises(error): _ipv6_subnet_validator(subnet=subnet) @@ -110,7 +115,7 @@ def test_ipv6_subnet_validator_errors(subnet, error): TEST_CASES_PORT_STRIPPER_IPV4, ) def test_port_stripper_ipv4(address, protocol, excepted_address, excepted_port, excepted_valid): - """Test the port stripper with IPv4""" + """Test the port stripper with IPv4.""" address, port, valid = _port_stripper(address=address, protocol=protocol) assert address == excepted_address assert port == excepted_port @@ -122,7 +127,7 @@ def test_port_stripper_ipv4(address, protocol, excepted_address, excepted_port, TEST_CASES_PORT_STRIPPER_IPV6, ) def test_port_stripper_ipv6(address, protocol, excepted_address, excepted_port, excepted_valid): - """Test the port stripper with IPv6""" + """Test the port stripper with IPv6.""" address, port, valid = _port_stripper(address=address, protocol=protocol) assert address == excepted_address assert port == excepted_port @@ -130,7 +135,6 @@ def test_port_stripper_ipv6(address, protocol, excepted_address, excepted_port, def test_port_stripper_invalid_protocol(): - """Test the port stripper for using invalid protocol""" - + """Test the port stripper for using invalid protocol.""" with pytest.raises(ValueError, match="Invalid subnet type"): _port_stripper("127.0.0.1:8080", protocol='IPv9') # type: ignore # noqa: PGH003