├── Dockerfile ├── .dockerignore ├── .flake8 ├── docs └── example-ninja_build_1.8.2.png ├── .pre-commit-config.yaml ├── .travis.yml ├── pyproject.toml ├── LICENSE ├── README.md ├── .gitignore ├── ninjavis.py └── poetry.lock /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7 2 | RUN pip install ninjavis 3 | ENTRYPOINT ["ninjavis"] 4 | 5 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | docs/ 2 | .gitignore 3 | .travis.yml 4 | Dockerfile 5 | LICENSE 6 | README.md 7 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | max-complexity = 10 4 | select = B,C,E,F,W,T4,B9 5 | 6 | -------------------------------------------------------------------------------- /docs/example-ninja_build_1.8.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chagui/ninjavis/HEAD/docs/example-ninja_build_1.8.2.png -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v6.0.0 4 | hooks: 5 | - id: check-ast 6 | - id: check-docstring-first 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-yaml 10 | - id: fix-encoding-pragma 11 | - id: mixed-line-ending 12 | - id: trailing-whitespace 13 | 14 | - repo: https://github.com/ambv/black 15 | rev: 25.1.0 16 | hooks: 17 | - id: black 18 | 19 | 20 | - repo: https://github.com/pycqa/isort 21 | rev: 6.0.1 22 | hooks: 23 | - id: isort 24 | name: isort 25 | 26 | - repo: https://github.com/pycqa/flake8 27 | rev: 7.3.0 28 | hooks: 29 | - id: flake8 30 | additional_dependencies: [flake8-bugbear, flake8-comprehensions] 31 | 32 | - repo: https://github.com/pre-commit/mirrors-mypy 33 | rev: v1.17.1 34 | hooks: 35 | - id: mypy 36 | 37 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | language: python 3 | python: 4 | - '3.6' 5 | before_install: 6 | - pip install poetry 7 | install: 8 | - poetry install -v 9 | script: 10 | - poetry run pylint ninjavis.py 11 | - poetry run mypy ninjavis.py 12 | deploy: 13 | provider: script 14 | script: poetry publish --build 15 | skip_existing: true 16 | on: 17 | tags: true 18 | branch: main 19 | env: 20 | global: 21 | - secure: mYX813z6kURtqP6DrR19khqkFeqxcXz8o7jqCS5Weecw2VuHdlSO4P0QGJD3KwqDXsOtx4TftE2D1cevcTu4UrZijJVmFZKZOYGxuYyCzjy2E4CfXw5HsQmahGDmpud4hPZlMLCK9lgT1Bif7YEGG7w0i/4YhBUYNyLUageKTTTpuCNgcSx+IfczDqoDNSLgmrYcnm/c6VFVa8M9AEpY5MAvHZILrz/JPY/+h68O2G8mkDFW1DNBth9Sfh/214TSUyscivl8IB5mo6YHYB0U7f4nmR3zY0YlWYL5Rls7nPfN6jyBYYowiICm1OSGVtcUlQyzy4hFMfqMUkLPrh+E7aSxOgze0dW0UC8WbPUFb6gSRSAjuKIwY8gpECouGWvdBl8Mp7+9aZtN8OVT3jAg+FpxL6VyqNgPahhW8KuAFxn3qbKr4Jwp0oHx3s+RTEnQKbSLB2qg3OErS1uCOaSM+T5i5tVFJ7+IwbWLYjgmkJpL0NybRTopj+g/Plf4UjyJpFVDiyzpFCl/TDV1ofLxi5SIvfbyc5EiMoBNhyhIyIwvxHJgvrDbvAuK2p5lINax28c0LJoFAkqfDr7PokaSLy93Bnl/plUlN3a7PCAMVZCHEKX3rmsnGCR4yt1tncrFiRqeDaFXGRoN0g+GGkbBW34ZwGQoOIuidJ2cjiLhYic= 22 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "ninjavis" 3 | version = "0.2.1" 4 | description = "Generate visualization from Ninja build logs." 5 | authors = ["Guilhem Charles"] 6 | readme = "README.md" 7 | license = "MIT" 8 | homepage = "https://github.com/chagui/ninjavis" 9 | repository = "https://github.com/chagui/ninjavis" 10 | 11 | [tool.poetry.dependencies] 12 | python = "^3.12" 13 | 14 | [tool.poetry.dev-dependencies] 15 | pylint = "^3.1" 16 | mypy = "^1.10" 17 | black = "^24.4" 18 | isort = "^5.13" 19 | flake8 = "7.0" 20 | flake8-bugbear = "^24.4" 21 | flake8-comprehensions = "^3.10.0" 22 | 23 | [tool.poetry.scripts] 24 | ninjavis = "ninjavis:main" 25 | 26 | [build-system] 27 | requires = ["poetry-core>=1.0.0"] 28 | build-backend = "poetry.core.masonry.api" 29 | 30 | [tool.black] 31 | line-length = 120 32 | target-version = ["py37", "py38", "py39", "py310"] 33 | include = '\.pyi?$' 34 | 35 | [tool.isort] 36 | balanced_wrapping = true 37 | combine_as_imports = true 38 | indent = " " 39 | lexicographical = true 40 | multi_line_output = 3 41 | order_by_type = false 42 | profile = "black" 43 | 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Guilhem C. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ninjavis # 2 | [![build](https://travis-ci.org/chagui/ninjavis.png)](https://travis-ci.org/chagui/ninjavis) 3 | [![version](https://pypip.in/v/ninjavis/badge.png?style=flat)](https://pypi.python.org/pypi/ninjavis) 4 | [![format](https://pypip.in/format/ninjavis/badge.png?style=flat)](https://pythonwheels.com/) 5 | [![license](https://pypip.in/license/ninjavis/badge.png?style=flat)](https://pypi.python.org/pypi/ninjavis) 6 | 7 | 8 | ## Introduction ## 9 | Generate visualization from [Ninja](https://github.com/ninja-build/ninja) build logs. Ninjavis parse the ninja build 10 | logs and for each item of the build extract its target, starting and end time. 11 | It output those information in a template containing a simple timeline ; the visualization is done by [vis.js](http://visjs.org/). 12 | 13 | Inspired by [buildbloat](https://github.com/nico/buildbloat). 14 | 15 | ## Usage ## 16 | ```bash 17 | usage: ninjavis --title "my build" ninja_build.log build_profile.html 18 | ``` 19 | :warning: Run ``ninja -t recompact`` first to make sure that no duplicate entries are in the build log. 20 | 21 | ## Example ## 22 | Profile of Ninja 1.8.2 build 23 | ![Ninja 1.8.2 build profile](https://raw.githubusercontent.com/chagui/ninjavis/main/docs/example-ninja_build_1.8.2.png) 24 | 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /ninjavis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # :copyright: (c) 2019-2020 Guilhem Charles. All rights reserved. 4 | """Generate visualization of a ninja build from its logs.""" 5 | import argparse 6 | import re 7 | import sys 8 | from os.path import getmtime 9 | from typing import List, Optional 10 | 11 | TIMELINE = """ 12 | 13 | 14 | 15 | {title} 16 | 17 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 41 | 42 | 43 | """ 44 | 45 | 46 | def generate_build_profile(logfile: str, time_offset: int) -> List[dict]: 47 | """ 48 | Parse a ninja build log file and generates a profile. A profile consist of the list of item 49 | part of the build. 50 | 51 | :param logfile: Path to the build log file. 52 | :param time_offset: Start time of the visualization. 53 | :return: Profile of the build. 54 | """ 55 | 56 | def parse_build_entry(line: str) -> Optional[dict]: 57 | try: 58 | # ignore comments 59 | if line[:1] != "#": 60 | start_time, end_time, _, command, _ = line.split() 61 | return { 62 | "content": command, 63 | "start": int(start_time) + time_offset, 64 | "end": int(end_time) + time_offset, 65 | } 66 | except ValueError: 67 | print(f"error: could not parse {line}", file=sys.stderr) 68 | return None 69 | 70 | profile = [] 71 | with open(logfile, "r") as build_log: 72 | # first line might be a header specifying ninja build log version 73 | header = build_log.readline() 74 | log_version = re.search(r"# ninja log v(\d+)", header) 75 | if log_version: 76 | parsed_version = log_version.group(1) 77 | if int(parsed_version) != 5: 78 | raise RuntimeError(f"unsupported log file version: {parsed_version}") 79 | else: 80 | # header is a log entry 81 | parsed_project = parse_build_entry(header) 82 | if parsed_project: 83 | profile = [parsed_project] 84 | # handle remaining lines, filter out entries that could not be parsed 85 | profile.extend(filter(None, (parse_build_entry(line) for line in build_log))) 86 | return profile 87 | 88 | 89 | def generate_timeline_from(profile: List[dict], output: str, title: str): 90 | """ 91 | Generate a visjs timeline from the ninja build profile. 92 | 93 | :param profile: Ninja build information. 94 | :param output: File to output the visualization. 95 | :param title: Title of the visualization. 96 | :return: 97 | """ 98 | try: 99 | with open(output, "w") as visualization: 100 | visualization.write(TIMELINE.format(title=title, dataset=profile)) 101 | except RuntimeError as exc: 102 | print(f"error: could not generate timeline: {exc}", file=sys.stderr) 103 | sys.exit(1) 104 | 105 | 106 | def get_argparser() -> argparse.ArgumentParser: 107 | """ 108 | ninjavis arguments parser. 109 | 110 | :return: Arguments parser. 111 | """ 112 | parser = argparse.ArgumentParser( 113 | prog="ninjavis", 114 | description="Parse ninja build log file and " "generates a timeline of the build", 115 | ) 116 | parser.add_argument("logfile", help="Ninja build log (.ninja_log)") 117 | parser.add_argument("output", help="Output file for the visualization") 118 | parser.add_argument("--title", help="Visualization title", default="Ninja build") 119 | return parser 120 | 121 | 122 | def main(): 123 | """ 124 | Parse the arguments and try to generate a visualization out of the provided build log. 125 | 126 | :return: 127 | """ 128 | args = get_argparser().parse_args(sys.argv[1:]) 129 | 130 | try: 131 | profile = generate_build_profile(args.logfile, int(getmtime(args.logfile))) 132 | generate_timeline_from(profile, args.output, args.title) 133 | except (RuntimeError, FileNotFoundError) as err: 134 | print(err, file=sys.stderr) 135 | sys.exit(1) 136 | sys.exit(0) 137 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "astroid" 5 | version = "3.1.0" 6 | description = "An abstract syntax tree for Python with inference support." 7 | optional = false 8 | python-versions = ">=3.8.0" 9 | files = [ 10 | {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, 11 | {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, 12 | ] 13 | 14 | [[package]] 15 | name = "attrs" 16 | version = "23.2.0" 17 | description = "Classes Without Boilerplate" 18 | optional = false 19 | python-versions = ">=3.7" 20 | files = [ 21 | {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, 22 | {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, 23 | ] 24 | 25 | [package.extras] 26 | cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] 27 | dev = ["attrs[tests]", "pre-commit"] 28 | docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] 29 | tests = ["attrs[tests-no-zope]", "zope-interface"] 30 | tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] 31 | tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] 32 | 33 | [[package]] 34 | name = "black" 35 | version = "24.4.1" 36 | description = "The uncompromising code formatter." 37 | optional = false 38 | python-versions = ">=3.8" 39 | files = [ 40 | {file = "black-24.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f7749fd0d97ff9415975a1432fac7df89bf13c3833cea079e55fa004d5f28c0"}, 41 | {file = "black-24.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859f3cc5d2051adadf8fd504a01e02b0fd866d7549fff54bc9202d524d2e8bd7"}, 42 | {file = "black-24.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59271c9c29dfa97f7fda51f56c7809b3f78e72fd8d2205189bbd23022a0618b6"}, 43 | {file = "black-24.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:5ed9c34cba223149b5a0144951a0f33d65507cf82c5449cb3c35fe4b515fea9a"}, 44 | {file = "black-24.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9dae3ae59d6f2dc93700fd5034a3115434686e66fd6e63d4dcaa48d19880f2b0"}, 45 | {file = "black-24.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5f8698974a81af83283eb47644f2711b5261138d6d9180c863fce673cbe04b13"}, 46 | {file = "black-24.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f404b6e77043b23d0321fb7772522b876b6de737ad3cb97d6b156638d68ce81"}, 47 | {file = "black-24.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:c94e52b766477bdcd010b872ba0714d5458536dc9d0734eff6583ba7266ffd89"}, 48 | {file = "black-24.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:962d9e953872cdb83b97bb737ad47244ce2938054dc946685a4cad98520dab38"}, 49 | {file = "black-24.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b1d8e3b2486b7dd522b1ab2ba1ec4907f0aa8f5e10a33c4271fb331d1d10b70c"}, 50 | {file = "black-24.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed77e214b785148f57e43ca425b6e0850165144aa727d66ac604e56a70bb7825"}, 51 | {file = "black-24.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:4ef4764437d7eba8386689cd06e1fb5341ee0ae2e9e22582b21178782de7ed94"}, 52 | {file = "black-24.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:92b183f8eef5baf7b20a513abcf982ad616f544f593f6688bb2850d2982911f1"}, 53 | {file = "black-24.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:945abd7b3572add997757c94295bb3e73c6ffaf3366b1f26cb2356a4bffd1dc3"}, 54 | {file = "black-24.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db5154b9e5b478031371d8bc41ff37b33855fa223a6cfba456c9b73fb96f77d4"}, 55 | {file = "black-24.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:afc84c33c1a9aaf3d73140cee776b4ddf73ff429ffe6b7c56dc1c9c10725856d"}, 56 | {file = "black-24.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0889f4eb8b3bdf8b189e41a71cf0dbb8141a98346cd1a2695dea5995d416e940"}, 57 | {file = "black-24.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5bb0143f175db45a55227eefd63e90849d96c266330ba31719e9667d0d5ec3b9"}, 58 | {file = "black-24.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:713a04a78e78f28ef7e8df7a16fe075670ea164860fcef3885e4f3dffc0184b3"}, 59 | {file = "black-24.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:171959bc879637a8cdbc53dc3fddae2a83e151937a28cf605fd175ce61e0e94a"}, 60 | {file = "black-24.4.1-py3-none-any.whl", hash = "sha256:ecbab810604fe02c70b3a08afd39beb599f7cc9afd13e81f5336014133b4fe35"}, 61 | {file = "black-24.4.1.tar.gz", hash = "sha256:5241612dc8cad5b6fd47432b8bd04db80e07cfbc53bb69e9ae18985063bcb8dd"}, 62 | ] 63 | 64 | [package.dependencies] 65 | click = ">=8.0.0" 66 | mypy-extensions = ">=0.4.3" 67 | packaging = ">=22.0" 68 | pathspec = ">=0.9.0" 69 | platformdirs = ">=2" 70 | 71 | [package.extras] 72 | colorama = ["colorama (>=0.4.3)"] 73 | d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] 74 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 75 | uvloop = ["uvloop (>=0.15.2)"] 76 | 77 | [[package]] 78 | name = "click" 79 | version = "8.1.7" 80 | description = "Composable command line interface toolkit" 81 | optional = false 82 | python-versions = ">=3.7" 83 | files = [ 84 | {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, 85 | {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, 86 | ] 87 | 88 | [package.dependencies] 89 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 90 | 91 | [[package]] 92 | name = "colorama" 93 | version = "0.4.6" 94 | description = "Cross-platform colored terminal text." 95 | optional = false 96 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 97 | files = [ 98 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 99 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 100 | ] 101 | 102 | [[package]] 103 | name = "dill" 104 | version = "0.3.8" 105 | description = "serialize all of Python" 106 | optional = false 107 | python-versions = ">=3.8" 108 | files = [ 109 | {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, 110 | {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, 111 | ] 112 | 113 | [package.extras] 114 | graph = ["objgraph (>=1.7.2)"] 115 | profile = ["gprof2dot (>=2022.7.29)"] 116 | 117 | [[package]] 118 | name = "flake8" 119 | version = "7.0.0" 120 | description = "the modular source code checker: pep8 pyflakes and co" 121 | optional = false 122 | python-versions = ">=3.8.1" 123 | files = [ 124 | {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, 125 | {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, 126 | ] 127 | 128 | [package.dependencies] 129 | mccabe = ">=0.7.0,<0.8.0" 130 | pycodestyle = ">=2.11.0,<2.12.0" 131 | pyflakes = ">=3.2.0,<3.3.0" 132 | 133 | [[package]] 134 | name = "flake8-bugbear" 135 | version = "24.4.21" 136 | description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." 137 | optional = false 138 | python-versions = ">=3.8.1" 139 | files = [ 140 | {file = "flake8_bugbear-24.4.21-py3-none-any.whl", hash = "sha256:58581060a1650f4b11344795db8a4934867d4450486319ece86d7720a9414036"}, 141 | {file = "flake8_bugbear-24.4.21.tar.gz", hash = "sha256:d1a87b8f6ca1ed28772c36515f751ea3709e041d78bca60590a570b9cb802e55"}, 142 | ] 143 | 144 | [package.dependencies] 145 | attrs = ">=19.2.0" 146 | flake8 = ">=6.0.0" 147 | 148 | [package.extras] 149 | dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] 150 | 151 | [[package]] 152 | name = "flake8-comprehensions" 153 | version = "3.14.0" 154 | description = "A flake8 plugin to help you write better list/set/dict comprehensions." 155 | optional = false 156 | python-versions = ">=3.8" 157 | files = [ 158 | {file = "flake8_comprehensions-3.14.0-py3-none-any.whl", hash = "sha256:7b9d07d94aa88e62099a6d1931ddf16c344d4157deedf90fe0d8ee2846f30e97"}, 159 | {file = "flake8_comprehensions-3.14.0.tar.gz", hash = "sha256:81768c61bfc064e1a06222df08a2580d97de10cb388694becaf987c331c6c0cf"}, 160 | ] 161 | 162 | [package.dependencies] 163 | flake8 = ">=3.0,<3.2.0 || >3.2.0" 164 | 165 | [[package]] 166 | name = "isort" 167 | version = "5.13.2" 168 | description = "A Python utility / library to sort Python imports." 169 | optional = false 170 | python-versions = ">=3.8.0" 171 | files = [ 172 | {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, 173 | {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, 174 | ] 175 | 176 | [package.extras] 177 | colors = ["colorama (>=0.4.6)"] 178 | 179 | [[package]] 180 | name = "mccabe" 181 | version = "0.7.0" 182 | description = "McCabe checker, plugin for flake8" 183 | optional = false 184 | python-versions = ">=3.6" 185 | files = [ 186 | {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, 187 | {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, 188 | ] 189 | 190 | [[package]] 191 | name = "mypy" 192 | version = "1.10.0" 193 | description = "Optional static typing for Python" 194 | optional = false 195 | python-versions = ">=3.8" 196 | files = [ 197 | {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"}, 198 | {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"}, 199 | {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"}, 200 | {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"}, 201 | {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"}, 202 | {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"}, 203 | {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"}, 204 | {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"}, 205 | {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"}, 206 | {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"}, 207 | {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"}, 208 | {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"}, 209 | {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"}, 210 | {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"}, 211 | {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"}, 212 | {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"}, 213 | {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"}, 214 | {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"}, 215 | {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"}, 216 | {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"}, 217 | {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"}, 218 | {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"}, 219 | {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"}, 220 | {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"}, 221 | {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"}, 222 | {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"}, 223 | {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"}, 224 | ] 225 | 226 | [package.dependencies] 227 | mypy-extensions = ">=1.0.0" 228 | typing-extensions = ">=4.1.0" 229 | 230 | [package.extras] 231 | dmypy = ["psutil (>=4.0)"] 232 | install-types = ["pip"] 233 | mypyc = ["setuptools (>=50)"] 234 | reports = ["lxml"] 235 | 236 | [[package]] 237 | name = "mypy-extensions" 238 | version = "1.0.0" 239 | description = "Type system extensions for programs checked with the mypy type checker." 240 | optional = false 241 | python-versions = ">=3.5" 242 | files = [ 243 | {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, 244 | {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, 245 | ] 246 | 247 | [[package]] 248 | name = "packaging" 249 | version = "24.0" 250 | description = "Core utilities for Python packages" 251 | optional = false 252 | python-versions = ">=3.7" 253 | files = [ 254 | {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, 255 | {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, 256 | ] 257 | 258 | [[package]] 259 | name = "pathspec" 260 | version = "0.12.1" 261 | description = "Utility library for gitignore style pattern matching of file paths." 262 | optional = false 263 | python-versions = ">=3.8" 264 | files = [ 265 | {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, 266 | {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, 267 | ] 268 | 269 | [[package]] 270 | name = "platformdirs" 271 | version = "4.2.1" 272 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." 273 | optional = false 274 | python-versions = ">=3.8" 275 | files = [ 276 | {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, 277 | {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, 278 | ] 279 | 280 | [package.extras] 281 | docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] 282 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] 283 | type = ["mypy (>=1.8)"] 284 | 285 | [[package]] 286 | name = "pycodestyle" 287 | version = "2.11.1" 288 | description = "Python style guide checker" 289 | optional = false 290 | python-versions = ">=3.8" 291 | files = [ 292 | {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, 293 | {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, 294 | ] 295 | 296 | [[package]] 297 | name = "pyflakes" 298 | version = "3.2.0" 299 | description = "passive checker of Python programs" 300 | optional = false 301 | python-versions = ">=3.8" 302 | files = [ 303 | {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, 304 | {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, 305 | ] 306 | 307 | [[package]] 308 | name = "pylint" 309 | version = "3.1.0" 310 | description = "python code static checker" 311 | optional = false 312 | python-versions = ">=3.8.0" 313 | files = [ 314 | {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, 315 | {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, 316 | ] 317 | 318 | [package.dependencies] 319 | astroid = ">=3.1.0,<=3.2.0-dev0" 320 | colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} 321 | dill = {version = ">=0.3.7", markers = "python_version >= \"3.12\""} 322 | isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" 323 | mccabe = ">=0.6,<0.8" 324 | platformdirs = ">=2.2.0" 325 | tomlkit = ">=0.10.1" 326 | 327 | [package.extras] 328 | spelling = ["pyenchant (>=3.2,<4.0)"] 329 | testutils = ["gitpython (>3)"] 330 | 331 | [[package]] 332 | name = "tomlkit" 333 | version = "0.12.4" 334 | description = "Style preserving TOML library" 335 | optional = false 336 | python-versions = ">=3.7" 337 | files = [ 338 | {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, 339 | {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, 340 | ] 341 | 342 | [[package]] 343 | name = "typing-extensions" 344 | version = "4.11.0" 345 | description = "Backported and Experimental Type Hints for Python 3.8+" 346 | optional = false 347 | python-versions = ">=3.8" 348 | files = [ 349 | {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, 350 | {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, 351 | ] 352 | 353 | [metadata] 354 | lock-version = "2.0" 355 | python-versions = "^3.12" 356 | content-hash = "0911f1698e7609a2426ccb9d0fa5d68c934498c22e3777710519c1d4539571cd" 357 | --------------------------------------------------------------------------------