diff --git a/flake.nix b/flake.nix index 9e98fa6..d651596 100644 --- a/flake.nix +++ b/flake.nix @@ -20,12 +20,21 @@ poetry2nix = forAllSystems (system: inputs.poetry2nix-lib.lib.mkPoetry2Nix {pkgs = pkgs.${system};}); in { # `nix build` - packages = forAllSystems (system: { - default = poetry2nix.${system}.mkPoetryApplication { - projectDir = self; - checkPhase = "mypy . && pytest"; - preferWheels = true; # avoid long build process for `mypy` - }; + packages = forAllSystems (system: let + mkPackage = {debug ? false}: + poetry2nix.${system}.mkPoetryApplication { + projectDir = self; + checkPhase = + if debug + then "pyright && pytest" + else ""; + # doCheck = debug; + preferWheels = false; + nativeBuildInputs = with pkgs.${system}; [pyright]; + }; + in { + default = mkPackage {debug = false;}; + debug = mkPackage {debug = true;}; vm = self.nixosConfigurations.vm.config.microvm.declaredRunner; }); @@ -49,6 +58,7 @@ # Use this shell for developing your app. default = pkgs.${system}.mkShellNoCC { inputsFrom = [self.packages.${system}.default]; + packages = [self.packages.${system}.default]; }; # Shell for poetry. diff --git a/poetry.lock b/poetry.lock index ae554e1..517765f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -31,6 +31,17 @@ doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphin test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] trio = ["trio (>=0.23)"] +[[package]] +name = "astroid" +version = "3.2.4" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25"}, + {file = "astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a"}, +] + [[package]] name = "certifi" version = "2024.7.4" @@ -166,6 +177,21 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + [[package]] name = "fastapi" version = "0.110.3" @@ -230,60 +256,28 @@ files = [ ] [[package]] -name = "mypy" -version = "1.11.1" -description = "Optional static typing for Python" +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." optional = false -python-versions = ">=3.8" +python-versions = ">=3.8.0" files = [ - {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"}, - {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"}, - {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"}, - {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"}, - {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, - {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, - {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, - {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, - {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, - {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, - {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, - {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"}, - {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"}, - {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"}, - {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"}, - {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"}, - {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"}, - {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"}, - {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"}, - {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"}, - {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"}, - {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, - {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, ] -[package.dependencies] -mypy-extensions = ">=1.0.0" -typing-extensions = ">=4.6.0" - [package.extras] -dmypy = ["psutil (>=4.0)"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] +colors = ["colorama (>=0.4.6)"] [[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] [[package]] @@ -297,6 +291,22 @@ files = [ {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] +[[package]] +name = "platformdirs" +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + [[package]] name = "pluggy" version = "1.5.0" @@ -435,6 +445,33 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pylint" +version = "3.2.6" +description = "python code static checker" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "pylint-3.2.6-py3-none-any.whl", hash = "sha256:03c8e3baa1d9fb995b12c1dbe00aa6c4bcef210c2a2634374aedeb22fb4a8f8f"}, + {file = "pylint-3.2.6.tar.gz", hash = "sha256:a5d01678349454806cff6d886fb072294f56a58c4761278c97fb557d708e1eb3"}, +] + +[package.dependencies] +astroid = ">=3.2.4,<=3.3.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, +] +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomlkit = ">=0.10.1" + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + [[package]] name = "pytest" version = "8.3.2" @@ -504,6 +541,17 @@ anyio = ">=3.4.0,<5" [package.extras] full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] +[[package]] +name = "tomlkit" +version = "0.13.0" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomlkit-0.13.0-py3-none-any.whl", hash = "sha256:7075d3042d03b80f603482d69bf0c8f345c2b30e41699fd8883227f89972b264"}, + {file = "tomlkit-0.13.0.tar.gz", hash = "sha256:08ad192699734149f5b97b45f1f18dad7eb1b6d16bc72ad0c2335772650d7b72"}, +] + [[package]] name = "types-requests" version = "2.32.0.20240712" @@ -567,4 +615,4 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "8c95de1de17ced501a0460661e22c77839a3afca88512e07ad95344e719679a5" +content-hash = "e6e581ec65b3dc73ff36e6931a73a3c6f42bef97e9b72473c6bc899c56fce16d" diff --git a/pyproject.toml b/pyproject.toml index a9fbe8e..ef96d07 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,8 +22,8 @@ testdata = "testdata.main:main" [tool.poetry.group.dev.dependencies] pytest = "^8.3.2" requests = "^2.32.3" -mypy = "^1.11.1" types-requests = "^2.32.0.20240712" +pylint = "^3.2.6" [build-system] requires = ["poetry-core"] diff --git a/test/test_run.py b/test/test_run.py index 45f5169..71dc174 100644 --- a/test/test_run.py +++ b/test/test_run.py @@ -55,6 +55,37 @@ def test_get_file(): assert response.content == b'\0' * 32 +def test_get_file_B(): + + response = requests.get( + f'{PROTOCOL}://{HOST}:{PORT}/?api_key={API_KEY}&size=32B', timeout=TIMEOUT) + assert response.content == b'\0' * 32 + + +def test_get_file_KB(): + + response = requests.get( + f'{PROTOCOL}://{HOST}:{PORT}/?api_key={API_KEY}&size=32KB', timeout=TIMEOUT) + assert response.status_code == 200 + assert response.content == b'\0' * 32 * 1000 + + +def test_get_file_KiB(): + + response = requests.get( + f'{PROTOCOL}://{HOST}:{PORT}/?api_key={API_KEY}&size=32KiB', timeout=TIMEOUT) + assert response.status_code == 200 + assert response.content == b'\0' * 32 * 1024 + + +def test_get_file_invalid_format(): + + response = requests.get( + f'{PROTOCOL}://{HOST}:{PORT}/?api_key={API_KEY}&size=32Invalid', timeout=TIMEOUT) + assert response.status_code == 400 + assert response.json()['detail'] == 'Invalid Format.' + + def test_database_data_used(server): requests.get(f'{PROTOCOL}://{HOST}:{PORT}/?api_key={API_KEY}&size=32', timeout = TIMEOUT) diff --git a/test/test_utils.py b/test/test_utils.py new file mode 100644 index 0000000..4391412 --- /dev/null +++ b/test/test_utils.py @@ -0,0 +1,23 @@ +from pytest import raises + +from testdata.utils import convert_to_bytes + + +def test_convert_to_bytes(): + test_values = { + ('0', 0), + ('32', 32), + ('10_000', 10000), + ('999999999999999999', 999999999999999999), + } + + for input, expected_output in test_values: + assert convert_to_bytes(input) == expected_output + + test_exceptions = { + ('9E3', ValueError) + } + + for input, exception in test_exceptions: + with raises(exception): + convert_to_bytes(input) diff --git a/testdata/api.py b/testdata/api.py index f957e0b..438fc90 100644 --- a/testdata/api.py +++ b/testdata/api.py @@ -36,7 +36,8 @@ def create_api(api_keys: set[str], max_size: int, max_data: int, database: str, if body.size < 0: raise MinSizePerRequestError - elif max_size < body.size: + + if max_size < body.size: raise MaxSizePerRequestError db = load_database(database) diff --git a/testdata/utils.py b/testdata/utils.py index bde8924..fe66f4b 100644 --- a/testdata/utils.py +++ b/testdata/utils.py @@ -9,7 +9,7 @@ def convert_to_bytes(size: int | str) -> int: try: return int(size) - except ValueError: + except ValueError as err: units = { 'TB': 1000 ** 4, 'TiB': 1024 ** 4, 'GB': 1000 ** 3, 'GiB': 1024 ** 3, @@ -18,11 +18,13 @@ def convert_to_bytes(size: int | str) -> int: 'B': 1 } - for unit in units: + for unit, value in units.items(): if size.endswith(unit): - return int(float(size.removesuffix(unit)) * units[unit]) - else: - raise ValueError('Invalid format. Expected integer or float ending with a data unit (B, KB, MiB,...).') + return int(float(size.removesuffix(unit)) * value) + + raise ValueError( + 'Invalid format. Expected integer or float ending with a data unit (B, KB, MiB,...).' + ) from err async def generate_data(size: int, buffer_size: int = 4 * 1024) -> AsyncGenerator[bytes, None]: @@ -36,18 +38,18 @@ async def generate_data(size: int, buffer_size: int = 4 * 1024) -> AsyncGenerato size_left -= buffer_size yield b'\0' * buffer_size await asyncio.sleep(0) - else: - yield b'\0' * size_left - await asyncio.sleep(0) - except asyncio.CancelledError: - raise GeneratorExit + + yield b'\0' * size_left + await asyncio.sleep(0) + except asyncio.CancelledError as err: + raise GeneratorExit from err def load_database(path: str) -> dict: - with open(path, 'r') as file: + with open(path, 'r', encoding='utf-8') as file: return json.load(file) def save_database(path: str, database: dict) -> None: - with open(path, 'w') as file: + with open(path, 'w', encoding='utf-8') as file: json.dump(database, file, indent=2)