├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── appimagecraft-aarch64.yml ├── appimagecraft-i386.yml ├── appimagecraft.yml ├── appimagecraft ├── __init__.py ├── __main__.py ├── _cli.py ├── _logging.py ├── _util.py ├── builders │ ├── __init__.py │ ├── autotools.py │ ├── base.py │ ├── cmake.py │ ├── qmake.py │ └── script.py ├── commands │ ├── __init__.py │ ├── base.py │ ├── build_cmd.py │ └── genscripts_cmd.py ├── generators │ ├── __init__.py │ ├── appimage_build_script.py │ ├── appimagecraft_yml.py │ ├── bash_script.py │ ├── build_scripts.py │ └── pre_post_build_scripts.py ├── parsers │ ├── __init__.py │ └── appimagecraft_yml.py └── validators │ ├── __init__.py │ ├── base.py │ ├── exceptions.py │ ├── shellcheck.py │ └── util.py ├── ci ├── build-appimage.sh └── build-example-projects.sh ├── deployment ├── AppRun.sh ├── appimagecraft.desktop └── appimagecraft.svg ├── example-projects ├── autotools │ ├── .gitignore │ ├── Makefile.am │ ├── appimagecraft.yml │ ├── autogen.sh │ ├── configure.ac │ └── main.c ├── cmake │ ├── CMakeLists.txt │ ├── appimagecraft.yml │ └── main.c └── qmake │ ├── .gitignore │ ├── appimagecraft.yml │ ├── example.pro │ └── main.cpp ├── poetry.lock ├── pyproject.toml └── setup.cfg /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Main pipeline 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | APPIMAGE_EXTRACT_AND_RUN: 1 7 | TERM: xterm-256color 8 | 9 | jobs: 10 | test: 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | # apparently these are interpreted as floats, so we have to wrap them in quotes 15 | PYTHON_VERSION: ["3.7", "3.8", "3.9", "3.10"] 16 | name: Test against Python ${{ matrix.PYTHON_VERSION }} 17 | runs-on: ubuntu-latest 18 | env: 19 | PYTHON: python${{ matrix.PYTHON_VERSION }} 20 | steps: 21 | - name: Install Python ${{ matrix.PYTHON_VERSION }} 22 | run: | 23 | sudo add-apt-repository ppa:deadsnakes/ppa 24 | sudo apt-get update 25 | sudo apt-get install -y --no-install-recommends \ 26 | python${{ matrix.PYTHON_VERSION }}-minimal \ 27 | python${{ matrix.PYTHON_VERSION }}-venv 28 | - uses: actions/checkout@v4 29 | - name: Build example projects 30 | run: bash -xe ci/build-example-projects.sh 31 | 32 | appimage: 33 | strategy: 34 | fail-fast: false 35 | matrix: 36 | OS: [ubuntu-latest] 37 | ARCH: [x86_64, i386] 38 | include: 39 | - OS: ubuntu-22.04-arm 40 | ARCH: aarch64 41 | name: Build AppImage for ${{ matrix.ARCH }} 42 | runs-on: ${{ matrix.OS }} 43 | steps: 44 | - uses: actions/checkout@v2 45 | - name: Build AppImage 46 | run: | 47 | export ARCH="${{ matrix.ARCH }}" 48 | bash -xe ci/build-appimage.sh 49 | - name: Archive artifacts 50 | uses: actions/upload-artifact@v4 51 | with: 52 | name: AppImage ${{ matrix.ARCH }} 53 | path: appimagecraft*.AppImage* 54 | 55 | upload: 56 | name: Create release and upload artifacts 57 | runs-on: ubuntu-latest 58 | needs: 59 | - test 60 | - appimage 61 | steps: 62 | - name: Download artifacts 63 | uses: actions/download-artifact@v4 64 | - name: Inspect directory after downloading artifacts 65 | run: ls -alFR 66 | - name: Create release and upload artifacts 67 | env: 68 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 69 | run: | 70 | wget -q https://github.com/TheAssassin/pyuploadtool/releases/download/continuous/pyuploadtool-x86_64.AppImage 71 | chmod +x pyuploadtool-x86_64.AppImage 72 | ./pyuploadtool-x86_64.AppImage **/appimagecraft*.AppImage* 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .appimage-build*/ 3 | __pycache__/ 4 | *.py[c|o] 5 | *-build-*/ 6 | *.egg-info/ 7 | *.AppImage* 8 | *.*swp* 9 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 TheAssassin 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # appimagecraft 2 | 3 | Building AppImages made easy. 4 | 5 | 6 | ## About 7 | 8 | appimagecraft completely automates the AppImage build process, building projects properly out of source and in temporary directories that allow for starting over in every build. This simulates a CI instance, and ensures that no step the developer did by hand can slip through and cause trouble on CI systems or other developers' workstations. 9 | 10 | The tool was developed knowing that most code used to build AppImages on CI systems is copy-pasted from some examples, often just into a CI-specific format that cannot be run locally. This has a few major drawbacks: 11 | 12 | - first of all, local testing consists of copy-pasting instructions from some other files, as most people don't write external scripts and call them from their CI scripts, and there's no way to run locally (*looking at you, Travis CI!*) 13 | - many people state they don't want to know too much about the packaging, they just copy code until there's a result, often producing b-grade quality packages 14 | - to change a minor aspect of packaging, you'd have to find the problem in a large script, instead of having a small compact representation of the process 15 | - those scripts, once copy-pasted, never will be updated and therefore bugs cannot be fixed and new features or ideas won't ever reach people 16 | - also, there's a lot of redundancy in people's AppImage build scripts, which shows that the process can be parametrized. 17 | 18 | appimagecraft solves all these issues. appimagecraft just requires a small YAML file in the repository that define a few aspects of the project (e.g., what build system is used, how to fetch a version number, ...). With those few information, it can guess the required processes in order to build the project and turn it into an AppImage. 19 | 20 | In contrary to many other tools that package applications, appimagecraft makes the entire process introspectable. It generates shell scripts (which it also can call after generation automatically). These scripts are simple to understand (no weird hackish boilerplate code). In case of issues, any developer can look into those scripts, there's no need to learn Python nor to read Python source code or add `print()`s in order to study the behavior of the build process. 21 | 22 | appimagecraft should work for a majority of projects with properly written build scripts. 23 | 24 | 25 | ## Usage 26 | 27 | appimagecraft requires a simple YAML-based file to lay in the repository root directory. Once the file has been written, you just have to run `appimagecraft` (or `appimagecraft build`) in the repository directory. appimagecraft creates a temporary directory and generates a bunch of shell scripts, all of which are called in the right order from a central script. Then, it runs the build scripts, ideally generates an AppImage and moves that back into your repository directory. Now, you should have an AppImage on hand! 28 | 29 | If you want to just generate the scripts, you can run `appimagecraft genscripts -d build/`. This will just generate all the scripts in said directory and exit. You can then inspect the contents. If you want to run the build, just call the `build.sh` script inside that directory. Please beware that the directory will not be cleaned up automatically, and artifacts (such as AppImages) will remain within that directory as well. 30 | 31 | For more information about the scripts, see [Contents of the build directory](#contents-of-the-build-directory). 32 | 33 | 34 | ## Use appimagecraft in a project 35 | 36 | To use appimagecraft in a project, you have to create an `appimagecraft.yml` configuration in the root directory. Please see the [examples](https://github.com/TheAssassin/appimagecraft/tree/master/example-projects) to get an impression of which configuration flags are currently available. If you miss anything, please open a new issue, the list is far from complete. 37 | 38 | The most minimal configuration for a regular CMake based project is: 39 | 40 | ```yml 41 | version: 1 42 | 43 | project: 44 | name: org.my.project 45 | version: 0.1.2 46 | # alternatively, you can specify a command that is run by appimagecraft to generate some version information, e.g.: 47 | # version_command: echo 1.2.3 48 | # version_command: git describe --tags 49 | # the command is run in your repository directory 50 | 51 | build: 52 | cmake: 53 | 54 | appimage: 55 | linuxdeploy: 56 | ``` 57 | 58 | Please note that this relies on a properly working CMake installation setup, which also installs a desktop file and at least one icon. 59 | 60 | You can also specify a custom script to generate or copy files like so: 61 | 62 | ```yaml 63 | scripts: 64 | post_build: 65 | - touch "$BUILD_DIR"/example.svg 66 | - |2 67 | cat > "$BUILD_DIR"/example.desktop < dict: 16 | assert_not_none(data) 17 | 18 | rv = {} 19 | 20 | for i in data: 21 | k, v = i.split("=") 22 | 23 | if " " in k: 24 | raise ValueError("Invalid key format: {}".format(k)) 25 | 26 | if k in rv: 27 | raise KeyError("keys must be unique ({} occurred more than once)".format(k)) 28 | 29 | rv[k] = v 30 | 31 | return rv 32 | -------------------------------------------------------------------------------- /appimagecraft/builders/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import BuilderBase 2 | from .cmake import CMakeBuilder 3 | from .autotools import AutotoolsBuilder 4 | from .qmake import QMakeBuilder 5 | from .script import ScriptBuilder 6 | 7 | 8 | def get_builder_by_name(name: str, config: dict) -> BuilderBase: 9 | if name == "cmake": 10 | return CMakeBuilder.from_dict(config) 11 | 12 | if name == "qmake": 13 | return QMakeBuilder.from_dict(config) 14 | 15 | if name == "script": 16 | return ScriptBuilder.from_dict(config) 17 | 18 | raise ValueError("could not find matching builder for name: {}".format(name)) 19 | 20 | 21 | __all__ = ( 22 | "CMakeBuilder", 23 | "QMakeBuilder", 24 | "ScriptBuilder", 25 | "get_builder_by_name", 26 | ) 27 | -------------------------------------------------------------------------------- /appimagecraft/builders/autotools.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import shlex 3 | 4 | from ..generators.bash_script import ProjectAwareBashScriptBuilder 5 | from .._util import get_appdir_path 6 | from . import BuilderBase 7 | 8 | 9 | class AutotoolsBuilder(BuilderBase): 10 | _script_filename = "build-autotools.sh" 11 | 12 | def _get_configure_extra_variables(self) -> list: 13 | default_params = [ 14 | "--prefix=/usr", 15 | ] 16 | 17 | configure_config = self._builder_config.get("configure", None) 18 | 19 | if not configure_config: 20 | configure_config = dict() 21 | 22 | extra_params = configure_config.get("extra_params", []) 23 | 24 | rv = list(default_params) 25 | rv += extra_params 26 | 27 | return rv 28 | 29 | @staticmethod 30 | def from_dict(data: dict): 31 | # TODO! 32 | raise NotImplementedError() 33 | 34 | def _get_source_dir(self, project_root_dir): 35 | source_dir = self._builder_config.get("source_dir", None) 36 | 37 | if not source_dir: 38 | return project_root_dir 39 | 40 | if not os.path.isabs(source_dir): 41 | source_dir = os.path.join(project_root_dir, source_dir) 42 | 43 | return source_dir 44 | 45 | def _generate_configure_command(self, project_root_dir: str): 46 | args = [os.path.join(self._get_source_dir(project_root_dir), "configure")] 47 | 48 | for param in self._get_configure_extra_variables(): 49 | escaped_value = shlex.quote(param) 50 | 51 | args.append(escaped_value) 52 | 53 | source_dir = self._get_source_dir(project_root_dir) 54 | args.append(source_dir) 55 | 56 | return " ".join(args) 57 | 58 | def generate_build_script(self, project_root_dir: str, build_dir: str) -> str: 59 | script_path = os.path.join(build_dir, self.__class__._script_filename) 60 | 61 | generator = ProjectAwareBashScriptBuilder(script_path, project_root_dir, build_dir) 62 | 63 | generator.add_lines( 64 | [ 65 | "# make sure we're in the build directory", 66 | "cd {}".format(shlex.quote(build_dir)), 67 | "", 68 | "# build in separate directory to avoid a mess in the build dir", 69 | "mkdir -p autotools-build", 70 | "cd autotools-build", 71 | "", 72 | ] 73 | ) 74 | 75 | autogen_path = os.path.join(self._get_source_dir(project_root_dir), "autogen.sh") 76 | 77 | if self._builder_config.get("allow_insource"): 78 | generator.add_lines( 79 | [ 80 | "# in case the project uses autogen.sh, we have to call that script to generate the configure script", 81 | "[ -f {0} ] && (cd {1} && {0})".format( 82 | shlex.quote(autogen_path), shlex.quote(os.path.dirname(autogen_path)) 83 | ), 84 | "", 85 | ] 86 | ) 87 | else: 88 | generator.add_lines( 89 | [ 90 | "# the user needs to explicitly allow in source operations in order to be able to auto call autogen.sh", 91 | "if [ -f {0} ]; then", 92 | ' echo "Warning: autogen.sh found, might have to be called by us"' 93 | ' echo "f so please add allow_insource: true to the autotools builder config"', 94 | "fi", 95 | "", 96 | ] 97 | ) 98 | 99 | if "configure" in self._builder_config: 100 | generator.add_lines( 101 | ["# set up build directory with configure", self._generate_configure_command(project_root_dir), ""] 102 | ) 103 | else: 104 | generator.add_lines( 105 | ["# configure: section not found, not generating configure call (this might be intentional)" ""] 106 | ) 107 | 108 | generator.add_lines( 109 | [ 110 | "# build project", 111 | "make -j $(nproc)", 112 | "", 113 | "# install binaries into AppDir (requires correct CMake install(...) configuration)", 114 | "make install DESTDIR={}".format(shlex.quote(get_appdir_path(build_dir))), 115 | ] 116 | ) 117 | 118 | generator.build_file() 119 | 120 | return os.path.basename(script_path) 121 | -------------------------------------------------------------------------------- /appimagecraft/builders/base.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | class BuilderBase: 5 | def __init__(self, config: dict = None): 6 | self._logger = None 7 | 8 | self._builder_config = config if config is not None else {} 9 | 10 | # support for different source directories in the project root 11 | # use this if your project is in a subdirectory of the project root directory 12 | def _get_source_dir(self, project_root_dir): 13 | source_dir = self._builder_config.get("source_dir", None) 14 | 15 | if not source_dir: 16 | return project_root_dir 17 | 18 | if not os.path.isabs(source_dir): 19 | source_dir = os.path.join(project_root_dir, source_dir) 20 | 21 | return source_dir 22 | 23 | @staticmethod 24 | def from_dict(data: dict): 25 | raise NotImplementedError 26 | 27 | def generate_build_script(self, project_root_dir: str, build_dir: str) -> str: 28 | raise NotImplementedError 29 | 30 | # def build_project(self): 31 | # raise NotImplementedError 32 | -------------------------------------------------------------------------------- /appimagecraft/builders/cmake.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import re 3 | import shlex 4 | from typing import Union 5 | 6 | from ..generators.bash_script import ProjectAwareBashScriptBuilder 7 | from .._util import get_appdir_path, convert_kv_list_to_dict 8 | from . import BuilderBase 9 | 10 | 11 | class CMakeBuilder(BuilderBase): 12 | _script_filename = "build-cmake.sh" 13 | 14 | def __init__(self, config: dict = None): 15 | super().__init__(config) 16 | 17 | for cmake_arg in self._get_cmake_extra_variables().keys(): 18 | self._validate_cmake_arg_name(cmake_arg) 19 | 20 | def _get_cmake_extra_variables(self) -> dict: 21 | default_vars = { 22 | "CMAKE_INSTALL_PREFIX": "/usr", 23 | "CMAKE_BUILD_TYPE": "Release", 24 | # GNUInstallDirs could set this to a different value otherwise 25 | "CMAKE_INSTALL_LIBDIR": "/usr/lib", 26 | } 27 | 28 | data = self._builder_config.get("extra_variables", None) or {} 29 | 30 | # allow for KEY=Value scheme for extra_variables 31 | if isinstance(data, list): 32 | data = convert_kv_list_to_dict(data) 33 | 34 | rv = dict() 35 | rv.update(default_vars) 36 | rv.update(data) 37 | 38 | return rv 39 | 40 | def _get_cmake_raw_extra_variables(self) -> dict: 41 | data = self._builder_config.get("raw_extra_variables", None) or {} 42 | 43 | # allow for KEY=Value scheme for extra_variables 44 | if isinstance(data, list): 45 | data = convert_kv_list_to_dict(data) 46 | 47 | return data 48 | 49 | @staticmethod 50 | def from_dict(data: dict): 51 | # TODO! 52 | raise NotImplementedError() 53 | 54 | @staticmethod 55 | def _validate_cmake_arg_name(name): 56 | # TODO 57 | if " " in name: 58 | raise ValueError("Spaces are not allowed in CMake argument names") 59 | 60 | def _generate_cmake_command(self, project_root_dir: str): 61 | args = ["cmake"] 62 | 63 | for key, value in self._get_cmake_extra_variables().items(): 64 | self._validate_cmake_arg_name(key) 65 | escaped_value = shlex.quote(value) 66 | args.append("-D{}={}".format(key, escaped_value)) 67 | 68 | for key, value in self._get_cmake_raw_extra_variables().items(): 69 | self._validate_cmake_arg_name(key) 70 | args.append("-D{}={}".format(key, value)) 71 | 72 | source_dir = self._get_source_dir(project_root_dir) 73 | args.append(source_dir) 74 | 75 | return " ".join(args) 76 | 77 | def generate_build_script(self, project_root_dir: str, build_dir: str) -> str: 78 | script_path = os.path.join(build_dir, self.__class__._script_filename) 79 | 80 | generator = ProjectAwareBashScriptBuilder(script_path, project_root_dir, build_dir) 81 | 82 | # export environment vars listed in config 83 | def try_export_env_vars(key_name, raw=False): 84 | try: 85 | env_config = self._builder_config[key_name] 86 | except KeyError: 87 | pass 88 | else: 89 | try: 90 | dict(env_config) 91 | except ValueError: 92 | try: 93 | iter(env_config) 94 | except ValueError: 95 | raise ValueError("environment config is in invalid format") 96 | else: 97 | env_config = convert_kv_list_to_dict(env_config) 98 | 99 | generator.add_line("# environment variables from {}".format(key_name)) 100 | generator.export_env_vars(env_config, raw=raw) 101 | 102 | # add some space between this and the next block 103 | generator.add_line() 104 | 105 | try_export_env_vars("environment") 106 | try_export_env_vars("raw_environment", raw=True) 107 | 108 | generator.add_lines( 109 | [ 110 | "# make sure we're in the build directory", 111 | "cd {}".format(shlex.quote(build_dir)), 112 | "", 113 | "# build in separate directory to avoid a mess in the build dir", 114 | "mkdir -p cmake-build", 115 | "cd cmake-build", 116 | "", 117 | "# it's always a good idea to print the CMake version in use", 118 | "cmake --version", 119 | "", 120 | "# set up build", 121 | self._generate_cmake_command(project_root_dir), 122 | "", 123 | "# build project", 124 | # TODO: use cmake --build somehow, also for install call below 125 | "# support for the $JOBS environment variable", 126 | "# in case $JOBS is not specified, we guess a value", 127 | "# we reserve one core outside CI environments", 128 | 'if [[ -z "$JOBS" ]]; then', 129 | ' if [[ -z "$CI" ]]; then', 130 | ' JOBS="$(nproc --ignore=1)"', 131 | " else", 132 | ' JOBS="$(nproc)"', 133 | " fi", 134 | "fi", 135 | 'make -j "$JOBS"', 136 | ] 137 | ) 138 | 139 | if self._builder_config.get("install", True): 140 | generator.add_lines( 141 | [ 142 | "", 143 | "# install binaries into AppDir (requires correct CMake install(...) configuration)", 144 | "make install DESTDIR={}".format(shlex.quote(get_appdir_path(build_dir))), 145 | ] 146 | ) 147 | 148 | # optional support for CPack 149 | # allows projects to also build packages, making use of appimagecraft features like auto-created clean build 150 | # directories, Docker container builds, ... 151 | cpack_args: dict = self._builder_config.get("cpack", False) 152 | 153 | # caution: must check for non-None value (like False) explicitly, an empty value is allowed and would be 154 | # represented as None 155 | if cpack_args is not False: 156 | generator.add_lines( 157 | [ 158 | "", 159 | "# build packages with cpack", 160 | ] 161 | ) 162 | 163 | cpack_generators: Union[dict, None] = None 164 | 165 | if cpack_args is not None: 166 | cpack_generators: dict = cpack_args.get("generators") 167 | 168 | if cpack_generators is not None: 169 | if not isinstance(cpack_generators, list): 170 | raise ValueError("generators: must be list") 171 | 172 | for gen in cpack_generators: 173 | # ensure correct format 174 | if not re.match(r"^[A-Z]+$", gen): 175 | raise ValueError("generator in invalid format: {}".format(gen)) 176 | 177 | generator.add_line("cpack -V {}".format(shlex.quote(gen))) 178 | 179 | else: 180 | generator.add_line("cpack -V") 181 | 182 | generator.build_file() 183 | 184 | return os.path.basename(script_path) 185 | -------------------------------------------------------------------------------- /appimagecraft/builders/qmake.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os.path 3 | import shlex 4 | 5 | from ..generators.bash_script import ProjectAwareBashScriptBuilder 6 | from .._util import get_appdir_path, convert_kv_list_to_dict 7 | from . import BuilderBase 8 | from .._logging import get_logger 9 | 10 | 11 | class QMakeBuilder(BuilderBase): 12 | _script_filename = "build-qmake.sh" 13 | 14 | def __init__(self, config: dict = None): 15 | super().__init__(config) 16 | 17 | self._logger = get_logger("qmake_builder") 18 | 19 | @staticmethod 20 | def from_dict(data: dict): 21 | # TODO! 22 | raise NotImplementedError() 23 | 24 | def _qenerate_qmake_command(self, project_root_dir: str): 25 | args = ["qmake"] 26 | 27 | # TODO: support for extra variables 28 | 29 | source_dir = self._get_source_dir(project_root_dir) 30 | 31 | project_file: str = self._builder_config.get("project_file") 32 | 33 | if not project_file: 34 | try: 35 | project_file = glob.glob(os.path.join(source_dir, "*.pro"))[0] 36 | except IndexError: 37 | raise RuntimeError( 38 | "Could not find QMake project file in source dir, please specify a different source_dir or a project_file" 39 | ) 40 | else: 41 | self._logger.warn("project_file not specified, using first found .pro file: %s" % project_file) 42 | 43 | # make sure we use an absolute path 44 | if not os.path.isabs(project_file): 45 | project_file = os.path.join(source_dir, project_file) 46 | 47 | args.append(project_file) 48 | 49 | return " ".join(args) 50 | 51 | def generate_build_script(self, project_root_dir: str, build_dir: str) -> str: 52 | script_path = os.path.join(build_dir, self.__class__._script_filename) 53 | 54 | generator = ProjectAwareBashScriptBuilder(script_path, project_root_dir, build_dir) 55 | 56 | # export environment vars listed in config 57 | def try_export_env_vars(key_name, raw=False): 58 | try: 59 | env_config = self._builder_config[key_name] 60 | except KeyError: 61 | pass 62 | else: 63 | try: 64 | dict(env_config) 65 | except ValueError: 66 | try: 67 | iter(env_config) 68 | except ValueError: 69 | raise ValueError("environment config is in invalid format") 70 | else: 71 | env_config = convert_kv_list_to_dict(env_config) 72 | 73 | generator.add_line("# environment variables from {}".format(key_name)) 74 | generator.export_env_vars(env_config, raw=raw) 75 | 76 | # add some space between this and the next block 77 | generator.add_line() 78 | 79 | try_export_env_vars("environment") 80 | try_export_env_vars("raw_environment", raw=True) 81 | 82 | generator.add_lines( 83 | [ 84 | "# make sure we're in the build directory", 85 | "cd {}".format(shlex.quote(build_dir)), 86 | "", 87 | "# build in separate directory to avoid a mess in the build dir", 88 | "mkdir -p qmake-build", 89 | "cd qmake-build", 90 | "", 91 | "# it's always a good idea to print the qmake version in use", 92 | "qmake --version", 93 | "", 94 | "# set up build", 95 | self._qenerate_qmake_command(project_root_dir), 96 | "", 97 | "# build project", 98 | "make -j $(nproc)", 99 | "", 100 | "# install binaries into AppDir (requires correct qmake install(...) configuration)", 101 | "make install INSTALL_ROOT={}".format(shlex.quote(get_appdir_path(build_dir))), 102 | ] 103 | ) 104 | 105 | generator.build_file() 106 | 107 | return os.path.basename(script_path) 108 | -------------------------------------------------------------------------------- /appimagecraft/builders/script.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | 3 | from ..generators.bash_script import ProjectAwareBashScriptBuilder 4 | from . import BuilderBase 5 | from .._logging import get_logger 6 | 7 | 8 | class ScriptBuilder(BuilderBase): 9 | _script_filename = "build-script.sh" 10 | 11 | def __init__(self, config: dict = None): 12 | super().__init__(config) 13 | 14 | self._logger = get_logger("script_builder") 15 | 16 | @staticmethod 17 | def from_dict(data: dict): 18 | # TODO! 19 | raise NotImplementedError() 20 | 21 | def generate_build_script(self, project_root_dir: str, build_dir: str) -> str: 22 | script_path = os.path.join(build_dir, self.__class__._script_filename) 23 | 24 | generator = ProjectAwareBashScriptBuilder(script_path, project_root_dir, build_dir) 25 | generator.add_lines(self._builder_config["commands"]) 26 | generator.build_file() 27 | 28 | return os.path.basename(script_path) 29 | -------------------------------------------------------------------------------- /appimagecraft/commands/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import CommandBase 2 | from .build_cmd import BuildCommand 3 | from .genscripts_cmd import GenerateScriptsCommand 4 | 5 | 6 | __all__ = ("CommandBase", "GenerateScriptsCommand", "BuildCommand") 7 | -------------------------------------------------------------------------------- /appimagecraft/commands/base.py: -------------------------------------------------------------------------------- 1 | class CommandBase: 2 | def __init__(self, config: dict, project_root_dir: str, build_dir: str, builder_name: str): 3 | self._config = config 4 | self._project_root_dir = project_root_dir 5 | self._build_dir = build_dir 6 | self._builder_name = builder_name 7 | 8 | def run(self): 9 | raise NotImplementedError 10 | -------------------------------------------------------------------------------- /appimagecraft/commands/build_cmd.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | import shutil 4 | import subprocess 5 | import sys 6 | 7 | from . import CommandBase 8 | from ..generators import AllBuildScriptsGenerator 9 | from ..validators import ValidationError 10 | from .. import _logging 11 | 12 | 13 | class BuildCommand(CommandBase): 14 | def __init__(self, config: dict, project_root_dir: str, build_dir: str, builder_name: str): 15 | super().__init__(config, project_root_dir, build_dir, builder_name) 16 | 17 | self._logger = _logging.get_logger("build") 18 | 19 | def set_build_dir(self, build_dir: str): 20 | self._build_dir = build_dir 21 | 22 | def _get_gen(self) -> AllBuildScriptsGenerator: 23 | gen = AllBuildScriptsGenerator(self._config, self._project_root_dir, self._builder_name) 24 | 25 | return gen 26 | 27 | def run(self): 28 | failed = False 29 | 30 | try: 31 | self._logger.info("Generating build scripts in {}".format(self._build_dir)) 32 | 33 | gen = self._get_gen() 34 | 35 | try: 36 | build_script = gen.generate_all_scripts(self._build_dir) 37 | except ValidationError: 38 | self._logger.critical("validation of shell scripts failed") 39 | sys.exit(1) 40 | 41 | self._logger.info("Calling main build script {}".format(build_script)) 42 | 43 | subprocess.check_call([build_script]) 44 | 45 | self._logger.info("Moving artifacts into project root directory") 46 | 47 | artifact_paths = glob.glob("{}/artifacts/*".format(self._build_dir)) 48 | 49 | if not artifact_paths: 50 | self._logger.warning("Could not find any artifacts to move to project root dir") 51 | 52 | else: 53 | for path in artifact_paths: 54 | # need to specify absolute destination path, otherwise move will raise and exception 55 | dest = os.path.join(self._project_root_dir, os.path.basename(path)) 56 | 57 | self._logger.debug("Moving artifact {} to {}".format(path, dest)) 58 | 59 | shutil.move(path, dest) 60 | 61 | except subprocess.CalledProcessError as e: 62 | self._logger.critical("Build script returned non-zero exit status {}".format(e.returncode)) 63 | failed = True 64 | 65 | except Exception as e: 66 | self._logger.exception(e) 67 | failed = True 68 | 69 | finally: 70 | self._logger.info("Cleaning up build directory") 71 | shutil.rmtree(self._build_dir) 72 | 73 | if failed: 74 | sys.exit(1) 75 | -------------------------------------------------------------------------------- /appimagecraft/commands/genscripts_cmd.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from . import CommandBase 4 | from ..generators import AllBuildScriptsGenerator 5 | from ..validators import ValidationError 6 | from .. import _logging 7 | 8 | 9 | # Generates build scripts based on configuration 10 | class GenerateScriptsCommand(CommandBase): 11 | def __init__(self, config: dict, project_root_dir: str, build_dir: str, builder_name: str): 12 | super().__init__(config, project_root_dir, build_dir, builder_name) 13 | 14 | self._logger = _logging.get_logger("genscripts") 15 | 16 | def set_build_dir(self, build_dir: str): 17 | self._build_dir = build_dir 18 | 19 | def _get_gen(self) -> AllBuildScriptsGenerator: 20 | gen = AllBuildScriptsGenerator(self._config, self._project_root_dir, self._builder_name) 21 | 22 | return gen 23 | 24 | def run(self): 25 | self._logger.info("Generating build scripts in {}".format(self._build_dir)) 26 | 27 | gen = self._get_gen() 28 | 29 | try: 30 | gen.generate_all_scripts(self._build_dir) 31 | except ValidationError: 32 | self._logger.critical("validation of shell scripts failed") 33 | sys.exit(1) 34 | -------------------------------------------------------------------------------- /appimagecraft/generators/__init__.py: -------------------------------------------------------------------------------- 1 | from .bash_script import BashScriptBuilder, ProjectAwareBashScriptBuilder 2 | from .appimage_build_script import AppImageBuildScriptGenerator 3 | from .pre_post_build_scripts import PrePostBuildScriptsGenerator 4 | from .build_scripts import AllBuildScriptsGenerator 5 | 6 | __all__ = ( 7 | "BashScriptBuilder", 8 | "ProjectAwareBashScriptBuilder", 9 | "AppImageBuildScriptGenerator", 10 | "PrePostBuildScriptsGenerator", 11 | "AllBuildScriptsGenerator", 12 | ) 13 | -------------------------------------------------------------------------------- /appimagecraft/generators/appimage_build_script.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import re 3 | import shlex 4 | from urllib.parse import urlparse 5 | 6 | from appimagecraft._logging import get_logger 7 | from appimagecraft._util import convert_kv_list_to_dict 8 | from .bash_script import ProjectAwareBashScriptBuilder 9 | 10 | 11 | class AppImageBuildScriptGenerator: 12 | def __init__(self, ld_config: dict = None): 13 | if ld_config is None: 14 | ld_config = dict() 15 | 16 | self._config = ld_config 17 | 18 | self._logger = get_logger("scriptgen") 19 | 20 | def build_file(self, path: str, project_root_dir: str, build_dir: str): 21 | gen = ProjectAwareBashScriptBuilder(path, project_root_dir, build_dir) 22 | 23 | arch = self._config.get("arch", platform.machine()) 24 | 25 | valid_archs = ["x86_64", "i386", "aarch64"] 26 | 27 | # there's a few valid aliases for the known valid archs, which we can substitute automatically 28 | substitutes = { 29 | "amd64": "x86_64", 30 | "i586": "i386", 31 | "i686": "i386", 32 | } 33 | try: 34 | arch = substitutes[arch] 35 | except KeyError: 36 | pass 37 | 38 | if arch not in valid_archs: 39 | raise ValueError("Invalid arch: {}".format(arch)) 40 | 41 | url = ( 42 | "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/" 43 | "linuxdeploy-{}.AppImage".format(arch) 44 | ) 45 | 46 | # change to custom directory 47 | gen.add_lines( 48 | [ 49 | "# switch to separate build dir", 50 | "mkdir -p appimage-build", 51 | "cd appimage-build", 52 | "", 53 | ] 54 | ) 55 | 56 | # export architecture, might be used by some people 57 | gen.add_lines( 58 | [ 59 | "export ARCH={}".format(shlex.quote(arch)), 60 | "", 61 | ] 62 | ) 63 | 64 | gen.add_lines( 65 | [ 66 | "# we store all downloaded files in a separate directory so we can easily skip them when moving the artifacts around", 67 | "mkdir -p downloads", 68 | "pushd downloads", 69 | "", 70 | ] 71 | ) 72 | 73 | gen.add_lines( 74 | [ 75 | "# fetch linuxdeploy from GitHub releases", 76 | "wget -c {}".format(shlex.quote(url)), 77 | "chmod +x linuxdeploy-{}.AppImage".format(arch), 78 | ] 79 | ) 80 | 81 | def build_official_plugin_url(name: str, filename: str = None): 82 | if filename is None: 83 | filename = "linuxdeploy-plugin-{name}-$ARCH.AppImage".format(name=name) 84 | 85 | return ( 86 | "https://github.com/linuxdeploy/linuxdeploy-plugin-{name}" 87 | "/releases/download/continuous/{filename}".format(name=name, filename=filename) 88 | ) 89 | 90 | def build_official_shell_script_plugin_url(name: str): 91 | return ( 92 | f"https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-{name}/master/" 93 | f"linuxdeploy-plugin-{name}.sh" 94 | ) 95 | 96 | def build_official_misc_plugin_url(name: str): 97 | return ( 98 | f"https://raw.githubusercontent.com/linuxdeploy/misc-plugins/master/{name}/" 99 | f"linuxdeploy-plugin-{name}.sh" 100 | ) 101 | 102 | known_plugin_urls = { 103 | "qt": build_official_plugin_url("qt"), 104 | } 105 | 106 | for plugin_name in ["conda", "gtk", "perl", "gstreamer", "ncurses"]: 107 | known_plugin_urls[plugin_name] = build_official_shell_script_plugin_url(plugin_name) 108 | 109 | for misc_plugin_name in ["gdb", "gettext"]: 110 | known_plugin_urls[misc_plugin_name] = build_official_misc_plugin_url(plugin_name) 111 | 112 | ld_plugins = {} 113 | 114 | plugin_name_expr = r"^linuxdeploy-plugin-([^\s\.-]+)(?:-[^\.]+)?(?:\..+)?$" 115 | 116 | try: 117 | ld_plugins_config = (self._config.get("linuxdeploy", None) or {}).get("plugins", {}) 118 | except KeyError: 119 | pass 120 | else: 121 | # required check for empty section 122 | if ld_plugins_config is not None: 123 | for plugin_entry in ld_plugins_config: 124 | # check whether the entry is an absolute URL 125 | parsed_url = urlparse(plugin_entry) 126 | is_url = parsed_url.scheme and parsed_url.netloc and parsed_url.path 127 | 128 | if is_url: 129 | url = plugin_entry 130 | # try to detect plugin name from URL 131 | filename = url.split("/")[-1] 132 | match = re.match(plugin_name_expr, filename) 133 | 134 | if not match: 135 | raise ValueError("Could not detect linuxdeploy plugin name from URL {}".format(url)) 136 | 137 | plugin_name = match.group(1) 138 | 139 | else: 140 | try: 141 | url = known_plugin_urls[plugin_entry] 142 | except KeyError: 143 | raise ValueError("Unknown plugin: {}".format(plugin_entry)) 144 | 145 | plugin_name = plugin_entry 146 | 147 | ld_plugins[plugin_name] = url 148 | 149 | for plugin_name, plugin_url in ld_plugins.items(): 150 | # allow for inserting plugin architecture dynamically 151 | plugin_url = plugin_url.replace("$ARCH", arch) 152 | 153 | gen.add_lines( 154 | [ 155 | "# fetch {} plugin".format(plugin_name), 156 | "wget -c {}".format(shlex.quote(plugin_url)), 157 | "chmod +x linuxdeploy-plugin-{}*".format(shlex.quote(plugin_name)), 158 | ] 159 | ) 160 | 161 | gen.add_line() 162 | 163 | gen.add_lines(["# we're done downloading, let's move back to the root directory", "popd", ""]) 164 | 165 | # export environment vars listed in config 166 | def try_export_env_vars(key_name, raw=False): 167 | try: 168 | env_config = (self._config.get("linuxdeploy", None) or {}).get(key_name, {}) 169 | except KeyError: 170 | pass 171 | else: 172 | try: 173 | dict(env_config) 174 | except ValueError: 175 | try: 176 | iter(env_config) 177 | except ValueError: 178 | raise ValueError("environment config is in invalid format") 179 | else: 180 | env_config = convert_kv_list_to_dict(env_config) 181 | 182 | gen.add_line("# environment variables from {}".format(key_name)) 183 | gen.export_env_vars(env_config, raw=raw) 184 | 185 | # add some space between this and the next block 186 | gen.add_line() 187 | 188 | try_export_env_vars("environment") 189 | try_export_env_vars("raw_environment", raw=True) 190 | 191 | # run linuxdeploy with the configured plugins 192 | ld_command = [ 193 | "./downloads/linuxdeploy-{}.AppImage".format(arch), 194 | "--appdir", 195 | "../AppDir", 196 | "--output", 197 | "appimage", 198 | ] 199 | 200 | for plugin_name in ld_plugins.keys(): 201 | ld_command.append("--plugin") 202 | ld_command.append(shlex.quote(plugin_name)) 203 | 204 | # add extra arguments specified in config file 205 | extra_args = (self._config.get("linuxdeploy", None) or {}).get("extra_args", None) 206 | if extra_args is not None: 207 | if isinstance(extra_args, list): 208 | ld_command += extra_args 209 | elif isinstance(extra_args, str): 210 | ld_command.append(extra_args) 211 | else: 212 | raise ValueError("Invalid type for extra_args: {}".format(type(extra_args))) 213 | 214 | gen.add_line(" ".join(ld_command)) 215 | 216 | gen.add_lines( 217 | [ 218 | "", 219 | "# move built AppImages to artifacts dir, excluding the downloaded linuxdeploy stuff", 220 | 'echo "===================="', 221 | 'echo "Generated AppImages:"', 222 | "find . -path ./downloads -prune -o -iname '*.AppImage*' -print -exec mv '{}' ../artifacts ';'", 223 | ] 224 | ) 225 | 226 | gen.build_file() 227 | -------------------------------------------------------------------------------- /appimagecraft/generators/appimagecraft_yml.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAssassin/appimagecraft/38f8edcd3e35c18d11442c72fb1db3ac3f845193/appimagecraft/generators/appimagecraft_yml.py -------------------------------------------------------------------------------- /appimagecraft/generators/bash_script.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shlex 3 | from typing import List, TextIO 4 | 5 | from .._logging import get_logger 6 | 7 | 8 | class BashScriptBuilder: 9 | """ 10 | Builder pattern style bash script generator. 11 | 12 | Provides some convenience methods for common operations like, e.g., exporting an environment variable. 13 | """ 14 | 15 | def __init__(self, path: str): 16 | self._path = path 17 | 18 | self._logger = get_logger("scriptgen") 19 | 20 | # initial value 21 | self._lines = [ 22 | "#! /bin/bash", 23 | "", 24 | "# make sure to quit on errors in subcommands", 25 | "set -e", 26 | "set -o pipefail", 27 | "", 28 | "# if $VERBOSE is set to a value, print all commands (useful for debugging)", 29 | '[[ "$VERBOSE" != "" ]] && set -x', 30 | "", 31 | ] 32 | 33 | def export_env_var(self, name: str, value: str, raw: bool = False): 34 | # convert to string explicitly, as we may receive ints from the YAML parser 35 | name = shlex.quote(str(name)) 36 | 37 | if not raw: 38 | value = shlex.quote(str(value)) 39 | 40 | self.add_line("{}={}".format(str(name), str(value))) 41 | self.add_line("export {}".format(str(name), str(value))) 42 | 43 | return self 44 | 45 | def export_env_vars(self, variables: dict = None, raw: bool = False): 46 | for env_var, value in dict(variables).items(): 47 | self.export_env_var(env_var, value, raw=raw) 48 | 49 | self.add_line() 50 | 51 | return self 52 | 53 | def add_lines(self, lines: List[str]): 54 | self._lines += lines 55 | 56 | return self 57 | 58 | def add_line(self, line: str = ""): 59 | self._lines.append(line) 60 | 61 | return self 62 | 63 | def build_string(self): 64 | # we want to force a blank line at the end, therefore we add an empty line 65 | rv = "\n".join(self._lines) 66 | 67 | if not rv.endswith("\n"): 68 | rv += "\n" 69 | 70 | return rv 71 | 72 | def build_file(self): 73 | with open(self._path, "w") as f: 74 | f.write(self.build_string()) 75 | 76 | # shell scripts are supposed to be executable 77 | os.chmod(self._path, 0o755) 78 | 79 | 80 | class ProjectAwareBashScriptBuilder(BashScriptBuilder): 81 | """ 82 | Builder pattern style bash script generator. Used to generate build scripts in the build dir which export a common 83 | set of variables. This allows all scripts to be run independently of the main build script. 84 | """ 85 | 86 | def __init__(self, path: str, project_root_dir: str, build_dir: str): 87 | super().__init__(path) 88 | 89 | # export PROJECT_ROOT and BUILD_DIR so they can be used by scripts etc. 90 | # convenience feature 91 | self.add_line("# convenience variables, may be used in config file") 92 | self.export_env_var("PROJECT_ROOT", project_root_dir) 93 | self.export_env_var("BUILD_DIR", build_dir) 94 | self.add_line() 95 | -------------------------------------------------------------------------------- /appimagecraft/generators/build_scripts.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import shlex 3 | 4 | from appimagecraft.validators import ShellCheckValidator, ValidationError 5 | from ..builders import CMakeBuilder, AutotoolsBuilder, QMakeBuilder, ScriptBuilder 6 | from .._logging import get_logger 7 | from .._util import convert_kv_list_to_dict 8 | from . import ( 9 | ProjectAwareBashScriptBuilder, 10 | AppImageBuildScriptGenerator, 11 | PrePostBuildScriptsGenerator, 12 | ) 13 | 14 | 15 | class AllBuildScriptsGenerator: 16 | def __init__(self, config: dict, project_root_dir: str, builder_name: str): 17 | self._config = config 18 | 19 | self._project_root_dir = project_root_dir 20 | self._builder_name = builder_name 21 | 22 | self._logger = get_logger("script_gen") 23 | 24 | def _generate_main_script(self, build_dir: str, build_scripts: dict) -> str: 25 | project_config = self._config["project"] 26 | 27 | main_script_path = os.path.join(build_dir, "build.sh") 28 | 29 | main_script_gen = ProjectAwareBashScriptBuilder(main_script_path, self._project_root_dir, build_dir) 30 | 31 | # $VERSION is used by various tools and may also be picked up by the build system of the target app 32 | project_version = project_config.get("version") 33 | if project_version is not None: 34 | main_script_gen.export_env_var("VERSION", project_version) 35 | else: 36 | project_version_cmd = project_config.get("version_command") 37 | if project_version_cmd is not None: 38 | main_script_gen.export_env_var("VERSION", "$({})".format(project_version_cmd), raw=True) 39 | 40 | main_script_gen.add_line() 41 | 42 | build_env_vars = self._config.get("environment") 43 | if build_env_vars is not None: 44 | main_script_gen.add_line("# user specified environment variables") 45 | 46 | if isinstance(build_env_vars, list): 47 | build_env_vars = convert_kv_list_to_dict(build_env_vars) 48 | 49 | for k, v in build_env_vars.items(): 50 | main_script_gen.export_env_var(k, v) 51 | 52 | main_script_gen.add_line() 53 | 54 | # header 55 | main_script_gen.add_lines( 56 | [ 57 | "# make sure to be in the build dir", 58 | "cd {}".format(shlex.quote(build_dir)), 59 | "", 60 | "# create artifacts directory (called scripts shall put their build results into this directory)", 61 | "[ ! -d artifacts ] && mkdir artifacts", 62 | "", 63 | "# call pre-build script (if available)", 64 | "[ -f pre_build.sh ] && bash pre_build.sh", 65 | "", 66 | "# create AppDir so that tools which are sensitive to that won't complain", 67 | "mkdir -p AppDir", 68 | "", 69 | ] 70 | ) 71 | 72 | if self._is_null_builder(self._builder_name): 73 | get_logger().debug("skipping generation of entry for null builder as main builder in main script") 74 | else: 75 | # add entry for main builder 76 | # use subshell to set SHLVL properly, which makes output with -x prettier 77 | main_script_gen.add_lines( 78 | [ 79 | "# call script for main builder {}".format(self._builder_name), 80 | "(source {})".format(build_scripts[self._builder_name]), 81 | ] 82 | ) 83 | 84 | # handled that one already 85 | del build_scripts[self._builder_name] 86 | 87 | # generate commented entries for remaining script 88 | # it doesn't make very much sense to run additional builders, since they most likely create the same artifacts 89 | # and we don't want to overwrite previously built files in our artifacts directory 90 | # also, it's much cleaner to build with different builders in separate directory 91 | if build_scripts: 92 | main_script_gen.add_line( 93 | "# additional available builders (call appimagecraft with --builder to switch)" 94 | ) 95 | 96 | for builder_name, script in build_scripts.items(): 97 | 98 | if self._is_null_builder(builder_name): 99 | get_logger().debug( 100 | "skipping generation of entry for null builder as additional builder in main script" 101 | ) 102 | continue 103 | 104 | main_script_gen.add_line("# script for builder {}".format(builder_name)) 105 | main_script_gen.add_line("#source {}\n".format(script)) 106 | 107 | main_script_gen.add_lines( 108 | [ 109 | "", 110 | "# call post-build script (if available)", 111 | "[ -f post_build.sh ] && (source post_build.sh)", 112 | "", 113 | ] 114 | ) 115 | 116 | # set up AppImage build script 117 | appimage_build_config = self._config.get("appimage", None) 118 | 119 | appimage_script_path = os.path.join(build_dir, "build-appimage.sh") 120 | 121 | appimage_script_gen = AppImageBuildScriptGenerator(appimage_build_config) 122 | appimage_script_gen.build_file(appimage_script_path, self._project_root_dir, build_dir) 123 | 124 | # call AppImage build script 125 | main_script_gen.add_line("# build AppImage") 126 | main_script_gen.add_line("(source {})".format(shlex.quote(appimage_script_path))) 127 | 128 | # (re-)create script file 129 | main_script_gen.build_file() 130 | 131 | return main_script_path 132 | 133 | def generate_builder_scripts(self, build_dir: str) -> dict: 134 | build_config: dict = self._config["build"] 135 | 136 | # generate build configs for every 137 | builders_map = { 138 | "cmake": CMakeBuilder, 139 | "autotools": AutotoolsBuilder, 140 | "qmake": QMakeBuilder, 141 | "script": ScriptBuilder, 142 | } 143 | 144 | build_scripts = {} 145 | 146 | for builder_name in build_config.keys(): 147 | # skip null builder 148 | if self._is_null_builder(builder_name): 149 | get_logger().debug("skipping generation of build script for null builder") 150 | continue 151 | 152 | try: 153 | builder = builders_map[builder_name](build_config[builder_name]) 154 | except KeyError: 155 | self._logger.error("No builder named {} available, skipping".format(builder_name)) 156 | continue 157 | else: 158 | script_filename = builder.generate_build_script(self._project_root_dir, build_dir) 159 | build_scripts[builder_name] = script_filename 160 | 161 | return build_scripts 162 | 163 | def generate_pre_post_build_scripts(self, build_dir: str): 164 | gen = PrePostBuildScriptsGenerator(self._config.get("scripts", None)) 165 | gen.build_files(self._project_root_dir, build_dir) 166 | 167 | def generate_all_scripts(self, build_dir) -> str: 168 | if build_dir is None: 169 | raise ValueError("build dir has not been set") 170 | 171 | self.generate_pre_post_build_scripts(build_dir) 172 | 173 | build_scripts = self.generate_builder_scripts(build_dir) 174 | 175 | main_script_path = self._generate_main_script(build_dir, build_scripts) 176 | 177 | # validate script(s), if possible 178 | if ShellCheckValidator.is_available(): 179 | self._logger.debug("validating scripts with shellcheck") 180 | validator = ShellCheckValidator() 181 | 182 | validator.validate(main_script_path) 183 | 184 | else: 185 | self._logger.debug("shellcheck validator not available, skipping validation") 186 | 187 | return main_script_path 188 | 189 | @staticmethod 190 | def _is_null_builder(builder_name): 191 | return builder_name is None or builder_name.lower() == "null" 192 | -------------------------------------------------------------------------------- /appimagecraft/generators/pre_post_build_scripts.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | 3 | from typing import List 4 | 5 | from .bash_script import ProjectAwareBashScriptBuilder 6 | 7 | 8 | class PrePostBuildScriptsGenerator: 9 | def __init__(self, scripts_config: dict = None): 10 | if scripts_config is None: 11 | scripts_config = dict() 12 | 13 | self._config = scripts_config 14 | 15 | def build_files(self, project_root_dir: str, build_dir: str): 16 | def write_build_script(path: str, lines: List[str]): 17 | gen = ProjectAwareBashScriptBuilder(path, project_root_dir, build_dir) 18 | gen.add_lines(lines) 19 | gen.build_file() 20 | 21 | stages = ["pre_build", "post_build"] 22 | 23 | # validate config 24 | invalid_stages = set(self._config.keys()) - set(stages) 25 | if invalid_stages: 26 | raise ValueError("Invalid script stage: {}".format(list(invalid_stages)[0])) 27 | 28 | for stage in stages: 29 | try: 30 | script_lines = self._config["{}".format(stage)] 31 | except KeyError: 32 | pass 33 | else: 34 | write_build_script(os.path.join(build_dir, "{}.sh".format(stage)), script_lines) 35 | -------------------------------------------------------------------------------- /appimagecraft/parsers/__init__.py: -------------------------------------------------------------------------------- 1 | from appimagecraft.parsers.appimagecraft_yml import AppImageCraftYMLParser 2 | 3 | 4 | __all__ = ("AppImageCraftYMLParser",) 5 | -------------------------------------------------------------------------------- /appimagecraft/parsers/appimagecraft_yml.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import yaml 3 | 4 | 5 | class AppImageCraftYMLParser: 6 | def __init__(self, config_path): 7 | if not os.path.exists(config_path): 8 | raise IOError("Config file {} does not exist".format(config_path)) 9 | 10 | self._config_path = config_path 11 | self._data = None 12 | 13 | self._parse() 14 | self._validate() 15 | 16 | def _parse(self): 17 | with open(self._config_path, "r") as f: 18 | self._data = yaml.safe_load(f.read()) 19 | 20 | def data(self): 21 | return dict(self._data) 22 | 23 | # validates parsed data 24 | # validation only takes place to some extent 25 | # the validator only guarantees correctness on a root level, builder-specific stuff needs to be validated there 26 | def _validate(self): 27 | c: dict = self._data 28 | 29 | def _assert(condition, message): 30 | if not condition: 31 | if not message: 32 | message = "Unknown error while parsing appimagecraft config" 33 | 34 | raise ValueError(message) 35 | 36 | def assert_reverse_dns_format(data): 37 | message = "project name must be in " 38 | _assert(not data.endswith("."), message) 39 | _assert(not data.startswith("."), message) 40 | _assert(data.count(".") > 0, message) 41 | # TODO: check there's at least one char between periods 42 | 43 | valid_root_keys = {"version", "project", "build", "environment", "appimage", "scripts"} 44 | required_root_keys = {"version", "project", "build"} 45 | 46 | all_root_keys = set(c.keys()) 47 | 48 | invalid_root_keys = list(all_root_keys - valid_root_keys) 49 | if invalid_root_keys: 50 | raise ValueError("invalid key in config: {}".format(invalid_root_keys[0])) 51 | 52 | missing_root_keys = list(required_root_keys - all_root_keys) 53 | if missing_root_keys: 54 | raise ValueError("missing key in config: {}".format(missing_root_keys[0])) 55 | 56 | # check file version 57 | version = c["version"] 58 | _assert(version == 1, "unsupported config file version: {}".format(version)) 59 | 60 | if not isinstance(c["project"], dict): 61 | raise ValueError("project: data must be in key: value format") 62 | 63 | if "name" not in c["project"]: 64 | raise ValueError("project name missing") 65 | 66 | # project name should be in reverse DNS style, similar to AppStream 67 | assert_reverse_dns_format(c["project"]["name"]) 68 | -------------------------------------------------------------------------------- /appimagecraft/validators/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import ValidatorBase 2 | from .shellcheck import ShellCheckValidator 3 | from .exceptions import ValidationError 4 | 5 | 6 | __all__ = ("ValidatorBase", "ShellCheckValidator", "ValidationError") 7 | -------------------------------------------------------------------------------- /appimagecraft/validators/base.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class ValidatorBase: 5 | def __init__(self): 6 | # make sure validator is available, otherwise refuse instantiation 7 | if not self.is_available(): 8 | raise RuntimeError("validator {} not available".format(self.__class__)) 9 | 10 | @staticmethod 11 | def supported_file_types() -> List[str]: 12 | """ 13 | Fetch fnmatch patterns of supported file types. Can be used by a factory function to auto-create a suitable 14 | validator based on the file name. 15 | 16 | :return: matching validator or None 17 | :rtype: ValidatorBase 18 | """ 19 | 20 | raise NotImplementedError 21 | 22 | @staticmethod 23 | def is_available() -> bool: 24 | """ 25 | Check if the validator is available on this platform, i.e., required external tools are installed, etc. 26 | 27 | :return: whether validator can be used 28 | """ 29 | 30 | raise NotImplementedError 31 | 32 | def validate(self, path: str): 33 | """ 34 | Perform validation. 35 | 36 | :param path: path to file that shall be validated 37 | :raises ValidationError: in case validation fails 38 | """ 39 | 40 | raise NotImplementedError 41 | -------------------------------------------------------------------------------- /appimagecraft/validators/exceptions.py: -------------------------------------------------------------------------------- 1 | class ValidationError(Exception): 2 | def __init__(self, message: str): 3 | super().__init__(message) 4 | self.message = message 5 | -------------------------------------------------------------------------------- /appimagecraft/validators/shellcheck.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | from . import ValidatorBase 5 | from .exceptions import ValidationError 6 | 7 | from subprocess import CalledProcessError, check_call 8 | 9 | 10 | class ShellCheckValidator(ValidatorBase): 11 | def __init__(self): 12 | super().__init__() 13 | 14 | @staticmethod 15 | def _find_shellcheck(): 16 | shellcheck_env = os.environ.get("SHELLCHECK", None) 17 | 18 | if shellcheck_env: 19 | return shellcheck_env 20 | 21 | return shutil.which("shellcheck") 22 | 23 | @staticmethod 24 | def is_available() -> bool: 25 | return bool(ShellCheckValidator._find_shellcheck()) 26 | 27 | @staticmethod 28 | def supported_file_types(): 29 | return ["*.sh", "*.bash"] 30 | 31 | def validate(self, path: str): 32 | shellcheck_path = self._find_shellcheck() 33 | 34 | if not shellcheck_path: 35 | raise ValidationError("could not find shellcheck") 36 | 37 | try: 38 | # SC2116 can be ignored safely, it's just about a "useless echo", but we want to test the version_cmd 39 | # feature with an echo call 40 | check_call([shellcheck_path, "-e", "SC2116", "-e", "SC1091", "-x", path], cwd=os.path.dirname(path)) 41 | except CalledProcessError: 42 | raise ValidationError("failed to run shellcheck") 43 | -------------------------------------------------------------------------------- /appimagecraft/validators/util.py: -------------------------------------------------------------------------------- 1 | import fnmatch 2 | import os.path 3 | 4 | from typing import List, Type 5 | 6 | from . import ValidatorBase, __all__ as _all_validators 7 | 8 | 9 | def _get_validators_map() -> dict: 10 | validators: List[ValidatorBase] = _all_validators 11 | validators.remove(ValidatorBase) 12 | 13 | rv = {v: v.supported_file_types() for v in validators} 14 | return rv 15 | 16 | 17 | def get_validator(path: str) -> Type[ValidatorBase]: 18 | """ 19 | Create suitable validator for provided path. 20 | 21 | :param path: path to file 22 | :return: the suitable validator 23 | :raises KeyError: in case no suitable validator could be found 24 | :raises ValueError: if there are multiple suitable validators and we can't decide which one to use (should not happen!) 25 | """ 26 | 27 | validators_map = _get_validators_map() 28 | 29 | suitable_validators = set() 30 | 31 | for validator, patterns in validators_map.items(): 32 | for pattern in patterns: 33 | if fnmatch.fnmatch(os.path.basename(path), pattern): 34 | suitable_validators.add(validator) 35 | 36 | if len(suitable_validators) > 1: 37 | raise ValueError("multiple suitable validators found for path {}".format(path)) 38 | elif len(suitable_validators) < 1: 39 | raise KeyError("could not find suitable validator for path {}") 40 | 41 | return suitable_validators.pop() 42 | 43 | 44 | def validate_file(path: str): 45 | """ 46 | Try to validate file. 47 | 48 | :param path: path to file to validate 49 | :raises KeyError: if no validator is available 50 | :raises ValueError: if there are multiple suitable validators and we can't decide which one to use (should not happen!) 51 | :raises ValidationError: if validation fails 52 | """ 53 | 54 | validator_class = get_validator(path) 55 | validator = validator_class() 56 | validator.validate(path) 57 | -------------------------------------------------------------------------------- /ci/build-appimage.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -x 4 | set -e 5 | 6 | tmpdir=$(mktemp -d appimagecraft-XXXXXX) 7 | 8 | cleanup() { rm -r "$tmpdir" ; } 9 | trap cleanup EXIT 10 | 11 | "${PYTHON:-python3}" -m venv "$tmpdir" 12 | . "$tmpdir"/bin/activate 13 | 14 | pip install . 15 | 16 | 17 | EXTRA_ARGS=() 18 | 19 | case "$ARCH" in 20 | i386|i686) 21 | EXTRA_ARGS=("-f" "appimagecraft-i386.yml") 22 | ;; 23 | aarch64) 24 | EXTRA_ARGS=("-f" "appimagecraft-aarch64.yml") 25 | ;; 26 | esac 27 | 28 | appimagecraft build "${EXTRA_ARGS[@]}" 29 | -------------------------------------------------------------------------------- /ci/build-example-projects.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | 5 | log() { 6 | tput setaf 2 7 | tput bold 8 | echo "$@" 9 | tput sgr0 10 | } 11 | 12 | log "Set up test infrastructure" 13 | 14 | if [[ "$CI" == "" ]] && [[ -d /dev/shm ]]; then 15 | export TMPDIR=/dev/shm 16 | fi 17 | 18 | WORKDIR=$(mktemp -d) 19 | 20 | _cleanup() { 21 | [[ -d "$WORKDIR" ]] && rm -r "$WORKDIR" 22 | } 23 | 24 | trap _cleanup EXIT 25 | 26 | REPO_ROOT=$(readlink -f $(dirname "$0")/..) 27 | 28 | pushd "$WORKDIR" 29 | 30 | log "Create virtual environment and install appimagecraft" 31 | 32 | "${PYTHON:-python3}" -m venv venv 33 | . venv/bin/activate 34 | 35 | pip install "$REPO_ROOT" 36 | 37 | log "Try out different commands on example CMake project" 38 | 39 | pushd "$REPO_ROOT"/example-projects/cmake 40 | log "-- Try genscripts command" 41 | appimagecraft genscripts --build-dir "$WORKDIR"/cmake-genscripts 42 | log "-- Clean up and recreate workdir for next command" 43 | rm -r "$WORKDIR"/cmake-genscripts 44 | log "-- Try build command" 45 | appimagecraft build --build-dir "$WORKDIR"/cmake 46 | popd 47 | 48 | pushd "$REPO_ROOT"/example-projects/autotools 49 | log "Try out different commands on example autotools command" 50 | log "-- Try genscripts command" 51 | appimagecraft genscripts --build-dir "$WORKDIR"/autotools-genscripts 52 | log "-- Clean up and recreate workdir for next command" 53 | rm -r "$WORKDIR"/autotools-genscripts 54 | log "-- Try build command" 55 | appimagecraft build --build-dir "$WORKDIR"/autotools 56 | popd 57 | -------------------------------------------------------------------------------- /deployment/AppRun.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | this_dir="$(dirname "$0")" 4 | 5 | # we only bundle shellcheck for platforms for which shellcheck officially provides static binaries 6 | # our launch script therefore needs to check whether such a binary can be found in the AppDir 7 | # appimagecraft uses this binary if it exists, otherwise falls back to checking $PATH 8 | SHELLCHECK="$this_dir/usr/bin/shellcheck" 9 | [[ -f "$SHELLCHECK" ]] && export SHELLCHECK 10 | 11 | exec "$this_dir"/usr/bin/python -m appimagecraft "$@" 12 | -------------------------------------------------------------------------------- /deployment/appimagecraft.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=appimagecraft 3 | Type=Application 4 | Icon=appimagecraft 5 | Exec=appimagecraft 6 | NoDisplay=true 7 | Terminal=true 8 | Categories=Utility; 9 | -------------------------------------------------------------------------------- /deployment/appimagecraft.svg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAssassin/appimagecraft/38f8edcd3e35c18d11442c72fb1db3ac3f845193/deployment/appimagecraft.svg -------------------------------------------------------------------------------- /example-projects/autotools/.gitignore: -------------------------------------------------------------------------------- 1 | *.cache/ 2 | *.m4 3 | configure 4 | install-sh 5 | Makefile.in 6 | missing 7 | depcomp 8 | config.* 9 | compile 10 | Makefile 11 | -------------------------------------------------------------------------------- /example-projects/autotools/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = . 2 | 3 | bin_PROGRAMS = example 4 | example_SOURCES = main.c 5 | -------------------------------------------------------------------------------- /example-projects/autotools/appimagecraft.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | project: 4 | name: org.appimage.appimagecraft 5 | version: 0.0.1-demo-1 6 | 7 | build: 8 | autotools: 9 | source_dir: . 10 | allow_insource: true 11 | configure: 12 | 13 | scripts: 14 | post_build: 15 | - touch "$BUILD_DIR"/example.svg 16 | - |2 17 | cat > "$BUILD_DIR"/example.desktop < 2 | #include 3 | 4 | int main(int argc, char** argv) { 5 | printf("Hello world, this is the appimagecraft example project.\n"); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /example-projects/cmake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project("appimagecraft example project") 4 | 5 | add_executable(example main.c) 6 | 7 | install(TARGETS example RUNTIME DESTINATION bin) 8 | -------------------------------------------------------------------------------- /example-projects/cmake/appimagecraft.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | project: 4 | name: org.appimage.appimagecraft 5 | #version: 0.0.1 6 | version_command: echo 0.0.1 7 | 8 | environment: 9 | TEST: abcdefg 10 | 11 | build: 12 | cmake: 13 | source_dir: . 14 | extra_variables: 15 | - TEST=ON 16 | #cpack: 17 | 18 | scripts: 19 | post_build: 20 | - touch "$BUILD_DIR"/example.svg 21 | - |2 22 | cat > "$BUILD_DIR"/example.desktop < 2 | #include 3 | 4 | int main(int argc, char** argv) { 5 | printf("Hello world, this is the appimagecraft example project.\n"); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /example-projects/qmake/.gitignore: -------------------------------------------------------------------------------- 1 | *.AppImage 2 | *build*/ 3 | *.swp 4 | -------------------------------------------------------------------------------- /example-projects/qmake/appimagecraft.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | project: 4 | name: org.appimage.appimagecraft.examples.qmake 5 | #version: 0.0.1 6 | version_command: echo 0.0.1 7 | 8 | environment: 9 | TEST: abcdefg 10 | 11 | build: 12 | qmake: 13 | source_dir: . 14 | project_file: example.pro 15 | extra_variables: 16 | - TEST=ON 17 | #cpack: 18 | 19 | scripts: 20 | post_build: 21 | - touch "$BUILD_DIR"/example.svg 22 | - |2 23 | cat > "$BUILD_DIR"/example.desktop < 2 | 3 | int main(int argc, char** argv) { 4 | (void) argc; 5 | (void) argv; 6 | 7 | std::cout << "Hello world, this is the appimagecraft qmake example project." << std::endl; 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "aiohttp" 5 | version = "3.8.6" 6 | description = "Async http client/server framework (asyncio)" 7 | optional = false 8 | python-versions = ">=3.6" 9 | groups = ["dev"] 10 | files = [ 11 | {file = "aiohttp-3.8.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:41d55fc043954cddbbd82503d9cc3f4814a40bcef30b3569bc7b5e34130718c1"}, 12 | {file = "aiohttp-3.8.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1d84166673694841d8953f0a8d0c90e1087739d24632fe86b1a08819168b4566"}, 13 | {file = "aiohttp-3.8.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:253bf92b744b3170eb4c4ca2fa58f9c4b87aeb1df42f71d4e78815e6e8b73c9e"}, 14 | {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fd194939b1f764d6bb05490987bfe104287bbf51b8d862261ccf66f48fb4096"}, 15 | {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c5f938d199a6fdbdc10bbb9447496561c3a9a565b43be564648d81e1102ac22"}, 16 | {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2817b2f66ca82ee699acd90e05c95e79bbf1dc986abb62b61ec8aaf851e81c93"}, 17 | {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fa375b3d34e71ccccf172cab401cd94a72de7a8cc01847a7b3386204093bb47"}, 18 | {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9de50a199b7710fa2904be5a4a9b51af587ab24c8e540a7243ab737b45844543"}, 19 | {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e1d8cb0b56b3587c5c01de3bf2f600f186da7e7b5f7353d1bf26a8ddca57f965"}, 20 | {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8e31e9db1bee8b4f407b77fd2507337a0a80665ad7b6c749d08df595d88f1cf5"}, 21 | {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7bc88fc494b1f0311d67f29fee6fd636606f4697e8cc793a2d912ac5b19aa38d"}, 22 | {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ec00c3305788e04bf6d29d42e504560e159ccaf0be30c09203b468a6c1ccd3b2"}, 23 | {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad1407db8f2f49329729564f71685557157bfa42b48f4b93e53721a16eb813ed"}, 24 | {file = "aiohttp-3.8.6-cp310-cp310-win32.whl", hash = "sha256:ccc360e87341ad47c777f5723f68adbb52b37ab450c8bc3ca9ca1f3e849e5fe2"}, 25 | {file = "aiohttp-3.8.6-cp310-cp310-win_amd64.whl", hash = "sha256:93c15c8e48e5e7b89d5cb4613479d144fda8344e2d886cf694fd36db4cc86865"}, 26 | {file = "aiohttp-3.8.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e2f9cc8e5328f829f6e1fb74a0a3a939b14e67e80832975e01929e320386b34"}, 27 | {file = "aiohttp-3.8.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e6a00ffcc173e765e200ceefb06399ba09c06db97f401f920513a10c803604ca"}, 28 | {file = "aiohttp-3.8.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:41bdc2ba359032e36c0e9de5a3bd00d6fb7ea558a6ce6b70acedf0da86458321"}, 29 | {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14cd52ccf40006c7a6cd34a0f8663734e5363fd981807173faf3a017e202fec9"}, 30 | {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2d5b785c792802e7b275c420d84f3397668e9d49ab1cb52bd916b3b3ffcf09ad"}, 31 | {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1bed815f3dc3d915c5c1e556c397c8667826fbc1b935d95b0ad680787896a358"}, 32 | {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96603a562b546632441926cd1293cfcb5b69f0b4159e6077f7c7dbdfb686af4d"}, 33 | {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d76e8b13161a202d14c9584590c4df4d068c9567c99506497bdd67eaedf36403"}, 34 | {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e3f1e3f1a1751bb62b4a1b7f4e435afcdade6c17a4fd9b9d43607cebd242924a"}, 35 | {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:76b36b3124f0223903609944a3c8bf28a599b2cc0ce0be60b45211c8e9be97f8"}, 36 | {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:a2ece4af1f3c967a4390c284797ab595a9f1bc1130ef8b01828915a05a6ae684"}, 37 | {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:16d330b3b9db87c3883e565340d292638a878236418b23cc8b9b11a054aaa887"}, 38 | {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:42c89579f82e49db436b69c938ab3e1559e5a4409eb8639eb4143989bc390f2f"}, 39 | {file = "aiohttp-3.8.6-cp311-cp311-win32.whl", hash = "sha256:efd2fcf7e7b9d7ab16e6b7d54205beded0a9c8566cb30f09c1abe42b4e22bdcb"}, 40 | {file = "aiohttp-3.8.6-cp311-cp311-win_amd64.whl", hash = "sha256:3b2ab182fc28e7a81f6c70bfbd829045d9480063f5ab06f6e601a3eddbbd49a0"}, 41 | {file = "aiohttp-3.8.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:fdee8405931b0615220e5ddf8cd7edd8592c606a8e4ca2a00704883c396e4479"}, 42 | {file = "aiohttp-3.8.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d25036d161c4fe2225d1abff2bd52c34ed0b1099f02c208cd34d8c05729882f0"}, 43 | {file = "aiohttp-3.8.6-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d791245a894be071d5ab04bbb4850534261a7d4fd363b094a7b9963e8cdbd31"}, 44 | {file = "aiohttp-3.8.6-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0cccd1de239afa866e4ce5c789b3032442f19c261c7d8a01183fd956b1935349"}, 45 | {file = "aiohttp-3.8.6-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f13f60d78224f0dace220d8ab4ef1dbc37115eeeab8c06804fec11bec2bbd07"}, 46 | {file = "aiohttp-3.8.6-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a9b5a0606faca4f6cc0d338359d6fa137104c337f489cd135bb7fbdbccb1e39"}, 47 | {file = "aiohttp-3.8.6-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:13da35c9ceb847732bf5c6c5781dcf4780e14392e5d3b3c689f6d22f8e15ae31"}, 48 | {file = "aiohttp-3.8.6-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:4d4cbe4ffa9d05f46a28252efc5941e0462792930caa370a6efaf491f412bc66"}, 49 | {file = "aiohttp-3.8.6-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:229852e147f44da0241954fc6cb910ba074e597f06789c867cb7fb0621e0ba7a"}, 50 | {file = "aiohttp-3.8.6-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:713103a8bdde61d13490adf47171a1039fd880113981e55401a0f7b42c37d071"}, 51 | {file = "aiohttp-3.8.6-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:45ad816b2c8e3b60b510f30dbd37fe74fd4a772248a52bb021f6fd65dff809b6"}, 52 | {file = "aiohttp-3.8.6-cp36-cp36m-win32.whl", hash = "sha256:2b8d4e166e600dcfbff51919c7a3789ff6ca8b3ecce16e1d9c96d95dd569eb4c"}, 53 | {file = "aiohttp-3.8.6-cp36-cp36m-win_amd64.whl", hash = "sha256:0912ed87fee967940aacc5306d3aa8ba3a459fcd12add0b407081fbefc931e53"}, 54 | {file = "aiohttp-3.8.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e2a988a0c673c2e12084f5e6ba3392d76c75ddb8ebc6c7e9ead68248101cd446"}, 55 | {file = "aiohttp-3.8.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebf3fd9f141700b510d4b190094db0ce37ac6361a6806c153c161dc6c041ccda"}, 56 | {file = "aiohttp-3.8.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3161ce82ab85acd267c8f4b14aa226047a6bee1e4e6adb74b798bd42c6ae1f80"}, 57 | {file = "aiohttp-3.8.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95fc1bf33a9a81469aa760617b5971331cdd74370d1214f0b3109272c0e1e3c"}, 58 | {file = "aiohttp-3.8.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c43ecfef7deaf0617cee936836518e7424ee12cb709883f2c9a1adda63cc460"}, 59 | {file = "aiohttp-3.8.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca80e1b90a05a4f476547f904992ae81eda5c2c85c66ee4195bb8f9c5fb47f28"}, 60 | {file = "aiohttp-3.8.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:90c72ebb7cb3a08a7f40061079817133f502a160561d0675b0a6adf231382c92"}, 61 | {file = "aiohttp-3.8.6-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bb54c54510e47a8c7c8e63454a6acc817519337b2b78606c4e840871a3e15349"}, 62 | {file = "aiohttp-3.8.6-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:de6a1c9f6803b90e20869e6b99c2c18cef5cc691363954c93cb9adeb26d9f3ae"}, 63 | {file = "aiohttp-3.8.6-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:a3628b6c7b880b181a3ae0a0683698513874df63783fd89de99b7b7539e3e8a8"}, 64 | {file = "aiohttp-3.8.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:fc37e9aef10a696a5a4474802930079ccfc14d9f9c10b4662169671ff034b7df"}, 65 | {file = "aiohttp-3.8.6-cp37-cp37m-win32.whl", hash = "sha256:f8ef51e459eb2ad8e7a66c1d6440c808485840ad55ecc3cafefadea47d1b1ba2"}, 66 | {file = "aiohttp-3.8.6-cp37-cp37m-win_amd64.whl", hash = "sha256:b2fe42e523be344124c6c8ef32a011444e869dc5f883c591ed87f84339de5976"}, 67 | {file = "aiohttp-3.8.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9e2ee0ac5a1f5c7dd3197de309adfb99ac4617ff02b0603fd1e65b07dc772e4b"}, 68 | {file = "aiohttp-3.8.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01770d8c04bd8db568abb636c1fdd4f7140b284b8b3e0b4584f070180c1e5c62"}, 69 | {file = "aiohttp-3.8.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3c68330a59506254b556b99a91857428cab98b2f84061260a67865f7f52899f5"}, 70 | {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89341b2c19fb5eac30c341133ae2cc3544d40d9b1892749cdd25892bbc6ac951"}, 71 | {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71783b0b6455ac8f34b5ec99d83e686892c50498d5d00b8e56d47f41b38fbe04"}, 72 | {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f628dbf3c91e12f4d6c8b3f092069567d8eb17814aebba3d7d60c149391aee3a"}, 73 | {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b04691bc6601ef47c88f0255043df6f570ada1a9ebef99c34bd0b72866c217ae"}, 74 | {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ee912f7e78287516df155f69da575a0ba33b02dd7c1d6614dbc9463f43066e3"}, 75 | {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9c19b26acdd08dd239e0d3669a3dddafd600902e37881f13fbd8a53943079dbc"}, 76 | {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:99c5ac4ad492b4a19fc132306cd57075c28446ec2ed970973bbf036bcda1bcc6"}, 77 | {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f0f03211fd14a6a0aed2997d4b1c013d49fb7b50eeb9ffdf5e51f23cfe2c77fa"}, 78 | {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:8d399dade330c53b4106160f75f55407e9ae7505263ea86f2ccca6bfcbdb4921"}, 79 | {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ec4fd86658c6a8964d75426517dc01cbf840bbf32d055ce64a9e63a40fd7b771"}, 80 | {file = "aiohttp-3.8.6-cp38-cp38-win32.whl", hash = "sha256:33164093be11fcef3ce2571a0dccd9041c9a93fa3bde86569d7b03120d276c6f"}, 81 | {file = "aiohttp-3.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:bdf70bfe5a1414ba9afb9d49f0c912dc524cf60141102f3a11143ba3d291870f"}, 82 | {file = "aiohttp-3.8.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d52d5dc7c6682b720280f9d9db41d36ebe4791622c842e258c9206232251ab2b"}, 83 | {file = "aiohttp-3.8.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4ac39027011414dbd3d87f7edb31680e1f430834c8cef029f11c66dad0670aa5"}, 84 | {file = "aiohttp-3.8.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3f5c7ce535a1d2429a634310e308fb7d718905487257060e5d4598e29dc17f0b"}, 85 | {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b30e963f9e0d52c28f284d554a9469af073030030cef8693106d918b2ca92f54"}, 86 | {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:918810ef188f84152af6b938254911055a72e0f935b5fbc4c1a4ed0b0584aed1"}, 87 | {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:002f23e6ea8d3dd8d149e569fd580c999232b5fbc601c48d55398fbc2e582e8c"}, 88 | {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fcf3eabd3fd1a5e6092d1242295fa37d0354b2eb2077e6eb670accad78e40e1"}, 89 | {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:255ba9d6d5ff1a382bb9a578cd563605aa69bec845680e21c44afc2670607a95"}, 90 | {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d67f8baed00870aa390ea2590798766256f31dc5ed3ecc737debb6e97e2ede78"}, 91 | {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:86f20cee0f0a317c76573b627b954c412ea766d6ada1a9fcf1b805763ae7feeb"}, 92 | {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:39a312d0e991690ccc1a61f1e9e42daa519dcc34ad03eb6f826d94c1190190dd"}, 93 | {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e827d48cf802de06d9c935088c2924e3c7e7533377d66b6f31ed175c1620e05e"}, 94 | {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bd111d7fc5591ddf377a408ed9067045259ff2770f37e2d94e6478d0f3fc0c17"}, 95 | {file = "aiohttp-3.8.6-cp39-cp39-win32.whl", hash = "sha256:caf486ac1e689dda3502567eb89ffe02876546599bbf915ec94b1fa424eeffd4"}, 96 | {file = "aiohttp-3.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:3f0e27e5b733803333bb2371249f41cf42bae8884863e8e8965ec69bebe53132"}, 97 | {file = "aiohttp-3.8.6.tar.gz", hash = "sha256:b0cf2a4501bff9330a8a5248b4ce951851e415bdcce9dc158e76cfd55e15085c"}, 98 | ] 99 | 100 | [package.dependencies] 101 | aiosignal = ">=1.1.2" 102 | async-timeout = ">=4.0.0a3,<5.0" 103 | asynctest = {version = "0.13.0", markers = "python_version < \"3.8\""} 104 | attrs = ">=17.3.0" 105 | charset-normalizer = ">=2.0,<4.0" 106 | frozenlist = ">=1.1.1" 107 | multidict = ">=4.5,<7.0" 108 | typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} 109 | yarl = ">=1.0,<2.0" 110 | 111 | [package.extras] 112 | speedups = ["Brotli", "aiodns", "cchardet ; python_version < \"3.10\""] 113 | 114 | [[package]] 115 | name = "aiosignal" 116 | version = "1.2.0" 117 | description = "aiosignal: a list of registered asynchronous callbacks" 118 | optional = false 119 | python-versions = ">=3.6" 120 | groups = ["dev"] 121 | files = [ 122 | {file = "aiosignal-1.2.0-py3-none-any.whl", hash = "sha256:26e62109036cd181df6e6ad646f91f0dcfd05fe16d0cb924138ff2ab75d64e3a"}, 123 | {file = "aiosignal-1.2.0.tar.gz", hash = "sha256:78ed67db6c7b7ced4f98e495e572106d5c432a93e1ddd1bf475e1dc05f5b7df2"}, 124 | ] 125 | 126 | [package.dependencies] 127 | frozenlist = ">=1.1.0" 128 | 129 | [[package]] 130 | name = "async-timeout" 131 | version = "4.0.2" 132 | description = "Timeout context manager for asyncio programs" 133 | optional = false 134 | python-versions = ">=3.6" 135 | groups = ["dev"] 136 | files = [ 137 | {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, 138 | {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, 139 | ] 140 | 141 | [package.dependencies] 142 | typing-extensions = {version = ">=3.6.5", markers = "python_version < \"3.8\""} 143 | 144 | [[package]] 145 | name = "asynctest" 146 | version = "0.13.0" 147 | description = "Enhance the standard unittest package with features for testing asyncio libraries" 148 | optional = false 149 | python-versions = ">=3.5" 150 | groups = ["dev"] 151 | markers = "python_version < \"3.8\"" 152 | files = [ 153 | {file = "asynctest-0.13.0-py3-none-any.whl", hash = "sha256:5da6118a7e6d6b54d83a8f7197769d046922a44d2a99c21382f0a6e4fadae676"}, 154 | {file = "asynctest-0.13.0.tar.gz", hash = "sha256:c27862842d15d83e6a34eb0b2866c323880eb3a75e4485b079ea11748fd77fac"}, 155 | ] 156 | 157 | [[package]] 158 | name = "attrs" 159 | version = "22.1.0" 160 | description = "Classes Without Boilerplate" 161 | optional = false 162 | python-versions = ">=3.5" 163 | groups = ["dev"] 164 | files = [ 165 | {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, 166 | {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, 167 | ] 168 | 169 | [package.extras] 170 | dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] 171 | docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] 172 | tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] 173 | tests-no-zope = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] 174 | 175 | [[package]] 176 | name = "black" 177 | version = "22.6.0" 178 | description = "The uncompromising code formatter." 179 | optional = false 180 | python-versions = ">=3.6.2" 181 | groups = ["dev"] 182 | files = [ 183 | {file = "black-22.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69"}, 184 | {file = "black-22.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807"}, 185 | {file = "black-22.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6797f58943fceb1c461fb572edbe828d811e719c24e03375fd25170ada53825e"}, 186 | {file = "black-22.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c85928b9d5f83b23cee7d0efcb310172412fbf7cb9d9ce963bd67fd141781def"}, 187 | {file = "black-22.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6fe02afde060bbeef044af7996f335fbe90b039ccf3f5eb8f16df8b20f77666"}, 188 | {file = "black-22.6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cfaf3895a9634e882bf9d2363fed5af8888802d670f58b279b0bece00e9a872d"}, 189 | {file = "black-22.6.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94783f636bca89f11eb5d50437e8e17fbc6a929a628d82304c80fa9cd945f256"}, 190 | {file = "black-22.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2ea29072e954a4d55a2ff58971b83365eba5d3d357352a07a7a4df0d95f51c78"}, 191 | {file = "black-22.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e439798f819d49ba1c0bd9664427a05aab79bfba777a6db94fd4e56fae0cb849"}, 192 | {file = "black-22.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187d96c5e713f441a5829e77120c269b6514418f4513a390b0499b0987f2ff1c"}, 193 | {file = "black-22.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:074458dc2f6e0d3dab7928d4417bb6957bb834434516f21514138437accdbe90"}, 194 | {file = "black-22.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a218d7e5856f91d20f04e931b6f16d15356db1c846ee55f01bac297a705ca24f"}, 195 | {file = "black-22.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:568ac3c465b1c8b34b61cd7a4e349e93f91abf0f9371eda1cf87194663ab684e"}, 196 | {file = "black-22.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6c1734ab264b8f7929cef8ae5f900b85d579e6cbfde09d7387da8f04771b51c6"}, 197 | {file = "black-22.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9a3ac16efe9ec7d7381ddebcc022119794872abce99475345c5a61aa18c45ad"}, 198 | {file = "black-22.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b9fd45787ba8aa3f5e0a0a98920c1012c884622c6c920dbe98dbd05bc7c70fbf"}, 199 | {file = "black-22.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7ba9be198ecca5031cd78745780d65a3f75a34b2ff9be5837045dce55db83d1c"}, 200 | {file = "black-22.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3db5b6409b96d9bd543323b23ef32a1a2b06416d525d27e0f67e74f1446c8f2"}, 201 | {file = "black-22.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:560558527e52ce8afba936fcce93a7411ab40c7d5fe8c2463e279e843c0328ee"}, 202 | {file = "black-22.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b154e6bbde1e79ea3260c4b40c0b7b3109ffcdf7bc4ebf8859169a6af72cd70b"}, 203 | {file = "black-22.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:4af5bc0e1f96be5ae9bd7aaec219c901a94d6caa2484c21983d043371c733fc4"}, 204 | {file = "black-22.6.0-py3-none-any.whl", hash = "sha256:ac609cf8ef5e7115ddd07d85d988d074ed00e10fbc3445aee393e70164a2219c"}, 205 | {file = "black-22.6.0.tar.gz", hash = "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9"}, 206 | ] 207 | 208 | [package.dependencies] 209 | aiohttp = {version = ">=3.7.4", optional = true, markers = "extra == \"d\""} 210 | click = ">=8.0.0" 211 | mypy-extensions = ">=0.4.3" 212 | pathspec = ">=0.9.0" 213 | platformdirs = ">=2" 214 | tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} 215 | typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} 216 | typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} 217 | 218 | [package.extras] 219 | colorama = ["colorama (>=0.4.3)"] 220 | d = ["aiohttp (>=3.7.4)"] 221 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 222 | uvloop = ["uvloop (>=0.15.2)"] 223 | 224 | [[package]] 225 | name = "charset-normalizer" 226 | version = "2.1.1" 227 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 228 | optional = false 229 | python-versions = ">=3.6.0" 230 | groups = ["dev"] 231 | files = [ 232 | {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, 233 | {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, 234 | ] 235 | 236 | [package.extras] 237 | unicode-backport = ["unicodedata2"] 238 | 239 | [[package]] 240 | name = "click" 241 | version = "8.1.3" 242 | description = "Composable command line interface toolkit" 243 | optional = false 244 | python-versions = ">=3.7" 245 | groups = ["dev"] 246 | files = [ 247 | {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, 248 | {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, 249 | ] 250 | 251 | [package.dependencies] 252 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 253 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 254 | 255 | [[package]] 256 | name = "colorama" 257 | version = "0.4.5" 258 | description = "Cross-platform colored terminal text." 259 | optional = false 260 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 261 | groups = ["dev"] 262 | markers = "platform_system == \"Windows\"" 263 | files = [ 264 | {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, 265 | {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, 266 | ] 267 | 268 | [[package]] 269 | name = "coloredlogs" 270 | version = "15.0.1" 271 | description = "Colored terminal output for Python's logging module" 272 | optional = false 273 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 274 | groups = ["main"] 275 | files = [ 276 | {file = "coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934"}, 277 | {file = "coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0"}, 278 | ] 279 | 280 | [package.dependencies] 281 | humanfriendly = ">=9.1" 282 | 283 | [package.extras] 284 | cron = ["capturer (>=2.4)"] 285 | 286 | [[package]] 287 | name = "frozenlist" 288 | version = "1.3.1" 289 | description = "A list-like structure which implements collections.abc.MutableSequence" 290 | optional = false 291 | python-versions = ">=3.7" 292 | groups = ["dev"] 293 | files = [ 294 | {file = "frozenlist-1.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5f271c93f001748fc26ddea409241312a75e13466b06c94798d1a341cf0e6989"}, 295 | {file = "frozenlist-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9c6ef8014b842f01f5d2b55315f1af5cbfde284eb184075c189fd657c2fd8204"}, 296 | {file = "frozenlist-1.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:219a9676e2eae91cb5cc695a78b4cb43d8123e4160441d2b6ce8d2c70c60e2f3"}, 297 | {file = "frozenlist-1.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b47d64cdd973aede3dd71a9364742c542587db214e63b7529fbb487ed67cddd9"}, 298 | {file = "frozenlist-1.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2af6f7a4e93f5d08ee3f9152bce41a6015b5cf87546cb63872cc19b45476e98a"}, 299 | {file = "frozenlist-1.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a718b427ff781c4f4e975525edb092ee2cdef6a9e7bc49e15063b088961806f8"}, 300 | {file = "frozenlist-1.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c56c299602c70bc1bb5d1e75f7d8c007ca40c9d7aebaf6e4ba52925d88ef826d"}, 301 | {file = "frozenlist-1.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:717470bfafbb9d9be624da7780c4296aa7935294bd43a075139c3d55659038ca"}, 302 | {file = "frozenlist-1.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:31b44f1feb3630146cffe56344704b730c33e042ffc78d21f2125a6a91168131"}, 303 | {file = "frozenlist-1.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c3b31180b82c519b8926e629bf9f19952c743e089c41380ddca5db556817b221"}, 304 | {file = "frozenlist-1.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:d82bed73544e91fb081ab93e3725e45dd8515c675c0e9926b4e1f420a93a6ab9"}, 305 | {file = "frozenlist-1.3.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49459f193324fbd6413e8e03bd65789e5198a9fa3095e03f3620dee2f2dabff2"}, 306 | {file = "frozenlist-1.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:94e680aeedc7fd3b892b6fa8395b7b7cc4b344046c065ed4e7a1e390084e8cb5"}, 307 | {file = "frozenlist-1.3.1-cp310-cp310-win32.whl", hash = "sha256:fabb953ab913dadc1ff9dcc3a7a7d3dc6a92efab3a0373989b8063347f8705be"}, 308 | {file = "frozenlist-1.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:eee0c5ecb58296580fc495ac99b003f64f82a74f9576a244d04978a7e97166db"}, 309 | {file = "frozenlist-1.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0bc75692fb3770cf2b5856a6c2c9de967ca744863c5e89595df64e252e4b3944"}, 310 | {file = "frozenlist-1.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086ca1ac0a40e722d6833d4ce74f5bf1aba2c77cbfdc0cd83722ffea6da52a04"}, 311 | {file = "frozenlist-1.3.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b51eb355e7f813bcda00276b0114c4172872dc5fb30e3fea059b9367c18fbcb"}, 312 | {file = "frozenlist-1.3.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74140933d45271c1a1283f708c35187f94e1256079b3c43f0c2267f9db5845ff"}, 313 | {file = "frozenlist-1.3.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee4c5120ddf7d4dd1eaf079af3af7102b56d919fa13ad55600a4e0ebe532779b"}, 314 | {file = "frozenlist-1.3.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97d9e00f3ac7c18e685320601f91468ec06c58acc185d18bb8e511f196c8d4b2"}, 315 | {file = "frozenlist-1.3.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6e19add867cebfb249b4e7beac382d33215d6d54476bb6be46b01f8cafb4878b"}, 316 | {file = "frozenlist-1.3.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a027f8f723d07c3f21963caa7d585dcc9b089335565dabe9c814b5f70c52705a"}, 317 | {file = "frozenlist-1.3.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:61d7857950a3139bce035ad0b0945f839532987dfb4c06cfe160254f4d19df03"}, 318 | {file = "frozenlist-1.3.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:53b2b45052e7149ee8b96067793db8ecc1ae1111f2f96fe1f88ea5ad5fd92d10"}, 319 | {file = "frozenlist-1.3.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bbb1a71b1784e68870800b1bc9f3313918edc63dbb8f29fbd2e767ce5821696c"}, 320 | {file = "frozenlist-1.3.1-cp37-cp37m-win32.whl", hash = "sha256:ab6fa8c7871877810e1b4e9392c187a60611fbf0226a9e0b11b7b92f5ac72792"}, 321 | {file = "frozenlist-1.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f89139662cc4e65a4813f4babb9ca9544e42bddb823d2ec434e18dad582543bc"}, 322 | {file = "frozenlist-1.3.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4c0c99e31491a1d92cde8648f2e7ccad0e9abb181f6ac3ddb9fc48b63301808e"}, 323 | {file = "frozenlist-1.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:61e8cb51fba9f1f33887e22488bad1e28dd8325b72425f04517a4d285a04c519"}, 324 | {file = "frozenlist-1.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc2f3e368ee5242a2cbe28323a866656006382872c40869b49b265add546703f"}, 325 | {file = "frozenlist-1.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58fb94a01414cddcdc6839807db77ae8057d02ddafc94a42faee6004e46c9ba8"}, 326 | {file = "frozenlist-1.3.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:022178b277cb9277d7d3b3f2762d294f15e85cd2534047e68a118c2bb0058f3e"}, 327 | {file = "frozenlist-1.3.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:572ce381e9fe027ad5e055f143763637dcbac2542cfe27f1d688846baeef5170"}, 328 | {file = "frozenlist-1.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19127f8dcbc157ccb14c30e6f00392f372ddb64a6ffa7106b26ff2196477ee9f"}, 329 | {file = "frozenlist-1.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42719a8bd3792744c9b523674b752091a7962d0d2d117f0b417a3eba97d1164b"}, 330 | {file = "frozenlist-1.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2743bb63095ef306041c8f8ea22bd6e4d91adabf41887b1ad7886c4c1eb43d5f"}, 331 | {file = "frozenlist-1.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:fa47319a10e0a076709644a0efbcaab9e91902c8bd8ef74c6adb19d320f69b83"}, 332 | {file = "frozenlist-1.3.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52137f0aea43e1993264a5180c467a08a3e372ca9d378244c2d86133f948b26b"}, 333 | {file = "frozenlist-1.3.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:f5abc8b4d0c5b556ed8cd41490b606fe99293175a82b98e652c3f2711b452988"}, 334 | {file = "frozenlist-1.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1e1cf7bc8cbbe6ce3881863671bac258b7d6bfc3706c600008925fb799a256e2"}, 335 | {file = "frozenlist-1.3.1-cp38-cp38-win32.whl", hash = "sha256:0dde791b9b97f189874d654c55c24bf7b6782343e14909c84beebd28b7217845"}, 336 | {file = "frozenlist-1.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:9494122bf39da6422b0972c4579e248867b6b1b50c9b05df7e04a3f30b9a413d"}, 337 | {file = "frozenlist-1.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31bf9539284f39ff9398deabf5561c2b0da5bb475590b4e13dd8b268d7a3c5c1"}, 338 | {file = "frozenlist-1.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e0c8c803f2f8db7217898d11657cb6042b9b0553a997c4a0601f48a691480fab"}, 339 | {file = "frozenlist-1.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da5ba7b59d954f1f214d352308d1d86994d713b13edd4b24a556bcc43d2ddbc3"}, 340 | {file = "frozenlist-1.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74e6b2b456f21fc93ce1aff2b9728049f1464428ee2c9752a4b4f61e98c4db96"}, 341 | {file = "frozenlist-1.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526d5f20e954d103b1d47232e3839f3453c02077b74203e43407b962ab131e7b"}, 342 | {file = "frozenlist-1.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b499c6abe62a7a8d023e2c4b2834fce78a6115856ae95522f2f974139814538c"}, 343 | {file = "frozenlist-1.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab386503f53bbbc64d1ad4b6865bf001414930841a870fc97f1546d4d133f141"}, 344 | {file = "frozenlist-1.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f63c308f82a7954bf8263a6e6de0adc67c48a8b484fab18ff87f349af356efd"}, 345 | {file = "frozenlist-1.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:12607804084d2244a7bd4685c9d0dca5df17a6a926d4f1967aa7978b1028f89f"}, 346 | {file = "frozenlist-1.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:da1cdfa96425cbe51f8afa43e392366ed0b36ce398f08b60de6b97e3ed4affef"}, 347 | {file = "frozenlist-1.3.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f810e764617b0748b49a731ffaa525d9bb36ff38332411704c2400125af859a6"}, 348 | {file = "frozenlist-1.3.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:35c3d79b81908579beb1fb4e7fcd802b7b4921f1b66055af2578ff7734711cfa"}, 349 | {file = "frozenlist-1.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c92deb5d9acce226a501b77307b3b60b264ca21862bd7d3e0c1f3594022f01bc"}, 350 | {file = "frozenlist-1.3.1-cp39-cp39-win32.whl", hash = "sha256:5e77a8bd41e54b05e4fb2708dc6ce28ee70325f8c6f50f3df86a44ecb1d7a19b"}, 351 | {file = "frozenlist-1.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:625d8472c67f2d96f9a4302a947f92a7adbc1e20bedb6aff8dbc8ff039ca6189"}, 352 | {file = "frozenlist-1.3.1.tar.gz", hash = "sha256:3a735e4211a04ccfa3f4833547acdf5d2f863bfeb01cfd3edaffbc251f15cec8"}, 353 | ] 354 | 355 | [[package]] 356 | name = "humanfriendly" 357 | version = "10.0" 358 | description = "Human friendly output for text interfaces using Python" 359 | optional = false 360 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 361 | groups = ["main"] 362 | files = [ 363 | {file = "humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477"}, 364 | {file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"}, 365 | ] 366 | 367 | [package.dependencies] 368 | pyreadline = {version = "*", markers = "sys_platform == \"win32\" and python_version < \"3.8\""} 369 | pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_version >= \"3.8\""} 370 | 371 | [[package]] 372 | name = "idna" 373 | version = "3.7" 374 | description = "Internationalized Domain Names in Applications (IDNA)" 375 | optional = false 376 | python-versions = ">=3.5" 377 | groups = ["dev"] 378 | files = [ 379 | {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, 380 | {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, 381 | ] 382 | 383 | [[package]] 384 | name = "importlib-metadata" 385 | version = "4.12.0" 386 | description = "Read metadata from Python packages" 387 | optional = false 388 | python-versions = ">=3.7" 389 | groups = ["dev"] 390 | markers = "python_version < \"3.8\"" 391 | files = [ 392 | {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, 393 | {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"}, 394 | ] 395 | 396 | [package.dependencies] 397 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} 398 | zipp = ">=0.5" 399 | 400 | [package.extras] 401 | docs = ["jaraco.packaging (>=9)", "rst.linker (>=1.9)", "sphinx"] 402 | perf = ["ipython"] 403 | testing = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\"", "pytest-perf (>=0.9.2)"] 404 | 405 | [[package]] 406 | name = "isort" 407 | version = "5.10.1" 408 | description = "A Python utility / library to sort Python imports." 409 | optional = false 410 | python-versions = ">=3.6.1,<4.0" 411 | groups = ["dev"] 412 | files = [ 413 | {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, 414 | {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, 415 | ] 416 | 417 | [package.extras] 418 | colors = ["colorama (>=0.4.3,<0.5.0)"] 419 | pipfile-deprecated-finder = ["pipreqs", "requirementslib"] 420 | plugins = ["setuptools"] 421 | requirements-deprecated-finder = ["pip-api", "pipreqs"] 422 | 423 | [[package]] 424 | name = "multidict" 425 | version = "6.0.2" 426 | description = "multidict implementation" 427 | optional = false 428 | python-versions = ">=3.7" 429 | groups = ["dev"] 430 | files = [ 431 | {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"}, 432 | {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"}, 433 | {file = "multidict-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:041b81a5f6b38244b34dc18c7b6aba91f9cdaf854d9a39e5ff0b58e2b5773b9c"}, 434 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fdda29a3c7e76a064f2477c9aab1ba96fd94e02e386f1e665bca1807fc5386f"}, 435 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3368bf2398b0e0fcbf46d85795adc4c259299fec50c1416d0f77c0a843a3eed9"}, 436 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4f052ee022928d34fe1f4d2bc743f32609fb79ed9c49a1710a5ad6b2198db20"}, 437 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:225383a6603c086e6cef0f2f05564acb4f4d5f019a4e3e983f572b8530f70c88"}, 438 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50bd442726e288e884f7be9071016c15a8742eb689a593a0cac49ea093eef0a7"}, 439 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:47e6a7e923e9cada7c139531feac59448f1f47727a79076c0b1ee80274cd8eee"}, 440 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0556a1d4ea2d949efe5fd76a09b4a82e3a4a30700553a6725535098d8d9fb672"}, 441 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:626fe10ac87851f4cffecee161fc6f8f9853f0f6f1035b59337a51d29ff3b4f9"}, 442 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8064b7c6f0af936a741ea1efd18690bacfbae4078c0c385d7c3f611d11f0cf87"}, 443 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2d36e929d7f6a16d4eb11b250719c39560dd70545356365b494249e2186bc389"}, 444 | {file = "multidict-6.0.2-cp310-cp310-win32.whl", hash = "sha256:fcb91630817aa8b9bc4a74023e4198480587269c272c58b3279875ed7235c293"}, 445 | {file = "multidict-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:8cbf0132f3de7cc6c6ce00147cc78e6439ea736cee6bca4f068bcf892b0fd658"}, 446 | {file = "multidict-6.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:05f6949d6169878a03e607a21e3b862eaf8e356590e8bdae4227eedadacf6e51"}, 447 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2c2e459f7050aeb7c1b1276763364884595d47000c1cddb51764c0d8976e608"}, 448 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0509e469d48940147e1235d994cd849a8f8195e0bca65f8f5439c56e17872a3"}, 449 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:514fe2b8d750d6cdb4712346a2c5084a80220821a3e91f3f71eec11cf8d28fd4"}, 450 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19adcfc2a7197cdc3987044e3f415168fc5dc1f720c932eb1ef4f71a2067e08b"}, 451 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9d153e7f1f9ba0b23ad1568b3b9e17301e23b042c23870f9ee0522dc5cc79e8"}, 452 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aef9cc3d9c7d63d924adac329c33835e0243b5052a6dfcbf7732a921c6e918ba"}, 453 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4571f1beddff25f3e925eea34268422622963cd8dc395bb8778eb28418248e43"}, 454 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d48b8ee1d4068561ce8033d2c344cf5232cb29ee1a0206a7b828c79cbc5982b8"}, 455 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:45183c96ddf61bf96d2684d9fbaf6f3564d86b34cb125761f9a0ef9e36c1d55b"}, 456 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:75bdf08716edde767b09e76829db8c1e5ca9d8bb0a8d4bd94ae1eafe3dac5e15"}, 457 | {file = "multidict-6.0.2-cp37-cp37m-win32.whl", hash = "sha256:a45e1135cb07086833ce969555df39149680e5471c04dfd6a915abd2fc3f6dbc"}, 458 | {file = "multidict-6.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6f3cdef8a247d1eafa649085812f8a310e728bdf3900ff6c434eafb2d443b23a"}, 459 | {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0327292e745a880459ef71be14e709aaea2f783f3537588fb4ed09b6c01bca60"}, 460 | {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e875b6086e325bab7e680e4316d667fc0e5e174bb5611eb16b3ea121c8951b86"}, 461 | {file = "multidict-6.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feea820722e69451743a3d56ad74948b68bf456984d63c1a92e8347b7b88452d"}, 462 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc57c68cb9139c7cd6fc39f211b02198e69fb90ce4bc4a094cf5fe0d20fd8b0"}, 463 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:497988d6b6ec6ed6f87030ec03280b696ca47dbf0648045e4e1d28b80346560d"}, 464 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89171b2c769e03a953d5969b2f272efa931426355b6c0cb508022976a17fd376"}, 465 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684133b1e1fe91eda8fa7447f137c9490a064c6b7f392aa857bba83a28cfb693"}, 466 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd9fc9c4849a07f3635ccffa895d57abce554b467d611a5009ba4f39b78a8849"}, 467 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e07c8e79d6e6fd37b42f3250dba122053fddb319e84b55dd3a8d6446e1a7ee49"}, 468 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4070613ea2227da2bfb2c35a6041e4371b0af6b0be57f424fe2318b42a748516"}, 469 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:47fbeedbf94bed6547d3aa632075d804867a352d86688c04e606971595460227"}, 470 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5774d9218d77befa7b70d836004a768fb9aa4fdb53c97498f4d8d3f67bb9cfa9"}, 471 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2957489cba47c2539a8eb7ab32ff49101439ccf78eab724c828c1a54ff3ff98d"}, 472 | {file = "multidict-6.0.2-cp38-cp38-win32.whl", hash = "sha256:e5b20e9599ba74391ca0cfbd7b328fcc20976823ba19bc573983a25b32e92b57"}, 473 | {file = "multidict-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:8004dca28e15b86d1b1372515f32eb6f814bdf6f00952699bdeb541691091f96"}, 474 | {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2e4a0785b84fb59e43c18a015ffc575ba93f7d1dbd272b4cdad9f5134b8a006c"}, 475 | {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6701bf8a5d03a43375909ac91b6980aea74b0f5402fbe9428fc3f6edf5d9677e"}, 476 | {file = "multidict-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a007b1638e148c3cfb6bf0bdc4f82776cef0ac487191d093cdc316905e504071"}, 477 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07a017cfa00c9890011628eab2503bee5872f27144936a52eaab449be5eaf032"}, 478 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c207fff63adcdf5a485969131dc70e4b194327666b7e8a87a97fbc4fd80a53b2"}, 479 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:373ba9d1d061c76462d74e7de1c0c8e267e9791ee8cfefcf6b0b2495762c370c"}, 480 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfba7c6d5d7c9099ba21f84662b037a0ffd4a5e6b26ac07d19e423e6fdf965a9"}, 481 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19d9bad105dfb34eb539c97b132057a4e709919ec4dd883ece5838bcbf262b80"}, 482 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:de989b195c3d636ba000ee4281cd03bb1234635b124bf4cd89eeee9ca8fcb09d"}, 483 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7c40b7bbece294ae3a87c1bc2abff0ff9beef41d14188cda94ada7bcea99b0fb"}, 484 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:d16cce709ebfadc91278a1c005e3c17dd5f71f5098bfae1035149785ea6e9c68"}, 485 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:a2c34a93e1d2aa35fbf1485e5010337c72c6791407d03aa5f4eed920343dd360"}, 486 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:feba80698173761cddd814fa22e88b0661e98cb810f9f986c54aa34d281e4937"}, 487 | {file = "multidict-6.0.2-cp39-cp39-win32.whl", hash = "sha256:23b616fdc3c74c9fe01d76ce0d1ce872d2d396d8fa8e4899398ad64fb5aa214a"}, 488 | {file = "multidict-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:4bae31803d708f6f15fd98be6a6ac0b6958fcf68fda3c77a048a4f9073704aae"}, 489 | {file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"}, 490 | ] 491 | 492 | [[package]] 493 | name = "mypy-extensions" 494 | version = "0.4.3" 495 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 496 | optional = false 497 | python-versions = "*" 498 | groups = ["dev"] 499 | files = [ 500 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 501 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 502 | ] 503 | 504 | [[package]] 505 | name = "pathspec" 506 | version = "0.10.0" 507 | description = "Utility library for gitignore style pattern matching of file paths." 508 | optional = false 509 | python-versions = ">=3.7" 510 | groups = ["dev"] 511 | files = [ 512 | {file = "pathspec-0.10.0-py3-none-any.whl", hash = "sha256:aefa80ac32d5bf1f96139dca67cefb69a431beff4e6bf1168468f37d7ab87015"}, 513 | {file = "pathspec-0.10.0.tar.gz", hash = "sha256:01eecd304ba0e6eeed188ae5fa568e99ef10265af7fd9ab737d6412b4ee0ab85"}, 514 | ] 515 | 516 | [[package]] 517 | name = "platformdirs" 518 | version = "2.5.2" 519 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 520 | optional = false 521 | python-versions = ">=3.7" 522 | groups = ["dev"] 523 | files = [ 524 | {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, 525 | {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, 526 | ] 527 | 528 | [package.extras] 529 | docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] 530 | test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] 531 | 532 | [[package]] 533 | name = "pyreadline" 534 | version = "2.1" 535 | description = "A python implmementation of GNU readline." 536 | optional = false 537 | python-versions = "*" 538 | groups = ["main"] 539 | markers = "sys_platform == \"win32\" and python_version < \"3.8\"" 540 | files = [ 541 | {file = "pyreadline-2.1.zip", hash = "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1"}, 542 | ] 543 | 544 | [[package]] 545 | name = "pyreadline3" 546 | version = "3.4.1" 547 | description = "A python implementation of GNU readline." 548 | optional = false 549 | python-versions = "*" 550 | groups = ["main"] 551 | markers = "sys_platform == \"win32\" and python_version >= \"3.8\"" 552 | files = [ 553 | {file = "pyreadline3-3.4.1-py3-none-any.whl", hash = "sha256:b0efb6516fd4fb07b45949053826a62fa4cb353db5be2bbb4a7aa1fdd1e345fb"}, 554 | {file = "pyreadline3-3.4.1.tar.gz", hash = "sha256:6f3d1f7b8a31ba32b73917cefc1f28cc660562f39aea8646d30bd6eff21f7bae"}, 555 | ] 556 | 557 | [[package]] 558 | name = "pyyaml" 559 | version = "6.0" 560 | description = "YAML parser and emitter for Python" 561 | optional = false 562 | python-versions = ">=3.6" 563 | groups = ["main"] 564 | files = [ 565 | {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, 566 | {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, 567 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, 568 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, 569 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, 570 | {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, 571 | {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, 572 | {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, 573 | {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, 574 | {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, 575 | {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, 576 | {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, 577 | {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, 578 | {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, 579 | {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, 580 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, 581 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, 582 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, 583 | {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, 584 | {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, 585 | {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, 586 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, 587 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, 588 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, 589 | {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, 590 | {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, 591 | {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, 592 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, 593 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, 594 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, 595 | {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, 596 | {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, 597 | {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, 598 | {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, 599 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, 600 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, 601 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, 602 | {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, 603 | {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, 604 | {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, 605 | ] 606 | 607 | [[package]] 608 | name = "tomli" 609 | version = "2.0.1" 610 | description = "A lil' TOML parser" 611 | optional = false 612 | python-versions = ">=3.7" 613 | groups = ["dev"] 614 | markers = "python_full_version < \"3.11.0a7\"" 615 | files = [ 616 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 617 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 618 | ] 619 | 620 | [[package]] 621 | name = "typed-ast" 622 | version = "1.5.4" 623 | description = "a fork of Python 2 and 3 ast modules with type comment support" 624 | optional = false 625 | python-versions = ">=3.6" 626 | groups = ["dev"] 627 | markers = "python_version < \"3.8\" and implementation_name == \"cpython\"" 628 | files = [ 629 | {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, 630 | {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, 631 | {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, 632 | {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, 633 | {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, 634 | {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, 635 | {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, 636 | {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, 637 | {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, 638 | {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, 639 | {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, 640 | {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, 641 | {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, 642 | {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, 643 | {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, 644 | {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, 645 | {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, 646 | {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, 647 | {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, 648 | {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, 649 | {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, 650 | {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, 651 | {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, 652 | {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, 653 | ] 654 | 655 | [[package]] 656 | name = "typing-extensions" 657 | version = "4.3.0" 658 | description = "Backported and Experimental Type Hints for Python 3.7+" 659 | optional = false 660 | python-versions = ">=3.7" 661 | groups = ["dev"] 662 | markers = "python_version < \"3.10\"" 663 | files = [ 664 | {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, 665 | {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, 666 | ] 667 | 668 | [[package]] 669 | name = "yarl" 670 | version = "1.8.1" 671 | description = "Yet another URL library" 672 | optional = false 673 | python-versions = ">=3.7" 674 | groups = ["dev"] 675 | files = [ 676 | {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:abc06b97407868ef38f3d172762f4069323de52f2b70d133d096a48d72215d28"}, 677 | {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:07b21e274de4c637f3e3b7104694e53260b5fc10d51fb3ec5fed1da8e0f754e3"}, 678 | {file = "yarl-1.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9de955d98e02fab288c7718662afb33aab64212ecb368c5dc866d9a57bf48880"}, 679 | {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ec362167e2c9fd178f82f252b6d97669d7245695dc057ee182118042026da40"}, 680 | {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20df6ff4089bc86e4a66e3b1380460f864df3dd9dccaf88d6b3385d24405893b"}, 681 | {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5999c4662631cb798496535afbd837a102859568adc67d75d2045e31ec3ac497"}, 682 | {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed19b74e81b10b592084a5ad1e70f845f0aacb57577018d31de064e71ffa267a"}, 683 | {file = "yarl-1.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e4808f996ca39a6463f45182e2af2fae55e2560be586d447ce8016f389f626f"}, 684 | {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2d800b9c2eaf0684c08be5f50e52bfa2aa920e7163c2ea43f4f431e829b4f0fd"}, 685 | {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6628d750041550c5d9da50bb40b5cf28a2e63b9388bac10fedd4f19236ef4957"}, 686 | {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f5af52738e225fcc526ae64071b7e5342abe03f42e0e8918227b38c9aa711e28"}, 687 | {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:76577f13333b4fe345c3704811ac7509b31499132ff0181f25ee26619de2c843"}, 688 | {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0c03f456522d1ec815893d85fccb5def01ffaa74c1b16ff30f8aaa03eb21e453"}, 689 | {file = "yarl-1.8.1-cp310-cp310-win32.whl", hash = "sha256:ea30a42dc94d42f2ba4d0f7c0ffb4f4f9baa1b23045910c0c32df9c9902cb272"}, 690 | {file = "yarl-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:9130ddf1ae9978abe63808b6b60a897e41fccb834408cde79522feb37fb72fb0"}, 691 | {file = "yarl-1.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0ab5a138211c1c366404d912824bdcf5545ccba5b3ff52c42c4af4cbdc2c5035"}, 692 | {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0fb2cb4204ddb456a8e32381f9a90000429489a25f64e817e6ff94879d432fc"}, 693 | {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85cba594433915d5c9a0d14b24cfba0339f57a2fff203a5d4fd070e593307d0b"}, 694 | {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ca7e596c55bd675432b11320b4eacc62310c2145d6801a1f8e9ad160685a231"}, 695 | {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0f77539733e0ec2475ddcd4e26777d08996f8cd55d2aef82ec4d3896687abda"}, 696 | {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29e256649f42771829974e742061c3501cc50cf16e63f91ed8d1bf98242e5507"}, 697 | {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7fce6cbc6c170ede0221cc8c91b285f7f3c8b9fe28283b51885ff621bbe0f8ee"}, 698 | {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:59ddd85a1214862ce7c7c66457f05543b6a275b70a65de366030d56159a979f0"}, 699 | {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:12768232751689c1a89b0376a96a32bc7633c08da45ad985d0c49ede691f5c0d"}, 700 | {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:b19255dde4b4f4c32e012038f2c169bb72e7f081552bea4641cab4d88bc409dd"}, 701 | {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6c8148e0b52bf9535c40c48faebb00cb294ee577ca069d21bd5c48d302a83780"}, 702 | {file = "yarl-1.8.1-cp37-cp37m-win32.whl", hash = "sha256:de839c3a1826a909fdbfe05f6fe2167c4ab033f1133757b5936efe2f84904c07"}, 703 | {file = "yarl-1.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:dd032e8422a52e5a4860e062eb84ac94ea08861d334a4bcaf142a63ce8ad4802"}, 704 | {file = "yarl-1.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:19cd801d6f983918a3f3a39f3a45b553c015c5aac92ccd1fac619bd74beece4a"}, 705 | {file = "yarl-1.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6347f1a58e658b97b0a0d1ff7658a03cb79bdbda0331603bed24dd7054a6dea1"}, 706 | {file = "yarl-1.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c0da7e44d0c9108d8b98469338705e07f4bb7dab96dbd8fa4e91b337db42548"}, 707 | {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5587bba41399854703212b87071c6d8638fa6e61656385875f8c6dff92b2e461"}, 708 | {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31a9a04ecccd6b03e2b0e12e82131f1488dea5555a13a4d32f064e22a6003cfe"}, 709 | {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:205904cffd69ae972a1707a1bd3ea7cded594b1d773a0ce66714edf17833cdae"}, 710 | {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea513a25976d21733bff523e0ca836ef1679630ef4ad22d46987d04b372d57fc"}, 711 | {file = "yarl-1.8.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0b51530877d3ad7a8d47b2fff0c8df3b8f3b8deddf057379ba50b13df2a5eae"}, 712 | {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d2b8f245dad9e331540c350285910b20dd913dc86d4ee410c11d48523c4fd546"}, 713 | {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ab2a60d57ca88e1d4ca34a10e9fb4ab2ac5ad315543351de3a612bbb0560bead"}, 714 | {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:449c957ffc6bc2309e1fbe67ab7d2c1efca89d3f4912baeb8ead207bb3cc1cd4"}, 715 | {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a165442348c211b5dea67c0206fc61366212d7082ba8118c8c5c1c853ea4d82e"}, 716 | {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b3ded839a5c5608eec8b6f9ae9a62cb22cd037ea97c627f38ae0841a48f09eae"}, 717 | {file = "yarl-1.8.1-cp38-cp38-win32.whl", hash = "sha256:c1445a0c562ed561d06d8cbc5c8916c6008a31c60bc3655cdd2de1d3bf5174a0"}, 718 | {file = "yarl-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:56c11efb0a89700987d05597b08a1efcd78d74c52febe530126785e1b1a285f4"}, 719 | {file = "yarl-1.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e80ed5a9939ceb6fda42811542f31c8602be336b1fb977bccb012e83da7e4936"}, 720 | {file = "yarl-1.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6afb336e23a793cd3b6476c30f030a0d4c7539cd81649683b5e0c1b0ab0bf350"}, 721 | {file = "yarl-1.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4c322cbaa4ed78a8aac89b2174a6df398faf50e5fc12c4c191c40c59d5e28357"}, 722 | {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fae37373155f5ef9b403ab48af5136ae9851151f7aacd9926251ab26b953118b"}, 723 | {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5395da939ffa959974577eff2cbfc24b004a2fb6c346918f39966a5786874e54"}, 724 | {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:076eede537ab978b605f41db79a56cad2e7efeea2aa6e0fa8f05a26c24a034fb"}, 725 | {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d1a50e461615747dd93c099f297c1994d472b0f4d2db8a64e55b1edf704ec1c"}, 726 | {file = "yarl-1.8.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7de89c8456525650ffa2bb56a3eee6af891e98f498babd43ae307bd42dca98f6"}, 727 | {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4a88510731cd8d4befaba5fbd734a7dd914de5ab8132a5b3dde0bbd6c9476c64"}, 728 | {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2d93a049d29df172f48bcb09acf9226318e712ce67374f893b460b42cc1380ae"}, 729 | {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:21ac44b763e0eec15746a3d440f5e09ad2ecc8b5f6dcd3ea8cb4773d6d4703e3"}, 730 | {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d0272228fabe78ce00a3365ffffd6f643f57a91043e119c289aaba202f4095b0"}, 731 | {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99449cd5366fe4608e7226c6cae80873296dfa0cde45d9b498fefa1de315a09e"}, 732 | {file = "yarl-1.8.1-cp39-cp39-win32.whl", hash = "sha256:8b0af1cf36b93cee99a31a545fe91d08223e64390c5ecc5e94c39511832a4bb6"}, 733 | {file = "yarl-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:de49d77e968de6626ba7ef4472323f9d2e5a56c1d85b7c0e2a190b2173d3b9be"}, 734 | {file = "yarl-1.8.1.tar.gz", hash = "sha256:af887845b8c2e060eb5605ff72b6f2dd2aab7a761379373fd89d314f4752abbf"}, 735 | ] 736 | 737 | [package.dependencies] 738 | idna = ">=2.0" 739 | multidict = ">=4.0" 740 | typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} 741 | 742 | [[package]] 743 | name = "zipp" 744 | version = "3.8.1" 745 | description = "Backport of pathlib-compatible object wrapper for zip files" 746 | optional = false 747 | python-versions = ">=3.7" 748 | groups = ["dev"] 749 | markers = "python_version < \"3.8\"" 750 | files = [ 751 | {file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"}, 752 | {file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"}, 753 | ] 754 | 755 | [package.extras] 756 | docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] 757 | testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>=0.3.7) ; platform_python_implementation != \"PyPy\"", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1) ; platform_python_implementation != \"PyPy\""] 758 | 759 | [metadata] 760 | lock-version = "2.1" 761 | python-versions = "^3.7" 762 | content-hash = "7dcbf2a62cd719732ab9f65531d56f8cf8d2ffa073b30731a90be0a2941b6317" 763 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "appimagecraft" 3 | version = "0.0.1" 4 | description = "appimagecraft generates shell scripts for building AppImages from simple declarative configuration files" 5 | authors = ["TheAssassin "] 6 | license = "MIT" 7 | 8 | [tool.poetry.scripts] 9 | appimagecraft = "appimagecraft._cli:run" 10 | 11 | [tool.poetry.dependencies] 12 | python = "^3.7" 13 | PyYAML = "^6.0" 14 | coloredlogs = "^15.0.1" 15 | 16 | [tool.poetry.dev-dependencies] 17 | black = {extras = ["d"], version = "^22.6.0"} 18 | isort = "^5.10.1" 19 | 20 | [build-system] 21 | requires = ["poetry-core>=1.0.0"] 22 | build-backend = "poetry.core.masonry.api" 23 | 24 | [tool.black] 25 | line-length = 120 26 | 27 | [tool.isort] 28 | profile = "black" 29 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = F405, F403 3 | max-line-length = 120 4 | exclude = tests/* 5 | max-complexity = 10 6 | --------------------------------------------------------------------------------